diff --git a/.gitignore b/.gitignore index f8891476..2f614f11 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,6 @@ Config/secrets/ node_modules/ bower_components/ .swift-version -CMakeLists.txt \ No newline at end of file +CMakeLists.txt +Package.pins +Package.resolved diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..ab40828f --- /dev/null +++ b/.travis.yml @@ -0,0 +1,22 @@ +os: + - osx + +language: swift +sudo: required +osx_image: xcode8.3 + +before_install: + brew tap vapor/tap; + brew update; + brew install vapor; + brew install cmysql; + gem install xcpretty; + +script: + - swift build + - swift build -c release + - swift package generate-xcodeproj --enable-code-coverage + - xcodebuild -scheme AdminPanelProvider -enableCodeCoverage YES test | xcpretty && exit ${PIPESTATUS[0]} + +after_success: + - bash <(curl -s https://codecov.io/bash) diff --git a/Package.swift b/Package.swift index e19827b6..4b117e91 100644 --- a/Package.swift +++ b/Package.swift @@ -12,10 +12,8 @@ let package = Package( // Nodes .Package(url: "https://github.com/nodes-vapor/flash.git", majorVersion: 1), .Package(url: "https://github.com/nodes-vapor/slugify.git", majorVersion: 1), - .Package(url: "https://github.com/nodes-vapor/storage", majorVersion: 0, minor: 4), + .Package(url: "https://github.com/nodes-vapor/storage.git", majorVersion: 0, minor: 4), .Package(url: "https://github.com/nodes-vapor/paginator.git", majorVersion: 1), - - // Brett - .Package(url: "https://github.com/BrettRToomey/stencil-provider.git", majorVersion: 0), + .Package(url: "https://github.com/nodes-vapor/audit-provider.git", majorVersion: 0, minor: 1), ] ) diff --git a/Public/dist/js/admin-panel.js b/Public/dist/js/admin-panel.js new file mode 100644 index 00000000..c639ee55 --- /dev/null +++ b/Public/dist/js/admin-panel.js @@ -0,0 +1,69 @@ +var AdminPanel = (function() { + return { + confirmDelete: function(element) { + // Confirm modal title + var modalTitle = $(element).data('header'); + modalTitle = !modalTitle ? 'Please confirm' : modalTitle; + + // Confirm modal text + var modalText = $(element).data('text'); + modalText = !modalText ? 'Are you sure you want to delete?' : modalText; + + var closure = function(e) { + // Prevent default action + e.preventDefault(); + + // Generate bootbox dialog + bootbox.dialog({ + title: modalTitle, + message: ' ' + modalText, + className: 'modal modal-danger', + buttons: { + cancel: { + label: 'Cancel', + className: 'btn-outline' + }, + success: { + label: 'Delete', + className: 'btn-outline', + callback: function () { + if ($(element).is('form')) { + $(element).trigger('submit'); + } else { + // Since we're posting data, we need to add our CSRF token + // to our form so Laravel will accept our form + // var csrfToken = $(element).data('token'); + // if (!csrfToken) { + // alert('Missing CSRF token'); + // console.log('Missing CSRF token'); + // return; + // } + + // Since
's can't send a DELETE request + // we need to use `POST` + $('', { + 'method': 'POST', + 'action': $(element).attr('href') + }).appendTo('body').submit(); + } + } + } + }, + onEscape: true + }); + }; + + if ($(element).is('form')) { + $(element).find(':submit').click(closure); + } else { + $(element).click(closure); + } + } + } +})(); + +$( document ).ready(function() { + $('[data-delete="true"]').each(function() { + AdminPanel.confirmDelete($(this)); + }); +}) \ No newline at end of file diff --git a/Public/images/it-works.png b/Public/images/it-works.png deleted file mode 100644 index e4771b83..00000000 Binary files a/Public/images/it-works.png and /dev/null differ diff --git a/README.md b/README.md index 6e0c4ff4..2985d0b6 100644 --- a/README.md +++ b/README.md @@ -1 +1,126 @@ -# Admin Panel Provider +# Admin Panel ✍️ +[![Swift Version](https://img.shields.io/badge/Swift-3.1-brightgreen.svg)](http://swift.org) +[![Vapor Version](https://img.shields.io/badge/Vapor-2-F6CBCA.svg)](http://vapor.codes) +[![Linux Build Status](https://img.shields.io/circleci/project/github/nodes-vapor/admin-panel-provider.svg?label=Linux)](https://circleci.com/gh/nodes-vapor/admin-panel-provider) +[![macOS Build Status](https://img.shields.io/travis/nodes-vapor/admin-panel-provider.svg?label=macOS)](https://travis-ci.org/nodes-vapor/admin-panel-provider) +[![codebeat badge](https://codebeat.co/badges/52c2f960-625c-4a63-ae63-52a24d747da1)](https://codebeat.co/projects/github-com-nodes-vapor-admin-panel-provider) +[![codecov](https://codecov.io/gh/nodes-vapor/admin-panel-provider/branch/master/graph/badge.svg)](https://codecov.io/gh/nodes-vapor/admin-panel-provider) +[![Readme Score](http://readme-score-api.herokuapp.com/score.svg?url=https://github.com/nodes-vapor/admin-panel-provider)](http://clayallsopp.github.io/readme-score?url=https://github.com/nodes-vapor/admin-panel-provider) +[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/nodes-vapor/admin-panel-provider/master/LICENSE) + +Admin Panel makes it easy to setup and maintain admin features for your Vapor project. Here's a list of some of the top feautures that comes out of the box with this package: + +- **UI Components:** Admin Panel is built using [AdminLTE](https://adminlte.io/), a highly battle-tested and maintained Control Panel Template. This means that you'll have access to features from AdminLTE through [Leaf](https://docs.vapor.codes/2.0/leaf/leaf/#leaf) tags. +- **User System:** This package come with a (admin panel) user system with roles built-in. The package also handles welcome emails and reset-password flows. +- **SSO Support:** Built-in support for adding your own custom SSO provider. +- **Activities**: Need to broadcast certain updates to the admin panel users? No problem, Admin Panel gives you some convenient functionality to manage an activity log. + +## 📦 Installation + +### Install package using SPM + +Update your `Package.swift` file: + +```swift +.Package(url: "https://github.com/nodes-vapor/admin-panel-provider.git", majorVersion: 0, minorVersion: 1) +``` + +Next time you run e.g. `vapor update` Admin Panel will be installed. + +### Install resources + +Move the `Resources`and `Public` folders from this repo into your project. Unfortunately there's no convenient to this at the moment, but one option is to download this repo as a zip and then move the folders into the root of your project. Remember to check that you're not overwriting any files in your project. + + +## 🚀 Getting started + +### Add provider + +In your `Config+Setup.swift` (or wherever you setup your providers), make sure to add the Admin Panel provider: + +```swift +import AdminPanelProvider + +// ... + +private func setupProviders() throws { + // ... + try addProvider(AdminPanelProvider.Provider.self) +} +``` + +### Setup view renderer + +This package relies heavily on the [Leaf](https://docs.vapor.codes/2.0/leaf/package/) view renderer. For Admin Panel to work, please make sure that you have added the `LeafProvider`: + +```swift +import LeafProvider + +// ... + +private func setupProviders() throws { + // ... + try addProvider(LeafProvider.Provider.self) +} +``` + +After adding the provider, please make sure that your project is using Leaf as the view renderer. To do that, please ensure that the `view` key is set correctly in `droplet.json`: + +```json +"//": "Choose which view renderer to use", +"//": "leaf: Vapor's Leaf renderer", +"view": "leaf" +``` + +### Seed a user + +If you haven't added a SSO provider, the next thing you need to do is to seed a user in order to be able to login into your new admin panel. To do this, first add the seeder command to your `commands` array in your `droplet.json`: + +```json +"//": "Choose which commands this application can run", +"//": "prepare: Supplied by the Fluent provider. Prepares the database (configure in fluent.json)", +"commands": [ + "prepare", + "admin-panel:seeder" +], +``` + +Next run the seeder by doing: + +``` +vapor build; vapor run admin-panel:seeder +``` + +The user that will be created using the seeder will have the following credentials: + +- Email: **admin@admin.com** +- Password: **admin** + +## 🔧 Configurations + +Admin Panel can be configured by (adding or) modifying the `adminpanel.json` config file. Below is a breakdown of the available keys. + +| Key | Example value | Required | Description | +| --------- | ----------------------- | -------- | ---------------------------------------- | +| `name` | `My Tech Blog` | No | This will be the title inside of the admin panel. | +| `baseUrl` | `http://mytechblog.com` | No | This will be used to generate urls for the admin panel (e.g. when resetting a password). | +| `skin` | `green-light` | No | The skin to use for the admin panel. The options will correspond to the [available skins](https://adminlte.io/themes/AdminLTE/documentation/index.html#layout) supported by AdminLTE. Please omit the `skin-` prefix when specifying the skin. | + +### Mailgun + +To support sending password reset emails, configure Mailgun using `mailgun.json`. In addition to the values for `key` and `domain`, AdminPanelProvider requires a value for the key `fromAddress` and `fromName` which will be used as the sender for password reset emails. + +## 🔐 SSO + +Single sign-on can be a convenient way to offer users of your project to login into your admin panel. + + +## 🏆 Credits + +This package is developed and maintained by the Vapor team at [Nodes](https://www.nodesagency.com). +The package owner for this project is [Steffen](https://github.com/steffendsommer). + + +## 📄 License + +This package is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT) diff --git a/Resources/Views/BackendUser/edit.leaf b/Resources/Views/AdminPanel/BackendUser/edit.leaf similarity index 99% rename from Resources/Views/BackendUser/edit.leaf rename to Resources/Views/AdminPanel/BackendUser/edit.leaf index 0599f64e..9d78888c 100644 --- a/Resources/Views/BackendUser/edit.leaf +++ b/Resources/Views/AdminPanel/BackendUser/edit.leaf @@ -1,4 +1,4 @@ -#extend("Layout/page") +#extend("AdminPanel/Layout/page") #export("title") { Backend Users | #if(user) { Edit } ##else() { Create }} diff --git a/Resources/Views/BackendUser/index.leaf b/Resources/Views/AdminPanel/BackendUser/index.leaf similarity index 99% rename from Resources/Views/BackendUser/index.leaf rename to Resources/Views/AdminPanel/BackendUser/index.leaf index 6cb180b3..2c99bd71 100644 --- a/Resources/Views/BackendUser/index.leaf +++ b/Resources/Views/AdminPanel/BackendUser/index.leaf @@ -1,4 +1,4 @@ -#extend("Layout/page") +#extend("AdminPanel/Layout/page") #export("title") { Backend Users } diff --git a/Resources/Views/Dashboard/index.leaf b/Resources/Views/AdminPanel/Dashboard/index.leaf similarity index 99% rename from Resources/Views/Dashboard/index.leaf rename to Resources/Views/AdminPanel/Dashboard/index.leaf index 8ccae844..56133715 100644 --- a/Resources/Views/Dashboard/index.leaf +++ b/Resources/Views/AdminPanel/Dashboard/index.leaf @@ -1,4 +1,4 @@ -#extend("Layout/page") +#extend("AdminPanel/Layout/page") #export("nav-top") { diff --git a/Resources/Views/Dashboard/shared-menu.leaf b/Resources/Views/AdminPanel/Dashboard/shared-menu.leaf similarity index 82% rename from Resources/Views/Dashboard/shared-menu.leaf rename to Resources/Views/AdminPanel/Dashboard/shared-menu.leaf index acab56f2..586b72ad 100644 --- a/Resources/Views/Dashboard/shared-menu.leaf +++ b/Resources/Views/AdminPanel/Dashboard/shared-menu.leaf @@ -52,8 +52,8 @@ @@ -61,4 +61,4 @@ #menu:footer("See all activities", "/admin/activities") } } -#embed("Layout/Partials/user-menu") +#embed("AdminPanel/Layout/Partials/user-menu") diff --git a/Resources/Views/Dashboard/shared-sidebar.leaf b/Resources/Views/AdminPanel/Dashboard/shared-sidebar.leaf similarity index 100% rename from Resources/Views/Dashboard/shared-sidebar.leaf rename to Resources/Views/AdminPanel/Dashboard/shared-sidebar.leaf diff --git a/Resources/Views/Emails/reset-password.leaf b/Resources/Views/AdminPanel/Emails/reset-password.leaf similarity index 100% rename from Resources/Views/Emails/reset-password.leaf rename to Resources/Views/AdminPanel/Emails/reset-password.leaf diff --git a/Resources/Views/Emails/welcome.leaf b/Resources/Views/AdminPanel/Emails/welcome.leaf similarity index 100% rename from Resources/Views/Emails/welcome.leaf rename to Resources/Views/AdminPanel/Emails/welcome.leaf diff --git a/Resources/Views/Layout/Partials/alerts.leaf b/Resources/Views/AdminPanel/Layout/Partials/alerts.leaf similarity index 100% rename from Resources/Views/Layout/Partials/alerts.leaf rename to Resources/Views/AdminPanel/Layout/Partials/alerts.leaf diff --git a/Resources/Views/Layout/Partials/header-post.leaf b/Resources/Views/AdminPanel/Layout/Partials/header-post.leaf similarity index 100% rename from Resources/Views/Layout/Partials/header-post.leaf rename to Resources/Views/AdminPanel/Layout/Partials/header-post.leaf diff --git a/Resources/Views/Layout/Partials/header.leaf b/Resources/Views/AdminPanel/Layout/Partials/header.leaf similarity index 100% rename from Resources/Views/Layout/Partials/header.leaf rename to Resources/Views/AdminPanel/Layout/Partials/header.leaf diff --git a/Resources/Views/Layout/Partials/sidebar.leaf b/Resources/Views/AdminPanel/Layout/Partials/sidebar.leaf similarity index 81% rename from Resources/Views/Layout/Partials/sidebar.leaf rename to Resources/Views/AdminPanel/Layout/Partials/sidebar.leaf index 0acd61ca..bee775f0 100644 --- a/Resources/Views/Layout/Partials/sidebar.leaf +++ b/Resources/Views/AdminPanel/Layout/Partials/sidebar.leaf @@ -3,7 +3,7 @@ diff --git a/Resources/Views/Layout/Partials/user-menu.leaf b/Resources/Views/AdminPanel/Layout/Partials/user-menu.leaf similarity index 100% rename from Resources/Views/Layout/Partials/user-menu.leaf rename to Resources/Views/AdminPanel/Layout/Partials/user-menu.leaf diff --git a/Resources/Views/Layout/base.leaf b/Resources/Views/AdminPanel/Layout/base.leaf similarity index 96% rename from Resources/Views/Layout/base.leaf rename to Resources/Views/AdminPanel/Layout/base.leaf index 62b0e778..06fe730a 100644 --- a/Resources/Views/Layout/base.leaf +++ b/Resources/Views/AdminPanel/Layout/base.leaf @@ -77,7 +77,8 @@ + - + diff --git a/Resources/Views/Layout/fullscreen-page.leaf b/Resources/Views/AdminPanel/Layout/fullscreen-page.leaf similarity index 64% rename from Resources/Views/Layout/fullscreen-page.leaf rename to Resources/Views/AdminPanel/Layout/fullscreen-page.leaf index 7b69546a..592f77fe 100644 --- a/Resources/Views/Layout/fullscreen-page.leaf +++ b/Resources/Views/AdminPanel/Layout/fullscreen-page.leaf @@ -1,9 +1,9 @@ -#extend("Layout/base") +#extend("AdminPanel/Layout/base") #export("page") {
- #embed("Layout/Partials/alerts") + #embed("AdminPanel/Layout/Partials/alerts") #import("content")
diff --git a/Resources/Views/Layout/page.leaf b/Resources/Views/AdminPanel/Layout/page.leaf similarity index 62% rename from Resources/Views/Layout/page.leaf rename to Resources/Views/AdminPanel/Layout/page.leaf index 42eadb5d..6bacf6d4 100644 --- a/Resources/Views/Layout/page.leaf +++ b/Resources/Views/AdminPanel/Layout/page.leaf @@ -1,11 +1,11 @@ -#extend("Layout/base") +#extend("AdminPanel/Layout/base") #export("page") { - #embed("Layout/Partials/header") + #embed("AdminPanel/Layout/Partials/header") #import("nav-top") - #embed("Dashboard/shared-menu") - #embed("Layout/Partials/header-post") - #embed("Layout/Partials/sidebar") + #embed("AdminPanel/Dashboard/shared-menu") + #embed("AdminPanel/Layout/Partials/header-post") + #embed("AdminPanel/Layout/Partials/sidebar")
#import("content-header") @@ -15,7 +15,7 @@
- #embed("Layout/Partials/alerts") + #embed("AdminPanel/Layout/Partials/alerts") #import("content")
diff --git a/Resources/Views/Login/index.leaf b/Resources/Views/AdminPanel/Login/index.leaf similarity index 78% rename from Resources/Views/Login/index.leaf rename to Resources/Views/AdminPanel/Login/index.leaf index 49df0378..894e73b7 100644 --- a/Resources/Views/Login/index.leaf +++ b/Resources/Views/AdminPanel/Login/index.leaf @@ -10,8 +10,6 @@ - - @@ -69,19 +67,22 @@ box-shadow: 0 14px 20px rgba(0, 0, 0, 0.3) } -.icheckbox_square-blue { - margin-right: 4px; -}