├── Gandhi.paw ├── lib ├── modules │ ├── gandhi-emailTemplate-notification │ │ ├── template.html │ │ ├── package.json │ │ └── index.js │ ├── gandhi-component-form │ │ ├── readme.md │ │ ├── package.json │ │ ├── bower.json │ │ └── app │ │ │ └── default.html │ ├── gandhi-component-review │ │ ├── readme.md │ │ ├── package.json │ │ └── bower.json │ ├── gandhi-component-message │ │ ├── readme.md │ │ ├── package.json │ │ ├── bower.json │ │ ├── app │ │ │ ├── default.html │ │ │ └── stageAdmin.html │ │ └── api │ │ │ └── index.js │ ├── gandhi-component-schedule │ │ ├── readme.md │ │ ├── package.json │ │ ├── bower.json │ │ └── app │ │ │ └── default.html │ ├── gandhi-component-start │ │ ├── app │ │ │ ├── stageAdmin.html │ │ │ ├── default.html │ │ │ └── index.js │ │ ├── package.json │ │ ├── bower.json │ │ └── api │ │ │ └── index.js │ ├── gandhi-emailTemplate-recovery │ │ ├── template.html │ │ ├── package.json │ │ └── index.js │ ├── gandhi-action-notify │ │ ├── package.json │ │ └── index.js │ ├── gandhi-component-team │ │ ├── package.json │ │ ├── bower.json │ │ ├── api │ │ │ └── index.js │ │ └── app │ │ │ └── stageAdmin.html │ ├── gandhi-component │ │ ├── bower.json │ │ └── index.js │ ├── gandhi-decorator-users │ │ ├── bower.json │ │ ├── directive.html │ │ └── index.js │ ├── gandhi-decorator-ckeditor │ │ └── bower.json │ ├── gandhi-decorator-currency │ │ └── bower.json │ ├── gandhi-decorator-upload │ │ ├── bower.json │ │ └── index.html │ └── gandhi-decorator-elastic-textarea │ │ ├── bower.json │ │ └── index.js ├── app │ ├── favicon.ico │ ├── assets │ │ ├── icons │ │ │ ├── appicon-60.png │ │ │ ├── appicon-72.png │ │ │ ├── appicon-76.png │ │ │ ├── appicon-60@2x.png │ │ │ ├── appicon-60@3x.png │ │ │ ├── appicon-72@2x.png │ │ │ └── appicon-76@2x.png │ │ ├── ckeditor │ │ │ ├── plugins │ │ │ │ ├── icons.png │ │ │ │ ├── icons_hidpi.png │ │ │ │ ├── image │ │ │ │ │ └── images │ │ │ │ │ │ └── noimage.png │ │ │ │ ├── link │ │ │ │ │ ├── images │ │ │ │ │ │ ├── anchor.png │ │ │ │ │ │ └── hidpi │ │ │ │ │ │ │ └── anchor.png │ │ │ │ │ └── dialogs │ │ │ │ │ │ └── anchor.js │ │ │ │ ├── magicline │ │ │ │ │ └── images │ │ │ │ │ │ ├── icon.png │ │ │ │ │ │ ├── icon-rtl.png │ │ │ │ │ │ └── hidpi │ │ │ │ │ │ ├── icon.png │ │ │ │ │ │ └── icon-rtl.png │ │ │ │ ├── showprotected │ │ │ │ │ ├── images │ │ │ │ │ │ └── code.gif │ │ │ │ │ └── dialogs │ │ │ │ │ │ └── protected.js │ │ │ │ ├── about │ │ │ │ │ └── dialogs │ │ │ │ │ │ ├── logo_ckeditor.png │ │ │ │ │ │ ├── hidpi │ │ │ │ │ │ └── logo_ckeditor.png │ │ │ │ │ │ └── about.js │ │ │ │ ├── dialog │ │ │ │ │ └── dialogDefinition.js │ │ │ │ ├── wordcount │ │ │ │ │ └── css │ │ │ │ │ │ └── wordcount.css │ │ │ │ ├── specialchar │ │ │ │ │ └── dialogs │ │ │ │ │ │ └── lang │ │ │ │ │ │ ├── _translationstatus.txt │ │ │ │ │ │ ├── ja.js │ │ │ │ │ │ └── zh-cn.js │ │ │ │ ├── a11yhelp │ │ │ │ │ └── dialogs │ │ │ │ │ │ └── lang │ │ │ │ │ │ ├── _translationstatus.txt │ │ │ │ │ │ ├── zh-cn.js │ │ │ │ │ │ └── zh.js │ │ │ │ ├── wsc │ │ │ │ │ ├── README.md │ │ │ │ │ ├── LICENSE.md │ │ │ │ │ └── dialogs │ │ │ │ │ │ ├── wsc.css │ │ │ │ │ │ ├── ciframe.html │ │ │ │ │ │ └── tmpFrameset.html │ │ │ │ └── scayt │ │ │ │ │ ├── README.md │ │ │ │ │ ├── LICENSE.md │ │ │ │ │ └── dialogs │ │ │ │ │ └── toolbar.css │ │ │ ├── skins │ │ │ │ └── moono │ │ │ │ │ ├── icons.png │ │ │ │ │ ├── icons_hidpi.png │ │ │ │ │ ├── images │ │ │ │ │ ├── lock.png │ │ │ │ │ ├── arrow.png │ │ │ │ │ ├── close.png │ │ │ │ │ ├── lock-open.png │ │ │ │ │ ├── refresh.png │ │ │ │ │ └── hidpi │ │ │ │ │ │ ├── close.png │ │ │ │ │ │ ├── lock.png │ │ │ │ │ │ ├── refresh.png │ │ │ │ │ │ └── lock-open.png │ │ │ │ │ └── readme.md │ │ │ ├── samples │ │ │ │ ├── assets │ │ │ │ │ ├── sample.jpg │ │ │ │ │ ├── inlineall │ │ │ │ │ │ └── logo.png │ │ │ │ │ ├── uilanguages │ │ │ │ │ │ └── languages.js │ │ │ │ │ └── posteddata.php │ │ │ │ ├── plugins │ │ │ │ │ ├── htmlwriter │ │ │ │ │ │ └── assets │ │ │ │ │ │ │ └── outputforflash │ │ │ │ │ │ │ ├── outputforflash.fla │ │ │ │ │ │ │ └── outputforflash.swf │ │ │ │ │ └── dialog │ │ │ │ │ │ └── assets │ │ │ │ │ │ └── my_dialog.js │ │ │ │ ├── sample_posteddata.php │ │ │ │ ├── sample.js │ │ │ │ ├── appendto.html │ │ │ │ ├── tabindex.html │ │ │ │ └── uicolor.html │ │ │ ├── README.md │ │ │ ├── config.js │ │ │ └── build-config.js │ │ └── style │ │ │ ├── print.css │ │ │ ├── print.css.map │ │ │ ├── print.scss │ │ │ ├── _gandhi-list.scss │ │ │ ├── screen.css.map │ │ │ ├── screen.scss │ │ │ └── screen.css │ ├── portal │ │ ├── admin │ │ │ ├── dashboard.html │ │ │ ├── projects │ │ │ │ ├── show.contents.content.html │ │ │ │ ├── show.events.html │ │ │ │ ├── create.html │ │ │ │ └── show.contents.html │ │ │ ├── list.html │ │ │ ├── users │ │ │ │ ├── list.html │ │ │ │ └── create.html │ │ │ ├── cycles │ │ │ │ ├── list.html │ │ │ │ ├── show.stages.stage.html │ │ │ │ └── show.statuses.html │ │ │ └── permissions.html │ │ ├── cycles │ │ │ ├── list.html │ │ │ └── show.html │ │ ├── user │ │ │ ├── edit.html │ │ │ ├── index.js │ │ │ └── show.html │ │ ├── projects │ │ │ ├── list.html │ │ │ └── show.html │ │ └── notifications.html │ ├── embed.js │ ├── embed.html │ ├── recovery.html │ ├── .gitignore │ └── auth.html └── api │ ├── endpoints │ ├── tokens.js │ ├── notifications.js │ ├── files.js │ └── users.js │ ├── utils │ ├── uuid.js │ ├── tasks.js │ ├── auth.js │ ├── db.js │ └── cache.js │ ├── middleware │ ├── cors.js │ └── auth.js │ ├── collections │ ├── Cycles │ │ ├── Roles.js │ │ ├── Statuses.js │ │ ├── Triggers.js │ │ ├── Assignments.js │ │ ├── Invitations.js │ │ └── Stages.js │ ├── Projects │ │ ├── Assignments.js │ │ ├── Invitations.js │ │ └── Contents.js │ ├── Users.js │ ├── Cycles.js │ └── Files.js │ ├── schemas │ ├── notification.json │ ├── user.json │ └── file.json │ ├── EmbeddedCollection.js │ └── models │ ├── File.js │ └── Cycle │ ├── Role.js │ ├── Status.js │ ├── Assignment.js │ └── Invitation.js ├── .bowerrc ├── test ├── fixtures │ ├── logo.png │ ├── db │ │ ├── files.info │ │ ├── users.info │ │ ├── cycles.info │ │ ├── projects.info │ │ ├── notifications.info │ │ └── notifications.json │ └── uploads │ │ ├── 765d9d3c9328d06f0ebb388fa6076852.pdf │ │ ├── 88f71add75f29e624675c2cb557ac669.pdf │ │ ├── aa192c96ff6ef986b64f67a3ab776595.pdf │ │ ├── e4ada0dca20f5d3ec759c1a430b376c3.pdf │ │ ├── e9f1597863ef6f7375908b7aacccf126.pdf │ │ ├── 37a6289ea42d1cd6117249c74af19bdd.xlsx │ │ └── 75c35e748a4760bcd1acf900022e5b38.xlsx ├── init │ ├── 3.api.js │ ├── 2.fixtures.js │ └── 1.db.js ├── tests │ └── fixtures.js └── init.js ├── curl-timing.txt ├── util └── passwords.js ├── docker-compose.yml ├── config.test.js ├── server.js ├── migrations ├── v0.3.0.js ├── v0.2.0.js └── v0.1.0.js ├── .gitignore ├── setup └── index.js ├── package.json └── bower.json /Gandhi.paw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/Gandhi.paw -------------------------------------------------------------------------------- /lib/modules/gandhi-emailTemplate-notification/template.html: -------------------------------------------------------------------------------- 1 | {{content}} -------------------------------------------------------------------------------- /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory" : "lib/app/assets/bower", 3 | "allow_root": true 4 | } 5 | -------------------------------------------------------------------------------- /lib/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/lib/app/favicon.ico -------------------------------------------------------------------------------- /lib/modules/gandhi-component-form/readme.md: -------------------------------------------------------------------------------- 1 | Gandhi Component: Form 2 | ====================== 3 | 4 | -------------------------------------------------------------------------------- /test/fixtures/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/test/fixtures/logo.png -------------------------------------------------------------------------------- /lib/modules/gandhi-component-review/readme.md: -------------------------------------------------------------------------------- 1 | Gandhi Component: Review 2 | ======================== 3 | 4 | -------------------------------------------------------------------------------- /lib/modules/gandhi-component-message/readme.md: -------------------------------------------------------------------------------- 1 | Gandhi Component: Message 2 | ========================= 3 | 4 | -------------------------------------------------------------------------------- /lib/modules/gandhi-component-schedule/readme.md: -------------------------------------------------------------------------------- 1 | Gandhi Component: Schedule 2 | ========================== 3 | 4 | -------------------------------------------------------------------------------- /lib/app/assets/icons/appicon-60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/lib/app/assets/icons/appicon-60.png -------------------------------------------------------------------------------- /lib/app/assets/icons/appicon-72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/lib/app/assets/icons/appicon-72.png -------------------------------------------------------------------------------- /lib/app/assets/icons/appicon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/lib/app/assets/icons/appicon-76.png -------------------------------------------------------------------------------- /lib/app/assets/icons/appicon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/lib/app/assets/icons/appicon-60@2x.png -------------------------------------------------------------------------------- /lib/app/assets/icons/appicon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/lib/app/assets/icons/appicon-60@3x.png -------------------------------------------------------------------------------- /lib/app/assets/icons/appicon-72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/lib/app/assets/icons/appicon-72@2x.png -------------------------------------------------------------------------------- /lib/app/assets/icons/appicon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/lib/app/assets/icons/appicon-76@2x.png -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/plugins/icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/lib/app/assets/ckeditor/plugins/icons.png -------------------------------------------------------------------------------- /test/fixtures/db/files.info: -------------------------------------------------------------------------------- 1 | {"primary_key": "id", "type": "TABLE", "db": {"type": "DB", "name": "gandhi"}, "name": "files", "indexes": []} 2 | -------------------------------------------------------------------------------- /test/fixtures/db/users.info: -------------------------------------------------------------------------------- 1 | {"primary_key": "id", "type": "TABLE", "db": {"type": "DB", "name": "gandhi"}, "name": "users", "indexes": []} 2 | -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/skins/moono/icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/lib/app/assets/ckeditor/skins/moono/icons.png -------------------------------------------------------------------------------- /test/fixtures/db/cycles.info: -------------------------------------------------------------------------------- 1 | {"primary_key": "id", "type": "TABLE", "db": {"type": "DB", "name": "gandhi"}, "name": "cycles", "indexes": []} 2 | -------------------------------------------------------------------------------- /test/fixtures/db/projects.info: -------------------------------------------------------------------------------- 1 | {"primary_key": "id", "type": "TABLE", "db": {"type": "DB", "name": "gandhi"}, "name": "projects", "indexes": []} 2 | -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/plugins/icons_hidpi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/lib/app/assets/ckeditor/plugins/icons_hidpi.png -------------------------------------------------------------------------------- /lib/modules/gandhi-component-start/app/stageAdmin.html: -------------------------------------------------------------------------------- 1 |
There are no configuration options for this component.
2 | -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/samples/assets/sample.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/lib/app/assets/ckeditor/samples/assets/sample.jpg -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/skins/moono/icons_hidpi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/lib/app/assets/ckeditor/skins/moono/icons_hidpi.png -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/skins/moono/images/lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/lib/app/assets/ckeditor/skins/moono/images/lock.png -------------------------------------------------------------------------------- /test/fixtures/db/notifications.info: -------------------------------------------------------------------------------- 1 | {"primary_key": "id", "type": "TABLE", "db": {"type": "DB", "name": "gandhi"}, "name": "notifications", "indexes": []} 2 | -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/skins/moono/images/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/lib/app/assets/ckeditor/skins/moono/images/arrow.png -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/skins/moono/images/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/lib/app/assets/ckeditor/skins/moono/images/close.png -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/plugins/image/images/noimage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/lib/app/assets/ckeditor/plugins/image/images/noimage.png -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/plugins/link/images/anchor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/lib/app/assets/ckeditor/plugins/link/images/anchor.png -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/skins/moono/images/lock-open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/lib/app/assets/ckeditor/skins/moono/images/lock-open.png -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/skins/moono/images/refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/lib/app/assets/ckeditor/skins/moono/images/refresh.png -------------------------------------------------------------------------------- /lib/app/portal/admin/dashboard.html: -------------------------------------------------------------------------------- 1 |
With great power comes great responsibility.
- Voltaire (and Spiderman)
2 | -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/plugins/magicline/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/lib/app/assets/ckeditor/plugins/magicline/images/icon.png -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/samples/assets/inlineall/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/lib/app/assets/ckeditor/samples/assets/inlineall/logo.png -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/skins/moono/images/hidpi/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/lib/app/assets/ckeditor/skins/moono/images/hidpi/close.png -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/skins/moono/images/hidpi/lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/lib/app/assets/ckeditor/skins/moono/images/hidpi/lock.png -------------------------------------------------------------------------------- /test/fixtures/uploads/765d9d3c9328d06f0ebb388fa6076852.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/test/fixtures/uploads/765d9d3c9328d06f0ebb388fa6076852.pdf -------------------------------------------------------------------------------- /test/fixtures/uploads/88f71add75f29e624675c2cb557ac669.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/test/fixtures/uploads/88f71add75f29e624675c2cb557ac669.pdf -------------------------------------------------------------------------------- /test/fixtures/uploads/aa192c96ff6ef986b64f67a3ab776595.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/test/fixtures/uploads/aa192c96ff6ef986b64f67a3ab776595.pdf -------------------------------------------------------------------------------- /test/fixtures/uploads/e4ada0dca20f5d3ec759c1a430b376c3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/test/fixtures/uploads/e4ada0dca20f5d3ec759c1a430b376c3.pdf -------------------------------------------------------------------------------- /test/fixtures/uploads/e9f1597863ef6f7375908b7aacccf126.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/test/fixtures/uploads/e9f1597863ef6f7375908b7aacccf126.pdf -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/plugins/link/images/hidpi/anchor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/lib/app/assets/ckeditor/plugins/link/images/hidpi/anchor.png -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/plugins/magicline/images/icon-rtl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/lib/app/assets/ckeditor/plugins/magicline/images/icon-rtl.png -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/plugins/showprotected/images/code.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/lib/app/assets/ckeditor/plugins/showprotected/images/code.gif -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/skins/moono/images/hidpi/refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/lib/app/assets/ckeditor/skins/moono/images/hidpi/refresh.png -------------------------------------------------------------------------------- /test/fixtures/uploads/37a6289ea42d1cd6117249c74af19bdd.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/test/fixtures/uploads/37a6289ea42d1cd6117249c74af19bdd.xlsx -------------------------------------------------------------------------------- /test/fixtures/uploads/75c35e748a4760bcd1acf900022e5b38.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/test/fixtures/uploads/75c35e748a4760bcd1acf900022e5b38.xlsx -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/plugins/about/dialogs/logo_ckeditor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/lib/app/assets/ckeditor/plugins/about/dialogs/logo_ckeditor.png -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/plugins/magicline/images/hidpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/lib/app/assets/ckeditor/plugins/magicline/images/hidpi/icon.png -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/skins/moono/images/hidpi/lock-open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/lib/app/assets/ckeditor/skins/moono/images/hidpi/lock-open.png -------------------------------------------------------------------------------- /lib/app/portal/admin/projects/show.contents.content.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 |
6 | 7 |
8 | -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/plugins/magicline/images/hidpi/icon-rtl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/lib/app/assets/ckeditor/plugins/magicline/images/hidpi/icon-rtl.png -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/plugins/about/dialogs/hidpi/logo_ckeditor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/lib/app/assets/ckeditor/plugins/about/dialogs/hidpi/logo_ckeditor.png -------------------------------------------------------------------------------- /lib/modules/gandhi-emailTemplate-recovery/template.html: -------------------------------------------------------------------------------- 1 | 2 |

Hi there, {{user.name}}!

3 |

Here's your password reset link: Reset Password

