├── static ├── .gitkeep └── app.js ├── src ├── plugins │ └── index.js ├── assets │ ├── logo.png │ ├── avatar.jpg │ └── page-cannot-be-found.jpg ├── store │ └── index.js ├── stylesheets │ └── rtl.css ├── langs │ └── langs.js ├── components │ ├── Unavailable.vue │ ├── Login.vue │ ├── Sidebar.vue │ ├── Dashboard.vue │ ├── App.vue │ └── ModelList.vue ├── filters │ └── index.js └── main.js ├── api ├── .eslintignore ├── .eslintrc ├── .yo-rc.json ├── README.md ├── client │ └── README.md ├── common │ ├── models │ │ ├── posts.js │ │ ├── enquiries.js │ │ ├── uploads.js │ │ ├── uploads.json │ │ ├── enquiries.json │ │ └── posts.json │ └── User.json ├── uploads │ └── files │ │ ├── as.jpg │ │ └── 5d4f5de8-39b2-43f4-8e3a-6909c15722c6.jpg ├── server │ ├── models │ │ ├── uploads.js │ │ └── uploads.json │ ├── component-config.json │ ├── boot │ │ ├── authentication.js │ │ ├── debug.js │ │ ├── upload.js │ │ ├── script.js │ │ └── root.js │ ├── middleware.development.json │ ├── config.json │ ├── datasources.json │ ├── middleware.json │ ├── server.js │ └── model-config.json ├── .editorconfig └── package.json ├── _config.yml ├── config ├── prod.env.js ├── test.env.js ├── configs.js ├── dev.env.js └── index.js ├── docs ├── vue.png ├── demo.jpg ├── image.png ├── mongo.png ├── .gitignore ├── loopback.png ├── objects.inv ├── _static │ ├── up.png │ ├── demo.jpg │ ├── down.png │ ├── file.png │ ├── image.png │ ├── minus.png │ ├── plus.png │ ├── comment.png │ ├── up-pressed.png │ ├── ajax-loader.gif │ ├── comment-close.png │ ├── down-pressed.png │ ├── comment-bright.png │ ├── fonts │ │ ├── Lato-Bold.ttf │ │ ├── Lato-Regular.ttf │ │ ├── Inconsolata-Bold.ttf │ │ ├── RobotoSlab-Bold.ttf │ │ ├── Inconsolata-Regular.ttf │ │ ├── RobotoSlab-Regular.ttf │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.ttf │ │ └── fontawesome-webfont.woff │ ├── css │ │ └── badge_only.css │ ├── pygments.css │ ├── js │ │ ├── theme.js │ │ └── modernizr.min.js │ ├── doctools.js │ ├── underscore.js │ └── basic.css ├── .vscode │ └── settings.json ├── index.html ├── getting.html ├── project.html └── start.html ├── .gitignore ├── .editorconfig ├── .babelrc ├── index.html ├── npm-debug.log.746407735 ├── package.json └── README.md /static/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/app.js: -------------------------------------------------------------------------------- 1 | an -------------------------------------------------------------------------------- /src/plugins/index.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /api/.eslintignore: -------------------------------------------------------------------------------- 1 | /client/ -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /api/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "loopback" 3 | } -------------------------------------------------------------------------------- /api/.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "generator-loopback": {} 3 | } -------------------------------------------------------------------------------- /config/prod.env.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | NODE_ENV: '"production"' 3 | } 4 | -------------------------------------------------------------------------------- /docs/vue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vah7id/flashboard/HEAD/docs/vue.png -------------------------------------------------------------------------------- /docs/demo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vah7id/flashboard/HEAD/docs/demo.jpg -------------------------------------------------------------------------------- /docs/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vah7id/flashboard/HEAD/docs/image.png -------------------------------------------------------------------------------- /docs/mongo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vah7id/flashboard/HEAD/docs/mongo.png -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | _site/ 2 | .sass-cache/ 3 | .jekyll-metadata 4 | _pdf 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /docs/loopback.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vah7id/flashboard/HEAD/docs/loopback.png -------------------------------------------------------------------------------- /docs/objects.inv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vah7id/flashboard/HEAD/docs/objects.inv -------------------------------------------------------------------------------- /docs/_static/up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vah7id/flashboard/HEAD/docs/_static/up.png -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vah7id/flashboard/HEAD/src/assets/logo.png -------------------------------------------------------------------------------- /api/README.md: -------------------------------------------------------------------------------- 1 | # My Application 2 | 3 | The project is generated by [LoopBack](http://loopback.io). -------------------------------------------------------------------------------- /docs/_static/demo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vah7id/flashboard/HEAD/docs/_static/demo.jpg -------------------------------------------------------------------------------- /docs/_static/down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vah7id/flashboard/HEAD/docs/_static/down.png -------------------------------------------------------------------------------- /docs/_static/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vah7id/flashboard/HEAD/docs/_static/file.png -------------------------------------------------------------------------------- /docs/_static/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vah7id/flashboard/HEAD/docs/_static/image.png -------------------------------------------------------------------------------- /docs/_static/minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vah7id/flashboard/HEAD/docs/_static/minus.png -------------------------------------------------------------------------------- /docs/_static/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vah7id/flashboard/HEAD/docs/_static/plus.png -------------------------------------------------------------------------------- /src/assets/avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vah7id/flashboard/HEAD/src/assets/avatar.jpg -------------------------------------------------------------------------------- /api/client/README.md: -------------------------------------------------------------------------------- 1 | ## Client 2 | 3 | This is the place for your application front-end files. 4 | -------------------------------------------------------------------------------- /api/common/models/posts.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(Posts) { 4 | 5 | }; 6 | -------------------------------------------------------------------------------- /api/uploads/files/as.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vah7id/flashboard/HEAD/api/uploads/files/as.jpg -------------------------------------------------------------------------------- /docs/_static/comment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vah7id/flashboard/HEAD/docs/_static/comment.png -------------------------------------------------------------------------------- /api/common/models/enquiries.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(Enquiries) { 4 | 5 | }; 6 | -------------------------------------------------------------------------------- /api/common/models/uploads.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(Uploads) { 4 | 5 | }; 6 | -------------------------------------------------------------------------------- /api/server/models/uploads.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(Uploads) { 4 | 5 | }; 6 | -------------------------------------------------------------------------------- /docs/_static/up-pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vah7id/flashboard/HEAD/docs/_static/up-pressed.png -------------------------------------------------------------------------------- /docs/_static/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vah7id/flashboard/HEAD/docs/_static/ajax-loader.gif -------------------------------------------------------------------------------- /docs/_static/comment-close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vah7id/flashboard/HEAD/docs/_static/comment-close.png -------------------------------------------------------------------------------- /docs/_static/down-pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vah7id/flashboard/HEAD/docs/_static/down-pressed.png -------------------------------------------------------------------------------- /docs/_static/comment-bright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vah7id/flashboard/HEAD/docs/_static/comment-bright.png -------------------------------------------------------------------------------- /docs/_static/fonts/Lato-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vah7id/flashboard/HEAD/docs/_static/fonts/Lato-Bold.ttf -------------------------------------------------------------------------------- /docs/_static/fonts/Lato-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vah7id/flashboard/HEAD/docs/_static/fonts/Lato-Regular.ttf -------------------------------------------------------------------------------- /src/assets/page-cannot-be-found.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vah7id/flashboard/HEAD/src/assets/page-cannot-be-found.jpg -------------------------------------------------------------------------------- /api/server/component-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "loopback-component-explorer": { 3 | "mountPath": "/explorer" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /docs/_static/fonts/Inconsolata-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vah7id/flashboard/HEAD/docs/_static/fonts/Inconsolata-Bold.ttf -------------------------------------------------------------------------------- /docs/_static/fonts/RobotoSlab-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vah7id/flashboard/HEAD/docs/_static/fonts/RobotoSlab-Bold.ttf -------------------------------------------------------------------------------- /docs/_static/fonts/Inconsolata-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vah7id/flashboard/HEAD/docs/_static/fonts/Inconsolata-Regular.ttf -------------------------------------------------------------------------------- /docs/_static/fonts/RobotoSlab-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vah7id/flashboard/HEAD/docs/_static/fonts/RobotoSlab-Regular.ttf -------------------------------------------------------------------------------- /docs/_static/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vah7id/flashboard/HEAD/docs/_static/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /docs/_static/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vah7id/flashboard/HEAD/docs/_static/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /docs/_static/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vah7id/flashboard/HEAD/docs/_static/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/ 4 | api/uploads 5 | npm-debug.log 6 | test/unit/coverage 7 | test/e2e/reports 8 | selenium-debug.log 9 | -------------------------------------------------------------------------------- /api/uploads/files/5d4f5de8-39b2-43f4-8e3a-6909c15722c6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vah7id/flashboard/HEAD/api/uploads/files/5d4f5de8-39b2-43f4-8e3a-6909c15722c6.jpg -------------------------------------------------------------------------------- /config/test.env.js: -------------------------------------------------------------------------------- 1 | var merge = require('webpack-merge') 2 | var devEnv = require('./dev.env') 3 | 4 | module.exports = merge(devEnv, { 5 | NODE_ENV: '"testing"' 6 | }) 7 | -------------------------------------------------------------------------------- /api/server/boot/authentication.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function enableAuthentication(server) { 4 | // enable authentication 5 | server.enableAuth(); 6 | }; 7 | -------------------------------------------------------------------------------- /docs/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.associations": { 4 | "*.html": "liquid" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-2"], 3 | "plugins": ["transform-runtime"], 4 | "comments": false, 5 | "env": { 6 | "test": { 7 | "plugins": [ "istanbul" ] 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /api/server/middleware.development.json: -------------------------------------------------------------------------------- 1 | { 2 | "final:after": { 3 | "strong-error-handler": { 4 | "params": { 5 | "debug": true, 6 | "log": true 7 | } 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /config/configs.js: -------------------------------------------------------------------------------- 1 | 2 | var env = 'development'//process.env.APP_ENV || 3 | 4 | var config = { 5 | development: require('./dev.env.js'), 6 | production: require('./prod.env.js'), 7 | staging: require('./test.env.js') 8 | } 9 | 10 | module.exports = config[env] -------------------------------------------------------------------------------- /api/server/models/uploads.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Uploads", 3 | "base": "PersistedModel", 4 | "idInjection": true, 5 | "options": { 6 | "validateUpsert": true 7 | }, 8 | "properties": {}, 9 | "validations": [], 10 | "relations": {}, 11 | "acls": [], 12 | "methods": {} 13 | } 14 | -------------------------------------------------------------------------------- /api/common/models/uploads.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "uploads", 3 | "base": "Model", 4 | "idInjection": true, 5 | "options": { 6 | "validateUpsert": true, 7 | "hidden":true 8 | }, 9 | "properties": {}, 10 | "validations": [], 11 | "relations": {}, 12 | "acls": [], 13 | "methods": {} 14 | } 15 | -------------------------------------------------------------------------------- /api/server/boot/debug.js: -------------------------------------------------------------------------------- 1 | module.exports = function(app) { 2 | var User = app.loopback.getModel('User'); 3 | User.settings.acls = [ 4 | { 5 | "accessType": "EXECUTE", 6 | "principalType": "ROLE", 7 | "principalId": "$owner", 8 | "permission": "ALLOW", 9 | "property": "__get__orders" 10 | }]; 11 | }; -------------------------------------------------------------------------------- /api/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # http://editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | indent_style = space 9 | indent_size = 2 10 | end_of_line = lf 11 | charset = utf-8 12 | trim_trailing_whitespace = true 13 | insert_final_newline = true 14 | -------------------------------------------------------------------------------- /config/dev.env.js: -------------------------------------------------------------------------------- 1 | var merge = require('webpack-merge') 2 | var prodEnv = require('./prod.env') 3 | 4 | 5 | module.exports = merge(prodEnv, { 6 | //Application Configs Json 7 | NODE_ENV: '"development"', 8 | API_BASE_URL: 'http://127.0.0.1', 9 | API_PORT: 3000, 10 | THEME: 'carbon', 11 | LOGO: 'src/assets/logo.png', 12 | BRAND: 'FLASHBOARD', 13 | forbidden_download: false, 14 | disable_admin:false, 15 | lang: "en", 16 | rtl: false, 17 | GOOGLE_VIEWID: '157657595' 18 | }) 19 | -------------------------------------------------------------------------------- /api/server/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "restApiRoot": "/api", 3 | "host": "0.0.0.0", 4 | "port": 3000, 5 | "remoting": { 6 | "context": false, 7 | "rest": { 8 | "normalizeHttpPath": false, 9 | "xml": false 10 | }, 11 | "json": { 12 | "strict": false, 13 | "limit": "10000kb" 14 | }, 15 | "urlencoded": { 16 | "extended": true, 17 | "limit": "10000kb" 18 | }, 19 | "cors": true, 20 | "handleErrors": false 21 | }, 22 | "legacyExplorer": false 23 | } 24 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | FLASHBOARD 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /api/server/datasources.json: -------------------------------------------------------------------------------- 1 | { 2 | "db": { 3 | "name": "db", 4 | "connector": "remote" 5 | }, 6 | "flashDB": { 7 | "host": "198.199.102.94", 8 | "port": 27017, 9 | "url": "mongodb://198.199.102.94:27017/flashboard", 10 | "database": "flashboard", 11 | "password": "", 12 | "name": "flashDB", 13 | "user": "", 14 | "connector": "mongodb" 15 | }, 16 | "container": { 17 | "name": "container", 18 | "connector": "loopback-component-storage", 19 | "provider": "filesystem", 20 | "maxFileSize": "1000048576", 21 | "root": "uploads" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 3 | // User object will let us check authentication status 4 | store: {} , 5 | localStorage: window.localStorage, 6 | 7 | get(key){ 8 | return this.localStorage.getItem(key); 9 | }, 10 | 11 | set(key,value){ 12 | this.localStorage.setItem(key,value); 13 | this.store[key] = value; 14 | }, 15 | 16 | remove(key){ 17 | this.localStorage.removeItem(key); 18 | delete this.store[key]; 19 | }, 20 | 21 | clearAll(){ 22 | console.log('deletre kon dgee') 23 | this.localStorage.clear(); 24 | }, 25 | 26 | state(){ 27 | return this.store; 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /api/server/boot/upload.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(Uploads) { 4 | 5 | Uploads.upload = function(req, res, Uploads_id, cb) { 6 | 7 | var StorageContainer = Uploads.app.models.StorageContainer; 8 | 9 | StorageContainer.getContainers(function (err, containers) { 10 | if (containers.some(function(e) { return e.name == Uploads_id; })) { 11 | StorageContainer.upload(req, res, {container: Uploads_id}, cb); 12 | } 13 | else { 14 | StorageContainer.createContainer({name: Uploads_id}, function(err, c) { 15 | StorageContainer.upload(req, res, {container: c.name}, cb); 16 | }); 17 | } 18 | }); 19 | }; 20 | 21 | }; -------------------------------------------------------------------------------- /api/common/models/enquiries.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "enquiries", 3 | "base": "PersistedModel", 4 | "strict": false, 5 | "idInjection": false, 6 | "options": { 7 | "validateUpsert": true, 8 | "nocreate": true 9 | }, 10 | "properties": { 11 | "id": { 12 | "type": "string", 13 | "id": true, 14 | "index": true 15 | }, 16 | "title": { 17 | "type": "string", 18 | "required": true, 19 | "defaultColumn": true 20 | }, 21 | "message": { 22 | "type": "string", 23 | "uiType": "textarea" 24 | }, 25 | "type": { 26 | "type": "string", 27 | "uiType": "select", 28 | "options": [{"value":"emergency","label":"Emergency"},{"value":"not_emergency","label":"Not Emergency"}] 29 | } 30 | }, 31 | "validations": [], 32 | "relations": {}, 33 | "acls": [], 34 | "methods": {} 35 | } 36 | -------------------------------------------------------------------------------- /api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lemon-api", 3 | "version": "1.0.0", 4 | "main": "server/server.js", 5 | "scripts": { 6 | "lint": "eslint .", 7 | "start": "node .", 8 | "posttest": "npm run lint && nsp check" 9 | }, 10 | "dependencies": { 11 | "compression": "^1.0.3", 12 | "cors": "^2.5.2", 13 | "googleapis": "^20.1.0", 14 | "helmet": "^1.3.0", 15 | "loopback": "^2.22.0", 16 | "loopback-boot": "^2.6.5", 17 | "loopback-component-explorer": "^2.4.0", 18 | "loopback-component-storage": "^3.2.0", 19 | "loopback-connector-mongodb": "^3.0.1", 20 | "loopback-connector-mysql": "^3.0.0", 21 | "loopback-datasource-juggler": "^2.39.0", 22 | "serve-favicon": "^2.0.1", 23 | "strong-error-handler": "^1.0.1" 24 | }, 25 | "devDependencies": { 26 | "eslint": "^2.13.1", 27 | "eslint-config-loopback": "^4.0.0", 28 | "nsp": "^2.1.0" 29 | }, 30 | "repository": { 31 | "type": "", 32 | "url": "" 33 | }, 34 | "license": "UNLICENSED", 35 | "description": "lemon-api" 36 | } 37 | -------------------------------------------------------------------------------- /api/server/middleware.json: -------------------------------------------------------------------------------- 1 | { 2 | "initial:before": { 3 | "loopback#favicon": {} 4 | }, 5 | "initial": { 6 | "compression": {}, 7 | "cors": { 8 | "params": { 9 | "origin": true, 10 | "credentials": true, 11 | "maxAge": 86400 12 | } 13 | }, 14 | "helmet#xssFilter": {}, 15 | "helmet#frameguard": { 16 | "params": [ 17 | "deny" 18 | ] 19 | }, 20 | "helmet#hsts": { 21 | "params": { 22 | "maxAge": 0, 23 | "includeSubdomains": true 24 | } 25 | }, 26 | "helmet#hidePoweredBy": {}, 27 | "helmet#ieNoOpen": {}, 28 | "helmet#noSniff": {}, 29 | "helmet#noCache": { 30 | "enabled": false 31 | } 32 | }, 33 | "session": {}, 34 | "auth": {}, 35 | "parse": {}, 36 | "routes": { 37 | "loopback#rest": { 38 | "paths": [ 39 | "${restApiRoot}" 40 | ] 41 | } 42 | }, 43 | "files": {}, 44 | "final": { 45 | "loopback#urlNotFound": {} 46 | }, 47 | "final:after": { 48 | "strong-error-handler": {} 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /api/server/boot/script.js: -------------------------------------------------------------------------------- 1 | module.exports = function (app) { 2 | var _ = require('lodash'); 3 | var User = app.models.User; 4 | var Role = app.models.Role; 5 | var RoleMapping = app.models.RoleMapping; 6 | var ACL = app.models.ACL; 7 | 8 | User.create([{ 9 | username: 'flashboard', 10 | email: 'admin@flashboard.com', 11 | password: 'qwertyuiop', 12 | type: 'admin' 13 | }], function(err, users) { 14 | 15 | Role.find({ name: 'admin' }, function(err, results) { 16 | //if (err) console.log(err); 17 | 18 | // Create the admin role 19 | Role.create({ 20 | name: 'admin' 21 | }, function(err, role) { 22 | if (err); 23 | //debug(role); 24 | role.principals.create({ 25 | principalType: RoleMapping.USER, 26 | principalId: users[0].id 27 | }, function(err, principal) { 28 | if (err) console.log(err); 29 | 30 | }); 31 | }); 32 | 33 | //if (results.length < 1) { 34 | // now we know the DB doesn't have it already, so do the Role creation... 35 | // } 36 | }); 37 | 38 | }); 39 | }; 40 | 41 | -------------------------------------------------------------------------------- /api/server/server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var loopback = require('loopback'); 4 | var boot = require('loopback-boot'); 5 | 6 | var app = module.exports = loopback(); 7 | 8 | app.start = function() { 9 | // start the web server 10 | return app.listen(function() { 11 | app.emit('started'); 12 | var baseUrl = app.get('url').replace(/\/$/, ''); 13 | 14 | console.log('\x1b[32m', '\n******************************************* \n') 15 | console.log('\x1b[32m', 'FLASHBOARD Services started successfully :) \n'); 16 | console.log('\x1b[32m', '******************************************* \n'); 17 | 18 | console.log('Web server listening at: %s', baseUrl); 19 | 20 | if (app.get('loopback-component-explorer')) { 21 | var explorerPath = app.get('loopback-component-explorer').mountPath; 22 | console.log('Browse your REST API at %s%s', baseUrl, explorerPath); 23 | } 24 | }); 25 | }; 26 | 27 | // Bootstrap the application, configure models, datasources and middleware. 28 | // Sub-apps like REST API are mounted via boot scripts. 29 | boot(app, __dirname, function(err) { 30 | if (err) throw err; 31 | 32 | // start the server if `$ node server.js` 33 | if (require.main === module) 34 | app.start(); 35 | }); 36 | -------------------------------------------------------------------------------- /api/common/models/posts.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "posts", 3 | "base": "PersistedModel", 4 | "strict": false, 5 | "idInjection": false, 6 | "options": { 7 | "validateUpsert": true 8 | }, 9 | "properties": { 10 | "id": { 11 | "type": "string", 12 | "id": true, 13 | "index": true 14 | }, 15 | "title": { 16 | "type": "string", 17 | "required": true, 18 | "defaultColumn": true 19 | }, 20 | "thumbnail": { 21 | "type": "Object", 22 | "uiType": "file", 23 | "options": { 24 | "maxSize": "20000" 25 | } 26 | }, 27 | "author": { 28 | "label": "author", 29 | "type": "string", 30 | "uiType": "Relationship", 31 | "options": { 32 | "ref": "Users", 33 | "filter": { "type": "user" }, 34 | "key": "email" 35 | } 36 | }, 37 | "description": { 38 | "type": "string", 39 | "uiType": "html", 40 | "options": { "placeholder": "Some text...", "theme": "snow" } 41 | }, 42 | "published_date": { 43 | "type": "string", 44 | "uiType": "date", 45 | "defaultColumn": true 46 | 47 | } 48 | }, 49 | "validations": [], 50 | "relations": {}, 51 | "acls": [], 52 | "methods": {} 53 | } 54 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | // see http://vuejs-templates.github.io/webpack for documentation. 2 | var path = require('path') 3 | 4 | module.exports = { 5 | build: { 6 | env: require('./prod.env'), 7 | index: path.resolve(__dirname, '../index.html'), 8 | assetsRoot: path.resolve(__dirname, '../dist'), 9 | assetsSubDirectory: 'dist/static', 10 | assetsPublicPath: 'dist', 11 | productionSourceMap: false, 12 | // Gzip off by default as many popular static hosts such as 13 | // Surge or Netlify already gzip all static assets for you. 14 | // Before setting to `true`, make sure to: 15 | // npm install --save-dev compression-webpack-plugin 16 | productionGzip: false, 17 | productionGzipExtensions: ['js', 'css'] 18 | }, 19 | dev: { 20 | env: require('./dev.env'), 21 | port: 8080, 22 | assetsSubDirectory: 'static', 23 | assetsPublicPath: '/', 24 | proxyTable: {}, 25 | // CSS Sourcemaps off by default because relative paths are "buggy" 26 | // with this option, according to the CSS-Loader README 27 | // (https://github.com/webpack/css-loader#sourcemaps) 28 | // In our experience, they generally work as expected, 29 | // just be aware of this issue when enabling this option. 30 | cssSourceMap: false 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/stylesheets/rtl.css: -------------------------------------------------------------------------------- 1 | 2 | .mu-raised-button-wrapper{ 3 | padding: 10px !important; 4 | } 5 | 6 | a{color: #2196f3} 7 | body{ 8 | direction: rtl; 9 | text-align: right; 10 | font-family: Vazir !important; 11 | } 12 | .count{ 13 | font-family: Vazir !important; 14 | } 15 | h1,h2,h3,h4{ 16 | font-weight: 900; 17 | } 18 | .mu-text-field-input{ 19 | text-align: right !important; 20 | } 21 | .mu-drawer{ 22 | right: 0 !important; 23 | left: initial !important; 24 | } 25 | .page--title{ 26 | float: right !important; 27 | } 28 | #page--title{ 29 | padding-left: 0 !important; 30 | } 31 | 32 | .ripple,.ripple i{ 33 | float: right !important; 34 | } 35 | .ripple i{ 36 | padding-right: 10px !important; 37 | } 38 | .mu-content-block-board{ 39 | float: left; 40 | } 41 | 42 | .mu-paper{ 43 | float: right; 44 | } 45 | .focus-state .mu-text-field-label,.no-empty-state .mu-text-field-label{ 46 | right: -20px !important; 47 | } 48 | .btn-add{ 49 | float: left; 50 | margin-left: 30px !important; 51 | } 52 | .item-uploaded{ 53 | float: right; 54 | } 55 | .mu-drawer .mu-avatar{ 56 | float: right; 57 | } 58 | #btn-delete{ 59 | right: initial !important; 60 | left: 30px !important; 61 | } 62 | .mu-snackbar{ 63 | right: initial !important; 64 | left: 5% !important; 65 | } 66 | .mu-appbar-title{ 67 | padding-right: 55px !important; 68 | } 69 | 70 | /*# sourceMappingURL=rtl.css.map */ -------------------------------------------------------------------------------- /api/server/model-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "sources": [ 4 | "loopback/common/models", 5 | "loopback/server/models", 6 | "../common/models", 7 | "./models" 8 | ], 9 | "mixins": [ 10 | "loopback/common/mixins", 11 | "loopback/server/mixins", 12 | "../common/mixins", 13 | "./mixins" 14 | ] 15 | }, 16 | "User": { 17 | "dataSource": "flashDB", 18 | "public": true 19 | }, 20 | "AccessToken": { 21 | "dataSource": "flashDB", 22 | "public": false 23 | }, 24 | "ACL": { 25 | "dataSource": "flashDB", 26 | "public": true 27 | }, 28 | "RoleMapping": { 29 | "dataSource": "flashDB", 30 | "public": false 31 | }, 32 | "Role": { 33 | "dataSource": "flashDB", 34 | "public": true, 35 | "$options": { 36 | "base": "Role", 37 | "relations": { 38 | "principals": { 39 | "type": "hasMany", 40 | "model": "roleMapping", 41 | "foreignKey": "roleId" 42 | } 43 | } 44 | } 45 | }, 46 | "posts": { 47 | "dataSource": "flashDB", 48 | "public": true, 49 | "$promise": {}, 50 | "$resolved": true 51 | }, 52 | "enquiries": { 53 | "dataSource": "flashDB", 54 | "public": true, 55 | "$promise": {}, 56 | "$resolved": true 57 | }, 58 | "uploads": { 59 | "dataSource": "container", 60 | "public": true 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /api/server/boot/root.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var google = require('googleapis'); 4 | //const key = require('google.json'); 5 | 6 | var env = require("../../../config/configs.js"); 7 | 8 | module.exports = function(server) { 9 | // Install a `/` route that returns server status 10 | var router = server.loopback.Router(); 11 | router.get('/', server.loopback.status()); 12 | 13 | router.get('/googleAnalytics', function(req, res) { 14 | 15 | const VIEW_ID = 'ga:'+env.GOOGLE_VIEWID; 16 | 17 | if(require.resolve('google.json')){ 18 | var key = require('google.json'); 19 | } else { 20 | console.log('google json file dosn"t exist'); 21 | return; 22 | } 23 | let jwtClient = new google.auth.JWT( 24 | key.client_email, null, key.private_key, 25 | ['https://www.googleapis.com/auth/analytics.readonly'], null); 26 | 27 | jwtClient.authorize(function (err, tokens) { 28 | 29 | if (err) { 30 | console.log(err); 31 | return; 32 | } 33 | 34 | let analytics = google.analytics('v3'); 35 | 36 | analytics.data.ga.get({ 37 | 'auth': jwtClient, 38 | 'ids': VIEW_ID, 39 | 'metrics': 'ga:users,ga:pageviews', 40 | 'dimensions': 'ga:date', 41 | 'start-date': '30daysAgo', 42 | 'end-date': 'today', 43 | 'max-results': 31 44 | }, function (err, response) { 45 | if (err) { 46 | console.log(err); 47 | return; 48 | } 49 | res.status(200).json(response); 50 | }); 51 | 52 | }); 53 | 54 | }); 55 | 56 | server.use(router); 57 | }; 58 | -------------------------------------------------------------------------------- /npm-debug.log.746407735: -------------------------------------------------------------------------------- 1 | 0 info it worked if it ends with ok 2 | 1 verbose cli [ '/usr/local/bin/node', '/usr/local/bin/npm', 'run', 'de' ] 3 | 2 info using npm@4.1.2 4 | 3 info using node@v7.6.0 5 | 4 verbose stack Error: missing script: de 6 | 4 verbose stack at run (/usr/local/lib/node_modules/npm/lib/run-script.js:151:19) 7 | 4 verbose stack at /usr/local/lib/node_modules/npm/lib/run-script.js:61:5 8 | 4 verbose stack at /usr/local/lib/node_modules/npm/node_modules/read-package-json/read-json.js:356:5 9 | 4 verbose stack at checkBinReferences_ (/usr/local/lib/node_modules/npm/node_modules/read-package-json/read-json.js:320:45) 10 | 4 verbose stack at final (/usr/local/lib/node_modules/npm/node_modules/read-package-json/read-json.js:354:3) 11 | 4 verbose stack at then (/usr/local/lib/node_modules/npm/node_modules/read-package-json/read-json.js:124:5) 12 | 4 verbose stack at /usr/local/lib/node_modules/npm/node_modules/read-package-json/read-json.js:311:12 13 | 4 verbose stack at /usr/local/lib/node_modules/npm/node_modules/graceful-fs/graceful-fs.js:78:16 14 | 4 verbose stack at tryToString (fs.js:447:3) 15 | 4 verbose stack at FSReqWrap.readFileAfterClose [as oncomplete] (fs.js:434:12) 16 | 5 verbose cwd /Users/vahid/Documents/Development/flashboard 17 | 6 error Darwin 14.0.0 18 | 7 error argv "/usr/local/bin/node" "/usr/local/bin/npm" "run" "de" 19 | 8 error node v7.6.0 20 | 9 error npm v4.1.2 21 | 10 error missing script: de 22 | 11 error If you need help, you may report this error at: 23 | 11 error 24 | 12 verbose exit [ 1, true ] 25 | -------------------------------------------------------------------------------- /src/langs/langs.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | 5 | "en":{ 6 | "sign_in": "Sign In", 7 | "sign_in_msg": "Sign In To", 8 | "email":"email", 9 | "password":"password", 10 | "username or email is required":"username or email is required", 11 | "login failed": "login failed", 12 | "edit_profile":"Edit Profile", 13 | "sign_out":"Sign Out", 14 | "Dashboard": "Dashboard", 15 | "api_explorer":"Api Explorer", 16 | "api_docs":"API Documentation", 17 | "items": "items", 18 | "create_item": "Create Item", 19 | "edit_item":"Edit Item", 20 | "upload_now": "Upload Now", 21 | "save_item": "Save Item", 22 | "actions": "Actions", 23 | "total":"Total", 24 | "columns": "Columns", 25 | "search":"Search", 26 | "download":"Download", 27 | "json_object":"JSON Object", 28 | "csv_format":"CSV Format", 29 | "edit":"Edit", 30 | "empty_list":"Empty list items" 31 | }, 32 | "fa": { 33 | "sign_in": "ورود به پیشخوان", 34 | "sign_in_msg": "ورود به", 35 | "email":"پست الکترونیکی", 36 | "password":"کلمه عبور", 37 | "username or email is required":"پست الکترونیکی یا کلمه عب.ر اشتباه وارد شده است", 38 | "login failed": "مشکل در ورود به سیستم", 39 | "edit_profile":"ویرایش پروفایل", 40 | "sign_out":"خروج", 41 | "dashboard": "پیشخوان", 42 | "api_explorer":"لیست سرویس ها", 43 | "api_docs":"مستندات فنی", 44 | "items": "مورد", 45 | "create_item": "اضافه کردن", 46 | "edit_item":"ویرایش اطلاعات", 47 | "upload_now": "بارگذاری فایل", 48 | "save_item": "دخیره اطلاعات", 49 | "actions": "عملیات", 50 | "total":"مورد", 51 | "columns": "ستون ها", 52 | "search":"جستجو", 53 | "download":"دانلود", 54 | "json_object":"JSON Object", 55 | "csv_format":"CSV Format", 56 | "edit":"ویرایش", 57 | "empty_list":"اطلاعاتی موجود نیست" 58 | } 59 | 60 | } -------------------------------------------------------------------------------- /src/components/Unavailable.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 30 | 31 | -------------------------------------------------------------------------------- /src/filters/index.js: -------------------------------------------------------------------------------- 1 | const urlParser = document.createElement('a'); 2 | const request = require("browser-request"); 3 | 4 | import store from '../store'; 5 | 6 | export function checkAuth (url) { 7 | 8 | var token = store.get('flashboard_token'); 9 | 10 | if(token != null){ 11 | request({method:'GET', 12 | url: url+'Users/findOne', 13 | headers: { 14 | 'Authorization': token, 15 | 'origin-when-cross-origin':'*' 16 | }, 17 | "xhrFields": { "withCredentials": true } 18 | }, function (er, response, body) { 19 | if(er){ 20 | window.offline_mode = true; 21 | window.location.assign('#/unavailable?url='+window.location.hash); 22 | throw er 23 | } 24 | 25 | if(typeof JSON.parse(body).error != "undefined"){ 26 | window.offline_mode = false; 27 | 28 | if(JSON.parse(body).error['statusCode']==401){ 29 | window.location.assign('#/login'); 30 | } else{ 31 | } 32 | } 33 | 34 | }); 35 | } else { 36 | console.log('boro berim') 37 | window.location.assign('#/login'); 38 | } 39 | 40 | } 41 | 42 | export function loading(mode){ 43 | if(mode!='start') 44 | document.querySelector('.mu-linear-progress').classList.add('hide'); 45 | else 46 | document.querySelector('.mu-linear-progress').classList.remove('hide'); 47 | } 48 | 49 | export function domain (url) { 50 | urlParser.href = url 51 | return urlParser.hostname 52 | } 53 | 54 | export function fromNow (time) { 55 | const between = Date.now() / 1000 - Number(time) 56 | if (between < 3600) { 57 | return pluralize(~~(between / 60), ' minute') 58 | } else if (between < 86400) { 59 | return pluralize(~~(between / 3600), ' hour') 60 | } else { 61 | return pluralize(~~(between / 86400), ' day') 62 | } 63 | } 64 | 65 | function pluralize(time, label) { 66 | if (time === 1) { 67 | return time + label 68 | } 69 | 70 | return time + label + 's'; 71 | } 72 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flashboard", 3 | "version": "1.0.0", 4 | "description": "flashboard handle the back end, you handle the front", 5 | "author": "VT ", 6 | "private": false, 7 | "scripts": { 8 | "service": "cd api && node .", 9 | "preinstall": "cd api && npm install", 10 | "build": "node build/build.js", 11 | "postinstall": "npm install --dev && npm run build", 12 | "dev": "node build/dev-server.js" 13 | }, 14 | "dependencies": { 15 | "hchs-vue-charts": "^1.2.8", 16 | "vue": "^2.1.0", 17 | "vue-router": "^2.2.0" 18 | }, 19 | "devDependencies": { 20 | "autoprefixer": "^6.4.0", 21 | "babel-core": "^6.0.0", 22 | "babel-loader": "^6.0.0", 23 | "babel-plugin-istanbul": "^3.0.0", 24 | "babel-plugin-transform-runtime": "^6.0.0", 25 | "babel-preset-es2015": "^6.0.0", 26 | "babel-preset-stage-2": "^6.0.0", 27 | "babel-register": "^6.0.0", 28 | "chai": "^3.5.0", 29 | "chalk": "^1.1.3", 30 | "connect-history-api-fallback": "^1.1.0", 31 | "cross-env": "^3.1.3", 32 | "cross-spawn": "^4.0.2", 33 | "css-loader": "^0.25.0", 34 | "eventsource-polyfill": "^0.9.6", 35 | "express": "^4.13.3", 36 | "extract-text-webpack-plugin": "^1.0.1", 37 | "file-loader": "^0.9.0", 38 | "friendly-errors-webpack-plugin": "^1.1.2", 39 | "function-bind": "^1.0.2", 40 | "html-webpack-plugin": "^2.8.1", 41 | "http-proxy-middleware": "^0.17.2", 42 | "inject-loader": "^2.0.1", 43 | "json-loader": "^0.5.4", 44 | "lolex": "^1.4.0", 45 | "nightwatch": "^0.9.8", 46 | "opn": "^4.0.2", 47 | "ora": "^0.3.0", 48 | "cors": "^2.8.3", 49 | "phantomjs-prebuilt": "^2.1.3", 50 | "selenium-server": "2.53.1", 51 | "semver": "^5.3.0", 52 | "shelljs": "^0.7.4", 53 | "codemirror": "^5.25.0", 54 | "simple-color-picker": "^0.1.2", 55 | "whatwg-fetch": "^2.0.3", 56 | "browser-request": "^0.3.3", 57 | "muse-ui": "^2.0.3", 58 | "url-loader": "^0.5.7", 59 | "vue-quill-editor": "^2.0.0", 60 | "vue-loader": "^10.0.0", 61 | "vue-style-loader": "^1.0.0", 62 | "vue-template-compiler": "^2.1.0", 63 | "webpack": "^1.13.2", 64 | "webpack-dev-middleware": "^1.8.3", 65 | "webpack-hot-middleware": "^2.12.2", 66 | "webpack-merge": "^0.14.1" 67 | }, 68 | "engines": { 69 | "node": ">= 4.0.0", 70 | "npm": ">= 3.0.0" 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /docs/_static/css/badge_only.css: -------------------------------------------------------------------------------- 1 | .fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-weight:normal;font-style:normal;src:url("../font/fontawesome_webfont.eot");src:url("../font/fontawesome_webfont.eot?#iefix") format("embedded-opentype"),url("../font/fontawesome_webfont.woff") format("woff"),url("../font/fontawesome_webfont.ttf") format("truetype"),url("../font/fontawesome_webfont.svg#FontAwesome") format("svg")}.fa:before{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa{display:inline-block;text-decoration:inherit}li .fa{display:inline-block}li .fa-large:before,li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.fas li .fa{width:0.8em}ul.fas li .fa-large:before,ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before{content:""}.icon-book:before{content:""}.fa-caret-down:before{content:""}.icon-caret-down:before{content:""}.fa-caret-up:before{content:""}.icon-caret-up:before{content:""}.fa-caret-left:before{content:""}.icon-caret-left:before{content:""}.fa-caret-right:before{content:""}.icon-caret-right:before{content:""}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980B9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27AE60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#E74C3C;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#F1C40F;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}} 2 | /*# sourceMappingURL=badge_only.css.map */ 3 | -------------------------------------------------------------------------------- /api/common/User.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "User", 3 | "properties": { 4 | "realm": { 5 | "type": "string" 6 | }, 7 | "username": { 8 | "type": "string", 9 | "defaultColumn":true 10 | }, 11 | "type": { 12 | "type": "string", 13 | "uiType": "select", 14 | "default": "user", 15 | "options": [ 16 | { 17 | "value": "admin", 18 | "label": "Administrator" 19 | }, 20 | { 21 | "value": "user", 22 | "label": "User" 23 | } 24 | ] 25 | }, 26 | "password": { 27 | "type": "string", 28 | "uiType": "password", 29 | "required": true 30 | }, 31 | "email": { 32 | "type": "string", 33 | "required": true, 34 | "defaultColumn":true 35 | }, 36 | "emailVerified": {"type":"boolean"}, 37 | "verificationToken": {"type":"string"} 38 | }, 39 | "options": { 40 | "caseSensitiveEmail": true 41 | }, 42 | "hidden": ["verificationToken","realm"], 43 | "acls": [ 44 | { 45 | "principalType": "ROLE", 46 | "principalId": "$everyone", 47 | "permission": "DENY" 48 | }, 49 | { 50 | "principalType": "ROLE", 51 | "principalId": "$everyone", 52 | "permission": "ALLOW", 53 | "property": "create" 54 | }, 55 | { 56 | "principalType": "ROLE", 57 | "principalId": "$owner", 58 | "permission": "ALLOW", 59 | "property": "deleteById" 60 | }, 61 | { 62 | "principalType": "ROLE", 63 | "principalId": "$everyone", 64 | "permission": "ALLOW", 65 | "property": "login" 66 | }, 67 | { 68 | "principalType": "ROLE", 69 | "principalId": "$everyone", 70 | "permission": "ALLOW", 71 | "property": "logout" 72 | }, 73 | { 74 | "principalType": "ROLE", 75 | "principalId": "$owner", 76 | "permission": "ALLOW", 77 | "property": "findById" 78 | }, 79 | { 80 | "principalType": "ROLE", 81 | "principalId": "$owner", 82 | "permission": "ALLOW", 83 | "property": "patchAttributes" 84 | }, 85 | { 86 | "principalType": "ROLE", 87 | "principalId": "$owner", 88 | "permission": "ALLOW", 89 | "property": "replaceById" 90 | }, 91 | { 92 | "principalType": "ROLE", 93 | "principalId": "$everyone", 94 | "permission": "ALLOW", 95 | "property": "verify", 96 | "accessType": "EXECUTE" 97 | }, 98 | { 99 | "principalType": "ROLE", 100 | "principalId": "$everyone", 101 | "permission": "ALLOW", 102 | "property": "confirm" 103 | }, 104 | { 105 | "principalType": "ROLE", 106 | "principalId": "$everyone", 107 | "permission": "ALLOW", 108 | "property": "resetPassword", 109 | "accessType": "EXECUTE" 110 | }, 111 | { 112 | "principalType": "ROLE", 113 | "principalId": "$authenticated", 114 | "permission": "ALLOW", 115 | "property": "changePassword", 116 | "accessType": "EXECUTE" 117 | }, 118 | { 119 | "principalType": "ROLE", 120 | "principalId": "$authenticated", 121 | "permission": "ALLOW", 122 | "property": "setPassword", 123 | "accessType": "EXECUTE" 124 | } 125 | ], 126 | "methodes": ["login","logout"], 127 | "relations": { 128 | "accessTokens": { 129 | "type": "hasMany", 130 | "model": "AccessToken", 131 | "foreignKey": "userId", 132 | "options": { 133 | "disableInclude": true 134 | } 135 | } 136 | } 137 | } -------------------------------------------------------------------------------- /src/components/Login.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 41 | 42 | -------------------------------------------------------------------------------- /src/components/Sidebar.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 53 | 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # flashboard handle the back end, you handle the front 2 | 3 | > flashboard can handle your whole back-end includes services, rest-full api and admin dashboard based on loopback node.js framework. 4 | Simply can define your models with several options and get your rest-full api and admin dashboard automatically. 5 | 6 | Documentations: [https://vah7id.github.io/flashboard/start.html](https://vah7id.github.io/flashboard/start.html) 7 | 8 | Admin demo : [http://138.68.46.113/flashboard](http://138.68.46.113/flashboard) 9 | credentials: 10 | email: ``` admin@flashboard.com ``` 11 | password: ``` qwertyuiop ``` 12 | 13 | ![alt](https://vah7id.github.io/flashboard/demo.jpg) 14 | 15 | ## Getting Started 16 | The getting started guide walks you through how to manually setup a admin dashboard. flashboard actually create your back-end package services such as rest-full api of your models based on loopback node.js framework and generate admin dashboard automatically for data moderation. 17 | 18 | ## Models 19 | In this part we can learn how to create own models and start to build your back-end project. there are several options to create your models and configurations options. 20 | 21 | ## Configuration 22 | Flashboard create a admin dashboard based on configuration of loopback models. there are several settings and options on your models and user interface generation types and formats. 23 | 24 | ## Rest-full API 25 | After create your models you have a rest-full api based on your models. In this section we want to learn how to use generated api’s. Also there are options to create your own remote functions. 26 | 27 | 28 | ## Installation 29 | 30 | Before you begin, make sure you have Node.js and MongoDB installed. For best results, use the latest LTS (long-term support) release of Node.js. 31 | 32 | Running below command in your empty project directory path : 33 | ``` $ git clone https://github.com/vah7id/flashboard.git ``` 34 | 35 | First install your services packages by run below command on your project directory : 36 | ``` $ npm run preinstall ``` 37 | 38 | Install your admin generator packages by run below command on root directory: 39 | ``` $ npm install ``` 40 | 41 | Running back-end services first before running admin dashboard : 42 | ``` $ npm run service ``` 43 | 44 | At last you can run your admin dashboard by running below command: 45 | ``` $ npm run dev ``` 46 | 47 | for deploy on production you can run build command : 48 | ``` $ npm run build ``` 49 | 50 | ## Changelogs 51 | 52 | Google Analytics Integration for reports 53 | Responsive Admin UI layouts 54 | Add rtl support 55 | Add Multi language support 56 | Fix build issues for production 57 | Add Relational model support for admin 58 | 59 | ## Articles 60 | New generation of back-end services: 61 | https://medium.com/@vah7id/new-generation-of-back-end-services-53b0922ed8e2 62 | 63 | ## Contribute 64 | 65 | Contributions welcome! Like seriously 66 | 67 | If you have a any issue with flashboard feel free to report it in github 68 | 69 | We need to improve the admin dashboard with more options and features. 70 | Next step is creating the GUI for creating and managing models and API's. 71 | 72 | ## License 73 | 74 | (The MIT License) Copyright (c) 2016 Vahid Taghizadeh Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 75 | -------------------------------------------------------------------------------- /docs/_static/pygments.css: -------------------------------------------------------------------------------- 1 | .highlight .hll { background-color: #ffffcc } 2 | .highlight { background: #eeffcc; } 3 | .highlight .c { color: #408090; font-style: italic } /* Comment */ 4 | .highlight .err { border: 1px solid #FF0000 } /* Error */ 5 | .highlight .k { color: #007020; font-weight: bold } /* Keyword */ 6 | .highlight .o { color: #666666 } /* Operator */ 7 | .highlight .ch { color: #408090; font-style: italic } /* Comment.Hashbang */ 8 | .highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */ 9 | .highlight .cp { color: #007020 } /* Comment.Preproc */ 10 | .highlight .cpf { color: #408090; font-style: italic } /* Comment.PreprocFile */ 11 | .highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */ 12 | .highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */ 13 | .highlight .gd { color: #A00000 } /* Generic.Deleted */ 14 | .highlight .ge { font-style: italic } /* Generic.Emph */ 15 | .highlight .gr { color: #FF0000 } /* Generic.Error */ 16 | .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 17 | .highlight .gi { color: #00A000 } /* Generic.Inserted */ 18 | .highlight .go { color: #333333 } /* Generic.Output */ 19 | .highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ 20 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 21 | .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 22 | .highlight .gt { color: #0044DD } /* Generic.Traceback */ 23 | .highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ 24 | .highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ 25 | .highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ 26 | .highlight .kp { color: #007020 } /* Keyword.Pseudo */ 27 | .highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ 28 | .highlight .kt { color: #902000 } /* Keyword.Type */ 29 | .highlight .m { color: #208050 } /* Literal.Number */ 30 | .highlight .s { color: #4070a0 } /* Literal.String */ 31 | .highlight .na { color: #4070a0 } /* Name.Attribute */ 32 | .highlight .nb { color: #007020 } /* Name.Builtin */ 33 | .highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */ 34 | .highlight .no { color: #60add5 } /* Name.Constant */ 35 | .highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */ 36 | .highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */ 37 | .highlight .ne { color: #007020 } /* Name.Exception */ 38 | .highlight .nf { color: #06287e } /* Name.Function */ 39 | .highlight .nl { color: #002070; font-weight: bold } /* Name.Label */ 40 | .highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ 41 | .highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */ 42 | .highlight .nv { color: #bb60d5 } /* Name.Variable */ 43 | .highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */ 44 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 45 | .highlight .mb { color: #208050 } /* Literal.Number.Bin */ 46 | .highlight .mf { color: #208050 } /* Literal.Number.Float */ 47 | .highlight .mh { color: #208050 } /* Literal.Number.Hex */ 48 | .highlight .mi { color: #208050 } /* Literal.Number.Integer */ 49 | .highlight .mo { color: #208050 } /* Literal.Number.Oct */ 50 | .highlight .sa { color: #4070a0 } /* Literal.String.Affix */ 51 | .highlight .sb { color: #4070a0 } /* Literal.String.Backtick */ 52 | .highlight .sc { color: #4070a0 } /* Literal.String.Char */ 53 | .highlight .dl { color: #4070a0 } /* Literal.String.Delimiter */ 54 | .highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ 55 | .highlight .s2 { color: #4070a0 } /* Literal.String.Double */ 56 | .highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ 57 | .highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */ 58 | .highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ 59 | .highlight .sx { color: #c65d09 } /* Literal.String.Other */ 60 | .highlight .sr { color: #235388 } /* Literal.String.Regex */ 61 | .highlight .s1 { color: #4070a0 } /* Literal.String.Single */ 62 | .highlight .ss { color: #517918 } /* Literal.String.Symbol */ 63 | .highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ 64 | .highlight .fm { color: #06287e } /* Name.Function.Magic */ 65 | .highlight .vc { color: #bb60d5 } /* Name.Variable.Class */ 66 | .highlight .vg { color: #bb60d5 } /* Name.Variable.Global */ 67 | .highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */ 68 | .highlight .vm { color: #bb60d5 } /* Name.Variable.Magic */ 69 | .highlight .il { color: #208050 } /* Literal.Number.Integer.Long */ -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from 'vue-router' 3 | 4 | import ModelListView from './components/ModelList.vue' 5 | import ModelEditView from './components/ModelEdit.vue' 6 | import ModelCreateView from './components/ModelCreate.vue' 7 | 8 | import VueQuillEditor from 'vue-quill-editor' 9 | import 'chart.js'; 10 | import VueCharts from 'hchs-vue-charts' 11 | 12 | import store from './store'; 13 | 14 | import LoginView from './components/Login.vue' 15 | import DashboardView from './components/Dashboard.vue' 16 | import UnavailableView from './components/Unavailable.vue' 17 | 18 | import { checkAuth, domain, fromNow } from './filters' 19 | 20 | import App from './components/App.vue' 21 | 22 | import 'whatwg-fetch' 23 | 24 | import env from "../config/configs.js" 25 | import keys from "./langs/langs.js" 26 | 27 | import MuseUI from 'muse-ui' 28 | import 'muse-ui/dist/muse-ui.css' 29 | 30 | require('muse-ui/dist/theme-'+env.THEME+'.css') 31 | 32 | if( env.rtl ){ 33 | require('../src/stylesheets/rtl.css') 34 | } 35 | 36 | Vue.use(MuseUI) 37 | Vue.use(VueQuillEditor) 38 | Vue.use(window.VueCharts); 39 | 40 | store.set('env',JSON.stringify(env)); 41 | 42 | window.api_url = env.API_BASE_URL +':'+env.API_PORT+'/api/'; 43 | 44 | if(env.disable_admin){ 45 | window.location.assign('#/unavailable'); 46 | } 47 | 48 | // install router 49 | 50 | fetch('api/server/model-config.json') 51 | .then(function(response) { 52 | response.json().then(function(models) { 53 | 54 | var _models = models, _counter = 0; 55 | 56 | delete _models.ACL; 57 | delete _models.AccessToken; 58 | delete _models.Role; 59 | delete _models._meta; 60 | delete _models.RoleMapping; 61 | 62 | for(var m in _models){ 63 | var model = _models[m]; 64 | model['name'] = m; 65 | model['icon'] = 'folder'; 66 | 67 | if(m=='User') 68 | model['icon'] = 'people'; 69 | 70 | if(m=='enquiries') 71 | model['icon'] = 'phone'; 72 | 73 | _counter++; 74 | } 75 | 76 | store.set('models',JSON.stringify(_models)) 77 | store.set('models_count',_counter) 78 | 79 | 80 | var _routes = { 81 | routes: [ 82 | { path: '', component: DashboardView, name: 'Dashboard' }, 83 | { path: '/dashboard', component: DashboardView, name: 'dashboard' }, 84 | { path: '/login', component: LoginView, name: 'login' }, 85 | { path: '/unavailable', component: UnavailableView, name: 'unavailable' } 86 | ] 87 | }; 88 | 89 | for(var model in models){ 90 | _routes.routes.push({ path: '/'+model, component: ModelListView, name: model }); 91 | _routes.routes.push({ path: '/'+model+'/create', component: ModelCreateView, name: model+'-create' }); 92 | _routes.routes.push({ path: '/'+model+'/:id', component: ModelEditView , name: model+'-edit' }); 93 | } 94 | 95 | _routes.routes.push({ 96 | path: '*', 97 | redirect: '' 98 | }); 99 | 100 | Vue.use(VueRouter); 101 | 102 | // Create the router instance and pass the `routes` option 103 | // You can pass in additional options here, but let's 104 | // keep it simple for now. 105 | const router = new VueRouter({ 106 | routes: _routes.routes, 107 | history:true 108 | }) 109 | 110 | router.beforeEach(function (to, from, next) { 111 | 112 | window.scrollTo(0, 0); 113 | 114 | if(document.querySelector('.mu-linear-progress') != null) 115 | document.querySelector('.mu-linear-progress').classList.remove('hide'); 116 | 117 | next(); 118 | 119 | }); 120 | 121 | router.afterEach(function (route) { 122 | 123 | var name = route.name; 124 | 125 | if(name.indexOf('-')>0) 126 | name = name.split('-')[0]; 127 | 128 | if(name.toLowerCase() == 'dashboard') 129 | document.querySelector('.mu-linear-progress').classList.add('hide'); 130 | 131 | store.set('current_model',name); 132 | document.title = route.name; 133 | 134 | }); 135 | 136 | //instatinat the vue instance 137 | new Vue({ 138 | //define the selector for the root component 139 | el: '#app', 140 | data(){ 141 | return { 142 | models: [], 143 | keys: keys[env.lang] 144 | } 145 | }, 146 | watch:{ 147 | models: function(val){ 148 | this.$root.models = val; 149 | return val; 150 | }, 151 | $route: function(val){ 152 | if( store.get('flashboard_token') == null ){ 153 | window.location.assign('#/login'); 154 | document.querySelector('.mu-appbar').classList.add('hide') 155 | document.querySelector('.mu-linear-progress').classList.add('hide'); 156 | } 157 | } 158 | }, 159 | //pass the template to the root component 160 | template: '', 161 | //declare components that the root component can access 162 | components: { App }, 163 | //pass in the router to the vue instance 164 | router 165 | }).$mount('#app')//mount the router on the app 166 | 167 | }); 168 | 169 | }).then(function(json) { 170 | 171 | }).catch(function(ex) { 172 | console.log('parsing failed', ex) 173 | }); 174 | 175 | 176 | -------------------------------------------------------------------------------- /src/components/Dashboard.vue: -------------------------------------------------------------------------------- 1 | 50 | 51 | 125 | 126 | -------------------------------------------------------------------------------- /docs/_static/js/theme.js: -------------------------------------------------------------------------------- 1 | require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o"); 80 | 81 | // Add expand links to all parents of nested ul 82 | $('.wy-menu-vertical ul').not('.simple').siblings('a').each(function () { 83 | var link = $(this); 84 | expand = $(''); 85 | expand.on('click', function (ev) { 86 | self.toggleCurrent(link); 87 | ev.stopPropagation(); 88 | return false; 89 | }); 90 | link.prepend(expand); 91 | }); 92 | }; 93 | 94 | nav.reset = function () { 95 | // Get anchor from URL and open up nested nav 96 | var anchor = encodeURI(window.location.hash); 97 | if (anchor) { 98 | try { 99 | var link = $('.wy-menu-vertical') 100 | .find('[href="' + anchor + '"]'); 101 | // If we didn't find a link, it may be because we clicked on 102 | // something that is not in the sidebar (eg: when using 103 | // sphinxcontrib.httpdomain it generates headerlinks but those 104 | // aren't picked up and placed in the toctree). So let's find 105 | // the closest header in the document and try with that one. 106 | if (link.length === 0) { 107 | var doc_link = $('.document a[href="' + anchor + '"]'); 108 | var closest_section = doc_link.closest('div.section'); 109 | // Try again with the closest section entry. 110 | link = $('.wy-menu-vertical') 111 | .find('[href="#' + closest_section.attr("id") + '"]'); 112 | 113 | } 114 | $('.wy-menu-vertical li.toctree-l1 li.current') 115 | .removeClass('current'); 116 | link.closest('li.toctree-l2').addClass('current'); 117 | link.closest('li.toctree-l3').addClass('current'); 118 | link.closest('li.toctree-l4').addClass('current'); 119 | } 120 | catch (err) { 121 | console.log("Error expanding nav for anchor", err); 122 | } 123 | } 124 | }; 125 | 126 | nav.onScroll = function () { 127 | this.winScroll = false; 128 | var newWinPosition = this.win.scrollTop(), 129 | winBottom = newWinPosition + this.winHeight, 130 | navPosition = this.navBar.scrollTop(), 131 | newNavPosition = navPosition + (newWinPosition - this.winPosition); 132 | if (newWinPosition < 0 || winBottom > this.docHeight) { 133 | return; 134 | } 135 | this.navBar.scrollTop(newNavPosition); 136 | this.winPosition = newWinPosition; 137 | }; 138 | 139 | nav.onResize = function () { 140 | this.winResize = false; 141 | this.winHeight = this.win.height(); 142 | this.docHeight = $(document).height(); 143 | }; 144 | 145 | nav.hashChange = function () { 146 | this.linkScroll = true; 147 | this.win.one('hashchange', function () { 148 | this.linkScroll = false; 149 | }); 150 | }; 151 | 152 | nav.toggleCurrent = function (elem) { 153 | var parent_li = elem.closest('li'); 154 | parent_li.siblings('li.current').removeClass('current'); 155 | parent_li.siblings().find('li.current').removeClass('current'); 156 | parent_li.find('> ul li.current').removeClass('current'); 157 | parent_li.toggleClass('current'); 158 | } 159 | 160 | return nav; 161 | }; 162 | 163 | module.exports.ThemeNav = ThemeNav(); 164 | 165 | if (typeof(window) != 'undefined') { 166 | window.SphinxRtdTheme = { StickyNav: module.exports.ThemeNav }; 167 | } 168 | 169 | },{"jquery":"jquery"}]},{},["sphinx-rtd-theme"]); 170 | -------------------------------------------------------------------------------- /src/components/App.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 52 | 53 | -------------------------------------------------------------------------------- /docs/_static/doctools.js: -------------------------------------------------------------------------------- 1 | /* 2 | * doctools.js 3 | * ~~~~~~~~~~~ 4 | * 5 | * Sphinx JavaScript utilities for all documentation. 6 | * 7 | * :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | /** 13 | * select a different prefix for underscore 14 | */ 15 | $u = _.noConflict(); 16 | 17 | /** 18 | * make the code below compatible with browsers without 19 | * an installed firebug like debugger 20 | if (!window.console || !console.firebug) { 21 | var names = ["log", "debug", "info", "warn", "error", "assert", "dir", 22 | "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", 23 | "profile", "profileEnd"]; 24 | window.console = {}; 25 | for (var i = 0; i < names.length; ++i) 26 | window.console[names[i]] = function() {}; 27 | } 28 | */ 29 | 30 | /** 31 | * small helper function to urldecode strings 32 | */ 33 | jQuery.urldecode = function(x) { 34 | return decodeURIComponent(x).replace(/\+/g, ' '); 35 | }; 36 | 37 | /** 38 | * small helper function to urlencode strings 39 | */ 40 | jQuery.urlencode = encodeURIComponent; 41 | 42 | /** 43 | * This function returns the parsed url parameters of the 44 | * current request. Multiple values per key are supported, 45 | * it will always return arrays of strings for the value parts. 46 | */ 47 | jQuery.getQueryParameters = function(s) { 48 | if (typeof s == 'undefined') 49 | s = document.location.search; 50 | var parts = s.substr(s.indexOf('?') + 1).split('&'); 51 | var result = {}; 52 | for (var i = 0; i < parts.length; i++) { 53 | var tmp = parts[i].split('=', 2); 54 | var key = jQuery.urldecode(tmp[0]); 55 | var value = jQuery.urldecode(tmp[1]); 56 | if (key in result) 57 | result[key].push(value); 58 | else 59 | result[key] = [value]; 60 | } 61 | return result; 62 | }; 63 | 64 | /** 65 | * highlight a given string on a jquery object by wrapping it in 66 | * span elements with the given class name. 67 | */ 68 | jQuery.fn.highlightText = function(text, className) { 69 | function highlight(node) { 70 | if (node.nodeType == 3) { 71 | var val = node.nodeValue; 72 | var pos = val.toLowerCase().indexOf(text); 73 | if (pos >= 0 && !jQuery(node.parentNode).hasClass(className)) { 74 | var span = document.createElement("span"); 75 | span.className = className; 76 | span.appendChild(document.createTextNode(val.substr(pos, text.length))); 77 | node.parentNode.insertBefore(span, node.parentNode.insertBefore( 78 | document.createTextNode(val.substr(pos + text.length)), 79 | node.nextSibling)); 80 | node.nodeValue = val.substr(0, pos); 81 | } 82 | } 83 | else if (!jQuery(node).is("button, select, textarea")) { 84 | jQuery.each(node.childNodes, function() { 85 | highlight(this); 86 | }); 87 | } 88 | } 89 | return this.each(function() { 90 | highlight(this); 91 | }); 92 | }; 93 | 94 | /* 95 | * backward compatibility for jQuery.browser 96 | * This will be supported until firefox bug is fixed. 97 | */ 98 | if (!jQuery.browser) { 99 | jQuery.uaMatch = function(ua) { 100 | ua = ua.toLowerCase(); 101 | 102 | var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || 103 | /(webkit)[ \/]([\w.]+)/.exec(ua) || 104 | /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || 105 | /(msie) ([\w.]+)/.exec(ua) || 106 | ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || 107 | []; 108 | 109 | return { 110 | browser: match[ 1 ] || "", 111 | version: match[ 2 ] || "0" 112 | }; 113 | }; 114 | jQuery.browser = {}; 115 | jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; 116 | } 117 | 118 | /** 119 | * Small JavaScript module for the documentation. 120 | */ 121 | var Documentation = { 122 | 123 | init : function() { 124 | this.fixFirefoxAnchorBug(); 125 | this.highlightSearchWords(); 126 | this.initIndexTable(); 127 | 128 | }, 129 | 130 | /** 131 | * i18n support 132 | */ 133 | TRANSLATIONS : {}, 134 | PLURAL_EXPR : function(n) { return n == 1 ? 0 : 1; }, 135 | LOCALE : 'unknown', 136 | 137 | // gettext and ngettext don't access this so that the functions 138 | // can safely bound to a different name (_ = Documentation.gettext) 139 | gettext : function(string) { 140 | var translated = Documentation.TRANSLATIONS[string]; 141 | if (typeof translated == 'undefined') 142 | return string; 143 | return (typeof translated == 'string') ? translated : translated[0]; 144 | }, 145 | 146 | ngettext : function(singular, plural, n) { 147 | var translated = Documentation.TRANSLATIONS[singular]; 148 | if (typeof translated == 'undefined') 149 | return (n == 1) ? singular : plural; 150 | return translated[Documentation.PLURALEXPR(n)]; 151 | }, 152 | 153 | addTranslations : function(catalog) { 154 | for (var key in catalog.messages) 155 | this.TRANSLATIONS[key] = catalog.messages[key]; 156 | this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); 157 | this.LOCALE = catalog.locale; 158 | }, 159 | 160 | /** 161 | * add context elements like header anchor links 162 | */ 163 | addContextElements : function() { 164 | $('div[id] > :header:first').each(function() { 165 | $('\u00B6'). 166 | attr('href', '#' + this.id). 167 | attr('title', _('Permalink to this headline')). 168 | appendTo(this); 169 | }); 170 | $('dt[id]').each(function() { 171 | $('\u00B6'). 172 | attr('href', '#' + this.id). 173 | attr('title', _('Permalink to this definition')). 174 | appendTo(this); 175 | }); 176 | }, 177 | 178 | /** 179 | * workaround a firefox stupidity 180 | * see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075 181 | */ 182 | fixFirefoxAnchorBug : function() { 183 | if (document.location.hash) 184 | window.setTimeout(function() { 185 | document.location.href += ''; 186 | }, 10); 187 | }, 188 | 189 | /** 190 | * highlight the search words provided in the url in the text 191 | */ 192 | highlightSearchWords : function() { 193 | var params = $.getQueryParameters(); 194 | var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; 195 | if (terms.length) { 196 | var body = $('div.body'); 197 | if (!body.length) { 198 | body = $('body'); 199 | } 200 | window.setTimeout(function() { 201 | $.each(terms, function() { 202 | body.highlightText(this.toLowerCase(), 'highlighted'); 203 | }); 204 | }, 10); 205 | $('') 207 | .appendTo($('#searchbox')); 208 | } 209 | }, 210 | 211 | /** 212 | * init the domain index toggle buttons 213 | */ 214 | initIndexTable : function() { 215 | var togglers = $('img.toggler').click(function() { 216 | var src = $(this).attr('src'); 217 | var idnum = $(this).attr('id').substr(7); 218 | $('tr.cg-' + idnum).toggle(); 219 | if (src.substr(-9) == 'minus.png') 220 | $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); 221 | else 222 | $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); 223 | }).css('display', ''); 224 | if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { 225 | togglers.click(); 226 | } 227 | }, 228 | 229 | /** 230 | * helper function to hide the search marks again 231 | */ 232 | hideSearchWords : function() { 233 | $('#searchbox .highlight-link').fadeOut(300); 234 | $('span.highlighted').removeClass('highlighted'); 235 | }, 236 | 237 | /** 238 | * make the url absolute 239 | */ 240 | makeURL : function(relativeURL) { 241 | return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; 242 | }, 243 | 244 | /** 245 | * get the current relative url 246 | */ 247 | getCurrentURL : function() { 248 | var path = document.location.pathname; 249 | var parts = path.split(/\//); 250 | $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { 251 | if (this == '..') 252 | parts.pop(); 253 | }); 254 | var url = parts.join('/'); 255 | return path.substring(url.lastIndexOf('/') + 1, path.length - 1); 256 | }, 257 | 258 | initOnKeyListeners: function() { 259 | $(document).keyup(function(event) { 260 | var activeElementType = document.activeElement.tagName; 261 | // don't navigate when in search box or textarea 262 | if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT') { 263 | switch (event.keyCode) { 264 | case 37: // left 265 | var prevHref = $('link[rel="prev"]').prop('href'); 266 | if (prevHref) { 267 | window.location.href = prevHref; 268 | return false; 269 | } 270 | case 39: // right 271 | var nextHref = $('link[rel="next"]').prop('href'); 272 | if (nextHref) { 273 | window.location.href = nextHref; 274 | return false; 275 | } 276 | } 277 | } 278 | }); 279 | } 280 | }; 281 | 282 | // quick alias for translations 283 | _ = Documentation.gettext; 284 | 285 | $(document).ready(function() { 286 | Documentation.init(); 287 | }); -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Flashboard 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 |
44 | 45 | 74 | 75 |
76 | 77 |
78 | 79 |
80 | 81 |

