diff --git a/.env.dist b/.env.dist new file mode 100644 index 0000000..cb55faa --- /dev/null +++ b/.env.dist @@ -0,0 +1,40 @@ +APP_RELEASE=1.0 +APP_DEFAULT_MODULE=front +APP_ENV=development +DEBUG_MODE=1 + +# Sets the environment variable using an embedded command, this will NOT work on windows +#APP_VERSION=$(git rev-parse --verify HEAD) +APP_VERSION=1 + +# Generic settings +TIMEZONE=UTC +DEFAULT_LOCALE="en-gb" + +# Common flags +COMPILE_ALWAYS_FLAG=3 +COMPILE_ON_CACHE_FLAG=2 +COMPILE_NOT_EXISTS_FLAG=1 +COMPILE_NONE_FLAG=0 + +# Runtime configuration +LOGGER_ENABLED=1 +ERROR_NOTIFICATIONS_ENABLED=1 +ROUTER_DEBUG=0 +DISPATCHER_DEBUG=0 + +MAILER_ENABLED=0 + +# Optimize application behaviour +MINIFY_ASSETS_MODE=${COMPILE_NOT_EXISTS_FLAG} +SLEET_COMPILER_MODE=${COMPILE_NOT_EXISTS_FLAG} + +# Configure database +MONGODB_HOST=mongo_db +MONGODB_PORT=27017 +MONGODB_DATABASE=iceapp_db +MONGODB_USERNAME=iceapp +MONGODB_PASSWORD=y0urPassw0rd +MONGODB_AUTH_MECHANISM=SCRAM-SHA-1 + + diff --git a/.gitignore b/.gitignore index 8113660..518eafa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,16 @@ -.* +.idea +*.log +*.env +var/cache/* +var/log/* +!var/stubs/HOWTO.md +var/stubs + _* *~ !.gitignore +!.env.dist !.travis.yml !.htaccess /vendor/ /composer.lock -/App/Modules/Front/Controllers/TestController.php \ No newline at end of file diff --git a/App/Boot/Base.php b/App/Boot/Base.php deleted file mode 100644 index cbfbe03..0000000 --- a/App/Boot/Base.php +++ /dev/null @@ -1,261 +0,0 @@ - - * @license iceframework.org Ice - * @link iceframework.org - */ -class Base extends App -{ - - /** - * Meta description. - * - * @var string - */ - public $description; - - /** - * Meta keywords. - * - * @var string - */ - public $keywords; - - /** - * Initialize the application. - * - * @return object Base - */ - public function initialize() - { - // Handle the errors by Error class - $this->di->errors(); - - // Load the config - $this->di->config = new Ini(__ROOT__ . '/App/cfg/config.ini'); - $this->di->config->set('assets', new Ini(__ROOT__ . '/App/cfg/assets.ini')); - - // Set the environment - $this->di->env = new Env(__ROOT__ . '/App/.env'); - $this->di->env = new Env(__ROOT__ . '/App/.env.' . $this->di->env->environment); - - // Register modules - $this->setModules($this->di->config->{$this->di->config->modules->application->modules}->toArray()); - - // Set dump - if ($this->di->env->environment == "development") { - $this->dump->setDetailed(true); - } - - // Configure services - $this->di->crypt->setKey($this->di->config->crypt->key); - $this->di->cookies->setSalt($this->di->config->cookie->salt); - $this->di->i18n = new I18n($this->di->config->i18n->toArray()); - $this->di->auth = new Auth($this->di->config->auth->toArray()); - $this->di->url->setBaseUri($this->di->config->app->base_uri); - $this->di->url->setStaticUri($this->di->config->app->static_uri); - - // Set the assets service - $this->di->assets->setOptions([ - 'source' => __ROOT__ . '/public/', - 'target' => 'min/', - 'minify' => $this->di->env->assets->minify - ]); - - // Set the dispatcher service - $this->di->dispatcher->setSilent($this->di->env->silent->dispatcher); - - // Set the router service - $this->di->set('router', function () { - $router = new Router(); - $router->setDefaultModule($this->di->config->modules->application->default); - $router->setSilent($this->di->env->silent->router); - $router->setRoutes((new Routes())->universal()); - return $router; - }); - - // Set the db service - $this->di->set('db', function () { - $db = new Db( - $this->di->config->database->type, - $this->di->config->database->host, - $this->di->config->database->port, - $this->di->config->database->name, - $this->di->config->database->user, - $this->di->config->database->password, - $this->di->config->database->options->toArray() - ); - - if ($this->di->config->app->env == "development" && $this->di->config->database->type != "mongodb") { - $db->getDriver()->getClient()->setAttribute(\Pdo::ATTR_ERRMODE, \Pdo::ERRMODE_EXCEPTION); - } - - return $db; - }); - - // Set the view service - $this->di->set('view', function () { - $view = new View(); - $view->setViewsDir(__ROOT__ . '/App/views/'); - - // Options for Sleet template engine - $sleet = new Sleet($view, $this->di); - $sleet->setOptions([ - 'compileDir' => __ROOT__ . '/App/tmp/sleet/', - 'trimPath' => __ROOT__, - 'compile' => $this->di->env->sleet->compile - ]); - - // Set template engines - $view->setEngines([ - '.md' => 'App\Libraries\Markdown', - '.sleet' => $sleet, - '.phtml' => 'Ice\Mvc\View\Engine\Php' - ]); - - return $view; - }); - - // Register hooks - $this->registerHooks(); - - return $this; - } - - /** - * Register hooks in the di. - * - * @return void - */ - public function registerHooks() - { - // Response code - $this->di->hook('app.after.handle', function ($response) { - // Display pretty view for some response codes - if (!$response->isInformational() && !$response->isSuccessful() && !$response->isRedirect()) { - $code = $response->getStatus(); - - // Add meta tags - $this->di->tag - ->setDocType(Tag::XHTML5) - ->setTitle(_t('status :code', [':code' => $response->getStatus()])) - ->appendTitle($this->di->config->app->name) - ->setMeta([]) - ->addMeta(['charset' => 'utf-8']) - ->addMeta(['initial-scale=1, minimum-scale=1, width=device-width', 'viewport']); - - // Add styles to assets - $this->di->assets - ->setCollections([]) - ->add('css/response.css'); - - // Restore default view settings - $this->di->view - ->setViewsDir(__ROOT__ . '/App/views/') - ->setPartialsDir('partials/') - ->setLayoutsDir('layouts/') - ->setFile('partials/status') - ->setContent($this->di->view->render()); - - $response->setBody($this->di->view->layout('minimal')); - } - }); - - // Pretty exception - $this->di->hook('exception.after.uncaught', function ($e, $di) { - $error = get_class($e) . '[' . $e->getCode() . ']: ' . $e->getMessage(); - $info = $e->getFile() . '[' . $e->getLine() . ']'; - $debug = "Trace: \n" . $e->getTraceAsString() . "\n"; - - if ($di->env->error->log) { - // Log error into the file - $logger = new Logger(__ROOT__ . '/App/log/' . date('Ymd') . '.log'); - $logger->error($error); - $logger->info($info); - $logger->debug($debug); - } - - if ($di->env->error->email) { - // Send email to admin - $log = $di->dump->vars($error, $info, $debug); - - if ($di->has("request")) { - $log .= $di->dump->one($di->request->getData(), '_REQUEST'); - $log .= $di->dump->one($di->request->getServer()->getData(), '_SERVER'); - $log .= $di->dump->one($di->request->getPost()->getData(), '_POST'); - $log .= $di->dump->one($di->request->getQuery()->getData(), '_GET'); - } - - $email = new Email(); - $email->prepare(_t('somethingIsWrong'), $di->config->app->admin, 'email/error', ['log' => $log]); - - if ($email->Send() !== true) { - $logger = new Logger(__ROOT__ . '/App/log/' . date('Ymd') . '.log'); - $logger->error($email->ErrorInfo); - } - } - - $response = $di->get('response'); - $response->setStatus(500); - - if (PHP_SAPI == 'cli') { - $response->setBody($e->getMessage()); - } elseif ($di->env->error->hide) { - $di->applyHook("app.after.handle", [$response]); - } else { - // Add meta tags - $di->tag - ->setDocType(Tag::XHTML5) - ->setTitle(_t('status :code', [':code' => $e->getCode()])) - ->appendTitle($di->config->app->name) - ->setMeta([]) - ->addMeta(['charset' => 'utf-8']) - ->addMeta(['IE=edge', 'http-equiv' => 'X-UA-Compatible']) - ->addMeta(['width=device-width, initial-scale=1.0', 'viewport']) - ->addMeta(['noindex, nofollow', 'robots']); - - // Add styles to assets - $di->assets - ->setCollections([]) - ->add('css/exception.css') - ->add('css/highlight/tomorrow.min.css', $this->config->assets->highlight) - ->add('js/jquery.min.js', $this->config->assets->jquery) - ->add('js/plugins/highlight.min.js', $this->config->assets->highlight) - ->add('js/exception.js'); - - // Restore default view settings - $di->view - ->setViewsDir(__ROOT__ . '/App/views/') - ->setPartialsDir('partials/') - ->setLayoutsDir('layouts/') - ->setFile('partials/exception') - ->setVar('e', $e) - ->setContent($di->view->render()); - - $response->setBody($di->view->layout('minimal')); - } - }); - } -} diff --git a/App/cfg/env.ini b/App/cfg/env.ini deleted file mode 100644 index 19a2b1b..0000000 --- a/App/cfg/env.ini +++ /dev/null @@ -1,55 +0,0 @@ -; App Environment -; -; Compile sleet views / minify assets -; ALWAYS = 3 -; IF_CHANGE = 2 -; NOT_EXIST = 1 (Not checks for changes, used for performance reasons) -; NEVER = 0 (Just load compiled/minified files, you'll get an error if file doesn't exist) - -[development] -; Dump & Log exception -error[debug] = true -error[hide] = false -error[log] = true -error[email] = false -silent[router] = false -silent[dispatcher] = false -email = false -sleet[compile] = 3 -assets[minify] = 3 - -[testing] -; Log & display pretty exception message -error[debug] = false -error[hide] = false -error[log] = true -error[email] = false -silent[router] = false -silent[dispatcher] = false -email = false -sleet[compile] = 2 -assets[minify] = 2 - -[staging] -; Log & Email & display pretty response -error[debug] = false -error[hide] = false -error[log] = true -error[email] = true -silent[router] = true -silent[dispatcher] = true -email = true -sleet[compile] = 1 -assets[minify] = 1 - -[production] -; Log & Email & hide message & display pretty response -error[debug] = false -error[hide] = true -error[log] = true -error[email] = true -silent[router] = true -silent[dispatcher] = true -email = true -sleet[compile] = 0 -assets[minify] = 0 \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ee2c654 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,462 @@ +# +# This dockerfile was created mainly for development purposes. Some configurations are production ready, others were only +# used for development purposes. +# Feel free to reuse whatever you want, and know that your efforts to help improve this code is greatly appreciated. +# +# Kind regards, +# Daan Biesterbos +# + +# +# PURPOSE: Nginx Webserver +# VERSION: 1.0.0 +# + +FROM nginx:stable as WEBSERVER + +MAINTAINER Daan Biesterbos + +ENV WWW_USER=nginx +ENV WWW_GROUP=nginx +ENV WWW_DOCUMENT_ROOT=/var/www/httpdocs +ENV WWW_UID=1000 +ENV WWW_GID=1000 +ENV UPLOADS_URL_PATH=/media/uploads +ENV ERROR_LOG=/var/log/nginx/app_error_prod.log +ENV ERROR_DEV_LOG=/var/log/nginx/app_error_dev.log +ENV ACCESS_LOG=/var/log/nginx/app_access_prod.log +ENV ACCESS_DEV_LOG=/var/log/nginx/app_access_dev.log +ENV ACCESS_SECURITY_LOG=/var/log/nginx/app_security_access.log +ENV STATIC_FILE_ACCESS_LOG=off +ENV PHP_FPM_HOST=php_iceapp +ENV PHP_FPM_PORT=9000 +ENV NGINX_HTTP_PORT=80 +ENV NGINX_HTTPS_PORT=443 +ENV NGINX_DOCUMENT_ROOT=/var/www/httpdocs/public +ENV NGINX_CACHE_MIN_USES=5 +ENV NGINX_CACHE_NAME=app_cache +ENV NGINX_CACHE_METADATA_MEMORY_SIZE=50m +ENV NGINX_CACHE_TTL=60m +ENV NGINX_CACHE_PATH=/tmp/cache +ENV NGINX_CACHE_MAX_SIZE=2g +ENV NGINX_CACHE_USE_TEMP_PATH=off +ENV STATIC_FILE_EXTENSIONS='jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|pdf|txt|tar|wav|bmp|rtf|js|html|xhtml' +ENV DENIED_EXTENSIONS='dist|markdown|md|twig|yaml|yml|sass|scss|zip|tar|tar.gz|rar' + +RUN rm /etc/nginx/conf.d/default.conf +RUN usermod -u $WWW_UID $WWW_USER +RUN groupmod -g $WWW_GID $WWW_GROUP 2> /dev/null || usermod -a -G $WWW_GID $WWW_USER + +COPY docker/conf/nginx/default.conf.template /etc/nginx/conf.d/default.conf.template +COPY docker/conf/nginx/include.fastcgi_common /etc/nginx/conf.d/include.fastcgi_common +COPY docker/conf/nginx/include.whitelist /etc/nginx/conf.d/include.whitelist + +RUN mkdir -p /var/www/httpdocs/.git +# We don't want to copy .git to our container, ever. Make it a volume. +VOLUME /var/www/httpdocs/.git +WORKDIR /var/www/httpdocs +COPY . /var/www/httpdocs + +RUN chown nginx:nginx /var/www/httpdocs -R + +CMD /bin/bash -c "envsubst '\$UPLOADS_URL_PATH,\$ERROR_LOG, \$ERROR_DEV_LOG, \$ACCESS_LOG, \$ACCESS_DEV_LOG, \$ACCESS_SECURITY_LOG, \$STATIC_FILE_ACCESS_LOG, \$PHP_FPM_HOST, \$PHP_FPM_PORT, \$NGINX_HTTP_PORT, \$NGINX_HTTPS_PORT, \$NGINX_DOCUMENT_ROOT, \$NGINX_CACHE_MIN_USES, \$NGINX_CACHE_NAME, \$NGINX_CACHE_METADATA_MEMORY_SIZE, \$NGINX_CACHE_TTL, \$NGINX_CACHE_PATH, \$NGINX_CACHE_MAX_SIZE, \$NGINX_CACHE_USE_TEMP_PATH, \$STATIC_FILE_EXTENSIONS, \$DENIED_EXTENSIONS' < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf && exec nginx -g 'daemon off;'" + + +# +# PURPOSE: MONGODB +# VERSION: 1.0.0 +# + +FROM bitnami/mongodb:latest as MONGODB + +MAINTAINER Daan Biesterbos + + +# +# PURPOSE: PHP-FPM +# VERSION: 1.0.0 +# + +FROM ubuntu:18.04 as PHP-FPM + +MAINTAINER Daan Biesterbos + + +# In order to run commands inside the container we'll need to set the following environment variable +# Note that we also need to provide the same value to the nginx container because PHP-FPM might not have +# access to the environment variables. +ENV SECRETS_ENV=/run/secrets/app_conf + +ENV APP_ENV=prod +ENV WWW_USER=www-data +ENV COMPOSER_ALLOW_SUPERUSER=1 +ENV WWW_GROUP=www-data +ENV WWW_DOCUMENT_ROOT=/var/www/httpdocs +ENV WWW_UID=1000 +ENV WWW_GID=1000 +ENV DEBIAN_FRONTEND noninteractive + +# Define environment variables for each directory that we commonly refer want from configurations and scripts. +ENV PHP_MODS_AVAILABLE_DIR=/etc/php/7.2/mods-available +ENV PHP_FPM_CONFD_DIR=/etc/php/7.2/fpm/conf.d +ENV PHP_CLI_CONFD_DIR=/etc/php/7.2/cli/conf.d +ENV PHP_CLI_INI=/etc/php/7.2/cli/php.ini +ENV PHP_FPM_INI=/etc/php/7.2/fpm/php.ini +ENV PHP_FPM_CONF=/etc/php/7.2/fpm/php-fpm.conf +ENV PHP_FPM_WWW_CONF=/etc/php/7.2/fpm/pool.d/www.conf + +ENV TIMEZONE=UTC +ENV ICE_FRAMEWORK_VERSION=1.4.0 + +# Define arguments +ARG buildno +ARG gitcommithash +ARG ZEPHIR_PARSER_VERSION="1.1.4" +ARG ZEPHIR_LANG_VERSION="0.11.8" +ARG OPCACHE_EXT=1 +ARG OPCACHE_EXT_VERSION=\* +ARG BCMATH_EXT=0 +ARG BCMATH_EXT_VERSION=\* +ARG PROTOBUF_EXT=1 +ARG SQLITE3_EXT=0 +ARG SQLITE3_EXT_VERSION=\* +ARG REDIS_EXT=1 +ARG PHPDBG_EXT=0 +ARG PHPDBG_EXT_VERSION=\* +ARG MONGODB_EXT=1 +ARG MONGODB_EXT_VERSION=1.5.3 +ARG MYSQL_EXT=1 +ARG MYSQL_EXT_VERSION=\* +ARG PGSQL_EXT=0 +ARG PGSQL_EXT_VERSION=\* +ARG SOAP_EXT=0 +ARG SOAP_EXT_VERSION=\* +ARG GD_EXT=1 +ARG GD_EXT_VERSION=\* +ARG IMAP_EXT=1 +ARG IMAP_EXT_VERSION=\* +ARG BZ2_EXT=0 +ARG BZ2_EXT_VERSION=\* +ARG INTL_EXT=1 +ARG INTL_EXT_VERSION=\* +ARG PSPELL_EXT=0 +ARG PSPELL_EXT_VERSION=\* +ARG ODBC_EXT=0 +ARG ODBC_EXT_VERSION=\* +ARG XMLRPC_EXT=0 +ARG XMLRPC_EXT_VERSION=\* +ARG CGI_EXT=0 +ARG CGI_EXT_VERSION=\* +ARG TIDEWAYS_EXT=0 +ARG TIDEWAYS_EXT_VERSION=\* +ARG TIDY_EXT=1 +ARG TIDY_EXT_VERSION=\* +ARG RECODE_EXT=0 +ARG RECODE_EXT_VERSION=\* +ARG ZIP_EXT=1 +ARG ZIP_EXT_VERSION=\* +ARG LDAP_EXT=0 +ARG LDAP_EXT_VERSION=\* +ARG SOLR_EXT=1 +ARG SOLR_EXT_VERSION=2.4.0 +ARG INSTALL_IMAGE_OPTIMIZERS=0 +ARG INSTALL_FRONTEND_TOOLS=0 + +# Output version info +RUN echo "Build number: $buildno" +RUN echo "Based on commit: $gitcommithash" + +# Fix permissions +RUN usermod -u $WWW_UID $WWW_USER +RUN groupmod -g $WWW_GID $WWW_GROUP 2> /dev/null || usermod -a -G $WWW_GID $WWW_USER + +COPY docker/conf/opcache.ini ${PHP_MODS_AVAILABLE_DIR}/opcache.user.ini +COPY docker/conf/opcache.ini ${PHP_FPM_CONFD_DIR}/opcache.user.ini +#COPY docker/conf/solr.ini ${PHP_MODS_AVAILABLE_DIR}/solr.ini + +COPY docker/conf/xdebug.ini ${PHP_MODS_AVAILABLE_DIR}/xdebug.user.ini +COPY docker/conf/xdebug.ini ${PHP_FPM_CONFD_DIR}/xdebug.user.ini + +RUN apt-get clean && apt-get -y update && apt-get install -y locales curl software-properties-common git apt-utils zip unzip re2c iproute2 && locale-gen en_US.UTF-8 +RUN LC_ALL=en_US.UTF-8 add-apt-repository ppa:ondrej/php + +RUN apt-get update && apt-get install -y \ + php-pear \ + php7.2-dev \ + php7.2-fpm \ + net-tools \ + iputils-ping \ + iproute2 \ + php7.2-cli \ + php7.2-common \ + php7.2-curl \ + php7.2-json \ + php7.2-mbstring \ + php7.2-readline \ + php7.2-xml \ + php7.2-xsl \ + php7.2-json \ + php7.2-dev \ + gcc \ + make \ + re2c \ + libpcre3-dev \ + build-essential \ + autoconf \ + automake + +RUN sed -i -e "s/;date.timezone =.*/date.timezone = UTC/" ${PHP_CLI_INI} +RUN sed -i -e "s/;date.timezone =.*/date.timezone = UTC/" ${PHP_FPM_INI} + +RUN sed -i "/daemonize /c \ +daemonize = no" ${PHP_FPM_CONF} + +RUN sed -i "/cgi.fix_pathinfo /c \ +cgi.fix_pathinfo=1" ${PHP_FPM_CONF} + +RUN sed -i "/^listen /c \ +listen = 0.0.0.0:9000" ${PHP_FPM_WWW_CONF} + +# Ensure worker stdout and stderr are sent to the main error log. +RUN sed -i "/^;catch_workers_output /c \ +catch_workers_output = yes" ${PHP_FPM_WWW_CONF} + +RUN sed -i "/^;ping.path /c \ +ping.path = /ping" ${PHP_FPM_WWW_CONF} + +RUN sed -i "/^;ping.response /c \ +ping.response = pong" ${PHP_FPM_WWW_CONF} + + +VOLUME /var/www/httpdocs + +RUN sed -i "/^;chdir /c \ +chdir = /var/www/httpdocs" ${PHP_FPM_WWW_CONF} + +# clear_env = no + +RUN sed -i "/^upload_max_filesize /c \ +upload_max_filesize = 5M" ${PHP_FPM_INI} + +RUN sed -i "/^post_max_size /c \ +post_max_size = 5M" ${PHP_FPM_INI} + +RUN sed -i "/^memory_limit /c \ +memory_limit = 1024M" ${PHP_CLI_INI} + +RUN sed -i "/^max_execution_time /c \ +max_execution_time = -1" ${PHP_CLI_INI} + + +RUN sed -i -e "s/;catch_workers_output =.*/catch_workers_output = yes/" ${PHP_FPM_WWW_CONF} +RUN sed -i -e "s/;clear_env =.*/clear_env = no/" ${PHP_FPM_WWW_CONF} +RUN sed -i -e "s/pid =.*/pid = \/var\/run\/php7.2-fpm.pid/" ${PHP_FPM_CONF} + +##################################### +# APP ENVIRONMENT (dev, prod, ...) +##################################### + +RUN if [ ${APP_ENV} = "dev" ]; then \ + sed -i -e "s/display_errors =.*/display_errors = On/" ${PHP_FPM_INI} \ + && apt-get install -y php7.2-xdebug \ +;else \ + sed -i "s/display_errors = .*/display_errors = Off/" ${PHP_FPM_INI} \ + && rm ${PHP_MODS_AVAILABLE_DIR}/xdebug.user.ini \ + && rm ${PHP_FPM_CONFD_DIR}/xdebug.user.ini \ +;fi + + +USER root + +##################################### +# INSTALL PHP EXTENSIONS +##################################### + +RUN if [ ${INTL_EXT} = 1 ]; then \ + apt-get install -y php7.2-intl=${INTL_EXT_VERSION} \ +;fi + +RUN if [ ${BZ2_EXT} = 1 ]; then \ + apt-get install -y php7.2-bz2=${BZ2_EXT_VERSION} \ +;fi + +RUN if [ ${PSPELL_EXT} = 1 ]; then \ + apt-get install -y php7.2-pspell=${PSPELL_EXT_VERSION} \ +;fi + +RUN if [ ${GD_EXT} = 1 ]; then \ + apt-get install -y php7.2-gd=${GD_EXT_VERSION} \ +;fi + +RUN if [ ${IMAP_EXT} = 1 ]; then \ + apt-get install -y php7.2-imap=${IMAP_EXT_VERSION} \ +;fi +RUN if [ ${CGI_EXT} = 1 ]; then \ + apt-get install -y php7.2-cgi=${CGI_EXT_VERSION} \ +;fi + +RUN if [ ${XMLRPC_EXT} = 1 ]; then \ + apt-get install -y php7.2-xmlrpc=${XMLRPC_EXT_VERSION} \ +;fi + +RUN if [ ${SOAP_EXT} = 1 ]; then \ + apt-get install -y php7.2-soap=${SOAP_EXT_VERSION} \ +;fi + +RUN if [ ${ZIP_EXT} = 1 ]; then \ + apt-get install -y php7.2-zip=${ZIP_EXT_VERSION} \ +;fi + +RUN if [ ${RECODE_EXT} = 1 ]; then \ + apt-get install -y php7.2-recode=${RECODE_EXT_VERSION} \ +;fi + +RUN if [ ${PGSQL_EXT} = 1 ]; then \ + apt-get install -y php7.2-pgsql=${PGSQL_EXT_VERSION} \ +;fi + +RUN if [ ${TIDY_EXT} = 1 ]; then \ + apt-get install -y php7.2-tidy=${TIDY_EXT_VERSION} \ +;fi + +RUN if [ ${ODBC_EXT} = 1 ]; then \ + apt-get install -y php7.2-odbc=${ODBC_EXT_VERSION} \ +;fi + +RUN if [ ${LDAP_EXT} = 1 ]; then \ + apt-get install -y php7.2-ldap=${LDAP_EXT_VERSION} \ +;fi + +RUN if [ ${PHPDBG_EXT} = 1 ]; then \ + apt-get install -y php7.2-phpdbg=${PHPDBG_EXT_VERSION} \ +;fi + +RUN if [ ${MYSQL_EXT} = 1 ]; then \ + apt-get install -y php7.2-mysql=${MYSQL_EXT_VERSION} \ +;fi + +RUN if [ ${TIDEWAYS_EXT} = 1 ]; then \ + apt-get install -y php-tideways=${TIDEWAYS_EXT_VERSION} \ +;fi + +RUN if [ ${MONGODB_EXT} = 1 ]; then \ + apt-get install -y php-mongodb=${SQLITE3_EXT_VERSION} --fix-missing \ +;fi + +RUN if [ ${SOLR_EXT} = 1 ]; then \ + apt-get install -y php-solr \ +;fi + +RUN if [ ${PROTOBUF_EXT} = 1 ]; then \ + pecl install -o -f protobuf \ +;fi + +RUN if [ ${SQLITE3_EXT} = 1 ]; then \ + apt-get install -y php7.2-sqlite3=${SQLITE3_EXT_VERSION} \ +;fi + +RUN if [ ${BCMATH_EXT} = 1 ]; then \ + apt-get install -y php7.2-bcmath=${BCMATH_EXT_VERSION} \ +;fi + +RUN if [ ${REDIS_EXT} = 1 ]; then \ + pecl install redis \ +;fi + +RUN if [ ${OPCACHE_EXT} = 1 ]; then \ + apt-get install -y php7.2-opcache=${OPCACHE_EXT_VERSION} \ +;else \ + rm ${PHP_MODS_AVAILABLE_DIR}/opcache.user.ini \ + rm ${PHP_FPM_CONFD_DIR}/opcache.user.ini \ +;fi + +ENV INSTALL_IMAGE_OPTIMIZERS ${INSTALL_IMAGE_OPTIMIZERS} +RUN if [ ${INSTALL_IMAGE_OPTIMIZERS} = 1 ]; then \ + apt-get update -yqq && apt-get install -y jpegoptim optipng pngquant gifsicle \ +;fi + +##################################### +# INSTALL TOOLS & UTILS +##################################### + +RUN if [ ${INSTALL_FRONTEND_TOOLS} = 1 ]; then \ + apt-get update && apt-get install -my wget gnupg \ + && curl -sL https://deb.nodesource.com/setup_6.x | bash - \ + && apt-get install -y nodejs \ + && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ + && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list \ + && apt-get update \ + && apt-get install yarn \ + && yarn global add gulp \ +;fi + +RUN curl -sS https://getcomposer.org/installer -o composer-setup.php && php composer-setup.php --install-dir=/usr/bin --filename=composer + +# Install zephir language, zephir parser and dependencies +RUN pecl install ds \ + && pecl install psr \ + && echo "extension=ds.so" > ${PHP_MODS_AVAILABLE_DIR}/ds.ini \ + && echo "extension=ds.so" > ${PHP_FPM_CONFD_DIR}/ds.ini \ + && echo "extension=ds.so" > ${PHP_CLI_CONFD_DIR}/ds.ini \ + && echo "extension=psr.so" > ${PHP_MODS_AVAILABLE_DIR}/psr.ini \ + && echo "extension=psr.so" > ${PHP_FPM_CONFD_DIR}/psr.ini \ + && echo "extension=psr.so" > ${PHP_CLI_CONFD_DIR}/psr.ini \ + && cd / && curl -sSL "https://github.com/phalcon/php-zephir-parser/archive/v${ZEPHIR_PARSER_VERSION}.tar.gz" | tar -xz \ + && cd php-zephir-parser-${ZEPHIR_PARSER_VERSION} \ + && /usr/bin/phpize7.2 \ + && ./configure --with-php-config=/usr/bin/php-config7.2 \ + && make \ + && make install \ + && echo "extension=zephir_parser.so" > ${PHP_MODS_AVAILABLE_DIR}/zephir_parser.ini \ + && echo "extension=zephir_parser.so" > ${PHP_FPM_CONFD_DIR}/zephir_parser.ini \ + && echo "extension=zephir_parser.so" > ${PHP_CLI_CONFD_DIR}/zephir_parser.ini \ + && cd / && git clone https://github.com/phalcon/zephir \ + && cd zephir/ext \ + && export CC="gcc" \ + && export CFLAGS="-O2 -Wall -fvisibility=hidden -flto -DZEPHIR_RELEASE=1" \ + && make -s clean \ + && phpize7.2 --silent \ + && ./configure --with-php-config=/usr/bin/php-config7.2 --silent --enable-test \ + && make -s && make -s install \ + && apt-get install -y php-gmp php7.2-sqlite curl php-mbstring git unzip \ + && cd ../ \ + && composer install \ + && make test \ + && ln -s /zephir/zephir /usr/bin/zephir + + +##################################### +# PACKAGE SOURCE CODE (NO VOLUME IN PROD) +##################################### + +RUN mkdir -p /var/www/httpdocs/.git +# We don't want to copy .git to our container, ever. Make it a volume. +VOLUME /var/www/httpdocs/.git +WORKDIR /var/www/httpdocs +COPY . /var/www/httpdocs + +RUN chown www-data:www-data /var/www/httpdocs -R + +##################################### +# CLEANUP +##################################### + +RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + + +##################################### +# DOCKER CONTAINER CONFIGS +##################################### + +EXPOSE 9000 + +COPY docker/entrypoint_runall.sh /usr/bin/docker-entrypoint.sh +COPY docker/bin/iceframework.sh /opt/docker/provision/entrypoint.d/iceframework.sh +COPY docker/bin/permissions.sh /opt/docker/provision/entrypoint.d/permissions.sh + + +RUN chmod +x /usr/bin/docker-entrypoint.sh +RUN chmod +x /opt/docker/provision/entrypoint.d/* +ENTRYPOINT ["/usr/bin/docker-entrypoint.sh"] + +CMD ["php-fpm7.2"] diff --git a/composer.json b/composer.json index e51d892..6ac5df1 100644 --- a/composer.json +++ b/composer.json @@ -10,13 +10,19 @@ "email": "info@iceframework.org" } ], + "autoload": { + "psr-4": { + "App\\": "src/App/" + } + }, "require": { "php": ">=5.6", "ext-ice": ">=1.4.0", "phpmailer/phpmailer": ">=5.2.10", "crossjoin/pre-mailer": "~1.0.5", "erusev/parsedown-extra": ">=0.7.0", - "mongodb/mongodb": "1.2.0" + "mongodb/mongodb": "1.2.0", + "symfony/dotenv": "^4.2" }, "require-dev": { "squizlabs/php_codesniffer": ">=2.3.3", diff --git a/App/cfg/assets.ini b/config/assets.ini similarity index 100% rename from App/cfg/assets.ini rename to config/assets.ini diff --git a/App/cfg/config.ini b/config/config.ini similarity index 85% rename from App/cfg/config.ini rename to config/config.ini index 4bad121..67a1581 100644 --- a/App/cfg/config.ini +++ b/config/config.ini @@ -7,7 +7,6 @@ domain = "example.com" timezone = "Europe/London" base_uri = "/" static_uri = "/" -env = "development" admin = "admin@example.com" [key] @@ -17,12 +16,12 @@ ms_validate = "" [database] type = "mongodb" -host = "localhost" -port = 27017 -user = "demo" -password = "demo" + name = "ice" -options[authMechanism] = "MONGODB-CR" +options[authMechanism] = "SCRAM-SHA-1" +; For old MongoDB versions you might need to use MONGODB-CR, this authentication is deprecated since MongoDB 4.0 and +; was removed in MongoDB 4.1 +;options[authMechanism] = "MONGODB-CR" [auth] hash_method = "sha256" @@ -51,10 +50,11 @@ lifetime = 3600 [i18n] lang = "en-gb" -dir = __ROOT__"/App/i18n/" +dir = __ROOT__"/translations/" langs[en-gb] = "English" langs[pl-pl] = "Polish" langs[ja-jp] = "Japanese" +langs[nl-nl] = "Nederlands" [modules] application[modules] = "applicationModules" diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..ea0084e --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,114 @@ +version: '3.4' +services: + + ######################################################## + # NGINX # + ######################################################## + + nginx_iceapp: + build: + context: . + target: WEBSERVER + ports: + - "17002:80" + - "443" + volumes: + - ".:/var/www/httpdocs" + - "/var/www/httpdocs/.git" # always exclude from volume + depends_on: + - php_iceapp + networks: + - app-tier + - web + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:80"] + interval: 1m30s + timeout: 10s + retries: 3 + #start_period: 40s + environment: + WWW_UID: 1000 + WWW_GUI: 1000 + WWW_USER: nginx + WWW_GROUP: nginx + PHP_FPM_HOST: php_iceapp + + + ######################################################## + # PHP / PHP-FPM # + ######################################################## + + php_iceapp: + build: + context: . + target: PHP-FPM + args: + MYSQL_EXT: 0, + TIDEWAYS_XHPPROF_EXT: 0, + TIDEWAYS_EXT: 0, + RECODE_EXT: 0 + ports: + - "9000" + volumes: + - ".:/var/www/httpdocs" + #- "../../ext:/ext" + - "/var/www/httpdocs/.git" # always exclude from volume + networks: + - app-tier + - web + depends_on: + - mongo_db + healthcheck: + test: ["CMD", "curl", "-f", "http://nginx_iceapp:80"] + interval: 1m30s + timeout: 10s + retries: 3 + #start_period: 40s + environment: + WWW_UID: 1000 + WWW_GUI: 1000 + WWW_USER: "nginx" + WWW_GROUP: "www-data" + MONGODB_HOST: "mongo_db" + MONGODB_PORT: "27017" + MONGODB_USERNAME: "iceapp" + MONGODB_PASSWORD: "y0urPassw0rd" + MONGODB_DATABASE: "iceapp_db" + #secrets: + # - "reviewsite_conf_dev" + + ######################################################## + # MONGODB # + ######################################################## + + mongo_db: + build: + context: . + target: MONGODB + ports: + - "27044:27017" + hostname: mongo_db + domainname: mongo_db.local + networks: + - app-tier + environment: + ALLOW_EMPTY_PASSWORD: 'yes' + MONGODB_EXTRA_FLAGS: --wiredTigerCacheSizeGB=2 + MONGODB_ROOT_PASSWORD: "y0urPassw0rd" + MONGODB_USERNAME: "iceapp" + MONGODB_PASSWORD: "y0urPassw0rd" + MONGODB_DATABASE: "iceapp_db" + +volumes: + iceapp_cache: + driver: "local" + iceapp_logs: + driver: "local" + +networks: + app-tier: + web: + +#secrets: +# secrets_conf: +# file: ./deployments/jobsites/iqhire/iqhire.env \ No newline at end of file diff --git a/docker/bin/iceframework.sh b/docker/bin/iceframework.sh new file mode 100755 index 0000000..4b33cca --- /dev/null +++ b/docker/bin/iceframework.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +cd / && mkdir /ext +cd /ext && curl -sSL "https://github.com/ice/framework/archive/$ICE_FRAMEWORK_VERSION.tar.gz" | tar -xz +cd framework-${ICE_FRAMEWORK_VERSION} + +sed -i -e "s/sudo / /" install +sed -i -e "s/sudo / /" build/php7/install +sed -i -e "s/sudo / /" build/php5/install + +chmod u+x install +chmod u+x build/php7/install +chmod u+x build/php7/install + +./install + +echo "extension=ice.so" > ${PHP_MODS_AVAILABLE_DIR}/ice.ini +echo "extension=ice.so" > ${PHP_FPM_CONFD_DIR}/ice.ini +echo "extension=ice.so" > ${PHP_CLI_CONFD_DIR}/ice.ini + + +service php7.2-fpm reload \ No newline at end of file diff --git a/docker/bin/permissions.sh b/docker/bin/permissions.sh new file mode 100644 index 0000000..db6e0fd --- /dev/null +++ b/docker/bin/permissions.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +# trace ERR through pipes +set -o pipefail + +# trace ERR through 'time command' and other functions +set -o errtrace + +# set -u : exit the script if you try to use an uninitialised variable +set -o nounset + +# set -e : exit the script if any statement returns a non-true return value +set -o errexit + +WWW_USER=${WWW_USER:-} +WWW_GROUP=${WWW_GROUP:-} +WWW_DOCUMENT_ROOT=${WWW_DOCUMENT_ROOT:-} + +chown $WWW_GROUP:$WWW_USER $WWW_DOCUMENT_ROOT -R \ No newline at end of file diff --git a/docker/conf/nginx/default.conf b/docker/conf/nginx/default.conf new file mode 100644 index 0000000..b38c061 --- /dev/null +++ b/docker/conf/nginx/default.conf @@ -0,0 +1,176 @@ +server { + + set $root_path '/var/www/httpdocs/public'; + root $root_path; + + # Ipv4 + listen 80; + + # IPv6 + # listen [::]:80; + + # SSL Ipv4 & v6 + listen 443 ssl; + #listen [::]:443 ssl; + + # Your SSL Certificates, don't forget to take a look at Certbot (https://certbot.eff.org) + # ssl_certificate /etc/ssl/fullchain.pem; + # ssl_certificate_key /etc/ssl/privkey.pem; + + # ssl_session_timeout 24h; + # ssl_session_cache shared:SSL:10m; + # ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + # ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:RSA+3DES:AES128-SHA:!ADH:!AECDH:!MD5; + # ssl_prefer_server_ciphers on; + # Do not forget to create this file before with OpenSSL : "openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048" + # ssl_dhparam /etc/nginx/ssl/dhparam.pem; + + index index.php; + + # Redirect 404 errors to app + #error_page 404 /index.php?controller=404; + + # Gzip Settings, convert all types. + gzip on; + gzip_vary on; + gzip_proxied any; + + # Can be enhance to 5, but it can slow you server + # gzip_comp_level 5; + # gzip_min_length 256; + + gzip_types + application/atom+xml + application/javascript + application/json + application/ld+json + application/manifest+json + application/rss+xml + application/vnd.geo+json + application/vnd.ms-fontobject + application/x-font-ttf + application/x-web-app-manifest+json + application/xhtml+xml + application/xml + font/opentype + image/bmp + image/svg+xml + image/x-icon + text/cache-manifest + text/css + text/plain + text/vcard + text/vnd.rim.location.xloc + text/vtt + text/x-component + text/x-cross-domain-policy; + + # + # protect internal assets + # + location ~* /(.*)\.(?:dist|markdown|md|twig|yaml|yml|sass|scss)$ { + deny all; + } + + # block access to certain JSON files (just for additional hardening, most applications don't have these files in the webroot) + location ~ /(?:bower|composer|jsdoc|package)\.json$ { + deny all; + } + # block access to certain js files (just for additional hardening, most applications don't have these files in the webroot) + location ~ /(?:gulpfile)\.js$ { + deny all; + } + + gzip_disable "MSIE [1-6]\.(?!.*SV1)"; + + # HSTS (Force clients to interact with your website using HTTPS only) + # For enhanced security, register your site here: https://hstspreload.org/ + # WARNING: Don't use this if your site is not fully on HTTPS! + # add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" preload; always; + + # Cloudflare / Max CDN + location ~* \.(eot|otf|ttf|woff|woff2)$ { + add_header Access-Control-Allow-Origin *; + } + + # Do not save logs for these + location = /favicon.ico { + log_not_found off; + access_log off; + } + + location = /robots.txt { + auth_basic off; + allow all; + log_not_found off; + access_log off; + } + + location ~ ^/(status|ping)$ { + access_log off; + } + + # File security + # Block access to .htaccess .DS_Store .htpasswd etc + location ~ /\. { + deny all; + } + + # Prevent exposing sensitive files + location ~ \.(yaml|zip|xml|tar|tar.gz|rar)$ { + deny all; + } + + # Prevent injection of php files, block any request attempting to execute a PHP file as + location /media/uploads { + location ~ \.php$ { + deny all; + } + } + + try_files $uri $uri/ @rewrite; + location @rewrite { + rewrite ^/(.*)$ /index.php?_url=/$1; + } + + location ~ \.php { + #try_files $uri =404; + fastcgi_pass php_iceapp:9000; + include fastcgi_params; + fastcgi_split_path_info ^(.+\.php)(/.*)$; + include conf.d/include.fastcgi_common; + #include conf.d/include.fastcgi_app_variables; + + #fastcgi_param APP_ENV "production"; + fastcgi_index index.php; + # Performance enhancement, keep socket between nginx and php-fpm open after the request has been processed + fastcgi_keep_conn on; + fastcgi_read_timeout 30s; + fastcgi_send_timeout 30s; + + # In case of long loading or 502 / 504 errors + # fastcgi_buffer_size 256k; + # fastcgi_buffers 256 16k; + # fastcgi_busy_buffers_size 256k; + client_max_body_size 10M; + } + + + location ~* ^/(css|fonts|img|js|min)/(.+)$ { + root $root_path; + } + + # return 404 for all other php files not matching the front controller + # this prevents access to other php files you don't want to be accessible. + location ~ \.php$ { + return 404; + } + + location ~ /\.ht { + deny all; + } + + error_log /var/log/nginx/iqhire_error.log; + access_log /var/log/nginx/iqhire_access.log; +} + diff --git a/docker/conf/nginx/default.conf.template b/docker/conf/nginx/default.conf.template new file mode 100644 index 0000000..4665748 --- /dev/null +++ b/docker/conf/nginx/default.conf.template @@ -0,0 +1,192 @@ +proxy_cache_path $NGINX_CACHE_PATH levels=1:2 keys_zone=$NGINX_CACHE_NAME:$NGINX_CACHE_METADATA_MEMORY_SIZE max_size=$NGINX_CACHE_MAX_SIZE inactive=$NGINX_CACHE_TTL use_temp_path=$NGINX_CACHE_USE_TEMP_PATH; + +server { + # Short cache: All 200 responses are cached for X seconds. + proxy_cache $NGINX_CACHE_NAME; + proxy_cache_lock on; + proxy_cache_valid 200 302 10m; + proxy_cache_valid 404 5s; + proxy_cache_valid 500 1s; + proxy_cache_min_uses $NGINX_CACHE_MIN_USES; + proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504; + root $NGINX_DOCUMENT_ROOT; + + # Ipv4 + listen $NGINX_HTTP_PORT; + + # IPv6 + # listen [::]:$NGINX_HTTP_PORT; + + # SSL Ipv4 & v6 + listen $NGINX_HTTPS_PORT ssl; + #listen [::]:$NGINX_HTTPS_PORT ssl; + + # Your SSL Certificates, don't forget to take a look at Certbot (https://certbot.eff.org) + # ssl_certificate /etc/ssl/fullchain.pem; + # ssl_certificate_key /etc/ssl/privkey.pem; + + # ssl_session_timeout 24h; + # ssl_session_cache shared:SSL:10m; + # ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + # ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:RSA+3DES:AES128-SHA:!ADH:!AECDH:!MD5; + # ssl_prefer_server_ciphers on; + # Do not forget to create this file before with OpenSSL : "openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048" + # ssl_dhparam /etc/nginx/ssl/dhparam.pem; + + index index.php; + + # Redirect 404 errors to app + #error_page 404 /index.php?controller=404; + + # Gzip Settings, convert all types. + gzip on; + gzip_vary on; + gzip_proxied any; + + # Can be enhance to 5, but it can slow you server + # gzip_comp_level 5; + # gzip_min_length 256; + + gzip_types + application/atom+xml + application/javascript + application/json + application/ld+json + application/manifest+json + application/rss+xml + application/vnd.geo+json + application/vnd.ms-fontobject + application/x-font-ttf + application/x-web-app-manifest+json + application/xhtml+xml + application/xml + font/opentype + image/bmp + image/svg+xml + image/x-icon + text/cache-manifest + text/css + text/plain + text/vcard + text/vnd.rim.location.xloc + text/vtt + text/x-component + text/x-cross-domain-policy; + + # See PHP-FPM documentation about ping.path and ping.response. We can use it to perform healthchecks. + # Obviously we don't want those requests in our access log. + location ~ ^/(status|ping)$ { + log_not_found off; + access_log off; + } + + # + # protect internal assets + # + location ~* /(.*)\.(?:$DENIED_EXTENSIONS)$ { + access_log $ACCESS_SECURITY_LOG; + log_not_found on; + deny all; + } + + # block access to certain JSON files (just for additional hardening, most applications don't have these files in the webroot) + location ~ /(?:bower|composer|jsdoc|package)\.json$ { + access_log $ACCESS_SECURITY_LOG; + log_not_found on; + deny all; + } + # block access to certain js files (just for additional hardening, most applications don't have these files in the webroot) + location ~ /(?:gulpfile)\.js$ { + access_log $ACCESS_SECURITY_LOG; + log_not_found on; + deny all; + } + + gzip_disable "MSIE [1-6]\.(?!.*SV1)"; + + # HSTS (Force clients to interact with your website using HTTPS only) + # For enhanced security, register your site here: https://hstspreload.org/ + # WARNING: Don't use this if your site is not fully on HTTPS! + # add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" preload; always; + + # Cloudflare / Max CDN + location ~* \.(eot|otf|ttf|woff|woff2)$ { + add_header Access-Control-Allow-Origin *; + } + + # Do not save logs for these + location = /favicon.ico { + log_not_found off; + access_log off; + } + + location = /robots.txt { + auth_basic off; + allow all; + log_not_found off; + access_log off; + } + + # File security + # Block access to .htaccess .DS_Store .htpasswd etc + location ~ /\. { + access_log $ACCESS_SECURITY_LOG; + log_not_found on; + deny all; + } + + # Prevent injection of php files, block any request attempting to execute a PHP file as + location /$$UPLOADS_URL_PATH { + location ~ \.php$ { + access_log $ACCESS_SECURITY_LOG; + log_not_found on; + deny all; + } + } + + location ~ \.($STATIC_FILE_EXTENSIONS)$ { + access_log $STATIC_FILE_ACCESS_LOG; + expires 365d; + } + + location / { + try_files $uri $uri/ /index.php$is_args$args; + } + + location ~ \.php { + try_files $uri =404; + fastcgi_pass $PHP_FPM_HOST:$PHP_FPM_PORT; + include fastcgi_params; + fastcgi_split_path_info ^(.+\.php)(/.*)$; + include conf.d/include.fastcgi_common; + #include conf.d/include.fastcgi_app_variables; + + fastcgi_index index.php; + # Performance enhancement, keep socket between nginx and php-fpm open after the request has been processed + fastcgi_keep_conn on; + fastcgi_read_timeout 30s; + fastcgi_send_timeout 30s; + + # In case of long loading or 502 / 504 errors + # fastcgi_buffer_size 256k; + # fastcgi_buffers 256 16k; + # fastcgi_busy_buffers_size 256k; + client_max_body_size 10M; + } + + # return 404 for all other php files not matching the front controller + # this prevents access to other php files you don't want to be accessible. + location ~ \.php$ { + return 404; + } + + location ~ /\.ht { + access_log $ACCESS_SECURITY_LOG; + log_not_found on; + deny all; + } + + error_log $ERROR_LOG; + access_log $ACCESS_LOG; +} + diff --git a/docker/conf/nginx/include.fastcgi_common b/docker/conf/nginx/include.fastcgi_common new file mode 100644 index 0000000..ca8c35f --- /dev/null +++ b/docker/conf/nginx/include.fastcgi_common @@ -0,0 +1,42 @@ +# When you are using symlinks to link the document root to the +# current version of your application, you should pass the real +# application path instead of the path to the symlink to PHP FPM. +# Otherwise, PHP's OPcache may not properly detect changes to +# your PHP files +fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; +fastcgi_param DOCUMENT_ROOT $realpath_root; + +fastcgi_param REQUEST_METHOD $request_method; +fastcgi_param QUERY_STRING $query_string; +fastcgi_param PATH_INFO $fastcgi_path_info; + +fastcgi_param QUERY_STRING $query_string; +fastcgi_param REQUEST_METHOD $request_method; +fastcgi_param CONTENT_TYPE $content_type; +fastcgi_param CONTENT_LENGTH $content_length; + +fastcgi_param SCRIPT_NAME $fastcgi_script_name; +fastcgi_param PATH_INFO $fastcgi_path_info; +fastcgi_param PATH_TRANSLATED $realpath_root$fastcgi_path_info; +fastcgi_param REQUEST_URI $request_uri; +fastcgi_param DOCUMENT_URI $document_uri; +fastcgi_param DOCUMENT_ROOT $realpath_root; +fastcgi_param SERVER_PROTOCOL $server_protocol; + +fastcgi_param GATEWAY_INTERFACE CGI/1.1; +fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; + +fastcgi_param REMOTE_ADDR $remote_addr; +fastcgi_param REMOTE_PORT $remote_port; +fastcgi_param SERVER_ADDR $server_addr; +fastcgi_param SERVER_PORT $server_port; +fastcgi_param SERVER_NAME $server_name; + +fastcgi_param HTTPS $https; + +# PHP only, required if PHP was built with --enable-force-cgi-redirect +fastcgi_param REDIRECT_STATUS 200; + +# httpoxy mitigation +fastcgi_param HTTP_PROXY ""; + diff --git a/docker/conf/nginx/include.whitelist b/docker/conf/nginx/include.whitelist new file mode 100644 index 0000000..c193b8c --- /dev/null +++ b/docker/conf/nginx/include.whitelist @@ -0,0 +1,2 @@ +#allow 127.0.0.1; +#deny all; diff --git a/docker/conf/opcache.ini b/docker/conf/opcache.ini new file mode 100644 index 0000000..ab1a0da --- /dev/null +++ b/docker/conf/opcache.ini @@ -0,0 +1,7 @@ +opcache.enable="1" +opcache.memory_consumption="256" +opcache.use_cwd="0" +opcache.max_file_size="0" +opcache.max_accelerated_files = 30000 +opcache.validate_timestamps="1" +opcache.revalidate_freq="0" \ No newline at end of file diff --git a/docker/conf/www.conf b/docker/conf/www.conf new file mode 100644 index 0000000..7b12051 --- /dev/null +++ b/docker/conf/www.conf @@ -0,0 +1,417 @@ +; Start a new pool named 'www'. +; the variable $pool can be used in any directive and will be replaced by the +; pool name ('www' here) +[www] + +; Per pool prefix +; It only applies on the following directives: + ; - 'access.log' +; - 'slowlog' +; - 'listen' (unixsocket) +; - 'chroot' +; - 'chdir' +; - 'php_values' +; - 'php_admin_values' +; When not set, the global prefix (or NONE) applies instead. +; Note: This directive can also be relative to the global prefix. +; Default Value: none +;prefix = /path/to/pools/$pool + +; Unix user/group of processes +; Note: The user is mandatory. If the group is not set, the default user's group +; will be used. +user = www-data +group = www-data + +; The address on which to accept FastCGI requests. +; Valid syntaxes are: + ; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific IPv4 address on +; a specific port; +; '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6 address on +; a specific port; +; 'port' - to listen on a TCP socket to all addresses +; (IPv6 and IPv4-mapped) on a specific port; +; '/path/to/unix/socket' - to listen on a unix socket. +; Note: This value is mandatory. +listen = 127.0.0.1:9000 + +; Set listen(2) backlog. +; Default Value: 511 (-1 on FreeBSD and OpenBSD) +;listen.backlog = 511 + +; Set permissions for unix socket, if one is used. In Linux, read/write +; permissions must be set in order to allow connections from a web server. Many +; BSD-derived systems allow connections regardless of permissions. +; Default Values: user and group are set as the running user +; mode is set to 0660 +;listen.owner = www-data +;listen.group = www-data +;listen.mode = 0660 +; When POSIX Access Control Lists are supported you can set them using +; these options, value is a comma separated list of user/group names. +; When set, listen.owner and listen.group are ignored +;listen.acl_users = + ;listen.acl_groups = + +; List of addresses (IPv4/IPv6) of FastCGI clients which are allowed to connect. +; Equivalent to the FCGI_WEB_SERVER_ADDRS environment variable in the original +; PHP FCGI (5.2.2+). Makes sense only with a tcp listening socket. Each address +; must be separated by a comma. If this value is left blank, connections will be +; accepted from any ip address. +; Default Value: any +;listen.allowed_clients = 127.0.0.1 + +; Specify the nice(2) priority to apply to the pool processes (only if set) +; The value can vary from -19 (highest priority) to 20 (lower priority) +; Note: - It will only work if the FPM master process is launched as root +; - The pool processes will inherit the master process priority +; unless it specified otherwise +; Default Value: no set +; process.priority = -19 + +; Choose how the process manager will control the number of child processes. +; Possible Values: + ; static - a fixed number (pm.max_children) of child processes; +; dynamic - the number of child processes are set dynamically based on the +; following directives. With this process management, there will be +; always at least 1 children. +; pm.max_children - the maximum number of children that can +; be alive at the same time. +; pm.start_servers - the number of children created on startup. +; pm.min_spare_servers - the minimum number of children in 'idle' +; state (waiting to process). If the number +; of 'idle' processes is less than this +; number then some children will be created. +; pm.max_spare_servers - the maximum number of children in 'idle' +; state (waiting to process). If the number +; of 'idle' processes is greater than this +; number then some children will be killed. +; ondemand - no children are created at startup. Children will be forked when +; new requests will connect. The following parameter are used: + ; pm.max_children - the maximum number of children that +; can be alive at the same time. +; pm.process_idle_timeout - The number of seconds after which +; an idle process will be killed. +; Note: This value is mandatory. +pm = dynamic + +; The number of child processes to be created when pm is set to 'static' and the +; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'. +; This value sets the limit on the number of simultaneous requests that will be +; served. Equivalent to the ApacheMaxClients directive with mpm_prefork. +; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP +; CGI. The below defaults are based on a server without much resources. Don't +; forget to tweak pm.* to fit your needs. +; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand' +; Note: This value is mandatory. +pm.max_children = 5 + +; The number of child processes created on startup. +; Note: Used only when pm is set to 'dynamic' +; Default Value: min_spare_servers + (max_spare_servers - min_spare_servers) / 2 +pm.start_servers = 2 + +; The desired minimum number of idle server processes. +; Note: Used only when pm is set to 'dynamic' +; Note: Mandatory when pm is set to 'dynamic' +pm.min_spare_servers = 1 + +; The desired maximum number of idle server processes. +; Note: Used only when pm is set to 'dynamic' +; Note: Mandatory when pm is set to 'dynamic' +pm.max_spare_servers = 3 + +; The number of seconds after which an idle process will be killed. +; Note: Used only when pm is set to 'ondemand' +; Default Value: 10s +;pm.process_idle_timeout = 10s; + +; The number of requests each child process should execute before respawning. +; This can be useful to work around memory leaks in 3rd party libraries. For +; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS. +; Default Value: 0 +;pm.max_requests = 500 + +; The URI to view the FPM status page. If this value is not set, no URI will be +; recognized as a status page. It shows the following informations: + ; pool - the name of the pool; +; process manager - static, dynamic or ondemand; +; start time - the date and time FPM has started; +; start since - number of seconds since FPM has started; +; accepted conn - the number of request accepted by the pool; +; listen queue - the number of request in the queue of pending +; connections (see backlog in listen(2)); +; max listen queue - the maximum number of requests in the queue +; of pending connections since FPM has started; +; listen queue len - the size of the socket queue of pending connections; +; idle processes - the number of idle processes; +; active processes - the number of active processes; +; total processes - the number of idle + active processes; +; max active processes - the maximum number of active processes since FPM +; has started; +; max children reached - number of times, the process limit has been reached, +; when pm tries to start more children (works only for +; pm 'dynamic' and 'ondemand'); +; Value are updated in real time. +; Example output: + ; pool: www +; process manager: static +; start time: 01/Jul/2011:17:53:49 +0200 +; start since: 62636 +; accepted conn: 190460 +; listen queue: 0 +; max listen queue: 1 +; listen queue len: 42 +; idle processes: 4 +; active processes: 11 +; total processes: 15 +; max active processes: 12 +; max children reached: 0 +; +; By default the status page output is formatted as text/plain. Passing either +; 'html', 'xml' or 'json' in the query string will return the corresponding +; output syntax. Example: + ; http://www.foo.bar/status +; http://www.foo.bar/status?json + ; http://www.foo.bar/status?html +; http://www.foo.bar/status?xml + ; +; By default the status page only outputs short status. Passing 'full' in the +; query string will also return status for each pool process. +; Example: + ; http://www.foo.bar/status?full +; http://www.foo.bar/status?json&full + ; http://www.foo.bar/status?html&full +; http://www.foo.bar/status?xml&full + ; The Full status returns for each process: +; pid - the PID of the process; +; state - the state of the process (Idle, Running, ...); +; start time - the date and time the process has started; +; start since - the number of seconds since the process has started; +; requests - the number of requests the process has served; +; request duration - the duration in µs of the requests; +; request method - the request method (GET, POST, ...); +; request URI - the request URI with the query string; +; content length - the content length of the request (only with POST); +; user - the user (PHP_AUTH_USER) (or '-' if not set); +; script - the main script called (or '-' if not set); +; last request cpu - the %cpu the last request consumed +; it's always 0 if the process is not in Idle state +; because CPU calculation is done when the request +; processing has terminated; +; last request memory - the max amount of memory the last request consumed +; it's always 0 if the process is not in Idle state +; because memory calculation is done when the request +; processing has terminated; +; If the process is in Idle state, then informations are related to the +; last request the process has served. Otherwise informations are related to +; the current request being served. +; Example output: + ; ************************ +; pid: 31330 +; state: Running +; start time: 01/Jul/2011:17:53:49 +0200 +; start since: 63087 +; requests: 12808 +; request duration: 1250261 +; request method: GET +; request URI: /test_mem.php?N=10000 +; content length: 0 +; user: - +; script: /home/fat/web/docs/php/test_mem.php +; last request cpu: 0.00 +; last request memory: 0 +; +; Note: There is a real-time FPM status monitoring sample web page available +; It's available in: /usr/local/share/php/fpm/status.html +; +; Note: The value must start with a leading slash (/). The value can be +; anything, but it may not be a good idea to use the .php extension or it +; may conflict with a real PHP file. +; Default Value: not set +;pm.status_path = /status + +; The ping URI to call the monitoring page of FPM. If this value is not set, no +; URI will be recognized as a ping page. This could be used to test from outside +; that FPM is alive and responding, or to +; - create a graph of FPM availability (rrd or such); +; - remove a server from a group if it is not responding (load balancing); +; - trigger alerts for the operating team (24/7). +; Note: The value must start with a leading slash (/). The value can be +; anything, but it may not be a good idea to use the .php extension or it +; may conflict with a real PHP file. +; Default Value: not set +;ping.path = /ping + +; This directive may be used to customize the response of a ping request. The +; response is formatted as text/plain with a 200 response code. +; Default Value: pong +;ping.response = pong + +; The access log file +; Default: not set +;access.log = log/$pool.access.log + +; The access log format. +; The following syntax is allowed +; %%: the '%' character +; %C: %CPU used by the request +; it can accept the following format: + ; - %{user}C for user CPU only +; - %{system}C for system CPU only +; - %{total}C for user + system CPU (default) +; %d: time taken to serve the request +; it can accept the following format: + ; - %{seconds}d (default) +; - %{miliseconds}d +; - %{mili}d +; - %{microseconds}d +; - %{micro}d +; %e: an environment variable (same as $_ENV or $_SERVER) +; it must be associated with embraces to specify the name of the env +; variable. Some exemples: + ; - server specifics like: %{REQUEST_METHOD}e or %{SERVER_PROTOCOL}e +; - HTTP headers like: %{HTTP_HOST}e or %{HTTP_USER_AGENT}e +; %f: script filename +; %l: content-length of the request (for POST request only) +; %m: request method +; %M: peak of memory allocated by PHP +; it can accept the following format: + ; - %{bytes}M (default) +; - %{kilobytes}M +; - %{kilo}M +; - %{megabytes}M +; - %{mega}M +; %n: pool name +; %o: output header +; it must be associated with embraces to specify the name of the header: + ; - %{Content-Type}o +; - %{X-Powered-By}o +; - %{Transfert-Encoding}o +; - .... +; %p: PID of the child that serviced the request +; %P: PID of the parent of the child that serviced the request +; %q: the query string +; %Q: the '?' character if query string exists +; %r: the request URI (without the query string, see %q and %Q) +; %R: remote IP address +; %s: status (response code) +; %t: server time the request was received +; it can accept a strftime(3) format: + ; %d/%b/%Y:%H:%M:%S %z (default) +; The strftime(3) format must be encapsuled in a %{}t tag +; e.g. for a ISO8601 formatted timestring, use: %{%Y-%m-%dT%H:%M:%S%z}t +; %T: time the log has been written (the request has finished) +; it can accept a strftime(3) format: + ; %d/%b/%Y:%H:%M:%S %z (default) +; The strftime(3) format must be encapsuled in a %{}t tag +; e.g. for a ISO8601 formatted timestring, use: %{%Y-%m-%dT%H:%M:%S%z}t +; %u: remote user +; +; Default: "%R - %u %t \"%m %r\" %s" +;access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%" + +; The log file for slow requests +; Default Value: not set +; Note: slowlog is mandatory if request_slowlog_timeout is set +;slowlog = log/$pool.log.slow + +; The timeout for serving a single request after which a PHP backtrace will be +; dumped to the 'slowlog' file. A value of '0s' means 'off'. +; Available units: s(econds)(default), m(inutes), h(ours), or d(ays) +; Default Value: 0 +;request_slowlog_timeout = 0 + +; Depth of slow log stack trace. +; Default Value: 20 +;request_slowlog_trace_depth = 20 + +; The timeout for serving a single request after which the worker process will +; be killed. This option should be used when the 'max_execution_time' ini option +; does not stop script execution for some reason. A value of '0' means 'off'. +; Available units: s(econds)(default), m(inutes), h(ours), or d(ays) +; Default Value: 0 +;request_terminate_timeout = 0 + +; Set open file descriptor rlimit. +; Default Value: system defined value +;rlimit_files = 1024 + +; Set max core size rlimit. +; Possible Values: 'unlimited' or an integer greater or equal to 0 +; Default Value: system defined value +;rlimit_core = 0 + +; Chroot to this directory at the start. This value must be defined as an +; absolute path. When this value is not set, chroot is not used. +; Note: you can prefix with '$prefix' to chroot to the pool prefix or one +; of its subdirectories. If the pool prefix is not set, the global prefix +; will be used instead. +; Note: chrooting is a great security feature and should be used whenever +; possible. However, all PHP paths will be relative to the chroot +; (error_log, sessions.save_path, ...). +; Default Value: not set +;chroot = + + ; Chdir to this directory at the start. +; Note: relative path can be used. +; Default Value: current directory or / when chroot +;chdir = /var/www + +; Redirect worker stdout and stderr into main error log. If not set, stdout and +; stderr will be redirected to /dev/null according to FastCGI specs. +; Note: on highloaded environement, this can cause some delay in the page +; process time (several ms). +; Default Value: no +;catch_workers_output = yes + +; Clear environment in FPM workers +; Prevents arbitrary environment variables from reaching FPM worker processes +; by clearing the environment in workers before env vars specified in this +; pool configuration are added. +; Setting to "no" will make all environment variables available to PHP code +; via getenv(), $_ENV and $_SERVER. +; Default Value: yes +;clear_env = no + +; Limits the extensions of the main script FPM will allow to parse. This can +; prevent configuration mistakes on the web server side. You should only limit +; FPM to .php extensions to prevent malicious users to use other extensions to +; execute php code. +; Note: set an empty value to allow all extensions. +; Default Value: .php +;security.limit_extensions = .php .php3 .php4 .php5 .php7 + +; Pass environment variables like LD_LIBRARY_PATH. All $VARIABLEs are taken from +; the current environment. +; Default Value: clean env +;env[HOSTNAME] = $HOSTNAME +;env[PATH] = /usr/local/bin:/usr/bin:/bin +;env[TMP] = /tmp +;env[TMPDIR] = /tmp +;env[TEMP] = /tmp + +; Additional php.ini defines, specific to this pool of workers. These settings +; overwrite the values previously defined in the php.ini. The directives are the +; same as the PHP SAPI: + ; php_value/php_flag - you can set classic ini defines which can +; be overwritten from PHP call 'ini_set'. +; php_admin_value/php_admin_flag - these directives won't be overwritten by +; PHP call 'ini_set' +; For php_*flag, valid values are on, off, 1, 0, true, false, yes or no. + +; Defining 'extension' will load the corresponding shared extension from +; extension_dir. Defining 'disable_functions' or 'disable_classes' will not +; overwrite previously defined php.ini values, but will append the new value +; instead. + +; Note: path INI options can be relative and will be expanded with the prefix +; (pool, global or /usr/local) + +; Default Value: nothing is defined by default except the values in php.ini and +; specified at startup with the -d argument +;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f www@my.domain.com +;php_flag[display_errors] = off +;php_admin_value[error_log] = /var/log/fpm-php.www.log +;php_admin_flag[log_errors] = on +;php_admin_value[memory_limit] = 32M diff --git a/docker/conf/xdebug.ini b/docker/conf/xdebug.ini new file mode 100644 index 0000000..5d63540 --- /dev/null +++ b/docker/conf/xdebug.ini @@ -0,0 +1,17 @@ +;xdebug.remote_host=your-ip +xdebug.remote_connect_back=1 +xdebug.remote_port=9000 +xdebug.idekey=ICEAPP + +xdebug.remote_autostart=0 +xdebug.remote_enable=0 +xdebug.cli_color=0 +xdebug.profiler_enable=0 +xdebug.profiler_output_dir="~/xdebug/phpstorm/tmp/profiling" + +xdebug.remote_handler=dbgp +xdebug.remote_mode=req + +xdebug.var_display_max_children=-1 +xdebug.var_display_max_data=-1 +xdebug.var_display_max_depth=-1 \ No newline at end of file diff --git a/docker/entrypoint_runall.sh b/docker/entrypoint_runall.sh new file mode 100644 index 0000000..f011d0b --- /dev/null +++ b/docker/entrypoint_runall.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +cd /opt/docker/provision/entrypoint.d/ +for f in *.sh; do + bash "$f" -H || break # if needed +done + +exec "$@" \ No newline at end of file diff --git a/phpcs.xml b/phpcs.xml index 2d50f96..0de89f3 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -14,7 +14,7 @@ - App/i18n/* + translations/* vendor/* *.min.js *.min.css diff --git a/public/index.php b/public/index.php index 70e0dd3..a328924 100644 --- a/public/index.php +++ b/public/index.php @@ -15,8 +15,23 @@ use App\Boot\Base; use Ice\Di; +use Symfony\Component\Dotenv\Dotenv; + +$dotenv = new Dotenv(); +if (file_exists(__DIR__.'/../.env')) { + // The load method will never overwrite environment variables. Usually this is what you want. + // When you load the .env file file you generally only want to load the variables that are not yet defined. + // For example, when using Nginx you may set the APP_ENV variable using a fastcgi parameter. Another common use case + // is when using a containerization technology such as docker. You would define environment variables in a + // docker-compose.yml file (- or a secrets file in cloud mode. If you do want to overwrite existing variables than + // you must use the overload() method instead of the load method. + $dotenv->load(__DIR__.'/../.env'); + + // You can also load several files + //$dotenv->load(__DIR__.'/.env', __DIR__.'/.env.dev'); +} // Initialize website, handle a MVC request and display the HTTP response body -echo (new Base(new Di)) - ->initialize() - ->handle(); +$app = (new Base(new Di))->initialize(); + +echo $app->handle($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']); \ No newline at end of file diff --git a/src/App/Boot/Base.php b/src/App/Boot/Base.php new file mode 100644 index 0000000..731effe --- /dev/null +++ b/src/App/Boot/Base.php @@ -0,0 +1,353 @@ + + * @license iceframework.org Ice + * @link iceframework.org + */ +class Base extends App +{ + /** + * Meta description. + * + * @var string + */ + public $description; + + /** + * Meta keywords. + * + * @var string + */ + public $keywords; + + /** + * Initialize the application. + * + * @return object Base + */ + public function initialize() + { + $di = $this->configureContainer(); + + // Load the config + if (!defined('APP_ENV')) { + define('APP_ENV', $di->config->app->env); + } + + // Register modules + $this->setModules($this->loadModules()); + + // Set dump + if (APP_ENV == "development") { + $this->dump->setDetailed(true); + } + + // Configure services + $di->crypt->setKey($di->config->crypt->key); + $di->cookies->setSalt($di->config->cookie->salt); + $di->i18n = new I18n($di->config->i18n->toArray()); + $di->auth = new Auth($di->config->auth->toArray()); + $di->url->setBaseUri($this->getBaseUri()); + $di->url->setStaticUri($this->getStaticUri()); + + // Set the assets service + /*$di->assets->setOptions([ + 'source' => __ROOT__ . '/public/', + 'target' => 'min/', + 'minify' => getenv('MINIFY_ASSETS_MODE') + ]);*/ + + // Set the dispatcher service + $di->dispatcher->setSilent(!getenv('DISPATCHER_DEBUG')); + + // Set the router service + $di->set('router', function () use($di) { + $router = new Router(); + $defaultModule = $di->config->modules->application->default; + if (!$defaultModule) { + $defaultModule = 'front'; + } + $router->setDefaultModule($defaultModule); + $router->setSilent(!getenv('ROUTER_DEBUG')); + $router->setRoutes(Routes::universal()); + + return $router; + }); + + // Set the db service + $di->set('db', function() use($di){ + if (!$di->has('config')) { + throw new \LogicException("The 'config' service is not registered."); + } + + $config = $di->get('config'); + if (!isset($config->database)) { + throw new \LogicException("The 'database' configuration is missing."); + } + + // To use another database you may need to add your own environment variables + $db = new Db( + 'mongodb', + (string) getenv('MONGODB_HOST'), + (int) getenv('MONGODB_PORT'), + (string) getenv('MONGODB_DATABASE'), + (string) getenv('MONGODB_USERNAME'), + (string) getenv('MONGODB_PASSWORD'), + [ + 'authMechanism' => getenv('MONGODB_AUTH_MECHANISM') + ] + ); + + if (APP_ENV == "development") { + $dbClient = $db->getDriver()->getClient(); + if ($dbClient instanceof \PDO) { + $dbClient->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + } + } + + return $db; + }); + + // Set the view service + $di->set('view', function() use($di) { + $view = new \Ice\Mvc\View(); + $view->setViewsDir(__ROOT__ . '/templates/'); + $view->setLayoutsDir(__ROOT__ . '/templates/layouts/'); + $view->setPartialsDir(__ROOT__ . '/templates/partials/'); + + $templateCacheDir = __ROOT__ . '/var/cache/' . APP_ENV . '/sleet/'; + if (!is_dir($templateCacheDir)) { + mkdir($templateCacheDir, 0755, true); + } + + // Options for Sleet template engine + $sleet = new ViewEngine\Sleet($view, $di); + $sleet->setOptions([ + 'compileDir' => $templateCacheDir, + 'trimPath' => __ROOT__ . '/src', + 'compile' => (int) getenv('SLEET_COMPILER_MODE') ?: 0 + ]); + + // Set template engines + $view->setEngines([ + '.sleet' => $sleet, + '.md' => Markdown::class, + '.phtml' => ViewEngine\Php::class + ]); + + return $view; + }); + + // Register hooks + $this->registerHooks(); + + return $this; + } + + /** + * @param Response $response + */ + public function postHandleHandler($response) + { + // Display pretty view for some response codes + if (!$response->isInformational() && !$response->isSuccessful() && !$response->isRedirect()) { + $code = $response->getStatus(); + + // Add meta tags + $this->di->tag + ->setDocType(Tag::XHTML5) + ->setTitle(_t('status :code', [':code' => $response->getStatus()])) + ->appendTitle($this->di->config->app->name) + ->setMeta([]) + ->addMeta(['charset' => 'utf-8']) + ->addMeta(['noindex, nofollow', 'robots']) + ->addMeta(['initial-scale=1, minimum-scale=1, width=device-width', 'viewport']); + + // Add styles to assets + $this->di->assets + ->setCollections([]) + ->add('css/response.css'); + + // Restore default view settings + $this->di->view + ->setViewsDir(__ROOT__ . '/templates/') + ->setPartialsDir('partials/') + ->setLayoutsDir('layouts/') + ->setFile('partials/status') + ->setContent($this->di->view->render()); + + $response->setBody($this->di->view->layout('minimal')); + } + } + + /** + * @param \Exception $e + * @param Di $di + */ + public function uncaughtExceptionHandler($e, $di) + { + $error = get_class($e) . '[' . $e->getCode() . ']: ' . $e->getMessage(); + $info = $e->getFile() . '[' . $e->getLine() . ']'; + + if (getenv('LOGGER_ENABLED')) { + // Log error into the file + $logDir = __ROOT__ . '/var/log/' . APP_ENV . '/'; + if (!is_dir($logDir)) { + mkdir($logDir, 0775, true); + } + $logger = new Logger($logDir . date('Ymd') . '.log'); + $logger->error($error); + $logger->info($info); + if (getenv('DEBUG_MODE')) { + $logger->debug("Trace: \n" . $e->getTraceAsString() . "\n"); + } + } + + if (getenv('MAILER_ENABLED') and getenv('ERROR_NOTIFICATIONS_ENABLED')) { + // Send email to admin + $log = $di->dump->vars($error, $info, "Trace: \n" . $e->getTraceAsString() . "\n"); + + if ($di->has("request")) { + $log .= $di->dump->one($di->request->getData(), '_REQUEST'); + $log .= $di->dump->one($di->request->getServer()->getData(), '_SERVER'); + $log .= $di->dump->one($di->request->getPost()->getData(), '_POST'); + $log .= $di->dump->one($di->request->getQuery()->getData(), '_GET'); + } + + $email = new Email(); + $email->prepare(_t('somethingIsWrong'), $di->config->app->admin, 'email/error', ['log' => $log]); + + if ($email->send() !== true) { + $logger = new Logger(__ROOT__ . '/var/log/' . date('Ymd') . '.log'); + $logger->error($e->getMessage()); + } + } + + $response = $di->get('response'); + $response->setStatus(500); + + if (PHP_SAPI == 'cli') { + $response->setBody($e->getMessage()); + } elseif (!getenv('DEBUG_MODE')) { + $di->applyHook("app.after.handle", [$response]); + } else { + // Add meta tags + $di->tag + ->setDocType(Tag::XHTML5) + ->setTitle(_t('status :code', [':code' => $e->getCode()])) + ->appendTitle($di->config->app->name) + ->setMeta([]) + ->addMeta(['charset' => 'utf-8']) + ->addMeta(['IE=edge', 'http-equiv' => 'X-UA-Compatible']) + ->addMeta(['width=device-width, initial-scale=1.0', 'viewport']) + ->addMeta(['noindex, nofollow', 'robots']); + + // Add styles to assets + $di->assets + ->setCollections([]) + ->add('css/exception.css') + ->add('css/highlight/tomorrow.min.css', $this->config->assets->highlight) + ->add('js/jquery.min.js', $this->config->assets->jquery) + ->add('js/plugins/highlight.min.js', $this->config->assets->highlight) + ->add('js/exception.js'); + + // Restore default view settings + $di->view + ->setViewsDir(__ROOT__ . '/templates/') + ->setPartialsDir('partials/') + ->setLayoutsDir('layouts/') + ->setFile('partials/exception') + ->setVar('e', $e) + ->setContent($di->view->render()); + + $response->setBody($di->view->layout('minimal')); + } + } + + /** + * Register hooks in the di. + * + * @return void + */ + public function registerHooks() + { + // Response code + $this->di->hook('app.after.handle', [$this, 'postHandleHandler']); + + // Pretty exception + $this->di->hook('exception.after.uncaught', [$this, 'uncaughtExceptionHandler']); + } + + /** + * @return string + */ + private function getBaseUri() + { + $baseUri = $this->di->config->app->base_uri; + if (empty($baseUri)) { + throw new \RuntimeException("The base uri is not set."); + } + + return $baseUri; + } + + /** + * @return string + */ + private function getStaticUri() + { + $staticUrl = $this->di->config->app->static_uri; + if (empty($staticUrl)) { + throw new \RuntimeException("The static uri is not set."); + } + + return $staticUrl; + } + + /** + * @return string[] + */ + private function loadModules() + { + $modules = $this->di->config->{$this->di->config->modules->application->modules}->toArray(); + if (empty($modules)) { + throw new \RuntimeException("There are no registered modules."); + } + + return $modules; + } + + private function configureContainer() + { + // Handle the errors by Error class + $this->di->errors(); + $this->di->config = new Ini(__ROOT__ . '/config/config.ini'); + $this->di->config->set('assets', new Ini(__ROOT__ . '/config/assets.ini')); + + return $this->di; + } +} diff --git a/App/Boot/Console.php b/src/App/Boot/Console.php similarity index 79% rename from App/Boot/Console.php rename to src/App/Boot/Console.php index 4b15c36..8ac45d5 100644 --- a/App/Boot/Console.php +++ b/src/App/Boot/Console.php @@ -7,7 +7,7 @@ use Ice\Config\Ini; use Ice\Dump; use Ice\Log\Driver\File as Logger; -use App\Lib\Email; +use App\Libraries\Email; /** * Base console application. @@ -32,10 +32,9 @@ public function initialize() $this->di->errors(); // Load the config - $config = new Ini(__ROOT__ . '/App/cfg/config.ini'); + $config = new Ini(__ROOT__ . '/config/config.ini'); // Set environment settings - $config->set('env', (new Ini(__ROOT__ . '/App/cfg/env.ini'))->{$config->app->env}); $this->di->config = $config; // Register modules @@ -45,7 +44,7 @@ public function initialize() // Set dump $this->di->dump->setPlain(true); - if ($this->di->env->environment == "development") { + if (APP_ENV == "development") { $this->dump->setDetailed(true); } @@ -79,15 +78,15 @@ public function registerHooks() $info = $e->getFile() . '[' . $e->getLine() . ']'; $debug = "Trace: \n" . $e->getTraceAsString() . "\n"; - if ($di->env->error->log) { + if (getenv('LOGGER_ENABLED')) { // Log error into the file - $logger = new Logger(__ROOT__ . '/App/log/' . date('Ymd') . '.log'); + $logger = new Logger(__ROOT__ . '/var/log/' . date('Ymd') . '.log'); $logger->error($error); $logger->info($info); $logger->debug($debug); } - if ($di->env->error->email) { + if (getenv('MAILER_ENABLED') and getenv('ERROR_NOTIFICATIONS_ENABLED')) { // Send email to admin $log = $di->dump->vars($error, $info, $debug); @@ -101,20 +100,16 @@ public function registerHooks() $email = new Email(); $email->prepare(_t('somethingIsWrong'), $di->config->app->admin, 'email/error', ['log' => $log]); - if ($email->Send() !== true) { - $logger = new Logger(__ROOT__ . '/App/log/' . date('Ymd') . '.log'); - $logger->error($email->ErrorInfo); + if ($email->send() !== true) { + $logger = new Logger(__ROOT__ . '/var/log/' . date('Ymd') . '.log'); + $logger->error($message); } } - if ($di->env->error->debug) { + if (getenv('DEBUG_MODE')) { echo $di->dump->vars($error, $info, $debug); } else { - if ($di->env->error->hide) { - $message = _t('somethingIsWrong'); - } - - echo $message; + echo _t('somethingIsWrong'); } }); } diff --git a/src/App/Boot/Env.php b/src/App/Boot/Env.php new file mode 100644 index 0000000..46bcc67 --- /dev/null +++ b/src/App/Boot/Env.php @@ -0,0 +1,14 @@ +jsTranslations = new Arr([]); + } + + /** * Before execute action. * diff --git a/App/Extensions/Doc.php b/src/App/Extensions/Doc.php similarity index 100% rename from App/Extensions/Doc.php rename to src/App/Extensions/Doc.php diff --git a/App/Extensions/Front.php b/src/App/Extensions/Front.php similarity index 100% rename from App/Extensions/Front.php rename to src/App/Extensions/Front.php diff --git a/App/Extensions/Task.php b/src/App/Extensions/Task.php similarity index 100% rename from App/Extensions/Task.php rename to src/App/Extensions/Task.php diff --git a/App/Libraries/Email.php b/src/App/Libraries/Email.php similarity index 79% rename from App/Libraries/Email.php rename to src/App/Libraries/Email.php index 2d69711..c539f09 100644 --- a/App/Libraries/Email.php +++ b/src/App/Libraries/Email.php @@ -26,6 +26,7 @@ class Email extends PHPMailer private $di; private $premailer; + private $error = ''; /** * Email constructor @@ -65,13 +66,13 @@ public function getTemplate($name, $params = []) { // Prepare view service $view = new View(); - $view->setViewsDir(__ROOT__ . '/App/views/'); + $view->setViewsDir(__ROOT__ . '/templates/'); $view->setMainView('email'); // Options for Sleet template engine $sleet = new Sleet($view, $this->di); $sleet->setOptions([ - 'compileDir' => __ROOT__ . '/App/tmp/sleet/', + 'compileDir' => __ROOT__ . '/Appvar/cache/sleet/', 'trimPath' => __ROOT__, 'compile' => Compiler::IF_CHANGE ]); @@ -115,7 +116,7 @@ public function getInline($source) */ public function prepare($subject, $to, $view, $params = []) { - if ($this->di->config->app->env == 'development') { + if (APP_ENV == 'development') { $to = $this->di->config->app->admin; } $this->Subject = $subject; @@ -135,26 +136,31 @@ public function prepare($subject, $to, $view, $params = []) */ public function send() { - if ($this->di->config->env->email) { - return parent::send(); - } else { - $this->preSend(); - // Log email into the file - $logger = new Logger(__ROOT__ . '/App/log/' . date('Ymd') . '.log'); - $logger->info('Subject: ' . $this->Subject . '; To: ' . json_encode($this->to)); - $logger->error($this->ErrorInfo); - $logger->debug($this->Body); - return true; + try { + if (getenv('MAILER_ENABLED')) { + return parent::send(); + } + } catch(\Exception $e) { + $error = "Error occurred: {$e->getMessage()} on line {$e->getLine()} in file {$e->getFile()}"; } + + $this->preSend(); + // Log email into the file + $logger = new Logger(__ROOT__ . '/var/log/' . date('Ymd') . '.log'); + $logger->info('Subject: ' . $this->Subject . '; To: ' . json_encode($this->to)); + $logger->error($this->getError()); + $logger->debug($this->Body); + + return true; } - /** - * Get error info - * - * @return mixed - */ public function getError() { - return $this->ErrorInfo; + return $this->error; + } + + public function setError($error) + { + $this->error = $error; } } diff --git a/App/Libraries/Markdown.php b/src/App/Libraries/Markdown.php similarity index 94% rename from App/Libraries/Markdown.php rename to src/App/Libraries/Markdown.php index 74c636c..2f3c180 100644 --- a/App/Libraries/Markdown.php +++ b/src/App/Libraries/Markdown.php @@ -44,7 +44,7 @@ public function __construct(ViewInterface $view, Di $di = null) * * @return string */ - public function render($path, array $data = null) + public function render(string $path, ?array $data = null) { $content = $this->parser->text(file_get_contents($path)); $this->view->setContent($content); diff --git a/App/Libraries/Twig.php b/src/App/Libraries/Twig.php similarity index 100% rename from App/Libraries/Twig.php rename to src/App/Libraries/Twig.php diff --git a/App/Models/Users.php b/src/App/Models/Users.php similarity index 100% rename from App/Models/Users.php rename to src/App/Models/Users.php diff --git a/App/Modules/Admin/Controllers/IndexController.php b/src/App/Modules/Admin/Controllers/IndexController.php similarity index 100% rename from App/Modules/Admin/Controllers/IndexController.php rename to src/App/Modules/Admin/Controllers/IndexController.php diff --git a/App/Modules/Admin/Module.php b/src/App/Modules/Admin/Module.php similarity index 87% rename from App/Modules/Admin/Module.php rename to src/App/Modules/Admin/Module.php index 4fdfef7..a6d058e 100644 --- a/App/Modules/Admin/Module.php +++ b/src/App/Modules/Admin/Module.php @@ -44,8 +44,8 @@ public function registerServices(Di $di) // Overwrite views dirs $di->view->setViewsDir(__DIR__ . '/views/'); - $di->view->setPartialsDir('../../../views/partials/'); - $di->view->setLayoutsDir('../../../views/layouts/'); + $di->view->setPartialsDir(__ROOT__ . '/templates/partials/admin/'); + $di->view->setLayoutsDir(__ROOT__ . '/templates/layouts/'); $di->view->setLayout('bootstrap'); } } diff --git a/App/Modules/Admin/views/index/index.sleet b/src/App/Modules/Admin/views/index/index.sleet similarity index 100% rename from App/Modules/Admin/views/index/index.sleet rename to src/App/Modules/Admin/views/index/index.sleet diff --git a/App/Modules/Doc/Controllers/IndexController.php b/src/App/Modules/Doc/Controllers/IndexController.php similarity index 100% rename from App/Modules/Doc/Controllers/IndexController.php rename to src/App/Modules/Doc/Controllers/IndexController.php diff --git a/App/Modules/Doc/Module.php b/src/App/Modules/Doc/Module.php similarity index 82% rename from App/Modules/Doc/Module.php rename to src/App/Modules/Doc/Module.php index 877d432..696c20c 100644 --- a/App/Modules/Doc/Module.php +++ b/src/App/Modules/Doc/Module.php @@ -55,14 +55,17 @@ public function registerServices(Di $di) $lang = $di->i18n->lang(); } + $language = $di->i18n->iso($lang); + // Overwrite views dirs $di->view->setViewsDir([ - __DIR__ . '/views/' . $di->i18n->iso($lang) . '/', - __DIR__ . '/views/en/' + __DIR__ . '/views/' . $language . '/', + __DIR__ . '/views/en/', + __DIR__ . '/views/' ]); - $di->view->setPartialsDir('../../../../views/partials/'); - $di->view->setLayoutsDir('../../../../views/layouts/'); + $di->view->setPartialsDir('../../../../../templates/partials/'); + $di->view->setLayoutsDir('../../../../../templates/layouts/'); $di->view->setLayout('bootstrap'); } } diff --git a/App/Modules/Doc/views/en/index/index.md b/src/App/Modules/Doc/views/en/index/index.md similarity index 96% rename from App/Modules/Doc/views/en/index/index.md rename to src/App/Modules/Doc/views/en/index/index.md index 5377787..f2353fb 100644 --- a/App/Modules/Doc/views/en/index/index.md +++ b/src/App/Modules/Doc/views/en/index/index.md @@ -2,7 +2,7 @@ *** ### Configuration: -Set *base_uri* and other settings in the `/app/cfg/config.ini` file: +Set *base_uri* and other settings in the `/config/config.ini` file: ```ini [app] domain = "example.com" diff --git a/App/Modules/Doc/views/ja/index/index.md b/src/App/Modules/Doc/views/ja/index/index.md similarity index 95% rename from App/Modules/Doc/views/ja/index/index.md rename to src/App/Modules/Doc/views/ja/index/index.md index 05a7af6..75d6a01 100644 --- a/App/Modules/Doc/views/ja/index/index.md +++ b/src/App/Modules/Doc/views/ja/index/index.md @@ -2,7 +2,7 @@ *** ### 設定: -`/app/cfg/config.ini` ファイルの *base_uri* とその他の設定をおこなってください: +`/config/config.ini` ファイルの *base_uri* とその他の設定をおこなってください: ```ini [app] domain = "example.com" diff --git a/src/App/Modules/Doc/views/nl/index/index.md b/src/App/Modules/Doc/views/nl/index/index.md new file mode 100644 index 0000000..f2353fb --- /dev/null +++ b/src/App/Modules/Doc/views/nl/index/index.md @@ -0,0 +1,94 @@ +# Documentation +*** + +### Configuration: +Set *base_uri* and other settings in the `/config/config.ini` file: +```ini +[app] +domain = "example.com" +base_uri = "/" +static_uri = "http://www.example.com/" +admin = "admin@example.com" +``` +
+Enter the settings to connect to the database: +```ini +[database] +type = "mongodb" +host = "localhost" +port = 27017 +user = "demo" +password = "demo" +name = "demo_base" +options[authMechanism] = "MONGODB-CR" +``` +
+Change default hash keys. It is **very important** for safety reasons: +```ini +[auth] +hash_key = "secret_key" + +[crypt] +key = "secret_key" + +[cookie] +salt = "secret_key" +``` +
+Use `auth.sql` file to create required tables or run for _mongodb_: +```bash +cd /path/to/base/private +php index.php --module=shell --handler=prepare --action=roles +``` + +Set the chmods: +```bash +php index.php --module=shell --handler=prepare --action=chmod +``` + +To recompile views or minify assets manually (eg. for _production_ `env`): +``` +php index.php --module=shell --handler=prepare --action=sleet +php index.php --module=shell --handler=prepare --action=assets +``` +*** + +### Requirements: +* Ice framework + +*** +### Example sleet usage: +Access to `auth` service in the views: +```twig +{% if this.auth.loggedIn() %} + {{ this.auth.getUser().username }} +{% endif %} +``` +
+Easy translation with `_t()` function: +```twig +{% set username = this.auth.getUser().username %} +{{ _t('Hello :user', [':user' : username]) }} +{{ _t('Hello %s', [username]) }} +``` +
+Mixed usage: +```twig +{% if this.auth.loggedIn('admin') %} + {{ _t('Hello :user', [':user' : this.auth.getUser().username]) }} + {{ link_to(['admin', _t('Admin panel')]) }} +{% endif %} +``` +
+Use class in the view: +```twig +{% use App\Models\Users %} + +{% set user = Users::findOne(1) %} +{{ user.username }} +``` +
+Debug variables: +```php +{{ dump('string', 1, 2.5, true, null, ['key': 'value']) }} +``` \ No newline at end of file diff --git a/App/Modules/Doc/views/pl/index/index.md b/src/App/Modules/Doc/views/pl/index/index.md similarity index 96% rename from App/Modules/Doc/views/pl/index/index.md rename to src/App/Modules/Doc/views/pl/index/index.md index ffc5b16..99bdf36 100644 --- a/App/Modules/Doc/views/pl/index/index.md +++ b/src/App/Modules/Doc/views/pl/index/index.md @@ -2,7 +2,7 @@ *** ### Konfiguracja: -Ustaw *base_uri* i inne opcje w pliku `/app/cfg/config.ini`: +Ustaw *base_uri* i inne opcje w pliku `/config/config.ini`: ```ini [app] diff --git a/App/Modules/Front/Controllers/IndexController.php b/src/App/Modules/Front/Controllers/IndexController.php similarity index 92% rename from App/Modules/Front/Controllers/IndexController.php rename to src/App/Modules/Front/Controllers/IndexController.php index f524e4a..4203100 100644 --- a/App/Modules/Front/Controllers/IndexController.php +++ b/src/App/Modules/Front/Controllers/IndexController.php @@ -33,7 +33,7 @@ public function indexAction() * Contact form. * * @return void - * @throws Error + * @throws \Error */ public function contactAction() { @@ -66,11 +66,9 @@ public function contactAction() $email->addReplyTo($this->request->getPost('email')); // Try to send email - if ($email->Send() === true) { + if ($email->send() === true) { $this->flash->success(_t('flash/success/contact')); unset($_POST); - } else { - throw new Error($email->ErrorInfo); } } } @@ -91,7 +89,7 @@ public function langAction() // Go to the last place $referer = $this->request->getServer("HTTP_REFERER"); if (strpos($referer, $this->request->getServer("HTTP_HOST") . "/") !== false) { - return $this->response->setHeader("Location", $referer); + $this->response->setHeader("Location", $referer); } else { $this->response->redirect(); } diff --git a/App/Modules/Front/Controllers/UserController.php b/src/App/Modules/Front/Controllers/UserController.php similarity index 100% rename from App/Modules/Front/Controllers/UserController.php rename to src/App/Modules/Front/Controllers/UserController.php diff --git a/App/Modules/Front/Module.php b/src/App/Modules/Front/Module.php similarity index 77% rename from App/Modules/Front/Module.php rename to src/App/Modules/Front/Module.php index 57f10cf..4686a91 100644 --- a/App/Modules/Front/Module.php +++ b/src/App/Modules/Front/Module.php @@ -27,9 +27,9 @@ class Module implements ModuleInterface */ public function registerAutoloaders(Loader $loader = null) { - $loader - ->addNamespace(__NAMESPACE__ . '\Controllers', __DIR__ . '/controllers/') - ->register(); + //$loader + // ->addNamespace(__NAMESPACE__ . '\Controllers', __DIR__ . '/controllers/') + // ->register(); } /** @@ -46,8 +46,8 @@ public function registerServices(Di $di) // Overwrite views dirs $di->view->setViewsDir(__DIR__ . '/views/'); - $di->view->setPartialsDir('../../../views/partials/'); - $di->view->setLayoutsDir('../../../views/layouts/'); + $di->view->setPartialsDir('../../../../../templates/partials/'); + $di->view->setLayoutsDir('../../../../../templates/layouts/'); $di->view->setLayout('bootstrap'); } } diff --git a/App/Modules/Front/views/index/contact.sleet b/src/App/Modules/Front/views/index/contact.sleet similarity index 100% rename from App/Modules/Front/views/index/contact.sleet rename to src/App/Modules/Front/views/index/contact.sleet diff --git a/App/Modules/Front/views/index/index.sleet b/src/App/Modules/Front/views/index/index.sleet similarity index 100% rename from App/Modules/Front/views/index/index.sleet rename to src/App/Modules/Front/views/index/index.sleet diff --git a/App/Modules/Front/views/user/index.sleet b/src/App/Modules/Front/views/user/index.sleet similarity index 100% rename from App/Modules/Front/views/user/index.sleet rename to src/App/Modules/Front/views/user/index.sleet diff --git a/App/Modules/Front/views/user/signin.sleet b/src/App/Modules/Front/views/user/signin.sleet similarity index 100% rename from App/Modules/Front/views/user/signin.sleet rename to src/App/Modules/Front/views/user/signin.sleet diff --git a/App/Modules/Front/views/user/signup.sleet b/src/App/Modules/Front/views/user/signup.sleet similarity index 100% rename from App/Modules/Front/views/user/signup.sleet rename to src/App/Modules/Front/views/user/signup.sleet diff --git a/App/Modules/Front/views/user/signupby.sleet b/src/App/Modules/Front/views/user/signupby.sleet similarity index 100% rename from App/Modules/Front/views/user/signupby.sleet rename to src/App/Modules/Front/views/user/signupby.sleet diff --git a/App/Modules/Shell/Module.php b/src/App/Modules/Shell/Module.php similarity index 100% rename from App/Modules/Shell/Module.php rename to src/App/Modules/Shell/Module.php diff --git a/App/Modules/Shell/Tasks/MainTask.php b/src/App/Modules/Shell/Tasks/MainTask.php similarity index 94% rename from App/Modules/Shell/Tasks/MainTask.php rename to src/App/Modules/Shell/Tasks/MainTask.php index 713e8a1..436773e 100644 --- a/App/Modules/Shell/Tasks/MainTask.php +++ b/src/App/Modules/Shell/Tasks/MainTask.php @@ -60,12 +60,12 @@ public function before() // Set the view service $this->di->set('view', function () { $view = new View(); - $view->setViewsDir(__ROOT__ . '/App/views/'); + $view->setViewsDir(__ROOT__ . '/templates/'); // Options for Sleet template engine $sleet = new Sleet($view, $this->di); $sleet->setOptions([ - 'compileDir' => __ROOT__ . '/App/tmp/sleet/', + 'compileDir' => __ROOT__ . '/Appvar/cache/sleet/', 'trimPath' => __ROOT__, 'compile' => Compiler::IF_CHANGE ]); diff --git a/App/Modules/Shell/Tasks/PrepareTask.php b/src/App/Modules/Shell/Tasks/PrepareTask.php similarity index 92% rename from App/Modules/Shell/Tasks/PrepareTask.php rename to src/App/Modules/Shell/Tasks/PrepareTask.php index ff0e19f..e13fdce 100644 --- a/App/Modules/Shell/Tasks/PrepareTask.php +++ b/src/App/Modules/Shell/Tasks/PrepareTask.php @@ -27,8 +27,8 @@ class PrepareTask extends MainTask public function chmodAction() { $dirs = [ - '/App/tmp', - '/App/log', + '/Appvar/cache', + '/var/log', '/public/min', '/public/upload', ]; @@ -58,19 +58,19 @@ public function chmodAction() */ public function rmAction() { - if ($this->config->app->env == 'development' || $this->config->app->env == 'testing') { + if (APP_ENV == 'development' || APP_ENV == 'testing') { $params = $this->dispatcher->getParams(); if (isset($params["dirs"])) { $dirs = explode('|', $params["dirs"]); } else { $dirs = [ - '/App/tmp/', - '/App/log/', + '/Appvar/cache/', + '/var/log/', '/public/min/', ]; - if ($this->config->app->env == 'development' + if (APP_ENV == 'development' && isset($params["upload"]) && $params["upload"] == 'yes' ) { $dirs[] = '/public/upload/'; @@ -118,7 +118,7 @@ public function sleetAction() $sleet = new Sleet($this->view, $this->di); $sleet->setOptions([ - 'compileDir' => __ROOT__ . '/App/tmp/sleet/', + 'compileDir' => __ROOT__ . '/Appvar/cache/sleet/', 'trimPath' => __ROOT__, 'compile' => Compiler::ALWAYS ]); @@ -127,7 +127,7 @@ public function sleetAction() '/App/Modules/Admin/views/', '/App/Modules/Doc/views/', '/App/Modules/Front/views/', - '/App/views/', + '/templates/', ]; foreach ($dirs as $dir) { foreach ($iterator = new \RecursiveIteratorIterator( @@ -206,25 +206,9 @@ public function langAction() { $lang = $this->dispatcher->getParam(0, null, 'en', true); - $dir = '/app/'; $scan = []; - - foreach ($iterator = new \RecursiveIteratorIterator( - new \RecursiveDirectoryIterator(__ROOT__ . $dir, \RecursiveDirectoryIterator::SKIP_DOTS), - \RecursiveIteratorIterator::SELF_FIRST - ) as $item) { - if (!$item->isDir() && in_array($item->getExtension(), ['php', 'sleet'])) { - $content = file_get_contents(__ROOT__ . $dir . $iterator->getSubPathName()); - - preg_match_all('/(?:field\s=\s|_t\()[\'"]([^\'"]+)/i', $content, $matches); - - if (count($matches[1])) { - foreach ($matches[1] as $value) { - $scan[$value] = ""; - } - } - } - } + $this->translationSearch($scan, '/src/'); + $this->translationSearch($scan, '/templates/'); $path = $this->config->i18n->dir . $lang . '.php'; $file = file_exists($path) ? include_once $path : []; @@ -246,4 +230,28 @@ public function langAction() echo Console::color(" '" . $key . "' => '" . $value . "'," . PHP_EOL, $color); } } + + /** + * @param array $scan + * @param string $dir + */ + private function translationSearch(array & $scan, $dir) + { + foreach ($iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator(__ROOT__ . $dir, \RecursiveDirectoryIterator::SKIP_DOTS), + \RecursiveIteratorIterator::SELF_FIRST + ) as $item) { + if (!$item->isDir() && in_array($item->getExtension(), ['php', 'sleet'])) { + $content = file_get_contents(__ROOT__ . $dir . $iterator->getSubPathName()); + + preg_match_all('/(?:field\s=\s|_t\()[\'"]([^\'"]+)/i', $content, $matches); + + if (count($matches[1])) { + foreach ($matches[1] as $value) { + $scan[$value] = ""; + } + } + } + } + } } diff --git a/App/Services/UserService.php b/src/App/Services/UserService.php similarity index 99% rename from App/Services/UserService.php rename to src/App/Services/UserService.php index 3ab7cc6..998f16f 100644 --- a/App/Services/UserService.php +++ b/src/App/Services/UserService.php @@ -144,7 +144,7 @@ public function signup($data = null) ['username' => $this->get('username'), 'id' => $this->getId(), 'hash' => $hash] ); - if ($email->Send() === true) { + if ($email->send() === true) { unset($_POST); // Return the user return $this->getModel(); diff --git a/App/views/email/activation.sleet b/templates/email/activation.sleet similarity index 100% rename from App/views/email/activation.sleet rename to templates/email/activation.sleet diff --git a/App/views/email/contact.sleet b/templates/email/contact.sleet similarity index 100% rename from App/views/email/contact.sleet rename to templates/email/contact.sleet diff --git a/App/views/email/error.sleet b/templates/email/error.sleet similarity index 100% rename from App/views/email/error.sleet rename to templates/email/error.sleet diff --git a/App/views/layouts/bootstrap.sleet b/templates/layouts/bootstrap.sleet similarity index 100% rename from App/views/layouts/bootstrap.sleet rename to templates/layouts/bootstrap.sleet diff --git a/App/views/layouts/email.sleet b/templates/layouts/email.sleet similarity index 100% rename from App/views/layouts/email.sleet rename to templates/layouts/email.sleet diff --git a/App/views/layouts/minimal.sleet b/templates/layouts/minimal.sleet similarity index 100% rename from App/views/layouts/minimal.sleet rename to templates/layouts/minimal.sleet diff --git a/App/views/partials/admin/aside.sleet b/templates/partials/admin/aside.sleet similarity index 100% rename from App/views/partials/admin/aside.sleet rename to templates/partials/admin/aside.sleet diff --git a/App/views/partials/admin/footer.sleet b/templates/partials/admin/footer.sleet similarity index 100% rename from App/views/partials/admin/footer.sleet rename to templates/partials/admin/footer.sleet diff --git a/App/views/partials/admin/header.sleet b/templates/partials/admin/header.sleet similarity index 100% rename from App/views/partials/admin/header.sleet rename to templates/partials/admin/header.sleet diff --git a/App/views/partials/doc/aside.sleet b/templates/partials/doc/aside.sleet similarity index 100% rename from App/views/partials/doc/aside.sleet rename to templates/partials/doc/aside.sleet diff --git a/App/views/partials/doc/footer.sleet b/templates/partials/doc/footer.sleet similarity index 100% rename from App/views/partials/doc/footer.sleet rename to templates/partials/doc/footer.sleet diff --git a/App/views/partials/doc/header.sleet b/templates/partials/doc/header.sleet similarity index 100% rename from App/views/partials/doc/header.sleet rename to templates/partials/doc/header.sleet diff --git a/App/views/partials/exception.sleet b/templates/partials/exception.sleet similarity index 100% rename from App/views/partials/exception.sleet rename to templates/partials/exception.sleet diff --git a/App/views/partials/front/aside.sleet b/templates/partials/front/aside.sleet similarity index 100% rename from App/views/partials/front/aside.sleet rename to templates/partials/front/aside.sleet diff --git a/App/views/partials/front/footer.sleet b/templates/partials/front/footer.sleet similarity index 100% rename from App/views/partials/front/footer.sleet rename to templates/partials/front/footer.sleet diff --git a/App/views/partials/front/header.sleet b/templates/partials/front/header.sleet similarity index 100% rename from App/views/partials/front/header.sleet rename to templates/partials/front/header.sleet diff --git a/App/views/partials/message.sleet b/templates/partials/message.sleet similarity index 100% rename from App/views/partials/message.sleet rename to templates/partials/message.sleet diff --git a/App/views/partials/status.sleet b/templates/partials/status.sleet similarity index 100% rename from App/views/partials/status.sleet rename to templates/partials/status.sleet diff --git a/App/i18n/en.php b/translations/en.php similarity index 100% rename from App/i18n/en.php rename to translations/en.php diff --git a/App/i18n/ja.php b/translations/ja.php similarity index 100% rename from App/i18n/ja.php rename to translations/ja.php diff --git a/translations/nl.php b/translations/nl.php new file mode 100644 index 0000000..781b259 --- /dev/null +++ b/translations/nl.php @@ -0,0 +1,125 @@ + "Let op! De activatie is mislukt. Dit is een ongeldige gebruikersnaam of activatiecode.", + 'flash/danger/forbidden' => "Oh oh! Je hebt geen toegang tot deze pagina.", + "flash/notice/activation" => "OK De activatie is voltooid.", + "flash/notice/checkEmail" => "Controleer je e-mail om het account te activeren.", + "flash/success/activation" => "OK! De activatie is voltooid. Je kunt nu inloggen.", + "flash/success/contact" => "OK!! Het bericht is verstuurd", + 'flash/warning/errors' => 'Er ging iets mis. Los de problemen eerst op.', + // App + "account" => "Account", + "activation" => "activatie", + "adminPanel" => "Websitebeheer", + "close" => "Sluiten", + "contact" => "Contact", + "content" => "Inhoud", + "documentation" => "Documentatie", + "email" => "Email", + "error" => "Foutmelding", + "fillFields" => "Vul de velden in", + "fullName" => "Volledige naam", + "niceDay!" => "Een fijne dag!", + "hello %s" => "Hallo %s.", + "hi" => "Hoi", + "home" => "Home", + "iHaveAccount." => "Ik heb al een account.", + "lastLogin" => "Last login", + "logins" => "Logins", + "noAccess" => "Geen toegang", + "noAccessToPage" => "Je hebt geen toegang tot deze pagina.", + "noAccount?" => "Heb je nog geen account?", + "notFound" => "Niet gevonden", + "or" => "of", + "password" => "Wachtwoord", + "rememberMe" => "Wachtwoord onthouden", + "repeatEmail" => "Herhaal e-mail", + "repeatPassword" => "Herhaal wachtwoord", + "send" => "Versturen", + "sender" => "Verzender", + "signIn" => "Inloggen", + "signInBy" => "Sign in by", + "signInToAccess" => "Om deze pagina te zien moet je zijn ingelogd.", + "signOut" => "Sign out", + "signUp" => "Sign up", + "signUpBy" => "Sign up by", + "somethingIsWrong" => "Er ging iets verkeerd!", + "status :code" => "Status :code", + "toggle" => "Toggle", + "username" => "Gebruikersnaam", + // Base + "baseInfo" => 'Deze applicatie is ontwikkeld met het Ice Framework.', + "baseStart" => "Gebruik deze applicatie om snel te kunnen starten met je nieuwe project.", + // Email + "beforeSignIn" => "Activeer eerst je account om in te loggen.", + "toActivateClick" => "Klik op de link om je account te activeren:", + // Langs + "english" => "English", + "language" => "Language", + "polish" => "Polish", + "dutch" => "Nederlands", + "japanese" => "Japanese", + + // Ice validation + /** alnum */ + + /** alnum */ + "Het veld :field mag uitsluitend letters en cijfers bevatten" => "Field :field must contain only letters and numbers", + /** alpha */ + "Het veld :field mag uitsluitend letters bevatten" => "Field :field must contain only letters", + /** between */ + "Het veld :field moet groter of gelijk zijn aan :min en kleiner of gelijk aan :max" => "Field :field must be within the range of :min to :max", + /** digit */ + "Het veld :field moet een numerieke waarde bevatten" => "Field :field must be numeric", + /** email */ + "Het veld :field moet een geldig e-mail adres bevatten." => "Field :field must be an email address", + + + "Field :field must contain only letters and numbers" => "Het veld :field mag uitsluitend letters en cijfers bevatten", + /** alpha */ + "Field :field must contain only letters" => "Het veld :field mag uitsluitend letters bevatten", + /** between */ + "Field :field must be within the range of :min to :max" => "Het veld :field moet groter zijn of gelijk aan :min en kleiner of gelijk aan :max", + /** digit */ + "Field :field must be numeric" => "Het veld :field moet een numerieke waarde bevatten", + /** email */ + "Field :field must be an email address" => "Het veld :field moet een geldig e-mail adres gevatten", + /** in */ + "Field :field must be a part of list: :values" => "Het veld :field moet een van de volgende waardes bevatten: :values", + /** lengthMax */ + "Field :field must not exceed :max characters long" => "Het veld :field mag niet meer dan :max tekens bevatten", + /** lengthMin */ + "Field :field must be at least :min characters long" => "Het veld :field moet tenminste :min tekens bevatten", + /** notIn */ + "Field :field must not be a part of list: :values" => "Het veld :field mag niet hetzelfde zijn als een van de volgende waardes: :values", + /** regex */ + "Field :field does not match the required format" => "Het veld :field bevat een waarde in een ongeldig formaat.", + /** required */ + "Field :field is required" => "Het veld :field is verplicht", + /** same */ + "Field :field and :other must match" => "Het veld :field en :other moeten overeenkomen", + /** unique */ + "Field :field must be unique" => "Het veld :field moet een unieke waarde bevatten", + /** url */ + "Field :field must be a url" => "Het veld :field moet een geldige URL bevatten.", + /** with */ + "Field :field must occur together with :fields" => "Het veld :field moet gebruikt worden in combinatie met de volgende velden: :fields", + /** without */ + "Field :field must not occur together with :fields" => "Het veld :field mag niet gebruikt in combinatie met de volgende velden: :fields", + /* FileEmpty */ + "Field :field must not be empty" => "Het veld :field mag niet leeg zijn", + /* FileIniSize */ + "File :field exceeds the maximum file size" => "Het bestand :field is overschrijdt de maximale bestandsgrootte", + /* FileMaxResolution */ + "File :field must not exceed :max resolution" => "De resolutie van het bestand :field mag niet groter zijn dan :max", + /* FileMinResolution */ + "File :field must be at least :min resolution" => "De resolutie van het bestand :field mag niet kleiner zijn dan :min", + /* FileSize */ + "File :field exceeds the size of :max" => "Het bestand :field overschrijdt de maximum grootte van :max", + /* FileType */ + "File :field must be of type: :types" => "Het bestand :field is ongeldig. De volgende bestandtypes zijn toegestaan: :types", + /* FileValid / default */ + "Field :field is not valid" => "Het veld :field bevat een ongeldige waarde", +]; diff --git a/App/i18n/pl.php b/translations/pl.php similarity index 100% rename from App/i18n/pl.php rename to translations/pl.php diff --git a/App/log/.gitignore b/var/cache/.gitignore similarity index 100% rename from App/log/.gitignore rename to var/cache/.gitignore diff --git a/App/tmp/.gitignore b/var/log/.gitignore similarity index 100% rename from App/tmp/.gitignore rename to var/log/.gitignore diff --git a/var/stubs/HOWTO.md b/var/stubs/HOWTO.md new file mode 100644 index 0000000..b9110e3 --- /dev/null +++ b/var/stubs/HOWTO.md @@ -0,0 +1,17 @@ +## How to setup auto-completion in your IDE + + +Probably the cleanest way to add stubs to your IDEA is to clone the repository and add the directory to the global path. +There are various ways to do this (Eclipse path in eclipse, global path in netbeans, multiple project roots in phpstorm etc). +I personally prefer to put the files in my var folder along with my cache and log files. I don't like global settings +as versions may vary. Also, I don't like to load a lot of stuff in memory I don't need in other projects. +And heck, I may even edit the stub files when I discover a bug (and planning to submit a pull request later). +Either way, feel free to load the stubs anyway you see fit. + +### Clone the repository to get the code +``` +git clone https://github.com/ice/ide.git +``` + + +