4 | -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/plugins/dialog/dialogDefinition.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved. 3 | For licensing, see LICENSE.md or http://ckeditor.com/license 4 | */ 5 | -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/plugins/wordcount/css/wordcount.css: -------------------------------------------------------------------------------- 1 | .cke_wordcount {display:block;float:right;margin-top:-2px;margin-right:3px;color:black;} 2 | 3 | .cke_wordcountLimitReached span {color:red! important} 4 | -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/samples/plugins/htmlwriter/assets/outputforflash/outputforflash.fla: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/lib/app/assets/ckeditor/samples/plugins/htmlwriter/assets/outputforflash/outputforflash.fla -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/samples/plugins/htmlwriter/assets/outputforflash/outputforflash.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-marcacci/gandhi/HEAD/lib/app/assets/ckeditor/samples/plugins/htmlwriter/assets/outputforflash/outputforflash.swf -------------------------------------------------------------------------------- /lib/modules/gandhi-action-notify/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gandhi-action-notify", 3 | "description": "Gandhi Action: Notify", 4 | "version": "0.0.1", 5 | "license": "MIT", 6 | "private": false, 7 | "main": "index.js", 8 | "dependencies": {} 9 | } 10 | -------------------------------------------------------------------------------- /lib/modules/gandhi-component-form/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gandhi-component-form", 3 | "description": "Gandhi Component: Form", 4 | "version": "0.0.1", 5 | "license": "MIT", 6 | "private": true, 7 | "main": "api/index.js", 8 | "dependencies": {} 9 | } 10 | -------------------------------------------------------------------------------- /lib/modules/gandhi-component-team/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gandhi-component-team", 3 | "description": "Gandhi Component: Team", 4 | "version": "0.0.1", 5 | "license": "MIT", 6 | "private": true, 7 | "main": "api/index.js", 8 | "dependencies": {} 9 | } 10 | -------------------------------------------------------------------------------- /lib/modules/gandhi-component-review/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gandhi-component-review", 3 | "description": "Gandhi Component: Review", 4 | "version": "0.0.1", 5 | "license": "MIT", 6 | "private": true, 7 | "main": "api/index.js", 8 | "dependencies": {} 9 | } 10 | -------------------------------------------------------------------------------- /lib/modules/gandhi-component-start/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gandhi-component-start", 3 | "description": "Gandhi Component: Start", 4 | "version": "0.0.1", 5 | "license": "MIT", 6 | "private": true, 7 | "main": "api/index.js", 8 | "dependencies": {} 9 | } 10 | -------------------------------------------------------------------------------- /lib/modules/gandhi-component-message/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gandhi-component-message", 3 | "description": "Gandhi Component: Message", 4 | "version": "0.0.1", 5 | "license": "MIT", 6 | "private": true, 7 | "main": "api/index.js", 8 | "dependencies": {} 9 | } 10 | -------------------------------------------------------------------------------- /lib/app/assets/style/print.css: -------------------------------------------------------------------------------- 1 | #sidebar { 2 | display: none; 3 | width: 0 !important; } 4 | 5 | #body { 6 | margin-left: 0 !important; 7 | padding-left: 0 !important; 8 | left: 0 !important; 9 | width: 100% !important; } 10 | 11 | /*# sourceMappingURL=print.css.map */ 12 | -------------------------------------------------------------------------------- /lib/app/assets/style/print.css.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "mappings": "AAGA,QAAS;EACR,OAAO,EAAE,IAAI;EACb,KAAK,EAAE,YAAY;;AAGpB,KAAM;EACL,WAAW,EAAE,YAAY;EACzB,YAAY,EAAE,YAAY;EAC1B,IAAI,EAAE,YAAY;EAClB,KAAK,EAAE,eAAe", 4 | "sources": ["print.scss"], 5 | "names": [], 6 | "file": "print.css" 7 | } -------------------------------------------------------------------------------- /lib/modules/gandhi-component-schedule/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gandhi-component-schedule", 3 | "description": "Gandhi Component: Schedule", 4 | "version": "0.0.1", 5 | "license": "MIT", 6 | "private": true, 7 | "main": "api/index.js", 8 | "dependencies": {} 9 | } 10 | -------------------------------------------------------------------------------- /lib/modules/gandhi-emailTemplate-recovery/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gandhi-emailTemplate-recovery", 3 | "description": "Gandhi Email Template: Recovery", 4 | "version": "0.0.1", 5 | "license": "MIT", 6 | "private": false, 7 | "main": "index.js", 8 | "dependencies": {} 9 | } 10 | -------------------------------------------------------------------------------- /lib/api/endpoints/tokens.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(config, router, resources){ 4 | 5 | var controller = require('../controllers/tokens')(config, resources); 6 | 7 | // post 8 | // ---- 9 | router.post( 10 | '/api/tokens', 11 | controller.post 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /lib/modules/gandhi-emailTemplate-notification/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gandhi-emailTemplate-notification", 3 | "description": "Gandhi Email Template: Notification", 4 | "version": "0.0.1", 5 | "license": "MIT", 6 | "private": false, 7 | "main": "index.js", 8 | "dependencies": {} 9 | } 10 | -------------------------------------------------------------------------------- /lib/modules/gandhi-emailTemplate-recovery/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var handlebars = require('handlebars'); 5 | 6 | module.exports = function(router, resources){ 7 | resources.emailTemplates.recovery = handlebars.compile(fs.readFileSync(__dirname + '/template.html').toString('utf8')); 8 | } 9 | -------------------------------------------------------------------------------- /lib/modules/gandhi-emailTemplate-notification/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var handlebars = require('handlebars'); 5 | 6 | module.exports = function(router, resources){ 7 | resources.emailTemplates.notification = handlebars.compile(fs.readFileSync(__dirname + '/template.html').toString('utf8')); 8 | } 9 | -------------------------------------------------------------------------------- /lib/api/utils/uuid.js: -------------------------------------------------------------------------------- 1 | function rand(c) { 2 | var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); 3 | return v.toString(16); 4 | } 5 | 6 | function uuid(){ 7 | return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, rand); 8 | // return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, rand); 9 | } 10 | 11 | module.exports = uuid; -------------------------------------------------------------------------------- /curl-timing.txt: -------------------------------------------------------------------------------- 1 | time_namelookup: %{time_namelookup}\n 2 | time_connect: %{time_connect}\n 3 | time_appconnect: %{time_appconnect}\n 4 | time_pretransfer: %{time_pretransfer}\n 5 | time_redirect: %{time_redirect}\n 6 | time_starttransfer: %{time_starttransfer}\n 7 | ----------\n 8 | time_total: %{time_total}\n -------------------------------------------------------------------------------- /lib/modules/gandhi-component/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gandhi-component", 3 | "description": "Gandhi Component Layer", 4 | "version": "0.0.1", 5 | "license": "MIT", 6 | "private": true, 7 | "main": "./index.js", 8 | "dependencies": { 9 | "angular": "*", 10 | "angular-sanitize": "*" 11 | }, 12 | "module": "gandhi-component" 13 | } 14 | -------------------------------------------------------------------------------- /lib/modules/gandhi-component-start/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gandhi-component-start", 3 | "description": "Gandhi Component: Start", 4 | "version": "0.0.1", 5 | "license": "MIT", 6 | "private": true, 7 | "main": "./app/index.js", 8 | "dependencies": { 9 | "angular": "*", 10 | "gandhi-component": "*" 11 | }, 12 | "module": "gandhi-component-start" 13 | } 14 | -------------------------------------------------------------------------------- /util/passwords.js: -------------------------------------------------------------------------------- 1 | var scrypt = require('@gbradley/scrypt'); 2 | scrypt.hash.config.keyEncoding = scrypt.verify.config.keyEncoding = 'utf8'; 3 | scrypt.hash.config.outputEncoding = scrypt.verify.config.outputEncoding = 'base64'; 4 | 5 | process.argv.splice(2, process.argv.length-2).forEach(function(p){ 6 | console.log(p + ' : ' + scrypt.hash(p, scrypt.params(0.1))); 7 | }); 8 | -------------------------------------------------------------------------------- /lib/app/embed.js: -------------------------------------------------------------------------------- 1 | ;!function(base, id){ 2 | 'use strict'; 3 | 4 | var script = document.getElementById(id); 5 | var iframe = document.createElement('iframe'); 6 | iframe.style.width = '100%'; 7 | iframe.style.height = '400px'; 8 | iframe.setAttribute('frameborder', '0'); 9 | iframe.src = '//' + base + '#/embed'; 10 | script.parentElement.insertBefore(iframe, script); 11 | } -------------------------------------------------------------------------------- /lib/modules/gandhi-component-form/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gandhi-component-form", 3 | "description": "Gandhi Component: Form", 4 | "version": "0.0.1", 5 | "license": "MIT", 6 | "private": true, 7 | "main": "./app/index.js", 8 | "dependencies": { 9 | "angular": "*", 10 | "gandhi-component": "*" 11 | }, 12 | "module": "gandhi-component-form" 13 | } 14 | -------------------------------------------------------------------------------- /lib/modules/gandhi-component-team/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gandhi-component-team", 3 | "description": "Gandhi Component: Team", 4 | "version": "0.0.1", 5 | "license": "MIT", 6 | "private": true, 7 | "main": "./app/index.js", 8 | "dependencies": { 9 | "angular": "*", 10 | "gandhi-component": "*" 11 | }, 12 | "module": "gandhi-component-team" 13 | } 14 | -------------------------------------------------------------------------------- /lib/modules/gandhi-component-review/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gandhi-component-review", 3 | "description": "Gandhi Component: Review", 4 | "version": "0.0.1", 5 | "license": "MIT", 6 | "private": true, 7 | "main": "./app/index.js", 8 | "dependencies": { 9 | "angular": "*", 10 | "gandhi-component": "*" 11 | }, 12 | "module": "gandhi-component-review" 13 | } 14 | -------------------------------------------------------------------------------- /lib/modules/gandhi-component-message/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gandhi-component-message", 3 | "description": "Gandhi Component: Message", 4 | "version": "0.0.1", 5 | "license": "MIT", 6 | "private": true, 7 | "main": "./app/index.js", 8 | "dependencies": { 9 | "angular": "*", 10 | "gandhi-component": "*" 11 | }, 12 | "module": "gandhi-component-message" 13 | } 14 | -------------------------------------------------------------------------------- /lib/app/portal/admin/list.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | List 4 |
5 | 6 |
7 |
8 |
9 | -------------------------------------------------------------------------------- /lib/modules/gandhi-component-schedule/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gandhi-component-schedule", 3 | "description": "Gandhi Component: Schedule", 4 | "version": "0.0.1", 5 | "license": "MIT", 6 | "private": true, 7 | "main": "./app/index.js", 8 | "dependencies": { 9 | "angular": "*", 10 | "gandhi-component": "*" 11 | }, 12 | "module": "gandhi-component-schedule" 13 | } 14 | -------------------------------------------------------------------------------- /test/init/3.api.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require('lodash'); 4 | var express = require('express'); 5 | 6 | module.exports = { 7 | setup: function(done){ 8 | var app = express(); 9 | app.use(require("../../lib/index.js")(global.setup.config)); 10 | global.setup.api = require("supertest")(app); 11 | done(); 12 | }, 13 | teardown: function(done){ 14 | done(); 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /lib/modules/gandhi-decorator-users/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gandhi-decorator-users", 3 | "description": "Gandhi Decorator: Users", 4 | "version": "0.0.1", 5 | "license": "MIT", 6 | "private": true, 7 | "main": [ 8 | "index.js", 9 | "index.html" 10 | ], 11 | "dependencies": { 12 | "angular": "*", 13 | "angular-schema-form": "*" 14 | }, 15 | "module": "gandhi-decorator-users" 16 | } 17 | -------------------------------------------------------------------------------- /lib/modules/gandhi-decorator-ckeditor/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gandhi-decorator-ckeditor", 3 | "description": "Gandhi Decorator: CKEditor", 4 | "version": "0.0.1", 5 | "license": "MIT", 6 | "private": true, 7 | "main": [ 8 | "index.js", 9 | "index.html" 10 | ], 11 | "dependencies": { 12 | "angular": "*", 13 | "angular-schema-form": "*" 14 | }, 15 | "module": "gandhi-decorator-ckeditor" 16 | } 17 | -------------------------------------------------------------------------------- /lib/modules/gandhi-decorator-currency/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gandhi-decorator-currency", 3 | "description": "Gandhi Decorator: Currency", 4 | "version": "0.0.1", 5 | "license": "MIT", 6 | "private": true, 7 | "main": [ 8 | "index.js", 9 | "index.html" 10 | ], 11 | "dependencies": { 12 | "angular": "*", 13 | "angular-schema-form": "*" 14 | }, 15 | "module": "gandhi-decorator-currency" 16 | } 17 | -------------------------------------------------------------------------------- /lib/app/assets/style/print.scss: -------------------------------------------------------------------------------- 1 | // This file's entire existance is a hack that only exists because chrome for windows sucks 2 | // and can't handle media queries when printing... even though Chrome for mac has no issue 3 | 4 | #sidebar { 5 | display: none; 6 | width: 0 !important; 7 | } 8 | 9 | #body { 10 | margin-left: 0 !important; 11 | padding-left: 0 !important; 12 | left: 0 !important; 13 | width: 100% !important; 14 | } 15 | -------------------------------------------------------------------------------- /lib/modules/gandhi-decorator-upload/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gandhi-decorator-upload", 3 | "description": "Gandhi Decorator: Upload", 4 | "version": "0.0.1", 5 | "license": "MIT", 6 | "private": true, 7 | "main": [ 8 | "index.js", 9 | "index.html" 10 | ], 11 | "dependencies": { 12 | "angular": "*", 13 | "angular-schema-form": "*", 14 | "angular-ui-select": "*" 15 | }, 16 | "module": "gandhi-decorator-upload" 17 | } 18 | -------------------------------------------------------------------------------- /lib/app/embed.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |

Welcome.

4 |
5 | 6 |

Welcome back, {{ currentUser.name }}