"Simply can define your models with several options and get your rest-full api and admin dashboard automatically."

82 | 83 | 84 | 85 |
86 |

Admin Dashboard Demo : 138.68.46.113/flashboard

87 |

email: admin@flashboard.com | password: qwertyuiop

88 |
89 | 90 |
91 | 92 | 93 | 94 |
95 |
96 | 97 |
98 | 99 | 100 | 110 | 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /docs/getting.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Flashboard documentation 12 | 13 | 14 | 15 | 16 | 17 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 |
50 | 51 | 52 | 103 | 104 |
105 | 106 | 107 | 113 | 114 | 115 |
116 |
117 | 118 | 119 | 120 |
121 | 122 |
    123 | 124 |
  • Docs »
  • 125 | 126 |
  • Flashboard documentation
  • 127 | 128 | 129 |
  • 130 | 131 | 132 | 133 | Edit on GitHub 134 | 135 | 136 | 137 |
  • 138 | 139 |
140 | 141 | 142 |
143 |
144 |
145 |
146 | 147 |
148 | 149 |

Let’s jump in
150 | Use the sidebar on the left to navigate the docs.

151 |

Getting Started
152 | The getting started guide walks you through how to manually setup a admin dashboard. flashboard actually create your back-end package services such as rest-full api of your models based on loopback node.js framework and generate admin dashboard automatically for data moderation.

