├── .github └── workflows │ └── php.yml ├── .gitignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── LICENSE.md ├── README.md ├── Vagrantfile ├── app ├── bootstrap │ ├── controllers.php │ ├── dependencies.php │ ├── errors.php │ ├── middleware.php │ └── sentinel.php ├── routes │ ├── admin-blog.php │ ├── admin-email.php │ ├── admin-media.php │ ├── admin-menus.php │ ├── admin-oauth.php │ ├── admin-pages.php │ ├── admin-seo.php │ ├── admin-settings.php │ ├── admin-users.php │ ├── admin.php │ ├── all.php │ ├── auth.php │ ├── blog.php │ ├── custom.php │ ├── users.php │ └── webhooks.php ├── src │ ├── App │ │ ├── App.php │ │ └── FileBrowser.php │ ├── Controller │ │ ├── Admin │ │ │ ├── Admin.php │ │ │ ├── Blog.php │ │ │ ├── BlogCategories.php │ │ │ ├── BlogComments.php │ │ │ ├── BlogTags.php │ │ │ ├── Developer.php │ │ │ ├── Email.php │ │ │ ├── Media.php │ │ │ ├── Menus.php │ │ │ ├── Oauth2.php │ │ │ ├── Pages.php │ │ │ ├── Roles.php │ │ │ ├── Seo.php │ │ │ ├── Settings.php │ │ │ └── Users.php │ │ ├── App.php │ │ ├── Auth.php │ │ ├── Blog.php │ │ ├── Controller.php │ │ ├── Cron.php │ │ ├── Oauth2.php │ │ ├── Profile.php │ │ └── Webhooks │ │ │ └── Mailgun.php │ ├── Middleware │ │ ├── Admin.php │ │ ├── Auth.php │ │ ├── BlogCheck.php │ │ ├── Guest.php │ │ ├── Maintenance.php │ │ ├── Middleware.php │ │ ├── PageConfig.php │ │ ├── ProfileCheck.php │ │ ├── RouteName.php │ │ ├── Seo.php │ │ └── TwoFactorAuth.php │ ├── Migration │ │ └── Migration.php │ ├── Model │ │ ├── Activations.php │ │ ├── BlogCategories.php │ │ ├── BlogPosts.php │ │ ├── BlogPostsComments.php │ │ ├── BlogPostsReplies.php │ │ ├── BlogPostsTags.php │ │ ├── BlogTags.php │ │ ├── Config.php │ │ ├── ConfigGroups.php │ │ ├── ConfigTypes.php │ │ ├── ContactRequests.php │ │ ├── Emails.php │ │ ├── EmailsStatus.php │ │ ├── EmailsTemplates.php │ │ ├── Menus.php │ │ ├── Oauth2Providers.php │ │ ├── Oauth2Users.php │ │ ├── RoleRoutes.php │ │ ├── RoleUsers.php │ │ ├── Roles.php │ │ ├── Routes.php │ │ ├── Seo.php │ │ ├── Users.php │ │ └── UsersProfile.php │ └── TwigExtension │ │ └── .gitkeep └── views │ └── .gitkeep ├── composer.json ├── composer.lock ├── database ├── migrations │ ├── 20170118012924_init_database.php │ ├── 20180216163853_add_seo_options.php │ ├── 20180222064705_add_oauth.php │ ├── 20180717054011_admin_lte_config.php │ ├── 20180722001608_add_two_factor_auth.php │ ├── 20180722211504_add_custom_pages.php │ ├── 20180728191437_add_menu_editor.php │ └── 20180810003809_add_side_bar_to_custom_routes.php └── templates │ └── create-template.php ├── phinx.php ├── public ├── .htaccess ├── index.php ├── robots.txt └── uploads │ ├── .htaccess │ └── index.php ├── settings.json.dist └── storage ├── cache └── twig │ └── .gitkeep ├── certs ├── deployment │ └── .gitkeep └── google │ └── .gitkeep ├── log ├── cron │ └── .gitkeep ├── deployment │ └── .gitkeep └── monolog │ └── .gitkeep └── vagrant ├── provision.sh ├── push.migrate.sh └── push.rollback.sh /.github/workflows/php.yml: -------------------------------------------------------------------------------- 1 | name: PHP Composer 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | 17 | - name: Validate composer.json and composer.lock 18 | run: composer validate 19 | 20 | - name: Cache Composer packages 21 | id: composer-cache 22 | uses: actions/cache@v2 23 | with: 24 | path: vendor 25 | key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} 26 | restore-keys: | 27 | ${{ runner.os }}-php- 28 | 29 | - name: Install dependencies 30 | if: steps.composer-cache.outputs.cache-hit != 'true' 31 | run: composer install --prefer-dist --no-progress --no-suggest 32 | 33 | # Add a test script to composer.json, for instance: "test": "vendor/bin/phpunit" 34 | # Docs: https://getcomposer.org/doc/articles/scripts.md 35 | 36 | # - name: Run test suite 37 | # run: composer run-script test 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | composer.phar 2 | vendor/ 3 | storage/cache/twig/* 4 | !storage/cache/twig/.gitkeep 5 | storage/log/monolog/* 6 | !storage/log/monolog/.gitkeep 7 | storage/log/cron/* 8 | !storage/log/cron/.gitkeep 9 | storage/certs/* 10 | !storage/certs/google/.gitkeep 11 | 12 | public/uploads/* 13 | !public/uploads/index.php 14 | !public/uploads/.htaccess 15 | 16 | settings.json 17 | bower_components 18 | node_modules 19 | 20 | .phpintel 21 | 22 | .vagrant -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at info@dappur.io. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Dappur.io 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dappur PHP Framework 2 | 3 | A stylish PHP application framework crafted using Slim, Twig, Eloquent and Sentinel designed to get you from clone to production in a matter of minutes. 4 | 5 | Built on the Slim PHP Micro Framework, Twig templating engine, Eloquent ORM database interactions, Phinx database migrations, Sentinel user management, Monolog w/ Logentries Support, form validation with CSRF protection, cookie management, database controlled config,Cloudinary CMS integration, blog, SEO, oauth2 login, and two-factor authentication. 6 | 7 | This is a lightweight full featured framework intended for PHP developers who need an open source, fast and reliable platform to build your apps from. Have your new projects up and running in minutes with the provided basic bootstrap pages and basic bootstrap admin. 8 | 9 | ## Important Links 10 | **[Demo](https://demo.dappur.io)** 11 | **[Documentation](https://docs.dappur.io)** 12 | **[Changelog](https://github.com/dappur/framework/blob/master/CHANGELOG.md)** 13 | **[dApp CLI](https://github.com/dappur/dapp)** 14 | 15 | ## Quick Start Via Vagrant 16 | Once installed, run `vagrant up` in the project root to provision a box that contains: 17 | 18 | - Ubuntu 18 19 | - PHP 7.2 20 | - Composer 21 | - Phinx 22 | - MariaDB 10.3 23 | - Apache 2 24 | 25 | The script will also fetch dependencies, create a `dev` database, and run the initial migration for you. Services will be accessible through: 26 | - **Web Server:** http://localhost:8181 27 | - **Phpmyadmin:** http://localhost:8181/phpmyadmin 28 | - **MySQL Direct:** localhost:8306 29 | 30 | ## [Frontend Theme](https://github.com/dappur/theme-dappur) 31 | This framework comes with several pre-made Bootstrap pages to help get your project moving. All of these pages and their respective controllers/views provide you an insight into how the framework functions including form validation, CSRF, working with Eloquent ORM and other plugins. You can expand on the default template or create a completely new template using Twig and the front-end framework of your choosing. 32 | 33 | ## [Dashboard Theme](https://github.com/dappur/theme-AdminLTE) 34 | In addition to the few basic front end templates, this framework also comes pre-built with a basic Bootstrap 3 admin dashboard. This dashboard can be accessed automatically by logging in with the admin user credentials. 35 | 36 | ## //TODO 37 | * Create Documentation 38 | * Beef up the dApp CLI 39 | * Add Unit Testing 40 | 41 | ## Pre-Requisites 42 | [PHP](https://secure.php.net/) - PHP is a popular general-purpose scripting language that is especially suited to web development 43 | 44 | [MySQL Server](https://github.com/mysql/mysql-server) - MySQL Server, the world's most popular open source database, and MySQL Cluster, a real-time, open source transactional database. 45 | 46 | [Composer](https://getcomposer.org/) - Dependency manager is required in order to use the Dappur PHP Framework. [Installation Instructions](https://getcomposer.org/doc/00-intro.md) 47 | 48 | [Phinx](https://phinx.org/) - Phinx is required in order to utilize the database migrations. It is recommended that you install Phinx globally via composer by running: 49 | ```bash 50 | $ composer global require robmorgan/phinx 51 | ``` 52 | 53 | ## Install with [dApp](https://github.com/dappur/dapp) (Experimental) 54 | This command clones your project via the `composer create-project` command and downloads the themes as well as prepare your `settings.json` file. 55 | ```bash 56 | $ dapp new new_app 57 | ``` 58 | 59 | ## Install Via Composer 60 | #### Step 1 (Create the project) 61 | You can start a new project user the Composer `create-project` command. 62 | ```bash 63 | $ composer create-project dappur/framework new_app 64 | ``` 65 | 66 | #### Step 2 (Install the themes) 67 | If you install via composer, you will have to install both of the themes manually. Simply copy the theme folder from the theme repository into you `app/views` folder inside your project. If you are using a custom theme, you will have to change the initial migration to support that theme before you migrate your database. 68 | - [Dappur - Frontend Theme](https://github.com/dappur/theme-dappur) 69 | - [AdminLTE - Dashboard Theme](https://github.com/dappur/theme-AdminLTE) 70 | 71 | #### Step 3 (Prepare settings.json file) 72 | Inside the root of your project, you will need to copy the `settings.json.dist` to `settings.json`. 73 | ```bash 74 | $ cp settings.json.dist settings.json 75 | ``` 76 | Once copied, open `settings.json` and change the "framework" name to your project name. You can also set up any other options as well as your database credentials. 77 | 78 | ## Initial Database Migration 79 | If you are not using Vagrant, you have one more step to go before you are live. You will need to ensure that you database credentials are correct in `settings.json` and then run the following command in a terminal from your root project directory: 80 | ```bash 81 | $ phinx migrate 82 | ``` 83 | 84 | ## Run & Test Project 85 | If you've chosen to use vagrant, you can simply visit the configured URL:PORT after completing the `vagrant up` command. Otherwise, once you have successfully done the initial migration, you can simply use PHP's built in web server to test your application by running the following from your root project directory: 86 | ```bash 87 | $ php -S localhost:8181 -t public 88 | ``` 89 | 90 | Navigate to [http://localhost:8181](http://localhost:8181) to view your project. 91 | 92 | **Default Admin Username:** `admin` 93 | **Default Admin Password:** `admin123` 94 | 95 | It is HIGHLY recommended that you change the password immediately after your initial migration. 96 | 97 | ## Created Using 98 | * [Slim](https://github.com/slimphp/Slim) - Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs 99 | * [Slim Twig-View](https://github.com/slimphp/Twig-View) - Slim Framework 3 view helper built on top of the Twig 2 templating component 100 | * [Slim Flash Messaging](https://github.com/slimphp/Slim-Flash) - Slim Framework Flash message service provider 101 | * [Slim CSRF](https://github.com/slimphp/Slim-Csrf) - Slim Framework 3 CSRF protection middleware 102 | * [Slim Validation](https://github.com/awurth/slim-validation) - A validator for Slim micro-framework using [Respect\Validation](https://github.com/Respect/Validation) 103 | * [Cartalyst Sentinel](https://github.com/cartalyst/sentinel) - PHP 5.4+ Fully-featured Authentication & Authorization System 104 | * [Illuminate Database](https://github.com/illuminate/database) - The Illuminate Database component is a full database toolkit for PHP, providing an expressive query builder, ActiveRecord style ORM, and schema builder. 105 | * [Monolog Logging](https://github.com/Seldaek/monolog) - Send logs to files, sockets, inboxes, databases and various web services. 106 | * [Fig Cookies](https://github.com/dflydev/dflydev-fig-cookies) - Cookies for PSR-7 HTTP Message Interface. 107 | * [Phinx Database Migrations](https://github.com/robmorgan/phinx) - Phinx makes it ridiculously easy to manage the database migrations for your PHP app. 108 | * [Cloudinary Image CDN](https://github.com/cloudinary/cloudinary_php) - Cloudinary is a cloud service that offers a solution to a web application's entire image management pipeline. 109 | * [PHPMailer](https://github.com/PHPMailer/PHPMailer) - A full-featured email creation and transfer class for PHP. 110 | * [Paginator](https://github.com/jasongrimes/php-paginator) - A lightweight PHP paginator, for generating pagination controls in the style of Stack Overflow and Flickr. 111 | * [UUID](https://github.com/ramsey/uuid) - A PHP library for generating RFC 4122 version 1, 3, 4, and 5 universally unique identifiers (UUID). 112 | * [Jobby](https://github.com/jobbyphp/jobby) - Manage all your cron jobs without modifying crontab. Handles locking, logging, error emails, and more. 113 | * [TwoFactorAuth](https://github.com/RobThree/TwoFactorAuth) - PHP library for Two Factor Authentication (TFA / 2FA) -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | require 'securerandom' 4 | 5 | # Globals 6 | httpPort = 8181 7 | mysqlPort = 8381 8 | rootPass = "rootpass" 9 | 10 | # Check if settings json file exists or create 11 | if not File.file?('settings.json') 12 | IO.copy_stream('settings.json.dist', 'settings.json') 13 | end 14 | 15 | # Get Settings & Environment 16 | settings = JSON.parse(File.read('settings.json')) 17 | env = settings['environment'] 18 | 19 | # Update environment db port 20 | settings['db'][env]['port'] = mysqlPort 21 | 22 | if settings['cron']['token'].empty? 23 | settings['cron']['token'] = SecureRandom.uuid.split('-').join 24 | end 25 | 26 | File.open("settings.json","w") do |f| 27 | f.write(JSON.pretty_generate(settings, :indent => "\t")) 28 | end 29 | 30 | # Get Database From settings.json 31 | database = settings['db'][env] 32 | dbName = database['database'] 33 | dbUser = database['username'] 34 | dbPass = database['password'] 35 | 36 | Vagrant.configure("2") do |config| 37 | # ubuntu 18.04 38 | config.vm.box = "bento/ubuntu-20.04" 39 | config.vm.box_version = "202303.13.0" 40 | # forward http port 41 | config.vm.network "forwarded_port", 42 | guest: httpPort, 43 | host: httpPort 44 | # forward mysql port 45 | config.vm.network "forwarded_port", 46 | guest: mysqlPort, 47 | host: mysqlPort 48 | # provision 49 | config.vm.provision "shell", 50 | path: "storage/vagrant/provision.sh", 51 | privileged: false, 52 | args: [ 53 | httpPort, 54 | rootPass, 55 | dbName, 56 | dbUser, 57 | dbPass, 58 | mysqlPort, 59 | ] 60 | # migrate push command 61 | config.push.define "migrate", strategy: "local-exec" do |push| 62 | push.script = "storage/vagrant/push.migrate.sh" 63 | end 64 | # rollback push command 65 | config.push.define "rollback", strategy: "local-exec" do |push| 66 | push.script = "storage/vagrant/push.rollback.sh" 67 | end 68 | end -------------------------------------------------------------------------------- /app/bootstrap/controllers.php: -------------------------------------------------------------------------------- 1 | getContainer(); 4 | 5 | // Configure Database 6 | $db = $container['settings']['db'][$container['settings']['environment']]; 7 | $capsule = new \Illuminate\Database\Capsule\Manager(); 8 | $capsule->addConnection((array)$db); 9 | $capsule->setAsGlobal(); 10 | $capsule->bootEloquent(); 11 | 12 | $container['db'] = function () use ($capsule) { 13 | return $capsule; 14 | }; 15 | 16 | $container['session'] = function () use ($container) { 17 | return new \SlimSession\Helper; 18 | }; 19 | 20 | $container['projectDir'] = function ($container) { 21 | $directory = __DIR__ . "/../../"; 22 | return realpath($directory); 23 | }; 24 | 25 | $container['publicDir'] = function ($container) { 26 | $directory = __DIR__ . "/../../public/"; 27 | return realpath($directory); 28 | }; 29 | 30 | $container['uploadDir'] = function ($container) { 31 | $directory = __DIR__ . "/../../public/uploads/"; 32 | return realpath($directory); 33 | }; 34 | 35 | // Bind config table from database 36 | $container['config'] = function ($container) { 37 | $config = new \Dappur\Dappurware\SiteConfig; 38 | $config = $config->getGlobalConfig(); 39 | 40 | return $config; 41 | }; 42 | 43 | 44 | 45 | // Bind Sentinel Authorization plugin 46 | $container['auth'] = function ($container) { 47 | $sentinel = new \Cartalyst\Sentinel\Native\Facades\Sentinel( 48 | new \Cartalyst\Sentinel\Native\SentinelBootstrapper( 49 | __DIR__ . '/sentinel.php' 50 | ) 51 | ); 52 | 53 | return $sentinel->getSentinel(); 54 | }; 55 | 56 | // Bind User Permissions 57 | $container['userAccess'] = function ($container) { 58 | return (new \Dappur\Dappurware\Sentinel($container))->userAccess(); 59 | }; 60 | 61 | // Bind Flash Messages 62 | $container['flash'] = function ($container) { 63 | return new \Slim\Flash\Messages(); 64 | }; 65 | 66 | // Bind Respect Validation 67 | $container['validator'] = function ($container) { 68 | return new \Awurth\SlimValidation\Validator(); 69 | }; 70 | 71 | $container['cookies'] = function ($container) { 72 | return new \Dappur\Dappurware\Cookies; 73 | }; 74 | 75 | // CSRF 76 | $container['csrf'] = function ($container) { 77 | $guard = new \Slim\Csrf\Guard( 78 | $container->settings['csrf']['prefix'], 79 | $storage, 80 | null, 81 | $container->settings['csrf']['storage_limit'], 82 | $container->settings['csrf']['strength'], 83 | $container->settings['csrf']['persist_tokens'] 84 | ); 85 | 86 | $guard->setFailureCallable( 87 | function ($request, $response, $next) use ($container) { 88 | return $container['view'] 89 | ->render($response, 'errors/csrf.twig') 90 | ->withHeader('Content-type', 'text/html') 91 | ->withStatus(401); 92 | } 93 | ); 94 | 95 | return $guard; 96 | }; 97 | 98 | // Bind Twig View 99 | $container['view'] = function ($container) { 100 | $template_path = __DIR__ . '/../views/' . $container->config['theme']; 101 | if (strpos($container['request']->getUri()->getPath(), '/dashboard') !== false) { 102 | $template_path = __DIR__ . '/../views/' . $container->config['dashboard-theme']; 103 | } 104 | 105 | $view = new \Slim\Views\Twig( 106 | $template_path, 107 | $container['settings']['view']['twig'] 108 | ); 109 | 110 | // Add Twig Extensions 111 | $view->addExtension( 112 | new \Slim\Views\TwigExtension( 113 | $container['router'], 114 | $container['request']->getUri() 115 | ) 116 | ); 117 | $view->addExtension(new \Twig_Extension_Debug()); 118 | $view->addExtension(new \Dappur\TwigExtension\Asset($container['request'])); 119 | $view->addExtension(new \Dappur\TwigExtension\JsonDecode($container['request'])); 120 | $view->addExtension(new \Dappur\TwigExtension\Oauth2($container)); 121 | $view->addExtension(new \Dappur\TwigExtension\Recaptcha($container['settings']['recaptcha'])); 122 | $view->addExtension(new \Dappur\TwigExtension\Csrf($container['csrf'])); 123 | $view->addExtension(new \Awurth\SlimValidation\ValidatorExtension($container['validator'])); 124 | $view->addExtension(new \Dappur\TwigExtension\Md5($container['request'])); 125 | $view->addExtension(new \Dappur\TwigExtension\Gravatar($container['request'])); 126 | $view->addExtension(new \Dappur\TwigExtension\Menus($container)); 127 | $view->addExtension(new \Twig_Extension_StringLoader()); 128 | 129 | // Globla Variables 130 | $view->getEnvironment()->addGlobal('flash', $container['flash']); 131 | $view->getEnvironment()->addGlobal('auth', $container['auth']); 132 | $view->getEnvironment()->addGlobal('config', $container['config']); 133 | $view->getEnvironment()->addGlobal('displayErrorDetails', $container['settings']['displayErrorDetails']); 134 | $view->getEnvironment()->addGlobal('currentRoute', $container['request']->getUri()->getPath()); 135 | $view->getEnvironment()->addGlobal('requestParams', $container['request']->getParams()); 136 | 137 | // Cloudinary View Settings 138 | $view->addExtension(new \Dappur\TwigExtension\Cloudinary()); 139 | $view->getEnvironment()->addGlobal('hasCloudinary', 0); 140 | if ($container['cloudinary']) { 141 | $view->getEnvironment()->addGlobal('hasCloudinary', 1); 142 | if ($container->auth->check() && $container->auth->hasAccess('media.cloudinary')) { 143 | $view->getEnvironment()->addGlobal( 144 | 'cloudinaryCmsUrl', 145 | \Dappur\Controller\Admin\Media::getCloudinaryCMS($container) 146 | ); 147 | $view->getEnvironment()->addGlobal( 148 | 'cloudinarySignature', 149 | \Dappur\Controller\Admin\Media::getCloudinaryCMS($container, true) 150 | ); 151 | $view->getEnvironment()->addGLobal( 152 | 'cloudinaryApiKey', 153 | $container['settings']['cloudinary']['api_key'] 154 | ); 155 | $view->getEnvironment()->addGLobal( 156 | 'cloudinaryCloudName', 157 | $container['settings']['cloudinary']['cloud_name'] 158 | ); 159 | } 160 | } 161 | 162 | // Blog View Settings 163 | if ($container['config']['blog-enabled']) { 164 | // Get Categories With Count 165 | $blog_categories = new \Dappur\Model\BlogCategories; 166 | $blog_categories = $blog_categories->withCount( 167 | ['posts' => function ($query) { 168 | $query->where('blog_posts.status', 1); 169 | }] 170 | ) 171 | ->whereHas( 172 | 'posts', 173 | function ($query) { 174 | $query->where('blog_posts.status', 1); 175 | } 176 | ) 177 | ->get(); 178 | 179 | // Get Tags With Count 180 | $blog_tags = new \Dappur\Model\BlogTags; 181 | $blog_tags = $blog_tags->withCount([ 182 | 'posts' => function ($query) { 183 | $query->where('blog_posts.status', 1); 184 | } 185 | ]) 186 | ->whereHas('posts', function ($query) { 187 | $query->where('blog_posts.status', 1); 188 | }) 189 | ->get(); 190 | 191 | // Get Recent Posts 192 | $blogPosts = \Dappur\Model\BlogPosts::orderBy('publish_at', 'DESC') 193 | ->where('publish_at', '<=', \Carbon\Carbon::now()) 194 | ->where('status', 1) 195 | ->whereNotNull('featured_image') 196 | ->skip(0) 197 | ->take(4) 198 | ->get(); 199 | 200 | // Get Recent Comments 201 | $blogComments = \Dappur\Model\BlogPostsComments::orderBy('created_at', 'DESC') 202 | ->where('status', 1) 203 | ->skip(0) 204 | ->take(4) 205 | ->get(); 206 | 207 | $view->getEnvironment()->addGlobal('blogCategories', $blog_categories); 208 | $view->getEnvironment()->addGlobal('blogTags', $blog_tags); 209 | $view->getEnvironment()->addGlobal('blogRecent', $blogPosts); 210 | $view->getEnvironment()->addGlobal('blogComments', $blogComments); 211 | } 212 | 213 | 214 | 215 | $page_name = $container['request']->getAttribute('name'); 216 | if (strpos($container['request']->getUri()->getPath(), '/dashboard') !== false) { 217 | $page_settings = new \Dappur\Model\ConfigGroups; 218 | $page_settings = $page_settings 219 | ->select('page_name') 220 | ->whereNotNull('page_name') 221 | ->groupBy('page_name') 222 | ->orderBy('page_name') 223 | ->get(); 224 | $view->getEnvironment()->addGlobal('userAccess', $container['userAccess']); 225 | $view->getEnvironment()->addGlobal('pageSettings', $page_settings); 226 | } 227 | return $view; 228 | }; 229 | 230 | // Bind Found Handler 231 | $container['foundHandler'] = function ($container) { 232 | return new \Slim\Handlers\Strategies\RequestResponseArgs(); 233 | }; 234 | 235 | // Bind Monolog Logging System 236 | $container['logger'] = function ($container) { 237 | 238 | // Stream Log output to file 239 | $logger = new Monolog\Logger($container['settings']['logger']['log_name']); 240 | $logPath = __DIR__ . "/../../storage/log/monolog/"; 241 | $logFile = date("Y-m-d-") . $container['settings']['logger']['log_file_name']; 242 | $fileStream = new \Monolog\Handler\StreamHandler($logPath . $logFile); 243 | $logger->pushHandler($fileStream); 244 | 245 | //Stream log output to Logentries 246 | if ($container['settings']['logger']['le_token'] != '') { 247 | $le_stream = new Logentries\Handler\LogentriesHandler($container['settings']['logger']['le_token']); 248 | $logger->pushHandler($le_stream); 249 | } 250 | 251 | return $logger; 252 | }; 253 | 254 | // Cloudinary PHP API 255 | $container['cloudinary'] = function ($container) { 256 | if ($container['settings']['cloudinary']['enabled']) { 257 | \Cloudinary::config( 258 | array( "cloud_name" => $container['settings']['cloudinary']['cloud_name'], 259 | "api_key" => $container['settings']['cloudinary']['api_key'], 260 | "api_secret" => $container['settings']['cloudinary']['api_secret'] 261 | ) 262 | ); 263 | 264 | return new \Cloudinary; 265 | } else { 266 | return false; 267 | } 268 | }; 269 | 270 | // Mail Relay 271 | $container['mail'] = function ($container) { 272 | $mailSettings = $container['settings']['mail']; 273 | 274 | $mail = new \PHPMailer\PHPMailer\PHPMailer; 275 | 276 | switch ($mailSettings['relay']) { 277 | case 'phpmail': 278 | break; 279 | 280 | case 'smtp': 281 | $mail->isSMTP(); 282 | $mail->Host = $mailSettings['smtp']['host']; 283 | $mail->Port = $mailSettings['smtp']['port']; 284 | if ($mailSettings['smtp']['smtp_auth']) { 285 | $mail->SMTPAuth = true; 286 | $mail->Username = $mailSettings['smtp']['username']; 287 | $mail->Password = $mailSettings['smtp']['password']; 288 | } 289 | $mail->SMTPSecure = $mailSettings['smtp']['smtp_secure']; 290 | break; 291 | 292 | default: 293 | break; 294 | } 295 | return $mail; 296 | }; 297 | -------------------------------------------------------------------------------- /app/bootstrap/errors.php: -------------------------------------------------------------------------------- 1 | sendEmail( 9 | array( 10 | $container['config']['error-email']), 11 | "404 Error on " . $container['config']['site-name'], 12 | "
Route: " . $request->getUri()->getPath() . "" . 13 | "
Headers: " . json_encode($request->getHeaders(), JSON_PRETTY_PRINT) . "" 14 | ); 15 | } 16 | 17 | $container->logger->addNotice( 18 | "404 Error", 19 | array( 20 | "route" => $request->getUri()->getPath(), 21 | "headers" => $request->getHeaders() 22 | ) 23 | ); 24 | 25 | return $container['view'] 26 | ->render($response, 'errors/404.twig') 27 | ->withHeader('Content-type', 'text/html') 28 | ->withStatus(404); 29 | }; 30 | }; 31 | 32 | $container['notAllowedHandler'] = function ($container) { 33 | return function ($request, $response, $methods) use ($container) { 34 | if (filter_var($container['config']['error-email'], FILTER_VALIDATE_EMAIL) 35 | && $container['config']['error-email-405']) { 36 | $email = new \Dappur\Dappurware\Email($container); 37 | $email->sendEmail( 38 | array( 39 | $container['config']['error-email']), 40 | "405 Error on " . $container['config']['site-name'], 41 | "
Route: " . $request->getUri()->getPath() . "" . 42 | "
Headers: " . json_encode($request->getHeaders(), JSON_PRETTY_PRINT) . "" 43 | ); 44 | } 45 | 46 | $container->logger->addNotice( 47 | "405 Error", 48 | array( 49 | "route" => $request->getUri()->getPath(), 50 | "headers" => $request->getHeaders() 51 | ) 52 | ); 53 | 54 | return $container['view'] 55 | ->render($response, 'errors/405.twig', array("methods" => $methods)) 56 | ->withStatus(405) 57 | ->withHeader('Allow', implode(', ', $methods)) 58 | ->withHeader('Content-type', 'text/html'); 59 | }; 60 | }; 61 | 62 | $container['errorHandler'] = function ($container) { 63 | return function ($request, $response, $exception) use ($container) { 64 | if (filter_var($container['config']['error-email'], FILTER_VALIDATE_EMAIL) 65 | && $container['config']['error-email-500']) { 66 | $email = new \Dappur\Dappurware\Email($container); 67 | $email->sendEmail( 68 | array( 69 | $container['config']['error-email']), 70 | "PHP Error on " . $container['config']['site-name'], 71 | "
" . $exception . "" 72 | ); 73 | } 74 | 75 | $container->logger->addError( 76 | "Server Error", 77 | array( 78 | "exception" => $exception, 79 | "headers" => $request->getHeaders() 80 | ) 81 | ); 82 | 83 | return $container['view'] 84 | ->render($response, 'errors/500.twig', array("exception" => $exception)) 85 | ->withStatus(500) 86 | ->withHeader('Content-type', 'text/html'); 87 | }; 88 | }; 89 | 90 | $container['phpErrorHandler'] = function ($container) { 91 | return function ($request, $response, $exception) use ($container) { 92 | if (filter_var($container['config']['error-email'], FILTER_VALIDATE_EMAIL) 93 | && $container['config']['error-email-500']) { 94 | $email = new \Dappur\Dappurware\Email($container); 95 | $email->sendEmail( 96 | array( 97 | $container['config']['error-email']), 98 | "Application Error on " . $container['config']['site-name'], 99 | "
" . $exception . "" 100 | ); 101 | } 102 | 103 | $container->logger->addError( 104 | "Application Error", 105 | array( 106 | "exception" => $exception, 107 | "headers" => $request->getHeaders() 108 | ) 109 | ); 110 | 111 | return $container['view'] 112 | ->render($response, 'errors/500-php.twig', array("exception" => $exception)) 113 | ->withStatus(500) 114 | ->withHeader('Content-type', 'text/html'); 115 | }; 116 | }; 117 | -------------------------------------------------------------------------------- /app/bootstrap/middleware.php: -------------------------------------------------------------------------------- 1 | add(new \Slim\Middleware\Session([ 5 | 'name' => $container->settings['framework'], 6 | 'autorefresh' => true, 7 | 'lifetime' => '1 hour' 8 | ])); 9 | -------------------------------------------------------------------------------- /app/bootstrap/sentinel.php: -------------------------------------------------------------------------------- 1 | 'cartalyst_sentinel', 33 | 34 | /* 35 | |-------------------------------------------------------------------------- 36 | | Cookie Key 37 | |-------------------------------------------------------------------------- 38 | | 39 | | Please provide your cookie key for Sentinel. 40 | | 41 | */ 42 | 43 | 'cookie' => 'cartalyst_sentinel', 44 | 45 | /* 46 | |-------------------------------------------------------------------------- 47 | | Users 48 | |-------------------------------------------------------------------------- 49 | | 50 | | Please provide the user model used in Sentinel. 51 | | 52 | */ 53 | 54 | 'users' => [ 55 | 56 | 'model' => 'Dappur\Model\Users', 57 | 58 | ], 59 | 60 | /* 61 | |-------------------------------------------------------------------------- 62 | | Roles 63 | |-------------------------------------------------------------------------- 64 | | 65 | | Please provide the role model used in Sentinel. 66 | | 67 | */ 68 | 69 | 'roles' => [ 70 | 71 | 'model' => 'Cartalyst\Sentinel\Roles\EloquentRole', 72 | 73 | ], 74 | 75 | /* 76 | |-------------------------------------------------------------------------- 77 | | Permissions 78 | |-------------------------------------------------------------------------- 79 | | 80 | | Here you may specify the permissions class. Sentinel ships with two 81 | | permission types. 82 | | 83 | | 'Cartalyst\Sentinel\Permissions\StandardPermissions' 84 | | 'Cartalyst\Sentinel\Permissions\StrictPermissions' 85 | | 86 | | "StandardPermissions" will assign a higher priority to the user 87 | | permissions over role permissions, once a user is allowed or denied 88 | | a specific permission, it will be used regardless of the 89 | | permissions set on the role. 90 | | 91 | | "StrictPermissions" will deny any permission as soon as it finds it 92 | | rejected on either the user or any of the assigned roles. 93 | | 94 | */ 95 | 96 | 'permissions' => [ 97 | 98 | 'class' => 'Cartalyst\Sentinel\Permissions\StandardPermissions', 99 | 100 | ], 101 | 102 | /* 103 | |-------------------------------------------------------------------------- 104 | | Persistences 105 | |-------------------------------------------------------------------------- 106 | | 107 | | Here you may specify the persistences model used and weather to use the 108 | | single persistence mode. 109 | | 110 | */ 111 | 112 | 'persistences' => [ 113 | 114 | 'model' => 'Cartalyst\Sentinel\Persistences\EloquentPersistence', 115 | 116 | 'single' => false, 117 | 118 | ], 119 | 120 | /* 121 | |-------------------------------------------------------------------------- 122 | | Checkpoints 123 | |-------------------------------------------------------------------------- 124 | | 125 | | When logging in, checking for existing sessions and failed logins occur, 126 | | you may configure an indefinite number of "checkpoints". These are 127 | | classes which may respond to each event and handle accordingly. 128 | | We ship with two, a throttling checkpoint and an activation 129 | | checkpoint. Feel free to add, remove or re-order 130 | | these. 131 | | 132 | */ 133 | 134 | 'checkpoints' => [ 135 | 136 | 'throttle', 137 | 'activation', 138 | 139 | ], 140 | 141 | /* 142 | |-------------------------------------------------------------------------- 143 | | Activations 144 | |-------------------------------------------------------------------------- 145 | | 146 | | Here you may specify the activations model used and the time (in seconds) 147 | | which activation codes expire. By default, activation codes expire after 148 | | three days. The lottery is used for garbage collection, expired 149 | | codes will be cleared automatically based on the provided odds. 150 | | 151 | */ 152 | 153 | 'activations' => [ 154 | 155 | 'model' => 'Cartalyst\Sentinel\Activations\EloquentActivation', 156 | 157 | 'expires' => 259200, 158 | 159 | 'lottery' => [2, 100], 160 | 161 | ], 162 | 163 | /* 164 | |-------------------------------------------------------------------------- 165 | | Reminders 166 | |-------------------------------------------------------------------------- 167 | | 168 | | Here you may specify the reminders model used and the time (in seconds) 169 | | which reminder codes expire. By default, reminder codes expire 170 | | after four hours. The lottery is used for garbage collection, expired 171 | | codes will be cleared automatically based on the provided odds. 172 | | 173 | */ 174 | 175 | 'reminders' => [ 176 | 177 | 'model' => 'Cartalyst\Sentinel\Reminders\EloquentReminder', 178 | 179 | 'expires' => 14400, 180 | 181 | 'lottery' => [2, 100], 182 | 183 | ], 184 | 185 | /* 186 | |-------------------------------------------------------------------------- 187 | | Throttling 188 | |-------------------------------------------------------------------------- 189 | | 190 | | Here, you may configure your site's throttling settings. There are three 191 | | types of throttling. 192 | | 193 | | The first type is "global". Global throttling will monitor the overall 194 | | failed login attempts across your site and can limit the effects of an 195 | | attempted DDoS attack. 196 | | 197 | | The second type is "ip". This allows you to throttle the failed login 198 | | attempts (across any account) of a given IP address. 199 | | 200 | | The third type is "user". This allows you to throttle the login attempts 201 | | on an individual user account. 202 | | 203 | | Each type of throttling has the same options. The first is the interval. 204 | | This is the time (in seconds) for which we check for failed logins. Any 205 | | logins outside this time are no longer assessed when throttling. 206 | | 207 | | The second option is thresholds. This may be approached one of two ways. 208 | | the first way, is by providing an key/value array. The key is the number 209 | | of failed login attempts, and the value is the delay, in seconds, before 210 | | the next attempt can occur. 211 | | 212 | | The second way is by providing an integer. If the number of failed login 213 | | attempts outweigh the thresholds integer, that throttle is locked until 214 | | there are no more failed login attempts within the specified interval. 215 | | 216 | | On this premise, we encourage you to use array thresholds for global 217 | | throttling (and perhaps IP throttling as well), so as to not lock your 218 | | whole site out for minutes on end because it's being DDoS'd. However, 219 | | for user throttling, locking a single account out because somebody is 220 | | attempting to breach it could be an appropriate response. 221 | | 222 | | You may use any type of throttling for any scenario, and the specific 223 | | configurations are designed to be customized as your site grows. 224 | | 225 | */ 226 | 227 | 'throttling' => [ 228 | 229 | 'model' => 'Cartalyst\Sentinel\Throttling\EloquentThrottle', 230 | 231 | 'global' => [ 232 | 233 | 'interval' => 300, 234 | 235 | 'thresholds' => [ 236 | 10 => 1, 237 | 20 => 2, 238 | 30 => 4, 239 | 40 => 8, 240 | 50 => 16, 241 | 60 => 12 242 | ], 243 | 244 | ], 245 | 246 | 'ip' => [ 247 | 248 | 'interval' => 300, 249 | 250 | 'thresholds' => 5 251 | 252 | ], 253 | 254 | 'user' => [ 255 | 256 | 'interval' => 300, 257 | 258 | 'thresholds' => 5 259 | 260 | ], 261 | 262 | ], 263 | 264 | ]; 265 | -------------------------------------------------------------------------------- /app/routes/admin-blog.php: -------------------------------------------------------------------------------- 1 | group('/dashboard', function () use ($app, $container) { 4 | 5 | // Blog Admin 6 | $app->group('/blog', function () use ($app) { 7 | // Main Blog Admin 8 | $app->get('', 'AdminBlog:blog') 9 | ->setName('admin-blog'); 10 | 11 | $app->get('/datatables', 'AdminBlog:datatables') 12 | ->setName('admin-blog-datatables'); 13 | 14 | // Blog Post Actions 15 | $app->group('', function () use ($app) { 16 | // Unpublish Blog 17 | $app->post('/unpublish', 'AdminBlog:blogUnpublish') 18 | ->setName('admin-blog-unpublish'); 19 | // Publish Blog 20 | $app->post('/publish', 'AdminBlog:blogPublish') 21 | ->setName('admin-blog-publish'); 22 | // Edit Blog Post 23 | $app->map(['GET', 'POST'], '/edit[/{post_id}]', 'AdminBlog:blogEdit') 24 | ->setName('admin-blog-edit'); 25 | // Add Blog Post 26 | $app->map(['GET', 'POST'], '/add', 'AdminBlog:blogAdd') 27 | ->setName('admin-blog-add'); 28 | // Delete Blog Post 29 | $app->post('/delete', 'AdminBlog:blogDelete') 30 | ->setName('admin-blog-delete'); 31 | }); 32 | 33 | // Blog Comments 34 | $app->group('/comments', function () use ($app) { 35 | // View Comments 36 | $app->get('', 'AdminBlogComments:comments') 37 | ->setName('admin-blog-comments'); 38 | // Datatables 39 | $app->get('/datatables', 'AdminBlogComments:datatables') 40 | ->setName('admin-blog-comment-datatables'); 41 | $app->get('/{comment_id}', 'AdminBlogComments:commentDetails') 42 | ->setName('admin-blog-comment-details'); 43 | // Unpublish Comment 44 | $app->post('/unpublish', 'AdminBlogComments:commentUnpublish') 45 | ->setName('admin-blog-comment-unpublish'); 46 | // Publish Comment 47 | $app->post('/publish', 'AdminBlogComments:commentPublish') 48 | ->setName('admin-blog-comment-publish'); 49 | // Delte Comment 50 | $app->post('/delete', 'AdminBlogComments:commentDelete') 51 | ->setName('admin-blog-comment-delete'); 52 | }); 53 | 54 | // Blog Replies 55 | $app->group('/replies', function () use ($app) { 56 | 57 | // Unpublish Comment 58 | $app->post('/publish', 'AdminBlogComments:replyUnpublish') 59 | ->setName('admin-blog-reply-unpublish'); 60 | // Publish Comment 61 | $app->post('/unpublish', 'AdminBlogComments:replyPublish') 62 | ->setName('admin-blog-reply-publish'); 63 | // Delte Comment 64 | $app->post('/delete', 'AdminBlogComments:replyDelete') 65 | ->setName('admin-blog-reply-delete'); 66 | }); 67 | 68 | // Blog Categories Actions 69 | $app->group('/categories', function () use ($app) { 70 | // Delete Category 71 | $app->post('/delete', 'AdminBlogCategories:categoriesDelete') 72 | ->setName('admin-blog-categories-delete'); 73 | // Edit Category 74 | $app->map(['GET', 'POST'], '/edit[/{category}]', 'AdminBlogCategories:categoriesEdit') 75 | ->setName('admin-blog-categories-edit'); 76 | // Add Category 77 | $app->post('/add', 'AdminBlogCategories:categoriesAdd') 78 | ->setName('admin-blog-categories-add'); 79 | }); 80 | 81 | // Blog Tag Actions 82 | $app->group('/tags', function () use ($app) { 83 | // Delete Tag 84 | $app->post('/delete', 'AdminBlogTags:tagsDelete') 85 | ->setName('admin-blog-tags-delete'); 86 | // Edit Tag 87 | $app->map(['GET', 'POST'], '/edit[/{tag_id}]', 'AdminBlogTags:tagsEdit') 88 | ->setName('admin-blog-tags-edit'); 89 | // Delete Tag 90 | $app->post('/add', 'AdminBlogTags:tagsAdd') 91 | ->setName('admin-blog-tags-add'); 92 | }); 93 | 94 | // Preview Blog Post 95 | $app->get('/preview[/{slug}]', 'AdminBlog:blogPreview') 96 | ->setName('admin-blog-preview'); 97 | }) 98 | ->add(new Dappur\Middleware\BlogCheck($container)); 99 | }) 100 | ->add(new Dappur\Middleware\Auth($container)) 101 | ->add(new Dappur\Middleware\Admin($container)) 102 | ->add($container->get('csrf')) 103 | ->add(new Dappur\Middleware\TwoFactorAuth($container)) 104 | ->add(new Dappur\Middleware\RouteName($container)); 105 | -------------------------------------------------------------------------------- /app/routes/admin-email.php: -------------------------------------------------------------------------------- 1 | group('/dashboard', function () use ($app, $container) { 3 | // Email Manager 4 | $app->group('/email', function () use ($app) { 5 | $app->map(['GET'], '', 'AdminEmail:email') 6 | ->setName('admin-email'); 7 | 8 | $app->map(['GET'], '/details/{email}', 'AdminEmail:emailDetails') 9 | ->setName('admin-email-details'); 10 | 11 | $app->map(['GET','POST'], '/new', 'AdminEmail:emailNew') 12 | ->setName('admin-email-new'); 13 | 14 | $app->map(['GET'], '/templates', 'AdminEmail:templates') 15 | ->setName('admin-email-template'); 16 | 17 | $app->map(['GET','POST'], '/templates/add', 'AdminEmail:templatesAdd') 18 | ->setName('admin-email-template-add'); 19 | 20 | $app->map(['GET','POST'], '/templates/edit/{template_id}', 'AdminEmail:templatesEdit') 21 | ->setName('admin-email-template-edit'); 22 | 23 | $app->map(['POST'], '/templates/delete', 'AdminEmail:templatesDelete') 24 | ->setName('admin-email-template-delete'); 25 | 26 | $app->map(['POST'], '/test', 'AdminEmail:testEmail') 27 | ->setName('admin-email-test'); 28 | 29 | // Email Ajax 30 | $app->get('/datatables', 'AdminEmail:dataTables') 31 | ->setName('admin-email-datatables'); 32 | 33 | // Email Ajax 34 | $app->get('/search-users', 'AdminEmail:searchUsers') 35 | ->setName('admin-email-search-users'); 36 | }); 37 | }) 38 | ->add(new Dappur\Middleware\Auth($container)) 39 | ->add(new Dappur\Middleware\Admin($container)) 40 | ->add($container->get('csrf')) 41 | ->add(new Dappur\Middleware\TwoFactorAuth($container)) 42 | ->add(new Dappur\Middleware\RouteName($container)); 43 | -------------------------------------------------------------------------------- /app/routes/admin-media.php: -------------------------------------------------------------------------------- 1 | group('/dashboard', function () use ($app, $container) { 4 | 5 | // Media Manager 6 | $app->group('/media', function () use ($app) { 7 | // Media 8 | $app->map(['GET'], '', 'AdminMedia:media') 9 | ->setName('admin-media'); 10 | // Media 11 | $app->map(['POST'], '/folder', 'AdminMedia:mediaFolder') 12 | ->setName('admin-media-folder'); 13 | 14 | $app->map(['POST'], '/folder/new', 'AdminMedia:mediaFolderNew') 15 | ->setName('admin-media-folder-new'); 16 | 17 | $app->map(['POST'], '/upload', 'AdminMedia:mediaUpload') 18 | ->setName('admin-media-upload'); 19 | 20 | $app->map(['POST'], '/delete', 'AdminMedia:mediaDelete') 21 | ->setName('admin-media-delete'); 22 | 23 | $app->map(['GET'], '/cloudinary-sign', 'AdminMedia:cloudinarySign') 24 | ->setName('cloudinary-sign'); 25 | }); 26 | }) 27 | ->add(new Dappur\Middleware\Auth($container)) 28 | ->add(new Dappur\Middleware\Admin($container)) 29 | ->add($container->get('csrf')) 30 | ->add(new Dappur\Middleware\TwoFactorAuth($container)) 31 | ->add(new Dappur\Middleware\RouteName($container)); 32 | -------------------------------------------------------------------------------- /app/routes/admin-menus.php: -------------------------------------------------------------------------------- 1 | group('/dashboard', function () use ($app, $container) { 4 | 5 | // Menu Manager 6 | $app->group('/menus', function () use ($app) { 7 | $app->map(['GET'], '', 'AdminMenus:menus') 8 | ->setName('admin-menus'); 9 | 10 | $app->map(['GET','POST'], '/add', 'AdminMenus:add') 11 | ->setName('admin-menus-add'); 12 | 13 | $app->map(['GET'], '/get', 'AdminMenus:get') 14 | ->setName('admin-menus-get'); 15 | 16 | $app->map(['POST'], '/update', 'AdminMenus:update') 17 | ->setName('admin-menus-update'); 18 | 19 | $app->map(['POST'], '/delete', 'AdminMenus:delete') 20 | ->setName('admin-menus-delete'); 21 | 22 | $app->map(['GET'], '/export', 'AdminMenus:export') 23 | ->setName('admin-menus-export'); 24 | 25 | $app->map(['POST'], '/import', 'AdminMenus:import') 26 | ->setName('admin-menus-import'); 27 | }); 28 | }) 29 | ->add(new Dappur\Middleware\Auth($container)) 30 | ->add(new Dappur\Middleware\Admin($container)) 31 | ->add($container->get('csrf')) 32 | ->add(new Dappur\Middleware\TwoFactorAuth($container)) 33 | ->add(new Dappur\Middleware\RouteName($container)); 34 | -------------------------------------------------------------------------------- /app/routes/admin-oauth.php: -------------------------------------------------------------------------------- 1 | group('/dashboard', function () use ($app, $container) { 4 | 5 | // Oauth Manager 6 | $app->group('/oauth2', function () use ($app) { 7 | $app->map(['GET'], '', 'AdminOauth2:providers') 8 | ->setName('admin-oauth2'); 9 | 10 | $app->map(['GET','POST'], '/add', 'AdminOauth2:oauth2Add') 11 | ->setName('admin-oauth2-add'); 12 | 13 | $app->map(['GET','POST'], '/edit/{provider_id}', 'AdminOauth2:oauth2Edit') 14 | ->setName('admin-oauth2-edit'); 15 | 16 | $app->map(['POST'], '/enable[/login]', 'AdminOauth2:oauth2Enable') 17 | ->setName('admin-oauth2-enable'); 18 | 19 | $app->map(['POST'], '/disable[/login]', 'AdminOauth2:oauth2Disable') 20 | ->setName('admin-oauth2-disable'); 21 | 22 | $app->map(['POST'], '/delete', 'AdminOauth2:oauth2Delete') 23 | ->setName('admin-oauth2-delete'); 24 | }); 25 | }) 26 | ->add(new Dappur\Middleware\Auth($container)) 27 | ->add(new Dappur\Middleware\Admin($container)) 28 | ->add($container->get('csrf')) 29 | ->add(new Dappur\Middleware\TwoFactorAuth($container)) 30 | ->add(new Dappur\Middleware\RouteName($container)); 31 | -------------------------------------------------------------------------------- /app/routes/admin-pages.php: -------------------------------------------------------------------------------- 1 | group('/dashboard', function () use ($app, $container) { 4 | 5 | // Page Manager 6 | $app->group('/pages', function () use ($app) { 7 | $app->map(['GET'], '', 'AdminPages:view') 8 | ->setName('admin-pages'); 9 | 10 | $app->map(['GET','POST'], '/add', 'AdminPages:add') 11 | ->setName('admin-pages-add'); 12 | 13 | $app->map(['GET','POST'], '/edit/{page_id}', 'AdminPages:edit') 14 | ->setName('admin-pages-edit'); 15 | 16 | $app->map(['POST'], '/delete', 'AdminPages:delete') 17 | ->setName('admin-pages-delete'); 18 | 19 | $app->map(['GET'], '/export', 'AdminPages:export') 20 | ->setName('admin-pages-export'); 21 | 22 | $app->map(['POST'], '/import', 'AdminPages:import') 23 | ->setName('admin-pages-import'); 24 | 25 | $app->map(['GET'], '/datatables', 'AdminPages:datatables') 26 | ->setName('admin-pages-datatables'); 27 | }); 28 | }) 29 | ->add(new Dappur\Middleware\Auth($container)) 30 | ->add(new Dappur\Middleware\Admin($container)) 31 | ->add($container->get('csrf')) 32 | ->add(new Dappur\Middleware\TwoFactorAuth($container)) 33 | ->add(new Dappur\Middleware\RouteName($container)); 34 | -------------------------------------------------------------------------------- /app/routes/admin-seo.php: -------------------------------------------------------------------------------- 1 | group('/dashboard', function () use ($app, $container) { 4 | 5 | // SEO Manager 6 | $app->group('/seo', function () use ($app) { 7 | $app->map(['GET'], '', 'AdminSeo:seo') 8 | ->setName('admin-seo'); 9 | $app->map(['GET','POST'], '/add', 'AdminSeo:seoAdd') 10 | ->setName('admin-seo-add'); 11 | 12 | $app->map(['GET','POST'], '/edit/{seo_id}', 'AdminSeo:seoEdit') 13 | ->setName('admin-seo-edit'); 14 | 15 | $app->map(['POST'], '/delete', 'AdminSeo:seoDelete') 16 | ->setName('admin-seo-delete'); 17 | 18 | $app->map(['POST'], '/default', 'AdminSeo:seoDefault') 19 | ->setName('admin-seo-default'); 20 | }); 21 | }) 22 | ->add(new Dappur\Middleware\Auth($container)) 23 | ->add(new Dappur\Middleware\Admin($container)) 24 | ->add($container->get('csrf')) 25 | ->add(new Dappur\Middleware\TwoFactorAuth($container)) 26 | ->add(new Dappur\Middleware\RouteName($container)); 27 | -------------------------------------------------------------------------------- /app/routes/admin-settings.php: -------------------------------------------------------------------------------- 1 | group('/dashboard', function () use ($app, $container) { 4 | 5 | // Global Settings 6 | $app->map(['GET', 'POST'], '/settings/global', 'AdminSettings:settingsGlobal') 7 | ->setName('settings-global'); 8 | $app->post('/settings/add', 'AdminSettings:settingsGlobalAdd') 9 | ->setName('settings-global-add'); 10 | $app->post('/settings/group/add', 'AdminSettings:settingsGlobalAddGroup') 11 | ->setName('settings-global-group-add'); 12 | $app->post('/settings/group/delete', 'AdminSettings:settingsGlobalDeleteGroup') 13 | ->setName('settings-global-group-delete'); 14 | 15 | $app->map(['GET', 'POST'], '/settings/page-settings/{page_name}', 'AdminSettings:settingsPage') 16 | ->setName('settings-page'); 17 | 18 | // View Logs 19 | $app->map(['GET'], '/developer/logs', 'AdminDeveloper:logs') 20 | ->setName('developer-logs'); 21 | 22 | // Get Logs 23 | $app->map(['GET'], '/developer/logs/get', 'AdminDeveloper:get') 24 | ->setName('developer-logs-get'); 25 | 26 | // Export Settings 27 | $app->get('/settings/export', 'AdminSettings:export') 28 | ->setName('settings-export'); 29 | 30 | // Import Settings 31 | $app->post('/settings/import', 'AdminSettings:import') 32 | ->setName('settings-import'); 33 | 34 | // Import Settings 35 | $app->post('/settings/save', 'AdminSettings:save') 36 | ->setName('settings-save'); 37 | 38 | // Delete Setting 39 | $app->post('/settings/delete', 'AdminSettings:delete') 40 | ->setName('settings-delete'); 41 | }) 42 | ->add(new Dappur\Middleware\Auth($container)) 43 | ->add(new Dappur\Middleware\Admin($container)) 44 | ->add($container->get('csrf')) 45 | ->add(new Dappur\Middleware\TwoFactorAuth($container)) 46 | ->add(new Dappur\Middleware\RouteName($container)); 47 | -------------------------------------------------------------------------------- /app/routes/admin-users.php: -------------------------------------------------------------------------------- 1 | group('/dashboard', function () use ($app, $container) { 4 | // Users Routes 5 | $app->group('/users', function () use ($app) { 6 | // User List 7 | $app->get('', 'AdminUsers:users') 8 | ->setName('admin-users'); 9 | // Add New User 10 | $app->map(['GET', 'POST'], '/add', 'AdminUsers:usersAdd') 11 | ->setName('admin-users-add'); 12 | // Edit User 13 | $app->map(['GET', 'POST'], '/edit/{user_id}', 'AdminUsers:usersEdit') 14 | ->setName('admin-users-edit'); 15 | // Delete User 16 | $app->post('/delete', 'AdminUsers:usersDelete') 17 | ->setName('admin-users-delete'); 18 | // Activate User 19 | $app->post('/activate', 'AdminUsers:activate') 20 | ->setName('admin-users-activate'); 21 | // User Ajax 22 | $app->get('/datatables', 'AdminUsers:dataTables') 23 | ->setName('admin-users-datatables'); 24 | 25 | //User Roles 26 | $app->group('/roles', function () use ($app) { 27 | $app->post('/delete', 'AdminRoles:rolesDelete') 28 | ->setName('admin-roles-delete'); 29 | $app->map(['GET', 'POST'], '/edit/{role}', 'AdminRoles:rolesEdit') 30 | ->setName('admin-roles-edit'); 31 | $app->post('/add', 'AdminRoles:rolesAdd') 32 | ->setName('admin-roles-add'); 33 | }); 34 | 35 | // Change User Password 36 | $app->map(['POST'], '/change-password', 'AdminUsers:changePassword') 37 | ->setName('admin-user-change-password'); 38 | 39 | // Change User Password 40 | $app->map(['POST'], '/2fa/disable', 'AdminUsers:disable2fa') 41 | ->setName('admin-users-disable-2fa'); 42 | }); 43 | }) 44 | ->add(new Dappur\Middleware\Auth($container)) 45 | ->add(new Dappur\Middleware\Admin($container)) 46 | ->add($container->get('csrf')) 47 | ->add(new Dappur\Middleware\TwoFactorAuth($container)) 48 | ->add(new Dappur\Middleware\RouteName($container)); 49 | -------------------------------------------------------------------------------- /app/routes/admin.php: -------------------------------------------------------------------------------- 1 | group('/dashboard', function () use ($app, $container) { 4 | 5 | // Dashboard Home 6 | $app->map(['GET', 'POST'], '', 'Admin:dashboard') 7 | ->setName('dashboard'); 8 | // Contact Requests 9 | $app->map(['GET'], '/contact', 'Admin:contact') 10 | ->setName('admin-contact'); 11 | // Contact Requests 12 | $app->map(['GET'], '/contact/datatables', 'Admin:contactDatatables') 13 | ->setName('admin-contact-datatables'); 14 | }) 15 | ->add(new Dappur\Middleware\Auth($container)) 16 | ->add(new Dappur\Middleware\Admin($container)) 17 | ->add($container->get('csrf')) 18 | ->add(new Dappur\Middleware\TwoFactorAuth($container)) 19 | ->add(new Dappur\Middleware\RouteName($container)); 20 | -------------------------------------------------------------------------------- /app/routes/all.php: -------------------------------------------------------------------------------- 1 | group('/', function () use ($app, $container, $settings) { 4 | // Contact 5 | $this->map(['GET', 'POST'], 'contact', 'App:contact') 6 | ->setName('contact'); 7 | // Cron Jobs 8 | $this->map(['GET'], 'cron', 'Cron:run') 9 | ->setName('cron'); 10 | // Oauth 11 | $this->map(['GET'], 'oauth/{slug}', 'Oauth2:oauth2') 12 | ->setName('oauth'); 13 | }) 14 | ->add($container->get('csrf')) 15 | ->add(new Dappur\Middleware\Maintenance($container)) 16 | ->add(new Dappur\Middleware\PageConfig($container)) 17 | ->add(new Dappur\Middleware\Seo($container)) 18 | ->add(new Dappur\Middleware\ProfileCheck($container)) 19 | ->add(new Dappur\Middleware\TwoFactorAuth($container)) 20 | ->add(new Dappur\Middleware\RouteName($container)); 21 | 22 | // Maintenance Mode Bypasses All Middleware 23 | $app->map(['GET'], '/maintenance', 'App:maintenance') 24 | ->setName('maintenance-mode') 25 | ->add(new Dappur\Middleware\PageConfig($container)) 26 | ->add(new Dappur\Middleware\Seo($container)) 27 | ->add(new Dappur\Middleware\RouteName($container)); 28 | 29 | // Assets Bypass All Middleware 30 | $app->map(['GET'], '/asset', 'App:asset') 31 | ->setName('asset'); 32 | 33 | // CSRF Bypasses Middleware 34 | $app->map(['GET'], '/csrf', 'App:csrf') 35 | ->setName('csrf') 36 | ->add($container->get('csrf')); 37 | 38 | // Robots.txt 39 | $app->map(['GET'], '/robots.txt', 'Robots:view') 40 | ->setName('robots'); 41 | -------------------------------------------------------------------------------- /app/routes/auth.php: -------------------------------------------------------------------------------- 1 | group('/', function () { 4 | // Registration 5 | $this->map(['GET', 'POST'], 'register', 'Auth:register') 6 | ->setName('register'); 7 | // Forgot Password 8 | $this->map(['GET', 'POST'], 'forgot-password', 'Auth:forgotPassword') 9 | ->setName('forgot-password'); 10 | // Password Reset 11 | $this->map(['GET', 'POST'], 'reset-password', 'Auth:resetPassword') 12 | ->setName('reset-password'); 13 | // Activation Account 14 | $this->map(['GET', 'POST'], 'activate', 'Auth:activate') 15 | ->setName('activate'); 16 | }) 17 | ->add(new Dappur\Middleware\Guest($container)) 18 | ->add($container->get('csrf')) 19 | ->add(new Dappur\Middleware\Maintenance($container)) 20 | ->add(new Dappur\Middleware\Seo($container)) 21 | ->add(new Dappur\Middleware\RouteName($container)) 22 | ->add(new Dappur\Middleware\PageConfig($container)); 23 | 24 | // Login 25 | $app->map(['GET', 'POST'], '/login', 'Auth:login') 26 | ->setName('login') 27 | ->add(new Dappur\Middleware\Guest($container)) 28 | ->add($container->get('csrf')) 29 | ->add(new Dappur\Middleware\Seo($container)) 30 | ->add(new Dappur\Middleware\RouteName($container)) 31 | ->add(new Dappur\Middleware\PageConfig($container)); 32 | 33 | // Logout 34 | $app->get('/logout', 'Auth:logout')->setName('logout'); 35 | -------------------------------------------------------------------------------- /app/routes/blog.php: -------------------------------------------------------------------------------- 1 | group('/blog', function () use ($app) { 5 | $this->get('[/{page}]', 'Blog:blog') 6 | ->setName('blog'); 7 | 8 | $this->map(['GET', 'POST'], '/{year}/{month}/{day}/{slug}', 'Blog:blogPost') 9 | ->setName('blog-post'); 10 | 11 | $this->get('/author/{username}[/{page}]', 'Blog:blogAuthor') 12 | ->setName('blog-author'); 13 | 14 | $this->get('/tag/{slug}[/{page}]', 'Blog:blogTag') 15 | ->setName('blog-tag'); 16 | 17 | $this->get('/category/{slug}[/{page}]', 'Blog:blogCategory') 18 | ->setName('blog-category'); 19 | }) 20 | ->add($container->get('csrf')) 21 | ->add(new Dappur\Middleware\Maintenance($container)) 22 | ->add(new Dappur\Middleware\BlogCheck($container)) 23 | ->add(new Dappur\Middleware\PageConfig($container)) 24 | ->add(new Dappur\Middleware\Seo($container)) 25 | ->add(new Dappur\Middleware\ProfileCheck($container)) 26 | ->add(new Dappur\Middleware\TwoFactorAuth($container)) 27 | ->add(new Dappur\Middleware\RouteName($container)); 28 | -------------------------------------------------------------------------------- /app/routes/custom.php: -------------------------------------------------------------------------------- 1 | where('status', 1)->get(); 5 | if ($customRoutes->count()) { 6 | $app->group('/', function () use ($app, $customRoutes) { 7 | foreach ($customRoutes as $cRoute) { 8 | $app->get($cRoute->pattern, 'App:customRoute')->setName($cRoute->name); 9 | } 10 | }) 11 | ->add($container->get('csrf')) 12 | ->add(new Dappur\Middleware\Maintenance($container)) 13 | ->add(new Dappur\Middleware\PageConfig($container)) 14 | ->add(new Dappur\Middleware\Seo($container)) 15 | ->add(new Dappur\Middleware\ProfileCheck($container)) 16 | ->add(new Dappur\Middleware\TwoFactorAuth($container)) 17 | ->add(new Dappur\Middleware\RouteName($container)); 18 | } 19 | -------------------------------------------------------------------------------- /app/routes/users.php: -------------------------------------------------------------------------------- 1 | group('/', function () use ($app) { 4 | // User Profile 5 | $app->group('profile', function () use ($app) { 6 | //Profile 7 | $this->map(['GET', 'POST'], '', 'Profile:profile') 8 | ->setName('profile'); 9 | // Check Password 10 | $this->map(['POST'], '/password-check', 'Profile:checkPassword') 11 | ->setName('password-check'); 12 | // Change Password 13 | $this->map(['POST'], '/change-password', 'Profile:changePassword') 14 | ->setName('change-password'); 15 | 16 | $app->group('/2fa', function () use ($app) { 17 | $this->post('[/{validate}]', 'Profile:twoFactor') 18 | ->setName('2fa'); 19 | }); 20 | }); 21 | }) 22 | ->add($container->get('csrf')) 23 | ->add(new Dappur\Middleware\Auth($container)) 24 | ->add(new Dappur\Middleware\Maintenance($container)) 25 | ->add(new Dappur\Middleware\PageConfig($container)) 26 | ->add(new Dappur\Middleware\Seo($container)) 27 | ->add(new Dappur\Middleware\ProfileCheck($container)) 28 | ->add(new Dappur\Middleware\TwoFactorAuth($container)) 29 | ->add(new Dappur\Middleware\RouteName($container)); 30 | 31 | // Incomplete Profile Page 32 | $app->map(['GET','POST'], '/profile/incomplete', 'Profile:profileIncomplete') 33 | ->setName('profile-incomplete') 34 | ->add($container->get('csrf')) 35 | ->add(new Dappur\Middleware\Auth($container)) 36 | ->add(new Dappur\Middleware\Maintenance($container)) 37 | ->add(new Dappur\Middleware\PageConfig($container)) 38 | ->add(new Dappur\Middleware\Seo($container)) 39 | ->add(new Dappur\Middleware\TwoFactorAuth($container)) 40 | ->add(new Dappur\Middleware\RouteName($container)); 41 | 42 | // 2 Factor Authentication 43 | $app->map(['GET', 'POST'], '/2fa/confirm', 'Profile:twoFactorConfirm') 44 | ->setName('2fa-confirm') 45 | ->add($container->get('csrf')) 46 | ->add(new Dappur\Middleware\Auth($container)) 47 | ->add(new Dappur\Middleware\Maintenance($container)) 48 | ->add(new Dappur\Middleware\PageConfig($container)) 49 | ->add(new Dappur\Middleware\Seo($container)) 50 | ->add(new Dappur\Middleware\RouteName($container)); 51 | -------------------------------------------------------------------------------- /app/routes/webhooks.php: -------------------------------------------------------------------------------- 1 | group('/webhooks', function () use ($app, $container, $settings) { 4 | // Contact 5 | $this->map(['POST'], '/mailgun', 'WebhooksMailgun:hook') 6 | ->setName('contact'); 7 | }) 8 | ->add(new Dappur\Middleware\Maintenance($container)); 9 | -------------------------------------------------------------------------------- /app/src/App/App.php: -------------------------------------------------------------------------------- 1 | container = $container; 20 | } 21 | 22 | public function __get($property) 23 | { 24 | return $this->container->get($property); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/App/FileBrowser.php: -------------------------------------------------------------------------------- 1 | base && strlen($this->base)) { 24 | if (strpos($temp, $this->base) !== 0) { 25 | throw new Exception('Path is not inside base ('.$this->base.'): ' . $temp); 26 | } 27 | } 28 | return $temp; 29 | } 30 | 31 | protected function path($pathId) 32 | { 33 | $pathId = str_replace('/', DIRECTORY_SEPARATOR, $pathId); 34 | $pathId = trim($pathId, DIRECTORY_SEPARATOR); 35 | $pathId = $this->real($this->base . DIRECTORY_SEPARATOR . $pathId); 36 | return $pathId; 37 | } 38 | 39 | protected function pathId($path) 40 | { 41 | $path = $this->real($path); 42 | $path = substr($path, strlen($this->base)); 43 | $path = str_replace(DIRECTORY_SEPARATOR, '/', $path); 44 | $path = trim($path, '/'); 45 | return strlen($path) ? $path : '/'; 46 | } 47 | 48 | public function __construct($base) 49 | { 50 | $this->base = $this->real($base); 51 | if (!$this->base) { 52 | throw new Exception('Base directory does not exist'); 53 | } 54 | } 55 | 56 | public function lst($pathId, $withRoot = 0) 57 | { 58 | $dir = $this->path($pathId); 59 | $lst = @scandir($dir); 60 | if (!$lst) { 61 | throw new Exception('Could not list path: ' . $dir); 62 | } 63 | $res = array(); 64 | foreach ($lst as $item) { 65 | if ($item == '.' || $item == '..' || $item === null || $item == '.gitkeep' || $item == '.gitignore') { 66 | continue; 67 | } 68 | $tmp = preg_match('([^ a-zа-я-_0-9.]+)ui', $item); 69 | if ($tmp === false || $tmp === 1) { 70 | continue; 71 | } 72 | if (is_dir($dir . DIRECTORY_SEPARATOR . $item)) { 73 | $res[] = array( 74 | 'text' => $item, 75 | 'children' => true, 76 | 'id' => $this->pathId($dir . DIRECTORY_SEPARATOR . $item), 77 | 'icon' => 'folder' 78 | ); 79 | } 80 | if (!is_dir($dir . DIRECTORY_SEPARATOR . $item)) { 81 | $res[] = array( 82 | 'text' => $item, 83 | 'children' => false, 84 | 'id' => $this->pathId($dir . DIRECTORY_SEPARATOR . $item), 85 | 'type' => 'file', 86 | 'icon' => 'file file-'.substr($item, strrpos($item, '.') + 1) 87 | ); 88 | } 89 | } 90 | if ($withRoot && $this->pathId($dir) === '/') { 91 | $res = array(array( 92 | 'text' => basename($this->base), 93 | 'children' => $res, 94 | 'id' => '/', 95 | 'icon'=>'folder', 96 | 'state' => array('opened' => true, 'disabled' => true) 97 | )); 98 | } 99 | return $res; 100 | } 101 | 102 | public function data($pathId) 103 | { 104 | if (strpos($pathId, ":")) { 105 | $pathId = array_map(array($this, 'id'), explode(':', $pathId)); 106 | return array('type'=>'multiple', 'content'=> 'Multiple selected: ' . implode(' ', $pathId)); 107 | } 108 | $dir = $this->path($pathId); 109 | if (is_dir($dir)) { 110 | return array('type'=>'folder', 'content'=> $pathId); 111 | } 112 | if (is_file($dir)) { 113 | $ext = strpos($dir, '.') !== false ? substr($dir, strrpos($dir, '.') + 1) : ''; 114 | $dat = array('type' => $ext, 'content' => ''); 115 | switch ($ext) { 116 | case 'txt': 117 | case 'text': 118 | case 'md': 119 | case 'js': 120 | case 'json': 121 | case 'css': 122 | case 'html': 123 | case 'htm': 124 | case 'xml': 125 | case 'c': 126 | case 'cpp': 127 | case 'h': 128 | case 'sql': 129 | case 'log': 130 | case 'py': 131 | case 'rb': 132 | case 'htaccess': 133 | case 'php': 134 | $dat['content'] = file_get_contents($dir); 135 | break; 136 | case 'jpg': 137 | case 'jpeg': 138 | case 'gif': 139 | case 'png': 140 | case 'bmp': 141 | $dat['content'] = 'data:'.finfo_file(finfo_open(FILEINFO_MIME_TYPE), $dir). 142 | ';base64,'.base64_encode(file_get_contents($dir)); 143 | break; 144 | default: 145 | $dat['content'] = 'File not recognized: '.$this->pathId($dir); 146 | break; 147 | } 148 | return $dat; 149 | } 150 | throw new Exception('Not a valid selection: ' . $dir); 151 | } 152 | 153 | public function create($pathId, $name, $mkdir = 0) 154 | { 155 | $dir = $this->path($pathId); 156 | if (preg_match('([^ a-zа-я-_0-9.]+)ui', $name) || !strlen($name)) { 157 | throw new Exception('Invalid name: ' . $name); 158 | } 159 | if ($mkdir) { 160 | mkdir($dir . DIRECTORY_SEPARATOR . $name); 161 | } 162 | if (!$mkdir) { 163 | file_put_contents($dir . DIRECTORY_SEPARATOR . $name, ''); 164 | } 165 | return array('id' => $this->pathId($dir . DIRECTORY_SEPARATOR . $name)); 166 | } 167 | 168 | public function rename($pathId, $name) 169 | { 170 | $dir = $this->path($pathId); 171 | if ($dir === $this->base) { 172 | throw new Exception('Cannot rename root'); 173 | } 174 | if (preg_match('([^ a-zа-я-_0-9.]+)ui', $name) || !strlen($name)) { 175 | throw new Exception('Invalid name: ' . $name); 176 | } 177 | $new = explode(DIRECTORY_SEPARATOR, $dir); 178 | array_pop($new); 179 | array_push($new, $name); 180 | $new = implode(DIRECTORY_SEPARATOR, $new); 181 | if ($dir !== $new) { 182 | if (is_file($new) || is_dir($new)) { 183 | throw new Exception('Path already exists: ' . $new); 184 | } 185 | rename($dir, $new); 186 | } 187 | return array('id' => $this->pathId($new)); 188 | } 189 | 190 | public function remove($pathId) 191 | { 192 | $dir = $this->path($pathId); 193 | if ($dir === $this->base) { 194 | throw new Exception('Cannot remove root'); 195 | } 196 | if (is_dir($dir)) { 197 | foreach (array_diff(scandir($dir), array(".", "..")) as $f) { 198 | $this->remove($this->pathId($dir . DIRECTORY_SEPARATOR . $f)); 199 | } 200 | rmdir($dir); 201 | } 202 | if (is_file($dir)) { 203 | unlink($dir); 204 | } 205 | return array('status' => 'OK'); 206 | } 207 | 208 | public function move($pathId, $par) 209 | { 210 | $dir = $this->path($pathId); 211 | $par = $this->path($par); 212 | $new = explode(DIRECTORY_SEPARATOR, $dir); 213 | $new = array_pop($new); 214 | $new = $par . DIRECTORY_SEPARATOR . $new; 215 | rename($dir, $new); 216 | return array('id' => $this->pathId($new)); 217 | } 218 | 219 | public function copy($pathId, $par) 220 | { 221 | $dir = $this->path($pathId); 222 | $par = $this->path($par); 223 | $new = explode(DIRECTORY_SEPARATOR, $dir); 224 | $new = array_pop($new); 225 | $new = $par . DIRECTORY_SEPARATOR . $new; 226 | if (is_file($new) || is_dir($new)) { 227 | throw new Exception('Path already exists: ' . $new); 228 | } 229 | 230 | if (is_dir($dir)) { 231 | mkdir($new); 232 | foreach (array_diff(scandir($dir), array(".", "..")) as $f) { 233 | $this->copy($this->pathId($dir . DIRECTORY_SEPARATOR . $f), $this->pathId($new)); 234 | } 235 | } 236 | if (is_file($dir)) { 237 | copy($dir, $new); 238 | } 239 | return array('id' => $this->pathId($new)); 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /app/src/Controller/Admin/Admin.php: -------------------------------------------------------------------------------- 1 | sentinel->hasPerm('contact.view', 'dashboard')) { 17 | return $check; 18 | } 19 | 20 | return $this->view->render( 21 | $response, 22 | 'contact.twig', 23 | array("contactRequests" => \Dappur\Model\ContactRequests::orderBy('created_at', 'desc')->get()) 24 | ); 25 | } 26 | 27 | public function contactDatatables(Request $request, Response $response) 28 | { 29 | if ($check = $this->sentinel->hasPerm('contact.view', 'dashboard')) { 30 | return $check; 31 | } 32 | 33 | $contactRequests = new \Dappur\Model\ContactRequests; 34 | 35 | $totalData = $contactRequests->count(); 36 | 37 | $totalFiltered = $totalData; 38 | 39 | $limit = $request->getParam('length'); 40 | $start = $request->getParam('start'); 41 | $order = $request->getParam('columns')[$request->getParam('order')[0]['column']]['data']; 42 | $dir = $request->getParam('order')[0]['dir']; 43 | 44 | $contact = $contactRequests->select('id', 'name', 'email', 'phone', 'comment', 'created_at') 45 | ->skip($start) 46 | ->take($limit) 47 | ->orderBy($order, $dir); 48 | 49 | if (!empty($request->getParam('search')['value'])) { 50 | $search = $request->getParam('search')['value']; 51 | 52 | $contact = $contact->where('name', 'LIKE', "%{$search}%") 53 | ->orWhere('email', 'LIKE', "%{$search}%") 54 | ->orWhere('phone', 'LIKE', "%{$search}%") 55 | ->orWhere('comment', 'LIKE', "%{$search}%"); 56 | 57 | $totalFiltered = $contactRequests->where('name', 'LIKE', "%{$search}%") 58 | ->orWhere('email', 'LIKE', "%{$search}%") 59 | ->orWhere('phone', 'LIKE', "%{$search}%") 60 | ->orWhere('comment', 'LIKE', "%{$search}%") 61 | ->count(); 62 | } 63 | 64 | $jsonData = array( 65 | "draw" => intval($request->getParam('draw')), 66 | "recordsTotal" => intval($totalData), 67 | "recordsFiltered" => intval($totalFiltered), 68 | "data" => $contact->get()->toArray() 69 | ); 70 | 71 | return $response->withJSON( 72 | $jsonData, 73 | 200 74 | ); 75 | } 76 | 77 | /** 78 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 79 | */ 80 | public function dashboard(Request $request, Response $response) 81 | { 82 | if ($check = $this->sentinel->hasPerm('dashboard.view')) { 83 | return $check; 84 | } 85 | 86 | if ($request->isPost() && $this->auth->inRole('admin')) { 87 | $files = $request->getUploadedFiles(); 88 | 89 | if (empty($files['sa_cert'])) { 90 | throw new \Exception('Expected a newfile'); 91 | } 92 | 93 | $newFile = $files['sa_cert']; 94 | 95 | move_uploaded_file( 96 | $newFile->file, 97 | __DIR__ . '/../../../../storage/certs/google/analytics-service-account.json' 98 | ); 99 | } 100 | 101 | // Get basic stats 102 | $userCount = new \Dappur\Model\Users; 103 | $userCount = $userCount->count(); 104 | 105 | $commentCount = new \Dappur\Model\BlogPostsComments; 106 | $commentCount = $commentCount->where('status', 0)->count(); 107 | 108 | $replyCount = new \Dappur\Model\BlogPostsReplies; 109 | $replyCount = $replyCount->where('status', 0)->count(); 110 | 111 | $blogCount = new \Dappur\Model\BlogPosts; 112 | $blogCount = $blogCount->count(); 113 | 114 | $contactCount = new \Dappur\Model\ContactRequests; 115 | $contactCount = $contactCount->where('created_at', '>=', \Carbon\Carbon::now()->subDays(30))->count(); 116 | 117 | // Generate Analytics Access Token 118 | $credentialsFilePath = __DIR__ . '/../../../../storage/certs/google/analytics-service-account.json'; 119 | $accessToken = null; 120 | if (file_exists($credentialsFilePath)) { 121 | $client = new \Google_Client(); 122 | $client->setAuthConfig($credentialsFilePath); 123 | $client->addScope('https://www.googleapis.com/auth/analytics.readonly'); 124 | $client->setApplicationName("GoogleAnalytics"); 125 | $client->refreshTokenWithAssertion(); 126 | $token = $client->getAccessToken(); 127 | $accessToken = $token['access_token']; 128 | } 129 | 130 | return $this->view->render( 131 | $response, 132 | 'dashboard.twig', 133 | array( 134 | 'userCount' => $userCount, 135 | 'pendingComments' => $replyCount + $commentCount, 136 | 'blogCount' => $blogCount, 137 | 'contactCount' => $contactCount, 138 | 'accessToken' => $accessToken 139 | ) 140 | ); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /app/src/Controller/Admin/BlogCategories.php: -------------------------------------------------------------------------------- 1 | sentinel->hasPerm('blog_categories.create', 'dashboard', $this->config['blog-enabled'])) { 16 | return $check; 17 | } 18 | 19 | 20 | if ($request->isPost()) { 21 | $this->validator->validate($request, [ 22 | 'category_name' => \Respect\Validation\Validator::length(2, 25)->alpha('\''), 23 | 'category_slug' => \Respect\Validation\Validator::slug() 24 | ]); 25 | 26 | $checkSlug = \Dappur\Model\BlogCategories::where('slug', '=', $request->getParam('category_slug')) 27 | ->first(); 28 | 29 | if ($checkSlug) { 30 | $this->validator->addError('category_slug', 'Slug already in use.'); 31 | } 32 | 33 | if ($this->validator->isValid()) { 34 | $addCategory = new \Dappur\Model\BlogCategories; 35 | $addCategory->name = $request->getParam('category_name'); 36 | $addCategory->slug = $request->getParam('category_slug'); 37 | 38 | if ($addCategory->save()) { 39 | $this->flash('success', 'Category added successfully.'); 40 | return $this->redirect($response, 'admin-blog'); 41 | } 42 | } 43 | $this->flash('danger', 'An error occured while adding this category.'); 44 | } 45 | 46 | return $this->redirect($response, 'admin-blog'); 47 | } 48 | 49 | // Delete Blog Category 50 | public function categoriesDelete(Request $request, Response $response) 51 | { 52 | if ($check = $this->sentinel->hasPerm('blog_categories.delete', 'dashboard', $this->config['blog-enabled'])) { 53 | return $check; 54 | } 55 | $blogCategories = new \Dappur\Model\BlogCategories; 56 | $category = $blogCategories->find($request->getParam('category_id')); 57 | 58 | if (!$category) { 59 | $this->flash('danger', 'Category doesn\'t exist.'); 60 | return $this->redirect($response, 'admin-blog'); 61 | } 62 | 63 | if ($category->delete()) { 64 | $this->flash('success', 'Category has been removed.'); 65 | return $this->redirect($response, 'admin-blog'); 66 | } 67 | 68 | $this->flash('danger', 'There was a problem removing the category.'); 69 | return $this->redirect($response, 'admin-blog'); 70 | } 71 | 72 | // Edit Blog Category 73 | /** @SuppressWarnings(PHPMD.StaticAccess) */ 74 | public function categoriesEdit(Request $request, Response $response, $categoryId) 75 | { 76 | if ($check = $this->sentinel->hasPerm('blog_categories.update', 'dashboard', $this->config['blog-enabled'])) { 77 | return $check; 78 | } 79 | 80 | $blogCategories = new \Dappur\Model\BlogCategories; 81 | $category = $blogCategories->find($categoryId); 82 | 83 | if (!$category) { 84 | $this->flash('danger', 'Sorry, that category was not found.'); 85 | return $response->withRedirect($this->router->pathFor('admin-blog')); 86 | } 87 | 88 | if ($request->isPost()) { 89 | // Get Vars 90 | $categoryName = $request->getParam('category_name'); 91 | $categorySlug = $request->getParam('category_slug'); 92 | 93 | // Validate Data 94 | $validateData = array( 95 | 'category_name' => array( 96 | 'rules' => \Respect\Validation\Validator::length(2, 25)->alpha('\''), 97 | 'messages' => array( 98 | 'length' => 'Must be between 2 and 25 characters.', 99 | 'alpha' => 'Letters only and can contain \'' 100 | ) 101 | ), 102 | 'category_slug' => array( 103 | 'rules' => \Respect\Validation\Validator::slug(), 104 | 'messages' => array( 105 | 'slug' => 'May only contain lowercase letters, numbers and hyphens.' 106 | ) 107 | ) 108 | ); 109 | 110 | $this->validator->validate($request, $validateData); 111 | 112 | // Validate Category Slug 113 | $checkSlug = $category->where('id', '!=', $category->id) 114 | ->where('slug', '=', $categorySlug) 115 | ->get() 116 | ->count(); 117 | if ($checkSlug > 0 && $categorySlug != $category->slug) { 118 | $this->validator->addError('category_slug', 'Category slug is already in use.'); 119 | } 120 | 121 | 122 | if ($this->validator->isValid()) { 123 | if ($category->id == 1) { 124 | $this->flash('danger', 'Cannot edit uncategorized category.'); 125 | return $this->redirect($response, 'admin-blog'); 126 | } 127 | 128 | $category->name = $categoryName; 129 | $category->slug = $categorySlug; 130 | 131 | if ($category->save()) { 132 | $this->flash('success', 'Category has been updated successfully.'); 133 | return $this->redirect($response, 'admin-blog'); 134 | } 135 | } 136 | $this->flash('danger', 'An error occured updating the category.'); 137 | } 138 | 139 | return $this->view->render($response, 'blog-categories-edit.twig', ['category' => $category]); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /app/src/Controller/Admin/BlogTags.php: -------------------------------------------------------------------------------- 1 | sentinel->hasPerm('blog_tags.create', 'dashboard', $this->config['blog-enabled'])) { 16 | return $check; 17 | } 18 | 19 | if ($request->isPost()) { 20 | $tagName = $request->getParam('tag_name'); 21 | $tagSlug = $request->getParam('tag_slug'); 22 | 23 | $this->validator->validate($request, [ 24 | 'tag_name' => \Respect\Validation\Validator::length(2, 25)->alpha('\''), 25 | 'tag_slug' => \Respect\Validation\Validator::slug() 26 | ]); 27 | 28 | $checkSlug = \Dappur\Model\BlogTags::where('slug', '=', $request->getParam('tag_slug'))->get()->count(); 29 | 30 | if ($checkSlug > 0) { 31 | $this->validator->addError('tag_slug', 'Slug already in use.'); 32 | } 33 | 34 | if ($this->validator->isValid()) { 35 | $addTag = new \Dappur\Model\BlogTags; 36 | $addTag->name = $tagName; 37 | $addTag->slug = $tagSlug; 38 | 39 | if ($addTag->save()) { 40 | $this->flash('success', 'Category added successfully.'); 41 | return $this->redirect($response, 'admin-blog'); 42 | } 43 | } 44 | 45 | $this->flash('danger', 'There was a problem adding the tag.'); 46 | return $this->redirect($response, 'admin-blog'); 47 | } 48 | } 49 | 50 | // Delete Blog Tag 51 | public function tagsDelete(Request $request, Response $response) 52 | { 53 | if ($check = $this->sentinel->hasPerm('blog_tags.delete', 'dashboard', $this->config['blog-enabled'])) { 54 | return $check; 55 | } 56 | $blogTags = new \Dappur\Model\BlogTags; 57 | $tag = $blogTags->find($request->getParam('tag_id')); 58 | 59 | 60 | if (!$tag) { 61 | $this->flash('danger', 'Tag doesn\'t exist.'); 62 | return $this->redirect($response, 'admin-blog'); 63 | } 64 | 65 | if ($tag->delete()) { 66 | $this->flash('success', 'Tag has been removed.'); 67 | return $this->redirect($response, 'admin-blog'); 68 | } 69 | 70 | $this->flash('danger', 'There was a problem removing the tag.'); 71 | return $this->redirect($response, 'admin-blog'); 72 | } 73 | 74 | // Edit Blog Tag 75 | /** @SuppressWarnings(PHPMD.StaticAccess) */ 76 | public function tagsEdit(Request $request, Response $response, $tagId) 77 | { 78 | if ($check = $this->sentinel->hasPerm('blog_tags.update', 'dashboard', $this->config['blog-enabled'])) { 79 | return $check; 80 | } 81 | $blogTags = new \Dappur\Model\BlogTags; 82 | $tag = $blogTags->find($tagId); 83 | 84 | if (!$tag) { 85 | $this->flash('danger', 'Tag doesn\'t exist.'); 86 | return $this->redirect($response, 'admin-blog'); 87 | } 88 | 89 | if ($request->isPost()) { 90 | // Get Vars 91 | $tagName = $request->getParam('tag_name'); 92 | $tagSlug = $request->getParam('tag_slug'); 93 | // Validate Data 94 | $validateData = array( 95 | 'tag_name' => array( 96 | 'rules' => \Respect\Validation\Validator::length(2, 25)->alpha('\''), 97 | 'messages' => array( 98 | 'length' => 'Must be between 2 and 25 characters.', 99 | 'alpha' => 'Letters only and can contain \'' 100 | ) 101 | ), 102 | 'tag_slug' => array( 103 | 'rules' => \Respect\Validation\Validator::slug(), 104 | 'messages' => array( 105 | 'slug' => 'May only contain lowercase letters, numbers and hyphens.' 106 | ) 107 | ) 108 | ); 109 | 110 | $this->validator->validate($request, $validateData); 111 | 112 | //Validate Category Slug 113 | $checkSlug = $tag->where('id', '!=', $tagId)->where('slug', '=', $tagSlug)->get()->count(); 114 | if ($checkSlug > 0 && $tagSlug != $tag['slug']) { 115 | $this->validator->addError('tag_slug', 'Category slug is already in use.'); 116 | } 117 | 118 | 119 | if ($this->validator->isValid()) { 120 | $tag->name = $tagName; 121 | $tag->slug = $tagSlug; 122 | 123 | if ($tag->save()) { 124 | $this->flash('success', 'Category has been updated successfully.'); 125 | return $this->redirect($response, 'admin-blog'); 126 | } 127 | 128 | $this->flash('success', 'An unknown error occured.'); 129 | } 130 | } 131 | return $this->view->render($response, 'blog-tags-edit.twig', ['tag' => $tag]); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /app/src/Controller/Admin/Developer.php: -------------------------------------------------------------------------------- 1 | sentinel->hasPerm('settings.developer', 'dashboard')) { 20 | return $check; 21 | } 22 | 23 | if ($request->getParam('operation')) { 24 | $fileSystem = new \Dappur\App\FileBrowser(realpath(dirname(__FILE__) . '/../../../../storage/log')); 25 | $rslt = null; 26 | switch ($request->getParam('operation')) { 27 | case 'get_node': 28 | $node = $request->getParam('id') && 29 | $request->getParam('id') !== '#' ? $request->getParam('id') : '/'; 30 | $rslt = $fileSystem->lst($node, ($request->getParam('id') && 31 | $request->getParam('id') === '#')); 32 | break; 33 | case "get_content": 34 | $node = $request->getParam('id') && 35 | $request->getParam('id') !== '#' ? $request->getParam('id') : '/'; 36 | $rslt = $fileSystem->data($node); 37 | break; 38 | case 'create_node': 39 | $node = $request->getParam('id') && 40 | $request->getParam('id') !== '#' ? $request->getParam('id') : '/'; 41 | $rslt = $fileSystem->create( 42 | $node, 43 | $request->getParam('text') ? $request->getParam('text') : '', 44 | (!$request->getParam('type') || $request->getParam('type') !== 'file') 45 | ); 46 | break; 47 | case 'rename_node': 48 | $node = $request->getParam('id') && 49 | $request->getParam('id') !== '#' ? $request->getParam('id') : '/'; 50 | $rslt = $fileSystem->rename($node, $request->getParam('text') ? $request->getParam('text') : ''); 51 | break; 52 | case 'delete_node': 53 | $node = $request->getParam('id') && 54 | $request->getParam('id') !== '#' ? $request->getParam('id') : '/'; 55 | $rslt = $fileSystem->remove($node); 56 | break; 57 | case 'move_node': 58 | $node = $request->getParam('id') && 59 | $request->getParam('id') !== '#' ? $request->getParam('id') : '/'; 60 | $parn = $request->getParam('parent') && 61 | $request->getParam('parent') !== '#' ? $request->getParam('parent') : '/'; 62 | $rslt = $fileSystem->move($node, $parn); 63 | break; 64 | case 'copy_node': 65 | $node = $request->getParam('id') && 66 | $request->getParam('id') !== '#' ? $request->getParam('id') : '/'; 67 | $parn = $request->getParam('parent') && 68 | $request->getParam('parent') !== '#' ? $request->getParam('parent') : '/'; 69 | $rslt = $fileSystem->copy($node, $parn); 70 | break; 71 | default: 72 | throw new Exception('Unsupported operation: ' . $request->getParam('operation')); 73 | break; 74 | } 75 | $response = $response->write(json_encode($rslt)); 76 | $response = $response->withHeader('Content-Type', 'application/json;charset=utf-8'); 77 | return $response->withStatus(201); 78 | 79 | return false; 80 | } 81 | 82 | return $this->view->render($response, 'developer-logs.twig'); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /app/src/Controller/Admin/Email.php: -------------------------------------------------------------------------------- 1 | container); 19 | $this->email = $email; 20 | } 21 | 22 | public function dataTables(Request $request, Response $response) 23 | { 24 | if ($check = $this->sentinel->hasPerm('email.view', 'dashboard')) { 25 | return $check; 26 | } 27 | 28 | 29 | $totalData = new \Dappur\Model\Emails; 30 | $totalData = $totalData->count(); 31 | 32 | $totalFiltered = $totalData; 33 | 34 | $limit = $request->getParam('length'); 35 | $start = $request->getParam('start'); 36 | $order = $request->getParam('columns')[$request->getParam('order')[0]['column']]['data']; 37 | $dir = $request->getParam('order')[0]['dir']; 38 | 39 | $emails = \Dappur\Model\Emails::select('secure_id', 'id', 'send_to', 'subject', 'created_at') 40 | ->with('recentStatus') 41 | ->skip($start) 42 | ->take($limit) 43 | ->orderBy($order, $dir); 44 | 45 | if (!empty($request->getParam('search')['value'])) { 46 | $search = $request->getParam('search')['value']; 47 | 48 | $emails = $emails->where('send_to', 'LIKE', "%{$search}%") 49 | ->orWhere('subject', 'LIKE', "%{$search}%"); 50 | 51 | $totalFiltered = \Dappur\Model\Emails::where('send_to', 'LIKE', "%{$search}%") 52 | ->orWhere('subject', 'LIKE', "%{$search}%") 53 | ->count(); 54 | } 55 | 56 | $jsonData = array( 57 | "draw" => intval($request->getParam('draw')), 58 | "recordsTotal" => intval($totalData), 59 | "recordsFiltered" => intval($totalFiltered), 60 | "data" => $emails->get()->toArray() 61 | ); 62 | 63 | return $response->withJSON( 64 | $jsonData, 65 | 200 66 | ); 67 | } 68 | 69 | /** 70 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 71 | */ 72 | public function email(Request $request, Response $response) 73 | { 74 | if ($check = $this->sentinel->hasPerm('email.view', 'dashboard')) { 75 | return $check; 76 | } 77 | 78 | $emails = \Dappur\Model\Emails::take(200)->get(); 79 | 80 | return $this->view->render($response, 'emails.twig', array("emails" => $emails)); 81 | } 82 | 83 | /** 84 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 85 | */ 86 | public function searchUsers(Request $request, Response $response) 87 | { 88 | if ($check = $this->sentinel->hasPerm('email.create', 'dashboard')) { 89 | return $check; 90 | } 91 | 92 | $return = new \stdClass(); 93 | $return->status = "error"; 94 | 95 | if (!$request->getParam('search')) { 96 | $return->message = "Search term not defined."; 97 | return $response->withJSON($return, 200, JSON_UNESCAPED_UNICODE); 98 | } 99 | 100 | $users = new \Dappur\Model\Users; 101 | $users = $users->where(function ($query) use ($request) { 102 | $query->where('first_name', 'LIKE', "%{$request->getParam('search')}%") 103 | ->orWhere('last_name', 'LIKE', "%{$request->getParam('search')}%") 104 | ->orWhere('username', 'LIKE', "%{$request->getParam('search')}%") 105 | ->orWhere('email', 'LIKE', "%{$request->getParam('search')}%"); 106 | }); 107 | 108 | if ($users->count() == 0) { 109 | $return->message = "No results."; 110 | return $response->withJSON($return, 200, JSON_UNESCAPED_UNICODE); 111 | } 112 | 113 | $return->status = "success"; 114 | $return->results = $users->get(); 115 | 116 | return $response->withJSON($return, 200, JSON_UNESCAPED_UNICODE); 117 | } 118 | 119 | public function emailDetails(Request $request, Response $response) 120 | { 121 | if ($check = $this->sentinel->hasPerm('email.details', 'dashboard')) { 122 | return $check; 123 | } 124 | 125 | $routeArgs = $request->getAttribute('route')->getArguments(); 126 | 127 | $email = \Dappur\Model\Emails::with('status')->find($routeArgs['email']); 128 | 129 | if (!$email) { 130 | $this->flash('danger', 'There was a problem finding that email in the database.'); 131 | return $this->redirect($response, 'admin-email'); 132 | } 133 | 134 | $user = \Dappur\Model\Users::where('email', $email->send_to)->first(); 135 | 136 | return $this->view->render($response, 'emails-details.twig', array("email" => $email, "user" => $user)); 137 | } 138 | 139 | public function testEmail(Request $request, Response $response) 140 | { 141 | if ($check = $this->sentinel->hasPerm('email.test', 'dashboard')) { 142 | return $check; 143 | } 144 | 145 | $user = $this->auth->check(); 146 | 147 | $email = new \Dappur\Dappurware\Email($this->container); 148 | $email = $email->sendEmail( 149 | array($user->id), 150 | $request->getParam('subject'), 151 | $request->getParam('html') 152 | ); 153 | 154 | return $response->write(json_encode($email), 201); 155 | } 156 | 157 | /** 158 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 159 | */ 160 | public function templates(Request $request, Response $response) 161 | { 162 | if ($check = $this->sentinel->hasPerm('email.templates', 'dashboard')) { 163 | return $check; 164 | } 165 | 166 | $templates = \Dappur\Model\EmailsTemplates::take(200)->get(); 167 | 168 | return $this->view->render($response, 'emails-templates.twig', array("templates" => $templates)); 169 | } 170 | 171 | /** 172 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 173 | */ 174 | public function templatesDelete(Request $request, Response $response) 175 | { 176 | if ($check = $this->sentinel->hasPerm('email.templates', 'dashboard')) { 177 | return $check; 178 | } 179 | 180 | $check = new \Dappur\Model\EmailsTemplates; 181 | $check = $check->find($request->getParam('template_id')); 182 | 183 | if ($check) { 184 | $check->delete(); 185 | $this->flash('success', 'Template has been successfully deleted.'); 186 | return $this->redirect($response, 'admin-email-template'); 187 | } 188 | 189 | $this->flash('danger', 'There was an error deleting the template.'); 190 | return $this->redirect($response, 'admin-email-template'); 191 | } 192 | 193 | public function templatesAdd(Request $request, Response $response) 194 | { 195 | if ($check = $this->sentinel->hasPerm('email.templates', 'dashboard')) { 196 | return $check; 197 | } 198 | 199 | $placeholders = $this->email->getPlaceholders(); 200 | 201 | if ($request->isPost()) { 202 | if ($this->email->addTemplate()) { 203 | $this->flash('success', 'Template has been successfully added.'); 204 | return $this->redirect($response, 'admin-email-template'); 205 | } 206 | $this->flashNow('danger', 'There was an error adding your template.'); 207 | } 208 | 209 | return $this->view->render($response, 'emails-templates-add.twig', array("placeholders" => $placeholders)); 210 | } 211 | 212 | public function templatesEdit(Request $request, Response $response, $templateId) 213 | { 214 | if ($check = $this->sentinel->hasPerm('email.templates', 'dashboard')) { 215 | return $check; 216 | } 217 | 218 | $placeholders = $this->email->getPlaceholders(); 219 | 220 | $template = new \Dappur\Model\EmailsTemplates; 221 | $template = $template->find($templateId); 222 | 223 | if (!$template) { 224 | $this->flash('danger', 'Template not found.'); 225 | return $this->redirect($response, 'admin-email-template'); 226 | } 227 | 228 | if ($request->isPost()) { 229 | if ($this->email->updateTemplate($templateId)) { 230 | $this->flash('success', 'Template has been successfully updated.'); 231 | return $this->redirect($response, 'admin-email-template'); 232 | } 233 | $this->flashNow('danger', 'There was an error updating your template.'); 234 | } 235 | 236 | return $this->view->render( 237 | $response, 238 | 'emails-templates-edit.twig', 239 | array( 240 | "template" => $template, 241 | "placeholders" => $placeholders 242 | ) 243 | ); 244 | } 245 | 246 | /** @SuppressWarnings(PHPMD.StaticAccess) */ 247 | public function emailNew(Request $request, Response $response) 248 | { 249 | if ($check = $this->sentinel->hasPerm('email.create', 'dashboard')) { 250 | return $check; 251 | } 252 | 253 | $placeholders = $this->email->getPlaceholders(); 254 | 255 | if ($request->isPost()) { 256 | // Validate Text Fields 257 | $this->validator->validate( 258 | $request, 259 | array( 260 | 'subject' => array( 261 | 'rules' => \Respect\Validation\Validator::notEmpty(), 262 | 'messages' => array( 263 | 'notEmpty' => 'Cannot be empty.' 264 | ) 265 | ), 266 | 'html' => array( 267 | 'rules' => \Respect\Validation\Validator::notEmpty(), 268 | 'messages' => array( 269 | 'notEmpty' => 'Cannot be empty.' 270 | ) 271 | ) 272 | ) 273 | ); 274 | 275 | // Check send_to 276 | if (empty($request->getParam('send_to'))) { 277 | $this->validator->addError('send_to', 'Please enter an email address.'); 278 | } 279 | 280 | if ($this->validator->isValid()) { 281 | $email = new \Dappur\Dappurware\Email($this->container); 282 | $email = $email->sendEmail( 283 | $request->getParam('send_to'), 284 | $request->getParam('subject'), 285 | $request->getParam('html') 286 | ); 287 | 288 | if ($email['status'] == "success") { 289 | $this->flash('success', 'Email has been successfully sent.'); 290 | return $this->redirect($response, 'admin-email'); 291 | } 292 | 293 | $this->flashNow('danger', 'There was a problem sending your email.'); 294 | } 295 | } 296 | 297 | return $this->view->render( 298 | $response, 299 | 'emails-new.twig', 300 | array( 301 | "placeholders" => $placeholders 302 | ) 303 | ); 304 | } 305 | } 306 | -------------------------------------------------------------------------------- /app/src/Controller/Admin/Menus.php: -------------------------------------------------------------------------------- 1 | sentinel->hasPerm('pages.view', 'dashboard')) { 17 | return $check; 18 | } 19 | 20 | $roles = new \Dappur\Model\Roles; 21 | return $this->view->render( 22 | $response, 23 | 'pages.twig', 24 | [ 25 | "roles" => $roles->get() 26 | ] 27 | ); 28 | } 29 | 30 | /** 31 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 32 | */ 33 | public function get(Request $request, Response $response) 34 | { 35 | $output['result'] = "error"; 36 | $output['message'] = "An unknown error occured"; 37 | 38 | if (!$this->auth->hasAccess('menus.view')) { 39 | $output['message'] = "Permission denied"; 40 | return $response->withJson($output); 41 | } 42 | $menus = new \Dappur\Model\Menus; 43 | $menu = $menus->find($request->getParam('menu_id')); 44 | 45 | if (!$menu) { 46 | $output['message'] = "Menu not found"; 47 | return $response->withJson($output); 48 | } 49 | 50 | $output['result'] = "success"; 51 | unset($output['message']); 52 | $output['menu']['id'] = $menu->id; 53 | $output['menu']['name'] = $menu->name; 54 | $output['menu']['json'] = json_decode($menu->json); 55 | return $response->withJson($output); 56 | } 57 | 58 | /** 59 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 60 | */ 61 | public function update(Request $request, Response $response) 62 | { 63 | $output['result'] = "error"; 64 | $output['message'] = "An unknown error occured"; 65 | 66 | if (!$this->auth->hasAccess('menus.update')) { 67 | $output['message'] = "Permission denied"; 68 | return $response->withJson($output); 69 | } 70 | 71 | $menus = new \Dappur\Model\Menus; 72 | $menu = $menus->find($request->getParam('menu_id')); 73 | 74 | if (!$menu) { 75 | $output['message'] = "Menu not found"; 76 | return $response->withJson($output); 77 | } 78 | 79 | $menu->json = $request->getParam('json'); 80 | if ($menu->save()) { 81 | $output['result'] = "success"; 82 | unset($output['message']); 83 | return $response->withJson($output); 84 | } 85 | 86 | return $response->withJson($output); 87 | } 88 | 89 | /** 90 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 91 | */ 92 | public function menus(Request $request, Response $response) 93 | { 94 | if ($check = $this->sentinel->hasPerm('menus.create', 'dashboard')) { 95 | return $check; 96 | } 97 | 98 | $routes = $this->container->router->getRoutes(); 99 | $routeNames = array(); 100 | foreach ($routes as $route) { 101 | $routeNames[] = $route->getName(); 102 | } 103 | asort($routeNames); 104 | 105 | $roles = new \Dappur\Model\Roles; 106 | $menus = new \Dappur\Model\Menus; 107 | 108 | return $this->view->render( 109 | $response, 110 | 'menus.twig', 111 | [ 112 | "roles" => $roles->get(), 113 | "menus" => $menus->get(), 114 | "routes" => $routeNames, 115 | "configOptions" => \Dappur\Model\Config::where('type_id', 6)->get() 116 | ] 117 | ); 118 | } 119 | 120 | /** 121 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 122 | */ 123 | public function add(Request $request, Response $response) 124 | { 125 | $output['result'] = "error"; 126 | $output['message'] = "An unknown error occured"; 127 | 128 | if (!$this->auth->hasAccess('menus.create')) { 129 | $output['message'] = "Permission denied"; 130 | return $response->withJson($output); 131 | } 132 | 133 | $checkName = \Dappur\Model\Menus::where('name', $request->getParam('name'))->first(); 134 | 135 | if ($checkName) { 136 | $output['message'] = "There is already a menu with that name."; 137 | return $response->withJson($output); 138 | } 139 | 140 | $ins = new \Dappur\Model\Menus; 141 | $ins->name = $request->getParam('name'); 142 | 143 | if ($ins->save()) { 144 | $output['result'] = "success"; 145 | unset($output['message']); 146 | $output['menu']['id'] = $ins->id; 147 | $output['menu']['name'] = $ins->name; 148 | $output['menu']['json'] = json_decode($ins->json); 149 | return $response->withJson($output); 150 | } 151 | 152 | return $response->withJson($output); 153 | } 154 | 155 | /** 156 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 157 | */ 158 | public function delete(Request $request, Response $response) 159 | { 160 | $output['result'] = "error"; 161 | $output['message'] = "An unknown error occured"; 162 | 163 | if (!$this->auth->hasAccess('menus.delete')) { 164 | $output['message'] = "Permission denied"; 165 | return $response->withJson($output); 166 | } 167 | $menus = new \Dappur\Model\Menus; 168 | $menu = $menus->find($request->getParam('menu_id')); 169 | 170 | if (!$menu) { 171 | $output['message'] = "Menu doesn't exist."; 172 | return $response->withJson($output); 173 | } 174 | 175 | if ($menu->id == 1 || $menu->id == 2) { 176 | $output['message'] = "You cannot delete the default menu."; 177 | return $response->withJson($output); 178 | } 179 | 180 | if ($menu->delete()) { 181 | $output['result'] = "success"; 182 | unset($output['message']); 183 | return $response->withJson($output); 184 | } 185 | 186 | return $response->withJson($output); 187 | } 188 | 189 | /** 190 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 191 | */ 192 | public function export(Request $request, Response $response) 193 | { 194 | if ($check = $this->sentinel->hasPerm('menus.view', 'admin-menus')) { 195 | return $check; 196 | } 197 | 198 | if ($request->getparam("all")) { 199 | $menus = new \Dappur\Model\Menus; 200 | $menu = $menus->get(); 201 | } 202 | 203 | if (is_numeric($request->getparam("menu_id"))) { 204 | $menu = \Dappur\Model\Menus::where('id', $request->getParam('menu_id'))->get(); 205 | } 206 | 207 | if (!$menu) { 208 | $this->flash('danger', 'Export unsuccessful. Menu Not Found.'); 209 | return $this->redirect($response, 'admin-menus'); 210 | } 211 | 212 | $final = array(); 213 | $final['framework'] = $this->settings['framework']; 214 | $final['version'] = $this->settings['version']; 215 | $final['menus'] = $menu->toArray(); 216 | 217 | $tempFile = tmpfile(); 218 | fwrite($tempFile, json_encode($final, JSON_UNESCAPED_SLASHES)); 219 | $metaDatas = stream_get_meta_data($tempFile); 220 | $filePath = $metaDatas['uri']; 221 | $fileResponse = new \Dappur\Dappurware\FileResponse; 222 | return $fileResponse->getResponse( 223 | $response, 224 | $filePath, 225 | "menu-dappur" . 226 | "-" . date("Y-m-d-H-i-s") . ".json" 227 | ); 228 | fclose($tempFile); 229 | } 230 | 231 | /** 232 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 233 | */ 234 | public function import(Request $request, Response $response) 235 | { 236 | if ($check = $this->sentinel->hasPerm('menus.import', 'admin-menus')) { 237 | return $check; 238 | } 239 | 240 | $return = new \stdClass(); 241 | $return->status = "error"; 242 | 243 | if (!$request->getUploadedFiles()['import_file']) { 244 | $return->message = "No file detected."; 245 | return $response->withJSON($return, 200, JSON_UNESCAPED_UNICODE); 246 | } 247 | 248 | $file = $request->getUploadedFiles()['import_file']; 249 | 250 | $json = $file->getStream(); 251 | 252 | if (!$this->isJson($json)) { 253 | $return->message = "error - not a valid json file"; 254 | return $response->withJSON($return, 200, JSON_UNESCAPED_UNICODE); 255 | } 256 | $overwrite = false; 257 | if ($request->getParam('overwrite')) { 258 | $overwrite = true; 259 | } 260 | 261 | $import = $this->processImport($json, $overwrite); 262 | 263 | if ($import->status) { 264 | $return->status = "success"; 265 | $this->flash('success', 'Manu imported successfully'); 266 | } 267 | 268 | if (!$import->status) { 269 | $return->message = $import->message; 270 | } 271 | 272 | return $response->withJSON($return, 200, JSON_UNESCAPED_UNICODE); 273 | } 274 | 275 | public function processImport($json, $overwrite = 0) 276 | { 277 | $decoded = json_decode($json); 278 | 279 | // Create Return Object 280 | $return = new \stdClass(); 281 | $return->status = false; 282 | 283 | if (!$decoded->framework || $decoded->framework != $this->settings['framework']) { 284 | $return->message = "Framework mismatch."; 285 | return $return; 286 | } 287 | 288 | if (!$decoded->version || $decoded->version != $this->settings['version']) { 289 | $return->message = "Version mismatch."; 290 | return $return; 291 | } 292 | 293 | foreach ($decoded->menus as $value) { 294 | $this->importMenu($value, $overwrite); 295 | } 296 | 297 | $return->status = true; 298 | return $return; 299 | } 300 | 301 | private function importMenu($value, $overwrite = 0) 302 | { 303 | // Check if Exists 304 | $menu = \Dappur\Model\Menus::where('id', $value->id)->first(); 305 | 306 | // Update Group if Overwrite 307 | if ($overwrite && $menu) { 308 | $menu->name = $value->name; 309 | $menu->json = $value->json; 310 | $menu->save(); 311 | } 312 | 313 | if (!$menu) { 314 | // Create Group 315 | $menu = new \Dappur\Model\Menus; 316 | if (isset($value->id)) { 317 | $menu->id = $value->id; 318 | } 319 | $menu->name = $value->name; 320 | $menu->json = $value->json; 321 | $menu->save(); 322 | } 323 | return $menu; 324 | } 325 | 326 | private function isJson($string) 327 | { 328 | json_decode($string); 329 | return (json_last_error() == JSON_ERROR_NONE); 330 | } 331 | } 332 | -------------------------------------------------------------------------------- /app/src/Controller/Admin/Roles.php: -------------------------------------------------------------------------------- 1 | sentinel->hasPerm('role.create', 'dashboard')) { 17 | return $check; 18 | } 19 | 20 | if ($request->isPost()) { 21 | $this->validator->validate($request, [ 22 | 'role_name' => \Respect\Validation\Validator::length(2, 25)->alpha('\''), 23 | 'role_slug' => \Respect\Validation\Validator::slug() 24 | ]); 25 | 26 | if ($this->validator->isValid()) { 27 | $role = $this->auth->getRoleRepository()->createModel()->create([ 28 | 'name' => $request->getParam('role_name'), 29 | 'slug' => $request->getParam('role_slug'), 30 | 'permissions' => [] 31 | ]); 32 | 33 | if ($role) { 34 | $this->flash('success', 'Role has been successfully added.'); 35 | return $this->redirect($response, 'admin-users'); 36 | } 37 | } 38 | } 39 | 40 | $this->flash('danger', 'There was a problem adding the role.'); 41 | return $this->redirect($response, 'admin-users'); 42 | } 43 | 44 | public function rolesDelete(Request $request, Response $response) 45 | { 46 | if ($check = $this->sentinel->hasPerm('role.delete', 'dashboard')) { 47 | return $check; 48 | } 49 | 50 | $role = \Dappur\Model\Roles::find($request->getParam('role_id')); 51 | 52 | if ($role && $role->id != 1) { 53 | \Dappur\Model\RoleUsers::where('role_id', '=', $request->getParam('role_id'))->delete(); 54 | 55 | $removeRole = \Dappur\Model\Roles::find($request->getParam('role_id')); 56 | if ($removeRole->delete()) { 57 | $this->flash('success', 'Role has been removed.'); 58 | return $this->redirect($response, 'admin-users'); 59 | } 60 | } 61 | 62 | $this->flash('danger', 'There was a problem removing the role.'); 63 | return $this->redirect($response, 'admin-users'); 64 | } 65 | 66 | public function rolesEdit(Request $request, Response $response, $roleid) 67 | { 68 | if ($check = $this->sentinel->hasPerm('role.update', 'dashboard')) { 69 | return $check; 70 | } 71 | 72 | $role = \Dappur\Model\Roles::find($roleid); 73 | 74 | if ($role) { 75 | if ($request->isPost()) { 76 | // Validate Data 77 | $validateData = array( 78 | 'role_name' => array( 79 | 'rules' => \Respect\Validation\Validator::length(2, 25)->alpha('\''), 80 | 'messages' => array( 81 | 'length' => 'Must be between 2 and 25 characters.', 82 | 'alpha' => 'Letters only and can contain \'' 83 | ) 84 | ), 85 | 'role_slug' => array( 86 | 'rules' => \Respect\Validation\Validator::slug(), 87 | 'messages' => array( 88 | 'slug' => 'May only contain lowercase letters, numbers and hyphens.' 89 | ) 90 | ) 91 | ); 92 | 93 | $this->validator->validate($request, $validateData); 94 | 95 | //Validate Role Name 96 | $checkName = $role->where('id', '!=', $role->id) 97 | ->where('name', '=', $role->name) 98 | ->first(); 99 | if ($checkName) { 100 | $this->validator->addError('role_name', 'Role name is already in use.'); 101 | } 102 | 103 | //Validate Role Name 104 | $checkSlug = $role->where('id', '!=', $role->id) 105 | ->where('slug', '=', $role->name) 106 | ->first(); 107 | if ($checkSlug) { 108 | $this->validator->addError('role_slug', 'Role slug is already in use.'); 109 | } 110 | 111 | // Create Permissions Array 112 | $permissionsArray = array(); 113 | foreach ($request->getParam('perm_name') as $pkey => $pvalue) { 114 | $val = false; 115 | if ($request->getParam('perm_value')[$pkey] == "true") { 116 | $val = true; 117 | } 118 | $permissionsArray[$pvalue] = $val; 119 | } 120 | 121 | 122 | 123 | if ($this->validator->isValid()) { 124 | $updateRole = $role; 125 | $updateRole->name = $request->getParam('role_name'); 126 | $updateRole->slug = $request->getParam('role_slug'); 127 | $updateRole->save(); 128 | 129 | $rolePerms = $this->auth->findRoleById($updateRole->id); 130 | $rolePerms->permissions = $permissionsArray; 131 | $rolePerms->save(); 132 | 133 | $this->flash('success', 'Role has been updated successfully.'); 134 | return $this->redirect($response, 'admin-users'); 135 | } 136 | } 137 | 138 | return $this->view->render($response, 'roles-edit.twig', ['role' => $role]); 139 | } 140 | 141 | $this->flash('danger', 'Sorry, that role was not found.'); 142 | return $response->withRedirect($this->router->pathFor('admin-users')); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /app/src/Controller/Admin/Seo.php: -------------------------------------------------------------------------------- 1 | sentinel->hasPerm('seo.view', 'dashboard')) { 17 | return $check; 18 | } 19 | $seo = new \Dappur\Model\Seo; 20 | return $this->view->render($response, 'seo.twig', array("seo" => $seo->get())); 21 | } 22 | 23 | /** 24 | * @SuppressWarnings(PHPMD.CyclomaticComplexity) At threshold 25 | * @SuppressWarnings(PHPMD.StaticAccess) 26 | */ 27 | public function seoAdd(Request $request, Response $response) 28 | { 29 | if ($check = $this->sentinel->hasPerm('seo.create', 'dashboard')) { 30 | return $check; 31 | } 32 | 33 | $availableRoutes = $this->getAllRoutes(); 34 | 35 | if ($request->isPost()) { 36 | // Validate Form Data 37 | $validateData = array( 38 | 'title' => array( 39 | 'rules' => \Respect\Validation\Validator::notEmpty()->length(10, 60), 40 | 'messages' => array( 41 | 'notEmpty' => 'Title is required.', 42 | 'length' => 'SEO titles need to be between 10-60 characters.' 43 | ) 44 | ), 45 | 'description' => array( 46 | 'rules' => \Respect\Validation\Validator::notEmpty()->length(50, 300), 47 | 'messages' => array( 48 | 'notEmpty' => 'Title is required.', 49 | 'length' => 'SEO descriptions need to be between 50-300 characters.' 50 | ) 51 | ), 52 | 'featured_image' => array( 53 | 'rules' => \Respect\Validation\Validator::notEmpty(), 54 | 'messages' => array( 55 | 'notEmpty' => 'Featured image is required.' 56 | ) 57 | ) 58 | ); 59 | $this->validator->validate($request, $validateData); 60 | 61 | 62 | // Validate Page 63 | $pageAvailable = false; 64 | foreach ($availableRoutes as $arvalue) { 65 | if ($arvalue['name'] == $request->getParam('page')) { 66 | $pageAvailable = true; 67 | } 68 | } 69 | 70 | if (!$pageAvailable) { 71 | $this->validator->addError('page', 'Page is not available for SEO optimization.'); 72 | } 73 | 74 | if ($this->validator->isValid()) { 75 | $add = new \Dappur\Model\Seo; 76 | $add->page = $request->getParam('page'); 77 | $add->title = $request->getParam('title'); 78 | $add->description = $request->getParam('description'); 79 | if ($request->getParam('featured_image') != "") { 80 | $add->image = $request->getParam('featured_image'); 81 | } 82 | if ($request->getParam('video') != "") { 83 | $add->video = $request->getParam('video'); 84 | } 85 | 86 | if ($add->save()) { 87 | $this->flash('success', 'SEO settings have been saved successfully.'); 88 | return $this->redirect($response, 'admin-seo'); 89 | } 90 | 91 | $this->flashNow('danger', 'There was an error saving your settings. Please try again.'); 92 | return $this->redirect($response, 'admin-seo'); 93 | } 94 | } 95 | 96 | return $this->view->render($response, 'seo-add.twig', array("availableRoutes" => $availableRoutes)); 97 | } 98 | 99 | public function seoDelete(Request $request, Response $response) 100 | { 101 | if ($check = $this->sentinel->hasPerm('seo.delete', 'dashboard')) { 102 | return $check; 103 | } 104 | $seo = new \Dappur\Model\Seo; 105 | $seo = $seo->find($request->getParam('seo_id')); 106 | 107 | if (!$seo) { 108 | $this->flash('danger', 'Could not find SEO record.'); 109 | return $this->redirect($response, 'admin-seo'); 110 | } 111 | 112 | if ($seo->default) { 113 | $this->flash('danger', 'You canot delete the default SEO configuration.'); 114 | return $this->redirect($response, 'admin-seo'); 115 | } 116 | 117 | if ($seo->delete()) { 118 | $this->flash('success', 'SEO configuration was deleted successfully.'); 119 | return $this->redirect($response, 'admin-seo'); 120 | } 121 | 122 | $this->flash('danger', 'There was an error deleting that SEO record.'); 123 | return $this->redirect($response, 'admin-seo'); 124 | } 125 | 126 | public function seoDefault(Request $request, Response $response) 127 | { 128 | if ($check = $this->sentinel->hasPerm('seo.default')) { 129 | return $check; 130 | } 131 | $seo = new \Dappur\Model\Seo; 132 | $seo = $seo->find($request->getParam('seo_id')); 133 | 134 | if (!$seo) { 135 | $this->flash('danger', 'Could not find SEO record.'); 136 | return $this->redirect($response, 'admin-seo'); 137 | } 138 | 139 | if ($seo->default) { 140 | $this->flash('info', 'This is already the default SEO configuration.'); 141 | return $this->redirect($response, 'admin-seo'); 142 | } 143 | 144 | 145 | \Dappur\Model\Seo::where('default', 1)->update(['default' => 0]); 146 | $seo->default = 1; 147 | if ($seo->save()) { 148 | $this->flash('success', 'New default SEO configruation was set.'); 149 | return $this->redirect($response, 'admin-seo'); 150 | } 151 | 152 | $this->flash('danger', 'There was an error making that SEO record default.'); 153 | return $this->redirect($response, 'admin-seo'); 154 | } 155 | 156 | /** @SuppressWarnings(PHPMD.StaticAccess) */ 157 | public function seoEdit(Request $request, Response $response) 158 | { 159 | if ($check = $this->sentinel->hasPerm('seo.update', 'dashboard')) { 160 | return $check; 161 | } 162 | $seo = new \Dappur\Model\Seo; 163 | $seo = $seo->find($request->getAttribute('route')->getArgument('seo_id')); 164 | 165 | if (!$seo) { 166 | $this->flash('danger', 'Could not find SEO record.'); 167 | return $this->redirect($response, 'admin-seo'); 168 | } 169 | 170 | $availableRoutes = $this->getAllRoutes(false); 171 | $routeInfo = array(); 172 | foreach ($availableRoutes as $arvalue) { 173 | if ($arvalue['name'] == $seo->page) { 174 | $routeInfo = array("name" => $arvalue['name'], "pattern" => $arvalue['pattern']); 175 | } 176 | } 177 | 178 | if ($request->isPost()) { 179 | // Validate Form Data 180 | $validateData = array( 181 | 'title' => array( 182 | 'rules' => \Respect\Validation\Validator::notEmpty()->length(10, 60), 183 | 'messages' => array( 184 | 'notEmpty' => 'Title is required.', 185 | 'length' => 'SEO titles need to be between 10-60 characters.' 186 | ) 187 | ), 188 | 'description' => array( 189 | 'rules' => \Respect\Validation\Validator::notEmpty()->length(50, 300), 190 | 'messages' => array( 191 | 'notEmpty' => 'Title is required.', 192 | 'length' => 'SEO descriptions need to be between 50-300 characters.' 193 | ) 194 | ), 195 | 'featured_image' => array( 196 | 'rules' => \Respect\Validation\Validator::notEmpty(), 197 | 'messages' => array( 198 | 'notEmpty' => 'Featured image is required.' 199 | ) 200 | ) 201 | ); 202 | $this->validator->validate($request, $validateData); 203 | 204 | 205 | if ($this->validator->isValid()) { 206 | $seo->title = $request->getParam('title'); 207 | $seo->description = $request->getParam('description'); 208 | $seo->image = $request->getParam('featured_image'); 209 | $seo->video = $request->getParam('video'); 210 | 211 | if ($seo->save()) { 212 | $this->flash('success', 'SEO settings have been saved successfully for ' . $routeInfo['pattern']); 213 | return $this->redirect($response, 'admin-seo'); 214 | } 215 | 216 | $this->flashNow( 217 | 'danger', 218 | 'There was an error saving the settings for ' . $routeInfo['pattern'] . '. Please try again.' 219 | ); 220 | } 221 | } 222 | 223 | return $this->view->render($response, 'seo-edit.twig', array("seo" => $seo, "route_info" => $routeInfo)); 224 | } 225 | 226 | private function getAllRoutes($available = null) 227 | { 228 | $routes = $this->container->router->getRoutes(); 229 | $allRoutes = array(); 230 | 231 | $existing = []; 232 | if ($available) { 233 | $existing = \Dappur\Model\Seo::select('page')->get()->pluck('page')->toArray(); 234 | } 235 | 236 | $excludePages = array("blog-post","deploy","asset","csrf","logout","oauth","profile","profile-incomplete"); 237 | 238 | foreach ($routes as $route) { 239 | // Do not include pages that are pre optimized or unnecessary/non-GET 240 | if (strpos($route->getPattern(), '/dashboard') !== false || 241 | in_array($route->getName(), $excludePages) || 242 | in_array($route->getName(), $existing) || 243 | !in_array("GET", $route->getMethods())) { 244 | continue; 245 | } 246 | 247 | $allRoutes[] = array("name" => $route->getName(), "pattern" => $route->getPattern()); 248 | } 249 | usort($allRoutes, function ($first, $second) { 250 | return strcmp($first["name"], $second["name"]); 251 | }); 252 | 253 | return $allRoutes; 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /app/src/Controller/App.php: -------------------------------------------------------------------------------- 1 | getParam('path')); 14 | $assetPath = realpath(__DIR__ . "/../../views/" . str_replace("../", "", $assetPath)); 15 | 16 | // If file doesn't exist 17 | if (!is_file($assetPath)) { 18 | throw new \Slim\Exception\NotFoundException($request, $response); 19 | } 20 | 21 | // If file is in theme root folder 22 | $regex = '#'.preg_quote($baseThemePath).'(.*)'.preg_quote(DIRECTORY_SEPARATOR).'(.*)#'; 23 | preg_match($regex, $assetPath, $gotoUrl); 24 | if (substr_count($gotoUrl[1], DIRECTORY_SEPARATOR) < 2) { 25 | throw new \Slim\Exception\NotFoundException($request, $response); 26 | } 27 | 28 | // Return file 29 | $fileResponse = new \Dappur\Dappurware\FileResponse; 30 | return $fileResponse->getResponse($response, $assetPath); 31 | } 32 | 33 | /** @SuppressWarnings(PHPMD.StaticAccess) */ 34 | public function contact(Request $request, Response $response) 35 | { 36 | if ($request->isPost()) { 37 | // Validate Form Data 38 | $validateData = array( 39 | 'name' => array( 40 | 'rules' => \Respect\Validation\Validator::length(2, 64)->alnum('\''), 41 | 'messages' => array( 42 | 'length' => 'Must be between 2 and 64 characters.', 43 | 'alnum' => 'Alphanumeric and can contain \'' 44 | ) 45 | ), 46 | 'email' => array( 47 | 'rules' => \Respect\Validation\Validator::email(), 48 | 'messages' => array( 49 | 'email' => 'Enter a valid email.', 50 | ) 51 | ), 52 | 'phone' => array( 53 | 'rules' => \Respect\Validation\Validator::phone(), 54 | 'messages' => array( 55 | 'phone' => 'Enter a valid phone number.' 56 | ) 57 | ), 58 | 'comment' => array( 59 | 'rules' => \Respect\Validation\Validator::alnum('\'!@#$%^&:",.?/'), 60 | 'messages' => array( 61 | 'alnum' => 'Text and punctuation only.', 62 | ) 63 | ) 64 | ); 65 | $this->validator->validate($request, $validateData); 66 | 67 | if ($this->config['recaptcha-enabled']) { 68 | // Validate Recaptcha 69 | $recaptcha = new \Dappur\Dappurware\Recaptcha($this->container); 70 | $recaptcha = $recaptcha->validate($request->getParam('g-recaptcha-response')); 71 | if (!$recaptcha) { 72 | $this->validator->addError('recaptcha', 'Recaptcha was invalid.'); 73 | } 74 | } 75 | 76 | if ($this->validator->isValid()) { 77 | $add = new \Dappur\Model\ContactRequests; 78 | $add->name = $request->getParam("name"); 79 | $add->email = $request->getParam("email"); 80 | $add->phone = $request->getParam("phone"); 81 | $add->comment = $request->getParam("comment"); 82 | 83 | if ($add->save()) { 84 | if ($this->container->pageConfig['contact-send-email']) { 85 | $sendTo = array($request->getParam('email')); 86 | $confirmEmail = $this->container->pageConfig['contact-confirmation']; 87 | 88 | if (filter_var($confirmEmail, FILTER_VALIDATE_EMAIL)) { 89 | $sendTo[] = $confirmEmail; 90 | } 91 | 92 | $sendEmail = new \Dappur\Dappurware\Email($this->container); 93 | $sendEmail = $sendEmail->sendTemplate( 94 | $sendTo, 95 | 'contact-confirmation', 96 | array( 97 | 'name' => $request->getParam('name'), 98 | 'phone' => $request->getParam('phone'), 99 | 'comment' => $request->getParam('comment') 100 | ) 101 | ); 102 | } 103 | 104 | $this->flash('success', 'Your contact request has been submitted successfully.'); 105 | return $this->redirect($response, 'contact'); 106 | } 107 | } 108 | 109 | $this->flashNow( 110 | 'danger', 111 | 'An unknown error occured. Please try again or email us at: ' . 112 | $this->config['contact-email'] 113 | ); 114 | } 115 | 116 | return $this->view->render($response, 'contact.twig', array("requestParams" => $request->getParams())); 117 | } 118 | 119 | /** 120 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 121 | */ 122 | public function csrf(Request $request, Response $response) 123 | { 124 | $csrf = array( 125 | "name_key" => $this->csrf->getTokenNameKey(), 126 | "name" => $this->csrf->getTokenName(), 127 | "value_key" => $this->csrf->getTokenValueKey(), 128 | "value" => $this->csrf->getTokenValue()); 129 | 130 | echo json_encode($csrf); 131 | } 132 | 133 | /** 134 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 135 | */ 136 | public function home(Request $request, Response $response) 137 | { 138 | return $this->view->render($response, 'home.twig'); 139 | } 140 | 141 | /** 142 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 143 | * @SuppressWarnings(PHPMD.CyclomaticComplexity) 144 | * @SuppressWarnings(PHPMD.NPathComplexity) 145 | */ 146 | public function customRoute(Request $request, Response $response) 147 | { 148 | $routeName = $request->getAttribute('route')->getName(); 149 | $route = \Dappur\Model\Routes::where('name', $routeName) 150 | ->with('roles') 151 | ->where('status', 1) 152 | ->first(); 153 | 154 | if (!$route) { 155 | throw new NotFoundException($request, $response); 156 | } 157 | 158 | // Check for If Permissions are set 159 | if ((($route->permission || $route->roles->count() > 0) && !$this->auth->check())) { 160 | $this->flash('warning', 'You must be logged in to access this page.'); 161 | return $response->withRedirect($this->router->pathFor('login', array(), array('redirect' => $route->name))); 162 | } 163 | 164 | // Check For permission if logged in and set 165 | if ($route->permission && $this->auth->check()) { 166 | return $this->sentinel->hasPerm($route->permission); 167 | } 168 | 169 | // Check for role if logged in and set 170 | if ($route->roles->count() > 0 && $this->auth->check()) { 171 | $userRoles = $this->auth->check()->roles->pluck('id')->toArray(); 172 | $access = false; 173 | foreach ($route->roles as $rval) { 174 | if (in_array($rval->id, $userRoles)) { 175 | $access = true; 176 | } 177 | } 178 | 179 | if (!$access) { 180 | $this->flash('danger', 'You do not have permission to access that page.'); 181 | return $this->redirect($response, 'home'); 182 | } 183 | } 184 | 185 | return $this->view->render($response, 'custom-route.twig', array('route' => $route)); 186 | } 187 | 188 | /** 189 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 190 | */ 191 | public function maintenance(Request $request, Response $response) 192 | { 193 | return $this->view->render($response, 'maintenance.twig'); 194 | } 195 | 196 | /** 197 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 198 | */ 199 | public function terms(Request $request, Response $response) 200 | { 201 | return $this->view->render($response, 'terms.twig'); 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /app/src/Controller/Controller.php: -------------------------------------------------------------------------------- 1 | container = $container; 17 | $this->sentinel = new \Dappur\Dappurware\Sentinel($this->container); 18 | } 19 | 20 | public function redirect(Response $response, $route, array $params = array()) 21 | { 22 | return $response->withRedirect($this->router->pathFor($route, $params)); 23 | } 24 | 25 | public function redirectTo(Response $response, $url) 26 | { 27 | return $response->withRedirect($url); 28 | } 29 | 30 | public function json(Response $response, $data, $status = 200) 31 | { 32 | return $response->withJson($data, $status); 33 | } 34 | 35 | public function write(Response $response, $data, $status = 200) 36 | { 37 | return $response->withStatus($status)->getBody()->write($data); 38 | } 39 | 40 | public function flash($name, $message) 41 | { 42 | $this->flash->addMessage($name, $message); 43 | } 44 | 45 | public function flashNow($name, $message) 46 | { 47 | $this->flash->addMessageNow($name, $message); 48 | } 49 | 50 | public function notFoundException(Request $request, Response $response) 51 | { 52 | return new \Slim\Exception\NotFoundException($request, $response); 53 | } 54 | 55 | public function __get($property) 56 | { 57 | return $this->container->get($property); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /app/src/Controller/Cron.php: -------------------------------------------------------------------------------- 1 | getParam('token') || 19 | $request->getparam('token') != 20 | $this->settings['cron']['token'] || 21 | $this->settings['cron']['token'] == "") { 22 | throw new \Slim\Exception\NotFoundException($request, $response); 23 | } 24 | 25 | $jobby = new \Jobby\Jobby(); 26 | // Sample Job 27 | $jobby->add('SampleCron', [ 28 | /* 'command' => 'ls', //Run a shell command */ 29 | // Run a PHP Closure 30 | 'closure' => function () { 31 | $text = "I'm a function!\n"; 32 | echo $text; 33 | return true; 34 | }, 35 | // Define Schedule 36 | 'schedule' => '* * * * *', 37 | // This will add a log for this cron job 38 | 'output' => __DIR__ . '/../../../storage/log/cron/sample.log', 39 | // Enable/Dsable Cron Job 40 | 'enabled' => true 41 | ]); 42 | 43 | $jobby->run(); 44 | 45 | if ($jobby->getJobs()) { 46 | return "1"; 47 | } 48 | 49 | throw new \Exception("Cron Failed", 500); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/Controller/Oauth2.php: -------------------------------------------------------------------------------- 1 | oauthUtils = new \Dappur\Dappurware\Oauth2($container); 15 | } 16 | 17 | /** 18 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 19 | */ 20 | public function oauth2(Request $request, Response $response) 21 | { 22 | $slug = $request->getAttribute('route')->getArgument('slug'); 23 | 24 | $provider = \Dappur\Model\Oauth2Providers::where('slug', $slug)->where('status', 1)->first(); 25 | 26 | if (!$provider) { 27 | $this->flash->addMessage('danger', 'Oauth2 provider not found.'); 28 | return $this->oauthRedirect(); 29 | } 30 | if ($request->getParam('error')) { 31 | $this->flash->addMessage('danger', 'Oauth2 Error: ' . $request->getParam('error_description')); 32 | return $this->oauthRedirect(); 33 | } 34 | 35 | // When Github redirects the user back here, there will be a "code" and "state" parameter in the query string 36 | if ($request->getParam('code') || $request->getParam('oauth_token')) { 37 | $token = $this->oauthUtils->getAccessToken($provider); 38 | // Redirect to login if oauth error 39 | if (isset($token->error)) { 40 | $this->flash->addMessage('danger', 'Oauth2 Error: ' . $token->error->message); 41 | return $this->oauthRedirect(); 42 | } 43 | 44 | // Get and Process User Data 45 | if (isset($token->access_token)) { 46 | $userInfo = $this->oauthUtils->getUserInfo($token, $provider); 47 | 48 | if ($userInfo['uid']) { 49 | return $this->processOauthUser($userInfo, $provider); 50 | } 51 | 52 | $this->flash->addMessage( 53 | 'danger', 54 | 'Oauth2 Error: ' . "An unknown error occured logging you in with " . $provider->name 55 | ); 56 | return $this->oauthRedirect(); 57 | } 58 | 59 | $this->flash->addMessage( 60 | 'danger', 61 | 'Oauth2 Error: ' . "An unknown error occured logging you in with " . $provider->name 62 | ); 63 | return $this->oauthRedirect(); 64 | } 65 | } 66 | 67 | private function processOauthUser($userInfo, $provider) 68 | { 69 | 70 | $oauthUser = \Dappur\Model\Oauth2Users::where('uid', $userInfo['uid']) 71 | ->where('provider_id', $provider->id) 72 | ->first(); 73 | 74 | // if exists log in and update record 75 | if ($oauthUser) { 76 | return $this->updateOauth2User($userInfo, $provider); 77 | } 78 | 79 | if ($this->auth->check()) { 80 | // Handle if logged in 81 | return $this->createOauth2User($this->auth->check(), $userInfo, $provider); 82 | } 83 | 84 | // Handle if not logged in 85 | // Check user email if exists in array 86 | $emailCheck = false; 87 | if (isset($userInfo['email']) && $userInfo['email'] != "") { 88 | $emailCheck = \Dappur\Model\Users::where('email', $userInfo['email'])->first(); 89 | } 90 | 91 | 92 | // Create account if email doesnt exist 93 | if (!$emailCheck) { 94 | return $this->createUser($userInfo, $provider); 95 | } 96 | 97 | 98 | 99 | // Create Oauth2 entry for existing user 100 | return $this->createOauth2User($emailCheck, $userInfo, $provider); 101 | } 102 | 103 | private function oauthRedirect($page = 'login') 104 | { 105 | if ($this->session->exists('oauth2-redirect')) { 106 | return $this->response->withRedirect($this->router->pathFor($this->session->get('oauth2-redirect'))); 107 | } 108 | 109 | return $this->response->withRedirect($this->router->pathFor($page)); 110 | } 111 | 112 | private function updateOauth2User(array $userInfo, $provider) 113 | { 114 | $oauthUser = \Dappur\Model\Oauth2Users::where('uid', $userInfo['uid']) 115 | ->where('provider_id', $provider->id) 116 | ->first(); 117 | 118 | $oauthUser->access_token = $userInfo['access_token']; 119 | 120 | if (isset($userInfo['token_secret'])) { 121 | $oauthUser->token_secret = $userInfo['token_secret']; 122 | } 123 | if (isset($userInfo['refresh_token'])) { 124 | $oauthUser->refresh_token = $userInfo['refresh_token']; 125 | } 126 | $oauthUser->expires = null; 127 | if ($userInfo['expires_in'] != 0) { 128 | $oauthUser->expires = \Carbon\Carbon::now()->addSeconds($userInfo['expires_in']); 129 | } 130 | 131 | $oauthUser->save(); 132 | 133 | $user = $this->auth->findById($oauthUser->user_id); 134 | $this->auth->login($user); 135 | 136 | $this->flash->addMessage('success', "You have been logged in using your {$provider->name} account."); 137 | 138 | if ($this->auth->inRole('admin')) { 139 | return $this->oauthRedirect('dashboard'); 140 | } 141 | 142 | return $this->oauthRedirect('home'); 143 | } 144 | 145 | private function createUser(array $userInfo, $provider) 146 | { 147 | // Create user account and log in user 148 | $role = $this->auth->findRoleByName('User'); 149 | 150 | $userDetails = array(); 151 | 152 | if (isset($userInfo['first_name'])) { 153 | $userDetails['first_name'] = $userInfo['first_name']; 154 | } 155 | if (isset($userInfo['last_name'])) { 156 | $userDetails['last_name'] = $userInfo['last_name']; 157 | } 158 | if (isset($userInfo['email'])) { 159 | $userDetails['email'] = $userInfo['email']; 160 | } 161 | 162 | // Generate a username based on first & last with db check 163 | $originalUsername = preg_replace( 164 | '/[^ \w]+/', 165 | '', 166 | strtolower($userInfo['first_name'] . $userInfo['last_name']) 167 | ); 168 | $username = $originalUsername; 169 | $usernameCheck = \Dappur\Model\Users::where('username', $username)->first(); 170 | $usernameCount = 0; 171 | while ($usernameCheck) { 172 | $usernameCount++; 173 | $username = $originalUsername . $usernameCount; 174 | $usernameCheck = \Dappur\Model\Users::where('username', $username)->first(); 175 | } 176 | $userDetails['username'] = $username; 177 | 178 | // Generate random password 179 | $bytes = openssl_random_pseudo_bytes(8); 180 | $userDetails['password'] = bin2hex($bytes); 181 | 182 | // Add user permissions 183 | $userDetails['permissions'] = ['user.delete' => 0]; 184 | 185 | 186 | 187 | // Create user account 188 | $user = $this->auth->registerAndActivate($userDetails); 189 | $role->users()->attach($user); 190 | 191 | // Send Welcome email 192 | $sendEmail = new \Dappur\Dappurware\Email($this->container); 193 | $sendEmail = $sendEmail->sendTemplate(array($user->id), 'registration'); 194 | $this->flash('success', 'Your account has been created.'); 195 | $this->auth->login($user); 196 | 197 | // Add Oauth record 198 | $oauthUser = new \Dappur\Model\Oauth2Users; 199 | $oauthUser->user_id = $user->id; 200 | $oauthUser->provider_id = $provider->id; 201 | $oauthUser->uid = $userInfo['uid']; 202 | $oauthUser->access_token = $userInfo['access_token']; 203 | 204 | if (isset($userInfo['token_secret'])) { 205 | $oauthUser->token_secret = $userInfo['token_secret']; 206 | } 207 | if (isset($userInfo['refresh_token'])) { 208 | $oauthUser->refresh_token = $userInfo['refresh_token']; 209 | } 210 | $oauthUser->expires = null; 211 | if ($userInfo['expires_in'] != 0) { 212 | $oauthUser->expires = \Carbon\Carbon::now()->addSeconds($userInfo['expires_in']); 213 | } 214 | $oauthUser->save(); 215 | 216 | return $this->oauthRedirect('home'); 217 | } 218 | 219 | private function createOauth2User($user, array $userInfo, $provider) 220 | { 221 | $this->auth->login($user); 222 | 223 | // Add Oauth record 224 | $oauthUser = new \Dappur\Model\Oauth2Users; 225 | $oauthUser->user_id = $user->id; 226 | $oauthUser->provider_id = $provider->id; 227 | $oauthUser->uid = $userInfo['uid']; 228 | $oauthUser->access_token = $userInfo['access_token']; 229 | 230 | if (isset($userInfo['token_secret'])) { 231 | $oauthUser->token_secret = $userInfo['token_secret']; 232 | } 233 | if (isset($userInfo['refresh_token'])) { 234 | $oauthUser->refresh_token = $userInfo['refresh_token']; 235 | } 236 | $oauthUser->expires = null; 237 | if ($userInfo['expires_in'] != 0) { 238 | $oauthUser->expires = \Carbon\Carbon::now()->addSeconds($userInfo['expires_in']); 239 | } 240 | 241 | $oauthUser->save(); 242 | 243 | $this->flash->addMessage('success', "Your {$provider->name} account has been successfully linked."); 244 | 245 | return $this->oauthRedirect('home'); 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /app/src/Controller/Webhooks/Mailgun.php: -------------------------------------------------------------------------------- 1 | getBody()); 15 | 16 | $apiKey = $this->settings['webhooks']['mailgun']['api_key']; 17 | 18 | // Check For Setting 19 | if (empty($apiKey)) { 20 | throw new \Slim\Exception\NotFoundException($request, $response); 21 | } 22 | 23 | // Check For payload 24 | if (!isset($payload->signature->timestamp, $payload->signature->token, $payload->signature->signature)) { 25 | throw new \Slim\Exception\NotFoundException($request, $response); 26 | } 27 | 28 | $hash = hash_hmac( 29 | 'sha256', 30 | $payload->signature->timestamp . $payload->signature->token, 31 | $apiKey 32 | ); 33 | 34 | if ($hash != $payload->signature->signature) { 35 | throw new \Slim\Exception\NotFoundException($request, $response); 36 | } 37 | 38 | if (isset($payload->{'event-data'}->message->headers->{'message-id'})) { 39 | $messageId = $payload->{'event-data'}->message->headers->{'message-id'}; 40 | $email = \Dappur\Model\Emails::where('secure_id', $messageId)->first(); 41 | 42 | if ($email) { 43 | $addStatus = new \Dappur\Model\EmailsStatus; 44 | $addStatus->email_id = $email->id; 45 | $addStatus->status = $payload->{'event-data'}->event; 46 | 47 | switch ($payload->{'event-data'}->event) { 48 | case 'clicked': 49 | $addStatus->details = $payload->{'event-data'}->url; 50 | break; 51 | } 52 | $addStatus->save(); 53 | return $response->withJSON( 54 | json_encode(array('status' => "success")), 55 | 200 56 | ); 57 | } 58 | 59 | return $response->withJSON( 60 | json_encode(array("status" => "error")), 61 | 200 62 | ); 63 | } 64 | 65 | throw new \Slim\Exception\NotFoundException($request, $response); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /app/src/Middleware/Admin.php: -------------------------------------------------------------------------------- 1 | auth->check()) { 8 | $currentRoute = $this->request->getUri()->getPath(); 9 | return $response->withRedirect($this->router->pathFor('login') . "?redirect=" . $currentRoute); 10 | } 11 | if (!$this->auth->hasAccess('dashboard.*')) { 12 | return $response->withRedirect($this->router->pathFor('home')); 13 | } 14 | return $next($request, $response); 15 | } 16 | } -------------------------------------------------------------------------------- /app/src/Middleware/Auth.php: -------------------------------------------------------------------------------- 1 | auth->check()) { 10 | $currentRoute = $this->request->getUri()->getPath(); 11 | 12 | $this->flash->addMessage('danger', 'You must be logged in to access this page!'); 13 | return $response->withRedirect($this->router->pathFor('login') . "?redirect=" . $currentRoute); 14 | } 15 | 16 | return $next($request, $response); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/src/Middleware/BlogCheck.php: -------------------------------------------------------------------------------- 1 | config['blog-enabled']) { 10 | $this->flash->addMessage('danger', 'The blog is not enabled for this site!'); 11 | 12 | if (strpos($request->getUri()->getPath(), 'dashboard') !== false) { 13 | return $response->withRedirect($this->router->pathFor('dashboard')); 14 | } 15 | 16 | return $response->withRedirect($this->router->pathFor('home')); 17 | } 18 | 19 | return $next($request, $response); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/Middleware/Guest.php: -------------------------------------------------------------------------------- 1 | auth->check()) { 10 | return $response->withRedirect($this->router->pathFor('home')); 11 | } 12 | 13 | return $next($request, $response); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/Middleware/Maintenance.php: -------------------------------------------------------------------------------- 1 | config['maintenance-mode']) { 10 | return $response->withRedirect($this->router->pathFor('maintenance-mode')); 11 | } 12 | 13 | return $next($request, $response); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/Middleware/Middleware.php: -------------------------------------------------------------------------------- 1 | container = $container; 14 | } 15 | 16 | public function __get($property) 17 | { 18 | return $this->container->get($property); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/src/Middleware/PageConfig.php: -------------------------------------------------------------------------------- 1 | getAttribute('route')->getName(); 10 | 11 | $pageConfig = \Dappur\Model\ConfigGroups::where('page_name', '=', $pageName)->with('config')->get(); 12 | 13 | if ($pageConfig) { 14 | $cfg = array(); 15 | foreach ($pageConfig as $pc) { 16 | foreach ($pc->config as $value) { 17 | $cfg[$value->name] = $value->value; 18 | } 19 | } 20 | 21 | $this->view->getEnvironment()->addGlobal('pageConfig', $cfg); 22 | if (!empty($cfg)) { 23 | $this->container->pageConfig = $cfg; 24 | } 25 | return $next($request, $response); 26 | } 27 | 28 | return $next($request, $response); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/src/Middleware/ProfileCheck.php: -------------------------------------------------------------------------------- 1 | auth->check()) { 10 | $user = $this->auth->check(); 11 | $err = 0; 12 | if (!$user->first_name || $user->first_name == "") { 13 | $err++; 14 | } 15 | if (!$user->last_name || $user->last_name == "") { 16 | $err++; 17 | } 18 | if (!$user->email || $user->email == "") { 19 | $err++; 20 | } 21 | 22 | if ($err) { 23 | $this->flash->addMessage( 24 | 'warning', 25 | 'Oops! It appears that your profile is missing some information.'. 26 | ' Please review/correct your profile below in order to continue.' 27 | ); 28 | return $response->withRedirect($this->router->pathFor('profile-incomplete')); 29 | } 30 | } 31 | 32 | return $next($request, $response); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/Middleware/RouteName.php: -------------------------------------------------------------------------------- 1 | getAttribute('route')) { 10 | $pageName = $request->getAttribute('route')->getName(); 11 | $this->view->getEnvironment()->addGlobal('pageName', $pageName); 12 | } 13 | 14 | return $next($request, $response); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/Middleware/Seo.php: -------------------------------------------------------------------------------- 1 | getAttribute('route')->getName(); 10 | 11 | $seoConfig = \Dappur\Model\Seo::where('page', '=', $page)->first(); 12 | 13 | if (!$seoConfig) { 14 | $seoConfig = \Dappur\Model\Seo::where('default', 1)->first(); 15 | } 16 | 17 | $this->view->getEnvironment()->addGlobal('seo', $seoConfig); 18 | 19 | return $next($request, $response); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/Middleware/TwoFactorAuth.php: -------------------------------------------------------------------------------- 1 | auth->check(); 10 | if ($user && $user['2fa'] && !$this->session->exists('2fa-confirmed') && $this->config['2fa-enabled']) { 11 | return $response->withRedirect($this->router->pathFor('2fa-confirm')); 12 | } 13 | return $next($request, $response); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/Migration/Migration.php: -------------------------------------------------------------------------------- 1 | capsule = new \Illuminate\Database\Capsule\Manager; 20 | $this->capsule->addConnection([ 21 | 'driver' => 'mysql', 22 | 'host' => $dbconf['host'], 23 | 'port' => $dbconf['port'], 24 | 'database' => $dbconf['database'], 25 | 'username' => $dbconf['username'], 26 | 'password' => $dbconf['password'], 27 | 'charset' => $dbconf['charset'], 28 | 'collation' => $dbconf['collation'], 29 | 'timezone' => $dbconf['timezone'] 30 | ]); 31 | 32 | $this->capsule->bootEloquent(); 33 | $this->capsule->setAsGlobal(); 34 | $this->schema = $this->capsule->schema(); 35 | 36 | $this->sentinel = ( 37 | new \Cartalyst\Sentinel\Native\Facades\Sentinel( 38 | new \Cartalyst\Sentinel\Native\SentinelBootstrapper(__DIR__ . '/../../bootstrap/sentinel.php') 39 | ) 40 | )->getSentinel(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/src/Model/Activations.php: -------------------------------------------------------------------------------- 1 | hasMany('\Dappur\Model\BlogPosts', 'category_id'); 18 | } 19 | 20 | public function name() 21 | { 22 | $query = $this->select('name')->get()->pluck('name'); 23 | return $query; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/Model/BlogPosts.php: -------------------------------------------------------------------------------- 1 | belongsTo('\Dappur\Model\BlogCategories', 'category_id'); 27 | } 28 | 29 | public function tags() 30 | { 31 | return $this->belongsToMany('\Dappur\Model\BlogTags', 'blog_posts_tags', 'post_id', 'tag_id'); 32 | } 33 | 34 | public function comments() 35 | { 36 | return $this->hasMany('\Dappur\Model\BlogPostsComments', 'post_id', 'id'); 37 | } 38 | 39 | public function replies() 40 | { 41 | return $this->hasManyThrough( 42 | '\Dappur\Model\BlogPostsReplies', 43 | '\Dappur\Model\BlogPostsComments', 44 | 'post_id', 45 | 'comment_id', 46 | 'id' 47 | ); 48 | } 49 | 50 | public function approvedComments() 51 | { 52 | return $this->hasMany('\Dappur\Model\BlogPostsComments', 'post_id', 'id')->where('status', 1); 53 | } 54 | 55 | public function pendingComments() 56 | { 57 | return $this->hasMany('\Dappur\Model\BlogPostsComments', 'post_id', 'id')->where('status', 0); 58 | } 59 | 60 | public function author() 61 | { 62 | return $this->hasOne('\Dappur\Model\Users', 'id', 'user_id'); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/src/Model/BlogPostsComments.php: -------------------------------------------------------------------------------- 1 | hasMany('\Dappur\Model\BlogPostsReplies', 'comment_id', 'id'); 20 | } 21 | 22 | public function post() 23 | { 24 | return $this->belongsTo('\Dappur\Model\BlogPosts', 'post_id'); 25 | } 26 | 27 | public function approvedReplies() 28 | { 29 | return $this->hasMany('\Dappur\Model\BlogPostsReplies', 'comment_id', 'id')->where('status', 1); 30 | } 31 | 32 | public function pendingReplies() 33 | { 34 | return $this->hasMany('\Dappur\Model\BlogPostsReplies', 'comment_id', 'id')->where('status', 0); 35 | } 36 | 37 | public function user() 38 | { 39 | return $this->belongsTo('\Dappur\Model\users', 'user_id'); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/src/Model/BlogPostsReplies.php: -------------------------------------------------------------------------------- 1 | belongsTo('\Dappur\Model\BlogPostsComments', 'comment_id'); 21 | } 22 | 23 | public function user() 24 | { 25 | return $this->belongsTo('\Dappur\Model\users', 'user_id'); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/Model/BlogPostsTags.php: -------------------------------------------------------------------------------- 1 | belongsToMany('\Dappur\Model\BlogPosts', 'blog_posts_tags', 'tag_id', 'post_id'); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/Model/Config.php: -------------------------------------------------------------------------------- 1 | belongsTo('\Dappur\Model\ConfigGroups', 'group_id'); 21 | } 22 | 23 | public function type() 24 | { 25 | return $this->belongsTo('\Dappur\Model\ConfigTypes', 'type_id'); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/Model/ConfigGroups.php: -------------------------------------------------------------------------------- 1 | hasMany('\Dappur\Model\Config', 'group_id'); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/src/Model/ConfigTypes.php: -------------------------------------------------------------------------------- 1 | hasMany('\Dappur\Model\Config', 'type_id'); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/src/Model/ContactRequests.php: -------------------------------------------------------------------------------- 1 | hasOne('\Dappur\Model\EmailsStatus', 'email_id', 'id') 22 | ->orderBy('created_at', 'DESC'); 23 | } 24 | 25 | public function status() 26 | { 27 | return $this->hasMany('\Dappur\Model\EmailsStatus', 'email_id', 'id') 28 | ->orderBy('created_at', 'DESC'); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/src/Model/EmailsStatus.php: -------------------------------------------------------------------------------- 1 | hasManyThrough( 24 | '\Dappur\Model\Oauth2Users', 25 | '\Dappur\Model\Users', 26 | 'id', 27 | 'provider_id', 28 | 'id', 29 | 'id' 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/Model/Oauth2Users.php: -------------------------------------------------------------------------------- 1 | belongsTo('Users', 'user_id'); 23 | } 24 | 25 | public function provider() 26 | { 27 | return $this->belongsTo('\Dappur\Model\Oauth2Providers', 'provider_id'); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/Model/RoleRoutes.php: -------------------------------------------------------------------------------- 1 | belongsToMany('\Dappur\Model\Roles', 'role_routes', 'route_id', 'role_id'); 27 | } 28 | 29 | public function roleIds() 30 | { 31 | return $this->belongsToMany('\Dappur\Model\Roles', 'role_routes', 'route_id', 'role_id') 32 | ->get() 33 | ->pluck('id'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/Model/Seo.php: -------------------------------------------------------------------------------- 1 | hasOne('\Dappur\Model\UsersProfile', 'user_id', 'id'); 25 | } 26 | 27 | public function posts() 28 | { 29 | return $this->hasMany('\Dappur\Model\BlogPosts', 'user_id', 'id'); 30 | } 31 | 32 | public function oauth2() 33 | { 34 | return $this->hasMany('\Dappur\Model\Oauth2Users', 'user_id', 'id'); 35 | } 36 | 37 | public function notActivated() 38 | { 39 | return $this->hasOne('\Dappur\Model\Activations', 'user_id', 'id') 40 | ->where('completed', 0); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/src/Model/UsersProfile.php: -------------------------------------------------------------------------------- 1 | =7.2.0", 22 | "dappur/dappurware": "^4.0.0", 23 | "dappur/dappurware-email": "^4.0.0", 24 | "dappur/dappurware-oauth2": "^4.0.0", 25 | "dappur/dappurware-video": "^4.0.0", 26 | "slim/slim": "^3.12", 27 | "twig/twig": "^2.11", 28 | "slim/twig-view": "^2.5", 29 | "slim/flash": "^0.4.0", 30 | "slim/csrf": "^0.8.3", 31 | "illuminate/database": "^5.8", 32 | "cartalyst/sentinel": "^2.0", 33 | "illuminate/events": "^5.8", 34 | "symfony/http-foundation": "^4.3.8", 35 | "awurth/slim-validation": "^3.1", 36 | "monolog/monolog": "^1.24", 37 | "logentries/logentries-monolog-handler": "^2.1", 38 | "dflydev/fig-cookies": "^2.0", 39 | "cloudinary/cloudinary_php": "^1.14", 40 | "robmorgan/phinx": "^0.10.8", 41 | "phpmailer/phpmailer": "^6.1.6", 42 | "jasongrimes/paginator": "^1.0", 43 | "abraham/twitteroauth": "^1.0", 44 | "ramsey/uuid": "^3.8", 45 | "bryanjhv/slim-session": "^3.0", 46 | "hellogerard/jobby": "^3.4", 47 | "robthree/twofactorauth": "^1.6", 48 | "google/apiclient": "^2.2" 49 | }, 50 | "require-dev": { 51 | "squizlabs/php_codesniffer": "^3.2", 52 | "phpmd/phpmd": "^2.6" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /database/migrations/20180216163853_add_seo_options.php: -------------------------------------------------------------------------------- 1 | schema->create('seo', function (Blueprint $table) { 11 | $table->increments('id'); 12 | $table->string('page')->nullable(); 13 | $table->string('title')->nullable(); 14 | $table->string('description')->nullable(); 15 | $table->string('image')->nullable(); 16 | $table->string('video')->nullable(); 17 | $table->boolean('default')->default(0); 18 | $table->timestamps(); 19 | }); 20 | 21 | $ins_seo = new \Dappur\Model\Seo; 22 | $ins_seo->page = "home"; 23 | $ins_seo->title = "Dappur PHP Framework"; 24 | $ins_seo->description = "A stylish PHP application framework crafted using Slim, Twig, Eloquent and Sentinel designed to get you from clone to production in a matter of minutes."; 25 | $ins_seo->image = "https://res.cloudinary.com/dappur/image/upload/v1519256235/seo/bbn48kaoq35hm7zbuuzk.jpg"; 26 | $ins_seo->default = 1; 27 | $ins_seo->save(); 28 | 29 | // Add SEO Config Group 30 | $config = new Dappur\Model\ConfigGroups; 31 | $config->name = "SEO Settings"; 32 | $config->description = "SEO Settings"; 33 | $config->save(); 34 | 35 | $init_config = array( 36 | array($config->id, 'fb-admins', 'Facebook Admins', 2, ''), 37 | array($config->id, 'fb-app-id', 'Facebook App ID', 2, ''), 38 | array($config->id, 'fb-page-id', 'Facebook Page ID', 2, ''), 39 | array($config->id, 'tw-author', 'Twitter Author', 2, ''), 40 | array($config->id, 'tw-publisher', 'Twitter Publisher', 2, '') 41 | ); 42 | 43 | // Seed Config Table 44 | foreach ($init_config as $key => $value) { 45 | $config = new Dappur\Model\Config; 46 | $config->group_id = $value[0]; 47 | $config->name = $value[1]; 48 | $config->description = $value[2]; 49 | $config->type_id = $value[3]; 50 | $config->value = $value[4]; 51 | $config->save(); 52 | } 53 | 54 | // Update Admin Role 55 | $admin_role = $this->sentinel->findRoleByName('Admin'); 56 | $admin_role->addPermission('seo.*'); 57 | $admin_role->save(); 58 | // Update Manager Role 59 | $manager_role = $this->sentinel->findRoleByName('Manager'); 60 | $manager_role->addPermission('seo.create'); 61 | $manager_role->addPermission('seo.view'); 62 | $manager_role->addPermission('seo.update'); 63 | $manager_role->save(); 64 | // Update Auditor Role 65 | $auditor_role = $this->sentinel->findRoleByName('Auditor'); 66 | $auditor_role->addPermission('seo.view'); 67 | $auditor_role->save(); 68 | 69 | } 70 | 71 | public function down() 72 | { 73 | // Remove Config Group 74 | $config = new Dappur\Model\ConfigGroups; 75 | $config = $config->where("name", "SEO Settings")->first(); 76 | if ($config) { 77 | $config->delete(); 78 | } 79 | 80 | $this->schema->dropIfExists('seo'); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /database/migrations/20180222064705_add_oauth.php: -------------------------------------------------------------------------------- 1 | schema->create('oauth2_providers', function (Blueprint $table) { 11 | $table->increments('id'); 12 | $table->string('name')->unique(); 13 | $table->string('slug')->unique(); 14 | $table->text('scopes')->nullable(); 15 | $table->string('authorize_url'); 16 | $table->string('token_url'); 17 | $table->string('resource_url'); 18 | $table->string('button')->nullable(); 19 | $table->boolean('login')->default(0); 20 | $table->boolean('status')->default(0); 21 | $table->timestamps(); 22 | }); 23 | 24 | $this->schema->create('oauth2_users', function (Blueprint $table) { 25 | $table->increments('id'); 26 | $table->integer('user_id')->unsigned(); 27 | $table->integer('provider_id')->unsigned(); 28 | $table->string('uid'); 29 | $table->text('access_token'); 30 | $table->text('token_secret')->nullable(); 31 | $table->text('refresh_token')->nullable(); 32 | $table->timestamp('expires')->nullable(); 33 | $table->timestamps(); 34 | $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); 35 | $table->foreign('provider_id')->references('id')->on('oauth2_providers')->onDelete('cascade'); 36 | }); 37 | 38 | $oauth2_providers = array( 39 | array( 40 | "name" => "Facebook", 41 | "slug" => "facebook", 42 | "scopes" => "email,public_profile", 43 | "authorize_url" => "https://www.facebook.com/dialog/oauth", 44 | "token_url" => "https://graph.facebook.com/oauth/access_token", 45 | "resource_url" => "https://graph.facebook.com/me?fields=id,email,first_name,last_name", 46 | "button" => "facebook", 47 | "login" => 0, 48 | "status" => 0 49 | ), 50 | array( 51 | "name" => "Google", 52 | "slug" => "google", 53 | "scopes" => "email profile", 54 | "authorize_url" => "https://accounts.google.com/o/oauth2/auth", 55 | "token_url" => "https://accounts.google.com/o/oauth2/token", 56 | "resource_url" => "https://www.googleapis.com/oauth2/v1/userinfo", 57 | "button" => "google", 58 | "login" => 0, 59 | "status" => 0 60 | ), 61 | array( 62 | "name" => "Twitter", 63 | "slug" => "twitter", 64 | "scopes" => "", 65 | "authorize_url" => "https://api.twitter.com/oauth/authorize", 66 | "token_url" => "https://api.twitter.com/oauth2/token", 67 | "resource_url" => "account/verify_credentials", 68 | "button" => "twitter", 69 | "login" => 0, 70 | "status" => 0 71 | ), 72 | array( 73 | "name" => "LinkedIn", 74 | "slug" => "linkedin", 75 | "scopes" => "r_liteprofile,r_emailaddress", 76 | "authorize_url" => "https://www.linkedin.com/oauth/v2/authorization", 77 | "token_url" => "https://www.linkedin.com/oauth/v2/accessToken", 78 | "resource_url" => "https://api.linkedin.com/v2/me", 79 | "button" => "linkedin", 80 | "login" => 0, 81 | "status" => 0 82 | ), 83 | array( 84 | "name" => "Instagram", 85 | "slug" => "instagram", 86 | "scopes" => "basic", 87 | "authorize_url" => "https://api.instagram.com/oauth/authorize", 88 | "token_url" => "https://api.instagram.com/oauth/access_token", 89 | "resource_url" => "https://api.instagram.com/v1/users/self", 90 | "button" => "instagram", 91 | "login" => 0, 92 | "status" => 0 93 | ), 94 | array( 95 | "name" => "Github", 96 | "slug" => "github", 97 | "scopes" => "user", 98 | "authorize_url" => "https://github.com/login/oauth/authorize", 99 | "token_url" => "https://github.com/login/oauth/access_token", 100 | "resource_url" => "https://api.github.com/user", 101 | "button" => "github", 102 | "login" => 0, 103 | "status" => 0 104 | ), 105 | array( 106 | "name" => "Microsoft Live", 107 | "slug" => "microsoft", 108 | "scopes" => "wl.basic,wl.emails,wl.signin", 109 | "authorize_url" => "https://login.live.com/oauth20_authorize.srf", 110 | "token_url" => "https://login.live.com/oauth20_token.srf", 111 | "resource_url" => "https://apis.live.net/v5.0/me", 112 | "button" => "microsoft", 113 | "login" => 0, 114 | "status" => 0 115 | ) 116 | ); 117 | 118 | foreach ($oauth2_providers as $key => $value) { 119 | $ins = new \Dappur\Model\Oauth2Providers; 120 | $ins->name = $value['name']; 121 | $ins->slug = $value['slug']; 122 | $ins->scopes = $value['scopes']; 123 | $ins->authorize_url = $value['authorize_url']; 124 | $ins->token_url = $value['token_url']; 125 | $ins->resource_url = $value['resource_url']; 126 | $ins->button = $value['button']; 127 | $ins->login = $value['login']; 128 | $ins->status = $value['status']; 129 | $ins->save(); 130 | } 131 | 132 | // Add Oauth2 Config Group 133 | $config = new Dappur\Model\ConfigGroups; 134 | $config->name = "Oauth2"; 135 | $config->description = "Oauth2 Login Provider Settings"; 136 | $config->save(); 137 | 138 | $init_config = array( 139 | array($config->id, 'oauth2-enabled', 'Enable Oauth2', 6, 0) 140 | ); 141 | 142 | // Seed Config Table 143 | foreach ($init_config as $key => $value) { 144 | $config = new Dappur\Model\Config; 145 | $config->group_id = $value[0]; 146 | $config->name = $value[1]; 147 | $config->description = $value[2]; 148 | $config->type_id = $value[3]; 149 | $config->value = $value[4]; 150 | $config->save(); 151 | } 152 | 153 | // Update Admin Role 154 | $admin_role = $this->sentinel->findRoleByName('Admin'); 155 | $admin_role->addPermission('oauth2.*'); 156 | $admin_role->save(); 157 | // Update Manager Role 158 | $manager_role = $this->sentinel->findRoleByName('Manager'); 159 | $manager_role->addPermission('oauth2.create'); 160 | $manager_role->addPermission('oauth2.view'); 161 | $manager_role->addPermission('oauth2.update'); 162 | $manager_role->save(); 163 | // Update Auditor Role 164 | $auditor_role = $this->sentinel->findRoleByName('Auditor'); 165 | $auditor_role->addPermission('oauth2.view'); 166 | $auditor_role->save(); 167 | } 168 | 169 | public function down() 170 | { 171 | $this->schema->dropIfExists('oauth2_users'); 172 | $this->schema->dropIfExists('oauth2_providers'); 173 | 174 | // Delete Config and Group 175 | $delGroup = \Dappur\Model\ConfigGroups::where('name', 'Oauth2')->first(); 176 | $delConfig = \Dappur\Model\Config::where('group_id', $delGroup->id)->delete(); 177 | $delGroup->delete(); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /database/migrations/20180717054011_admin_lte_config.php: -------------------------------------------------------------------------------- 1 | group_id = 2; 12 | $config->name = "adminlte-skin"; 13 | $config->description = "AdminLTE Skin"; 14 | $config->type_id = 2; 15 | $config->value = "skin-yellow"; 16 | $config->save(); 17 | } 18 | 19 | public function down() 20 | { 21 | \Dappur\Model\Config::where('name', "adminlte-skin")->first()->delete(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /database/migrations/20180722001608_add_two_factor_auth.php: -------------------------------------------------------------------------------- 1 | schema->table('users', function (Blueprint $table) { 11 | $table->string('2fa')->after('password')->nullable(); 12 | }); 13 | 14 | // Add 2FA Config Group 15 | $config = new \Dappur\Model\ConfigGroups; 16 | $config->name = "2FA"; 17 | $config->description = "2FA Settings"; 18 | $config->save(); 19 | 20 | $init_config = array( 21 | array($config->id, '2fa-enabled', 'Enable 2FA', 6, 0) 22 | ); 23 | 24 | // Seed Config Table 25 | foreach ($init_config as $value) { 26 | $config = new Dappur\Model\Config; 27 | $config->group_id = $value[0]; 28 | $config->name = $value[1]; 29 | $config->description = $value[2]; 30 | $config->type_id = $value[3]; 31 | $config->value = $value[4]; 32 | $config->save(); 33 | } 34 | } 35 | 36 | public function down() 37 | { 38 | $this->schema->table('users', function (Blueprint $table) { 39 | $table->dropColumn('2fa'); 40 | }); 41 | 42 | // Delete Config and Group 43 | $delGroup = \Dappur\Model\ConfigGroups::where('name', '2FA')->first(); 44 | $delConfig = \Dappur\Model\Config::where('group_id', $delGroup->id)->delete(); 45 | $delGroup->delete(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /database/migrations/20180810003809_add_side_bar_to_custom_routes.php: -------------------------------------------------------------------------------- 1 | schema->table('routes', function (Blueprint $table) { 11 | $table->boolean('sidebar')->after('js')->default(0); 12 | $table->boolean('header')->after('sidebar')->default(0); 13 | $table->text('header_text')->after('header')->nullable(); 14 | $table->string('header_image')->after('header_text')->nullable(); 15 | }); 16 | } 17 | 18 | public function down() 19 | { 20 | $this->schema->table('routes', function (Blueprint $table) { 21 | $table->dropColumn('sidebar'); 22 | $table->dropColumn('header'); 23 | $table->dropColumn('header_text'); 24 | $table->dropColumn('header_image'); 25 | }); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /database/templates/create-template.php: -------------------------------------------------------------------------------- 1 | schema->create('sample', function (Blueprint $table) { 22 | $table->increments('id'); 23 | $table->string('email')->unique(); 24 | $table->string('last_name')->nullable(); 25 | $table->string('first_name')->nullable(); 26 | $table->timestamps(); 27 | }); 28 | } 29 | 30 | public function down() 31 | { 32 | $this->schema->dropIfExists('sample'); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /phinx.php: -------------------------------------------------------------------------------- 1 | [ 11 | 'migrations' => 'database/migrations' 12 | ], 13 | 'templates' => [ 14 | 'file' => '%%PHINX_CONFIG_DIR%%/database/templates/create-template.php' 15 | ], 16 | 'migration_base_class' => '\Dappur\Migration\Migration', 17 | 'environments' => [ 18 | 'default_migration_table' => 'phinxlog', 19 | 'default_database' => $settings['environment'] 20 | ] 21 | ]; 22 | 23 | foreach ($dbconf as $key => $value) { 24 | $output['environments'][$key] = [ 25 | 'adapter' => $value['driver'], 26 | 'host' => $value['host'], 27 | 'name' => $value['database'], 28 | 'user' => $value['username'], 29 | 'pass' => $value['password'], 30 | 'port' => $value['port'], 31 | 'charset' => $value['charset'], 32 | 'collation' => $value['collation'], 33 | 'prefix' => $value['prefix'], 34 | 'timezone' => $value['timezone'] 35 | ]; 36 | } 37 | 38 | return $output; 39 | -------------------------------------------------------------------------------- /public/.htaccess: -------------------------------------------------------------------------------- 1 | RewriteEngine On 2 | 3 | RewriteCond %{HTTP_HOST} =^www\. 4 | RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [NC,L,R] 5 | 6 | RewriteCond %{REQUEST_FILENAME} !-f 7 | RewriteCond %{REQUEST_FILENAME} !-d 8 | RewriteRule ^ index.php [QSA,L] -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | dApp 13 | or following the 14 | README.' 15 | ); 16 | } 17 | die('No Theme'); 18 | } 19 | 20 | $app = new Slim\App(array('settings' => $settings)); 21 | 22 | // Load Dependancies 23 | require __DIR__ . '/../app/bootstrap/dependencies.php'; 24 | 25 | // Set PHP Timezone 26 | date_default_timezone_set($container->get('config')['timezone']); 27 | 28 | // Load Controllers 29 | require __DIR__ . '/../app/bootstrap/controllers.php'; 30 | 31 | // Load Routes 32 | foreach (glob(__DIR__ . '/../app/routes/*.php') as $filename) { 33 | require $filename; 34 | } 35 | 36 | // Load Global Middleware 37 | require __DIR__ . '/../app/bootstrap/middleware.php'; 38 | 39 | //Load Error Handlers 40 | require __DIR__ . '/../app/bootstrap/errors.php'; 41 | 42 | // Run App 43 | $app->run(); 44 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # Default Robots.txt 2 | User-agent: * 3 | Disallow: /dashboard/ 4 | Disallow: /uploads/ -------------------------------------------------------------------------------- /public/uploads/.htaccess: -------------------------------------------------------------------------------- 1 | Options -Indexes -------------------------------------------------------------------------------- /public/uploads/index.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dappur/framework/870f6449647d599a5fd23e3c5c49a88061851714/public/uploads/index.php -------------------------------------------------------------------------------- /settings.json.dist: -------------------------------------------------------------------------------- 1 | { 2 | "framework": "dappur", 3 | "version": "4.0.0", 4 | "displayErrorDetails": false, 5 | "environment": "development", 6 | "db": { 7 | "development": { 8 | "driver": "mysql", 9 | "host": "localhost", 10 | "port": 3306, 11 | "database": "framework", 12 | "username": "dappur", 13 | "password": "dappur", 14 | "charset": "utf8mb4", 15 | "collation": "utf8mb4_unicode_520_ci", 16 | "prefix": "", 17 | "timezone": "+00:00" 18 | }, 19 | "staging": { 20 | "driver": "mysql", 21 | "host": "localhost", 22 | "port": 3306, 23 | "database": "", 24 | "username": "", 25 | "password": "", 26 | "charset": "utf8mb4", 27 | "collation": "utf8mb4_unicode_520_ci", 28 | "prefix": "", 29 | "timezone": "+00:00" 30 | }, 31 | "production": { 32 | "driver": "mysql", 33 | "host": "localhost", 34 | "port": 3306, 35 | "database": "", 36 | "username": "", 37 | "password": "", 38 | "charset": "utf8mb4", 39 | "collation": "utf8mb4_unicode_520_ci", 40 | "prefix": "", 41 | "timezone": "+00:00" 42 | } 43 | }, 44 | "view": { 45 | "template_path": "../app/views/", 46 | "twig": { 47 | "cache": false, 48 | "debug": false, 49 | "auto_reload": true 50 | }, 51 | "bootswatch": { 52 | "api_url" : "https://bootswatch.com/api/3.json" 53 | } 54 | }, 55 | "csrf": { 56 | "prefix": "dappurcsrf", 57 | "storage_limit": 200, 58 | "strength": 32, 59 | "persist_tokens": false 60 | }, 61 | "logger": { 62 | "log_name": "Dappur", 63 | "log_path": "../storage/log/monolog/", 64 | "log_file_name": "dappur.log", 65 | "le_token": "" 66 | }, 67 | "cron": { 68 | "token": "" 69 | }, 70 | "cloudinary": { 71 | "enabled": false, 72 | "cloud_name": "", 73 | "api_key": "", 74 | "api_secret": "" 75 | }, 76 | "mail": { 77 | "relay": "phpmail", 78 | "logging": true, 79 | "log_details": ["html", "plain_text"], 80 | "log_retention": 30, 81 | "smtp": { 82 | "host": "smtp.mailgun.org", 83 | "port": 587, 84 | "smtp_auth": true, 85 | "smtp_secure": "tls", 86 | "username": "", 87 | "password": "" 88 | } 89 | }, 90 | "recaptcha": { 91 | "site_key": "", 92 | "secret_key": "" 93 | }, 94 | "oauth2": { 95 | "facebook": { 96 | "client_id": "", 97 | "client_secret": "", 98 | "class": "\\Dappur\\Dappurware\\Oauth2\\Providers\\Facebook" 99 | }, 100 | "google": { 101 | "client_id": "", 102 | "client_secret": "", 103 | "class": "\\Dappur\\Dappurware\\Oauth2\\Providers\\Google" 104 | }, 105 | "twitter": { 106 | "client_id": "", 107 | "client_secret": "", 108 | "api_key": "", 109 | "api_secret": "", 110 | "class": "\\Dappur\\Dappurware\\Oauth2\\Providers\\Twitter" 111 | }, 112 | "linkedin": { 113 | "client_id": "", 114 | "client_secret": "", 115 | "class": "\\Dappur\\Dappurware\\Oauth2\\Providers\\Linkedin" 116 | }, 117 | "github": { 118 | "client_id": "", 119 | "client_secret": "", 120 | "class": "\\Dappur\\Dappurware\\Oauth2\\Providers\\Github" 121 | }, 122 | "instagram": { 123 | "client_id": "", 124 | "client_secret": "", 125 | "class": "\\Dappur\\Dappurware\\Oauth2\\Providers\\Instagram" 126 | }, 127 | "microsoft": { 128 | "client_id": "", 129 | "client_secret": "", 130 | "class": "\\Dappur\\Dappurware\\Oauth2\\Providers\\Microsoft" 131 | } 132 | }, 133 | "webhooks": { 134 | "mailgun": { 135 | "api_key": "" 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /storage/cache/twig/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dappur/framework/870f6449647d599a5fd23e3c5c49a88061851714/storage/cache/twig/.gitkeep -------------------------------------------------------------------------------- /storage/certs/deployment/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dappur/framework/870f6449647d599a5fd23e3c5c49a88061851714/storage/certs/deployment/.gitkeep -------------------------------------------------------------------------------- /storage/certs/google/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dappur/framework/870f6449647d599a5fd23e3c5c49a88061851714/storage/certs/google/.gitkeep -------------------------------------------------------------------------------- /storage/log/cron/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dappur/framework/870f6449647d599a5fd23e3c5c49a88061851714/storage/log/cron/.gitkeep -------------------------------------------------------------------------------- /storage/log/deployment/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dappur/framework/870f6449647d599a5fd23e3c5c49a88061851714/storage/log/deployment/.gitkeep -------------------------------------------------------------------------------- /storage/log/monolog/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dappur/framework/870f6449647d599a5fd23e3c5c49a88061851714/storage/log/monolog/.gitkeep -------------------------------------------------------------------------------- /storage/vagrant/provision.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | PORT=$1 3 | ROOTPASS=$2 4 | DBNAME=$3 5 | DBUSER=$4 6 | DBPASS=$5 7 | DBPORT=$6 8 | PMAUSER="phpmyadmin" 9 | PMAPASS=$(perl -e 'print map{("a".."z","A".."Z",0..9)[int(rand(62))]}(1..32)'); 10 | DEBIANPASS=$(perl -e 'print map{("a".."z","A".."Z",0..9)[int(rand(62))]}(1..16)'); 11 | BLOWFISH=$(perl -e 'print map{("a".."z","A".."Z",0..9)[int(rand(62))]}(1..32)'); 12 | 13 | # mariadb unattended install 14 | sudo debconf-set-selections <<< "mysql-server mysql-server/root_password password $ROOTPASS" 15 | sudo debconf-set-selections <<< "mysql-server mysql-server/root_password_again password $ROOTPASS" 16 | 17 | # install php 18 | sudo apt-get update 19 | sudo apt-get upgrade 20 | sudo apt-get install -y php composer php-pear php-fpm php-dev php-zip php-curl php-xmlrpc php-gd php-mysql php-mbstring php-xml libapache2-mod-php mysql-server mysql-client apache2 21 | 22 | # configure mariadb 23 | if [ ! -f "/etc/mysql/mariadb.conf.d/dev.cnf" ]; then 24 | printf "[mysqld]\nplugin-load-add = auth_socket.so" | sudo tee -a /etc/mysql/mariadb.conf.d/dev.cnf 25 | sudo sed -i "s/^bind-address.*/bind-address = 0.0.0.0\nport = ${DBPORT}/" /etc/mysql/mysql.conf.d/mysqld.cnf 26 | # sudo sed -i "s/^# port.*/port = ${DBPORT}/" /etc/mysql/my.cnf 27 | sudo systemctl restart mysql.service 28 | fi 29 | 30 | sudo debconf-set-selections <<< "phpmyadmin phpmyadmin/internal/skip-preseed boolean true" 31 | sudo debconf-set-selections <<< "phpmyadmin phpmyadmin/reconfigure-webserver multiselect apache2" 32 | sudo debconf-set-selections <<< "phpmyadmin phpmyadmin/dbconfig-install boolean false" 33 | sudo debconf-set-selections <<< "phpmyadmin phpmyadmin/app-password-confirm password ${PMAPASS}" 34 | sudo debconf-set-selections <<< "phpmyadmin phpmyadmin/mysql/admin-pass password ${ROOTPASS}" 35 | sudo debconf-set-selections <<< "phpmyadmin phpmyadmin/mysql/app-pass password ${PMAPASS}" 36 | 37 | sudo apt-get install -y phpmyadmin 38 | 39 | #if [ ! -d "/usr/share/phpmyadmin" ]; then 40 | # # install phpmyadmin 41 | # cd /usr/share 42 | # sudo wget https://files.phpmyadmin.net/phpMyAdmin/4.7.4/phpMyAdmin-4.7.4-all-languages.zip 43 | # sudo apt-get install unzip 44 | # sudo unzip phpMyAdmin-4.7.4-all-languages.zip 45 | # sudo rm phpMyAdmin-4.7.4-all-languages.zip 46 | # sudo mv phpMyAdmin-4.7.4-all-languages phpmyadmin 47 | # cd phpmyadmin 48 | # sudo cp config.sample.inc.php config.inc.php 49 | 50 | # update phpmyadmin config 51 | # sudo sed -i "s|blowfish_secret'] = ''|blowfish_secret'] = '$BLOWFISH'|g" /usr/share/phpmyadmin/config.inc.php 52 | # sudo sed -i "s|AllowNoPassword'] = false;|AllowNoPassword'] = true;|g" /usr/share/phpmyadmin/config.inc.php 53 | #fi 54 | 55 | # create phpmyadmin user and tables 56 | #if [ ! -d /var/lib/mysql/phpymadmin ]; then 57 | # sudo mysql -uroot -p$ROOTPASS <