7 |
8 | 9 |
10 | Launch Portal 11 | Log Out 12 |
13 | 14 |
-------------------------------------------------------------------------------- /lib/app/portal/admin/users/list.html: -------------------------------------------------------------------------------- 1 | 5 | 6 |
7 |
8 | 9 |
10 |
11 | 12 |
13 | -------------------------------------------------------------------------------- /lib/app/portal/admin/cycles/list.html: -------------------------------------------------------------------------------- 1 | 5 | 6 |
7 |
8 | 9 |
10 |
11 | 12 |
13 | -------------------------------------------------------------------------------- /lib/modules/gandhi-decorator-elastic-textarea/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gandhi-decorator-elastic-textarea", 3 | "description": "Gandhi Decorator: Elastic Textarea", 4 | "version": "0.0.1", 5 | "license": "MIT", 6 | "private": true, 7 | "main": [ 8 | "index.js", 9 | "index.html" 10 | ], 11 | "dependencies": { 12 | "angular": "*", 13 | "angular-schema-form": "*", 14 | "angular-elastic": "*" 15 | }, 16 | "module": "gandhi-decorator-elastic-textarea" 17 | } 18 | -------------------------------------------------------------------------------- /lib/modules/gandhi-decorator-users/directive.html: -------------------------------------------------------------------------------- 1 | 2 | {{$select.selected.name}} 3 | 4 |
5 | 6 |
7 |
8 | -------------------------------------------------------------------------------- /lib/api/middleware/cors.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(config, resources){ 4 | // allow all CORS 5 | return function cors(req, res, next) { 6 | res.header('Access-Control-Allow-Origin', '*'); 7 | res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,PATCH,DELETE,OPTIONS'); 8 | res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With'); 9 | 10 | // intercept OPTIONS method 11 | if ('OPTIONS' == req.method) { 12 | res.send(200); 13 | } else { 14 | next(); 15 | } 16 | }; 17 | }; 18 | -------------------------------------------------------------------------------- /test/init/2.fixtures.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require('lodash'); 4 | 5 | module.exports = { 6 | setup: function(done){ 7 | global.setup.fixtures = { 8 | db: { 9 | cycles: _.cloneDeep(require('../fixtures/db/cycles.json')), 10 | projects: _.cloneDeep(require('../fixtures/db/projects.json')), 11 | users: _.cloneDeep(require('../fixtures/db/users.json')), 12 | notifications: _.cloneDeep(require('../fixtures/db/notifications.json')) 13 | } 14 | }; 15 | 16 | return done(); 17 | }, 18 | teardown: function(done){ 19 | done(); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /lib/modules/gandhi-component-start/api/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require('lodash'); 4 | 5 | module.exports = function(router, resources){ 6 | resources.components['start'] = { 7 | stage: { 8 | read: function(conn, stage) { 9 | return stage; 10 | }, 11 | write: function(conn, data, stage) { 12 | return _.merge({}, stage, data); 13 | } 14 | }, 15 | content: { 16 | read: function(conn, content, stage) { 17 | return content; 18 | }, 19 | write: function(conn, data, content, stage) { 20 | return data; 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/app/portal/admin/permissions.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 |
Role{{p.title}}
{{role.title}} 13 | 14 |
18 |
-------------------------------------------------------------------------------- /test/fixtures/db/notifications.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "id": "2df77fb9-6fe0-448f-ad06-a4d145ca8c46", 3 | "user_id": "3cd2dc98-e280-4e72-a437-9a916d98b636", 4 | "subject": "Sample", 5 | "content": "This is a sample notification", 6 | "status_id": "unread", 7 | "created": 1420401884.663, 8 | "updated": 1420401884.663 9 | },{ 10 | "id": "df973a3b-99d2-420c-800b-6463778dedf2", 11 | "user_id": "84fbaa78-54b0-42fc-9754-c9ea7dc24538", 12 | "subject": "Tim's Notification", 13 | "content": "This is Tim's sample notification", 14 | "status_id": "read", 15 | "created": 1420401884.663, 16 | "updated": 1420401884.663 17 | }] 18 | -------------------------------------------------------------------------------- /lib/api/utils/tasks.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var prefix = 'gandhi:tasks:'; 4 | 5 | module.exports = function(config, resources) { 6 | 7 | 8 | 9 | 10 | 11 | // TODO: 12 | // - get a list of tasks `GET prefix:*` 13 | // - watch redis for key expiration 14 | // - perform method on collection with args 15 | 16 | 17 | 18 | 19 | 20 | 21 | return { 22 | set: function set(key, collection, method, args){ 23 | return resources.redis.set(prefix + key, { 24 | collection: collection, 25 | method: method, 26 | args: args 27 | }); 28 | }, 29 | unset: function unset(key){ 30 | resources.redis.del(prefix + key); 31 | } 32 | } 33 | }; -------------------------------------------------------------------------------- /lib/app/assets/style/_gandhi-list.scss: -------------------------------------------------------------------------------- 1 | .gandhi-list { 2 | 3 | table, thead, tbody { 4 | display: block; 5 | 6 | tr { 7 | display: flex; 8 | display: -webkit-flex; 9 | display: -moz-flex; 10 | display: -ms-flex; 11 | 12 | th, td { 13 | overflow: hidden; 14 | white-space: nowrap; 15 | text-overflow: ellipsis; 16 | 17 | div { 18 | overflow: hidden; 19 | text-overflow: ellipsis; 20 | } 21 | } 22 | } 23 | } 24 | 25 | thead.floating { 26 | display: none; 27 | position: fixed; 28 | background: white; 29 | top: 0; 30 | z-index: 1; 31 | } 32 | 33 | &.scrolling thead { 34 | display: block; 35 | } 36 | 37 | } -------------------------------------------------------------------------------- /lib/app/portal/admin/projects/show.events.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | Events 4 |
5 |
6 |
7 |
{{ triggersById[id].title }}
8 |

{{ triggersById[id].description }}

9 | 10 | 11 | 12 |
TimestampValue
{{ event.timestamp * 1000 | date:"yyyy.MM.dd - hh:mm a" }}{{ event.value ? 'TRUE' : 'FALSE' }}
13 |
14 |
15 |
16 |
-------------------------------------------------------------------------------- /lib/api/utils/auth.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Promise = require('bluebird'); 4 | var jwt = require('jsonwebtoken'); 5 | var crypto = require('crypto'); 6 | 7 | module.exports = function(config) { 8 | return { 9 | random: function(length){ 10 | return crypto.randomBytes(length || 64).toString('base64'); 11 | }, 12 | verifyToken: function(token) { 13 | return new Promise(function(resolve, reject, notify){ 14 | jwt.verify(token, config.auth.secret, function(err, res) { 15 | if(err) reject(err); else resolve(res); 16 | }); 17 | }); 18 | }, 19 | signToken: function(data, options) { 20 | return jwt.sign(data, config.auth.secret, options); 21 | } 22 | }; 23 | }; 24 | -------------------------------------------------------------------------------- /lib/modules/gandhi-component-message/app/default.html: -------------------------------------------------------------------------------- 1 |
Loading...
2 | 3 |
4 | 5 | 8 | 9 |
10 | 11 |
12 |
13 |
14 | {{cycle.title}} 15 |
16 |
17 |
18 |
19 |
20 |
21 | 22 |
23 |
-------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.9" 2 | 3 | services: 4 | 5 | gandhi: 6 | depends_on: 7 | - redis 8 | - rethinkdb 9 | image: node:16 10 | tty: true 11 | working_dir: /gandhi 12 | command: yarn start 13 | environment: 14 | NODE_ENV: development 15 | volumes: &volumes 16 | - type: bind 17 | source: . 18 | target: /gandhi 19 | ports: 20 | - target: 3000 21 | published: ${GANDHI_PORT:-0} 22 | protocol: tcp 23 | mode: host 24 | 25 | redis: 26 | image: redis:7 27 | tty: true 28 | 29 | rethinkdb: 30 | image: rethinkdb:2 31 | tty: true 32 | volumes: 33 | - type: bind 34 | source: ./rethinkdb_data 35 | target: /data/rethinkdb_data -------------------------------------------------------------------------------- /lib/api/collections/Cycles/Roles.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Promise = require('bluebird'); 4 | var util = require('util'); 5 | var errors = require('../../errors'); 6 | 7 | var EmbeddedCollection = require('../../EmbeddedCollection'); 8 | var Role = require('../../models/Cycle/Role'); 9 | 10 | function Roles (data, parent) { 11 | this.model = Role; 12 | EmbeddedCollection.call(this, data, parent); 13 | } 14 | 15 | util.inherits(Roles, EmbeddedCollection); 16 | 17 | Roles.prototype.query = function(conn, query) { 18 | var self = this; 19 | 20 | if(!self.parent.authorizations['cycle/roles:read']) 21 | return Promise.reject(new errors.ForbiddenError()); 22 | 23 | return EmbeddedCollection.prototype.query.call(self, conn, query); 24 | }; 25 | 26 | module.exports = Roles; -------------------------------------------------------------------------------- /lib/api/collections/Cycles/Statuses.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Promise = require('bluebird'); 4 | var util = require('util'); 5 | var errors = require('../../errors'); 6 | 7 | var EmbeddedCollection = require('../../EmbeddedCollection'); 8 | var Status = require('../../models/Cycle/Status'); 9 | 10 | function Statuses (data, parent) { 11 | this.model = Status; 12 | EmbeddedCollection.call(this, data, parent); 13 | } 14 | 15 | util.inherits(Statuses, EmbeddedCollection); 16 | 17 | Statuses.prototype.query = function(conn, query) { 18 | var self = this; 19 | 20 | if(!self.parent.authorizations['cycle/statuses:read']) 21 | return Promise.reject(new errors.ForbiddenError()); 22 | 23 | return EmbeddedCollection.prototype.query.call(self, conn, query); 24 | }; 25 | 26 | module.exports = Statuses; -------------------------------------------------------------------------------- /lib/api/collections/Cycles/Triggers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Promise = require('bluebird'); 4 | var util = require('util'); 5 | var errors = require('../../errors'); 6 | 7 | var EmbeddedCollection = require('../../EmbeddedCollection'); 8 | var Trigger = require('../../models/Cycle/Trigger'); 9 | 10 | function Triggers (data, parent) { 11 | this.model = Trigger; 12 | EmbeddedCollection.call(this, data, parent); 13 | } 14 | 15 | util.inherits(Triggers, EmbeddedCollection); 16 | 17 | Triggers.prototype.query = function(conn, query) { 18 | var self = this; 19 | 20 | if(!self.parent.authorizations['cycle/triggers:read']) 21 | return Promise.reject(new errors.ForbiddenError()); 22 | 23 | return EmbeddedCollection.prototype.query.call(self, conn, query); 24 | }; 25 | 26 | module.exports = Triggers; -------------------------------------------------------------------------------- /config.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs') 4 | 5 | module.exports = { 6 | root: '', 7 | db: { 8 | host: 'rethinkdb', 9 | db: `gandhi_${(Math.random() + 1).toString(36).substring(7)}` 10 | }, 11 | pool: { 12 | max: 1, 13 | min: 1, 14 | timeout: 30000 15 | }, 16 | redis: { 17 | host: 'redis', 18 | port: 6379 19 | }, 20 | lock: { 21 | retry: 50, 22 | timeout: 30000 23 | }, 24 | auth: { 25 | secret: 'rubber bunny' 26 | }, 27 | mail: { 28 | transport: null, 29 | defaults: { 30 | from: 'test@test.gandhi.io' 31 | } 32 | }, 33 | modules: fs.readdirSync(__dirname + '/lib/modules').map(function(dir){ 34 | return __dirname + '/lib/modules/' + dir; 35 | }), 36 | files: { 37 | directory: __dirname + '/uploads' 38 | }, 39 | port: 3000, 40 | log: false 41 | }; 42 | -------------------------------------------------------------------------------- /lib/api/utils/db.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Promise = require('bluebird') 4 | const Pool = require('generic-pool').Pool 5 | const r = require('rethinkdb') 6 | 7 | module.exports = function (options, max, min, idleTimeoutMillis) { 8 | let pool = new Pool({ 9 | name: 'rethinkdb', 10 | create: function (callback) { 11 | return r.connect(options, callback) 12 | }, 13 | destroy: function (connection) { 14 | return connection.close() 15 | }, 16 | log: false, 17 | min: min || 2, 18 | max: max || 10, 19 | idleTimeoutMillis: idleTimeoutMillis || 30000, 20 | }) 21 | 22 | pool.disposer = function () { 23 | return Promise.fromNode(pool.acquire.bind(pool)).disposer(function (conn) { 24 | pool.release(conn) 25 | }) 26 | } 27 | 28 | return pool 29 | } 30 | -------------------------------------------------------------------------------- /lib/api/collections/Cycles/Assignments.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Promise = require('bluebird'); 4 | var util = require('util'); 5 | var errors = require('../../errors'); 6 | 7 | var EmbeddedCollection = require('../../EmbeddedCollection'); 8 | var Assignment = require('../../models/Cycle/Assignment'); 9 | 10 | function Assignments (data, parent) { 11 | this.model = Assignment; 12 | EmbeddedCollection.call(this, data, parent); 13 | } 14 | 15 | util.inherits(Assignments, EmbeddedCollection); 16 | 17 | Assignments.prototype.query = function(conn, query) { 18 | var self = this; 19 | 20 | if(!self.parent.authorizations['cycle/assignments:read']) 21 | return Promise.reject(new errors.ForbiddenError()); 22 | 23 | return EmbeddedCollection.prototype.query.call(self, conn, query); 24 | }; 25 | 26 | module.exports = Assignments; -------------------------------------------------------------------------------- /lib/api/collections/Cycles/Invitations.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Promise = require('bluebird'); 4 | var util = require('util'); 5 | var errors = require('../../errors'); 6 | 7 | var EmbeddedCollection = require('../../EmbeddedCollection'); 8 | var Invitation = require('../../models/Cycle/Invitation'); 9 | 10 | function Invitations (data, parent) { 11 | this.model = Invitation; 12 | EmbeddedCollection.call(this, data, parent); 13 | } 14 | 15 | util.inherits(Invitations, EmbeddedCollection); 16 | 17 | Invitations.prototype.query = function(conn, query) { 18 | var self = this; 19 | 20 | if(!self.parent.authorizations['cycle/invitations:read']) 21 | return Promise.reject(new errors.ForbiddenError()); 22 | 23 | return EmbeddedCollection.prototype.query.call(self, conn, query); 24 | }; 25 | 26 | module.exports = Invitations; -------------------------------------------------------------------------------- /lib/app/portal/cycles/list.html: -------------------------------------------------------------------------------- 1 | 4 | 5 |
6 |
7 | 8 |
9 |
10 | 11 |
12 |
13 | All Cycles 14 |
15 |
16 | 17 | 26 | -------------------------------------------------------------------------------- /lib/api/collections/Projects/Assignments.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Promise = require('bluebird'); 4 | var util = require('util'); 5 | var errors = require('../../errors'); 6 | 7 | var EmbeddedCollection = require('../../EmbeddedCollection'); 8 | var Assignment = require('../../models/Project/Assignment'); 9 | 10 | function Assignments (data, parent) { 11 | this.model = Assignment; 12 | EmbeddedCollection.call(this, data, parent); 13 | } 14 | 15 | util.inherits(Assignments, EmbeddedCollection); 16 | 17 | Assignments.prototype.query = function(conn, query) { 18 | var self = this; 19 | 20 | if(!self.parent.authorizations['project/assignments:read']) 21 | return Promise.reject(new errors.ForbiddenError()); 22 | 23 | return EmbeddedCollection.prototype.query.call(self, conn, query); 24 | }; 25 | 26 | module.exports = Assignments; -------------------------------------------------------------------------------- /lib/api/collections/Projects/Invitations.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Promise = require('bluebird'); 4 | var util = require('util'); 5 | var errors = require('../../errors'); 6 | 7 | var EmbeddedCollection = require('../../EmbeddedCollection'); 8 | var Invitation = require('../../models/Project/Invitation'); 9 | 10 | function Invitations (data, parent) { 11 | this.model = Invitation; 12 | EmbeddedCollection.call(this, data, parent); 13 | } 14 | 15 | util.inherits(Invitations, EmbeddedCollection); 16 | 17 | Invitations.prototype.query = function(conn, query) { 18 | var self = this; 19 | 20 | if(!self.parent.authorizations['project/invitations:read']) 21 | return Promise.reject(new errors.ForbiddenError()); 22 | 23 | return EmbeddedCollection.prototype.query.call(self, conn, query); 24 | }; 25 | 26 | module.exports = Invitations; -------------------------------------------------------------------------------- /lib/api/collections/Cycles/Stages.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Promise = require('bluebird'); 4 | var util = require('util'); 5 | var errors = require('../../errors'); 6 | 7 | var EmbeddedCollection = require('../../EmbeddedCollection'); 8 | var Stage = require('../../models/Cycle/Stage'); 9 | 10 | function Stages (data, parent) { 11 | this.model = Stage; 12 | EmbeddedCollection.call(this, data, parent); 13 | } 14 | 15 | util.inherits(Stages, EmbeddedCollection); 16 | 17 | Stages.prototype.query = function(conn, query) { 18 | var self = this; 19 | 20 | if(!self.parent.authorizations['cycle/stages:read']) 21 | return Promise.reject(new errors.ForbiddenError()); 22 | 23 | // sort by order 24 | query.sort = 'order'; 25 | 26 | return EmbeddedCollection.prototype.query.call(self, conn, query); 27 | }; 28 | 29 | module.exports = Stages; -------------------------------------------------------------------------------- /lib/app/recovery.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |

Password Recovery.

4 |
5 | 6 |
7 |
8 |

Hi there, {{currentUser.name}}!

9 | 10 |
11 |
12 | 13 |
14 |
15 | 16 |
17 | 18 |
19 |
20 |
21 |
-------------------------------------------------------------------------------- /lib/app/assets/ckeditor/plugins/specialchar/dialogs/lang/_translationstatus.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved. 2 | For licensing, see LICENSE.md or http://ckeditor.com/license 3 | 4 | cs.js Found: 118 Missing: 0 5 | cy.js Found: 118 Missing: 0 6 | de.js Found: 118 Missing: 0 7 | el.js Found: 16 Missing: 102 8 | eo.js Found: 118 Missing: 0 9 | et.js Found: 31 Missing: 87 10 | fa.js Found: 24 Missing: 94 11 | fi.js Found: 23 Missing: 95 12 | fr.js Found: 118 Missing: 0 13 | hr.js Found: 23 Missing: 95 14 | it.js Found: 118 Missing: 0 15 | nb.js Found: 118 Missing: 0 16 | nl.js Found: 118 Missing: 0 17 | no.js Found: 118 Missing: 0 18 | tr.js Found: 118 Missing: 0 19 | ug.js Found: 39 Missing: 79 20 | zh-cn.js Found: 118 Missing: 0 21 | -------------------------------------------------------------------------------- /lib/api/schemas/notification.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "http://www.gandhi.io/schema/notification", 3 | "type":"object", 4 | "$schema": "http://json-schema.org/draft-04/schema", 5 | "required": [ 6 | "id", 7 | "user_id", 8 | "subject", 9 | "content" 10 | ], 11 | "additionalProperties": false, 12 | "properties":{ 13 | "id": { 14 | "type": "string" 15 | }, 16 | "user_id": { 17 | "type": "string" 18 | }, 19 | "subject": { 20 | "type": "string" 21 | }, 22 | "content": { 23 | "type": "string" 24 | }, 25 | "status_id": { 26 | "type": "string", 27 | "enum": [ 28 | "unread", 29 | "read", 30 | "archived" 31 | ], 32 | "default": "unread" 33 | }, 34 | "created": { 35 | "type": "number" 36 | }, 37 | "updated": { 38 | "type": "number" 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/samples/sample_posteddata.php: -------------------------------------------------------------------------------- 1 |
 2 | 
 3 | -------------------------------------------------------------------------------------------
 4 |   CKEditor - Posted Data
 5 | 
 6 |   We are sorry, but your Web server does not support the PHP language used in this script.
 7 | 
 8 |   Please note that CKEditor can be used with any other server-side language than just PHP.
 9 |   To save the content created with CKEditor you need to read the POST data on the server
10 |   side and write it to a file or the database.
11 | 
12 |   Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
13 |   For licensing, see LICENSE.md or http://ckeditor.com/license
14 | -------------------------------------------------------------------------------------------
15 | 
16 | 
*/ include "assets/posteddata.php"; ?> 17 | -------------------------------------------------------------------------------- /lib/api/schemas/user.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "http://www.gandhi.io/schema/user", 3 | "type":"object", 4 | "$schema": "http://json-schema.org/draft-04/schema", 5 | "required": [ 6 | "id", 7 | "email" 8 | ], 9 | "additionalProperties": false, 10 | "properties":{ 11 | "admin": { 12 | "type": "boolean", 13 | "default": false 14 | }, 15 | "email": { 16 | "type": "string" 17 | }, 18 | "id": { 19 | "type": "string" 20 | }, 21 | "name": { 22 | "type": "string", 23 | "default": "" 24 | }, 25 | "password": { 26 | "type": "string", 27 | "minLength": 4 28 | }, 29 | "recovery_token": { 30 | "type": ["null","string"], 31 | "default": null 32 | }, 33 | "created": { 34 | "type": "number" 35 | }, 36 | "updated": { 37 | "type": "number" 38 | }, 39 | "preferences": { 40 | "type": "object", 41 | "default": {} 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/app/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependency directory 23 | # Deployed apps should consider commenting this line out: 24 | # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git 25 | node_modules 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Icon must end with two \r 32 | Icon 33 | 34 | 35 | # Thumbnails 36 | ._* 37 | 38 | # Files that might appear on external disk 39 | .Spotlight-V100 40 | .Trashes 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/plugins/a11yhelp/dialogs/lang/_translationstatus.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved. 2 | For licensing, see LICENSE.md or http://ckeditor.com/license 3 | 4 | cs.js Found: 30 Missing: 0 5 | cy.js Found: 30 Missing: 0 6 | da.js Found: 12 Missing: 18 7 | de.js Found: 30 Missing: 0 8 | el.js Found: 25 Missing: 5 9 | eo.js Found: 30 Missing: 0 10 | fa.js Found: 30 Missing: 0 11 | fi.js Found: 30 Missing: 0 12 | fr.js Found: 30 Missing: 0 13 | gu.js Found: 12 Missing: 18 14 | he.js Found: 30 Missing: 0 15 | it.js Found: 30 Missing: 0 16 | mk.js Found: 5 Missing: 25 17 | nb.js Found: 30 Missing: 0 18 | nl.js Found: 30 Missing: 0 19 | no.js Found: 30 Missing: 0 20 | pt-br.js Found: 30 Missing: 0 21 | ro.js Found: 6 Missing: 24 22 | tr.js Found: 30 Missing: 0 23 | ug.js Found: 27 Missing: 3 24 | vi.js Found: 6 Missing: 24 25 | zh-cn.js Found: 30 Missing: 0 26 | -------------------------------------------------------------------------------- /lib/modules/gandhi-decorator-upload/index.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 |
14 | 15 | 18 |
21 |
22 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fs = require('fs') 4 | const cluster = require('cluster') 5 | 6 | // the config 7 | const config = process.argv.length > 2 ? 8 | 9 | // config from argument 10 | require(['/', '.'].indexOf(process.argv[2][0]) === -1 11 | ? './' + process.argv[2] 12 | : process.argv[2]) 13 | : fs.existsSync(__dirname + '/config.json') || 14 | fs.existsSync(__dirname + '/config.js') || 15 | fs.existsSync(__dirname + '/config/index.js') ? 16 | 17 | // config in root directory 18 | require('./config') 19 | 20 | // default config 21 | : require('./config.default.js') 22 | 23 | const gandhi = require('./lib/index.js')(config) 24 | 25 | require('./setup/index.js')(config) 26 | 27 | const app = require('express')() 28 | 29 | // bring in gandhi 30 | app.use(config.root, gandhi) 31 | 32 | // this needs to be here because Angular is stupid, and fails to use any sensible query string 33 | // format... we need to find a better way to fix this, probably on the client side. 34 | app.set('query parser', 'simple') 35 | 36 | app.listen(config.port) 37 | 38 | -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/plugins/wsc/README.md: -------------------------------------------------------------------------------- 1 | CKEditor WebSpellChecker Plugin 2 | =============================== 3 | 4 | This plugin brings Web Spell Checker (WSC) into CKEditor. 5 | 6 | WSC is "installation-less", using the web-services of [WebSpellChecker.net](http://www.webspellchecker.net/). It's an out of the box solution. 7 | 8 | Installation 9 | ------------ 10 | 11 | 1. Clone/copy this repository contents in a new "plugins/wsc" folder in your CKEditor installation. 12 | 2. Enable the "wsc" plugin in the CKEditor configuration file (config.js): 13 | 14 | config.extraPlugins = 'wsc'; 15 | 16 | That's all. WSC will appear on the editor toolbar and will be ready to use. 17 | 18 | License 19 | ------- 20 | 21 | Licensed under the terms of any of the following licenses at your choice: [GPL](http://www.gnu.org/licenses/gpl.html), [LGPL](http://www.gnu.org/licenses/lgpl.html) and [MPL](http://www.mozilla.org/MPL/MPL-1.1.html). 22 | 23 | See LICENSE.md for more information. 24 | 25 | Developed in cooperation with [WebSpellChecker.net](http://www.webspellchecker.net/). 26 | -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/plugins/scayt/README.md: -------------------------------------------------------------------------------- 1 | CKEditor SCAYT Plugin 2 | ===================== 3 | 4 | This plugin brings Spell Check As You Type (SCAYT) into up to CKEditor 4+. 5 | 6 | SCAYT is a "installation-less", using the web-services of [WebSpellChecker.net](http://www.webspellchecker.net/). It's an out of the box solution. 7 | 8 | Installation 9 | ------------ 10 | 11 | 1. Clone/copy this repository contents in a new "plugins/scayt" folder in your CKEditor installation. 12 | 2. Enable the "scayt" plugin in the CKEditor configuration file (config.js): 13 | 14 | config.extraPlugins = 'scayt'; 15 | 16 | That's all. SCAYT will appear on the editor toolbar and will be ready to use. 17 | 18 | License 19 | ------- 20 | 21 | Licensed under the terms of any of the following licenses at your choice: [GPL](http://www.gnu.org/licenses/gpl.html), [LGPL](http://www.gnu.org/licenses/lgpl.html) and [MPL](http://www.mozilla.org/MPL/MPL-1.1.html). 22 | 23 | See LICENSE.md for more information. 24 | 25 | Developed in cooperation with [WebSpellChecker.net](http://www.webspellchecker.net/). 26 | -------------------------------------------------------------------------------- /lib/api/collections/Projects/Contents.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require('lodash'); 4 | var Promise = require('bluebird'); 5 | var util = require('util'); 6 | var errors = require('../../errors'); 7 | 8 | var EmbeddedCollection = require('../../EmbeddedCollection'); 9 | var Content = require('../../models/Project/Content'); 10 | 11 | function Contents (data, parent) { 12 | this.model = Content; 13 | EmbeddedCollection.call(this, data, parent); 14 | } 15 | 16 | util.inherits(Contents, EmbeddedCollection); 17 | 18 | Contents.prototype.query = function(conn, query) { 19 | var self = this; 20 | 21 | if(!self.parent.authorizations['project/contents:read']) 22 | return Promise.reject(new errors.ForbiddenError()); 23 | 24 | return Promise.all([ 25 | self.parent.cycle.stages.query(conn, {}), 26 | EmbeddedCollection.prototype.query.call(self, conn, query) 27 | ]) 28 | .spread(function(stages, contents) { 29 | stages = _.keyBy(stages, 'id'); 30 | return _.sortBy(contents, function(content) { 31 | return stages[content.id] ? stages[content.id].order : 0; 32 | }); 33 | }); 34 | }; 35 | 36 | module.exports = Contents; -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/samples/plugins/dialog/assets/my_dialog.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license 4 | */ 5 | 6 | CKEDITOR.dialog.add( 'myDialog', function( editor ) { 7 | return { 8 | title: 'My Dialog', 9 | minWidth: 400, 10 | minHeight: 200, 11 | contents: [ 12 | { 13 | id: 'tab1', 14 | label: 'First Tab', 15 | title: 'First Tab', 16 | elements: [ 17 | { 18 | id: 'input1', 19 | type: 'text', 20 | label: 'Text Field' 21 | }, 22 | { 23 | id: 'select1', 24 | type: 'select', 25 | label: 'Select Field', 26 | items: [ 27 | [ 'option1', 'value1' ], 28 | [ 'option2', 'value2' ] 29 | ] 30 | } 31 | ] 32 | }, 33 | { 34 | id: 'tab2', 35 | label: 'Second Tab', 36 | title: 'Second Tab', 37 | elements: [ 38 | { 39 | id: 'button1', 40 | type: 'button', 41 | label: 'Button Field' 42 | } 43 | ] 44 | } 45 | ] 46 | }; 47 | } ); 48 | 49 | -------------------------------------------------------------------------------- /lib/modules/gandhi-component/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('gandhi-component', []) 4 | 5 | .provider('GandhiComponent', function() { 6 | var self = this; 7 | 8 | self.components = {}; 9 | 10 | self.register = function(c){ 11 | self.components[c.id] = c; 12 | } 13 | 14 | self.$get = function() { 15 | return self.components; 16 | }; 17 | }) 18 | 19 | .directive('gandhiComponent', function(GandhiComponent, $compile){ 20 | return { 21 | restrict: 'A', 22 | scope: { 23 | context: '=gandhiComponent' 24 | }, 25 | link: function(scope, element, attrs){ 26 | scope.$watchCollection('context', function(context) { 27 | if(!context.cycle || !context.stage) 28 | return; 29 | 30 | // make sure this component exists... we might actually want to error here... 31 | if(!GandhiComponent[context.stage.component.name]) 32 | return; 33 | 34 | // inject the HTML 35 | element.html('
'); 36 | 37 | // compile it 38 | $compile(element.contents())(scope); 39 | }) 40 | } 41 | } 42 | }) 43 | -------------------------------------------------------------------------------- /migrations/v0.3.0.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var r = require('rethinkdb'); 4 | var q = require('q'); 5 | 6 | var host = '127.0.0.1'; 7 | var db = 'gandhi'; 8 | 9 | // grab command line agrs 10 | for (var i = 2; i < process.argv.length; i++) { 11 | if(process.argv[i] === '--host'){ 12 | i++; host = process.argv[i]; 13 | } 14 | 15 | else if(process.argv[i] === '--db'){ 16 | i++; db = process.argv[i]; 17 | } 18 | } 19 | 20 | 21 | r.connect({host: host, db: db}).then(function(conn){ 22 | 23 | var tasks = []; 24 | 25 | // Cycles 26 | // ------ 27 | tasks.push(r.table('cycles').replace(function(cycle){ 28 | return cycle.merge({ 29 | triggers: cycle('triggers').coerceTo('array').map(function(triggerKV){ 30 | return [triggerKV.nth(0), triggerKV.nth(1).merge({ 31 | description: triggerKV.nth(1)('description').default(''), 32 | }).without('single')]; 33 | }).coerceTo('object') 34 | }); 35 | }).run(conn)); 36 | 37 | // run all the tasks 38 | q.all(tasks).then(function(res){ 39 | console.log('success:', JSON.stringify(res, null, 2)); 40 | conn.close(); 41 | }, function(err){ 42 | console.error(err); 43 | conn.close(); 44 | }); 45 | }); -------------------------------------------------------------------------------- /migrations/v0.2.0.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var r = require('rethinkdb'); 4 | var q = require('q'); 5 | 6 | var host = '127.0.0.1'; 7 | var db = 'gandhi'; 8 | 9 | // grab command line agrs 10 | for (var i = 2; i < process.argv.length; i++) { 11 | if(process.argv[i] === '--host'){ 12 | i++; host = process.argv[i]; 13 | } 14 | 15 | else if(process.argv[i] === '--db'){ 16 | i++; db = process.argv[i]; 17 | } 18 | } 19 | 20 | 21 | r.connect({host: host, db: db}).then(function(conn){ 22 | 23 | var tasks = []; 24 | 25 | // Cycles 26 | // ------ 27 | tasks.push(r.table('cycles').replace(function(cycle){ 28 | return cycle.merge({ 29 | triggers: cycle('triggers').coerceTo('array').map(function(triggerKV){ 30 | return [triggerKV.nth(0), triggerKV.nth(1).merge({ 31 | actions: triggerKV.nth(1)('actions').default(triggerKV.nth(1)('listeners')), 32 | }).without('listeners')]; 33 | }).coerceTo('object'), 34 | }); 35 | }).run(conn)); 36 | 37 | // run all the tasks 38 | q.all(tasks).then(function(res){ 39 | console.log('success:', JSON.stringify(res, null, 2)); 40 | conn.close(); 41 | }, function(err){ 42 | console.error(err); 43 | conn.close(); 44 | }); 45 | }); -------------------------------------------------------------------------------- /lib/api/schemas/file.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "http://www.gandhi.io/schema/file", 3 | "type":"object", 4 | "$schema": "http://json-schema.org/draft-04/schema", 5 | "required": [ 6 | "id", 7 | "name", 8 | "mimetype", 9 | "encoding", 10 | "path", 11 | "extension", 12 | "size" 13 | ], 14 | "additionalProperties": false, 15 | "properties":{ 16 | "user_id": { 17 | "type": "string" 18 | }, 19 | "id": { 20 | "type": "string" 21 | }, 22 | "name": { 23 | "type": "string", 24 | "default": "" 25 | }, 26 | "mimetype": { 27 | "type": "string", 28 | "default": "" 29 | }, 30 | "encoding": { 31 | "type": "string", 32 | "default": "" 33 | }, 34 | "path": { 35 | "type": "string", 36 | "default": "" 37 | }, 38 | "extension": { 39 | "type": "string", 40 | "default": "" 41 | }, 42 | "size": { 43 | "type": "number" 44 | }, 45 | "created": { 46 | "type": "number" 47 | }, 48 | "updated": { 49 | "type": "number" 50 | }, 51 | "lock": { 52 | "type": "boolean", 53 | "default": false 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/modules/gandhi-component-message/app/stageAdmin.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 |
6 | 7 | 10 |
11 | 12 | 13 |
14 |
You haven't specified any message for this stage.
15 |
16 |
17 |
18 | 19 |
20 | 21 |
-------------------------------------------------------------------------------- /lib/api/middleware/auth.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var jwt = require('jsonwebtoken'); 4 | var passport = require('passport'); 5 | var BearerStrategy = require('passport-http-bearer'); 6 | 7 | var errors = require('../errors'); 8 | var Users = require('../collections/Users'); 9 | var users = new Users(); 10 | 11 | module.exports = function(config, resources){ 12 | 13 | // configure passport 14 | passport.use(new BearerStrategy(function(token, done) { 15 | jwt.verify(token, config.secret, function(err, decoded) { 16 | if(err) return done(err.name === 'TokenExpiredError' ? 17 | new errors.UnauthorizedError('Authorization token has expired.') 18 | : new errors.UnauthorizedError()); 19 | 20 | if(!decoded.sub) 21 | return done(new errors.UnauthorizedError('No subject encoded in token.')); 22 | 23 | resources.db.acquire(function(err, conn) { 24 | if(err) return done(err); 25 | 26 | return users.get(conn, decoded.sub) 27 | .finally(function(){ 28 | resources.db.release(conn); 29 | }) 30 | .then(function(user){ 31 | return done(null, user); 32 | }) 33 | .catch(function(err){ 34 | return done(err); 35 | }); 36 | 37 | }); 38 | }); 39 | })); 40 | 41 | // return the initialized middleware 42 | return passport.initialize(); 43 | }; 44 | -------------------------------------------------------------------------------- /lib/modules/gandhi-decorator-users/index.js: -------------------------------------------------------------------------------- 1 | angular.module('gandhi-decorator-users', []) 2 | 3 | // .config(['schemaFormDecoratorsProvider', function(decoratorsProvider){ 4 | // decoratorsProvider.addMapping('bootstrapDecorator', 'users', 'modules/gandhi-decorator-users/index.html'); 5 | // }]) 6 | 7 | .directive('gandhiDecoratorUsers', function(){ 8 | return { 9 | template: '' 10 | +'' 11 | +'{{$select.selected.name}}' 12 | +'' 13 | +'
' 14 | +'' 15 | +'
' 16 | +'
' 17 | , 18 | restrict: 'A', 19 | require: 'ngModel', 20 | scope: { 21 | model: '=ngModel', 22 | ngDisabled: '=ngDisabled' 23 | }, 24 | controller: ['$scope', '$attrs', 'User', function($scope, $attrs, User){ 25 | $scope.users = []; 26 | $scope.disabled = $attrs.disabled !== undefined || $scope.ngDisabled; 27 | 28 | $scope.search = function(search){ 29 | 30 | } 31 | }] 32 | } 33 | }); -------------------------------------------------------------------------------- /lib/api/endpoints/notifications.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var passport = require('passport'); 4 | 5 | module.exports = function(config, router, resources){ 6 | 7 | var controller = require('../controllers/notifications')(config, resources); 8 | 9 | // create 10 | // ------ 11 | router.post( 12 | '/api/notifications', 13 | passport.authenticate('bearer', { session: false }), 14 | controller.create 15 | ); 16 | 17 | // query 18 | // ----- 19 | router.get( 20 | '/api/notifications', 21 | passport.authenticate('bearer', { session: false }), 22 | controller.query 23 | ); 24 | 25 | // get 26 | // --- 27 | router.get( 28 | '/api/notifications/:notification', 29 | passport.authenticate('bearer', { session: false }), 30 | controller.get 31 | ); 32 | 33 | // update 34 | // ------ 35 | router.patch( 36 | '/api/notifications/:notification', 37 | passport.authenticate('bearer', { session: false }), 38 | controller.update 39 | ); 40 | 41 | // save 42 | // ---- 43 | router.put( 44 | '/api/notifications/:notification', 45 | passport.authenticate('bearer', { session: false }), 46 | controller.save 47 | ); 48 | 49 | // delete 50 | // ------ 51 | router.delete( 52 | '/api/notifications/:notification', 53 | passport.authenticate('bearer', { session: false }), 54 | controller.delete 55 | ); 56 | }; 57 | -------------------------------------------------------------------------------- /lib/app/portal/user/edit.html: -------------------------------------------------------------------------------- 1 |

{{currentUser.name}}

2 |

 

3 |
4 |
5 | Edit 6 |
7 |
8 |
9 |
10 | 11 | 12 |
13 |
14 | 15 | 16 |
17 |
18 | 19 | 20 |
21 |
22 | 23 | 24 |
25 | 26 | 27 |
28 | 29 |
30 |
31 | 32 | -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/samples/assets/uilanguages/languages.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved. 3 | For licensing, see LICENSE.md or http://ckeditor.com/license 4 | */ 5 | var CKEDITOR_LANGS=function(){var c={af:"Afrikaans",ar:"Arabic",bg:"Bulgarian",bn:"Bengali/Bangla",bs:"Bosnian",ca:"Catalan",cs:"Czech",cy:"Welsh",da:"Danish",de:"German",el:"Greek",en:"English","en-au":"English (Australia)","en-ca":"English (Canadian)","en-gb":"English (United Kingdom)",eo:"Esperanto",es:"Spanish",et:"Estonian",eu:"Basque",fa:"Persian",fi:"Finnish",fo:"Faroese",fr:"French","fr-ca":"French (Canada)",gl:"Galician",gu:"Gujarati",he:"Hebrew",hi:"Hindi",hr:"Croatian",hu:"Hungarian",id:"Indonesian", 6 | is:"Icelandic",it:"Italian",ja:"Japanese",ka:"Georgian",km:"Khmer",ko:"Korean",ku:"Kurdish",lt:"Lithuanian",lv:"Latvian",mk:"Macedonian",mn:"Mongolian",ms:"Malay",nb:"Norwegian Bokmal",nl:"Dutch",no:"Norwegian",pl:"Polish",pt:"Portuguese (Portugal)","pt-br":"Portuguese (Brazil)",ro:"Romanian",ru:"Russian",si:"Sinhala",sk:"Slovak",sq:"Albanian",sl:"Slovenian",sr:"Serbian (Cyrillic)","sr-latn":"Serbian (Latin)",sv:"Swedish",th:"Thai",tr:"Turkish",tt:"Tatar",ug:"Uighur",uk:"Ukrainian",vi:"Vietnamese", 7 | zh:"Chinese Traditional","zh-cn":"Chinese Simplified"},b=[],a;for(a in CKEDITOR.lang.languages)b.push({code:a,name:c[a]||a});b.sort(function(a,b){return a.name
' 10 | }) 11 | 12 | .state('portal.user.show', { 13 | url: "/show", 14 | templateUrl: "portal/user/show.html", 15 | controller: ['$scope', 'User', function($scope, User){ 16 | $scope.edit = false; 17 | $scope.toggleEdit = function(){ 18 | $scope.edit = !$scope.edit; 19 | } 20 | 21 | // the model to edit 22 | $scope.$watch('currentUser', function(currentUser){ 23 | if(!currentUser) return; 24 | $scope.user = currentUser; 25 | $scope.userEdit = new User(currentUser); 26 | }); 27 | 28 | // update the user 29 | $scope.update = function(){ 30 | 31 | // don't save a blank password 32 | if($scope.userEdit.password == '') 33 | delete $scope.userEdit.password; 34 | 35 | $scope.userEdit.$update({id: $scope.currentUser.id}).then(function(user){ 36 | 37 | // update the local user 38 | angular.extend($scope.currentUser, user); 39 | 40 | // redirect 41 | $scope.edit = false; 42 | 43 | }, function(err){ 44 | if(err.data && err.data.message) 45 | alert(err.data.message); 46 | else 47 | alert("There was a problem saving your changes."); 48 | }) 49 | } 50 | }] 51 | }) 52 | 53 | }]); 54 | -------------------------------------------------------------------------------- /lib/app/assets/style/screen.css.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "mappings": "AACA,yBAA0B;EACzB,oBAAqB;IACpB,MAAM,EAAE,IAAI;IACZ,QAAQ,EAAE,MAAM;IAChB,KAAK,EAAE,IAAI;IAEX,yBAAK;MACJ,MAAM,EAAE,IAAI;MACZ,UAAU,EAAE,MAAM;IAGnB,6BAAS;MACR,UAAU,EAAE,cAAyB;MACrC,UAAU,EAAE,IAAI;MAChB,UAAU,EAAE,IAAI;MAEhB,mCAAM;QACL,UAAU,EAAE,gBAAgB;QAC5B,UAAU,EAAE,OAAO;IAIrB,iCAAa;MACZ,OAAO,EAAE,CAAC;MACV,gBAAgB,EAAE,EAAE;MACpB,UAAU,EAAE,OAAO;AAOtB,yBAA0B;EAEzB,KAAM;IACL,UAAU,EAAE,CAAC;;EAGd,QAAS;IACR,MAAM,EAAE,CAAC;IACT,UAAU,EAAE,8BAA8B;IAC1C,UAAU,EAAE,IAAI;IAChB,KAAK,EAAE,KAAK;IAEZ,mBAAW;MACV,UAAU,EAAE,OAAkB;MAC9B,aAAa,EAAE,WAAW;MAC1B,KAAK,EAAE,OAAkB;MACzB,MAAM,EAAE,OAAO;MACf,OAAO,EAAE,KAAK;MACd,WAAW,EAAE,sBAAsB;MACnC,SAAS,EAAE,GAAG;MACd,WAAW,EAAE,IAAI;MACjB,UAAU,EAAE,KAAK;MACjB,OAAO,EAAE,EAAE;MACX,YAAY,EAAE,GAAG;MACjB,QAAQ,EAAE,QAAQ;MAClB,KAAK,EAAE,CAAC;MACR,GAAG,EAAE,GAAG;MACR,UAAU,EAAE,WAAW;MACvB,cAAc,EAAE,MAAM;MAEtB,yBAAQ;QACP,OAAO,EAAE,CAAC;IAIZ,YAAI;MACH,UAAU,EAAE,gBAAgB;MAC5B,UAAU,EAAE,OAAO;MAEnB,QAAQ,EAAE,CAAC;MACX,SAAS,EAAE,CAAC;MACZ,YAAY,EAAE,CAAC;MACf,IAAI,EAAE,CAAC;;EAIT,KAAM;IACL,WAAW,EAAE,KAAK;;EAKlB,0BAAS;IACR,IAAI,EAAE,MAAM;IAEZ,qCAAW;MACV,OAAO,EAAE,CAAC;IAGX,6CAAmB;MAClB,UAAU,EAAE,QAAQ;MACpB,UAAU,EAAE,KAAK;MACjB,UAAU,EAAE,IAAI;IAIhB,yDAAmB;MAClB,YAAY,EAAE,CAAC;MACf,MAAM,EAAE,CAAC;MACT,UAAU,EAAE,CAAC;MACb,UAAU,EAAE,MAAM;MAClB,OAAO,EAAE,CAAC;IAIZ,gCAAQ;MACP,IAAI,EAAE,GAAG;MAET,2CAAW;QACV,OAAO,EAAE,CAAC;EAKb,uBAAM;IACL,WAAW,EAAE,IAAI", 4 | "sources": ["screen.scss"], 5 | "names": [], 6 | "file": "screen.css" 7 | } -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/README.md: -------------------------------------------------------------------------------- 1 | CKEditor 4 2 | ========== 3 | 4 | Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved. 5 | http://ckeditor.com - See LICENSE.md for license information. 6 | 7 | CKEditor is a text editor to be used inside web pages. It's not a replacement 8 | for desktop text editors like Word or OpenOffice, but a component to be used as 9 | part of web applications and websites. 10 | 11 | ## Documentation 12 | 13 | The full editor documentation is available online at the following address: 14 | http://docs.ckeditor.com 15 | 16 | ## Installation 17 | 18 | Installing CKEditor is an easy task. Just follow these simple steps: 19 | 20 | 1. **Download** the latest version from the CKEditor website: 21 | http://ckeditor.com. You should have already completed this step, but be 22 | sure you have the very latest version. 23 | 2. **Extract** (decompress) the downloaded file into the root of your website. 24 | 25 | **Note:** CKEditor is by default installed in the `ckeditor` folder. You can 26 | place the files in whichever you want though. 27 | 28 | ## Checking Your Installation 29 | 30 | The editor comes with a few sample pages that can be used to verify that 31 | installation proceeded properly. Take a look at the `samples` directory. 32 | 33 | To test your installation, just call the following page at your website: 34 | 35 | http:////samples/index.html 36 | 37 | For example: 38 | 39 | http://www.example.com/ckeditor/samples/index.html 40 | -------------------------------------------------------------------------------- /lib/api/EmbeddedCollection.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require('lodash'); 4 | var Promise = require('bluebird'); 5 | var errors = require('./errors'); 6 | 7 | function EmbeddedCollection (data, parent) { 8 | if(typeof this.model !== 'function') 9 | throw new Error('An EmbeddedCollection constructor must have a `model` function configured.'); 10 | 11 | this.data = data; 12 | this.parent = parent; 13 | } 14 | 15 | EmbeddedCollection.prototype.query = function(conn, query) { 16 | var self = this; 17 | return Promise.filter( 18 | _.map(self.data, function(o) { 19 | return new self.model(conn, o, self.parent).catch(function(err){ 20 | 21 | // suppress ForbiddenError 22 | if(err instanceof errors.ForbiddenError) 23 | return null; 24 | 25 | // re-throw all other errors 26 | return Promise.reject(err); 27 | }); 28 | }), 29 | 30 | function(o) { 31 | 32 | // TODO: apply filters 33 | 34 | return o; 35 | } 36 | ) 37 | .then(function(results) { 38 | return _.sortBy(results, query.sort || 'title'); 39 | }); 40 | }; 41 | 42 | EmbeddedCollection.prototype.get = function(conn, id) { 43 | var self = this; 44 | 45 | if(!self.data[id]) 46 | return Promise.reject(new errors.NotFoundError(self.model.name + ' not found.')); 47 | 48 | return Promise.resolve(new self.model(conn, self.data[id], self.parent)); 49 | }; 50 | 51 | EmbeddedCollection.prototype.create = function(conn, data) { 52 | var self = this; 53 | 54 | return self.model.create(conn, data, self.parent); 55 | }; 56 | 57 | module.exports = EmbeddedCollection; 58 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ##################### 2 | # Gandhi Stuff 3 | ##################### 4 | 5 | /config.json 6 | /config.js 7 | /lib/app/assets/bower 8 | /lib/app/assets/style/.sass-cache/* 9 | /rethinkdb_data 10 | /dump.rdb 11 | /uploads 12 | /files 13 | /secret.txt 14 | 15 | /_builds 16 | /_projects 17 | /_steps 18 | 19 | 20 | 21 | 22 | ##################### 23 | # Node Stuff 24 | ##################### 25 | 26 | # Logs 27 | logs 28 | *.log 29 | 30 | # Runtime data 31 | pids 32 | *.pid 33 | *.seed 34 | 35 | # Directory for instrumented libs generated by jscoverage/JSCover 36 | lib-cov 37 | 38 | # Coverage directory used by tools like istanbul 39 | coverage 40 | 41 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 42 | .grunt 43 | 44 | # Compiled binary addons (http://nodejs.org/api/addons.html) 45 | build/Release 46 | 47 | # Dependency directory 48 | # Deployed apps should consider commenting this line out: 49 | # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git 50 | node_modules 51 | 52 | 53 | ##################### 54 | # OSX Stuff 55 | ##################### 56 | 57 | .DS_Store 58 | .AppleDouble 59 | .LSOverride 60 | 61 | # Icon must ends with two \r. 62 | Icon 63 | 64 | 65 | # Thumbnails 66 | ._* 67 | 68 | # Files that might appear on external disk 69 | .Spotlight-V100 70 | .Trashes 71 | 72 | # Directories potentially created on remote AFP share 73 | .AppleDB 74 | .AppleDesktop 75 | Network Trash Folder 76 | Temporary Items 77 | 78 | ##################### 79 | # IntelliJ Stuff 80 | ##################### 81 | .idea 82 | -------------------------------------------------------------------------------- /lib/api/endpoints/files.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var multer = require('multer'); 4 | var passport = require('passport'); 5 | 6 | module.exports = function (config, router, resources) { 7 | 8 | let controller = require('../controllers/files')(config, resources) 9 | 10 | // create 11 | // ------ 12 | router.post( 13 | '/api/files', 14 | passport.authenticate('bearer', { session: false }), 15 | multer({ dest: config.files.directory }), 16 | controller.create, 17 | ) 18 | 19 | // query 20 | // ----- 21 | router.get( 22 | '/api/files', 23 | passport.authenticate('bearer', { session: false }), 24 | controller.query, 25 | ) 26 | 27 | // query (by user) 28 | // --------------- 29 | router.get( 30 | '/api/users/:user/files', 31 | passport.authenticate('bearer', { session: false }), 32 | controller.query, 33 | ) 34 | 35 | // get 36 | // --- 37 | router.get( 38 | '/api/files/:file', 39 | // passport.authenticate('bearer', { session: false }), 40 | controller.get, 41 | ) 42 | 43 | // update 44 | // ------ 45 | router.patch( 46 | '/api/files/:file', 47 | passport.authenticate('bearer', { session: false }), 48 | controller.update, 49 | ) 50 | 51 | // save 52 | // ---- 53 | router.put( 54 | '/api/files/:file', 55 | passport.authenticate('bearer', { session: false }), 56 | controller.save, 57 | ) 58 | 59 | // delete 60 | // ------ 61 | router.delete( 62 | '/api/files/:file', 63 | passport.authenticate('bearer', { session: false }), 64 | controller.delete, 65 | ) 66 | } 67 | -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/plugins/about/dialogs/about.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved. 3 | For licensing, see LICENSE.md or http://ckeditor.com/license 4 | */ 5 | CKEDITOR.dialog.add("about",function(a){var a=a.lang.about,b=CKEDITOR.plugins.get("about").path+"dialogs/"+(CKEDITOR.env.hidpi?"hidpi/":"")+"logo_ckeditor.png";return{title:CKEDITOR.env.ie?a.dlgTitle:a.title,minWidth:390,minHeight:230,contents:[{id:"tab1",label:"",title:"",expand:!0,padding:0,elements:[{type:"html",html:'

CKEditor '+CKEDITOR.version+" (revision "+CKEDITOR.revision+')
http://ckeditor.com

'+a.help.replace("$1",''+ 7 | a.userGuide+"")+"

"+a.moreInfo+'
http://ckeditor.com/about/license

'+a.copy.replace("$1",'CKSource - Frederico Knabben')+"

"}]}],buttons:[CKEDITOR.dialog.cancelButton]}}); -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/plugins/wsc/LICENSE.md: -------------------------------------------------------------------------------- 1 | Software License Agreement 2 | ========================== 3 | 4 | **CKEditor WSC Plugin** 5 | Copyright © 2012, [CKSource](http://cksource.com) - Frederico Knabben. All rights reserved. 6 | 7 | Licensed under the terms of any of the following licenses at your choice: 8 | 9 | * GNU General Public License Version 2 or later (the "GPL"): 10 | http://www.gnu.org/licenses/gpl.html 11 | 12 | * GNU Lesser General Public License Version 2.1 or later (the "LGPL"): 13 | http://www.gnu.org/licenses/lgpl.html 14 | 15 | * Mozilla Public License Version 1.1 or later (the "MPL"): 16 | http://www.mozilla.org/MPL/MPL-1.1.html 17 | 18 | You are not required to, but if you want to explicitly declare the license you have chosen to be bound to when using, reproducing, modifying and distributing this software, just include a text file titled "legal.txt" in your version of this software, indicating your license choice. 19 | 20 | Sources of Intellectual Property Included in this plugin 21 | -------------------------------------------------------- 22 | 23 | Where not otherwise indicated, all plugin content is authored by CKSource engineers and consists of CKSource-owned intellectual property. In some specific instances, the plugin will incorporate work done by developers outside of CKSource with their express permission. 24 | 25 | Trademarks 26 | ---------- 27 | 28 | CKEditor is a trademark of CKSource - Frederico Knabben. All other brand and product names are trademarks, registered trademarks or service marks of their respective holders. 29 | -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/plugins/scayt/LICENSE.md: -------------------------------------------------------------------------------- 1 | Software License Agreement 2 | ========================== 3 | 4 | **CKEditor SCAYT Plugin** 5 | Copyright © 2012, [CKSource](http://cksource.com) - Frederico Knabben. All rights reserved. 6 | 7 | Licensed under the terms of any of the following licenses at your choice: 8 | 9 | * GNU General Public License Version 2 or later (the "GPL"): 10 | http://www.gnu.org/licenses/gpl.html 11 | 12 | * GNU Lesser General Public License Version 2.1 or later (the "LGPL"): 13 | http://www.gnu.org/licenses/lgpl.html 14 | 15 | * Mozilla Public License Version 1.1 or later (the "MPL"): 16 | http://www.mozilla.org/MPL/MPL-1.1.html 17 | 18 | You are not required to, but if you want to explicitly declare the license you have chosen to be bound to when using, reproducing, modifying and distributing this software, just include a text file titled "legal.txt" in your version of this software, indicating your license choice. 19 | 20 | Sources of Intellectual Property Included in this plugin 21 | -------------------------------------------------------- 22 | 23 | Where not otherwise indicated, all plugin content is authored by CKSource engineers and consists of CKSource-owned intellectual property. In some specific instances, the plugin will incorporate work done by developers outside of CKSource with their express permission. 24 | 25 | Trademarks 26 | ---------- 27 | 28 | CKEditor is a trademark of CKSource - Frederico Knabben. All other brand and product names are trademarks, registered trademarks or service marks of their respective holders. 29 | -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/plugins/link/dialogs/anchor.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved. 3 | For licensing, see LICENSE.md or http://ckeditor.com/license 4 | */ 5 | CKEDITOR.dialog.add("anchor",function(c){function d(a,b){return a.createFakeElement(a.document.createElement("a",{attributes:b}),"cke_anchor","anchor")}return{title:c.lang.link.anchor.title,minWidth:300,minHeight:60,onOk:function(){var a=CKEDITOR.tools.trim(this.getValueOf("info","txtName")),a={id:a,name:a,"data-cke-saved-name":a};if(this._.selectedElement)this._.selectedElement.data("cke-realelement")?(a=d(c,a),a.replace(this._.selectedElement),CKEDITOR.env.ie&&c.getSelection().selectElement(a)): 6 | this._.selectedElement.setAttributes(a);else{var b=c.getSelection(),b=b&&b.getRanges()[0];b.collapsed?(a=d(c,a),b.insertNode(a)):(CKEDITOR.env.ie&&9>CKEDITOR.env.version&&(a["class"]="cke_anchor"),a=new CKEDITOR.style({element:"a",attributes:a}),a.type=CKEDITOR.STYLE_INLINE,c.applyStyle(a))}},onHide:function(){delete this._.selectedElement},onShow:function(){var a=c.getSelection(),b=a.getSelectedElement(),d=b&&b.data("cke-realelement"),e=d?CKEDITOR.plugins.link.tryRestoreFakeAnchor(c,b):CKEDITOR.plugins.link.getSelectedLink(c); 7 | e&&(this._.selectedElement=e,this.setValueOf("info","txtName",e.data("cke-saved-name")||""),!d&&a.selectElement(e),b&&(this._.selectedElement=b));this.getContentElement("info","txtName").focus()},contents:[{id:"info",label:c.lang.link.anchor.title,accessKey:"I",elements:[{type:"text",id:"txtName",label:c.lang.link.anchor.name,required:!0,validate:function(){return!this.getValue()?(alert(c.lang.link.anchor.errorName),!1):!0}}]}]}}); -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/plugins/wsc/dialogs/wsc.css: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved. 3 | For licensing, see LICENSE.html or http://ckeditor.com/license 4 | */ 5 | 6 | html, body 7 | { 8 | background-color: transparent; 9 | margin: 0px; 10 | padding: 0px; 11 | } 12 | 13 | body 14 | { 15 | padding: 10px; 16 | } 17 | 18 | body, td, input, select, textarea 19 | { 20 | font-size: 11px; 21 | font-family: 'Microsoft Sans Serif' , Arial, Helvetica, Verdana; 22 | } 23 | 24 | .midtext 25 | { 26 | padding:0px; 27 | margin:10px; 28 | } 29 | 30 | .midtext p 31 | { 32 | padding:0px; 33 | margin:10px; 34 | } 35 | 36 | .Button 37 | { 38 | border: #737357 1px solid; 39 | color: #3b3b1f; 40 | background-color: #c7c78f; 41 | } 42 | 43 | .PopupTabArea 44 | { 45 | color: #737357; 46 | background-color: #e3e3c7; 47 | } 48 | 49 | .PopupTitleBorder 50 | { 51 | border-bottom: #d5d59d 1px solid; 52 | } 53 | .PopupTabEmptyArea 54 | { 55 | padding-left: 10px; 56 | border-bottom: #d5d59d 1px solid; 57 | } 58 | 59 | .PopupTab, .PopupTabSelected 60 | { 61 | border-right: #d5d59d 1px solid; 62 | border-top: #d5d59d 1px solid; 63 | border-left: #d5d59d 1px solid; 64 | padding: 3px 5px 3px 5px; 65 | color: #737357; 66 | } 67 | 68 | .PopupTab 69 | { 70 | margin-top: 1px; 71 | border-bottom: #d5d59d 1px solid; 72 | cursor: pointer; 73 | } 74 | 75 | .PopupTabSelected 76 | { 77 | font-weight: bold; 78 | cursor: default; 79 | padding-top: 4px; 80 | border-bottom: #f1f1e3 1px solid; 81 | background-color: #f1f1e3; 82 | } 83 | -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/plugins/scayt/dialogs/toolbar.css: -------------------------------------------------------------------------------- 1 | a 2 | { 3 | text-decoration:none; 4 | padding: 2px 4px 4px 6px; 5 | display : block; 6 | border-width: 1px; 7 | border-style: solid; 8 | margin : 0px; 9 | } 10 | 11 | a.cke_scayt_toogle:hover, 12 | a.cke_scayt_toogle:focus, 13 | a.cke_scayt_toogle:active 14 | { 15 | border-color: #316ac5; 16 | background-color: #dff1ff; 17 | color : #000; 18 | cursor: pointer; 19 | margin : 0px; 20 | } 21 | a.cke_scayt_toogle { 22 | color : #316ac5; 23 | border-color: #fff; 24 | } 25 | .scayt_enabled a.cke_scayt_item { 26 | color : #316ac5; 27 | border-color: #fff; 28 | margin : 0px; 29 | } 30 | .scayt_disabled a.cke_scayt_item { 31 | color : gray; 32 | border-color : #fff; 33 | } 34 | .scayt_enabled a.cke_scayt_item:hover, 35 | .scayt_enabled a.cke_scayt_item:focus, 36 | .scayt_enabled a.cke_scayt_item:active 37 | { 38 | border-color: #316ac5; 39 | background-color: #dff1ff; 40 | color : #000; 41 | cursor: pointer; 42 | } 43 | .scayt_disabled a.cke_scayt_item:hover, 44 | .scayt_disabled a.cke_scayt_item:focus, 45 | .scayt_disabled a.cke_scayt_item:active 46 | { 47 | border-color: gray; 48 | background-color: #dff1ff; 49 | color : gray; 50 | cursor: no-drop; 51 | } 52 | .cke_scayt_set_on, .cke_scayt_set_off 53 | { 54 | display: none; 55 | } 56 | .scayt_enabled .cke_scayt_set_on 57 | { 58 | display: none; 59 | } 60 | .scayt_disabled .cke_scayt_set_on 61 | { 62 | display: inline; 63 | } 64 | .scayt_disabled .cke_scayt_set_off 65 | { 66 | display: none; 67 | } 68 | .scayt_enabled .cke_scayt_set_off 69 | { 70 | display: inline; 71 | } 72 | -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/plugins/showprotected/dialogs/protected.js: -------------------------------------------------------------------------------- 1 | 2 | CKEDITOR.dialog.add( 'showProtectedDialog', function( editor ) { 3 | 4 | return { 5 | title: 'Edit Protected Source', 6 | minWidth: 300, 7 | minHeight: 60, 8 | onOk: function() { 9 | var newSourceValue = this.getContentElement( 'info', 'txtProtectedSource' ).getValue(); 10 | 11 | var encodedSourceValue = CKEDITOR.plugins.showprotected.encodeProtectedSource( newSourceValue ); 12 | 13 | this._.selectedElement.setAttribute('data-cke-realelement', encodedSourceValue); 14 | this._.selectedElement.setAttribute('title', newSourceValue); 15 | this._.selectedElement.setAttribute('alt', newSourceValue); 16 | }, 17 | 18 | onHide: function() { 19 | delete this._.selectedElement; 20 | }, 21 | 22 | onShow: function() { 23 | this._.selectedElement = editor.getSelection().getSelectedElement(); 24 | 25 | var decodedSourceValue = CKEDITOR.plugins.showprotected.decodeProtectedSource( this._.selectedElement.getAttribute('data-cke-realelement') ); 26 | 27 | this.setValueOf( 'info', 'txtProtectedSource', decodedSourceValue ); 28 | }, 29 | contents: [ 30 | { 31 | id: 'info', 32 | label: 'Edit Protected Source', 33 | accessKey: 'I', 34 | elements: [ 35 | { 36 | type: 'text', 37 | id: 'txtProtectedSource', 38 | label: 'Value', 39 | required: true, 40 | validate: function() { 41 | if ( !this.getValue() ) { 42 | alert( 'The value cannot be empty' ); 43 | return false; 44 | } 45 | return true; 46 | } 47 | } 48 | ] 49 | } 50 | ] 51 | }; 52 | } ); -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/samples/assets/posteddata.php: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | Sample — CKEditor 12 | 13 | 14 | 15 |

16 | CKEditor — Posted Data 17 |

18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | $value ) 31 | { 32 | if ( ( !is_string($value) && !is_numeric($value) ) || !is_string($key) ) 33 | continue; 34 | 35 | if ( get_magic_quotes_gpc() ) 36 | $value = htmlspecialchars( stripslashes((string)$value) ); 37 | else 38 | $value = htmlspecialchars( (string)$value ); 39 | ?> 40 | 41 | 42 | 43 | 44 | 48 |
Field NameValue
49 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2013, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.html or http://ckeditor.com/license 4 | */ 5 | 6 | CKEDITOR.editorConfig = function( config ) { 7 | // Define changes to default configuration here. 8 | // For complete reference see: 9 | // http://docs.ckeditor.com/#!/api/CKEDITOR.config 10 | 11 | config.extraAllowedContent = 'x' 12 | config.extraPlugins = 'showprotected'; 13 | config.protectedSource.push( /(?:(?!<\/x>).)*.*?<\/x>/g ); 14 | 15 | // The toolbar groups arrangement, optimized for two toolbar rows. 16 | config.toolbarGroups = [ 17 | { name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ] }, 18 | { name: 'paragraph', groups: [ 'list', 'indent', 'blocks', 'align', 'bidi' ] }, 19 | { name: 'styles' }, 20 | { name: 'colors' }, 21 | // { name: 'clipboard', groups: [ 'clipboard', 'undo' ] }, 22 | // { name: 'editing', groups: [ 'find', 'selection', 'spellchecker' ] }, 23 | '/', 24 | { name: 'links' }, 25 | { name: 'insert' }, 26 | // { name: 'forms' }, 27 | { name: 'tools' }, 28 | { name: 'document', groups: [ 'mode', 'document', 'doctools' ] }, 29 | { name: 'others' }, 30 | // { name: 'about' } 31 | ]; 32 | 33 | // Remove some buttons provided by the standard plugins, which are 34 | // not needed in the Standard(s) toolbar. 35 | config.removeButtons = 'Underline,Subscript,Superscript'; 36 | 37 | // Set the most common block elements. 38 | config.format_tags = 'p;h1;h2;h3;h4;pre'; 39 | 40 | // Simplify the dialog windows. 41 | config.removeDialogTabs = 'image:advanced;link:advanced'; 42 | 43 | config.disableNativeSpellChecker = false; 44 | config.uiColor = '#FAFAFA'; 45 | }; 46 | -------------------------------------------------------------------------------- /setup/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var r = require('rethinkdb'); 4 | var _ = require('lodash'); 5 | var fs = require('fs'); 6 | var Q = require('q'); 7 | 8 | module.exports = function(config, callback) { 9 | 10 | // set up and populate uploads 11 | 12 | // set up and populate the database 13 | return r.connect(config.db).then(function(conn){ 14 | return r.dbList().run(conn) 15 | 16 | .then(function(dbs){ 17 | if(_.indexOf(dbs, config.db.db) !== -1) return; 18 | 19 | // create database 20 | return r.dbCreate(config.db.db).run(conn) 21 | 22 | // create schema and fixture 23 | .then(function(){ 24 | 25 | function requireJSON(f){ 26 | return JSON.parse(fs.readFileSync(f, 'utf8')); 27 | } 28 | 29 | return Q.all([ 30 | {info: requireJSON(__dirname + '/../test/fixtures/db/cycles.info'), data: require('../test/fixtures/db/cycles.json')}, 31 | {info: requireJSON(__dirname + '/../test/fixtures/db/projects.info'), data: require('../test/fixtures/db/projects.json')}, 32 | {info: requireJSON(__dirname + '/../test/fixtures/db/users.info'), data: require('../test/fixtures/db/users.json')}, 33 | {info: requireJSON(__dirname + '/../test/fixtures/db/notifications.info'), data: require('../test/fixtures/db/notifications.json')}, 34 | {info: requireJSON(__dirname + '/../test/fixtures/db/files.info'), data: require('../test/fixtures/db/files.json')} 35 | ].map(function(fixture) { 36 | return r.db(config.db.db).tableCreate(fixture.info.name, {primaryKey: fixture.info.primary_key}).run(conn) 37 | .then(function(){ return r.db(config.db.db).table(fixture.info.name).insert(fixture.data).run(conn); }); 38 | })); 39 | }) 40 | }) 41 | .finally(function(){ 42 | conn.close(); 43 | }); 44 | }); 45 | }; 46 | -------------------------------------------------------------------------------- /lib/modules/gandhi-component-start/app/default.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 6 | 7 | 10 | 11 |
12 | 13 |
14 |
15 |
16 | {{cycle.title}} 17 |
18 |
19 |
20 |
21 |
22 |
23 | 24 |
25 |
26 |
27 | {{project.title || 'Start'}} 28 |
29 |
30 |
31 |
32 | 33 | 34 |
35 | 36 | 37 |
38 | 39 |
40 |
41 |
42 | 43 |
44 |
45 | -------------------------------------------------------------------------------- /lib/api/endpoints/users.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var passport = require('passport'); 4 | 5 | module.exports = function(config, router, resources){ 6 | 7 | var controller = require('../controllers/users')(config, resources); 8 | 9 | // create 10 | // ------ 11 | router.post( 12 | '/api/users', 13 | controller.create 14 | ); 15 | 16 | // query 17 | // ----- 18 | router.get( 19 | '/api/users', 20 | passport.authenticate('bearer', { session: false }), 21 | controller.query 22 | ); 23 | 24 | // list (by cycle) 25 | // --------------- 26 | // router.get( 27 | // '/api/cycles/:cycle/users', 28 | // passport.authenticate('bearer', { session: false }), 29 | // controller.list 30 | // ); 31 | 32 | // list (by project) 33 | // ----------------- 34 | // router.get( 35 | // '/api/projects/:project/users', 36 | // passport.authenticate('bearer', { session: false }), 37 | // controller.list 38 | // ); 39 | 40 | // get 41 | // --- 42 | router.get( 43 | '/api/users/:user', 44 | passport.authenticate('bearer', { session: false }), 45 | controller.get 46 | ); 47 | 48 | // get (by file) 49 | // ------------- 50 | router.get( 51 | '/api/files/:file/user', 52 | passport.authenticate('bearer', { session: false }), 53 | controller.get 54 | ); 55 | 56 | // update 57 | // ------ 58 | router.patch( 59 | '/api/users/:user', 60 | passport.authenticate('bearer', { session: false }), 61 | controller.update 62 | ); 63 | 64 | // save 65 | // ---- 66 | router.put( 67 | '/api/users/:user', 68 | passport.authenticate('bearer', { session: false }), 69 | controller.save 70 | ); 71 | 72 | // delete 73 | // ------ 74 | router.delete( 75 | '/api/users/:user', 76 | passport.authenticate('bearer', { session: false }), 77 | controller.delete 78 | ); 79 | }; 80 | -------------------------------------------------------------------------------- /lib/modules/gandhi-component-start/app/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('gandhi-component-start', ['gandhi-component', 'schemaForm']) 4 | 5 | .config(function(GandhiComponentProvider) { 6 | GandhiComponentProvider.register({ 7 | id: 'start', 8 | title: 'Start', 9 | permissions: { 10 | read: { 11 | id: 'read', 12 | title: 'Read' 13 | }, 14 | write: { 15 | id: 'write', 16 | title: 'Write' 17 | } 18 | }, 19 | directives: { 20 | default: 'gandhi-component-start', 21 | contentAdmin: 'gandhi-component-start', 22 | stageAdmin: 'gandhi-component-start-stage-admin' 23 | } 24 | }); 25 | }) 26 | 27 | .directive('gandhiComponentStart', function() { 28 | return { 29 | scope: false, 30 | templateUrl: 'assets/bower/gandhi-component-start/app/default.html', 31 | controller: ['$scope', '$element', '$attrs', '$q', 'Project', 'Content', function($scope, $element, $attrs, $q, Project, Content) { 32 | $scope.$watch('context', function(context) { 33 | 34 | // give the view access to the context 35 | angular.extend($scope, context); 36 | 37 | $scope.update = function(){ 38 | new Project({title: context.project.title}).$update({ 39 | admin: context.mode === 'contentAdmin', 40 | id: context.project.id 41 | }).$promise 42 | } 43 | 44 | }); 45 | }] 46 | } 47 | }) 48 | 49 | .directive('gandhiComponentStartStageAdmin', function() { 50 | return { 51 | scope: false, 52 | templateUrl: 'assets/bower/gandhi-component-start/app/stageAdmin.html', 53 | controller: ['$scope', '$element', '$attrs', 'Stage', function($scope, $element, $attrs, Stage) { 54 | $scope.$watch('context', function(context) { 55 | 56 | // give the view access to the context 57 | angular.extend($scope, context); 58 | 59 | }); 60 | }] 61 | } 62 | }); 63 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gandhi", 3 | "version": "0.4.3", 4 | "description": "The open source, online grant management system.", 5 | "homepage": "https://www.gandhi.io", 6 | "license": "AGPL-3.0", 7 | "main": "lib/index.js", 8 | "engines": { 9 | "node": ">= 10" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/mike-marcacci/gandhi.git" 14 | }, 15 | "bugs": { 16 | "url": "https://github.com/mike-marcacci/gandhi/issues" 17 | }, 18 | "scripts": { 19 | "postinstall": "bower install", 20 | "start": "node server.js", 21 | "test": "mocha test/tests/*.js test/tests/**/*.js test/tests/**/**/*.js --reporter spec --trace-deprecation" 22 | }, 23 | "dependencies": { 24 | "@gbradley/scrypt": "^6.0.5", 25 | "@types/node": "^17.0.23", 26 | "async": "^3.2.3", 27 | "bluebird": "^3.7.2", 28 | "body-parser": "^1.13.1", 29 | "bower": "1.8.14", 30 | "express": "^4.12.4", 31 | "generic-pool": "^2.5.4", 32 | "glob": "^7.2.0", 33 | "handlebars": "^4.0.0", 34 | "ioredis": "^5.0.1", 35 | "jjv": "^1.0.2", 36 | "json-pointer": "^0.6.2", 37 | "jsonwebtoken": "^8.5.1", 38 | "li": "^1.3.0", 39 | "lodash": "^4.17.21", 40 | "method-override": "^3.0.0", 41 | "moment-timezone": "^0.5.34", 42 | "multer": "^0.1.8", 43 | "nodemailer": "^6.7.3", 44 | "objectpath": "^2.0.0", 45 | "passport": "^0.5.2", 46 | "passport-http-bearer": "^1.0.1", 47 | "passport-local": "^1.0.0", 48 | "q": "^1.4.1", 49 | "qs": "^6.10.3", 50 | "redlock": "^4.2.0", 51 | "res-error": "^0.3.1", 52 | "rethinkdb": "^2.0.0", 53 | "typescript": "^4.6.3", 54 | "wiredep": "^4.0.0" 55 | }, 56 | "devDependencies": { 57 | "chai": "~4.3.6", 58 | "mocha": "~9.2.2", 59 | "supertest": "^6.2.2" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/modules/gandhi-action-notify/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Promise = require('bluebird'); 4 | var Handlebars = require('handlebars'); 5 | var Notification = require('../../api/models/Notification'); 6 | 7 | var Users = require('../../api/collections/Users'); 8 | var users = new Users(); 9 | 10 | module.exports = function(router, resources) { 11 | resources.actions.notify = function(conn, project, options) { 12 | var roleIds = options.role_ids || []; 13 | 14 | // get all project assignments 15 | return Promise.settle(Object.keys(project.assignments.data).map(function(userId) { 16 | var assignment = project.assignments.data[userId]; 17 | 18 | // only notify specified roles 19 | if(roleIds.indexOf(assignment.role_id) === -1) 20 | return; 21 | 22 | // create a notification 23 | return Notification.create(conn, { 24 | user_id: userId, 25 | 26 | // TODO: first pull user from db to get name, etc 27 | subject: Handlebars.compile(options.subject || '')({ project: project, cycle: project.cycle }), 28 | content: Handlebars.compile(options.template || '')({ project: project, cycle: project.cycle }) 29 | }, true) 30 | 31 | .then(function(notification){ 32 | 33 | // no need to send an email 34 | if (options.send === false) 35 | return; 36 | 37 | return users.get(conn, notification.user_id, true) 38 | .then(function(user){ 39 | 40 | var mail = { 41 | to: user.email, 42 | subject: notification.subject, 43 | html: notification.content 44 | }; 45 | 46 | if (options.from) 47 | mail.from = options.from; 48 | 49 | // send mail in the background 50 | resources.mail(mail) 51 | 52 | // just log this error 53 | .catch(function(err) { 54 | console.error(err); 55 | }); 56 | 57 | }); 58 | }); 59 | })); 60 | }; 61 | }; 62 | -------------------------------------------------------------------------------- /lib/api/models/File.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Promise = require('bluebird'); 4 | var util = require('util'); 5 | var Model = require('../Model'); 6 | var uuid = require('../utils/uuid'); 7 | var errors = require('../errors'); 8 | 9 | 10 | // Schema Validator 11 | // ---------------- 12 | 13 | var validator = require('jjv')(); 14 | validator.addSchema(require('../schemas/file')); 15 | 16 | 17 | // Model Constructor 18 | // ----------------- 19 | 20 | function File (conn, data, user) { 21 | var self = this; 22 | 23 | if(typeof data === 'undefined' || typeof user === 'undefined') 24 | throw new Error('All constructor arguments are required by Model constructors.'); 25 | 26 | // user 27 | Object.defineProperty(self, 'user', { 28 | value: user 29 | }); 30 | 31 | return Model.call(self, conn, data); 32 | } 33 | 34 | 35 | // Model Configuration 36 | // ------------------- 37 | 38 | File.table = 'files'; 39 | File.collections = {}; 40 | File.reconstruct = function(conn, data, old) { 41 | return new File(conn, data, old.user); 42 | }; 43 | File.validate = function(data) { 44 | return validator.validate('http://www.gandhi.io/schema/file', data, {useDefault: true, removeAdditional: true}); 45 | }; 46 | File.create = function(conn, data, user) { 47 | 48 | // generate a new uuid 49 | data.id = uuid(); 50 | 51 | return new File(conn, data, user) 52 | .then(function(file){ 53 | return file.save(conn); 54 | }); 55 | }; 56 | 57 | 58 | 59 | // Public Methods 60 | // -------------- 61 | 62 | util.inherits(File, Model); 63 | 64 | 65 | // restrict to admin for delete 66 | File.prototype.delete = function(conn) { 67 | var self = this; 68 | 69 | // restrict to admin 70 | if(self.user !== true) 71 | return Promise.reject(new errors.ForbiddenError()); 72 | 73 | return Model.prototype.delete.call(self, conn); 74 | }; 75 | 76 | 77 | module.exports = File; 78 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gandhi", 3 | "description": "The Gandhi Grant Management System", 4 | "version": "0.0.0", 5 | "license": "MIT", 6 | "private": true, 7 | "main": [ 8 | "index.js", 9 | "portal/index.js", 10 | "portal/admin/index.js", 11 | "portal/admin/users/index.js", 12 | "portal/admin/cycles/index.js", 13 | "portal/admin/projects/index.js", 14 | "portal/cycles/index.js", 15 | "portal/projects/index.js", 16 | "portal/user/index.js" 17 | ], 18 | "dependencies": { 19 | "angular": "^1.8.2", 20 | "angular-ckeditor": "^1.0.3", 21 | "angular-elastic": "~2.5.0", 22 | "angular-json-human": "1.x.x", 23 | "angular-resource": "^1.8.2", 24 | "angular-sanitize": "1.x.x", 25 | "angular-schema-form": "0.8.x", 26 | "angular-ui-ace": "0.2.3", 27 | "angular-ui-router": "^1.0.30", 28 | "angular-ui-select": "0.19.x", 29 | "angular-ui-sortable": "0.19.x", 30 | "bootstrap-sass-official": "3.x.x", 31 | "bootstrap-vertical-tabs": "1.x.x", 32 | "checklist-model": "0.5.x", 33 | "lodash": "^4.17.21", 34 | "ng-file-upload": "~1.6.5", 35 | "objectpath": "1.x.x" 36 | }, 37 | "overrides": { 38 | "ace-builds": { 39 | "main": "src-min-noconflict/ace.js" 40 | }, 41 | "angular": { 42 | "main": "./angular.js", 43 | "dependencies": { 44 | "jquery": "^3.6.0" 45 | } 46 | } 47 | }, 48 | "modules": [ 49 | "angularFileUpload", 50 | "checklist-model", 51 | "ckeditor", 52 | "ngResource", 53 | "ngSanitize", 54 | "schemaForm", 55 | "ui.ace", 56 | "ui.router", 57 | "ui.select", 58 | "ui.sortable", 59 | "yaru22.jsonHuman", 60 | "monospaced.elastic" 61 | ], 62 | "resolutions": { 63 | "angular": "1.8.2", 64 | "lodash": "^4.17.21" 65 | }, 66 | "exclude": [ 67 | "bower/bootstrap/" 68 | ] 69 | } 70 | -------------------------------------------------------------------------------- /test/tests/fixtures.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require('lodash'); 4 | var fs = require('fs'); 5 | var assert = require('chai').assert; 6 | 7 | var validator = require('jjv')(); 8 | 9 | 10 | // customize validator 11 | validator.addType('date', function (v) { 12 | return true; 13 | // return !isNaN(Date.parse(v)); 14 | }); 15 | 16 | 17 | // add schemas 18 | fs.readdirSync(__dirname + '/../../lib/api/schemas/').forEach(function(file){ 19 | if(file.indexOf('.json') !== (file.length - 5)) 20 | return; 21 | 22 | var schema = require(__dirname + '/../../lib/api/schemas/' + file); 23 | validator.addSchema(schema); 24 | }); 25 | 26 | describe('Fixtures', function(){ 27 | 28 | it('should have valid cycles', function(){ 29 | require('../fixtures/db/cycles.json').forEach(function(cycle){ 30 | var err = validator.validate('http://www.gandhi.io/schema/cycle', cycle); 31 | if(err) throw new Error(JSON.stringify(_.extend(err, {id: cycle.id}))); 32 | }) 33 | }); 34 | 35 | it('should have valid projects', function(){ 36 | require('../fixtures/db/projects.json').forEach(function(cycle){ 37 | var err = validator.validate('http://www.gandhi.io/schema/project', cycle); 38 | if(err) throw new Error(JSON.stringify(_.extend(err, {id: cycle.id}))); 39 | }) 40 | }); 41 | 42 | it('should have valid users', function(){ 43 | require('../fixtures/db/users.json').forEach(function(cycle){ 44 | var err = validator.validate('http://www.gandhi.io/schema/user', cycle); 45 | if(err) throw new Error(JSON.stringify(_.extend(err, {id: cycle.id}))); 46 | }) 47 | }); 48 | 49 | it('should have valid notifications', function(){ 50 | require('../fixtures/db/notifications.json').forEach(function(cycle){ 51 | var err = validator.validate('http://www.gandhi.io/schema/notification', cycle); 52 | if(err) throw new Error(JSON.stringify(_.extend(err, {id: cycle.id}))); 53 | }) 54 | }); 55 | 56 | }); -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/samples/sample.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license 4 | */ 5 | 6 | // Tool scripts for the sample pages. 7 | // This file can be ignored and is not required to make use of CKEditor. 8 | 9 | ( function() { 10 | CKEDITOR.on( 'instanceReady', function( ev ) { 11 | // Check for sample compliance. 12 | var editor = ev.editor, 13 | meta = CKEDITOR.document.$.getElementsByName( 'ckeditor-sample-required-plugins' ), 14 | requires = meta.length ? CKEDITOR.dom.element.get( meta[ 0 ] ).getAttribute( 'content' ).split( ',' ) : [], 15 | missing = [], 16 | i; 17 | 18 | if ( requires.length ) { 19 | for ( i = 0; i < requires.length; i++ ) { 20 | if ( !editor.plugins[ requires[ i ] ] ) 21 | missing.push( '' + requires[ i ] + '' ); 22 | } 23 | 24 | if ( missing.length ) { 25 | var warn = CKEDITOR.dom.element.createFromHtml( 26 | '
' + 27 | 'To fully experience this demo, the ' + missing.join( ', ' ) + ' plugin' + ( missing.length > 1 ? 's are' : ' is' ) + ' required.' + 28 | '
' 29 | ); 30 | warn.insertBefore( editor.container ); 31 | } 32 | } 33 | 34 | // Set icons. 35 | var doc = new CKEDITOR.dom.document( document ), 36 | icons = doc.find( '.button_icon' ); 37 | 38 | for ( i = 0; i < icons.count(); i++ ) { 39 | var icon = icons.getItem( i ), 40 | name = icon.getAttribute( 'data-icon' ), 41 | style = CKEDITOR.skin.getIconStyle( name, ( CKEDITOR.lang.dir == 'rtl' ) ); 42 | 43 | icon.addClass( 'cke_button_icon' ); 44 | icon.addClass( 'cke_button__' + name + '_icon' ); 45 | icon.setAttribute( 'style', style ); 46 | icon.setStyle( 'float', 'none' ); 47 | 48 | } 49 | } ); 50 | } )(); 51 | -------------------------------------------------------------------------------- /lib/api/utils/cache.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var r = require('rethinkdb'); 4 | var projectModel = require('../models/projects.js'); 5 | 6 | // purge and reevaluate cache for all projects 7 | module.exports = function(conn, purge) { 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | // purge and reevaluate cache for all projects 16 | if(purge) return r.table('projects').replace(function(project){ 17 | return projectModel.processWriteHooks(project); 18 | }, {nonAtomic: true}).run(conn); 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | // purge and reevaluate expired cache for projects 27 | return r.now().toEpochTime().do(function(now){ 28 | 29 | // get all cycles 30 | return r.table('cycles').map(function(cycle){ 31 | return { 32 | cycle: cycle, 33 | 34 | // get the most recently fired date trigger 35 | threshold: cycle('triggers').coerceTo('array').map(function(kv){ 36 | return kv(1)('conditions').map(function(condition){ 37 | return condition.filter(function(rule){ 38 | return rule('name').eq('date').and( 39 | rule('options')('timestamp').lt(now)); 40 | }).map(function(rule){ 41 | return rule('options')('timestamp'); 42 | }).max().default(null); 43 | }).max(); 44 | }).max() 45 | }; 46 | }) 47 | 48 | // filter out any without a date trigger 49 | .filter(function(result){ 50 | return result('threshold').not().not(); 51 | }) 52 | .forEach(function(result){ 53 | 54 | // get projects that have not been update since the threshold 55 | return r.table('projects').filter(function(project){ 56 | return project('cycle_id').eq(result('cycle')('id')).and( 57 | project('updated').lt(result('threshold'))); 58 | }) 59 | 60 | // reevaluate the cache for selected projects 61 | .replace(function(project){ 62 | return projectModel.processWriteHooks(project, result('cycle')); 63 | }, {nonAtomic: true}); 64 | 65 | }); 66 | }).run(conn); 67 | }; 68 | -------------------------------------------------------------------------------- /lib/app/auth.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |

Welcome.

4 |
5 | 6 | 10 | 11 |
12 |
13 |
14 |
15 |
16 | 17 |
18 |
19 | 20 |
21 | 22 |
Recover
23 |
24 |
25 | 26 |
27 |
28 |
29 | 30 |
31 |
32 | 33 |
34 |
35 | 36 |
37 |
38 | 39 |
40 | 41 |
42 |
43 |
44 |
45 |
-------------------------------------------------------------------------------- /lib/app/assets/ckeditor/plugins/wsc/dialogs/ciframe.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 64 | 65 |

66 | 67 | -------------------------------------------------------------------------------- /lib/app/portal/admin/projects/create.html: -------------------------------------------------------------------------------- 1 | 6 |
7 | 10 | 11 |
12 |
13 |
14 | Create Project 15 |
16 |
Cancel
17 | 18 |
19 |
20 |
21 |
22 |
23 | 24 |
25 | 26 |
27 |
28 |
29 | 30 |
31 | 32 | {{$select.selected.title}} 33 | 34 |
35 | 36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | -------------------------------------------------------------------------------- /lib/modules/gandhi-component-team/api/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require('lodash'); 4 | var Handlebars = require('handlebars'); 5 | 6 | module.exports = function(router, resources){ 7 | resources.components.team = { 8 | stage: { 9 | read: function(conn, stage) { 10 | return stage; 11 | }, 12 | write: function(conn, input, stage) { 13 | var result = _.merge({}, stage, input); 14 | 15 | // make sure permissions are listed 16 | if(result.component && result.component.permissions) { 17 | 18 | // assignments 19 | result.component.permissions.assignments = ( (input.component && input.component.permissions && input.component.permissions.assignments) ? 20 | input.component.permissions.assignments 21 | : result.component.permissions.assignments ) || {}; 22 | 23 | // invitations 24 | result.component.permissions.invitations = ( (input.component && input.component.permissions && input.component.permissions.invitations) ? 25 | input.component.permissions.invitations : 26 | result.component.permissions.invitations ) || {}; 27 | } 28 | 29 | return result; 30 | } 31 | }, 32 | content: { 33 | read: function(conn, content, stage) { 34 | var project = content.parent; 35 | var cycle = stage.parent; 36 | 37 | 38 | // Note: it is an extremely bad idea to modify "exports" when reading, because 39 | // trigger comparisons and server-side template rendering do not use this 40 | // function. Instead, only export durable values, calculating them on write. 41 | // 42 | // If an exported value is time-dependent, schedule a refresh (update with {}) 43 | // at the desired time. 44 | 45 | 46 | // compile instructions 47 | if(stage.component.options.instructions) 48 | try { content.data.instructions = Handlebars.compile(stage.component.options.instructions)({ project: project, cycle: cycle, stage: stage }); } catch (e) {} 49 | 50 | 51 | return content; 52 | }, 53 | write: function(conn, input, content, stage) { 54 | return input; 55 | } 56 | } 57 | }; 58 | }; 59 | -------------------------------------------------------------------------------- /lib/app/portal/projects/list.html: -------------------------------------------------------------------------------- 1 | 4 | 5 |
6 |
7 |
8 | 9 | 10 |
11 |
12 | 13 | {{$select.selected.title}} 14 | 15 |
16 | 17 |
18 |
19 | 20 | 21 | 22 |
23 |
24 | 25 | 26 |
27 | 28 |
29 | 30 |
31 |
32 |
33 | 34 | 35 | 36 | 40 | 41 |
42 |
Loading...
43 |
44 | 45 |
46 |
You don't have any {{assignedOnly ? 'assigned' : ''}} projects.
47 |
48 | 49 |
-------------------------------------------------------------------------------- /lib/api/collections/Users.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var r = require('rethinkdb'); 4 | var Promise = require('bluebird'); 5 | var errors = require('../errors'); 6 | 7 | var User = require('../models/User'); 8 | 9 | function Users(){} 10 | 11 | Users.prototype.query = function(conn, query) { 12 | 13 | // Filter Users 14 | // ------------ 15 | 16 | var users = r.table('users'); 17 | 18 | // get by email 19 | if(typeof query.email === 'string') 20 | users = users.filter({email: query.email.toLowerCase()}); 21 | 22 | // filter 23 | if(query.filter) query.filter.forEach(function(f){ 24 | users = users.filter(f); 25 | }); 26 | 27 | // search 28 | if(typeof query.search === 'string' && query.search.length) 29 | users = users.filter(r.or( 30 | r.row('name').downcase().match(query.search.toLowerCase()), 31 | r.row('email').downcase().match(query.search.toLowerCase()) 32 | )); 33 | 34 | 35 | // Build Result 36 | // ------------ 37 | 38 | var result = users; 39 | 40 | // sort 41 | if(query.sort) 42 | result = result.orderBy.apply(result, query.sort); 43 | 44 | // skip 45 | if(query.skip) 46 | result = result.skip(query.skip); 47 | 48 | // limit 49 | if(query.limit) 50 | result = result.limit(query.limit); 51 | 52 | return r.expr({ 53 | 54 | // get the total results count 55 | total: users.count(), 56 | 57 | // get the processed users 58 | users: result.coerceTo('array') 59 | 60 | }).run(conn) 61 | 62 | // return as an array 63 | .then(function(results){ 64 | return Promise.all(results.users.map(function(data){ 65 | return new User(conn, data); 66 | })) 67 | .then(function(users){ 68 | users.total = results.total; 69 | return users; 70 | }); 71 | }); 72 | }; 73 | 74 | 75 | Users.prototype.get = function(conn, id) { 76 | return r.table('users').get(id).run(conn).then(function(data) { 77 | if(!data) return Promise.reject(new errors.NotFoundError()); 78 | return new User(conn, data); 79 | }); 80 | }; 81 | 82 | 83 | Users.prototype.create = function(conn, data) { 84 | return User.create(conn, data); 85 | }; 86 | 87 | 88 | module.exports = Users; 89 | -------------------------------------------------------------------------------- /lib/app/portal/admin/cycles/show.stages.stage.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 |
6 | 7 |
8 | 9 |
10 |
11 | Visibility 12 |
13 | 14 | 15 |
16 |
17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 31 | 32 | 33 |
RoleVisible
{{role.title}} 29 | 30 |
34 |
35 |
36 | 37 |
38 |
39 | Permissions 40 |
41 | 42 | 43 |
44 |
45 |
46 | 47 |
48 |
49 |
50 | 51 |
52 | -------------------------------------------------------------------------------- /lib/app/portal/admin/users/create.html: -------------------------------------------------------------------------------- 1 | 6 | 7 |
8 | 11 | 12 |
13 |
14 |
15 | Create User 16 |
17 |
18 |
19 |
20 | 21 |
22 | 23 |
24 |
25 |
26 | 27 |
28 | 29 |
30 |
31 |
32 | 33 |
34 | 35 |
36 |
37 |
38 | 39 |
40 | 41 |
42 |
43 |
44 |
45 |
Cancel
46 | 47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | -------------------------------------------------------------------------------- /lib/modules/gandhi-component-form/app/default.html: -------------------------------------------------------------------------------- 1 |
Loading...
2 | 3 |
4 | 5 | 8 | 9 | 12 | 13 |
14 | 15 |
16 |
17 |
18 | {{cycle.title}} 19 |
20 |
21 |
22 |
23 |
24 |
25 | 26 |
27 |
28 |
29 | {{stage.title}} - {{content.status_id | title}} 30 |
31 |
32 | 33 |
34 |
35 |
36 | 37 |
38 | 39 |
40 |
41 |
42 | 43 |
44 |
-------------------------------------------------------------------------------- /lib/app/assets/ckeditor/plugins/wsc/dialogs/tmpFrameset.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /lib/app/portal/admin/projects/show.contents.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 11 |
12 |
13 | 14 |
15 |
16 |
17 | Contents 18 |
19 | 20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 35 | 36 | 40 | 44 | 45 | 46 |
TitleDescriptionStatus
33 | {{ stagesById[content.id].title }} 34 | {{ stagesById[content.id].description }} 37 |

{{ content.status_id }}

38 | 39 |
41 | 42 | 43 |
47 |
48 |
49 |
-------------------------------------------------------------------------------- /lib/app/assets/ckeditor/plugins/specialchar/dialogs/lang/ja.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved. 3 | For licensing, see LICENSE.md or http://ckeditor.com/license 4 | */ 5 | CKEDITOR.plugins.setLang("specialchar","ja",{euro:"ユーロ記号",lsquo:"左シングル引用符",rsquo:"右シングル引用符",ldquo:"左ダブル引用符",rdquo:"右ダブル引用符",ndash:"半角ダッシュ",mdash:"全角ダッシュ",iexcl:"逆さ感嘆符",cent:"セント記号",pound:"ポンド記号",curren:"通貨記号",yen:"円記号",brvbar:"上下に分かれた縦棒",sect:"節記号",uml:"分音記号(ウムラウト)",copy:"著作権表示記号",ordf:"女性序数標識",laquo:" 始め二重山括弧引用記号",not:"論理否定記号",reg:"登録商標記号",macr:"長音符",deg:"度記号",sup2:"上つき2, 2乗",sup3:"上つき3, 3乗",acute:"揚音符",micro:"ミクロン記号",para:"段落記号",middot:"中黒",cedil:"セディラ",sup1:"上つき1",ordm:"男性序数標識",raquo:"終わり二重山括弧引用記号", 6 | frac14:"四分の一",frac12:"二分の一",frac34:"四分の三",iquest:"逆疑問符",Agrave:"抑音符つき大文字A",Aacute:"揚音符つき大文字A",Acirc:"曲折アクセントつき大文字A",Atilde:"チルダつき大文字A",Auml:"分音記号つき大文字A",Aring:"リングつき大文字A",AElig:"AとEの合字",Ccedil:"セディラつき大文字C",Egrave:"抑音符つき大文字E",Eacute:"揚音符つき大文字E",Ecirc:"曲折アクセントつき大文字E",Euml:"分音記号つき大文字E",Igrave:"抑音符つき大文字I",Iacute:"揚音符つき大文字I",Icirc:"曲折アクセントつき大文字I",Iuml:"分音記号つき大文字I",ETH:"[アイスランド語]大文字ETH",Ntilde:"チルダつき大文字N",Ograve:"抑音符つき大文字O",Oacute:"揚音符つき大文字O",Ocirc:"曲折アクセントつき大文字O",Otilde:"チルダつき大文字O",Ouml:" 分音記号つき大文字O", 7 | times:"乗算記号",Oslash:"打ち消し線つき大文字O",Ugrave:"抑音符つき大文字U",Uacute:"揚音符つき大文字U",Ucirc:"曲折アクセントつき大文字U",Uuml:"分音記号つき大文字U",Yacute:"揚音符つき大文字Y",THORN:"[アイスランド語]大文字THORN",szlig:"ドイツ語エスツェット",agrave:"抑音符つき小文字a",aacute:"揚音符つき小文字a",acirc:"曲折アクセントつき小文字a",atilde:"チルダつき小文字a",auml:"分音記号つき小文字a",aring:"リングつき小文字a",aelig:"aとeの合字",ccedil:"セディラつき小文字c",egrave:"抑音符つき小文字e",eacute:"揚音符つき小文字e",ecirc:"曲折アクセントつき小文字e",euml:"分音記号つき小文字e",igrave:"抑音符つき小文字i",iacute:"揚音符つき小文字i",icirc:"曲折アクセントつき小文字i",iuml:"分音記号つき小文字i",eth:"アイスランド語小文字eth", 8 | ntilde:"チルダつき小文字n",ograve:"抑音符つき小文字o",oacute:"揚音符つき小文字o",ocirc:"曲折アクセントつき小文字o",otilde:"チルダつき小文字o",ouml:"分音記号つき小文字o",divide:"除算記号",oslash:"打ち消し線つき小文字o",ugrave:"抑音符つき小文字u",uacute:"揚音符つき小文字u",ucirc:"曲折アクセントつき小文字u",uuml:"分音記号つき小文字u",yacute:"揚音符つき小文字y",thorn:"アイスランド語小文字thorn",yuml:"分音記号つき小文字y",OElig:"OとEの合字",oelig:"oとeの合字",372:"曲折アクセントつき大文字W",374:"曲折アクセントつき大文字Y",373:"曲折アクセントつき小文字w",375:"曲折アクセントつき小文字y",sbquo:"シングル下引用符",8219:"左右逆の左引用符",bdquo:"ダブル下引用符",hellip:"三点リーダ",trade:"商標記号",9658:"右黒三角ポインタ",bull:"黒丸", 9 | rarr:"右矢印",rArr:"右二重矢印",hArr:"左右二重矢印",diams:"ダイヤ",asymp:"漸近"}); -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/samples/appendto.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | Append To Page Element Using JavaScript Code — CKEditor Sample 10 | 11 | 12 | 13 | 14 |

15 | CKEditor Samples » Append To Page Element Using JavaScript Code 16 |

17 |
18 |
19 |

20 | The CKEDITOR.appendTo() method serves to to place editors inside existing DOM elements. Unlike CKEDITOR.replace(), 21 | a target container to be replaced is no longer necessary. A new editor 22 | instance is inserted directly wherever it is desired. 23 |

24 |
CKEDITOR.appendTo( 'container_id',
25 | 	{ /* Configuration options to be used. */ }
26 | 	'Editor content to be used.'
27 | );
28 |
29 | 43 |
44 |
45 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /lib/modules/gandhi-component-team/app/stageAdmin.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |
6 |
7 | 8 | 9 |
10 |
Instructions
11 |
12 | 13 |
14 |
You haven't specified any instructions for this stage.
15 |
16 |
17 |
18 | 19 |
20 |
21 |
22 | 23 |
24 | 25 |
26 |
27 |
28 |
29 |
30 | 33 |
34 |
35 |
36 |
37 | 38 |
39 | 40 |
41 |
42 |
43 |
44 | 45 | 46 |
47 |
48 | -------------------------------------------------------------------------------- /test/init/1.db.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var async = require('async'); 5 | var r = require('rethinkdb'); 6 | 7 | // was the setup successful? 8 | var setup = false; 9 | 10 | module.exports = { 11 | setup: function(done){ 12 | var config = global.setup.config; 13 | 14 | // let's make a temporary database for our test 15 | config.db.db = config.db.db + '_' + Date.now(); 16 | 17 | // create the database 18 | r.connect(config.db, function(err, conn){ 19 | r.dbDrop(config.db.db).run(conn, function(err, res) { 20 | r.dbCreate(config.db.db).run(conn, function(err, res) { 21 | if(err){ 22 | conn.close(); 23 | return done(err); 24 | } 25 | 26 | // now we have something to teardown 27 | setup = true; 28 | 29 | function requireJSON(f){ 30 | return JSON.parse(fs.readFileSync(f, 'utf8')); 31 | } 32 | 33 | var fixtures = [ 34 | {info: requireJSON(__dirname + '/../fixtures/db/cycles.info'), data: require('../fixtures/db/cycles.json')}, 35 | {info: requireJSON(__dirname + '/../fixtures/db/projects.info'), data: require('../fixtures/db/projects.json')}, 36 | {info: requireJSON(__dirname + '/../fixtures/db/users.info'), data: require('../fixtures/db/users.json')}, 37 | {info: requireJSON(__dirname + '/../fixtures/db/notifications.info'), data: require('../fixtures/db/notifications.json')}, 38 | {info: requireJSON(__dirname + '/../fixtures/db/files.info'), data: require('../fixtures/db/files.json')} 39 | ]; 40 | 41 | async.each(fixtures, function(fixture, loop){ 42 | r.db(config.db.db).tableCreate(fixture.info.name, {primaryKey: fixture.info.primary_key}).run(conn, function(err, res){ 43 | 44 | // TODO: add indices 45 | 46 | r.db(config.db.db).table(fixture.info.name).insert(fixture.data).run(conn, loop); 47 | }); 48 | }, done); 49 | }); 50 | }); 51 | }); 52 | }, 53 | teardown: function(done){ 54 | var config = global.setup.config; 55 | 56 | // we don't want to tear down if we were unable to setup 57 | // like if the database we're trying to use already exists 58 | if(!setup) 59 | return done(); 60 | 61 | // drop the database 62 | r.connect(config.db, function(err, conn){ 63 | r.dbDrop(config.db.db).run(conn, function(err, res) { 64 | done(err); 65 | }); 66 | }); 67 | } 68 | }; 69 | -------------------------------------------------------------------------------- /lib/app/assets/style/screen.scss: -------------------------------------------------------------------------------- 1 | 2 | @media (max-width: 767px) { 3 | html.show-navigation { 4 | height: 100%; 5 | overflow: hidden; 6 | width: 100%; 7 | 8 | body { 9 | height: 100%; 10 | overflow-y: hidden; 11 | } 12 | 13 | #sidebar { 14 | box-shadow: 0 0 20px rgba(0, 0, 0, 1); 15 | max-height: 100%; 16 | overflow-y: auto; 17 | 18 | > nav { 19 | transition: visibility 0s 0s; 20 | visibility: visible; 21 | } 22 | } 23 | 24 | #main::after { 25 | opacity: 1; 26 | transition-delay: 0s; 27 | visibility: visible; 28 | } 29 | 30 | } 31 | } 32 | 33 | 34 | @media (min-width: 768px) { 35 | 36 | #main { 37 | margin-top: 0; 38 | } 39 | 40 | #sidebar { 41 | bottom: 0; 42 | box-shadow: 2px 0px 1px rgba(0, 0, 0, 0.1); 43 | max-height: 100%; 44 | width: 210px; 45 | 46 | > .control { 47 | background: rgb(219, 219, 219); 48 | border-radius: 3px 0 0 3px; 49 | color: rgb(126, 126, 126); 50 | cursor: pointer; 51 | display: block; 52 | font-family: "Glyphicons Halflings"; 53 | font-size: 8px; 54 | line-height: 40px; 55 | margin-top: -20px; 56 | opacity: .5; 57 | padding-left: 1px; 58 | position: absolute; 59 | right: 0; 60 | top: 50%; 61 | transition: opacity .2s; 62 | vertical-align: middle; 63 | 64 | &:hover { 65 | opacity: 1; 66 | } 67 | } 68 | 69 | nav { 70 | transition: visibility 0s 0s; 71 | visibility: visible; 72 | 73 | -ms-flex: 1; 74 | -moz-flex: 1; 75 | -webkit-flex: 1; 76 | flex: 1; 77 | } 78 | } 79 | 80 | #body { 81 | margin-left: 210px; 82 | } 83 | 84 | .collapse-sidebar { 85 | 86 | #sidebar { 87 | left: -180px; 88 | 89 | > .control { 90 | opacity: 0; 91 | } 92 | 93 | nav > ul > li > ul { 94 | transition: all 0.8s; 95 | max-height: 400px; 96 | overflow-y: auto; 97 | } 98 | 99 | &:not(:hover) { 100 | nav > ul > li > ul { 101 | border-width: 0; 102 | margin: 0; 103 | max-height: 0; 104 | overflow-y: hidden; 105 | padding: 0; 106 | } 107 | } 108 | 109 | &:hover { 110 | left: 0px; 111 | 112 | > .control { 113 | opacity: 1; 114 | } 115 | } 116 | } 117 | 118 | #body { 119 | margin-left: 30px; 120 | } 121 | } 122 | } -------------------------------------------------------------------------------- /migrations/v0.1.0.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var r = require('rethinkdb'); 4 | var q = require('q'); 5 | 6 | var host = '127.0.0.1'; 7 | var db = 'gandhi'; 8 | 9 | // grab command line agrs 10 | for (var i = 2; i < process.argv.length; i++) { 11 | if(process.argv[i] === '--host'){ 12 | i++; host = process.argv[i]; 13 | } 14 | 15 | else if(process.argv[i] === '--db'){ 16 | i++; db = process.argv[i]; 17 | } 18 | } 19 | 20 | 21 | r.connect({host: host, db: db}).then(function(conn){ 22 | 23 | var tasks = []; 24 | 25 | // Cycles 26 | // ------ 27 | tasks.push(r.table('cycles').replace(function(cycle){ 28 | return cycle.merge({ 29 | permissions: { 30 | 'project:read': cycle('permissions')('project:read').default({}), 31 | 'project:create': cycle('permissions')('project:create').default({}), 32 | 'project:update': cycle('permissions')('project:update').default({}), 33 | 'project:delete': cycle('permissions')('project:delete').default({}), 34 | 'project/assignments:read': cycle('permissions')('project/assignments:read').default({}), 35 | 'project/assignments:write': cycle('permissions')('project/assignments:write').default({}), 36 | 'project/contents:read': cycle('permissions')('project/contents:read').default({}), 37 | 'project/contents:write': cycle('permissions')('project/contents:write').default({}), 38 | 'project/invitations:read': cycle('permissions')('project/assignments:read').default({}), 39 | 'project/invitations:write': cycle('permissions')('project/assignments:write').default({}) 40 | }, 41 | 42 | stages: cycle('stages').coerceTo('array').map(function(stagesKV){ 43 | return [stagesKV.nth(0), stagesKV.nth(1).merge({ 44 | visible: stagesKV.nth(1)('visible').default({}), 45 | order: stagesKV.nth(1)('order').default(0) 46 | })]; 47 | }).coerceTo('object'), 48 | 49 | roles: cycle('roles').coerceTo('array').map(function(rolesKV){ 50 | return [rolesKV.nth(0), rolesKV.nth(1).merge({ 51 | visible: rolesKV.nth(1)('visible').default({}), 52 | assignable: rolesKV.nth(1)('assignable').default({}) 53 | })]; 54 | }).coerceTo('object'), 55 | }); 56 | }).run(conn)); 57 | 58 | // run all the tasks 59 | q.all(tasks).then(function(res){ 60 | console.log('success:', JSON.stringify(res, null, 2)); 61 | conn.close(); 62 | }, function(err){ 63 | console.error(err); 64 | conn.close(); 65 | }); 66 | }); -------------------------------------------------------------------------------- /lib/app/assets/style/screen.css: -------------------------------------------------------------------------------- 1 | @media (max-width: 767px) { 2 | html.show-navigation { 3 | height: 100%; 4 | overflow: hidden; 5 | width: 100%; } 6 | html.show-navigation body { 7 | height: 100%; 8 | overflow-y: hidden; } 9 | html.show-navigation #sidebar { 10 | box-shadow: 0 0 20px black; 11 | max-height: 100%; 12 | overflow-y: auto; } 13 | html.show-navigation #sidebar > nav { 14 | transition: visibility 0s 0s; 15 | visibility: visible; } 16 | html.show-navigation #main::after { 17 | opacity: 1; 18 | transition-delay: 0s; 19 | visibility: visible; } } 20 | @media (min-width: 768px) { 21 | #main { 22 | margin-top: 0; } 23 | 24 | #sidebar { 25 | bottom: 0; 26 | box-shadow: 2px 0px 1px rgba(0, 0, 0, 0.1); 27 | max-height: 100%; 28 | width: 210px; } 29 | #sidebar > .control { 30 | background: #dbdbdb; 31 | border-radius: 3px 0 0 3px; 32 | color: #7e7e7e; 33 | cursor: pointer; 34 | display: block; 35 | font-family: "Glyphicons Halflings"; 36 | font-size: 8px; 37 | line-height: 40px; 38 | margin-top: -20px; 39 | opacity: .5; 40 | padding-left: 1px; 41 | position: absolute; 42 | right: 0; 43 | top: 50%; 44 | transition: opacity .2s; 45 | vertical-align: middle; } 46 | #sidebar > .control:hover { 47 | opacity: 1; } 48 | #sidebar nav { 49 | transition: visibility 0s 0s; 50 | visibility: visible; 51 | -ms-flex: 1; 52 | -moz-flex: 1; 53 | -webkit-flex: 1; 54 | flex: 1; } 55 | 56 | #body { 57 | margin-left: 210px; } 58 | 59 | .collapse-sidebar #sidebar { 60 | left: -180px; } 61 | .collapse-sidebar #sidebar > .control { 62 | opacity: 0; } 63 | .collapse-sidebar #sidebar nav > ul > li > ul { 64 | transition: all 0.8s; 65 | max-height: 400px; 66 | overflow-y: auto; } 67 | .collapse-sidebar #sidebar:not(:hover) nav > ul > li > ul { 68 | border-width: 0; 69 | margin: 0; 70 | max-height: 0; 71 | overflow-y: hidden; 72 | padding: 0; } 73 | .collapse-sidebar #sidebar:hover { 74 | left: 0px; } 75 | .collapse-sidebar #sidebar:hover > .control { 76 | opacity: 1; } 77 | .collapse-sidebar #body { 78 | margin-left: 30px; } } 79 | 80 | /*# sourceMappingURL=screen.css.map */ 81 | -------------------------------------------------------------------------------- /lib/api/collections/Cycles.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var r = require('rethinkdb'); 4 | var Promise = require('bluebird'); 5 | var errors = require('../errors'); 6 | 7 | var Cycle = require('../models/Cycle'); 8 | 9 | function Cycles(){} 10 | 11 | Cycles.prototype.query = function(conn, query, user) { 12 | 13 | // Filter Cycles 14 | // ------------- 15 | 16 | var cycles = r.table('cycles'); 17 | 18 | // OPTIMIZATION: hide drafts from non-admin users 19 | if(user !== true) 20 | cycles = cycles.filter(r.row('status_id').eq('draft').not()); 21 | 22 | // filter 23 | if(query.filter) query.filter.forEach(function(f){ 24 | cycles = cycles.filter(f); 25 | }); 26 | 27 | // search 28 | if(typeof query.search === 'string' && query.search.length) 29 | cycles = cycles.filter(r.row('title').downcase().match(query.search.toLowerCase())); 30 | 31 | 32 | // Build Result 33 | // ------------ 34 | 35 | var result = cycles; 36 | 37 | // sort 38 | if(query.sort) 39 | result = result.orderBy.apply(result, query.sort); 40 | 41 | // skip 42 | if(query.skip) 43 | result = result.skip(query.skip); 44 | 45 | // limit 46 | if(query.limit) 47 | result = result.limit(query.limit); 48 | 49 | return r.expr({ 50 | 51 | // get the total results count 52 | total: cycles.count(), 53 | 54 | // get the processed cycles 55 | cycles: result.coerceTo('array') 56 | 57 | }).run(conn) 58 | 59 | // return as an array 60 | .then(function(results){ 61 | return Promise.filter(results.cycles.map(function(data){ 62 | return new Cycle(conn, data, user) 63 | .catch(function(err){ 64 | 65 | // suppress ForbiddenError 66 | if(err instanceof errors.ForbiddenError) 67 | return null; 68 | 69 | // re-throw all other errors 70 | return Promise.reject(err); 71 | }); 72 | }), function(c){ return !!c; }) 73 | 74 | // set the total on the array 75 | .then(function(cycles){ 76 | cycles.total = results.total; 77 | return cycles; 78 | }); 79 | }); 80 | }; 81 | 82 | 83 | Cycles.prototype.get = function(conn, id, user) { 84 | return r.table('cycles').get(id).run(conn).then(function(data) { 85 | if(!data) return Promise.reject(new errors.NotFoundError()); 86 | 87 | return new Cycle(conn, data, user); 88 | }); 89 | }; 90 | 91 | 92 | Cycles.prototype.create = function(conn, data, user) { 93 | return Cycle.create(conn, data, user); 94 | }; 95 | 96 | 97 | module.exports = Cycles; -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/plugins/specialchar/dialogs/lang/zh-cn.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved. 3 | For licensing, see LICENSE.md or http://ckeditor.com/license 4 | */ 5 | CKEDITOR.plugins.setLang("specialchar","zh-cn",{euro:"欧元符号",lsquo:"左单引号",rsquo:"右单引号",ldquo:"左双引号",rdquo:"右双引号",ndash:"短划线",mdash:"长划线",iexcl:"竖翻叹号",cent:"分币符号",pound:"英镑符号",curren:"货币符号",yen:"日元符号",brvbar:"间断条",sect:"节标记",uml:"分音符",copy:"版权所有标记",ordf:"阴性顺序指示符",laquo:"左指双尖引号",not:"非标记",reg:"注册标记",macr:"长音符",deg:"度标记",sup2:"上标二",sup3:"上标三",acute:"锐音符",micro:"微符",para:"段落标记",middot:"中间点",cedil:"下加符",sup1:"上标一",ordm:"阳性顺序指示符",raquo:"右指双尖引号",frac14:"普通分数四分之一",frac12:"普通分数二分之一",frac34:"普通分数四分之三",iquest:"竖翻问号", 6 | Agrave:"带抑音符的拉丁文大写字母 A",Aacute:"带锐音符的拉丁文大写字母 A",Acirc:"带扬抑符的拉丁文大写字母 A",Atilde:"带颚化符的拉丁文大写字母 A",Auml:"带分音符的拉丁文大写字母 A",Aring:"带上圆圈的拉丁文大写字母 A",AElig:"拉丁文大写字母 Ae",Ccedil:"带下加符的拉丁文大写字母 C",Egrave:"带抑音符的拉丁文大写字母 E",Eacute:"带锐音符的拉丁文大写字母 E",Ecirc:"带扬抑符的拉丁文大写字母 E",Euml:"带分音符的拉丁文大写字母 E",Igrave:"带抑音符的拉丁文大写字母 I",Iacute:"带锐音符的拉丁文大写字母 I",Icirc:"带扬抑符的拉丁文大写字母 I",Iuml:"带分音符的拉丁文大写字母 I",ETH:"拉丁文大写字母 Eth",Ntilde:"带颚化符的拉丁文大写字母 N",Ograve:"带抑音符的拉丁文大写字母 O",Oacute:"带锐音符的拉丁文大写字母 O",Ocirc:"带扬抑符的拉丁文大写字母 O",Otilde:"带颚化符的拉丁文大写字母 O", 7 | Ouml:"带分音符的拉丁文大写字母 O",times:"乘号",Oslash:"带粗线的拉丁文大写字母 O",Ugrave:"带抑音符的拉丁文大写字母 U",Uacute:"带锐音符的拉丁文大写字母 U",Ucirc:"带扬抑符的拉丁文大写字母 U",Uuml:"带分音符的拉丁文大写字母 U",Yacute:"带抑音符的拉丁文大写字母 Y",THORN:"拉丁文大写字母 Thorn",szlig:"拉丁文小写字母清音 S",agrave:"带抑音符的拉丁文小写字母 A",aacute:"带锐音符的拉丁文小写字母 A",acirc:"带扬抑符的拉丁文小写字母 A",atilde:"带颚化符的拉丁文小写字母 A",auml:"带分音符的拉丁文小写字母 A",aring:"带上圆圈的拉丁文小写字母 A",aelig:"拉丁文小写字母 Ae",ccedil:"带下加符的拉丁文小写字母 C",egrave:"带抑音符的拉丁文小写字母 E",eacute:"带锐音符的拉丁文小写字母 E",ecirc:"带扬抑符的拉丁文小写字母 E",euml:"带分音符的拉丁文小写字母 E",igrave:"带抑音符的拉丁文小写字母 I", 8 | iacute:"带锐音符的拉丁文小写字母 I",icirc:"带扬抑符的拉丁文小写字母 I",iuml:"带分音符的拉丁文小写字母 I",eth:"拉丁文小写字母 Eth",ntilde:"带颚化符的拉丁文小写字母 N",ograve:"带抑音符的拉丁文小写字母 O",oacute:"带锐音符的拉丁文小写字母 O",ocirc:"带扬抑符的拉丁文小写字母 O",otilde:"带颚化符的拉丁文小写字母 O",ouml:"带分音符的拉丁文小写字母 O",divide:"除号",oslash:"带粗线的拉丁文小写字母 O",ugrave:"带抑音符的拉丁文小写字母 U",uacute:"带锐音符的拉丁文小写字母 U",ucirc:"带扬抑符的拉丁文小写字母 U",uuml:"带分音符的拉丁文小写字母 U",yacute:"带抑音符的拉丁文小写字母 Y",thorn:"拉丁文小写字母 Thorn",yuml:"带分音符的拉丁文小写字母 Y",OElig:"拉丁文大写连字 Oe",oelig:"拉丁文小写连字 Oe",372:"带扬抑符的拉丁文大写字母 W",374:"带扬抑符的拉丁文大写字母 Y", 9 | 373:"带扬抑符的拉丁文小写字母 W",375:"带扬抑符的拉丁文小写字母 Y",sbquo:"单下 9 形引号",8219:"单高横翻 9 形引号",bdquo:"双下 9 形引号",hellip:"水平省略号",trade:"商标标志",9658:"实心右指指针",bull:"加重号",rarr:"向右箭头",rArr:"向右双线箭头",hArr:"左右双线箭头",diams:"实心方块纸牌",asymp:"约等于"}); -------------------------------------------------------------------------------- /lib/app/portal/notifications.html: -------------------------------------------------------------------------------- 1 | 5 |
6 |
7 |
Your inbox is empty!
8 |
9 |
10 |
11 |
12 |
13 | {{n.subject}}
14 | {{n.created * 1000 | date:"yyyy.MM.dd - hh:mm a"}} 15 |
16 |
17 |
Read
18 |
Archive
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
You have no notifications.
30 |
31 |
32 |
33 |
34 |
35 | {{n.subject}}
36 | {{n.created * 1000 | date:"yyyy.MM.dd - hh:mm a"}} 37 |
38 |
39 |
Read
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
-------------------------------------------------------------------------------- /lib/api/models/Cycle/Role.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var util = require('util'); 4 | var Promise = require('bluebird'); 5 | var uuid = require('../../utils/uuid'); 6 | var errors = require('../../errors'); 7 | var EmbeddedModel = require('../../EmbeddedModel'); 8 | 9 | 10 | // Schema Validator 11 | // ---------------- 12 | 13 | var validator = require('jjv')(); 14 | validator.addSchema(require('../../schemas/cycle')); 15 | 16 | 17 | // EmbeddedModel Constructor 18 | // ------------------------- 19 | 20 | function Role (conn, data, parent) { 21 | return EmbeddedModel.call(this, conn, data, parent) 22 | .then(function(self) { 23 | 24 | // check authorizations 25 | if(!self.parent.authorizations['cycle/roles:read']) 26 | return Promise.reject(new errors.ForbiddenError()); 27 | 28 | // cycle_id 29 | self.cycle_id = self.parent.id; 30 | 31 | return self; 32 | }); 33 | } 34 | 35 | 36 | // EmbeddedModel Configuration 37 | // --------------------------- 38 | 39 | Role.key = 'roles'; 40 | Role.collections = {}; 41 | Role.validate = function(data) { 42 | return validator.validate('http://www.gandhi.io/schema/cycle#/definitions/role', data, {useDefault: true, removeAdditional: true}); 43 | }; 44 | Role.create = function(conn, data, parent) { 45 | 46 | if(!parent.authorizations['cycle/roles:write']) 47 | return Promise.reject(new errors.ForbiddenError()); 48 | 49 | // generate a new uuid 50 | data.id = uuid(); 51 | 52 | var err = Role.validate(data); 53 | if(err) return Promise.reject(new errors.ValidationError('The input is invalid.', err)); 54 | 55 | return new Role(conn, data, parent) 56 | .then(function(role) { 57 | return role.save(conn); 58 | }); 59 | }; 60 | 61 | 62 | // Public Methods 63 | // -------------- 64 | 65 | util.inherits(Role, EmbeddedModel); 66 | 67 | 68 | // check authorizations for update 69 | Role.prototype.update = function(conn, delta) { 70 | var self = this; 71 | 72 | if(!self.parent.authorizations['cycle/roles:write']) 73 | return Promise.reject(new errors.ForbiddenError()); 74 | 75 | return EmbeddedModel.prototype.update.call(self, conn, delta); 76 | }; 77 | 78 | // check authorizations for delete 79 | Role.prototype.delete = function(conn) { 80 | var self = this; 81 | 82 | if(!self.parent.authorizations['cycle/roles:write']) 83 | return Promise.reject(new errors.ForbiddenError()); 84 | 85 | return EmbeddedModel.prototype.delete.call(self, conn); 86 | }; 87 | 88 | 89 | module.exports = Role; 90 | -------------------------------------------------------------------------------- /test/init.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var async = require('async'); 5 | 6 | function setup(done){ 7 | 8 | // set the test environment 9 | process.env.NODE_ENV = 'test'; 10 | 11 | // don't setup twice 12 | if(global.setup){ 13 | global.setup.tests++; // increment the number of queued tests 14 | return done(); 15 | } 16 | 17 | global.setup = { 18 | tests: 1 // the number of queued tests 19 | }; 20 | 21 | // automatically load all init scripts 22 | require('fs').readdir(__dirname + '/init/', function(err, files){ 23 | if(err){ 24 | throw err; 25 | } 26 | 27 | var scripts = []; 28 | files.forEach(function(file){ 29 | if(file.indexOf('.js') === (file.length - 3)){ 30 | var script = require(__dirname + '/init/' + file); 31 | 32 | if(script.setup){ 33 | scripts.push(script.setup); 34 | } 35 | } 36 | }); 37 | 38 | global.setup.config = require('../config.test.js'); 39 | return async.parallel(scripts, function(setupErr){ 40 | if(setupErr){ 41 | var err = {setup: setupErr}; 42 | return teardown(function(teardownErr){ 43 | if(teardownErr){ 44 | err.teardown = teardownErr; 45 | } 46 | 47 | console.error(err); 48 | throw err; 49 | }); 50 | } 51 | 52 | return done(); 53 | }); 54 | }); 55 | } 56 | 57 | function teardown(done){ 58 | 59 | if(!done instanceof Function){ 60 | done = function(err){ if(err){ throw err; } }; 61 | } 62 | 63 | // decrement the number of queued tests 64 | global.setup.tests--; 65 | 66 | // don't teardown if we still have queued tests 67 | if(global.setup.tests){ 68 | return done(); 69 | } 70 | 71 | // automatically load all init scripts 72 | require('fs').readdir(__dirname + '/init/', function(err, files){ 73 | if(err){ 74 | throw err; 75 | } 76 | 77 | var scripts = []; 78 | files.forEach(function(file){ 79 | if(file.indexOf('.js') === (file.length - 3)){ 80 | var script = require(__dirname + '/init/' + file); 81 | 82 | if(script.teardown){ 83 | scripts.push(script.teardown); 84 | } 85 | } 86 | }); 87 | 88 | return async.parallel(scripts, done); 89 | }); 90 | } 91 | 92 | // setup the environment 93 | before(function(done){ 94 | this.timeout(60000); 95 | setup(done); 96 | }); 97 | 98 | // teardown the environment 99 | after(function(done){ 100 | this.timeout(60000); 101 | teardown(done); 102 | }); 103 | -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/build-config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license 4 | */ 5 | 6 | /** 7 | * This file was added automatically by CKEditor builder. 8 | * You may re-use it at any time to build CKEditor again. 9 | * 10 | * If you would like to build CKEditor online again 11 | * (for example to upgrade), visit one the following links: 12 | * 13 | * (1) http://ckeditor.com/builder 14 | * Visit online builder to build CKEditor from scratch. 15 | * 16 | * (2) http://ckeditor.com/builder/8a1d3eef208467a24eab16f3021f50e3 17 | * Visit online builder to build CKEditor, starting with the same setup as before. 18 | * 19 | * (3) http://ckeditor.com/builder/download/8a1d3eef208467a24eab16f3021f50e3 20 | * Straight download link to the latest version of CKEditor (Optimized) with the same setup as before. 21 | * 22 | * NOTE: 23 | * This file is not used by CKEditor, you may remove it. 24 | * Changing this file will not change your CKEditor configuration. 25 | */ 26 | 27 | var CKBUILDER_CONFIG = { 28 | skin: 'moono', 29 | preset: 'standard', 30 | ignore: [ 31 | '.bender', 32 | '.DS_Store', 33 | '.gitignore', 34 | '.gitattributes', 35 | '.idea', 36 | '.mailmap', 37 | 'bender.js', 38 | 'bender-err.log', 39 | 'bender-out.log', 40 | 'dev', 41 | 'node_modules', 42 | 'package.json', 43 | 'README.md', 44 | 'tests' 45 | ], 46 | plugins : { 47 | 'showprotected' : 1, 48 | 'a11yhelp' : 1, 49 | 'about' : 1, 50 | 'basicstyles' : 1, 51 | 'blockquote' : 1, 52 | 'clipboard' : 1, 53 | 'contextmenu' : 1, 54 | 'elementspath' : 1, 55 | 'enterkey' : 1, 56 | 'entities' : 1, 57 | 'filebrowser' : 1, 58 | 'floatingspace' : 1, 59 | 'format' : 1, 60 | 'horizontalrule' : 1, 61 | 'htmlwriter' : 1, 62 | 'image' : 1, 63 | 'indentlist' : 1, 64 | 'link' : 1, 65 | 'list' : 1, 66 | 'magicline' : 1, 67 | 'maximize' : 1, 68 | 'pastefromword' : 1, 69 | 'pastetext' : 1, 70 | 'removeformat' : 1, 71 | 'resize' : 1, 72 | 'scayt' : 1, 73 | 'showborders' : 1, 74 | 'sourcearea' : 1, 75 | 'specialchar' : 1, 76 | 'stylescombo' : 1, 77 | 'tab' : 1, 78 | 'table' : 1, 79 | 'tabletools' : 1, 80 | 'toolbar' : 1, 81 | 'undo' : 1, 82 | 'wordcount' : 1, 83 | 'wsc' : 1, 84 | 'wysiwygarea' : 1 85 | }, 86 | languages : { 87 | 'en' : 1 88 | } 89 | }; -------------------------------------------------------------------------------- /lib/api/models/Cycle/Status.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var util = require('util'); 4 | var Promise = require('bluebird'); 5 | var uuid = require('../../utils/uuid'); 6 | var errors = require('../../errors'); 7 | var EmbeddedModel = require('../../EmbeddedModel'); 8 | 9 | 10 | // Schema Validator 11 | // ---------------- 12 | 13 | var validator = require('jjv')(); 14 | validator.addSchema(require('../../schemas/cycle')); 15 | 16 | 17 | // EmbeddedModel Constructor 18 | // ------------------------- 19 | 20 | function Status (conn, data, parent) { 21 | return EmbeddedModel.call(this, conn, data, parent) 22 | .then(function(self) { 23 | 24 | // check authorizations 25 | if(!self.parent.authorizations['cycle/statuses:read']) 26 | return Promise.reject(new errors.ForbiddenError()); 27 | 28 | // cycle_id 29 | self.cycle_id = self.parent.id; 30 | 31 | return self; 32 | }); 33 | } 34 | 35 | 36 | // EmbeddedModel Configuration 37 | // --------------------------- 38 | 39 | Status.key = 'statuses'; 40 | Status.collections = {}; 41 | Status.validate = function(data) { 42 | return validator.validate('http://www.gandhi.io/schema/cycle#/definitions/status', data, {useDefault: true, removeAdditional: true}); 43 | }; 44 | Status.create = function(conn, data, parent) { 45 | 46 | if(!parent.authorizations['cycle/statuses:write']) 47 | return Promise.reject(new errors.ForbiddenError()); 48 | 49 | // generate a new uuid 50 | data.id = uuid(); 51 | 52 | var err = Status.validate(data); 53 | if(err) return Promise.reject(new errors.ValidationError('The input is invalid.', err)); 54 | 55 | return new Status(conn, data, parent) 56 | .then(function(status) { 57 | return status.save(conn); 58 | }); 59 | }; 60 | 61 | 62 | // Public Methods 63 | // -------------- 64 | 65 | util.inherits(Status, EmbeddedModel); 66 | 67 | 68 | // check authorizations for update 69 | Status.prototype.update = function(conn, delta) { 70 | var self = this; 71 | 72 | if(!self.parent.authorizations['cycle/statuses:write']) 73 | return Promise.reject(new errors.ForbiddenError()); 74 | 75 | return EmbeddedModel.prototype.update.call(self, conn, delta); 76 | }; 77 | 78 | // check authorizations for delete 79 | Status.prototype.delete = function(conn) { 80 | var self = this; 81 | 82 | if(!self.parent.authorizations['cycle/statuses:write']) 83 | return Promise.reject(new errors.ForbiddenError()); 84 | 85 | return EmbeddedModel.prototype.delete.call(self, conn); 86 | }; 87 | 88 | 89 | module.exports = Status; 90 | -------------------------------------------------------------------------------- /lib/app/portal/user/show.html: -------------------------------------------------------------------------------- 1 | 4 | 5 |
6 |
7 | {{user.name}} 8 |
9 | 10 | 11 |
12 |
13 |
14 |
15 |
16 | 17 |
18 |

{{user.id}}

19 |
20 |
21 |
22 | 23 |
24 |

{{user.name}}

25 | 26 |
27 |
28 |
29 | 30 |
31 |

{{user.email}}

32 | 33 |
34 |
35 |
36 | 37 |
38 |

(not shown)

39 | 40 |
41 |
42 |
43 | 44 |
45 |

{{user.created * 1000 | date:"yyyy.MM.dd - hh:mm a"}}

46 |
47 |
48 |
49 | 50 |
51 |

{{user.updated * 1000 | date:"yyyy.MM.dd - hh:mm a"}}

52 |
53 |
54 |
55 | 56 |
57 |
58 | -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/samples/tabindex.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | TAB Key-Based Navigation — CKEditor Sample 10 | 11 | 12 | 22 | 42 | 43 | 44 |

45 | CKEditor Samples » TAB Key-Based Navigation 46 |

47 |
48 |

49 | This sample shows how tab key navigation among editor instances is 50 | affected by the tabIndex attribute from 51 | the original page element. Use TAB key to move between the editors. 52 |

53 |
54 |

55 | 56 |

57 |
58 |

59 | 60 |

61 |

62 | 63 |

64 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /lib/api/models/Cycle/Assignment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var util = require('util'); 4 | var Promise = require('bluebird'); 5 | var errors = require('../../errors'); 6 | var EmbeddedModel = require('../../EmbeddedModel'); 7 | 8 | 9 | // Schema Validator 10 | // ---------------- 11 | 12 | var validator = require('jjv')(); 13 | validator.addSchema(require('../../schemas/cycle')); 14 | 15 | 16 | // EmbeddedModel Constructor 17 | // ------------------------- 18 | 19 | function Assignment (conn, data, parent) { 20 | return EmbeddedModel.call(this, conn, data, parent) 21 | .then(function(self) { 22 | 23 | // check authorizations 24 | if(self.parent.user !== true && self.parent.user.id !== self.id && !self.parent.authorizations['cycle/assignments:read']) 25 | return Promise.reject(new errors.ForbiddenError()); 26 | 27 | // cycle_id 28 | self.cycle_id = self.parent.id; 29 | 30 | return self; 31 | }); 32 | } 33 | 34 | 35 | // EmbeddedModel Configuration 36 | // --------------------------- 37 | 38 | Assignment.key = 'assignments'; 39 | Assignment.collections = {}; 40 | Assignment.validate = function(data) { 41 | return validator.validate('http://www.gandhi.io/schema/cycle#/definitions/assignment', data, {useDefault: true, removeAdditional: true}); 42 | }; 43 | Assignment.create = function(conn, data, parent) { 44 | 45 | if(!parent.authorizations['cycle/assignments:write']) 46 | return Promise.reject(new errors.ForbiddenError()); 47 | 48 | var err = Assignment.validate(data); 49 | if(err) return Promise.reject(new errors.ValidationError('The input is invalid.', err)); 50 | 51 | return new Assignment(conn, data, parent) 52 | .then(function(assignment) { 53 | return assignment.save(conn); 54 | }); 55 | }; 56 | 57 | 58 | // Public Methods 59 | // -------------- 60 | 61 | util.inherits(Assignment, EmbeddedModel); 62 | 63 | 64 | // check authorizations for update 65 | Assignment.prototype.update = function(conn, delta) { 66 | var self = this; 67 | 68 | if(!self.parent.authorizations['cycle/assignments:write']) 69 | return Promise.reject(new errors.ForbiddenError()); 70 | 71 | return EmbeddedModel.prototype.update.call(self, conn, delta); 72 | }; 73 | 74 | // check authorizations for delete 75 | Assignment.prototype.delete = function(conn) { 76 | var self = this; 77 | 78 | if(!self.parent.authorizations['cycle/assignments:write']) 79 | return Promise.reject(new errors.ForbiddenError()); 80 | 81 | return EmbeddedModel.prototype.delete.call(self, conn); 82 | }; 83 | 84 | 85 | module.exports = Assignment; 86 | -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/plugins/a11yhelp/dialogs/lang/zh-cn.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved. 3 | For licensing, see LICENSE.md or http://ckeditor.com/license 4 | */ 5 | CKEDITOR.plugins.setLang("a11yhelp","zh-cn",{title:"辅助功能说明",contents:"帮助内容。要关闭此对话框请按 ESC 键。",legend:[{name:"常规",items:[{name:"编辑器工具栏",legend:"按 ${toolbarFocus} 导航到工具栏,使用 TAB 键和 SHIFT+TAB 组合键移动到上一个和下一个工具栏组。使用左右箭头键移动到上一个和下一个工具栏按钮。按空格键或回车键以选中工具栏按钮。"},{name:"编辑器对话框",legend:"在对话框内,TAB 键移动到下一个字段,SHIFT + TAB 组合键移动到上一个字段,ENTER 键提交对话框,ESC 键取消对话框。对于有多选项卡的对话框,用ALT + F10来移到选项卡列表。然后用 TAB 键或者向右箭头来移动到下一个选项卡;SHIFT + TAB 组合键或者向左箭头移动到上一个选项卡。用 SPACE 键或者 ENTER 键选择选项卡。"},{name:"编辑器上下文菜单",legend:"用 ${contextMenu} 或者“应用程序键”打开上下文菜单。然后用 TAB 键或者下箭头键来移动到下一个菜单项;SHIFT + TAB 组合键或者上箭头键移动到上一个菜单项。用 SPACE 键或者 ENTER 键选择菜单项。用 SPACE 键,ENTER 键或者右箭头键打开子菜单。返回菜单用 ESC 键或者左箭头键。用 ESC 键关闭上下文菜单。"}, 6 | {name:"编辑器列表框",legend:"在列表框中,移到下一列表项用 TAB 键或者下箭头键。移到上一列表项用SHIFT + TAB 组合键或者上箭头键,用 SPACE 键或者 ENTER 键选择列表项。用 ESC 键收起列表框。"},{name:"编辑器元素路径栏",legend:"按 ${elementsPathFocus} 以导航到元素路径栏,使用 TAB 键或右箭头键选择下一个元素,使用 SHIFT+TAB 组合键或左箭头键选择上一个元素,按空格键或回车键以选定编辑器里的元素。"}]},{name:"命令",items:[{name:" 撤消命令",legend:"按 ${undo}"},{name:" 重做命令",legend:"按 ${redo}"},{name:" 加粗命令",legend:"按 ${bold}"},{name:" 倾斜命令",legend:"按 ${italic}"},{name:" 下划线命令",legend:"按 ${underline}"},{name:" 链接命令",legend:"按 ${link}"},{name:" 工具栏折叠命令",legend:"按 ${toolbarCollapse}"}, 7 | {name:"访问前一个焦点区域的命令",legend:"按 ${accessPreviousSpace} 访问^符号前最近的不可访问的焦点区域,例如:两个相邻的 HR 元素。重复此组合按键可以到达远处的焦点区域。"},{name:"访问下一个焦点区域命令",legend:"按 ${accessNextSpace} 以访问^符号后最近的不可访问的焦点区域。例如:两个相邻的 HR 元素。重复此组合按键可以到达远处的焦点区域。"},{name:"辅助功能帮助",legend:"按 ${a11yHelp}"}]}],backspace:"退格键",tab:"Tab 键",enter:"回车键",shift:"Shift 键",ctrl:"Ctrl 键",alt:"Alt 键",pause:"暂停键",capslock:"大写锁定键",escape:"Esc 键",pageUp:"上翻页键",pageDown:"下翻页键",end:"行尾键",home:"行首键",leftArrow:"向左箭头键",upArrow:"向上箭头键",rightArrow:"向右箭头键",downArrow:"向下箭头键", 8 | insert:"插入键","delete":"删除键",leftWindowKey:"左 WIN 键",rightWindowKey:"右 WIN 键",selectKey:"选择键",numpad0:"小键盘 0 键",numpad1:"小键盘 1 键",numpad2:"小键盘 2 键",numpad3:"小键盘 3 键",numpad4:"小键盘 4 键",numpad5:"小键盘 5 键",numpad6:"小键盘 6 键",numpad7:"小键盘 7 键",numpad8:"小键盘 8 键",numpad9:"小键盘 9 键",multiply:"星号键",add:"加号键",subtract:"减号键",decimalPoint:"小数点键",divide:"除号键",f1:"F1 键",f2:"F2 键",f3:"F3 键",f4:"F4 键",f5:"F5 键",f6:"F6 键",f7:"F7 键",f8:"F8 键",f9:"F9 键",f10:"F10 键",f11:"F11 键",f12:"F12 键",numLock:"数字锁定键",scrollLock:"滚动锁定键", 9 | semiColon:"分号键",equalSign:"等号键",comma:"逗号键",dash:"短划线键",period:"句号键",forwardSlash:"斜杠键",graveAccent:"重音符键",openBracket:"左中括号键",backSlash:"反斜杠键",closeBracket:"右中括号键",singleQuote:"单引号键"}); -------------------------------------------------------------------------------- /lib/modules/gandhi-component-schedule/app/default.html: -------------------------------------------------------------------------------- 1 |
Loading...
2 | 3 |
4 | 5 | 8 | 9 | 12 | 13 |
14 | 15 |
16 |
17 |
18 | {{cycle.title}} 19 |
20 |
21 |
22 |
23 |
24 |
25 | 26 |
27 |
28 |
29 | {{stage.title}} - {{content.status_id | title}} 30 |
31 |
32 | 33 |
34 | 35 | 36 | 37 | 38 | 39 | 48 | 49 |
{{group[0].begin | date:'EEEE, MMMM d'}}
40 | 47 |
50 |
51 | 52 |
53 |
54 | 55 |
56 | 57 |
58 |
59 |
60 | 61 |
62 |
-------------------------------------------------------------------------------- /lib/api/models/Cycle/Invitation.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var util = require('util'); 4 | var Promise = require('bluebird'); 5 | var uuid = require('../../utils/uuid'); 6 | var errors = require('../../errors'); 7 | var EmbeddedModel = require('../../EmbeddedModel'); 8 | 9 | 10 | // Schema Validator 11 | // ---------------- 12 | 13 | var validator = require('jjv')(); 14 | validator.addSchema(require('../../schemas/cycle')); 15 | 16 | 17 | // EmbeddedModel Constructor 18 | // ------------------------- 19 | 20 | function Invitation (conn, data, parent) { 21 | return EmbeddedModel.call(this, conn, data, parent) 22 | .then(function(self) { 23 | 24 | // check authorizations 25 | if(!self.parent.authorizations['cycle/invitations:read']) 26 | return Promise.reject(new errors.ForbiddenError()); 27 | 28 | // cycle_id 29 | self.cycle_id = self.parent.id; 30 | 31 | return self; 32 | }); 33 | } 34 | 35 | 36 | // EmbeddedModel Configuration 37 | // --------------------------- 38 | 39 | Invitation.key = 'invitations'; 40 | Invitation.collections = {}; 41 | Invitation.validate = function(data) { 42 | return validator.validate('http://www.gandhi.io/schema/cycle#/definitions/invitation', data, {useDefault: true, removeAdditional: true}); 43 | }; 44 | Invitation.create = function(conn, data, parent) { 45 | 46 | if(!parent.authorizations['cycle/invitations:write']) 47 | return Promise.reject(new errors.ForbiddenError()); 48 | 49 | // generate a new uuid 50 | data.id = uuid(); 51 | 52 | var err = Invitation.validate(data); 53 | if(err) return Promise.reject(new errors.ValidationError('The input is invalid.', err)); 54 | 55 | return new Invitation(conn, data, parent) 56 | .then(function(invitation) { 57 | return invitation.save(conn); 58 | }); 59 | }; 60 | 61 | 62 | // Public Methods 63 | // -------------- 64 | 65 | util.inherits(Invitation, EmbeddedModel); 66 | 67 | 68 | // check authorizations for update 69 | Invitation.prototype.update = function(conn, delta) { 70 | var self = this; 71 | 72 | if(!self.parent.authorizations['cycle/invitations:write']) 73 | return Promise.reject(new errors.ForbiddenError()); 74 | 75 | return EmbeddedModel.prototype.update.call(self, conn, delta); 76 | }; 77 | 78 | // check authorizations for delete 79 | Invitation.prototype.delete = function(conn) { 80 | var self = this; 81 | 82 | if(!self.parent.authorizations['cycle/invitations:write']) 83 | return Promise.reject(new errors.ForbiddenError()); 84 | 85 | return EmbeddedModel.prototype.delete.call(self, conn); 86 | }; 87 | 88 | 89 | module.exports = Invitation; 90 | -------------------------------------------------------------------------------- /lib/modules/gandhi-decorator-elastic-textarea/index.js: -------------------------------------------------------------------------------- 1 | angular.module('gandhi-decorator-elastic-textarea', ['schemaForm']) 2 | 3 | // This is a modified version of this: 4 | // https://github.com/json-schema-form/angular-schema-form-bootstrap/blob/0.2.0/src/textarea.html 5 | .run(['$templateCache', function(templateCache) { 6 | templateCache.put( 7 | 'modules/gandhi-decorator-elastic-textarea/index.html', 8 | ` 9 |
10 | 11 | 12 | 22 | 23 |
25 | 28 | 37 | 40 |
41 | 42 | 43 |
44 | `); 45 | }]) 46 | 47 | .config(['schemaFormDecoratorsProvider', 'sfBuilderProvider', function(schemaFormDecoratorsProvider, sfBuilderProvider){ 48 | schemaFormDecoratorsProvider.defineAddOn( 49 | 'bootstrapDecorator', 50 | 'elastic-textarea', 51 | 'modules/gandhi-decorator-elastic-textarea/index.html', 52 | sfBuilderProvider.stdBuilders 53 | ); 54 | }]); -------------------------------------------------------------------------------- /lib/api/collections/Files.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var r = require('rethinkdb'); 4 | var Promise = require('bluebird'); 5 | var errors = require('../errors'); 6 | 7 | var File = require('../models/File'); 8 | 9 | function Files(){} 10 | 11 | Files.prototype.query = function(conn, query, user) { 12 | 13 | // Filter Files 14 | // -------------------- 15 | 16 | var files = r.table('files'); 17 | 18 | // OPTIMIZATION: restrict to own files for non-admin users 19 | if(user !== true) 20 | files = files.filter({user_id: user.id}); 21 | 22 | // restrict to user 23 | if(typeof query.userId === 'string') 24 | files = files.filter({user_id: query.userId}); 25 | 26 | // filter 27 | if(query.filter) query.filter.forEach(function(f){ 28 | files = files.filter(f); 29 | }); 30 | 31 | // search 32 | if(typeof query.search === 'string' && query.search.length) 33 | files = files.filter(r.or( 34 | r.row('name').downcase().match(query.search.toLowerCase()), 35 | r.row('email').downcase().match(query.search.toLowerCase()) 36 | )); 37 | 38 | 39 | // Build Result 40 | // ------------ 41 | 42 | var result = files; 43 | 44 | // sort 45 | if(query.sort) 46 | result = result.orderBy.apply(result, query.sort); 47 | 48 | // skip 49 | if(query.skip) 50 | result = result.skip(query.skip); 51 | 52 | // limit 53 | if(query.limit) 54 | result = result.limit(query.limit); 55 | 56 | return r.expr({ 57 | 58 | // get the total results count 59 | total: files.count(), 60 | 61 | // get the processed files 62 | files: result.coerceTo('array') 63 | 64 | }).run(conn) 65 | 66 | // return as an array 67 | .then(function(results){ 68 | return Promise.filter(results.files.map(function(data){ 69 | return new File(conn, data, user) 70 | .catch(function(err){ 71 | 72 | // suppress ForbiddenError 73 | if(err instanceof errors.ForbiddenError) 74 | return null; 75 | 76 | // re-throw all other errors 77 | return Promise.reject(err); 78 | }); 79 | }), function(c){ return !!c; }) 80 | .then(function(files){ 81 | files.total = results.total; 82 | return files; 83 | }); 84 | }); 85 | }; 86 | 87 | 88 | Files.prototype.get = function(conn, id, user) { 89 | return r.table('files').get(id).run(conn).then(function(data) { 90 | if(!data) return Promise.reject(new errors.NotFoundError()); 91 | return new File(conn, data, user); 92 | }); 93 | }; 94 | 95 | 96 | Files.prototype.create = function(conn, data, user) { 97 | return File.create(conn, data, user); 98 | }; 99 | 100 | 101 | module.exports = Files; 102 | -------------------------------------------------------------------------------- /lib/app/assets/ckeditor/plugins/a11yhelp/dialogs/lang/zh.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved. 3 | For licensing, see LICENSE.md or http://ckeditor.com/license 4 | */ 5 | CKEDITOR.plugins.setLang("a11yhelp","zh",{title:"輔助工具指南",contents:"說明內容。若要關閉此對話框請按「ESC」。",legend:[{name:"一般",items:[{name:"編輯器工具列",legend:"請按「${toolbarFocus}」以瀏覽工具列。\r\n利用「TAB」或「SHIFT+TAB」以便移動到下一個或前一個工具列群組。\r\n利用「→」或「←」以便移動到下一個或前一個工具列按鈕。\r\n請按下「空白鍵」或「ENTER」鍵啟動工具列按鈕。"},{name:"編輯器對話方塊",legend:"在對話框中,請按 TAB 鍵以便移動到下個欄位,請按 SHIFT + TAB 以便移動到前個欄位;請按 ENTER 以提交對話框資料,或按下 ESC 取消對話框。\r\n若是有多個頁框的對話框,請按 ALT + F10 以移動到頁框列表,並以 TAB 或是 → 方向鍵移動到下個頁框。以 SHIFT + TAB 或是 ← 方向鍵移動到前個頁框。按下 空白鍵 或是 ENTER 以選取頁框。"},{name:"編輯器內容功能表", 6 | legend:"請按下「${contextMenu}」或是「應用程式鍵」以開啟內容選單。以「TAB」或是「↓」鍵移動到下一個選單選項。以「SHIFT + TAB」或是「↑」鍵移動到上一個選單選項。按下「空白鍵」或是「ENTER」鍵以選取選單選項。以「空白鍵」或「ENTER」或「→」開啟目前選項之子選單。以「ESC」或「←」回到父選單。以「ESC」鍵關閉內容選單」。"},{name:"編輯器清單方塊",legend:"在列表中,請利用 TAB 或 ↓ 方向鍵以移動到下一個項目;或利用 SHIFT + TAB 或 ↑ 方向鍵移動到前一個項目。請按下 空白鍵 或是 ENTER 以選取項目。請按 ESC 關閉列表。"},{name:"編輯器元件路徑工具列",legend:"請按「${elementsPathFocus}」以瀏覽元素路徑工具列。\r\n利用「TAB」或「→」以便移動到下一個元素按鈕。\r\n利用「SHIFT+TAB」或「←」以便移動到前一個元素按鈕。\r\n請按下「空白鍵」或「ENTER」鍵選擇編輯器中的元素。"}]},{name:"命令",items:[{name:"復原命令", 7 | legend:"請按下「${undo}」"},{name:"重複命令",legend:"請按下「 ${redo}」"},{name:"粗體命令",legend:"請按下「${bold}」"},{name:"斜體",legend:"請按下「${italic}」"},{name:"底線命令",legend:"請按下「${underline}」"},{name:"連結",legend:"請按下「${link}」"},{name:"隱藏工具列",legend:"請按下「${toolbarCollapse}」"},{name:"存取前一個焦點空間命令",legend:"請按下 ${accessPreviousSpace} 以存取最近但無法靠近之插字符號前的焦點空間。舉例:二個相鄰的 HR 元素。\r\n重複按鍵以存取較遠的焦點空間。"},{name:"存取下一個焦點空間命令",legend:"請按下 ${accessNextSpace} 以存取最近但無法靠近之插字符號後的焦點空間。舉例:二個相鄰的 HR 元素。\r\n重複按鍵以存取較遠的焦點空間。"},{name:"協助工具說明",legend:"請按下「${a11yHelp}」"}]}], 8 | backspace:"退格鍵",tab:"Tab",enter:"Enter",shift:"Shift",ctrl:"Ctrl",alt:"Alt",pause:"Pause",capslock:"Caps Lock",escape:"Escape",pageUp:"Page Up",pageDown:"Page Down",end:"End",home:"Home",leftArrow:"向左箭號",upArrow:"向上鍵號",rightArrow:"向右鍵號",downArrow:"向下鍵號",insert:"插入","delete":"刪除",leftWindowKey:"Left Windows key",rightWindowKey:"Right Windows key",selectKey:"Select key",numpad0:"Numpad 0",numpad1:"Numpad 1",numpad2:"Numpad 2",numpad3:"Numpad 3",numpad4:"Numpad 4",numpad5:"Numpad 5",numpad6:"Numpad 6", 9 | numpad7:"Numpad 7",numpad8:"Numpad 8",numpad9:"Numpad 9",multiply:"Multiply",add:"新增",subtract:"Subtract",decimalPoint:"Decimal Point",divide:"Divide",f1:"F1",f2:"F2",f3:"F3",f4:"F4",f5:"F5",f6:"F6",f7:"F7",f8:"F8",f9:"F9",f10:"F10",f11:"F11",f12:"F12",numLock:"Num Lock",scrollLock:"Scroll Lock",semiColon:"Semicolon",equalSign:"等號",comma:"逗號",dash:"虛線",period:"句點",forwardSlash:"斜線",graveAccent:"抑音符號",openBracket:"左方括號",backSlash:"反斜線",closeBracket:"右方括號",singleQuote:"單引號"}); -------------------------------------------------------------------------------- /lib/app/portal/admin/cycles/show.statuses.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | Create Status 4 |
5 | 6 | 7 |
8 |
9 |
10 |
11 |
12 | 13 |
14 | 15 |
16 |
17 |
18 |
19 |
20 | 21 |
22 |
23 | Statuses 24 |
25 | 26 |
27 |
28 | 29 |
30 | No statuses. Create one. 31 |
32 | 33 |
34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 47 | 51 | 56 | 57 | 58 |
IDTitle
45 |

{{status.id}}

46 |
48 |

{{status.title}}

49 | 50 |
52 | 53 | 54 | 55 |
59 |
60 |
-------------------------------------------------------------------------------- /lib/app/assets/ckeditor/samples/uicolor.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | UI Color Picker — CKEditor Sample 10 | 11 | 12 | 13 | 14 |

15 | CKEditor Samples » UI Color 16 |

17 |
18 |

19 | This sample shows how to automatically replace <textarea> elements 20 | with a CKEditor instance with an option to change the color of its user interface.
21 | Note:The UI skin color feature depends on the CKEditor skin 22 | compatibility. The Moono and Kama skins are examples of skins that work with it. 23 |

24 |
25 |
26 |

27 | This editor instance has a UI color value defined in configuration to change the skin color, 28 | To specify the color of the user interface, set the uiColor property: 29 |

30 |
31 | CKEDITOR.replace( 'textarea_id', {
32 | 	uiColor: '#14B8C4'
33 | });
34 |

35 | Note that textarea_id in the code above is the id attribute of 36 | the <textarea> element to be replaced. 37 |

38 |

39 | 40 | 53 |

54 |

55 | 56 |

57 |
58 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /lib/app/portal/cycles/show.html: -------------------------------------------------------------------------------- 1 | 5 | 6 |

{{cycle.title}}

7 |
8 | 9 | 13 |
14 |
15 | You have no assigned projects in this cycle. 16 | There are no projects in this cycle. 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
TitleRoleStatusCreatedUpdated
{{project.title}}{{project.role.title}}{{project.status.title}}{{project.created * 1000 | date:"yyyy.MM.dd - hh:mm a"}}{{project.updated * 1000 | date:"yyyy.MM.dd - hh:mm a"}}
38 |
39 | 40 | 41 | 42 |
43 | 44 |
45 |
46 |
47 | {{cycle.title}} 48 |
49 |
50 |
51 |
52 |
53 |
54 | 55 |
56 |
57 |
58 | New Project 59 |
60 |
61 |
This cycle is not open.
62 |
63 |
64 | 65 | 66 |
67 | 68 | 69 |
70 | 71 |
72 |
73 |
74 | 75 |
76 | -------------------------------------------------------------------------------- /lib/app/portal/projects/show.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 |
9 |
10 | 20 |
21 |
22 | 23 |
24 |
25 |
26 |
27 |
28 | 29 |
30 |

{{project.id}}

31 |
32 |
33 |
34 | 35 |
36 |

{{project.title}}

37 |
38 |
39 |
40 | 41 |
42 |

{{cycle.title}}

43 |
44 |
45 |
46 | 47 |
48 |

{{statusesById[project.status_id].title || project.status_id}}

49 |
50 |
51 |
52 | 53 |
54 |

{{project.created * 1000 | date:"yyyy.MM.dd - hh:mm a"}}

55 |
56 |
57 |
58 | 59 |
60 |

{{project.updated * 1000 | date:"yyyy.MM.dd - hh:mm a"}}

61 |
62 |
63 |
64 |
65 |
66 | 67 |
-------------------------------------------------------------------------------- /lib/app/assets/ckeditor/skins/moono/readme.md: -------------------------------------------------------------------------------- 1 | "Moono" Skin 2 | ==================== 3 | 4 | This skin has been chosen for the **default skin** of CKEditor 4.x, elected from the CKEditor 5 | [skin contest](http://ckeditor.com/blog/new_ckeditor_4_skin) and further shaped by 6 | the CKEditor team. "Moono" is maintained by the core developers. 7 | 8 | For more information about skins, please check the [CKEditor Skin SDK](http://docs.cksource.com/CKEditor_4.x/Skin_SDK) 9 | documentation. 10 | 11 | Features 12 | ------------------- 13 | "Moono" is a monochromatic skin, which offers a modern look coupled with gradients and transparency. 14 | It comes with the following features: 15 | 16 | - Chameleon feature with brightness, 17 | - high-contrast compatibility, 18 | - graphics source provided in SVG. 19 | 20 | Directory Structure 21 | ------------------- 22 | 23 | CSS parts: 24 | - **editor.css**: the main CSS file. It's simply loading several other files, for easier maintenance, 25 | - **mainui.css**: the file contains styles of entire editor outline structures, 26 | - **toolbar.css**: the file contains styles of the editor toolbar space (top), 27 | - **richcombo.css**: the file contains styles of the rich combo ui elements on toolbar, 28 | - **panel.css**: the file contains styles of the rich combo drop-down, it's not loaded 29 | until the first panel open up, 30 | - **elementspath.css**: the file contains styles of the editor elements path bar (bottom), 31 | - **menu.css**: the file contains styles of all editor menus including context menu and button drop-down, 32 | it's not loaded until the first menu open up, 33 | - **dialog.css**: the CSS files for the dialog UI, it's not loaded until the first dialog open, 34 | - **reset.css**: the file defines the basis of style resets among all editor UI spaces, 35 | - **preset.css**: the file defines the default styles of some UI elements reflecting the skin preference, 36 | - **editor_XYZ.css** and **dialog_XYZ.css**: browser specific CSS hacks. 37 | 38 | Other parts: 39 | - **skin.js**: the only JavaScript part of the skin that registers the skin, its browser specific files and its icons and defines the Chameleon feature, 40 | - **icons/**: contains all skin defined icons, 41 | - **images/**: contains a fill general used images, 42 | - **dev/**: contains SVG source of the skin icons. 43 | 44 | License 45 | ------- 46 | 47 | Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved. 48 | 49 | Licensed under the terms of any of the following licenses at your choice: [GPL](http://www.gnu.org/licenses/gpl.html), [LGPL](http://www.gnu.org/licenses/lgpl.html) and [MPL](http://www.mozilla.org/MPL/MPL-1.1.html). 50 | 51 | See LICENSE.md for more information. 52 | --------------------------------------------------------------------------------