153 |

Models
154 | In this part we can learn how to create own models and start to build your back-end project. there are several options to create your models and configurations options.

155 | 156 |

Configuration
157 | Flashboard create a admin dashboard based on configuration of loopback models. there are several settings and options on your models and user interface generation types and formats.

158 |

Rest-full API
159 | After create your models you have a rest-full api based on your models. In this section we want to learn how to use generated api’s. Also there are options to create your own remote functions.

160 | 161 |
162 | 163 |
164 | 165 | 166 | 167 |
168 |
169 | 170 |
171 |
172 |
173 | 174 | 175 |
176 | 177 |
178 |

179 | © Copyright 2014, Read the Docs. 180 | 181 | 182 | Revision ea83436a. 183 | 184 | Built with Sphinx using a theme provided by Read the Docs. 185 | 186 |

187 |
188 | 189 |
190 | 191 |
192 |
193 | 194 |
195 | 196 |
197 | 198 | 199 |
200 | 201 | Read the Docs 202 | v: stable 203 | 204 | 205 |
206 |
207 |
Versions
208 | 209 |
latest
210 | 211 |
stable
212 | 213 |
214 |
215 |
Downloads
216 | 217 |
218 |
219 |
On Read the Docs
220 |
221 | Project Home 222 |
223 |
224 | Builds 225 |
226 |
227 |
228 | Free document hosting provided by Read the Docs. 229 | 230 |
231 |
232 | 233 | 234 | 235 | 236 | 237 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 263 | 264 | 265 | 266 | -------------------------------------------------------------------------------- /docs/project.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Flashboard documentation 12 | 13 | 14 | 15 | 16 | 17 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 |
50 | 51 | 52 | 103 | 104 |
105 | 106 | 107 | 113 | 114 | 115 | 116 |
117 |
118 | 119 | 120 | 121 |
122 | 123 |
    124 | 125 |
  • Docs »
  • 126 | 127 |
  • Flashboard documentation
  • 128 | 129 | 130 |
  • 131 | 132 | 133 | 134 | Edit on GitHub 135 | 136 | 137 | 138 |
  • 139 | 140 |
