├── .bowerrc
├── .editorconfig
├── .gitignore
├── .jshintrc
├── .travis.yml
├── CONTRIBUTING.md
├── Gruntfile.js
├── LICENSE
├── README.md
├── api-stub
├── README.md
└── routes.js
├── app
├── adapters
│ ├── application.js
│ ├── content-type.js
│ ├── control-type-group.js
│ ├── control-type.js
│ └── redirect.js
├── app.js
├── components
│ ├── .gitkeep
│ ├── audio-player.js
│ ├── audio-upload.js
│ ├── auto-complete.js
│ ├── colored-element.js
│ ├── date-time.js
│ ├── disqus-embed.js
│ ├── download-backup.js
│ ├── embedly-control.js
│ ├── file-upload.js
│ ├── gallery-upload.js
│ ├── geolocation-control.js
│ ├── grid-control.js
│ ├── image-upload.js
│ ├── markdown-editor.js
│ ├── pretty-color.js
│ ├── redactor-rte.js
│ ├── select-file.js
│ ├── tabular-data.js
│ ├── template-less.js
│ ├── webhook-blog.js
│ └── webhook-modal.js
├── controllers
│ ├── .gitkeep
│ ├── confirm-email.js
│ ├── create-user.js
│ ├── form.js
│ ├── login.js
│ ├── password-reset.js
│ ├── reindex.js
│ ├── resend-email.js
│ ├── theme.js
│ ├── wh.js
│ ├── wh
│ │ ├── content
│ │ │ ├── all-types.js
│ │ │ ├── start.js
│ │ │ └── type
│ │ │ │ ├── edit.js
│ │ │ │ ├── index.js
│ │ │ │ ├── json.js
│ │ │ │ └── new.js
│ │ ├── index.js
│ │ ├── search-global-results.js
│ │ └── settings
│ │ │ ├── data.js
│ │ │ ├── domain.js
│ │ │ ├── general.js
│ │ │ ├── password-change.js
│ │ │ ├── team.js
│ │ │ └── urls.js
│ └── wordpress.js
├── helpers
│ ├── .gitkeep
│ ├── format-number.js
│ ├── format-time.js
│ ├── resize-image.js
│ ├── reverse-word.js
│ ├── to-markdown.js
│ ├── translate-control.js
│ └── truncate-string.js
├── index.html
├── initializers
│ ├── analytics.js
│ ├── environment.js
│ ├── session.js
│ ├── team.js
│ └── tracker.js
├── models
│ ├── .gitkeep
│ ├── content-type.js
│ ├── control-type-group.js
│ ├── control-type.js
│ ├── control.js
│ ├── data.js
│ ├── group.js
│ ├── item.js
│ ├── redirect.js
│ ├── settings.js
│ └── user.js
├── router.js
├── routes
│ ├── .gitkeep
│ ├── application.js
│ ├── component_test.js
│ ├── confirm-email.js
│ ├── create-user.js
│ ├── form.js
│ ├── helper_test.js
│ ├── import.js
│ ├── index.js
│ ├── login.js
│ ├── password-change.js
│ ├── reindex.js
│ ├── resend-email.js
│ ├── start.js
│ ├── theme.js
│ ├── wh.js
│ ├── wh
│ │ ├── content
│ │ │ ├── all-types.js
│ │ │ ├── start.js
│ │ │ ├── type.js
│ │ │ └── type
│ │ │ │ ├── edit.js
│ │ │ │ ├── index.js
│ │ │ │ ├── json.js
│ │ │ │ └── new.js
│ │ ├── index.js
│ │ ├── search-global-results.js
│ │ └── settings
│ │ │ ├── data.js
│ │ │ ├── domain.js
│ │ │ ├── general.js
│ │ │ ├── team.js
│ │ │ └── urls.js
│ └── wordpress.js
├── serializers
│ ├── application.js
│ ├── control-type-group.js
│ ├── control-type.js
│ ├── control.js
│ ├── data.js
│ ├── item.js
│ └── settings.js
├── styles
│ ├── .gitkeep
│ ├── _app_codemirror.sass
│ ├── _app_content_list.sass
│ ├── _app_font_icons.sass
│ ├── _app_fonts.sass
│ ├── _app_form_builder.sass
│ ├── _app_form_controls.sass
│ ├── _app_form_gallery.sass
│ ├── _app_form_markdown.sass
│ ├── _app_grid.sass
│ ├── _app_groups.sass
│ ├── _app_layout.sass
│ ├── _app_mixin.sass
│ ├── _app_variables.sass
│ └── app.sass
├── templates
│ ├── .gitkeep
│ ├── application.hbs
│ ├── component-test.hbs
│ ├── components
│ │ ├── .gitkeep
│ │ ├── audio-player.hbs
│ │ ├── audio-upload.hbs
│ │ ├── auto-complete.hbs
│ │ ├── colored-element.hbs
│ │ ├── date-time.hbs
│ │ ├── download-backup.hbs
│ │ ├── embedly-control.hbs
│ │ ├── file-upload.hbs
│ │ ├── formbuilder-widget.hbs
│ │ ├── gallery-upload.hbs
│ │ ├── geolocation-control.hbs
│ │ ├── grid-control.hbs
│ │ ├── image-upload.hbs
│ │ ├── markdown-editor.hbs
│ │ ├── pretty-color.hbs
│ │ ├── redactor-rte.hbs
│ │ ├── tabular-data.hbs
│ │ ├── webhook-blog.hbs
│ │ └── webhook-modal.hbs
│ ├── confirm-email.hbs
│ ├── create-user.hbs
│ ├── error.hbs
│ ├── expired.hbs
│ ├── form.hbs
│ ├── form
│ │ ├── _changedcontrols.hbs
│ │ ├── _initialscaffolding.hbs
│ │ └── _nav.hbs
│ ├── grid-row.hbs
│ ├── helper-test.hbs
│ ├── import.hbs
│ ├── index.hbs
│ ├── loading.hbs
│ ├── login.hbs
│ ├── password-reset.hbs
│ ├── reindex.hbs
│ ├── resend-email.hbs
│ ├── start.hbs
│ ├── theme.hbs
│ ├── wh.hbs
│ ├── wh
│ │ ├── _nav.hbs
│ │ ├── content
│ │ │ ├── all-types.hbs
│ │ │ ├── start.hbs
│ │ │ └── type
│ │ │ │ ├── edit.hbs
│ │ │ │ ├── index.hbs
│ │ │ │ ├── json.hbs
│ │ │ │ └── loading.hbs
│ │ ├── index.hbs
│ │ ├── search-global-results.hbs
│ │ └── settings
│ │ │ ├── billing.hbs
│ │ │ ├── data.hbs
│ │ │ ├── domain.hbs
│ │ │ ├── general.hbs
│ │ │ ├── group-permissions.hbs
│ │ │ ├── password-change.hbs
│ │ │ ├── team.hbs
│ │ │ ├── urls-rule.hbs
│ │ │ └── urls.hbs
│ ├── widgets
│ │ ├── _address.hbs
│ │ ├── _audio.hbs
│ │ ├── _boolean.hbs
│ │ ├── _checkbox.hbs
│ │ ├── _color.hbs
│ │ ├── _datetime.hbs
│ │ ├── _email.hbs
│ │ ├── _embedly.hbs
│ │ ├── _file.hbs
│ │ ├── _gallery.hbs
│ │ ├── _geolocation.hbs
│ │ ├── _grid.hbs
│ │ ├── _image.hbs
│ │ ├── _instruction.hbs
│ │ ├── _layout.hbs
│ │ ├── _markdown.hbs
│ │ ├── _name.hbs
│ │ ├── _number.hbs
│ │ ├── _phone.hbs
│ │ ├── _radio.hbs
│ │ ├── _rating.hbs
│ │ ├── _relation.hbs
│ │ ├── _select.hbs
│ │ ├── _tabular.hbs
│ │ ├── _tag.hbs
│ │ ├── _textarea.hbs
│ │ ├── _textfield.hbs
│ │ ├── _url.hbs
│ │ ├── _wysiwyg.hbs
│ │ ├── common
│ │ │ ├── _help.hbs
│ │ │ ├── _imagemodal.hbs
│ │ │ ├── _label.hbs
│ │ │ └── _minmaxchars.hbs
│ │ ├── info
│ │ │ ├── _address.hbs
│ │ │ ├── _audio.hbs
│ │ │ ├── _boolean.hbs
│ │ │ ├── _checkbox.hbs
│ │ │ ├── _color.hbs
│ │ │ ├── _datetime.hbs
│ │ │ ├── _email.hbs
│ │ │ ├── _embedly.hbs
│ │ │ ├── _file.hbs
│ │ │ ├── _gallery.hbs
│ │ │ ├── _geolocation.hbs
│ │ │ ├── _grid.hbs
│ │ │ ├── _image.hbs
│ │ │ ├── _instruction.hbs
│ │ │ ├── _layout.hbs
│ │ │ ├── _markdown.hbs
│ │ │ ├── _name.hbs
│ │ │ ├── _number.hbs
│ │ │ ├── _phone.hbs
│ │ │ ├── _radio.hbs
│ │ │ ├── _rating.hbs
│ │ │ ├── _relation.hbs
│ │ │ ├── _select.hbs
│ │ │ ├── _tabular.hbs
│ │ │ ├── _tag.hbs
│ │ │ ├── _textarea.hbs
│ │ │ ├── _textfield.hbs
│ │ │ ├── _url.hbs
│ │ │ └── _wysiwyg.hbs
│ │ ├── relation
│ │ │ ├── few.hbs
│ │ │ └── many.hbs
│ │ └── value
│ │ │ ├── _address.hbs
│ │ │ ├── _audio.hbs
│ │ │ ├── _boolean.hbs
│ │ │ ├── _checkbox.hbs
│ │ │ ├── _color.hbs
│ │ │ ├── _datetime.hbs
│ │ │ ├── _email.hbs
│ │ │ ├── _embedly.hbs
│ │ │ ├── _file.hbs
│ │ │ ├── _gallery.hbs
│ │ │ ├── _geolocation.hbs
│ │ │ ├── _grid.hbs
│ │ │ ├── _image.hbs
│ │ │ ├── _instruction.hbs
│ │ │ ├── _layout.hbs
│ │ │ ├── _markdown.hbs
│ │ │ ├── _name.hbs
│ │ │ ├── _number.hbs
│ │ │ ├── _phone.hbs
│ │ │ ├── _radio.hbs
│ │ │ ├── _rating.hbs
│ │ │ ├── _relation.hbs
│ │ │ ├── _select.hbs
│ │ │ ├── _tabular.hbs
│ │ │ ├── _tag.hbs
│ │ │ ├── _textarea.hbs
│ │ │ ├── _textfield.hbs
│ │ │ ├── _url.hbs
│ │ │ ├── _wysiwyg.hbs
│ │ │ └── relation-item.hbs
│ └── wordpress.hbs
├── transforms
│ └── json.js
├── utils
│ ├── .gitkeep
│ ├── ajax.js
│ ├── controls.js
│ ├── downcode.js
│ ├── meta-options.js
│ ├── search-index.js
│ ├── slugger.js
│ ├── uuid.js
│ └── validators.js
└── views
│ ├── .gitkeep
│ ├── animate-collection.js
│ ├── auto-complete.js
│ ├── autocomplete-results.js
│ ├── checkbox-control.js
│ ├── draggable.js
│ ├── error.js
│ ├── fluidbox.js
│ ├── form.js
│ ├── formbuilder-grid.js
│ ├── formbuilder-widget-grid.js
│ ├── formbuilder-widget.js
│ ├── formbuilder.js
│ ├── gallery.js
│ ├── grid-rows.js
│ ├── grid-widget.js
│ ├── group-panel.js
│ ├── group-permissions.js
│ ├── item-cell.js
│ ├── item-form.js
│ ├── item-row.js
│ ├── radio-button.js
│ ├── relation-value.js
│ ├── relation-values.js
│ ├── select-control.js
│ ├── sortable-redirect-rules.js
│ ├── sortable.js
│ ├── star-rating.js
│ ├── switch.js
│ ├── wh.js
│ └── widget.js
├── bower.json
├── config
├── environment.js
└── environments
│ ├── development.js
│ ├── production.js
│ └── test.js
├── libs
└── cloudStorage.js
├── package.json
├── public
├── assets
│ ├── .gitkeep
│ ├── fonts
│ │ ├── bitter-bold.woff
│ │ ├── bitter.woff
│ │ ├── hook.eot
│ │ ├── hook.svg
│ │ ├── hook.ttf
│ │ ├── hook.woff
│ │ └── selection.json
│ ├── html
│ │ └── analytics.html
│ ├── images
│ │ ├── Webhook_Graphics.png
│ │ ├── dragon.png
│ │ ├── favicon.png
│ │ ├── robot.png
│ │ └── wizard.png
│ └── javascript
│ │ ├── async.js
│ │ ├── codemirror-compressed.js
│ │ ├── codemirror.css
│ │ ├── highlight.pack.js
│ │ ├── layout.js
│ │ ├── marked.js
│ │ ├── shortcode.js
│ │ ├── tracker.js
│ │ ├── uslug.js
│ │ ├── wxml-converter.js
│ │ └── wxml-importer.js
├── crossdomain.xml
├── humans.txt
├── robots.txt
└── testem.js
├── tasks
├── .jshintrc
├── custom-options
│ └── .gitkeep
├── deploy.js
├── express-server.js
├── helpers.js
├── locking.js
├── options
│ ├── autoprefixer.js
│ ├── clean.js
│ ├── coffee.js
│ ├── compass.js
│ ├── concat_sourcemap.js
│ ├── concurrent.js
│ ├── copy.js
│ ├── emberTemplates.js
│ ├── emberscript.js
│ ├── emblem.js
│ ├── exec.js
│ ├── express-server.js
│ ├── fancySprites.js
│ ├── htmlmin.js
│ ├── imagemin.js
│ ├── jshint.js
│ ├── less.js
│ ├── preprocess.js
│ ├── rev.js
│ ├── sass.js
│ ├── stylus.js
│ ├── testem.js
│ ├── transpile.js
│ ├── uglify.js
│ ├── usemin.js
│ ├── useminPrepare.js
│ ├── validate-imports.js
│ └── watch.js
└── push-production.js
├── testem.json
└── tests
├── .jshintrc
├── acceptance
└── index-test.js
├── ember-shim.js
├── helpers
├── resolver.js
└── start-app.js
├── qunit-shim.js
├── test-helper.js
├── test-loader.js
└── unit
├── .gitkeep
└── routes
└── application-test.js
/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "vendor"
3 | }
4 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 |
7 | # dependencies
8 | /node_modules
9 | /vendor
10 |
11 | # misc
12 | /.sass-cache
13 | /connect.lock
14 | /libpeerconnection.log
15 | .DS_Store
16 | Thumbs.db
17 | /coverage/*
18 | npm-debug.log
19 |
20 | .cloudstorage.key
21 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "predef": [
3 | "document",
4 | "window",
5 | "location",
6 | "setTimeout",
7 | "Ember",
8 | "Em",
9 | "DS",
10 | "$",
11 | "EmberFire",
12 | "Firebase",
13 | "FirebaseSimpleLogin",
14 | "moment",
15 | "marked",
16 | "Webhook",
17 | "XMLHttpRequest",
18 | "ga"
19 | ],
20 | "node" : false,
21 | "browser" : false,
22 | "boss" : true,
23 | "curly": false,
24 | "debug": false,
25 | "devel": false,
26 | "eqeqeq": true,
27 | "evil": true,
28 | "forin": false,
29 | "immed": false,
30 | "laxbreak": false,
31 | "newcap": true,
32 | "noarg": true,
33 | "noempty": false,
34 | "nonew": false,
35 | "nomen": false,
36 | "onevar": false,
37 | "plusplus": false,
38 | "regexp": false,
39 | "undef": true,
40 | "sub": true,
41 | "strict": false,
42 | "white": false,
43 | "eqnull": true,
44 | "esnext": true
45 | }
46 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 0.10
4 | before_script:
5 | - npm install -g grunt-cli
6 | - npm install -g bower
7 | - bower install
8 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Getting Involved
2 |
3 | Help us help you.
4 |
5 | ## Issues
6 |
7 | Let us know about CMS bugs or feature requests in this repository's [issue tracker](https://github.com/webhook/webhook-cms/issues).
8 |
9 | ### Reporting Bugs
10 |
11 | 1. Search for similar bugs in the tracker to avoid duplicates.
12 | - Provide steps to reproduce the problem. The more detailed the better.
13 |
14 | ### Request Features
15 |
16 | 1. Search for similar feature requests in the tracker to avoid duplicates.
17 | - Provide an explanation of why you think this feature is needed and how your use case.
18 |
19 | ## Pull Requests
20 |
21 | 1. Follow the syntax and code style already present in the project (2 spaces, no extraneous whitespace, etc.).
22 | - Fork the repo.
23 | - Commit changes.
24 | - Push changes and submit a pull request.
25 | - We'll take a look provide feedback and/or merge it in a timely manner.
26 |
27 | # Questions
28 |
29 | Find us (enemykite, LtSquigs, and gpbmike) on IRC Freenode channel **#webhook**. We frequent our [Webhook forums](http://forums.webhook.com/). [@webhookcms](https://twitter.com/webhookcms) on Twitter and email (support@webhook.com) are also viable options.
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Webhook Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/api-stub/routes.js:
--------------------------------------------------------------------------------
1 | module.exports = function(server) {
2 |
3 | // Create an API namespace, so that the root does not
4 | // have to be repeated for each end point.
5 | server.namespace('/api', function() {
6 |
7 | // Return fixture data for '/api/posts/:id'
8 | server.get('/posts/:id', function(req, res) {
9 | var post = {
10 | "post": {
11 | "id": 1,
12 | "title": "Rails is omakase",
13 | "comments": ["1", "2"],
14 | "user" : "dhh"
15 | },
16 |
17 | "comments": [{
18 | "id": "1",
19 | "body": "Rails is unagi"
20 | }, {
21 | "id": "2",
22 | "body": "Omakase O_o"
23 | }]
24 | };
25 |
26 | res.send(post);
27 | });
28 |
29 | });
30 |
31 | };
--------------------------------------------------------------------------------
/app/adapters/control-type-group.js:
--------------------------------------------------------------------------------
1 | export default DS.FixtureAdapter.extend();
2 |
--------------------------------------------------------------------------------
/app/adapters/control-type.js:
--------------------------------------------------------------------------------
1 | export default DS.FixtureAdapter.extend();
2 |
--------------------------------------------------------------------------------
/app/adapters/redirect.js:
--------------------------------------------------------------------------------
1 | import ApplicationAdapter from 'appkit/adapters/application';
2 |
3 | export default ApplicationAdapter.extend({
4 | firebase: window.ENV.firebase.child('settings')
5 | });
6 |
--------------------------------------------------------------------------------
/app/components/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/webhook/webhook-cms/8b58110801bc0d3a749bb230bb9983ee5b77ffcd/app/components/.gitkeep
--------------------------------------------------------------------------------
/app/components/audio-player.js:
--------------------------------------------------------------------------------
1 | export default Ember.Component.extend({
2 | tagName: 'audio',
3 | didInsertElement: function () {
4 |
5 | var audioComponent = this;
6 |
7 | this.$().prop('controls', true);
8 | this.$().on('loadedmetadata', function () {
9 | audioComponent.sendAction('load', this);
10 | });
11 | }
12 | });
13 |
--------------------------------------------------------------------------------
/app/components/audio-upload.js:
--------------------------------------------------------------------------------
1 | import FileUploadComponent from 'appkit/components/file-upload';
2 |
3 | export default FileUploadComponent.extend({
4 | selectAccept : 'audio/*',
5 | defaultClasses: 'icon-music',
6 | successMsg : ' Audio upload complete.',
7 |
8 | actions: {
9 | audioLoaded: function (audio) {
10 | this.set('control.value.duration', audio.duration);
11 | }
12 | }
13 | });
14 |
--------------------------------------------------------------------------------
/app/components/colored-element.js:
--------------------------------------------------------------------------------
1 | export default Ember.Component.extend({
2 | tagName: 'span',
3 | classNames: 'wh-color-value',
4 | attributeBindings: ['customColor:style'],
5 | customColor: function () {
6 | return 'background-color:' + this.get('color');
7 | }.property('color')
8 | });
9 |
--------------------------------------------------------------------------------
/app/components/date-time.js:
--------------------------------------------------------------------------------
1 | export default Ember.Component.extend({
2 |
3 | tagName: 'span',
4 |
5 | date: function () {
6 |
7 | if (this.get('value') && moment(this.get('value')).isValid()) {
8 | return moment(this.get('value')).format('YYYY-MM-DD');
9 | } else {
10 | return null;
11 | }
12 |
13 | }.property('value'),
14 |
15 | time: function () {
16 |
17 | if (!this.get('value') || !moment(this.get('value')).isValid()) {
18 | return null;
19 | }
20 |
21 | if (moment(this.get('value')).minutes() + moment(this.get('value')).hours() === 0) {
22 | return null;
23 | }
24 |
25 | return moment(this.get('value')).format('HH:mm');
26 | }.property('value'),
27 |
28 | datetimeChanged: function () {
29 |
30 | var date = this.get('date');
31 | var time = this.get('time');
32 |
33 | if (!date && !time) {
34 | this.set('value', null);
35 | return;
36 | }
37 |
38 | if (this.get('meta.hideDate')) {
39 | if (!time) {
40 | this.set('value', null);
41 | return;
42 | }
43 | if (!date) {
44 | date = moment().format('YYYY-MM-DD');
45 | }
46 | }
47 |
48 | var value = time ? (date + ' ' + time) : date;
49 |
50 | if (moment(value).isValid()) {
51 | this.set('value', moment(value).format());
52 | } else {
53 | this.set('value', null);
54 | }
55 |
56 | }.observes('date', 'time')
57 | });
58 |
--------------------------------------------------------------------------------
/app/components/disqus-embed.js:
--------------------------------------------------------------------------------
1 | export default Ember.Component.extend({
2 | elementId: 'disqus_thread',
3 |
4 | didInsertElement: function () {
5 |
6 | window.disqus_shortname = this.get('shortname');
7 | window.disqus_identifier = this.get('identifier');
8 |
9 | var src = 'http://' + window.disqus_shortname + '.disqus.com/embed.js';
10 |
11 | if (!Ember.$('script[src="' + src + '"]').length) {
12 | var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
13 | dsq.src = src;
14 | (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
15 | } else {
16 | window.DISQUS.reset({
17 | reload: true
18 | });
19 | }
20 | }
21 | });
22 |
--------------------------------------------------------------------------------
/app/components/download-backup.js:
--------------------------------------------------------------------------------
1 | export default Ember.Component.extend({
2 | tagName: 'button',
3 | classNames: ['btn'],
4 |
5 | text: 'Download Backup',
6 |
7 | click: function () {
8 | var fileName = this.get('buildEnvironment.siteDisplayName') + '-' + moment().format() + '.json';
9 | window.ENV.firebase.once('value', function (snapshot) {
10 | var data = snapshot.val();
11 |
12 | var dataWhiteList = {
13 | contentType: data.contentType,
14 | data: data.data,
15 | settings: data.settings
16 | };
17 |
18 | var blob = new window.Blob([JSON.stringify(dataWhiteList, null, 2)], { type: "text/plain;charset=utf-8" });
19 | window.saveAs(blob, fileName);
20 | });
21 | }
22 | });
23 |
--------------------------------------------------------------------------------
/app/components/embedly-control.js:
--------------------------------------------------------------------------------
1 | export default Ember.Component.extend({
2 |
3 | showPreview: true,
4 | showCode : false,
5 |
6 | hasValue: function () {
7 | var value = this.get('control.value');
8 | return !Ember.isEmpty(value) && typeof value === 'object' && Object.keys(value).length;
9 | }.property('control.value'),
10 |
11 | dataString: function () {
12 | return JSON.stringify(this.get('control.value'), null, 2);
13 | }.property('control.value'),
14 |
15 | isVisual: function () {
16 | return ['video', 'rich', 'photo'].indexOf(this.get('control.value.type')) >= 0;
17 | }.property('control.value.type'),
18 |
19 | previewValue: function () {
20 |
21 | var preview = '';
22 |
23 | switch (this.get('control.value.type')) {
24 | case 'video':
25 | case 'rich':
26 | preview = this.get('control.value.html');
27 | break;
28 | case 'photo':
29 | preview = ' ';
30 | break;
31 | default:
32 | if (this.get('control.value.title')) {
33 | preview = this.get('control.value.title') + ' (' + this.get('control.value.original_url') + ')';
34 | }
35 | break;
36 | }
37 |
38 | return preview;
39 |
40 | }.property('control.value'),
41 |
42 | actions: {
43 | getEmbed: function () {
44 | if (!this.get('url') || this.get('isFetching')) {
45 | return;
46 | }
47 |
48 | this.set('isFetching', true);
49 | this.set('control.value', {});
50 |
51 | var embedlyControl = this;
52 |
53 | $.embedly.oembed(this.get('url'), {
54 | key: window.ENV.embedlyKey,
55 | query: this.get('control.meta.options')
56 | }).progress(function (data) {
57 | embedlyControl.set('isFetching', false);
58 | embedlyControl.set('control.value', data);
59 | });
60 | },
61 |
62 | togglePreview: function () {
63 | this.toggleProperty('showCode');
64 | this.toggleProperty('showPreview');
65 | },
66 |
67 | clearValue: function () {
68 | this.set('control.value', {});
69 | }
70 | }
71 | });
72 |
--------------------------------------------------------------------------------
/app/components/image-upload.js:
--------------------------------------------------------------------------------
1 | /* global Image */
2 |
3 | import FileUploadComponent from 'appkit/components/file-upload';
4 |
5 | export default FileUploadComponent.extend({
6 | selectAccept : 'image/*',
7 | defaultClasses: 'icon-picture',
8 | successMsg : ' Image upload complete.',
9 | tempUrl : null,
10 |
11 | postParams: {
12 | resize_url: true
13 | },
14 |
15 | hasPreview: function () {
16 | return !!(this.get('control.value.resize_url') || this.get('tempUrl'));
17 | }.property('control.value.resize_url', 'tempUrl'),
18 |
19 | // Show preview of file
20 | beforeUpload: function (file) {
21 | this._super.apply(this, arguments);
22 | this.set('tempUrl', typeof file === 'string' ? file : (window.URL || window.webkitURL).createObjectURL(file));
23 | },
24 |
25 | // Add image meta data
26 | doneUpload: function (file, response) {
27 | this._super.apply(this, arguments);
28 |
29 | this.set('control.value.resize_url', response.resize_url);
30 | this.set('tempUrl', null);
31 |
32 | var imageComponent = this;
33 |
34 | // Load image to get dimensions
35 | var image = new Image();
36 |
37 | image.onload = function() {
38 | imageComponent.set('control.value.width', this.width);
39 | imageComponent.set('control.value.height', this.height);
40 | };
41 |
42 | image.src = response.url;
43 | },
44 |
45 | failUpload: function () {
46 | this.$('.wy-form-upload-image').remove();
47 | }
48 | });
49 |
--------------------------------------------------------------------------------
/app/components/pretty-color.js:
--------------------------------------------------------------------------------
1 | export default Ember.Component.extend({
2 | classNames: ['pretty-color'],
3 | attributeBindings: ['style'],
4 | style: function(){
5 | return 'color: ' + this.get('name') + ';';
6 | }.property('name')
7 | });
8 |
--------------------------------------------------------------------------------
/app/components/select-file.js:
--------------------------------------------------------------------------------
1 | // Simple component for selecting files
2 | // usage: {{#select-file action='handleFile'}}Button Text{{/select-file}}
3 | export default Ember.Component.extend({
4 | tagName: 'button',
5 | classNames: 'btn btn-neutral',
6 |
7 | accept: '*',
8 | multiple: false,
9 |
10 | didInsertElement: function () {
11 |
12 | var component = this;
13 |
14 | this.$().selectFile({
15 | accept : this.get('accept'),
16 | multiple: this.get('multiple')
17 | }).on('selectedFile', function (event, file) {
18 | component.sendAction('action', file);
19 | });
20 | }
21 |
22 | });
23 |
--------------------------------------------------------------------------------
/app/components/tabular-data.js:
--------------------------------------------------------------------------------
1 | export default Ember.Component.extend({
2 | actions: {
3 | addTabularRow: function (control) {
4 | var emptyRow = Ember.A([]);
5 | control.get('meta.options').forEach(function () {
6 | emptyRow.pushObject(Ember.Object.create());
7 | });
8 | control.get('value').pushObject(emptyRow);
9 | },
10 |
11 | removeTabularRow: function (row, control) {
12 | control.get('value').removeObject(row);
13 | }
14 | }
15 | });
16 |
--------------------------------------------------------------------------------
/app/components/template-less.js:
--------------------------------------------------------------------------------
1 | export default Ember.Component.extend({
2 | classNames: ['look-ma-no-template'],
3 | tagName: ['span']
4 | });
5 |
--------------------------------------------------------------------------------
/app/components/webhook-blog.js:
--------------------------------------------------------------------------------
1 | export default Ember.Component.extend({
2 |
3 | posts: Ember.A([]),
4 |
5 | willInsertElement: function () {
6 |
7 | var component = this;
8 |
9 | $.ajax({
10 | url: 'http://www.webhook.com/blog-json/',
11 | dataType: 'jsonp',
12 | jsonpCallback: 'callback'
13 | }).success(function (data) {
14 |
15 | data.forEach(function (post) {
16 | component.get('posts').addObject(post);
17 | });
18 | });
19 |
20 | }
21 | });
22 |
--------------------------------------------------------------------------------
/app/components/webhook-modal.js:
--------------------------------------------------------------------------------
1 | export default Ember.Component.extend({
2 | classNames: ['wy-modal'],
3 |
4 | show: false,
5 | canClose: true,
6 |
7 | showChange: function () {
8 | this.$().toggle();
9 | this.$mask.css('z-index', parseInt(this.$().css('z-index'), 10) - 1);
10 | this.$mask.toggleClass('on');
11 | }.observes('show'),
12 |
13 | willInsertElement: function () {
14 | this.$mask = Ember.$('
').css('left', 0);
15 | },
16 |
17 | didInsertElement: function () {
18 | this.$mask.insertBefore(this.$());
19 | this.$().hide().css({
20 | top: '20%',
21 | marginTop: 0
22 | });
23 | },
24 |
25 | willDestroyElement: function () {
26 | this.$mask.remove();
27 | },
28 |
29 | actions: {
30 | confirm: function (data) {
31 | this.sendAction('confirm', data);
32 | },
33 | cancel: function () {
34 | this.sendAction('cancel');
35 | },
36 | close: function () {
37 | this.set('show', false);
38 | }
39 | }
40 | });
41 |
--------------------------------------------------------------------------------
/app/controllers/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/webhook/webhook-cms/8b58110801bc0d3a749bb230bb9983ee5b77ffcd/app/controllers/.gitkeep
--------------------------------------------------------------------------------
/app/controllers/confirm-email.js:
--------------------------------------------------------------------------------
1 | export default Ember.ObjectController.extend({
2 | isSending: true,
3 | success : false,
4 | error : null,
5 | });
6 |
--------------------------------------------------------------------------------
/app/controllers/login.js:
--------------------------------------------------------------------------------
1 | export default Ember.Controller.extend({
2 | email : null,
3 | password : null,
4 | isLoading: false,
5 |
6 | userChanged: function () {
7 | Ember.Logger.log('LoginController::userChanged');
8 | this.set('isLoading', false);
9 | if (this.get('session.transition')) {
10 | this.get('session.transition').retry();
11 | } else {
12 | this.transitionToRoute('index');
13 | }
14 | }.observes('session.user'),
15 |
16 | errorChanged: function () {
17 | this.set('isLoading', false);
18 | }.observes('session.error'),
19 |
20 | supportedLanguages: function () {
21 | var languages = Ember.A([]);
22 | Ember.$.each(Ember.ENV.I18N_CODE_MAP, function (code, language) {
23 | languages.push({ code: code, language: language });
24 | });
25 | return languages;
26 | }.property(),
27 |
28 | actions: {
29 | loginUser: function () {
30 | if (this.get('isLoading')) {
31 | return;
32 | }
33 |
34 | if (this.get('email') === '') {
35 | this.get('session').set('error', {
36 | code: 'Invalid Login',
37 | message: 'Please enter an email address.'
38 | });
39 | return;
40 | }
41 |
42 | if (this.get('password') === '') {
43 | this.get('session').set('error', {
44 | code: 'Invalid Login',
45 | message: 'Please enter a password.'
46 | });
47 | return;
48 | }
49 |
50 | this.get('session').set('error', null);
51 | this.set('isLoading', true);
52 |
53 | this.get('session.auth').login('password', {
54 | email : this.get('email'),
55 | password : this.get('password'),
56 | rememberMe: true
57 | });
58 | }
59 | }
60 | });
61 |
--------------------------------------------------------------------------------
/app/controllers/password-reset.js:
--------------------------------------------------------------------------------
1 | export default Ember.ObjectController.extend({
2 | email: null,
3 | isSending: false,
4 | success: false,
5 | error: null,
6 |
7 | actions: {
8 | resetPassword: function () {
9 |
10 | if (Ember.isEmpty(this.get('email'))) {
11 | return;
12 | }
13 |
14 | this.set('isSending', true);
15 | this.set('success', false);
16 | this.set('error', null);
17 |
18 | this.get('session.auth').sendPasswordResetEmail(this.get('email'), function () {
19 | this.set('success', true);
20 | this.set('isSending', false);
21 | }.bind(this));
22 |
23 | }
24 | }
25 | });
26 |
--------------------------------------------------------------------------------
/app/controllers/reindex.js:
--------------------------------------------------------------------------------
1 | export default Ember.ArrayController.extend({
2 | sortProperties: ['name'],
3 |
4 | isIndexing: function () {
5 | return !this.get('model').isEvery('indexingClass', 'complete');
6 | }.property('model.@each.isIndexing'),
7 |
8 | handleBeforeUnload: function () {
9 | return 'We are not done reindexing your content. If you leave now, content will be missing from the search index.';
10 | },
11 |
12 | watchForUnload: function () {
13 | if (this.get('isIndexing')) {
14 | Ember.$(window).one('beforeunload', this.handleBeforeUnload);
15 | } else {
16 | Ember.$(window).off('beforeunload', this.handleBeforeUnload);
17 | }
18 | }.observes('isIndexing')
19 |
20 | });
21 |
--------------------------------------------------------------------------------
/app/controllers/resend-email.js:
--------------------------------------------------------------------------------
1 | export default Ember.ObjectController.extend({
2 | email : null,
3 | isSending: false,
4 | success : false,
5 | error : null,
6 |
7 | actions: {
8 | resendEmail: function () {
9 |
10 | this.setProperties({
11 | success: false,
12 | error: null
13 | });
14 |
15 | this.set('isSending', true);
16 |
17 | function uniqueId() {
18 | return Date.now() + 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
19 | var r = Math.random()*16|0, v = c === 'x' ? r : (r&0x3|0x8);
20 | return v.toString(16);
21 | });
22 | }
23 |
24 | var escapedEmail = this.get('email').toLowerCase().replace(/\./g, ',1');
25 | var root = window.ENV.firebaseRoot.child('management/commands/verification/' + escapedEmail);
26 |
27 | root.set({ userid: this.get('email').toLowerCase(), siteref: window.location.host, id: uniqueId() }, function(err) {
28 |
29 | if(err) {
30 | this.set('error', err);
31 | return;
32 | } else {
33 | this.set('success', { message: 'Verification e-mail resent' });
34 | }
35 |
36 | this.set('isSending', false);
37 | }.bind(this));
38 | }
39 | }
40 | });
41 |
--------------------------------------------------------------------------------
/app/controllers/wh/content/type/json.js:
--------------------------------------------------------------------------------
1 | export default Ember.Controller.extend({
2 | error: null,
3 | saving: false,
4 |
5 | actions: {
6 | save: function (itemJSON) {
7 | this.set('error', null);
8 | this.set('saving', true);
9 |
10 | var itemData;
11 |
12 | try {
13 | itemData = JSON.parse(itemJSON);
14 | } catch (error) {
15 | return this.set('error', error);
16 | }
17 |
18 | this.get('model').set('itemData', itemData);
19 | this.get('model').save().then(function (item) {
20 | this.set('saving', false);
21 | this.send('notify', 'info', item.get('itemData.name') + ' saved!', { icon: 'ok-sign' });
22 | this.transitionToRoute('wh.content.type', item.get('constructor.typeKey'));
23 | }.bind(this));
24 |
25 | }
26 | }
27 | });
28 |
--------------------------------------------------------------------------------
/app/controllers/wh/content/type/new.js:
--------------------------------------------------------------------------------
1 | import EditController from 'appkit/controllers/wh/content/type/edit';
2 |
3 | export default EditController.extend();
4 |
--------------------------------------------------------------------------------
/app/controllers/wh/search-global-results.js:
--------------------------------------------------------------------------------
1 | export default Ember.Controller.extend({
2 | needs: ["wh"],
3 | searchResults: Ember.computed.alias("controllers.wh.searchResults"),
4 | isLoading: Ember.computed.alias("controllers.wh.searchLoading"),
5 | debouncedQuery: Ember.computed.alias("controllers.wh.debouncedQuery")
6 | });
7 |
--------------------------------------------------------------------------------
/app/controllers/wh/settings/general.js:
--------------------------------------------------------------------------------
1 | export default Ember.ObjectController.extend({
2 | actions: {
3 | saveSettings: function () {
4 | var controller = this;
5 | controller.get('model').save().then(function () {
6 | controller.send('notify', 'success', 'Settings saved!');
7 | controller.send('buildSignal');
8 | });
9 | }
10 | }
11 | });
12 |
--------------------------------------------------------------------------------
/app/controllers/wordpress.js:
--------------------------------------------------------------------------------
1 | /*global WXMLConverter,WXMLImporter*/
2 | import downcode from 'appkit/utils/downcode';
3 | import SearchIndex from 'appkit/utils/search-index';
4 |
5 | export default Ember.Controller.extend({
6 |
7 | needs: ['application'],
8 |
9 | wxmlDoneClass: 'pending',
10 | wxmlStatus: null,
11 |
12 | isComplete: false,
13 |
14 | convertXml: function () {
15 |
16 | var file = this.get('controllers.application.wordpressXML');
17 |
18 | var controller = this;
19 | var reader = new window.FileReader();
20 |
21 | reader.onload = function(e) {
22 | var data = reader.result;
23 |
24 | WXMLConverter.onConverterUpdated = function(updateEvent) {
25 | controller.set('wxmlStatus.messages', true);
26 | controller.set('wxmlStatus.' + updateEvent.event, updateEvent);
27 | };
28 |
29 | WXMLImporter.onImporterUpdated = function(updateEvent) {
30 | controller.set('wxmlStatus.messages', true);
31 | controller.set('wxmlStatus.' + updateEvent.event, updateEvent);
32 | };
33 |
34 | WXMLConverter.convert(data, function(parsedData) {
35 | WXMLImporter.import(parsedData, downcode, window.ENV.firebase, controller.get('session.site.name'), controller.get('session.site.token'), function() {
36 | controller.set('wxmlStatus.search', { running: true, class: 'active'});
37 | SearchIndex.reindex().then(function () {
38 | controller.set('wxmlStatus.search', { running: false, class: 'complete'});
39 | controller.set('wxmlDoneClass', 'complete');
40 | controller.set('isComplete', true);
41 | }, function (error) {
42 | controller.set('wxmlStatus.search', { running: false, class: 'danger'});
43 | controller.set('wxmlDoneClass', 'complete');
44 | controller.set('isComplete', false);
45 | });
46 | });
47 | });
48 | };
49 |
50 | if (Ember.isEmpty(file)) {
51 | this.transitionToRoute('wh');
52 | } else {
53 | reader.readAsText(file);
54 | }
55 |
56 | }
57 | });
58 |
--------------------------------------------------------------------------------
/app/helpers/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/webhook/webhook-cms/8b58110801bc0d3a749bb230bb9983ee5b77ffcd/app/helpers/.gitkeep
--------------------------------------------------------------------------------
/app/helpers/format-number.js:
--------------------------------------------------------------------------------
1 | // see http://numeraljs.com/ for formats
2 |
3 | /* global numeral */
4 | export default Ember.Handlebars.makeBoundHelper(function(number, options) {
5 | if (!number) {
6 | return "";
7 | }
8 | if (options.hash.format) {
9 | return numeral(number).format(options.hash.format);
10 | } else {
11 | return numeral(number).format();
12 | }
13 | });
14 |
--------------------------------------------------------------------------------
/app/helpers/format-time.js:
--------------------------------------------------------------------------------
1 | export default Ember.Handlebars.makeBoundHelper(function(datetime, options) {
2 | if (!datetime) {
3 | return "";
4 | }
5 | return moment(datetime).format(options.hash.format || 'LLLL');
6 | });
7 |
--------------------------------------------------------------------------------
/app/helpers/resize-image.js:
--------------------------------------------------------------------------------
1 | export default Ember.Handlebars.makeBoundHelper(function(src, options) {
2 |
3 | var params = [];
4 | ['width', 'height', 'grow'].forEach(function (key) {
5 | if (options.hash[key]) {
6 | params.push(key + '=' + options.hash[key]);
7 | }
8 | });
9 |
10 | var imageSource = '';
11 | var safeImageSource = '';
12 |
13 | // New image format
14 | if (typeof src === 'object' && src.resize_url) {
15 | imageSource = src.resize_url;
16 |
17 | if (src.resize_url.indexOf('http://static-cdn.jtvnw.net') === 0) {
18 | var parts = src.resize_url.split('.'),
19 | ext = parts.length > 1 ? ('.' + parts.pop()) : '',
20 | dim = options.hash.size || 100;
21 |
22 | imageSource = parts.join('.') + '-' + dim + 'x' + dim;
23 |
24 | if (options.hash.crop) {
25 | imageSource += '-c';
26 | } else {
27 | imageSource += '-a';
28 | }
29 |
30 | imageSource += ext;
31 | } else {
32 | imageSource = imageSource + '=s' + (options.hash.size || 100);
33 |
34 | if (options.hash.crop) {
35 | imageSource = imageSource + '-c';
36 | }
37 | }
38 |
39 | safeImageSource = Ember.Handlebars.Utils.escapeExpression(imageSource);
40 |
41 | return new Ember.Handlebars.SafeString('
');
42 |
43 | // Old image format
44 | } else if (typeof src === 'string') {
45 |
46 | // Relative url
47 | if (src.indexOf('http://') === -1) {
48 | src = 'http://' + window.ENV.siteDNS + src;
49 | }
50 |
51 | params.push('url=' + encodeURIComponent(src));
52 | params.push('key=' + window.ENV.embedlyKey);
53 |
54 | imageSource = window.ENV.displayUrl + (options.hash.crop ? 'crop' : 'resize') + '?' + params.join('&');
55 | safeImageSource = Ember.Handlebars.Utils.escapeExpression(imageSource);
56 |
57 | return new Ember.Handlebars.SafeString('
');
58 | } else {
59 | return '';
60 | }
61 |
62 | });
63 |
--------------------------------------------------------------------------------
/app/helpers/reverse-word.js:
--------------------------------------------------------------------------------
1 | // Please note that Handlebars helpers will only be found automatically by the
2 | // resolver if their name contains a dash (reverse-word, translate-text, etc.)
3 | // For more details: http://stefanpenner.github.io/ember-app-kit/guides/using-modules.html
4 |
5 | export default Ember.Handlebars.makeBoundHelper(function(word) {
6 | return word.split('').reverse().join('');
7 | });
8 |
9 |
--------------------------------------------------------------------------------
/app/helpers/to-markdown.js:
--------------------------------------------------------------------------------
1 | export default Ember.Handlebars.makeBoundHelper(function(string) {
2 | if (typeof string === 'string') {
3 | return new Ember.Handlebars.SafeString(marked(string));
4 | } else {
5 | return '';
6 | }
7 | });
8 |
--------------------------------------------------------------------------------
/app/helpers/translate-control.js:
--------------------------------------------------------------------------------
1 | // This is essentially a hack until you can pass variables to the {{t}} helper.
2 | // See https://github.com/jamesarosen/ember-i18n/issues/131 for updates
3 | export default Ember.Handlebars.makeBoundHelper(function(name, group) {
4 | if (!name) {
5 | return '';
6 | }
7 |
8 | return Ember.I18n.translations['form'][group === true ? 'group' : 'widget'][name.toLowerCase()];
9 | });
10 |
--------------------------------------------------------------------------------
/app/helpers/truncate-string.js:
--------------------------------------------------------------------------------
1 | export default Ember.Handlebars.makeBoundHelper(function(str,len) {
2 | if (!str || !len) { return str; }
3 |
4 | // strip the html
5 | str = Ember.$('
').html(str).text();
6 |
7 | if (str.length > len && str.length > 0) {
8 | var new_str = str + " ";
9 | new_str = str.substr(0, len);
10 | new_str = str.substr(0, new_str.lastIndexOf(" "));
11 | new_str = (new_str.length > 0) ? new_str : str.substr(0, len);
12 |
13 | return new Ember.Handlebars.SafeString(new_str + '...');
14 | }
15 | return str;
16 | });
17 |
--------------------------------------------------------------------------------
/app/initializers/analytics.js:
--------------------------------------------------------------------------------
1 | export default {
2 | name: 'analytics',
3 |
4 | initialize: function() {
5 | /**
6 | * Creates a temporary global ga object and loads analytics.js.
7 | * Paramenters o, a, and m are all used internally. They could have been declared using 'var',
8 | * instead they are declared as parameters to save 4 bytes ('var ').
9 | *
10 | * @param {Window} i The global context object.
11 | * @param {Document} s The DOM document object.
12 | * @param {string} o Must be 'script'.
13 | * @param {string} g URL of the analytics.js script. Inherits protocol from page.
14 | * @param {string} r Global name of analytics object. Defaults to 'ga'.
15 | * @param {DOMElement?} a Async script tag.
16 | * @param {DOMElement?} m First script tag in document.
17 | */
18 | (function(i, s, o, g, r, a, m){
19 | i['GoogleAnalyticsObject'] = r; // Acts as a pointer to support renaming.
20 |
21 | // Creates an initial ga() function. The queued commands will be executed once analytics.js loads.
22 | i[r] = i[r] || function() {
23 | (i[r].q = i[r].q || []).push(arguments);
24 | };
25 |
26 | // Sets the time (as an integer) this tag was executed. Used for timing hits.
27 | i[r].l = 1 * new Date();
28 |
29 | // Insert the script tag asynchronously. Inserts above current tag to prevent blocking in
30 | // addition to using the async attribute.
31 | a = s.createElement(o);
32 | m = s.getElementsByTagName(o)[0];
33 | a.async = 1;
34 | a.src = g;
35 | m.parentNode.insertBefore(a, m);
36 | })(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga');
37 | ga('create', 'UA-47335625-2', {'cookieDomain': 'none'});
38 | }
39 | };
40 |
--------------------------------------------------------------------------------
/app/initializers/session.js:
--------------------------------------------------------------------------------
1 | export default {
2 | name: 'session',
3 |
4 | initialize: function (container, application) {
5 |
6 | var session = Ember.Object.create({
7 | auth: null,
8 | user: null,
9 | site: Ember.Object.create({
10 | name: Ember.$('meta[name="siteName"]').attr('content'),
11 | token: null
12 | })
13 | });
14 |
15 | // Add `session` to all the things
16 | application.register('session:current', session, { instantiate: false });
17 | Ember.A(['model', 'controller', 'view', 'route', 'component']).forEach(function (component) {
18 | application.inject(component, 'session', 'session:current');
19 | });
20 |
21 | }
22 | };
23 |
--------------------------------------------------------------------------------
/app/initializers/team.js:
--------------------------------------------------------------------------------
1 | export default {
2 | name: 'team',
3 |
4 | initialize: function (container, application) {
5 |
6 | var team = Ember.Object.create();
7 |
8 | // Add `session` to all the things
9 | application.register('team:current', team, { instantiate: false });
10 | Ember.A(['model', 'controller', 'view', 'route', 'component']).forEach(function (component) {
11 | application.inject(component, 'team', 'team:current');
12 | });
13 |
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/app/models/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/webhook/webhook-cms/8b58110801bc0d3a749bb230bb9983ee5b77ffcd/app/models/.gitkeep
--------------------------------------------------------------------------------
/app/models/control-type-group.js:
--------------------------------------------------------------------------------
1 | var ControlTypeGroup = DS.Model.extend({
2 | name: DS.attr('string'),
3 | controlTypes: DS.hasMany('control-type', { async: true })
4 | });
5 |
6 | var fixtures = [];
7 |
8 | var controlTypeGroupId = 0,
9 | controlTypeId = 0;
10 |
11 | $.each(window.ENV.controlTypeGroups, function (index, group) {
12 | controlTypeGroupId++;
13 | fixtures.push({
14 | id: controlTypeGroupId,
15 | name: group.name,
16 | controlTypes: $.map(group.controlTypes, function (control, index) {
17 | return control.widget;
18 | })
19 | });
20 | });
21 |
22 | ControlTypeGroup.reopenClass({
23 | FIXTURES: fixtures
24 | });
25 |
26 | export default ControlTypeGroup;
27 |
--------------------------------------------------------------------------------
/app/models/control-type.js:
--------------------------------------------------------------------------------
1 | var ControlType = DS.Model.extend({
2 | name : DS.attr('string'),
3 | group : DS.belongsTo('control-type-group'),
4 | iconClass: DS.attr('string'),
5 | widget : DS.attr('string', { defaultValue: 'textfield' }),
6 |
7 | // The following are used as defaults for new controls of this type
8 | label : DS.attr('string'),
9 | placeholder: DS.attr('string'),
10 | help : DS.attr('string'),
11 |
12 | valueType : DS.attr('string', { defaultValue: 'string' }),
13 |
14 | controlPartialPath: function () {
15 | return 'widgets/' + this.get('widget');
16 | }.property('widget'),
17 | infoPartialPath: function () {
18 | return 'widgets/info/' + this.get('widget');
19 | }.property('widget'),
20 | valuePartialPath: function () {
21 | return 'widgets/value/' + this.get('widget');
22 | }.property('widget')
23 | });
24 |
25 | var fixtures = [];
26 |
27 | var controlTypeGroupId = 0,
28 | controlTypeId = 0;
29 |
30 | $.each(window.ENV.controlTypeGroups, function (index, group) {
31 | controlTypeGroupId++;
32 | $.each(group.controlTypes, function (index, control) {
33 | fixtures.push($.extend({
34 | id: control.widget,
35 | group: controlTypeGroupId
36 | }, control));
37 | });
38 | });
39 |
40 | ControlType.reopenClass({
41 | FIXTURES: fixtures
42 | });
43 |
44 | export default ControlType;
45 |
--------------------------------------------------------------------------------
/app/models/data.js:
--------------------------------------------------------------------------------
1 | // This is the "One Off" model
2 | // it is named `data` because that will put it in `dev/data/{id}`
3 |
4 | import Item from 'appkit/models/item';
5 |
6 | export default Item.extend();
7 |
--------------------------------------------------------------------------------
/app/models/group.js:
--------------------------------------------------------------------------------
1 | export default Ember.Object.extend({
2 | key: null,
3 | name: null,
4 | permissions: null,
5 | users: Ember.A([]),
6 | isOpen: false,
7 | isEditingName: false,
8 | isSaved: false,
9 | saveChanged: function () {
10 | if (!this.get('isSaving')) {
11 | var group = this;
12 | group.set('isSaved', true);
13 | Ember.run.later(function () {
14 | group.set('isSaved', false);
15 | }, 500);
16 | }
17 | }.observes('isSaving'),
18 | isSaving: function () {
19 | return !!this.get('saveQueue');
20 | }.property('saveQueue'),
21 | saveQueue: 0
22 | });
23 |
--------------------------------------------------------------------------------
/app/models/item.js:
--------------------------------------------------------------------------------
1 | import SearchIndex from 'appkit/utils/search-index';
2 |
3 | export default DS.Model.extend({
4 | itemData: DS.attr('json'),
5 |
6 | updateSearchIndex: function () {
7 | SearchIndex.indexItem(this);
8 | }.on('didUpdate', 'didCreate')
9 | });
10 |
--------------------------------------------------------------------------------
/app/models/redirect.js:
--------------------------------------------------------------------------------
1 | export default DS.Model.extend({
2 | pattern: DS.attr('string'),
3 | destination: DS.attr('string'),
4 |
5 | isValid: function () {
6 |
7 | try {
8 | new RegExp(this.get('pattern'));
9 | } catch (error) {
10 | return false;
11 | }
12 |
13 | return true;
14 |
15 | }.property('pattern')
16 | });
17 |
--------------------------------------------------------------------------------
/app/models/settings.js:
--------------------------------------------------------------------------------
1 | export default DS.Model.extend({
2 | siteName : DS.attr('string'),
3 | siteUrl : DS.attr('string'),
4 | siteDescription: DS.attr('string'),
5 | siteKeywords : DS.attr('string'),
6 | analyticsId : DS.attr('string'),
7 | siteTwitter : DS.attr('string'),
8 | siteFacebook : DS.attr('string'),
9 | siteMessage : DS.attr('string'),
10 | siteMessageType: DS.attr('string')
11 | });
12 |
--------------------------------------------------------------------------------
/app/models/user.js:
--------------------------------------------------------------------------------
1 | export default Ember.Object.extend({
2 | key: null,
3 | email: null,
4 | owner: false,
5 | user: false,
6 | potential: false,
7 | isUser: function () {
8 | return this.get('owner') || this.get('user') || this.get('potential');
9 | }.property('owner', 'user', 'potential')
10 | });
11 |
--------------------------------------------------------------------------------
/app/router.js:
--------------------------------------------------------------------------------
1 | /*globals ga*/
2 | // ensure we don't share routes between all Router instances
3 | var Router = Ember.Router.extend({
4 | location: 'hash'
5 | });
6 |
7 | Router.map(function() {
8 |
9 | this.route('login');
10 | this.route('password-reset');
11 | this.route('create-user');
12 | this.route('confirm-email');
13 | this.route('resend-email');
14 | this.route('start');
15 | this.route('theme');
16 | this.route('wordpress');
17 | this.route('reindex');
18 | this.route('import');
19 |
20 | this.route('expired');
21 |
22 | this.route('form', { path: '/form/:id' });
23 |
24 | this.resource('wh', function () {
25 | this.resource('wh.settings', { path: '/settings/' }, function () {
26 | this.route('billing');
27 | this.route('data');
28 | this.route('domain');
29 | this.route('general');
30 | this.route('team');
31 | this.route('password-change');
32 | this.route('urls');
33 | });
34 |
35 | this.route('search-global-results');
36 | this.resource('wh.content', { path: '/content/' }, function () {
37 | this.route('all-types');
38 | this.route('start');
39 |
40 | this.resource('wh.content.type', { path: '/:type_id' }, function () {
41 | this.route('index', { path: '/' });
42 | this.route('edit', { path: '/:item_id' });
43 | this.route('json', { path: '/:item_id/json' });
44 | this.route('new');
45 | });
46 |
47 | });
48 | });
49 |
50 | this.route('component-test');
51 | this.route('helper-test');
52 |
53 | });
54 |
55 | Router.reopen({
56 | notifyAnalytics: function() {
57 | var dim1 = Ember.$('meta[name="siteName"]').attr('content');
58 | Ember.Logger.log('Sending pageview to analytics.', dim1);
59 |
60 | ga('set', 'dimension1', dim1);
61 | ga('send', 'pageview');
62 | }.on('didTransition')
63 | });
64 |
65 | export default Router;
66 |
--------------------------------------------------------------------------------
/app/routes/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/webhook/webhook-cms/8b58110801bc0d3a749bb230bb9983ee5b77ffcd/app/routes/.gitkeep
--------------------------------------------------------------------------------
/app/routes/component_test.js:
--------------------------------------------------------------------------------
1 | export default Ember.Route.extend({
2 | model: function() {
3 | return ['purple', 'green', 'orange'];
4 | }
5 | });
6 |
--------------------------------------------------------------------------------
/app/routes/confirm-email.js:
--------------------------------------------------------------------------------
1 | export default Ember.Route.extend({
2 | setupController: function (controller) {
3 |
4 | function getURLParameter(name) {
5 | return decodeURI(
6 | ((new RegExp(name + '=' + '(.+?)(&|$)')).exec(location.hash)||[,''])[1]
7 | );
8 | }
9 |
10 | var username = getURLParameter('username');
11 | var key = getURLParameter('key');
12 |
13 | if(!username || !key) {
14 | controller.setProperties({
15 | isSending: false,
16 | error: {
17 | code: 'NOPE',
18 | message: 'NEED TO SET KEY AND USERNAME'
19 | }
20 | });
21 | } else {
22 | var root = window.ENV.firebaseRoot.child('management/users/' + username + '/verification');
23 |
24 | root.child('verified').once('value', function(snapshot) {
25 | var val = snapshot.val();
26 |
27 | if(val) {
28 | controller.setProperties({
29 | isSending: false,
30 | success: true
31 | });
32 |
33 | setTimeout(function() {
34 | this.transitionTo('login');
35 | }.bind(this), 1000);
36 | } else {
37 | root.set({ verification_key_match: key, verified: true }, function(err) {
38 | if(err) {
39 | controller.setProperties({
40 | isSending: false,
41 | error: {
42 | code: 'NOPE',
43 | message: 'Invalid key or username'
44 | }
45 | });
46 | } else {
47 | controller.setProperties({
48 | isSending: false,
49 | success: true
50 | });
51 |
52 | setTimeout(function() {
53 | this.transitionTo('login');
54 | }.bind(this), 1000);
55 | }
56 | }.bind(this));
57 | }
58 |
59 | }.bind(this));
60 | }
61 | }
62 | });
63 |
--------------------------------------------------------------------------------
/app/routes/create-user.js:
--------------------------------------------------------------------------------
1 | export default Ember.Route.extend({
2 | setupController: function (controller) {
3 |
4 | function getURLParameter(name) {
5 | return decodeURI(
6 | ((new RegExp(name + '=' + '(.+?)(&|$)')).exec(location.hash)||[,''])[1]
7 | );
8 | }
9 |
10 | var username = getURLParameter('username') || null;
11 | var key = getURLParameter('key') || null;
12 |
13 | controller.setProperties({
14 | email : username.toLowerCase(),
15 | verification_key: key,
16 | password : "",
17 | password2: "",
18 | isSending: false,
19 | success : false,
20 | error : null
21 | });
22 | }
23 | });
24 |
--------------------------------------------------------------------------------
/app/routes/helper_test.js:
--------------------------------------------------------------------------------
1 | export default Ember.Route.extend({
2 | model: function() {
3 | return {
4 | name: "rebmE"
5 | };
6 | }
7 | });
8 |
--------------------------------------------------------------------------------
/app/routes/import.js:
--------------------------------------------------------------------------------
1 | import SearchIndex from 'appkit/utils/search-index';
2 |
3 | export default Ember.Route.extend({
4 |
5 | controllerName: 'reindex',
6 |
7 | beforeModel: function () {
8 |
9 | var data = this.controllerFor('wh.settings.data').get('dataBackup');
10 |
11 | if (Ember.isEmpty(data)) {
12 | this.transitionTo('wh.settings.data');
13 | } else {
14 | this.set('data', data);
15 | }
16 |
17 | var removeContentTypes = this.modelFor('wh').map(function (contentType) {
18 | return contentType.destroyRecord();
19 | });
20 |
21 | var removeSettings = this.store.find('settings').then(function (settings) {
22 | return settings.map(function (setting) {
23 | return setting.destroyRecord();
24 | });
25 | });
26 |
27 | var removeRef = function (ref) {
28 | new Ember.RSVP.Promise(function (resolve, reject) {
29 | window.ENV.firebase.child(ref).remove(function (error) {
30 | if (error) {
31 | reject(error);
32 | } else {
33 | resolve();
34 | }
35 | });
36 | });
37 | };
38 |
39 | return Ember.RSVP.all([
40 | removeContentTypes,
41 | removeRef('settings'),
42 | removeRef('data')
43 | ]);
44 |
45 | },
46 |
47 | model: function () {
48 | return this.modelFor('wh');
49 | },
50 |
51 | afterModel: function (model) {
52 |
53 | var data = this.get('data');
54 |
55 | return new Ember.RSVP.Promise(function (resolve, reject) {
56 | window.ENV.firebase.update(data, function (error) {
57 | if (error) {
58 | reject(error);
59 | } else {
60 | resolve();
61 | }
62 | });
63 | }).then(function () {
64 | SearchIndex.indexSite();
65 | });
66 | },
67 |
68 | actions: {
69 | willTransition: function (transition) {
70 |
71 | if (this.controller.get('isIndexing')) {
72 | Ember.Logger.log('Indexing in progress, aborting transition');
73 | transition.abort();
74 | window.history.forward();
75 | } else {
76 | Ember.Logger.log('Indexing complete, continue with transition.');
77 | return true;
78 | }
79 |
80 | }
81 | }
82 | });
83 |
--------------------------------------------------------------------------------
/app/routes/index.js:
--------------------------------------------------------------------------------
1 | export default Ember.Route.extend({
2 | beforeModel: function () {
3 |
4 | this._super.apply(this, arguments);
5 |
6 | if (Ember.isEmpty(this.get('session.user'))) {
7 | return;
8 | }
9 |
10 | var route = this;
11 |
12 | this.store.find('content-type').then(function (types) {
13 | if (types.get('length')) {
14 | route.transitionTo('wh');
15 | } else {
16 | route.transitionTo('start');
17 | }
18 | });
19 |
20 | }
21 | });
22 |
--------------------------------------------------------------------------------
/app/routes/login.js:
--------------------------------------------------------------------------------
1 | export default Ember.Route.extend({
2 | setupController: function (controller) {
3 | controller.setProperties({
4 | email: null,
5 | password: null
6 | });
7 | }
8 | });
9 |
--------------------------------------------------------------------------------
/app/routes/password-change.js:
--------------------------------------------------------------------------------
1 | export default Ember.Route.extend({
2 | setupController: function (controller) {
3 | controller.reset();
4 | }
5 | });
6 |
--------------------------------------------------------------------------------
/app/routes/reindex.js:
--------------------------------------------------------------------------------
1 | import SearchIndex from 'appkit/utils/search-index';
2 |
3 | export default Ember.Route.extend({
4 | model: function () {
5 | return this.store.find('content-type');
6 | },
7 |
8 | afterModel: function () {
9 | SearchIndex.indexSite();
10 | },
11 |
12 | actions: {
13 | willTransition: function (transition) {
14 |
15 | if (this.controller.get('isIndexing')) {
16 | Ember.Logger.log('Indexing in progress, aborting transition');
17 | transition.abort();
18 | window.history.forward();
19 | } else {
20 | Ember.Logger.log('Indexing complete, continue with transition.');
21 | return true;
22 | }
23 |
24 | }
25 | }
26 | });
27 |
--------------------------------------------------------------------------------
/app/routes/resend-email.js:
--------------------------------------------------------------------------------
1 | export default Ember.Route.extend({
2 | setupController: function (controller) {
3 | controller.setProperties({
4 | email : null,
5 | isSending: false,
6 | success : false,
7 | error : null
8 | });
9 | }
10 | });
11 |
--------------------------------------------------------------------------------
/app/routes/start.js:
--------------------------------------------------------------------------------
1 | export default Ember.Route.extend({
2 | beforeModel: function () {
3 | if (!this.get('buildEnvironment.local')) {
4 | this.transitionTo('wh');
5 | }
6 | this._super.apply(this, arguments);
7 | }
8 | });
9 |
--------------------------------------------------------------------------------
/app/routes/theme.js:
--------------------------------------------------------------------------------
1 | export default Ember.Route.extend({
2 | beforeModel: function () {
3 | if (!this.get('buildEnvironment.local')) {
4 | this.transitionTo('wh');
5 | }
6 | this._super.apply(this, arguments);
7 | },
8 | setupController: function (controller) {
9 | controller.setProperties({
10 | themes: window.ENV.themes,
11 | isSending: false,
12 | success : false,
13 | error : null
14 | });
15 | }
16 | });
17 |
--------------------------------------------------------------------------------
/app/routes/wh.js:
--------------------------------------------------------------------------------
1 | export default Ember.Route.extend({
2 | model: function () {
3 | return this.store.find('content-type');
4 | }
5 | });
6 |
--------------------------------------------------------------------------------
/app/routes/wh/content/all-types.js:
--------------------------------------------------------------------------------
1 | export default Ember.Route.extend({
2 | beforeModel: function () {
3 | if (!this.modelFor('wh').get('length')) {
4 | this.transitionTo('wh.content.start');
5 | }
6 | },
7 | model: function () {
8 | return this.modelFor('wh');
9 | }
10 | });
11 |
--------------------------------------------------------------------------------
/app/routes/wh/content/start.js:
--------------------------------------------------------------------------------
1 | export default Ember.Route.extend({
2 | model: function () {
3 | return this.modelFor('wh');
4 | }
5 | });
6 |
--------------------------------------------------------------------------------
/app/routes/wh/content/type/json.js:
--------------------------------------------------------------------------------
1 | export default Ember.Route.extend({
2 | model: function (params) {
3 | return this.store.find(this.modelFor('wh.content.type').get('itemModelName'), params.item_id);
4 | },
5 |
6 | setupController: function (controller, model) {
7 | controller.set('itemJSON', JSON.stringify(model.get('itemData'), null, 2));
8 | controller.set('saving', false);
9 | controller.set('error', null);
10 | this._super.apply(this, arguments);
11 | },
12 |
13 | actions: {
14 | cancel: function () {
15 | this.transitionTo('wh.content.type', this.modelFor('wh.content.type').get('itemModelName'));
16 | }
17 | }
18 | });
19 |
--------------------------------------------------------------------------------
/app/routes/wh/content/type/new.js:
--------------------------------------------------------------------------------
1 | import EditRoute from 'appkit/routes/wh/content/type/edit';
2 |
3 | export default EditRoute.extend({
4 | templateName: 'wh/content/type/edit'
5 | });
6 |
--------------------------------------------------------------------------------
/app/routes/wh/index.js:
--------------------------------------------------------------------------------
1 | export default Ember.Route.extend({
2 |
3 | defaultMessage: function () {
4 | var msg = '';
5 |
6 | msg += '#' + Em.I18n.t('wh.index.welcome.title') + "\n\n";
7 | msg += Em.I18n.t('wh.index.welcome.description') + "\n\n";
8 |
9 | msg += '* [%@](%@)'.fmt(Em.I18n.t('wh.index.welcome.docs'), "http://www.webhook.com/docs/") + "\n";
10 | msg += '* [%@](%@)'.fmt(Em.I18n.t('wh.index.welcome.support'), "http://www.webhook.com/help/") + "\n";
11 | msg += '* [%@](%@)'.fmt(Em.I18n.t('wh.index.welcome.issues'), "https://github.com/webhook/webhook/issues?state=open") + "\n";
12 | msg += '* [%@](%@)'.fmt(Em.I18n.t('wh.index.welcome.forums'), "http://forums.webhook.com/") + "\n";
13 |
14 | return msg;
15 | }.property(),
16 |
17 | beforeModel: function () {
18 | var route = this;
19 |
20 | return this.store.find('settings', 'general').then(function (settings) {
21 | if (Ember.isEmpty(settings.get('siteMessage'))) {
22 | settings.set('siteMessage', route.get('defaultMessage'));
23 | }
24 | route.set('settings', settings);
25 | }, function (error) {
26 | var settings = route.store.getById('settings', 'general');
27 | settings.loadedData();
28 | if (route.get('session.isOwner')) {
29 | return settings.save().then(function () {
30 | settings.set('siteMessage', route.get('defaultMessage'));
31 | route.set('settings', settings);
32 | });
33 | }
34 | });
35 |
36 | },
37 |
38 | setupController: function (controller) {
39 |
40 | this._super.apply(this, arguments);
41 |
42 | controller.set('contentTypes', this.modelFor('wh'));
43 | controller.set('settings', this.get('settings'));
44 | controller.set('defaultMessage', this.get('defaultMessage'));
45 | },
46 |
47 | actions: {
48 | willTransition: function (transition) {
49 | this.get('settings').rollback();
50 | }
51 | }
52 | });
53 |
--------------------------------------------------------------------------------
/app/routes/wh/search-global-results.js:
--------------------------------------------------------------------------------
1 | export default Ember.Route.extend({
2 | model: function () {
3 | return Ember.A([]);
4 | }
5 | });
6 |
--------------------------------------------------------------------------------
/app/routes/wh/settings/data.js:
--------------------------------------------------------------------------------
1 | export default Ember.Route.extend({
2 | model: function () {
3 |
4 | var siteName = this.get('session.site.name');
5 | var siteToken = this.get('session.site.token');
6 |
7 | return new Ember.RSVP.Promise(function (resolve, reject) {
8 | var backupsRef = window.ENV.firebaseRoot.child('management/backups');
9 | backupsRef.once('value', function (snapshot) {
10 |
11 | var backups = Ember.$.map(snapshot.val() || [], function (timestamp) {
12 | return {
13 | fileName: siteName + '-' + moment(timestamp).format() + '.json',
14 | url: window.ENV.uploadUrl + 'backup-snapshot/?site=' + siteName + '&token=' + siteToken + '×tamp=' + timestamp,
15 | timestamp: timestamp
16 | };
17 | });
18 |
19 | Ember.run(null, resolve, backups.reverse());
20 | });
21 | });
22 | },
23 |
24 | setupController: function (controller) {
25 | controller.set('deleteOption', 'data');
26 | controller.set('wordpressFile', null);
27 |
28 | // controller.set('dataBackup', null);
29 | controller.set('dataError', null);
30 |
31 | controller.set('downloadLink', window.ENV.uploadUrl + 'download/?site=' +this.get('session.site.name') + '&token=' + this.get('session.site.token'));
32 | controller.set('downloadFileName', this.get('session.site.name'));
33 |
34 | window.ENV.firebaseRoot.child('management/sites').child(this.get('session.site.name')).child('api-key').once('value', function(snap) {
35 | var val = snap.val();
36 |
37 | if(!val) {
38 | //Generate and set
39 | var newKey = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {var r = Math.random()*16|0,v=c==='x'?r:r&0x3|0x8;return v.toString(16);});
40 |
41 | window.ENV.firebaseRoot.child('management/sites').child(this.get('session.site.name')).child('api-key').set(newKey, function(err) {
42 | controller.set('apiKey', newKey);
43 | }.bind(this));
44 | } else {
45 | controller.set('apiKey', val);
46 | }
47 | }.bind(this));
48 |
49 | return this._super.apply(this, arguments);
50 | },
51 |
52 | actions: {
53 | reindex: function () {
54 | this.transitionTo('reindex');
55 | }
56 | }
57 | });
58 |
--------------------------------------------------------------------------------
/app/routes/wh/settings/domain.js:
--------------------------------------------------------------------------------
1 | export default Ember.Route.extend({
2 |
3 | setupController: function (controller) {
4 |
5 | var siteName = this.get('session.site.name');
6 |
7 | controller.setProperties({
8 | domain : "",
9 | isSending: false,
10 | success : false,
11 | errors : Ember.A([]),
12 | });
13 |
14 | window.ENV.firebaseRoot.child("management/sites/" + siteName + "/dns").once('value', function(snapshot) {
15 | controller.set('domain', snapshot.val() || '');
16 | });
17 |
18 | this._super.apply(this, arguments);
19 | },
20 | });
21 |
--------------------------------------------------------------------------------
/app/routes/wh/settings/general.js:
--------------------------------------------------------------------------------
1 | // import Settings from 'appkit/models/settings';
2 |
3 | export default Ember.Route.extend({
4 | model: function () {
5 | var route = this;
6 | return this.store.find('settings', 'general').then(null, function () {
7 | var settings = route.store.getById('settings', 'general');
8 | settings.loadedData();
9 | return settings;
10 | });
11 | }
12 | });
13 |
--------------------------------------------------------------------------------
/app/routes/wh/settings/team.js:
--------------------------------------------------------------------------------
1 | export default Ember.Route.extend({
2 |
3 | beforeModel: function () {
4 |
5 | this._super.apply(this, arguments);
6 |
7 | // Check to see if site has been deployed
8 | if (!this.get('session.serverMessages.length')) {
9 |
10 | var route = this;
11 | var siteName = this.get('session.site.name');
12 |
13 | return new Ember.RSVP.Promise(function (resolve, reject) {
14 |
15 | window.ENV.firebaseRoot.child('management/sites/' + siteName + '/messages').limitToLast(10).once('value', function (snapshot) {
16 |
17 | snapshot.forEach(function (childSnapshot) {
18 | var message = Ember.$.extend({}, childSnapshot.val(), { id: childSnapshot.key() });
19 | if (typeof message.status !== 'undefined' && message.status === 0) {
20 | route.set('session.isDeployed', true);
21 | }
22 | });
23 |
24 | resolve();
25 |
26 | });
27 |
28 | });
29 |
30 | }
31 |
32 | },
33 |
34 | model: function () {
35 | return this.get('team.users');
36 | },
37 |
38 | setupController: function (controller) {
39 | controller.set('groups', this.get('team.groups'));
40 | controller.set('contentTypes', this.modelFor('wh'));
41 | controller.set('multiContentTypes', this.modelFor('wh').rejectBy('oneOff'));
42 | controller.set('singleContentTypes', this.modelFor('wh').filterBy('oneOff'));
43 |
44 | this._super.apply(this, arguments);
45 | }
46 | });
47 |
--------------------------------------------------------------------------------
/app/routes/wh/settings/urls.js:
--------------------------------------------------------------------------------
1 | export default Ember.Route.extend({
2 | model: function () {
3 | return this.store.find('redirect').then(function (redirects) {
4 | redirects.forEach(function (redirect, index) {
5 | redirect.set('priority', index);
6 | });
7 | return Ember.RSVP.Promise.resolve(redirects);
8 | });
9 | },
10 | setupController: function (controller) {
11 |
12 | var siteName = this.get('session.site.name');
13 |
14 | window.ENV.firebaseRoot.child("management/sites/" + siteName + "/dns").once('value', function(snapshot) {
15 | controller.set('domain', snapshot.val());
16 | });
17 |
18 | this._super.apply(this, arguments);
19 |
20 | }
21 | });
22 |
--------------------------------------------------------------------------------
/app/routes/wordpress.js:
--------------------------------------------------------------------------------
1 | export default Ember.Route.extend({
2 | setupController: function (controller) {
3 |
4 | controller.set('wxmlDoneClass', 'pending');
5 | controller.set('wxmlStatus', {
6 | messages: false,
7 | parsingXML: { running: false, class: 'pending' },
8 | siteInfo: { running: false, class: 'pending' },
9 | tags: { running: false, class: 'pending' },
10 | authors: { running: false, class: 'pending' },
11 | images: { running: false, class: 'pending' },
12 | posts: { running: false, class: 'pending' },
13 | pages: { running: false, class: 'pending' },
14 | firebase: { running: false, class: 'pending' },
15 | search: { running: false, class: 'pending' },
16 | });
17 |
18 | controller.set('isComplete', false);
19 |
20 | controller.convertXml();
21 |
22 | return this._super.apply(this, arguments);
23 | }
24 | });
25 |
--------------------------------------------------------------------------------
/app/serializers/application.js:
--------------------------------------------------------------------------------
1 | export default DS.FirebaseSerializer.extend({
2 |
3 | // fix legacy webhook relationships for emberfire
4 | // example: { "controls": { "control_id": true } }
5 | normalize: function (type, payload) {
6 |
7 | type.eachRelationship(function (key, relationship) {
8 | if (relationship.options.embedded && Ember.isArray(payload[key])) {
9 | var payloadKeyObject = {};
10 | payload[key].forEach(function (embed, index) {
11 | payloadKeyObject[window.ENV.firebase.push().key()] = embed;
12 | });
13 | payload[key] = payloadKeyObject;
14 | }
15 | });
16 |
17 | return this._super.apply(this, [type, payload]);
18 | },
19 |
20 | // firebase throws a fit with Ember's prototype extensions.
21 | // this returns the object to a native object
22 | serialize: function () {
23 | var jsonDirty = this._super.apply(this, arguments);
24 | var jsonClean = JSON.parse(JSON.stringify(jsonDirty));
25 | return jsonClean;
26 | }
27 |
28 | });
29 |
--------------------------------------------------------------------------------
/app/serializers/control-type-group.js:
--------------------------------------------------------------------------------
1 | export default DS.JSONSerializer.extend();
2 |
--------------------------------------------------------------------------------
/app/serializers/control-type.js:
--------------------------------------------------------------------------------
1 | export default DS.JSONSerializer.extend();
2 |
--------------------------------------------------------------------------------
/app/serializers/control.js:
--------------------------------------------------------------------------------
1 | import ApplicationSerializer from 'appkit/serializers/application';
2 |
3 | export default ApplicationSerializer.extend({
4 | serialize: function (record) {
5 |
6 | var serializedSubControls = [];
7 |
8 | record.get('controls').forEach(function (control) {
9 | serializedSubControls.push(control.serialize());
10 | });
11 |
12 | var json = this._super.apply(this, arguments);
13 |
14 | json.controls = serializedSubControls;
15 |
16 | return json;
17 | }
18 | });
19 |
--------------------------------------------------------------------------------
/app/serializers/data.js:
--------------------------------------------------------------------------------
1 | export default DS.JSONSerializer.extend({
2 | normalize: function (type, hash) {
3 | var newHash;
4 |
5 | // Was using Ember.isArray, but user had `length` as a field name.
6 | if (Ember.$.isArray(hash)) {
7 | newHash = Ember.$.map(hash, this._normalizeSingle);
8 | } else {
9 | newHash = this._normalizeSingle(hash);
10 | }
11 |
12 | return this._super(type, newHash);
13 | },
14 | serialize: function (record, options) {
15 | return JSON.parse(JSON.stringify(record.get('itemData')));
16 | },
17 | _normalizeSingle: function (hash) {
18 | var newHash = { itemData: {} };
19 |
20 | Ember.$.each(hash, function(key, value) {
21 | if (key === 'id') {
22 | newHash[key] = value;
23 | } else {
24 | newHash.itemData[key] = value;
25 | }
26 | });
27 |
28 | return newHash;
29 | }
30 | });
31 |
--------------------------------------------------------------------------------
/app/serializers/item.js:
--------------------------------------------------------------------------------
1 | import DataSerializer from 'appkit/serializers/data';
2 |
3 | export default DataSerializer.extend();
4 |
--------------------------------------------------------------------------------
/app/serializers/settings.js:
--------------------------------------------------------------------------------
1 | export default DS.JSONSerializer.extend({
2 | keyForAttribute: function(attr) {
3 | return Ember.String.underscore(attr);
4 | },
5 | normalize: function (type, payload) {
6 |
7 | var normalizedPayload = {};
8 |
9 | Ember.$.each(payload, function(key, value) {
10 | normalizedPayload[Ember.String.camelize(key)] = value;
11 | });
12 |
13 | return this._super(type, normalizedPayload);
14 | }
15 | });
16 |
--------------------------------------------------------------------------------
/app/styles/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/webhook/webhook-cms/8b58110801bc0d3a749bb230bb9983ee5b77ffcd/app/styles/.gitkeep
--------------------------------------------------------------------------------
/app/styles/_app_fonts.sass:
--------------------------------------------------------------------------------
1 | @font-face
2 | font-family: 'Bitter'
3 | src: url('#{$font-path}/bitter.woff')
4 | font-weight: normal
5 | font-style: normal
6 |
7 | @font-face
8 | font-family: 'Bitter'
9 | src: url('#{$font-path}/bitter-bold.woff')
10 | font-weight: bold
11 | font-style: normal
12 |
--------------------------------------------------------------------------------
/app/styles/_app_mixin.sass:
--------------------------------------------------------------------------------
1 | // Mixin to build the WH logo, variable is defined in app_variables.sass
2 |
3 | =wh-logo($logo-size: 45px, $logo-color: $link-color)
4 | background-image: $wh-logo
5 | width: $logo-size
6 | height: $logo-size
7 | background-color: $logo-color
8 | background-size: $logo-size * .6 $logo-size * .48
9 | background-position: center center
10 | background-repeat: no-repeat
11 | margin: auto
12 | border-radius: 100%
13 | display: block
14 | vertical-align: middle
15 |
16 |
--------------------------------------------------------------------------------
/app/templates/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/webhook/webhook-cms/8b58110801bc0d3a749bb230bb9983ee5b77ffcd/app/templates/.gitkeep
--------------------------------------------------------------------------------
/app/templates/application.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{#each notifications}}
3 |
6 | {{/each}}
7 |
8 |
9 |
10 |
11 |
Restart your runserver, then refresh
12 |
Or just click here when you've restarted
13 |
14 | Your local runserver died most likely because of a template error in your frontend code. Unfortunately, this
15 | means that saves you make in the current tab will not regenerate the site until you refresh this page AFTER
16 | you've restarted your runserver. It's a shitty bug, and we're fixing it. For now, bear with us.
17 |
18 |
19 |
20 |
21 |
22 | {{outlet}}
23 |
--------------------------------------------------------------------------------
/app/templates/component-test.hbs:
--------------------------------------------------------------------------------
1 | {{#each}}
2 | {{pretty-color name=this}}
3 | {{/each}}
4 |
--------------------------------------------------------------------------------
/app/templates/components/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/webhook/webhook-cms/8b58110801bc0d3a749bb230bb9983ee5b77ffcd/app/templates/components/.gitkeep
--------------------------------------------------------------------------------
/app/templates/components/audio-player.hbs:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/templates/components/audio-upload.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{#if control.value.url}}
4 |
{{ control.value.url }}
5 | {{#unless control.disabled}}
{{/unless}}
6 | {{audio-player src=control.value.url load="audioLoaded"}}
7 | {{/if}}
8 |
9 | {{#unless control.disabled}}
10 |
23 |
24 |
25 | {{t 'form.control.file.paste'}}
26 | Upload
27 | {{t 'form.control.file.cancel'}}
28 |
29 | {{/unless}}
30 |
31 |
32 |
--------------------------------------------------------------------------------
/app/templates/components/auto-complete.hbs:
--------------------------------------------------------------------------------
1 | {{#if isFew}}
2 | {{view 'sortable'
3 | content=currentSelection
4 | sortArray=control.value
5 | itemTemplate='widgets/relation/few'
6 | tagName='span'
7 | classNames='current-selection'
8 | itemTagName='span'
9 | itemClassNameBindings=':wy-tag content.justAdded:active'
10 | disabled=control.disabled
11 | }}
12 | {{/if}}
13 |
14 | {{#if isMany}}
15 | {{view 'sortable'
16 | content=currentSelection
17 | sortArray=control.value
18 | itemTemplate='widgets/relation/many'
19 | itemClassNameBindings='content.justAdded:active'
20 | disabled=control.disabled
21 | }}
22 | {{/if}}
23 |
24 | {{#if showAutocomplete}}
25 |
26 |
27 |
28 | {{view "auto-complete" placeholder="Add item" filter=control.meta.contentTypeId value=autocompleteValue}}
29 |
30 | {{#if isLoading}}
31 |
32 | {{/if}}
33 |
34 | {{#collection "autocomplete-results" content=results}}
35 | {{{name}}}
36 | {{/collection}}
37 |
38 |
39 | {{/if}}
40 |
--------------------------------------------------------------------------------
/app/templates/components/colored-element.hbs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/webhook/webhook-cms/8b58110801bc0d3a749bb230bb9983ee5b77ffcd/app/templates/components/colored-element.hbs
--------------------------------------------------------------------------------
/app/templates/components/date-time.hbs:
--------------------------------------------------------------------------------
1 | {{#unless meta.hideDate}}
2 | {{input type="date" value=date placeholder="mm/dd/yyyy" disabled=disabled}}
3 | {{/unless}}
4 |
5 | {{#unless meta.hideTime}}
6 | {{input type="time" value=time placeholder="hh:mm:ss" disabled=disabled}}
7 | {{/unless}}
8 |
--------------------------------------------------------------------------------
/app/templates/components/download-backup.hbs:
--------------------------------------------------------------------------------
1 | {{#if text}}{{text}}{{else}}{{yield}}{{/if}}
2 |
--------------------------------------------------------------------------------
/app/templates/components/embedly-control.hbs:
--------------------------------------------------------------------------------
1 | {{#unless control.disabled}}
2 | {{input type="url" value=url disabled=isFetching action="getEmbed"}}
3 |
4 | {{t 'form.control.embedly.action'}} {{#if isFetching}} {{/if}}
5 | {{/unless}}
6 |
7 | {{#if hasValue}}
8 |
9 | {{#if showPreview}}
10 | JSON
11 | {{/if}}
12 |
13 | {{#if showCode}}
14 | {{t 'form.control.embedly.preview'}}
15 | {{/if}}
16 |
17 | {{#unless control.disabled}} {{t 'form.control.embedly.clear'}} {{/unless}}
18 |
19 |
20 | {{#if showPreview}}
21 | {{#if isVisual}}
22 |
23 |
{{{previewValue}}}
24 |
25 | {{else}}
26 |
{{{previewValue}}}
27 | {{/if}}
28 | {{/if}}
29 |
30 | {{#if showCode}}
31 |
{{dataString}}
32 | {{/if}}
33 |
34 |
35 | {{/if}}
36 |
--------------------------------------------------------------------------------
/app/templates/components/file-upload.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{#if control.value.url}}
4 |
{{ control.value.url }}
5 | {{#unless control.disabled}}
{{/unless}}
6 | {{/if}}
7 |
8 | {{#unless control.disabled}}
9 |
10 |
23 |
24 |
25 | {{t 'form.control.file.paste'}}
26 | {{t 'form.control.file.upload'}}
27 | {{t 'form.control.file.cancel'}}
28 |
29 |
30 | {{/unless}}
31 |
32 |
33 |
--------------------------------------------------------------------------------
/app/templates/components/formbuilder-widget.hbs:
--------------------------------------------------------------------------------
1 | {{#if view.useDefaultLayout}}
2 |
3 | {{partial 'widgets/common/label'}}
4 |
5 |
6 | {{!-- Grid widget help is above the control. --}}
7 | {{#if view.isGridWidget}}
8 | {{partial 'widgets/common/help'}}
9 | {{/if}}
10 |
11 | {{partial view.content.controlType.controlPartialPath}}
12 |
13 | {{!-- All other widgets have the help below. --}}
14 | {{#unless view.isGridWidget}}
15 | {{partial 'widgets/common/help'}}
16 | {{/unless}}
17 |
18 | {{#if view.content.widgetErrors}}
19 |
20 | {{#each view.content.widgetErrors}}
21 | {{this}}
22 | {{/each}}
23 |
24 | {{/if}}
25 |
26 |
27 |
28 | {{else}}
29 |
30 |
31 | {{partial view.content.controlType.controlPartialPath}}
32 |
33 |
34 | {{/if}}
35 |
36 | {{!-- we only set `doEdit` on the formbuilder --}}
37 |
38 |
--------------------------------------------------------------------------------
/app/templates/components/geolocation-control.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{input action="parseInput" value=coordsString placeholder="Enter lat, lng coordinates or Google Maps URL" disabled=control.disabled}}
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/templates/components/grid-control.hbs:
--------------------------------------------------------------------------------
1 | {{#if control.isInFormbuilder}}
2 |
3 |
4 |
5 |
6 | 0
7 | {{t 'form.control.grid.valueName'}}
8 |
9 |
18 |
19 |
20 | {{else}}
21 |
22 | {{view "grid-rows" gridControl=control}}
23 |
24 | {{#if control.isPlaceholder}}
25 |
26 | {{/if}}
27 |
28 | {{/if}}
29 |
--------------------------------------------------------------------------------
/app/templates/components/image-upload.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
38 |
39 | {{#unless control.disabled}}
40 |
41 | {{t 'form.control.file.paste'}}
42 | {{t 'form.control.file.upload'}}
43 | {{t 'form.control.file.cancel'}}
44 |
45 | {{/unless}}
46 |
47 |
48 |
--------------------------------------------------------------------------------
/app/templates/components/markdown-editor.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{t 'form.control.markdown.syntax'}}
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | {{textarea placeholder=placeholder class="wh-form-markdown" value=value disabled=disabled}}
12 |
13 |
14 |
15 |
16 |
17 | {{webhook-modal
18 | show=showImageModal
19 | modalTemplate="widgets/common/imagemodal"
20 | session=session
21 | confirm="handleUpload"
22 | fakeControl=fakeImageControl
23 | }}
24 |
--------------------------------------------------------------------------------
/app/templates/components/pretty-color.hbs:
--------------------------------------------------------------------------------
1 | Pretty Color: {{name}}
2 |
--------------------------------------------------------------------------------
/app/templates/components/redactor-rte.hbs:
--------------------------------------------------------------------------------
1 | {{textarea id=id placeholder=placeholder disabled=disabled}}
2 |
3 | {{webhook-modal
4 | show=showImageModal
5 | modalTemplate="widgets/common/imagemodal"
6 | session=session
7 | confirm="handleUpload"
8 | fakeControl=fakeImageControl
9 | }}
10 |
--------------------------------------------------------------------------------
/app/templates/components/tabular-data.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{#each option in control.meta.options}}
5 | {{option.value}}
6 | {{/each}}
7 | {{#unless control.disabled}} {{/unless}}
8 |
9 |
10 |
11 | {{#each row in control.value}}
12 |
13 | {{#each data in row}}
14 | {{input value=data.value disabled=control.disabled}}
15 | {{/each}}
16 | {{#unless control.disabled}} {{/unless}}
17 |
18 | {{/each}}
19 |
20 |
21 |
22 | {{#unless control.disabled}} {{t 'form.control.tabular.add'}} {{/unless}}
23 |
--------------------------------------------------------------------------------
/app/templates/components/webhook-blog.hbs:
--------------------------------------------------------------------------------
1 |
2 |
{{t 'wh.index.updates'}}
3 |
14 |
15 |
--------------------------------------------------------------------------------
/app/templates/components/webhook-modal.hbs:
--------------------------------------------------------------------------------
1 | {{#if canClose}} {{/if}}
2 | {{partial modalTemplate}}
3 |
--------------------------------------------------------------------------------
/app/templates/confirm-email.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{#if isSending}}
3 |
4 | {{else}}
5 |
6 | {{/if}}
7 |
28 |
29 |
--------------------------------------------------------------------------------
/app/templates/create-user.hbs:
--------------------------------------------------------------------------------
1 |
42 |
--------------------------------------------------------------------------------
/app/templates/error.hbs:
--------------------------------------------------------------------------------
1 | Sorry, Something went wrong
2 | {{message}}
3 |
4 | {{stack}}
5 |
6 |
--------------------------------------------------------------------------------
/app/templates/expired.hbs:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/app/templates/form/_changedcontrols.hbs:
--------------------------------------------------------------------------------
1 |
2 | Are you sure you want to do this?
3 |
4 |
5 |
6 | Looks like you're making changes to existing data. This is normally safe, but we suggest downloading a backup before making this change. Also, don't forget to replicate these changes in your templates as well.
7 |
8 | {{download-backup classNames="btn btn-small btn-neutral icon icon-download" text=" Download a quick backup first"}}
9 |
10 |
11 | Yes, make the changes
12 | Cancel
13 |
14 |
--------------------------------------------------------------------------------
/app/templates/form/_initialscaffolding.hbs:
--------------------------------------------------------------------------------
1 |
2 | Templates created
3 |
4 |
5 |
6 | {{#if contentType.oneOff}}
7 | We automatically created a starter template for you. You can find it at
8 | /{{buildEnvironment.siteDisplayName}}/pages/{{contentType.id}}.html
.
9 | It will teach you the basics of pulling content into your site from the CMS.
10 | {{else}}
11 | We automatically created starter templates in your
12 | /{{buildEnvironment.siteDisplayName}}/templates/{{contentType.id}}/
13 | folder. They will teach you the basics of pulling content into your
14 | site from the CMS.
15 | {{/if}}
16 |
17 |
18 |
19 |
22 |
--------------------------------------------------------------------------------
/app/templates/grid-row.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{view.rowIndex}}
4 |
5 | {{#view 'item-cell' context=view.firstControl tagName='span'}}
6 | {{partial controlType.valuePartialPath}}
7 | {{/view}}
8 |
9 |
10 |
11 |
12 |
13 | {{#if view.isActive}}
14 |
28 | {{/if}}
29 |
--------------------------------------------------------------------------------
/app/templates/helper-test.hbs:
--------------------------------------------------------------------------------
1 | My name is {{reverse-word name}}.
2 |
--------------------------------------------------------------------------------
/app/templates/import.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Importing can take several minutes while we add your content to the CMS search engine. Please do not close your browser until complete.
4 |
5 | {{#each contentType in arrangedContent}}
6 |
7 | {{#if contentType.isIndexing}} {{/if}}
8 | {{contentType.name}} {{#if contentType.isIndexing}}{{contentType.indexingPercent}}%{{/if}}
9 |
10 | {{/each}}
11 |
12 | {{#unless isIndexing}}{{#link-to 'wh.settings.data' class="btn btn-success icon icon-ok-sign"}} Complete. Return to CMS.{{/link-to}}{{/unless}}
13 |
14 |
--------------------------------------------------------------------------------
/app/templates/index.hbs:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/templates/loading.hbs:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/templates/login.hbs:
--------------------------------------------------------------------------------
1 |
32 |
33 |
34 | {{#each supportedLanguages}}
35 |
36 | {{language}}
37 |
38 | {{/each}}
39 |
40 |
41 | {{t "login.addLanguage" }} .
42 |
43 |
--------------------------------------------------------------------------------
/app/templates/password-reset.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{#unless error}}
4 | {{#unless success}}
5 |
{{t 'passwordReset.provideEmail'}}
6 | {{/unless}}
7 | {{/unless}}
8 | {{#if error}}
9 |
10 |
{{error.code}}
11 | {{error.message}}
12 |
13 | {{/if}}
14 | {{#if success}}
15 |
{{t 'passwordReset.success'}}
16 | {{else}}
17 |
29 | {{/if}}
30 |
31 |
--------------------------------------------------------------------------------
/app/templates/reindex.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Reindexing can take several minutes. Please do not close your browser until complete.
4 |
5 | {{#each contentType in arrangedContent}}
6 |
7 | {{#if contentType.isIndexing}} {{/if}}
8 | {{contentType.name}} {{#if contentType.isIndexing}}{{contentType.indexingPercent}}%{{/if}}
9 |
10 | {{/each}}
11 |
12 | {{#unless isIndexing}}{{#link-to 'wh.settings.data' class="btn btn-success icon icon-ok-sign"}} Complete. Return to CMS.{{/link-to}}{{/unless}}
13 |
14 |
--------------------------------------------------------------------------------
/app/templates/resend-email.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{#if isSending}}
3 |
4 | {{else}}
5 |
6 | {{/if}}
7 |
32 |
33 |
--------------------------------------------------------------------------------
/app/templates/start.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{t 'start.title'}}
4 |
5 |
6 |
7 | {{#link-to 'wh.content.all-types' class="btn btn-neutral icon icon-leaf"}} {{t 'start.empty.action'}}{{/link-to}}
8 |
9 |
10 | {{t 'start.empty.instruction'}}
11 |
12 |
13 |
14 | {{#link-to 'theme' class="btn btn-neutral icon icon-rocket"}} {{t 'start.theme.action'}}{{/link-to}}
15 |
16 |
17 | {{t 'start.theme.instruction'}}
18 |
19 |
20 |
21 | {{#select-file action='importWordpressFile' accept='application/xml' class="icon icon-wordpress"}} {{t 'start.wordpress.action'}}{{/select-file}}
22 |
23 |
24 | {{t 'start.wordpress.instruction'}}
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/templates/wh.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{partial "wh/nav"}}
4 |
5 |
6 |
7 |
8 |
9 |
10 | Webhook
11 |
12 |
13 | {{#if buildEnvironment.local}}
14 | {{t 'wh.localNotice'}}
15 | {{/if}}
16 |
17 |
18 |
19 | {{outlet}}
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/templates/wh/content/all-types.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{#link-to 'wh.content.start' class="btn icon icon-plus-sign btn-preview btn-small btn-success"}} {{t 'wh.content.allTypes.addType'}}{{/link-to}}
3 | {{t 'wh.content.allTypes.title'}}
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | {{t 'wh.content.allTypes.contentType'}}
12 | {{t 'wh.content.allTypes.id'}}
13 | {{t 'wh.content.allTypes.formStyle'}}
14 | {{t 'wh.content.allTypes.actions'}}
15 |
16 |
17 |
18 | {{#each}}
19 | {{#link-to 'form' id tagName='tr'}}
20 | {{name}}
21 | {{id}}
22 | {{#if oneOff}}{{t 'wh.content.allTypes.single'}}{{else}}{{t 'wh.content.allTypes.multiple'}}{{/if}}
23 |
24 | {{t 'wh.content.allTypes.rebuild'}}
25 |
26 |
27 | {{/link-to}}
28 | {{/each}}
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/app/templates/wh/content/start.hbs:
--------------------------------------------------------------------------------
1 |
37 |
--------------------------------------------------------------------------------
/app/templates/wh/content/type/json.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 | Editing {{content.itemData.name}} JSON
4 |
5 |
6 |
7 |
8 |
9 |
10 | {{#if error}}
11 |
12 |
{{error.name}}
13 |
{{error.message}}
14 |
Try linting your JSON at jsonlint.com or another linting service.
15 |
16 | {{else}}
17 |
18 |
Warning
19 |
This is an advanced feature only available to site owners to directly manipulate data. Consult Webhook Documentation and proceed with caution.
20 |
21 | If you would like to remove a value, set it to null
.
22 | There are no data validations or safeguards when saving.
23 | Reverse relationships are not created when saving.
24 |
25 |
26 | {{/if}}
27 |
28 |
29 |
30 |
JSON
31 |
{{textarea value=itemJSON rows=30}}
32 |
33 |
34 |
35 |
36 | Cancel
37 | Save
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/app/templates/wh/content/type/loading.hbs:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/templates/wh/index.hbs:
--------------------------------------------------------------------------------
1 | {{#if noTypesLive}}
2 |
3 |
{{t 'wh.index.welcome.noTypesTitle'}}
4 |
{{t 'wh.index.welcome.noTypesMessage'}}
5 |
6 | {{else}}
7 |
8 | {{#unless isEditingMessage}}
9 |
10 | {{to-markdown settings.siteMessage}}
11 | {{#if session.isOwner}}
12 | {{t 'wh.index.welcome.edit'}}
13 | {{/if}}
14 |
15 | {{else}}
16 |
17 | {{markdown-editor value=settings.siteMessage session=session}}
18 |
19 |
20 | {{t 'wh.index.welcome.save'}}
21 | {{t 'wh.index.welcome.cancel'}}
22 |
23 | {{/unless}}
24 |
25 |
26 |
27 | {{#if buildEnvironment.local}}
28 | {{webhook-blog}}
29 | {{/if}}
30 |
31 | {{#if session.serverMessages}}
32 |
33 | {{t 'wh.index.history'}}
34 |
35 |
36 |
37 |
38 | Type
39 | Message
40 | Date
41 |
42 |
43 |
44 | {{#each session.serverMessages}}
45 |
46 | {{code}}
47 | {{message}}
48 | {{format-time timestamp}}
49 |
50 | {{/each}}
51 |
52 |
53 |
54 | {{#if moreServerMessages}}{{t 'wh.index.moreHistory'}} {{/if}}
55 |
56 | {{/if}}
57 | {{/if}}
58 |
--------------------------------------------------------------------------------
/app/templates/wh/search-global-results.hbs:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 | {{#if isLoading}}
8 |
9 | {{else}}
10 |
11 | {{#if debouncedQuery}}
12 | {{#if searchResults}}
13 | Search results for {{debouncedQuery}}.
14 | {{else}}
15 | No results for {{debouncedQuery}}.
16 | {{/if}}
17 | {{else}}
18 | Please enter a search query.
19 | {{/if}}
20 |
21 | {{#if searchResults}}
22 |
23 | {{#each searchResults}}
24 |
25 | {{#link-to 'wh.content.type.edit' type id }}
26 | {{{ name }}}
27 | {{ type }}
28 | {{/link-to}}
29 |
30 | {{#each highlights}}
31 | {{ key }}
32 | {{{ highlight }}}
33 | {{/each}}
34 |
35 |
36 | {{/each}}
37 |
38 | {{/if}}
39 |
40 | {{/if}}
41 |
42 |
--------------------------------------------------------------------------------
/app/templates/wh/settings/billing.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{t 'wh.settings.billing.title'}}
3 |
4 |
5 | {{#if session.billing.isTrial }}
6 |
7 | {{#if session.billing.endTrialIsLastDay}}
8 | {{t 'wh.settings.billing.trial.lastDay'}}
9 | {{else}}
10 | {{t 'wh.settings.billing.trial.days' countBinding="session.billing.endTrialDays"}}
11 | {{/if}}
12 |
13 | {{t 'wh.settings.billing.trial.instruction'}}
14 | {{t 'wh.settings.billing.trial.action'}}
15 | {{/if}}
16 | {{#if session.billing.isPaid }}
17 | {{t 'wh.settings.billing.paid.title'}}
18 | {{t 'wh.settings.billing.paid.instruction'}}
19 | {{t 'wh.settings.billing.paid.action'}}
20 | {{/if}}
21 |
22 |
--------------------------------------------------------------------------------
/app/templates/wh/settings/group-permissions.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{view.content.name}}
3 |
4 | {{#unless view.content.oneOff}}
5 |
6 | {{/unless}}
7 |
8 | {{#unless view.content.oneOff}}
9 |
10 | {{/unless}}
11 |
12 |
--------------------------------------------------------------------------------
/app/templates/wh/settings/urls-rule.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{input value=view.content.pattern placeholder="^/blog/([^/]+)/" classNameBindings="view.content.isValid::invalid" }}
4 | {{#unless view.content.isValid}}
5 | Invalid regular expression
6 | {{/unless}}
7 |
8 | {{input value=view.content.destination placeholder="/articles/$1/" }}
9 |
10 |
--------------------------------------------------------------------------------
/app/templates/wh/settings/urls.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{t 'wh.settings.urls.title'}}
3 |
4 |
5 |
6 |
7 |
{{t 'wh.settings.urls.infoTitle'}}
8 |
9 | {{t 'wh.settings.urls.infoMessage'}}
10 |
11 |
12 | {{t 'wh.settings.urls.infoRuleOne'}}
13 | {{t 'wh.settings.urls.infoRuleTwo'}}
14 |
15 |
16 |
17 | {{#unless domain}}
18 |
19 |
{{t 'wh.settings.urls.noDomainTitle'}}
20 |
{{t 'wh.settings.urls.noDomainMessage'}}
21 |
22 | {{/unless}}
23 |
24 |
25 |
26 |
27 |
28 | {{t 'wh.settings.urls.tableRedirect'}}
29 | {{t 'wh.settings.urls.tableDestination'}}
30 |
31 |
32 |
33 | {{view 'sortable-redirect-rules' content=arrangedContent}}
34 |
35 |
36 | {{t 'wh.settings.urls.tableAddRow'}}
37 |
38 |
39 |
40 | {{t 'wh.settings.urls.action'}}
41 |
42 |
43 |
--------------------------------------------------------------------------------
/app/templates/widgets/_address.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{input placeholder="Street address 1" class="street1" value=view.content.value.street1 disabled=view.content.disabled}}
3 |
4 |
5 | {{input placeholder="address 2" class="street2" value=view.content.value.street2 disabled=view.content.disabled}}
6 |
7 |
8 | {{input placeholder="City" class="city" value=view.content.value.city disabled=view.content.disabled}}
9 |
10 |
11 | {{input placeholder="State / province / region" class="state" value=view.content.value.state disabled=view.content.disabled}}
12 |
13 |
14 | {{input placeholder="Postal / zip code" class="postal" value=view.content.value.zip disabled=view.content.disabled}}
15 |
16 |
17 | {{input placeholder="Country" class="country" value=view.content.value.country disabled=view.content.disabled}}
18 |
19 |
--------------------------------------------------------------------------------
/app/templates/widgets/_audio.hbs:
--------------------------------------------------------------------------------
1 | {{audio-upload control=view.content session=controller.session}}
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/_boolean.hbs:
--------------------------------------------------------------------------------
1 | {{#view 'switch' control=view.content}}
2 |
3 | {{#if view.control.value}}
4 | {{#if view.control.meta.trueLabel}}
5 | {{view.control.meta.trueLabel}}
6 | {{else}}
7 | true
8 | {{/if}}
9 | {{else}}
10 | {{#if view.control.meta.falseLabel}}
11 | {{view.control.meta.falseLabel}}
12 | {{else}}
13 | false
14 | {{/if}}
15 | {{/if}}
16 |
17 | {{/view}}
18 |
--------------------------------------------------------------------------------
/app/templates/widgets/_checkbox.hbs:
--------------------------------------------------------------------------------
1 | {{#each option in view.content.meta.options}}
2 |
3 | {{view 'checkbox-control'
4 | checked=option.value
5 | control=view.content
6 | option=option
7 | disabled=disabled
8 | }}
9 | {{option.label}}
10 |
11 | {{/each}}
12 |
--------------------------------------------------------------------------------
/app/templates/widgets/_color.hbs:
--------------------------------------------------------------------------------
1 | {{input type="color" value=view.content.value disabled=view.content.disabled}}
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/_datetime.hbs:
--------------------------------------------------------------------------------
1 | {{date-time
2 | value=view.content.value
3 | meta=view.content.meta
4 | disabled=view.content.disabled
5 | }}
6 |
7 | {{#unless view.content.meta.hideDate}}
8 | {{#unless view.content.meta.hideTime}}
9 | {{format-time view.content.value}}
10 | {{/unless}}
11 | {{/unless}}
12 |
--------------------------------------------------------------------------------
/app/templates/widgets/_email.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{input type="email" placeholder=view.content.placeholder value=view.content.value disabled=view.content.disabled}}
3 |
4 |
--------------------------------------------------------------------------------
/app/templates/widgets/_embedly.hbs:
--------------------------------------------------------------------------------
1 | {{embedly-control control=view.content}}
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/_file.hbs:
--------------------------------------------------------------------------------
1 | {{file-upload control=view.content session=controller.session}}
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/_gallery.hbs:
--------------------------------------------------------------------------------
1 | {{gallery-upload control=view.content session=controller.session}}
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/_geolocation.hbs:
--------------------------------------------------------------------------------
1 | {{geolocation-control control=view.content}}
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/_grid.hbs:
--------------------------------------------------------------------------------
1 | {{grid-control control=view.content store=store formController=controller contentType=type}}
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/_image.hbs:
--------------------------------------------------------------------------------
1 | {{image-upload control=view.content session=controller.session}}
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/_instruction.hbs:
--------------------------------------------------------------------------------
1 | {{#if view.content.label}}{{view.content.label}}{{else}}{{view.content.controlType.name}}{{/if}}
2 |
3 | {{#if view.content.widgetErrors}}
4 | {{#each view.content.widgetErrors}}
5 | {{this}}
6 | {{/each}}
7 | {{/if}}
8 |
9 | {{to-markdown view.content.help}}
10 |
--------------------------------------------------------------------------------
/app/templates/widgets/_layout.hbs:
--------------------------------------------------------------------------------
1 | {{view "select-control"
2 | content=view.content.meta.options
3 | optionLabelPath="content.label"
4 | optionValuePath="content.value"
5 | defaultValue=view.content.meta.defaultValue
6 | value=view.content.value
7 | disabled=view.content.disabled
8 | }}
9 |
--------------------------------------------------------------------------------
/app/templates/widgets/_markdown.hbs:
--------------------------------------------------------------------------------
1 | {{markdown-editor placeholder=view.content.placeholder value=view.content.value session=controller.session disabled=view.content.disabled}}
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/_name.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{input placeholder="First" value=view.content.value.first disabled=view.content.disabled}}
3 |
4 |
5 | {{input placeholder="Last" value=view.content.value.last disabled=view.content.disabled}}
6 |
7 |
--------------------------------------------------------------------------------
/app/templates/widgets/_number.hbs:
--------------------------------------------------------------------------------
1 | {{input type="number" placeholder=view.content.placeholder value=view.content.value step="any" name=view.content.name disabled=view.content.disabled}}
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/_phone.hbs:
--------------------------------------------------------------------------------
1 | {{input type="tel" placeholder="x-(xxx)-xxx-xxxx" value=view.content.value disabled=view.content.disabled}}
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/_radio.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{#each option in view.content.meta.options}}
3 |
4 | {{view "radio-button"
5 | name=view.content.name
6 | selection=view.content.value
7 | value=option.value
8 | defaultValue=view.content.meta.defaultValue
9 | disabled=view.content.disabled
10 | }}
11 | {{option.value}}
12 |
13 | {{/each}}
14 |
--------------------------------------------------------------------------------
/app/templates/widgets/_rating.hbs:
--------------------------------------------------------------------------------
1 | {{view 'star-rating' control=view.content}}
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/_relation.hbs:
--------------------------------------------------------------------------------
1 | {{auto-complete control=view.content store=store}}
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/_select.hbs:
--------------------------------------------------------------------------------
1 | {{view "select-control"
2 | content=view.content.meta.options
3 | optionLabelPath="content.value"
4 | optionValuePath="content.value"
5 | defaultValue=view.content.meta.defaultValue
6 | value=view.content.value
7 | disabled=view.content.disabled
8 | }}
9 |
--------------------------------------------------------------------------------
/app/templates/widgets/_tabular.hbs:
--------------------------------------------------------------------------------
1 | {{tabular-data control=view.content}}
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/_tag.hbs:
--------------------------------------------------------------------------------
1 |
22 |
--------------------------------------------------------------------------------
/app/templates/widgets/_textarea.hbs:
--------------------------------------------------------------------------------
1 | {{textarea placeholder=view.content.placeholder value=view.content.value disabled=view.content.disabled}}
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/_textfield.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{input placeholder=view.content.placeholder value=view.content.value disabled=view.content.disabled}}
3 |
4 |
--------------------------------------------------------------------------------
/app/templates/widgets/_url.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{input type="url" placeholder=view.content.placeholder value=view.content.value disabled=view.content.disabled}}
3 |
4 |
--------------------------------------------------------------------------------
/app/templates/widgets/_wysiwyg.hbs:
--------------------------------------------------------------------------------
1 | {{redactor-rte placeholder=view.content.placeholder value=view.content.value options=view.content.meta session=controller.session disabled=view.content.disabled}}
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/common/_help.hbs:
--------------------------------------------------------------------------------
1 | {{#if view.content.help}}{{to-markdown view.content.help}} {{/if}}
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/common/_imagemodal.hbs:
--------------------------------------------------------------------------------
1 |
4 |
5 | {{image-upload
6 | session=session
7 | onDoneUpload="confirm"
8 | control=fakeControl
9 | }}
10 |
11 |
--------------------------------------------------------------------------------
/app/templates/widgets/common/_label.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{#if view.content.label}}
3 | {{view.content.label}}
4 | {{else}}
5 | {{view.content.controlType.name}}
6 | {{/if}}
7 | {{view.templateVar}}
8 |
9 |
--------------------------------------------------------------------------------
/app/templates/widgets/common/_minmaxchars.hbs:
--------------------------------------------------------------------------------
1 |
2 | Min characters
3 | {{input type="number" value=editingControl.meta.min}}
4 |
5 |
6 | Max characters
7 | {{input type="number" value=editingControl.meta.max}}
8 |
9 |
--------------------------------------------------------------------------------
/app/templates/widgets/info/_address.hbs:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/info/_audio.hbs:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/info/_boolean.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Default value and labels
5 |
6 |
7 |
8 | {{view "radio-button"
9 | name="default-option"
10 | selection=editingControl.meta.defaultValue
11 | value=false
12 | }}
13 | {{input placeholder="Label for false" value=editingControl.meta.falseLabel}}
14 |
15 |
16 |
17 | {{view "radio-button"
18 | name="default-option"
19 | selection=editingControl.meta.defaultValue
20 | value=true
21 | }}
22 | {{input placeholder="Label for true" value=editingControl.meta.trueLabel}}
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/templates/widgets/info/_checkbox.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Checkbox options
5 |
6 | {{#each option in editingControl.meta.indexedOptions}}
7 |
8 | {{input type="checkbox" checked=option.option.defaultValue}}
9 | {{input placeholder="Label/Value" value=option.option.label}}
10 |
11 | {{#unless editingControl.meta.isOneOption}} {{/unless}}
12 |
13 | {{/each}}
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/templates/widgets/info/_color.hbs:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/info/_datetime.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{input type="checkbox" checked=editingControl.meta.hideDate}}
7 | Hide date
8 |
9 |
10 | {{input type="checkbox" checked=editingControl.meta.hideTime}}
11 | Hide time
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/templates/widgets/info/_email.hbs:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/info/_embedly.hbs:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/info/_file.hbs:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/info/_gallery.hbs:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/info/_geolocation.hbs:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/info/_grid.hbs:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/info/_image.hbs:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/info/_instruction.hbs:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/info/_layout.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
What are page layouts? Page layouts allow the user to choose a specific HTML layout to use for each {{model.name}} object in the CMS. These HTML files need to be included in the /{{ model.id }}/layouts/
directory of the content object. By default, if no layout is selected, the /{{model.id}}/individual.html
file will be used as normal.
5 |
Page layouts
6 |
7 | {{#each option in editingControl.meta.indexedOptions}}
8 |
9 | {{view "radio-button"
10 | name="default-template-option"
11 | selection=editingControl.meta.defaultValue
12 | value=option.option.value
13 | }}
14 | {{input value=option.option.label placeholder="Layout Name"}}
15 | {{input value=option.option.value placeholder="layout_name.html"}}
16 |
17 | {{#unless editingControl.meta.isOneOption}} {{/unless}}
18 |
19 | {{/each}}
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/templates/widgets/info/_markdown.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{partial "widgets/common/minmaxchars"}}
5 |
6 |
--------------------------------------------------------------------------------
/app/templates/widgets/info/_name.hbs:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/info/_number.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Min value
6 | {{input type="number" valueBinding="editingControl.meta.min"}}
7 |
8 |
9 | Max number
10 | {{input type="number" valueBinding="editingControl.meta.max"}}
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/templates/widgets/info/_phone.hbs:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/info/_radio.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Multiple choice options
5 |
6 | {{#each option in editingControl.meta.indexedOptions}}
7 |
8 | {{view "radio-button"
9 | name="default-option"
10 | selection=editingControl.meta.defaultValue
11 | value=option.option.value
12 | }}
13 | {{input placeholder="Label/Value" value=option.option.value}}
14 |
15 | {{#unless editingControl.meta.isOneOption}} {{/unless}}
16 |
17 | {{/each}}
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/templates/widgets/info/_rating.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Min value
7 | {{input type="number" valueBinding="editingControl.meta.min"}}
8 |
9 |
10 |
11 | Max value
12 | {{input type="number" valueBinding="editingControl.meta.max"}}
13 |
14 |
15 |
16 | Step
17 | {{input type="number" valueBinding="editingControl.meta.step"}}
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/templates/widgets/info/_relation.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
29 |
--------------------------------------------------------------------------------
/app/templates/widgets/info/_select.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Dropdown options
5 |
6 | {{#each option in editingControl.meta.indexedOptions}}
7 |
8 | {{view "radio-button"
9 | name="default-option"
10 | selection=editingControl.meta.defaultValue
11 | value=option.option.value
12 | }}
13 | {{input placeholder="Label/Value" value=option.option.value}}
14 |
15 | {{#unless editingControl.meta.isOneOption}} {{/unless}}
16 |
17 | {{/each}}
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/templates/widgets/info/_tabular.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Columns
5 |
6 | {{#each option in editingControl.meta.indexedOptions}}
7 |
8 | {{input placeholder="Column Header" value=option.option.value}}
9 |
10 | {{#unless editingControl.meta.isOneOption}} {{/unless}}
11 |
12 | {{/each}}
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/templates/widgets/info/_tag.hbs:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/info/_textarea.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{partial "widgets/common/minmaxchars"}}
5 |
6 |
--------------------------------------------------------------------------------
/app/templates/widgets/info/_textfield.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{partial "widgets/common/minmaxchars"}}
5 |
6 |
--------------------------------------------------------------------------------
/app/templates/widgets/info/_url.hbs:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/info/_wysiwyg.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Min characters
7 | {{input type="number" valueBinding="editingControl.meta.min"}}
8 |
9 |
10 | Max characters
11 | {{input type="number" valueBinding="editingControl.meta.max"}}
12 |
13 |
14 |
15 | What can users add into the WYSIWYG?
16 |
17 | {{input type="checkbox" checked=editingControl.meta.table}}
18 | Tables
19 |
20 |
21 | {{input type="checkbox" checked=editingControl.meta.video}}
22 | Videos
23 |
24 |
25 | {{input type="checkbox" checked=editingControl.meta.image}}
26 | Images
27 |
28 |
29 | {{input type="checkbox" checked=editingControl.meta.quote}}
30 | Block quotes
31 |
32 |
33 | {{input type="checkbox" checked=editingControl.meta.link}}
34 | Links
35 |
36 |
37 | {{input type="checkbox" checked=editingControl.meta.javascript}}
38 | JavaScript
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/app/templates/widgets/relation/few.hbs:
--------------------------------------------------------------------------------
1 | {{view.content.itemData.name}}{{#unless view.context.control.disabled}} {{/unless}}
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/relation/many.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{view.content.itemData.name}}
3 | {{#unless view.context.control.disabled}}
4 |
5 | {{/unless}}
6 |
7 |
--------------------------------------------------------------------------------
/app/templates/widgets/value/_address.hbs:
--------------------------------------------------------------------------------
1 | {{value.street1}}
2 | {{value.street2}}
3 | {{value.city}}
4 | {{value.state}}
5 | {{value.postal}}
6 | {{value.country}}
7 |
--------------------------------------------------------------------------------
/app/templates/widgets/value/_audio.hbs:
--------------------------------------------------------------------------------
1 | {{#if value.url}}
2 |
3 | {{/if}}
4 |
--------------------------------------------------------------------------------
/app/templates/widgets/value/_boolean.hbs:
--------------------------------------------------------------------------------
1 | {{value}}
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/value/_checkbox.hbs:
--------------------------------------------------------------------------------
1 | {{#each value}}
2 | {{#if this.value}}{{this.label}}{{/if}}
3 | {{/each}}
4 |
--------------------------------------------------------------------------------
/app/templates/widgets/value/_color.hbs:
--------------------------------------------------------------------------------
1 | {{colored-element color=value}} {{value}}
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/value/_datetime.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{format-time value format="L LT"}}
3 |
4 |
--------------------------------------------------------------------------------
/app/templates/widgets/value/_email.hbs:
--------------------------------------------------------------------------------
1 | {{value}}
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/value/_embedly.hbs:
--------------------------------------------------------------------------------
1 | {{#if value.title.length}}
2 | {{value.title}}
3 | {{/if}}
4 |
--------------------------------------------------------------------------------
/app/templates/widgets/value/_file.hbs:
--------------------------------------------------------------------------------
1 | {{#if value.url}}
2 |
3 | {{/if}}
4 |
5 |
--------------------------------------------------------------------------------
/app/templates/widgets/value/_gallery.hbs:
--------------------------------------------------------------------------------
1 | {{#if value.length}}
2 | {{resize-image value.[0] size="30"}}
3 | {{ value.length }} images
4 | {{/if}}
5 |
--------------------------------------------------------------------------------
/app/templates/widgets/value/_geolocation.hbs:
--------------------------------------------------------------------------------
1 | {{#if value.latitude}}{{value.latitude}}{{/if}}{{#if value.longitude}}, {{value.longitude}}{{/if}}
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/value/_grid.hbs:
--------------------------------------------------------------------------------
1 | {{#if value.length}}
2 | {{ value.length }} items
3 | {{/if}}
4 |
--------------------------------------------------------------------------------
/app/templates/widgets/value/_image.hbs:
--------------------------------------------------------------------------------
1 | {{resize-image value size="30"}}
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/value/_instruction.hbs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/webhook/webhook-cms/8b58110801bc0d3a749bb230bb9983ee5b77ffcd/app/templates/widgets/value/_instruction.hbs
--------------------------------------------------------------------------------
/app/templates/widgets/value/_layout.hbs:
--------------------------------------------------------------------------------
1 | {{value}}
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/value/_markdown.hbs:
--------------------------------------------------------------------------------
1 | {{truncate-string value 100}}
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/value/_name.hbs:
--------------------------------------------------------------------------------
1 | {{value.first}} {{value.last}}
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/value/_number.hbs:
--------------------------------------------------------------------------------
1 | {{value}}
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/value/_phone.hbs:
--------------------------------------------------------------------------------
1 | {{value}}
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/value/_radio.hbs:
--------------------------------------------------------------------------------
1 | {{value}}
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/value/_rating.hbs:
--------------------------------------------------------------------------------
1 | {{value}}
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/value/_relation.hbs:
--------------------------------------------------------------------------------
1 | {{#if control.meta.isSingle}}
2 | {{view 'relation-value' context=value}}
3 | {{else}}
4 | {{#view 'relation-values' relationKeys=value}}
5 | {{each view.slicedKeys itemView='relation-value'}}
6 | {{#if view.more}}
7 | and {{view.more}} more
8 | {{/if}}
9 | {{/view}}
10 | {{/if}}
11 |
--------------------------------------------------------------------------------
/app/templates/widgets/value/_select.hbs:
--------------------------------------------------------------------------------
1 | {{value}}
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/value/_tabular.hbs:
--------------------------------------------------------------------------------
1 | tabular data
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/value/_tag.hbs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/webhook/webhook-cms/8b58110801bc0d3a749bb230bb9983ee5b77ffcd/app/templates/widgets/value/_tag.hbs
--------------------------------------------------------------------------------
/app/templates/widgets/value/_textarea.hbs:
--------------------------------------------------------------------------------
1 | {{truncate-string value 100}}
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/value/_textfield.hbs:
--------------------------------------------------------------------------------
1 | {{value}}
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/value/_url.hbs:
--------------------------------------------------------------------------------
1 | {{#if value.length}}
2 | {{truncate-string value 30}}
3 | {{/if}}
4 |
--------------------------------------------------------------------------------
/app/templates/widgets/value/_wysiwyg.hbs:
--------------------------------------------------------------------------------
1 | {{truncate-string value 100}}
2 |
--------------------------------------------------------------------------------
/app/templates/widgets/value/relation-item.hbs:
--------------------------------------------------------------------------------
1 | {{#link-to 'wh.content.type.edit' view.contentType.id view.relatedItem.id}}{{view.relatedItem.itemData.name}}{{/link-to}}{{view.comma}}
2 |
--------------------------------------------------------------------------------
/app/transforms/json.js:
--------------------------------------------------------------------------------
1 | export default DS.Transform.extend({
2 | serialize: function (value) {
3 | return value;
4 | },
5 | deserialize: function (value) {
6 | return Ember.Object.create(Ember.isBlank(value) ? {} : value);
7 | }
8 | });
9 |
--------------------------------------------------------------------------------
/app/utils/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/webhook/webhook-cms/8b58110801bc0d3a749bb230bb9983ee5b77ffcd/app/utils/.gitkeep
--------------------------------------------------------------------------------
/app/utils/ajax.js:
--------------------------------------------------------------------------------
1 | /* global ic */
2 | export default function ajax(){
3 | return ic.ajax.apply(null, arguments);
4 | }
5 |
--------------------------------------------------------------------------------
/app/utils/meta-options.js:
--------------------------------------------------------------------------------
1 | export default Ember.Object.extend({
2 | indexedOptions: function () {
3 | return this.get('options').map(function(option, index) {
4 | return { option: option, index: index };
5 | });
6 | }.property('options.@each'),
7 | isOneOption: function () {
8 | return this.get('options.length') === 1;
9 | }.property('options.length')
10 | });
11 |
--------------------------------------------------------------------------------
/app/utils/slugger.js:
--------------------------------------------------------------------------------
1 | /*global uslug*/
2 | export default function slugger (item, type, customUrls) {
3 | var tmpSlug = '';
4 | tmpSlug = uslug(item.name).toLowerCase();
5 |
6 | if(customUrls && customUrls.individualUrl) {
7 | tmpSlug = parseCustomUrl(customUrls.individualUrl, item, type) + '/' + tmpSlug;
8 | }
9 |
10 | if(customUrls && customUrls.listUrl) {
11 | tmpSlug = customUrls.listUrl + '/' + tmpSlug;
12 | } else {
13 | tmpSlug = type + '/' + tmpSlug;
14 | }
15 |
16 | return tmpSlug;
17 | }
18 |
19 | function parseCustomUrl (url, object, type) {
20 | var publishDate = object.publish_date ? object.publish_date : object;
21 |
22 | publishDate = moment(publishDate);
23 |
24 | function replacer(match, timeIdent, offset, string){
25 | if(timeIdent === 'Y') {
26 | return publishDate.format('YYYY').toLowerCase();
27 | } else if (timeIdent === 'y') {
28 | return publishDate.format('YY').toLowerCase();
29 | } else if (timeIdent === 'm') {
30 | return publishDate.format('MM').toLowerCase();
31 | } else if (timeIdent === 'n') {
32 | return publishDate.format('M').toLowerCase();
33 | } else if (timeIdent === 'F') {
34 | return publishDate.format('MMMM').toLowerCase();
35 | } else if (timeIdent === 'M') {
36 | return publishDate.format('MMM').toLowerCase();
37 | } else if (timeIdent === 'd') {
38 | return publishDate.format('DD').toLowerCase();
39 | } else if (timeIdent === 'j') {
40 | return publishDate.format('D').toLowerCase();
41 | } else if (timeIdent === 'T') {
42 | return type.toLowerCase();
43 | } else {
44 | return match;
45 | }
46 | }
47 |
48 | url = url.replace(/#(\w)/g, replacer);
49 |
50 | return url;
51 | }
--------------------------------------------------------------------------------
/app/utils/uuid.js:
--------------------------------------------------------------------------------
1 | function s4() {
2 | return Math.floor((1 + Math.random()) * 0x10000)
3 | .toString(16)
4 | .substring(1);
5 | }
6 |
7 | export default function guid() {
8 | return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
9 | s4() + '-' + s4() + s4() + s4();
10 | }
11 |
--------------------------------------------------------------------------------
/app/views/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/webhook/webhook-cms/8b58110801bc0d3a749bb230bb9983ee5b77ffcd/app/views/.gitkeep
--------------------------------------------------------------------------------
/app/views/animate-collection.js:
--------------------------------------------------------------------------------
1 | export default Ember.CollectionView.extend({
2 | tagName : "ul",
3 | initialsAdded : 0,
4 | animationLength: 5000,
5 |
6 | willInsertElement: function () {
7 | this.set('initialLength', this.get('content.length'));
8 | },
9 |
10 | itemViewClass: Ember.View.extend({
11 | classNames: ['wy-animate-add'],
12 | didInsertElement: function () {
13 | var collectionView = this.get('parentView');
14 |
15 | if (collectionView.get('initialsAdded') === collectionView.get('initialLength')) {
16 | this.$().addClass('wy-just-added');
17 | this.set('addedTimeout', Ember.run.later(this, function () {
18 | this.$().removeClass('wy-just-added');
19 | }, collectionView.get('animationLength')));
20 | } else {
21 | collectionView.incrementProperty('initialsAdded');
22 | }
23 |
24 | this.get('context').on('didUpdate', function () {
25 | this.$().addClass('wy-just-updated');
26 | this.set('updatedTimeout', Ember.run.later(this, function () {
27 | this.$().removeClass('wy-just-updated');
28 | }, collectionView.get('animationLength')));
29 | }.bind(this));
30 |
31 | },
32 | willDestroyElement: function () {
33 | this.get('context').off('didUpdate');
34 | Ember.run.cancel(this.get('addedTimeout'));
35 | Ember.run.cancel(this.get('updatedTimeout'));
36 | }
37 | })
38 | });
39 |
--------------------------------------------------------------------------------
/app/views/autocomplete-results.js:
--------------------------------------------------------------------------------
1 | export default Ember.CollectionView.extend({
2 | tagName: 'ul',
3 | classNames: ['wy-autocomplete-dropdown'],
4 |
5 | didInsertElement: function () {
6 | this.$().hide();
7 | },
8 |
9 | contentChanged: function () {
10 |
11 | if (this.get('content.length')) {
12 | this.get('content.firstObject').set('isSelected', true);
13 | this.$().show();
14 | } else {
15 | this.$().hide();
16 | }
17 |
18 | }.observes('content.@each'),
19 |
20 | itemViewClass: Ember.View.extend({
21 | classNameBindings: [
22 | 'context.isSelected:on',
23 | 'context.createStub:add'
24 | ],
25 |
26 | mouseEnter: function () {
27 | this.get('parentView.content').setEach('isSelected', false);
28 | this.set('context.isSelected', true);
29 | },
30 |
31 | click: function () {
32 | this.get('parentView.controller').send('addToSelection', this.get('context'));
33 | }
34 |
35 | })
36 | });
37 |
--------------------------------------------------------------------------------
/app/views/checkbox-control.js:
--------------------------------------------------------------------------------
1 | export default Ember.Checkbox.extend({
2 |
3 | stateChanged: function () {
4 | this.set('option.value', this.get('checked'));
5 | this.set('control.value', this.get('control.meta.options').map(function (option) {
6 | return { label: option.label, value: option.value };
7 | }).toArray());
8 | }.observes('checked'),
9 |
10 | defaultChanged: function () {
11 | this.set('checked', this.get('option.defaultValue'));
12 | }.observes('option.defaultValue'),
13 |
14 | willInsertElement: function () {
15 | if (this.get('option.value') === undefined) {
16 | this.set('checked', this.getWithDefault('option.defaultValue', false));
17 | }
18 | }
19 |
20 | });
21 |
--------------------------------------------------------------------------------
/app/views/draggable.js:
--------------------------------------------------------------------------------
1 | export default Ember.View.extend({
2 |
3 | didInsertElement: function () {
4 | // real dumb way of making sure the content types are there
5 | this.$().one('mouseenter', this.makeDraggable.bind(this));
6 | },
7 |
8 | makeDraggable: function () {
9 |
10 | var view = this;
11 |
12 | var sortable;
13 |
14 | // layout and grid control types are not allowed in grid
15 | switch (view.get('controlType.id')) {
16 | case 'layout':
17 | case 'grid':
18 | sortable = Ember.$('form > fieldset > .ui-sortable');
19 | break;
20 | default:
21 | sortable = Ember.$('form .ui-sortable');
22 | break;
23 | }
24 |
25 | this.$().draggable({
26 | helper: function (event) {
27 |
28 | var helper = $('