141 | 142 | 143 |
144 |
145 |
146 |
147 | 148 |
149 | 150 |

Contribute

151 | 152 |

Contributions welcome! Like seriously

153 |

If you have a any issue with flashboard feel free to report it in github

154 |

We need to improve the admin dashboard with more options and features. 155 |
156 | Next step is creating the GUI for creating and managing models and API's. 157 |

158 | 159 |

License

160 | 161 | (The MIT License) 162 | 163 | Copyright (c) 2016 Vahid Taghizadeh 164 | 165 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 166 | 167 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 168 | 169 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 170 | 171 |
172 |
173 |
174 | 175 | 176 |
177 | 178 |
179 |

180 | © Copyright 2014, Read the Docs. 181 | 182 | 183 | Revision ea83436a. 184 | 185 | Built with Sphinx using a theme provided by Read the Docs. 186 | 187 |

188 |
189 | 190 |
191 | 192 |
193 |
194 | 195 |
196 | 197 |
198 | 199 | 200 |
201 | 202 | Read the Docs 203 | v: stable 204 | 205 | 206 |
207 |
208 |
Versions
209 | 210 |
latest
211 | 212 |
stable
213 | 214 |
215 |
216 |
Downloads
217 | 218 |
219 |
220 |
On Read the Docs
221 |
222 | Project Home 223 |
224 |
225 | Builds 226 |
227 |
228 |
229 | Free document hosting provided by Read the Docs. 230 | 231 |
232 |
233 | 234 | 235 | 236 | 237 | 238 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 264 | 265 | 266 | 267 | -------------------------------------------------------------------------------- /docs/_static/underscore.js: -------------------------------------------------------------------------------- 1 | // Underscore.js 1.3.1 2 | // (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. 3 | // Underscore is freely distributable under the MIT license. 4 | // Portions of Underscore are inspired or borrowed from Prototype, 5 | // Oliver Steele's Functional, and John Resig's Micro-Templating. 6 | // For all details and documentation: 7 | // http://documentcloud.github.com/underscore 8 | (function(){function q(a,c,d){if(a===c)return a!==0||1/a==1/c;if(a==null||c==null)return a===c;if(a._chain)a=a._wrapped;if(c._chain)c=c._wrapped;if(a.isEqual&&b.isFunction(a.isEqual))return a.isEqual(c);if(c.isEqual&&b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return false;switch(e){case "[object String]":return a==String(c);case "[object Number]":return a!=+a?c!=+c:a==0?1/a==1/c:a==+c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source== 9 | c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if(typeof a!="object"||typeof c!="object")return false;for(var f=d.length;f--;)if(d[f]==a)return true;d.push(a);var f=0,g=true;if(e=="[object Array]"){if(f=a.length,g=f==c.length)for(;f--;)if(!(g=f in a==f in c&&q(a[f],c[f],d)))break}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return false;for(var h in a)if(b.has(a,h)&&(f++,!(g=b.has(c,h)&&q(a[h],c[h],d))))break;if(g){for(h in c)if(b.has(c, 10 | h)&&!f--)break;g=!f}}d.pop();return g}var r=this,G=r._,n={},k=Array.prototype,o=Object.prototype,i=k.slice,H=k.unshift,l=o.toString,I=o.hasOwnProperty,w=k.forEach,x=k.map,y=k.reduce,z=k.reduceRight,A=k.filter,B=k.every,C=k.some,p=k.indexOf,D=k.lastIndexOf,o=Array.isArray,J=Object.keys,s=Function.prototype.bind,b=function(a){return new m(a)};if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports)exports=module.exports=b;exports._=b}else r._=b;b.VERSION="1.3.1";var j=b.each= 11 | b.forEach=function(a,c,d){if(a!=null)if(w&&a.forEach===w)a.forEach(c,d);else if(a.length===+a.length)for(var e=0,f=a.length;e2;a== 12 | null&&(a=[]);if(y&&a.reduce===y)return e&&(c=b.bind(c,e)),f?a.reduce(c,d):a.reduce(c);j(a,function(a,b,i){f?d=c.call(e,d,a,b,i):(d=a,f=true)});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(z&&a.reduceRight===z)return e&&(c=b.bind(c,e)),f?a.reduceRight(c,d):a.reduceRight(c);var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g,c,d,e):b.reduce(g,c)};b.find=b.detect= 13 | function(a,c,b){var e;E(a,function(a,g,h){if(c.call(b,a,g,h))return e=a,true});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(A&&a.filter===A)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(B&&a.every===B)return a.every(c,b);j(a,function(a,g,h){if(!(e= 14 | e&&c.call(b,a,g,h)))return n});return e};var E=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(C&&a.some===C)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return n});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;return p&&a.indexOf===p?a.indexOf(c)!=-1:b=E(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(b.isFunction(c)?c||a:a[c]).apply(a,d)})};b.pluck= 15 | function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;bd?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};j(a,function(a,b){var c=e(a,b);(d[c]||(d[c]=[])).push(a)});return d};b.sortedIndex=function(a, 17 | c,d){d||(d=b.identity);for(var e=0,f=a.length;e>1;d(a[g])=0})})};b.difference=function(a){var c=b.flatten(i.call(arguments,1));return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e=0;d--)b=[a[d].apply(this,b)];return b[0]}}; 24 | b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=J||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var c=[],d;for(d in a)b.has(a,d)&&(c[c.length]=d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};b.defaults=function(a){j(i.call(arguments, 25 | 1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return q(a,b,[])};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(b.has(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=o||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===Object(a)}; 26 | b.isArguments=function(a){return l.call(a)=="[object Arguments]"};if(!b.isArguments(arguments))b.isArguments=function(a){return!(!a||!b.has(a,"callee"))};b.isFunction=function(a){return l.call(a)=="[object Function]"};b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)=="[object Date]"}; 27 | b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.has=function(a,b){return I.call(a,b)};b.noConflict=function(){r._=G;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")};b.mixin=function(a){j(b.functions(a), 28 | function(c){K(c,b[c]=a[c])})};var L=0;b.uniqueId=function(a){var b=L++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var t=/.^/,u=function(a){return a.replace(/\\\\/g,"\\").replace(/\\'/g,"'")};b.template=function(a,c){var d=b.templateSettings,d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.escape||t,function(a,b){return"',_.escape("+ 29 | u(b)+"),'"}).replace(d.interpolate||t,function(a,b){return"',"+u(b)+",'"}).replace(d.evaluate||t,function(a,b){return"');"+u(b).replace(/[\r\n\t]/g," ")+";__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');",e=new Function("obj","_",d);return c?e(c,b):function(a){return e.call(this,a,b)}};b.chain=function(a){return b(a).chain()};var m=function(a){this._wrapped=a};b.prototype=m.prototype;var v=function(a,c){return c?b(a).chain():a},K=function(a,c){m.prototype[a]= 30 | function(){var a=i.call(arguments);H.call(a,this._wrapped);return v(c.apply(b,a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];m.prototype[a]=function(){var d=this._wrapped;b.apply(d,arguments);var e=d.length;(a=="shift"||a=="splice")&&e===0&&delete d[0];return v(d,this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];m.prototype[a]=function(){return v(b.apply(this._wrapped,arguments),this._chain)}});m.prototype.chain=function(){this._chain= 31 | true;return this};m.prototype.value=function(){return this._wrapped}}).call(this); 32 | -------------------------------------------------------------------------------- /docs/start.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Flashboard documentation 12 | 13 | 14 | 15 | 16 | 17 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 |
50 | 51 | 52 | 103 | 104 |
105 | 106 | 107 | 113 | 114 | 115 | 116 |
117 |
118 | 119 | 120 | 121 |
122 | 123 |
    124 | 125 |
  • Docs »
  • 126 | 127 |
  • Flashboard documentation
  • 128 | 129 | 130 |
  • 131 | 132 | 133 | 134 | Edit on GitHub 135 | 136 | 137 | 138 |
  • 139 | 140 |
141 | 142 | 143 |
144 |
145 |
146 |
147 | 148 |

Getting Started

149 |

The getting started guide walks you through how to manually setup a admin dashboard. flashboard actually create your back-end package services such as rest-full api of your models based on loopback node.js framework and generate admin dashboard automatically for data moderation.

150 |

151 |

Introduction

152 |

flashboard can handle your whole back-end includes services, rest-full api and admin dashboard based on loopback node.js framework.
153 | Simply can define your models with several options and get your rest-full api and admin dashboard automatically.
154 | Admin dashboard implemented by vue.js 2 framework and muse-ui as ui-component library that allows you have a multiple themes for your user interface.
155 | You have a database-connector options that allows you to connect your back-end to each database you want such as mongodb or mysql and etc.

156 |

Admin demo : http://138.68.46.113/flashboard
157 | demo admin credential:
158 | email: admin@flashboard.com
159 | password: qwertyuiop

160 |

Let’s start to build your own back-end services.

161 |

162 |

Installation

163 |
    164 |
  1. Before you begin, make sure you have Node.js and MongoDB installed. For best results, use the latest LTS (long-term support) release of Node.js.
  2. 165 |
  3. Running below command in your empty project directory path :
  4. 166 |
167 |
$ git clone https://github.com/vah7id/flashboard.git
168 | 
169 |
    170 |
  1. First install your services packages by run below command on your project directory :
  2. 171 |
172 |
$ npm run preinstall
173 | 
174 |
    175 |
  1. Install your admin generator packages by run below command on root directory:
  2. 176 |
177 |
$ npm install
178 | 
179 |
    180 |
  1. Running back-end services first before running admin dashboard :
  2. 181 |
182 |
$ npm run service
183 | 
184 |
    185 |
  1. At last you can run your admin dashboard by running below command:
  2. 186 |
187 |
$ npm run dev
188 | 
189 |

for deploy on production you can run build command :

190 |
$ npm run build
191 | 
192 |

193 |

Admin Default User

194 |

In first-time you start the project api/server/boot/script.js create default admin account as you can see below :

195 |
196 |   module.exports = function (app) {
197 |   var _ = require('lodash');
198 |   var User = app.models.User;
199 |   var Role = app.models.Role;
200 |   var RoleMapping = app.models.RoleMapping;
201 |   var ACL = app.models.ACL;
202 | 
203 |   User.create([{
204 |       username: 'flashboard',
205 |       email: 'admin@flashboard.com',
206 |       password: 'qwertyuiop', 
207 |       type: 'admin'
208 |    }], function(err, users) {
209 | 
210 |   Role.find({ name: 'admin' }, function(err, results) {
211 |       if (err) console.log(err);
212 |       Role.create({
213 |         name: 'admin'
214 |       }, function(err, role) {
215 |         if (err);
216 |         //debug(role);
217 |         role.principals.create({
218 |           principalType: RoleMapping.USER,
219 |           principalId: users[0].id
220 |         }, function(err, principal) {
221 |           if (err) console.log(err);
222 |         
223 |         });
224 |       });
225 |   });
226 |     
227 |   });
228 | };
229 | 
230 |

You can change the initial credential in script.js before start your project.
231 | If you miss this step nevermind, you can login by default admin credentials then create new admin account in dashboard from users section for yourself and manage accounts in there.

232 |

233 | 234 |

Models

235 |

After run your project you can see built-in models on your dashboard such as posts, galleries and etc. for starting to create your own website models you should go to next section of documentation.

236 | 237 |
238 | 239 |
240 | 241 |
242 | 243 | 244 |
245 | 246 |
247 |

248 | © Copyright 2014, Read the Docs. 249 | 250 | 251 | Revision ea83436a. 252 | 253 | Built with Sphinx using a theme provided by Read the Docs. 254 | 255 |

256 |
257 | 258 |
259 | 260 |
261 |
262 | 263 |
264 | 265 |
266 | 267 | 268 |
269 | 270 | Read the Docs 271 | v: stable 272 | 273 | 274 |
275 |
276 |
Versions
277 | 278 |
latest
279 | 280 |
stable
281 | 282 |
283 |
284 |
Downloads
285 | 286 |
287 |
288 |
On Read the Docs
289 |
290 | Project Home 291 |
292 |
293 | Builds 294 |
295 |
296 |
297 | Free document hosting provided by Read the Docs. 298 | 299 |
300 |
301 | 302 | 303 | 304 | 305 | 306 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 333 | 334 | 335 | 336 | 341 | 342 | 343 | 344 | 345 | -------------------------------------------------------------------------------- /docs/_static/basic.css: -------------------------------------------------------------------------------- 1 | /* 2 | * basic.css 3 | * ~~~~~~~~~ 4 | * 5 | * Sphinx stylesheet -- basic theme. 6 | * 7 | * :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | /* -- main layout ----------------------------------------------------------- */ 13 | 14 | div.clearer { 15 | clear: both; 16 | } 17 | 18 | /* -- relbar ---------------------------------------------------------------- */ 19 | 20 | div.related { 21 | width: 100%; 22 | font-size: 90%; 23 | } 24 | 25 | div.related h3 { 26 | display: none; 27 | } 28 | 29 | div.related ul { 30 | margin: 0; 31 | padding: 0 0 0 10px; 32 | list-style: none; 33 | } 34 | 35 | div.related li { 36 | display: inline; 37 | } 38 | 39 | div.related li.right { 40 | float: right; 41 | margin-right: 5px; 42 | } 43 | 44 | /* -- sidebar --------------------------------------------------------------- */ 45 | 46 | div.sphinxsidebarwrapper { 47 | padding: 10px 5px 0 10px; 48 | } 49 | 50 | div.sphinxsidebar { 51 | float: left; 52 | width: 230px; 53 | margin-left: -100%; 54 | font-size: 90%; 55 | word-wrap: break-word; 56 | overflow-wrap : break-word; 57 | } 58 | 59 | div.sphinxsidebar ul { 60 | list-style: none; 61 | } 62 | 63 | div.sphinxsidebar ul ul, 64 | div.sphinxsidebar ul.want-points { 65 | margin-left: 20px; 66 | list-style: square; 67 | } 68 | 69 | div.sphinxsidebar ul ul { 70 | margin-top: 0; 71 | margin-bottom: 0; 72 | } 73 | 74 | div.sphinxsidebar form { 75 | margin-top: 10px; 76 | } 77 | 78 | div.sphinxsidebar input { 79 | border: 1px solid #98dbcc; 80 | font-family: sans-serif; 81 | font-size: 1em; 82 | } 83 | 84 | div.sphinxsidebar #searchbox input[type="text"] { 85 | width: 170px; 86 | } 87 | 88 | img { 89 | border: 0; 90 | max-width: 100%; 91 | } 92 | 93 | /* -- search page ----------------------------------------------------------- */ 94 | 95 | ul.search { 96 | margin: 10px 0 0 20px; 97 | padding: 0; 98 | } 99 | 100 | ul.search li { 101 | padding: 5px 0 5px 20px; 102 | background-image: url(file.png); 103 | background-repeat: no-repeat; 104 | background-position: 0 7px; 105 | } 106 | 107 | ul.search li a { 108 | font-weight: bold; 109 | } 110 | 111 | ul.search li div.context { 112 | color: #888; 113 | margin: 2px 0 0 30px; 114 | text-align: left; 115 | } 116 | 117 | ul.keywordmatches li.goodmatch a { 118 | font-weight: bold; 119 | } 120 | 121 | /* -- index page ------------------------------------------------------------ */ 122 | 123 | table.contentstable { 124 | width: 90%; 125 | margin-left: auto; 126 | margin-right: auto; 127 | } 128 | 129 | table.contentstable p.biglink { 130 | line-height: 150%; 131 | } 132 | 133 | a.biglink { 134 | font-size: 1.3em; 135 | } 136 | 137 | span.linkdescr { 138 | font-style: italic; 139 | padding-top: 5px; 140 | font-size: 90%; 141 | } 142 | 143 | /* -- general index --------------------------------------------------------- */ 144 | 145 | table.indextable { 146 | width: 100%; 147 | } 148 | 149 | table.indextable td { 150 | text-align: left; 151 | vertical-align: top; 152 | } 153 | 154 | table.indextable ul { 155 | margin-top: 0; 156 | margin-bottom: 0; 157 | list-style-type: none; 158 | } 159 | 160 | table.indextable > tbody > tr > td > ul { 161 | padding-left: 0em; 162 | } 163 | 164 | table.indextable tr.pcap { 165 | height: 10px; 166 | } 167 | 168 | table.indextable tr.cap { 169 | margin-top: 10px; 170 | background-color: #f2f2f2; 171 | } 172 | 173 | img.toggler { 174 | margin-right: 3px; 175 | margin-top: 3px; 176 | cursor: pointer; 177 | } 178 | 179 | div.modindex-jumpbox { 180 | border-top: 1px solid #ddd; 181 | border-bottom: 1px solid #ddd; 182 | margin: 1em 0 1em 0; 183 | padding: 0.4em; 184 | } 185 | 186 | div.genindex-jumpbox { 187 | border-top: 1px solid #ddd; 188 | border-bottom: 1px solid #ddd; 189 | margin: 1em 0 1em 0; 190 | padding: 0.4em; 191 | } 192 | 193 | /* -- domain module index --------------------------------------------------- */ 194 | 195 | table.modindextable td { 196 | padding: 2px; 197 | border-collapse: collapse; 198 | } 199 | 200 | /* -- general body styles --------------------------------------------------- */ 201 | 202 | div.body p, div.body dd, div.body li, div.body blockquote { 203 | -moz-hyphens: auto; 204 | -ms-hyphens: auto; 205 | -webkit-hyphens: auto; 206 | hyphens: auto; 207 | } 208 | 209 | a.headerlink { 210 | visibility: hidden; 211 | } 212 | 213 | h1:hover > a.headerlink, 214 | h2:hover > a.headerlink, 215 | h3:hover > a.headerlink, 216 | h4:hover > a.headerlink, 217 | h5:hover > a.headerlink, 218 | h6:hover > a.headerlink, 219 | dt:hover > a.headerlink, 220 | caption:hover > a.headerlink, 221 | p.caption:hover > a.headerlink, 222 | div.code-block-caption:hover > a.headerlink { 223 | visibility: visible; 224 | } 225 | 226 | div.body p.caption { 227 | text-align: inherit; 228 | } 229 | 230 | div.body td { 231 | text-align: left; 232 | } 233 | 234 | .first { 235 | margin-top: 0 !important; 236 | } 237 | 238 | p.rubric { 239 | margin-top: 30px; 240 | font-weight: bold; 241 | } 242 | 243 | img.align-left, .figure.align-left, object.align-left { 244 | clear: left; 245 | float: left; 246 | margin-right: 1em; 247 | } 248 | 249 | img.align-right, .figure.align-right, object.align-right { 250 | clear: right; 251 | float: right; 252 | margin-left: 1em; 253 | } 254 | 255 | img.align-center, .figure.align-center, object.align-center { 256 | display: block; 257 | margin-left: auto; 258 | margin-right: auto; 259 | } 260 | 261 | .align-left { 262 | text-align: left; 263 | } 264 | 265 | .align-center { 266 | text-align: center; 267 | } 268 | 269 | .align-right { 270 | text-align: right; 271 | } 272 | 273 | /* -- sidebars -------------------------------------------------------------- */ 274 | 275 | div.sidebar { 276 | margin: 0 0 0.5em 1em; 277 | border: 1px solid #ddb; 278 | padding: 7px 7px 0 7px; 279 | background-color: #ffe; 280 | width: 40%; 281 | float: right; 282 | } 283 | 284 | p.sidebar-title { 285 | font-weight: bold; 286 | } 287 | 288 | /* -- topics ---------------------------------------------------------------- */ 289 | 290 | div.topic { 291 | border: 1px solid #ccc; 292 | padding: 7px 7px 0 7px; 293 | margin: 10px 0 10px 0; 294 | } 295 | 296 | p.topic-title { 297 | font-size: 1.1em; 298 | font-weight: bold; 299 | margin-top: 10px; 300 | } 301 | 302 | /* -- admonitions ----------------------------------------------------------- */ 303 | 304 | div.admonition { 305 | margin-top: 10px; 306 | margin-bottom: 10px; 307 | padding: 7px; 308 | } 309 | 310 | div.admonition dt { 311 | font-weight: bold; 312 | } 313 | 314 | div.admonition dl { 315 | margin-bottom: 0; 316 | } 317 | 318 | p.admonition-title { 319 | margin: 0px 10px 5px 0px; 320 | font-weight: bold; 321 | } 322 | 323 | div.body p.centered { 324 | text-align: center; 325 | margin-top: 25px; 326 | } 327 | 328 | /* -- tables ---------------------------------------------------------------- */ 329 | 330 | table.docutils { 331 | border: 0; 332 | border-collapse: collapse; 333 | } 334 | 335 | table caption span.caption-number { 336 | font-style: italic; 337 | } 338 | 339 | table caption span.caption-text { 340 | } 341 | 342 | table.docutils td, table.docutils th { 343 | padding: 1px 8px 1px 5px; 344 | border-top: 0; 345 | border-left: 0; 346 | border-right: 0; 347 | border-bottom: 1px solid #aaa; 348 | } 349 | 350 | table.footnote td, table.footnote th { 351 | border: 0 !important; 352 | } 353 | 354 | th { 355 | text-align: left; 356 | padding-right: 5px; 357 | } 358 | 359 | table.citation { 360 | border-left: solid 1px gray; 361 | margin-left: 1px; 362 | } 363 | 364 | table.citation td { 365 | border-bottom: none; 366 | } 367 | 368 | /* -- figures --------------------------------------------------------------- */ 369 | 370 | div.figure { 371 | margin: 0.5em; 372 | padding: 0.5em; 373 | } 374 | 375 | div.figure p.caption { 376 | padding: 0.3em; 377 | } 378 | 379 | div.figure p.caption span.caption-number { 380 | font-style: italic; 381 | } 382 | 383 | div.figure p.caption span.caption-text { 384 | } 385 | 386 | /* -- field list styles ----------------------------------------------------- */ 387 | 388 | table.field-list td, table.field-list th { 389 | border: 0 !important; 390 | } 391 | 392 | .field-list ul { 393 | margin: 0; 394 | padding-left: 1em; 395 | } 396 | 397 | .field-list p { 398 | margin: 0; 399 | } 400 | 401 | /* -- other body styles ----------------------------------------------------- */ 402 | 403 | ol.arabic { 404 | list-style: decimal; 405 | } 406 | 407 | ol.loweralpha { 408 | list-style: lower-alpha; 409 | } 410 | 411 | ol.upperalpha { 412 | list-style: upper-alpha; 413 | } 414 | 415 | ol.lowerroman { 416 | list-style: lower-roman; 417 | } 418 | 419 | ol.upperroman { 420 | list-style: upper-roman; 421 | } 422 | 423 | dl { 424 | margin-bottom: 15px; 425 | } 426 | 427 | dd p { 428 | margin-top: 0px; 429 | } 430 | 431 | dd ul, dd table { 432 | margin-bottom: 10px; 433 | } 434 | 435 | dd { 436 | margin-top: 3px; 437 | margin-bottom: 10px; 438 | margin-left: 30px; 439 | } 440 | 441 | dt:target, .highlighted { 442 | background-color: #fbe54e; 443 | } 444 | 445 | dl.glossary dt { 446 | font-weight: bold; 447 | font-size: 1.1em; 448 | } 449 | 450 | .optional { 451 | font-size: 1.3em; 452 | } 453 | 454 | .sig-paren { 455 | font-size: larger; 456 | } 457 | 458 | .versionmodified { 459 | font-style: italic; 460 | } 461 | 462 | .system-message { 463 | background-color: #fda; 464 | padding: 5px; 465 | border: 3px solid red; 466 | } 467 | 468 | .footnote:target { 469 | background-color: #ffa; 470 | } 471 | 472 | .line-block { 473 | display: block; 474 | margin-top: 1em; 475 | margin-bottom: 1em; 476 | } 477 | 478 | .line-block .line-block { 479 | margin-top: 0; 480 | margin-bottom: 0; 481 | margin-left: 1.5em; 482 | } 483 | 484 | .guilabel, .menuselection { 485 | font-family: sans-serif; 486 | } 487 | 488 | .accelerator { 489 | text-decoration: underline; 490 | } 491 | 492 | .classifier { 493 | font-style: oblique; 494 | } 495 | 496 | abbr, acronym { 497 | border-bottom: dotted 1px; 498 | cursor: help; 499 | } 500 | 501 | /* -- code displays --------------------------------------------------------- */ 502 | 503 | pre { 504 | overflow: auto; 505 | overflow-y: hidden; /* fixes display issues on Chrome browsers */ 506 | } 507 | 508 | span.pre { 509 | -moz-hyphens: none; 510 | -ms-hyphens: none; 511 | -webkit-hyphens: none; 512 | hyphens: none; 513 | } 514 | 515 | td.linenos pre { 516 | padding: 5px 0px; 517 | border: 0; 518 | background-color: transparent; 519 | color: #aaa; 520 | } 521 | 522 | table.highlighttable { 523 | margin-left: 0.5em; 524 | } 525 | 526 | table.highlighttable td { 527 | padding: 0 0.5em 0 0.5em; 528 | } 529 | 530 | div.code-block-caption { 531 | padding: 2px 5px; 532 | font-size: small; 533 | } 534 | 535 | div.code-block-caption code { 536 | background-color: transparent; 537 | } 538 | 539 | div.code-block-caption + div > div.highlight > pre { 540 | margin-top: 0; 541 | } 542 | 543 | div.code-block-caption span.caption-number { 544 | padding: 0.1em 0.3em; 545 | font-style: italic; 546 | } 547 | 548 | div.code-block-caption span.caption-text { 549 | } 550 | 551 | div.literal-block-wrapper { 552 | padding: 1em 1em 0; 553 | } 554 | 555 | div.literal-block-wrapper div.highlight { 556 | margin: 0; 557 | } 558 | 559 | code.descname { 560 | background-color: transparent; 561 | font-weight: bold; 562 | font-size: 1.2em; 563 | } 564 | 565 | code.descclassname { 566 | background-color: transparent; 567 | } 568 | 569 | code.xref, a code { 570 | background-color: transparent; 571 | font-weight: bold; 572 | } 573 | 574 | h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { 575 | background-color: transparent; 576 | } 577 | 578 | .viewcode-link { 579 | float: right; 580 | } 581 | 582 | .viewcode-back { 583 | float: right; 584 | font-family: sans-serif; 585 | } 586 | 587 | div.viewcode-block:target { 588 | margin: -1px -10px; 589 | padding: 0 10px; 590 | } 591 | 592 | /* -- math display ---------------------------------------------------------- */ 593 | 594 | img.math { 595 | vertical-align: middle; 596 | } 597 | 598 | div.body div.math p { 599 | text-align: center; 600 | } 601 | 602 | span.eqno { 603 | float: right; 604 | } 605 | 606 | span.eqno a.headerlink { 607 | position: relative; 608 | left: 0px; 609 | z-index: 1; 610 | } 611 | 612 | div.math:hover a.headerlink { 613 | visibility: visible; 614 | } 615 | 616 | /* -- printout stylesheet --------------------------------------------------- */ 617 | 618 | @media print { 619 | div.document, 620 | div.documentwrapper, 621 | div.bodywrapper { 622 | margin: 0 !important; 623 | width: 100%; 624 | } 625 | 626 | div.sphinxsidebar, 627 | div.related, 628 | div.footer, 629 | #top-link { 630 | display: none; 631 | } 632 | } -------------------------------------------------------------------------------- /docs/_static/js/modernizr.min.js: -------------------------------------------------------------------------------- 1 | /* Modernizr 2.6.2 (Custom Build) | MIT & BSD 2 | * Build: http://modernizr.com/download/#-fontface-backgroundsize-borderimage-borderradius-boxshadow-flexbox-hsla-multiplebgs-opacity-rgba-textshadow-cssanimations-csscolumns-generatedcontent-cssgradients-cssreflections-csstransforms-csstransforms3d-csstransitions-applicationcache-canvas-canvastext-draganddrop-hashchange-history-audio-video-indexeddb-input-inputtypes-localstorage-postmessage-sessionstorage-websockets-websqldatabase-webworkers-geolocation-inlinesvg-smil-svg-svgclippaths-touch-webgl-shiv-mq-cssclasses-addtest-prefixed-teststyles-testprop-testallprops-hasevent-prefixes-domprefixes-load 3 | */ 4 | ;window.Modernizr=function(a,b,c){function D(a){j.cssText=a}function E(a,b){return D(n.join(a+";")+(b||""))}function F(a,b){return typeof a===b}function G(a,b){return!!~(""+a).indexOf(b)}function H(a,b){for(var d in a){var e=a[d];if(!G(e,"-")&&j[e]!==c)return b=="pfx"?e:!0}return!1}function I(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:F(f,"function")?f.bind(d||b):f}return!1}function J(a,b,c){var d=a.charAt(0).toUpperCase()+a.slice(1),e=(a+" "+p.join(d+" ")+d).split(" ");return F(b,"string")||F(b,"undefined")?H(e,b):(e=(a+" "+q.join(d+" ")+d).split(" "),I(e,b,c))}function K(){e.input=function(c){for(var d=0,e=c.length;d',a,""].join(""),l.id=h,(m?l:n).innerHTML+=f,n.appendChild(l),m||(n.style.background="",n.style.overflow="hidden",k=g.style.overflow,g.style.overflow="hidden",g.appendChild(n)),i=c(l,a),m?l.parentNode.removeChild(l):(n.parentNode.removeChild(n),g.style.overflow=k),!!i},z=function(b){var c=a.matchMedia||a.msMatchMedia;if(c)return c(b).matches;var d;return y("@media "+b+" { #"+h+" { position: absolute; } }",function(b){d=(a.getComputedStyle?getComputedStyle(b,null):b.currentStyle)["position"]=="absolute"}),d},A=function(){function d(d,e){e=e||b.createElement(a[d]||"div"),d="on"+d;var f=d in e;return f||(e.setAttribute||(e=b.createElement("div")),e.setAttribute&&e.removeAttribute&&(e.setAttribute(d,""),f=F(e[d],"function"),F(e[d],"undefined")||(e[d]=c),e.removeAttribute(d))),e=null,f}var a={select:"input",change:"input",submit:"form",reset:"form",error:"img",load:"img",abort:"img"};return d}(),B={}.hasOwnProperty,C;!F(B,"undefined")&&!F(B.call,"undefined")?C=function(a,b){return B.call(a,b)}:C=function(a,b){return b in a&&F(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=w.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(w.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(w.call(arguments)))};return e}),s.flexbox=function(){return J("flexWrap")},s.canvas=function(){var a=b.createElement("canvas");return!!a.getContext&&!!a.getContext("2d")},s.canvastext=function(){return!!e.canvas&&!!F(b.createElement("canvas").getContext("2d").fillText,"function")},s.webgl=function(){return!!a.WebGLRenderingContext},s.touch=function(){var c;return"ontouchstart"in a||a.DocumentTouch&&b instanceof DocumentTouch?c=!0:y(["@media (",n.join("touch-enabled),("),h,")","{#modernizr{top:9px;position:absolute}}"].join(""),function(a){c=a.offsetTop===9}),c},s.geolocation=function(){return"geolocation"in navigator},s.postmessage=function(){return!!a.postMessage},s.websqldatabase=function(){return!!a.openDatabase},s.indexedDB=function(){return!!J("indexedDB",a)},s.hashchange=function(){return A("hashchange",a)&&(b.documentMode===c||b.documentMode>7)},s.history=function(){return!!a.history&&!!history.pushState},s.draganddrop=function(){var a=b.createElement("div");return"draggable"in a||"ondragstart"in a&&"ondrop"in a},s.websockets=function(){return"WebSocket"in a||"MozWebSocket"in a},s.rgba=function(){return D("background-color:rgba(150,255,150,.5)"),G(j.backgroundColor,"rgba")},s.hsla=function(){return D("background-color:hsla(120,40%,100%,.5)"),G(j.backgroundColor,"rgba")||G(j.backgroundColor,"hsla")},s.multiplebgs=function(){return D("background:url(https://),url(https://),red url(https://)"),/(url\s*\(.*?){3}/.test(j.background)},s.backgroundsize=function(){return J("backgroundSize")},s.borderimage=function(){return J("borderImage")},s.borderradius=function(){return J("borderRadius")},s.boxshadow=function(){return J("boxShadow")},s.textshadow=function(){return b.createElement("div").style.textShadow===""},s.opacity=function(){return E("opacity:.55"),/^0.55$/.test(j.opacity)},s.cssanimations=function(){return J("animationName")},s.csscolumns=function(){return J("columnCount")},s.cssgradients=function(){var a="background-image:",b="gradient(linear,left top,right bottom,from(#9f9),to(white));",c="linear-gradient(left top,#9f9, white);";return D((a+"-webkit- ".split(" ").join(b+a)+n.join(c+a)).slice(0,-a.length)),G(j.backgroundImage,"gradient")},s.cssreflections=function(){return J("boxReflect")},s.csstransforms=function(){return!!J("transform")},s.csstransforms3d=function(){var a=!!J("perspective");return a&&"webkitPerspective"in g.style&&y("@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}",function(b,c){a=b.offsetLeft===9&&b.offsetHeight===3}),a},s.csstransitions=function(){return J("transition")},s.fontface=function(){var a;return y('@font-face {font-family:"font";src:url("https://")}',function(c,d){var e=b.getElementById("smodernizr"),f=e.sheet||e.styleSheet,g=f?f.cssRules&&f.cssRules[0]?f.cssRules[0].cssText:f.cssText||"":"";a=/src/i.test(g)&&g.indexOf(d.split(" ")[0])===0}),a},s.generatedcontent=function(){var a;return y(["#",h,"{font:0/0 a}#",h,':after{content:"',l,'";visibility:hidden;font:3px/1 a}'].join(""),function(b){a=b.offsetHeight>=3}),a},s.video=function(){var a=b.createElement("video"),c=!1;try{if(c=!!a.canPlayType)c=new Boolean(c),c.ogg=a.canPlayType('video/ogg; codecs="theora"').replace(/^no$/,""),c.h264=a.canPlayType('video/mp4; codecs="avc1.42E01E"').replace(/^no$/,""),c.webm=a.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/,"")}catch(d){}return c},s.audio=function(){var a=b.createElement("audio"),c=!1;try{if(c=!!a.canPlayType)c=new Boolean(c),c.ogg=a.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),c.mp3=a.canPlayType("audio/mpeg;").replace(/^no$/,""),c.wav=a.canPlayType('audio/wav; codecs="1"').replace(/^no$/,""),c.m4a=(a.canPlayType("audio/x-m4a;")||a.canPlayType("audio/aac;")).replace(/^no$/,"")}catch(d){}return c},s.localstorage=function(){try{return localStorage.setItem(h,h),localStorage.removeItem(h),!0}catch(a){return!1}},s.sessionstorage=function(){try{return sessionStorage.setItem(h,h),sessionStorage.removeItem(h),!0}catch(a){return!1}},s.webworkers=function(){return!!a.Worker},s.applicationcache=function(){return!!a.applicationCache},s.svg=function(){return!!b.createElementNS&&!!b.createElementNS(r.svg,"svg").createSVGRect},s.inlinesvg=function(){var a=b.createElement("div");return a.innerHTML="",(a.firstChild&&a.firstChild.namespaceURI)==r.svg},s.smil=function(){return!!b.createElementNS&&/SVGAnimate/.test(m.call(b.createElementNS(r.svg,"animate")))},s.svgclippaths=function(){return!!b.createElementNS&&/SVGClipPath/.test(m.call(b.createElementNS(r.svg,"clipPath")))};for(var L in s)C(s,L)&&(x=L.toLowerCase(),e[x]=s[L](),v.push((e[x]?"":"no-")+x));return e.input||K(),e.addTest=function(a,b){if(typeof a=="object")for(var d in a)C(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return e;b=typeof b=="function"?b():b,typeof f!="undefined"&&f&&(g.className+=" "+(b?"":"no-")+a),e[a]=b}return e},D(""),i=k=null,function(a,b){function k(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function l(){var a=r.elements;return typeof a=="string"?a.split(" "):a}function m(a){var b=i[a[g]];return b||(b={},h++,a[g]=h,i[h]=b),b}function n(a,c,f){c||(c=b);if(j)return c.createElement(a);f||(f=m(c));var g;return f.cache[a]?g=f.cache[a].cloneNode():e.test(a)?g=(f.cache[a]=f.createElem(a)).cloneNode():g=f.createElem(a),g.canHaveChildren&&!d.test(a)?f.frag.appendChild(g):g}function o(a,c){a||(a=b);if(j)return a.createDocumentFragment();c=c||m(a);var d=c.frag.cloneNode(),e=0,f=l(),g=f.length;for(;e",f="hidden"in a,j=a.childNodes.length==1||function(){b.createElement("a");var a=b.createDocumentFragment();return typeof a.cloneNode=="undefined"||typeof a.createDocumentFragment=="undefined"||typeof a.createElement=="undefined"}()}catch(c){f=!0,j=!0}})();var r={elements:c.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",shivCSS:c.shivCSS!==!1,supportsUnknownElements:j,shivMethods:c.shivMethods!==!1,type:"default",shivDocument:q,createElement:n,createDocumentFragment:o};a.html5=r,q(b)}(this,b),e._version=d,e._prefixes=n,e._domPrefixes=q,e._cssomPrefixes=p,e.mq=z,e.hasEvent=A,e.testProp=function(a){return H([a])},e.testAllProps=J,e.testStyles=y,e.prefixed=function(a,b,c){return b?J(a,b,c):J(a,"pfx")},g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(f?" js "+v.join(" "):""),e}(this,this.document),function(a,b,c){function d(a){return"[object Function]"==o.call(a)}function e(a){return"string"==typeof a}function f(){}function g(a){return!a||"loaded"==a||"complete"==a||"uninitialized"==a}function h(){var a=p.shift();q=1,a?a.t?m(function(){("c"==a.t?B.injectCss:B.injectJs)(a.s,0,a.a,a.x,a.e,1)},0):(a(),h()):q=0}function i(a,c,d,e,f,i,j){function k(b){if(!o&&g(l.readyState)&&(u.r=o=1,!q&&h(),l.onload=l.onreadystatechange=null,b)){"img"!=a&&m(function(){t.removeChild(l)},50);for(var d in y[c])y[c].hasOwnProperty(d)&&y[c][d].onload()}}var j=j||B.errorTimeout,l=b.createElement(a),o=0,r=0,u={t:d,s:c,e:f,a:i,x:j};1===y[c]&&(r=1,y[c]=[]),"object"==a?l.data=c:(l.src=c,l.type=a),l.width=l.height="0",l.onerror=l.onload=l.onreadystatechange=function(){k.call(this,r)},p.splice(e,0,u),"img"!=a&&(r||2===y[c]?(t.insertBefore(l,s?null:n),m(k,j)):y[c].push(l))}function j(a,b,c,d,f){return q=0,b=b||"j",e(a)?i("c"==b?v:u,a,b,this.i++,c,d,f):(p.splice(this.i++,0,a),1==p.length&&h()),this}function k(){var a=B;return a.loader={load:j,i:0},a}var l=b.documentElement,m=a.setTimeout,n=b.getElementsByTagName("script")[0],o={}.toString,p=[],q=0,r="MozAppearance"in l.style,s=r&&!!b.createRange().compareNode,t=s?l:n.parentNode,l=a.opera&&"[object Opera]"==o.call(a.opera),l=!!b.attachEvent&&!l,u=r?"object":l?"script":"img",v=l?"script":u,w=Array.isArray||function(a){return"[object Array]"==o.call(a)},x=[],y={},z={timeout:function(a,b){return b.length&&(a.timeout=b[0]),a}},A,B;B=function(a){function b(a){var a=a.split("!"),b=x.length,c=a.pop(),d=a.length,c={url:c,origUrl:c,prefixes:a},e,f,g;for(f=0;f 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |

{{ count }} {{ label }}

14 |

{{ note }}

15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | {{ _t('download') }} 40 | 41 | 42 | 43 | 44 | 45 | 46 |
47 | Download As : 48 |
49 | 50 | 51 |
52 |
53 | 54 | 55 |
56 |
57 |

{{ _t('empty_list') }} :(

58 |
59 | 60 | 61 | 62 | {{ column }} 63 | {{ _t('actions') }} 64 | 65 | 66 | 67 | 68 | {{ val }} 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 |
82 | 83 |
84 | 85 | 86 | 175 | 176 | --------------------------------------------------------------------------------