diff --git a/.image_optim.yml b/.image_optim.yml
index ba5d735b27..4a9ad0555c 100644
--- a/.image_optim.yml
+++ b/.image_optim.yml
@@ -1,11 +1,11 @@
skip_missing_workers: true
-allow_lossy: true
+allow_lossy: false
# PNG
advpng: false
-optipng: false
+optipng:
+ level: 2
pngcrush: false
pngout: false
-pngquant:
- quality: !ruby/range 75..90
+pngquant: false
# JPG
jpegrecompress: false
diff --git a/.travis.yml b/.travis.yml
index 9c2e282d89..352361f0f7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,9 +5,9 @@ env:
- DISCOURSE_HOSTNAME=www.example.com
- RUBY_GC_MALLOC_LIMIT=50000000
matrix:
+ - "RAILS_MASTER=0"
- "RAILS42=1"
- "RAILS_MASTER=1"
- - "RAILS_MASTER=0"
addons:
postgresql: 9.3
diff --git a/Gemfile.lock b/Gemfile.lock
index 57b8795210..593d89ab1f 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -164,10 +164,10 @@ GEM
mock_redis (0.14.0)
moneta (0.8.0)
msgpack (0.5.11)
- multi_json (1.11.0)
+ multi_json (1.11.2)
multi_xml (0.5.5)
multipart-post (2.0.0)
- mustache (0.99.8)
+ mustache (1.0.2)
netrc (0.10.3)
nokogiri (1.6.6.2)
mini_portile (~> 0.6.0)
@@ -206,11 +206,11 @@ GEM
omniauth-twitter (1.0.1)
multi_json (~> 1.3)
omniauth-oauth (~> 1.0)
- onebox (1.5.21)
- moneta (~> 0.7)
- multi_json (~> 1.7)
- mustache (~> 0.99)
- nokogiri (~> 1.6.1)
+ onebox (1.5.22)
+ moneta (~> 0.8)
+ multi_json (~> 1.11)
+ mustache
+ nokogiri (~> 1.6.6)
openid-redis-store (0.0.2)
redis
ruby-openid
diff --git a/app/assets/fonts/zocial-regular-webfont.eot b/app/assets/fonts/zocial-regular-webfont.eot
deleted file mode 100644
index 5db5d21666..0000000000
Binary files a/app/assets/fonts/zocial-regular-webfont.eot and /dev/null differ
diff --git a/app/assets/fonts/zocial-regular-webfont.svg b/app/assets/fonts/zocial-regular-webfont.svg
deleted file mode 100644
index 130d83ca10..0000000000
--- a/app/assets/fonts/zocial-regular-webfont.svg
+++ /dev/null
@@ -1,333 +0,0 @@
-
diff --git a/app/assets/fonts/zocial-regular-webfont.ttf b/app/assets/fonts/zocial-regular-webfont.ttf
deleted file mode 100644
index a043566360..0000000000
Binary files a/app/assets/fonts/zocial-regular-webfont.ttf and /dev/null differ
diff --git a/app/assets/fonts/zocial-regular-webfont.woff b/app/assets/fonts/zocial-regular-webfont.woff
deleted file mode 100644
index 5a91cf3056..0000000000
Binary files a/app/assets/fonts/zocial-regular-webfont.woff and /dev/null differ
diff --git a/app/assets/javascripts/admin/components/admin-form-row.js.es6 b/app/assets/javascripts/admin/components/admin-form-row.js.es6
new file mode 100644
index 0000000000..e7cef2edb0
--- /dev/null
+++ b/app/assets/javascripts/admin/components/admin-form-row.js.es6
@@ -0,0 +1,3 @@
+export default Ember.Component.extend({
+ classNames: ['row']
+});
diff --git a/app/assets/javascripts/admin/components/admin-report-counts.js.es6 b/app/assets/javascripts/admin/components/admin-report-counts.js.es6
index 25bf94db21..46ab32f609 100644
--- a/app/assets/javascripts/admin/components/admin-report-counts.js.es6
+++ b/app/assets/javascripts/admin/components/admin-report-counts.js.es6
@@ -1,3 +1,5 @@
export default Ember.Component.extend({
- tagName: 'tr'
+ tagName: 'tr',
+ reverseColors: Ember.computed.match('report.type', /^(time_to_first_response|topics_with_no_response)$/),
+ classNameBindings: ['reverseColors']
});
diff --git a/app/assets/javascripts/admin/components/admin-user-field-item.js.es6 b/app/assets/javascripts/admin/components/admin-user-field-item.js.es6
index bc96a0fc74..bccec30932 100644
--- a/app/assets/javascripts/admin/components/admin-user-field-item.js.es6
+++ b/app/assets/javascripts/admin/components/admin-user-field-item.js.es6
@@ -6,6 +6,17 @@ export default Ember.Component.extend(bufferedProperty('userField'), {
editing: Ember.computed.empty('userField.id'),
classNameBindings: [':user-field'],
+ cantMoveUp: Discourse.computed.propertyEqual('userField', 'firstField'),
+ cantMoveDown: Discourse.computed.propertyEqual('userField', 'lastField'),
+
+ userFieldsDescription: function() {
+ return I18n.t('admin.user_fields.description');
+ }.property(),
+
+ bufferedFieldType: function() {
+ return UserField.fieldTypeById(this.get('buffered.field_type'));
+ }.property('buffered.field_type'),
+
_focusOnEdit: function() {
if (this.get('editing')) {
Ember.run.scheduleOnce('afterRender', this, '_focusName');
@@ -36,26 +47,40 @@ export default Ember.Component.extend(bufferedProperty('userField'), {
}.property('userField.editable', 'userField.required', 'userField.show_on_profile'),
actions: {
- save: function() {
+ save() {
const self = this;
- const attrs = this.get('buffered').getProperties('name', 'description', 'field_type', 'editable', 'required', 'show_on_profile');
+ const buffered = this.get('buffered');
+ const attrs = buffered.getProperties('name',
+ 'description',
+ 'field_type',
+ 'editable',
+ 'required',
+ 'show_on_profile',
+ 'options');
- this.get('userField').save(attrs).then(function(res) {
- self.set('userField.id', res.user_field.id);
+ this.get('userField').save(attrs).then(function() {
self.set('editing', false);
self.commitBuffer();
}).catch(popupAjaxError);
},
- edit: function() {
+ moveUp() {
+ this.sendAction('moveUpAction', this.get('userField'));
+ },
+
+ moveDown() {
+ this.sendAction('moveDownAction', this.get('userField'));
+ },
+
+ edit() {
this.set('editing', true);
},
- destroy: function() {
+ destroy() {
this.sendAction('destroyAction', this.get('userField'));
},
- cancel: function() {
+ cancel() {
const id = this.get('userField.id');
if (Ember.isEmpty(id)) {
this.sendAction('destroyAction', this.get('userField'));
diff --git a/app/assets/javascripts/admin/components/value-list.js.es6 b/app/assets/javascripts/admin/components/value-list.js.es6
index c995097a9d..3aa66ff187 100644
--- a/app/assets/javascripts/admin/components/value-list.js.es6
+++ b/app/assets/javascripts/admin/components/value-list.js.es6
@@ -1,14 +1,81 @@
export default Ember.Component.extend({
classNameBindings: [':value-list'],
+ _enableSorting: function() {
+ const self = this;
+ const placeholder = document.createElement("div");
+ placeholder.className = "placeholder";
+
+ let dragging = null;
+ let over = null;
+ let nodePlacement;
+
+ this.$().on('dragstart.discourse', '.values .value', function(e) {
+ dragging = e.currentTarget;
+ e.dataTransfer.effectAllowed = 'move';
+ e.dataTransfer.setData("text/html", e.currentTarget);
+ });
+
+ this.$().on('dragend.discourse', '.values .value', function() {
+ Ember.run(function() {
+ dragging.parentNode.removeChild(placeholder);
+ dragging.style.display = 'block';
+
+ // Update data
+ const from = Number(dragging.dataset.index);
+ let to = Number(over.dataset.index);
+ if (from < to) to--;
+ if (nodePlacement === "after") to++;
+
+ const collection = self.get('collection');
+ const fromObj = collection.objectAt(from);
+ collection.replace(from, 1);
+ collection.replace(to, 0, [fromObj]);
+ self._saveValues();
+ });
+ return false;
+ });
+
+ this.$().on('dragover.discourse', '.values', function(e) {
+ e.preventDefault();
+ dragging.style.display = 'none';
+ if (e.target.className === "placeholder") { return; }
+ over = e.target;
+
+ const relY = e.originalEvent.clientY - over.offsetTop;
+ const height = over.offsetHeight / 2;
+ const parent = e.target.parentNode;
+
+ if (relY > height) {
+ nodePlacement = "after";
+ parent.insertBefore(placeholder, e.target.nextElementSibling);
+ } else if(relY < height) {
+ nodePlacement = "before";
+ parent.insertBefore(placeholder, e.target);
+ }
+ });
+ }.on('didInsertElement'),
+
+ _removeSorting: function() {
+ this.$().off('dragover.discourse').off('dragend.discourse').off('dragstart.discourse');
+ }.on('willDestroyElement'),
+
_setupCollection: function() {
const values = this.get('values');
- this.set('collection', (values && values.length) ? values.split("\n") : []);
+ if (this.get('inputType') === "array") {
+ this.set('collection', values || []);
+ } else {
+ this.set('collection', (values && values.length) ? values.split("\n") : []);
+ }
}.on('init').observes('values'),
- _collectionChanged: function() {
- this.set('values', this.get('collection').join("\n"));
- }.observes('collection.@each'),
+ _saveValues: function() {
+ if (this.get('inputType') === "array") {
+ this.set('values', this.get('collection'));
+ } else {
+ this.set('values', this.get('collection').join("\n"));
+ }
+ },
inputInvalid: Ember.computed.empty('newValue'),
@@ -24,11 +91,14 @@ export default Ember.Component.extend({
this.get('collection').addObject(this.get('newValue'));
this.set('newValue', '');
+
+ this._saveValues();
},
removeValue(value) {
const collection = this.get('collection');
collection.removeObject(value);
+ this._saveValues();
}
}
});
diff --git a/app/assets/javascripts/admin/controllers/admin-customize-css-html.js.es6 b/app/assets/javascripts/admin/controllers/admin-customize-css-html.js.es6
index be6834333d..08e4e3215b 100644
--- a/app/assets/javascripts/admin/controllers/admin-customize-css-html.js.es6
+++ b/app/assets/javascripts/admin/controllers/admin-customize-css-html.js.es6
@@ -10,6 +10,14 @@ import showModal from 'discourse/lib/show-modal';
**/
export default Ember.ArrayController.extend({
+ undoPreviewUrl: function() {
+ return Discourse.getURL("/?preview-style=");
+ }.property(),
+
+ defaultStyleUrl: function() {
+ return Discourse.getURL("/?preview-style=default");
+ }.property(),
+
actions: {
/**
diff --git a/app/assets/javascripts/admin/controllers/admin-user-fields.js.es6 b/app/assets/javascripts/admin/controllers/admin-user-fields.js.es6
index 0eb041c55b..70963a147e 100644
--- a/app/assets/javascripts/admin/controllers/admin-user-fields.js.es6
+++ b/app/assets/javascripts/admin/controllers/admin-user-fields.js.es6
@@ -1,39 +1,60 @@
-import UserField from 'admin/models/user-field';
+import { popupAjaxError } from 'discourse/lib/ajax-error';
-export default Ember.ArrayController.extend({
+const MAX_FIELDS = 20;
+
+export default Ember.Controller.extend({
fieldTypes: null,
- createDisabled: Em.computed.gte('model.length', 20),
+ createDisabled: Em.computed.gte('model.length', MAX_FIELDS),
- userFieldsDescription: function() {
- return I18n.t('admin.user_fields.description');
- }.property(),
-
- userFieldsName: function() {
- return I18n.t('admin.user_fields.name');
- }.property(),
-
- _performDestroy: function(f, model) {
- return f.destroy().then(function() {
- model.removeObject(f);
+ arrangedContent: function() {
+ return Ember.ArrayProxy.extend(Ember.SortableMixin).create({
+ sortProperties: ['position'],
+ content: this.get('model')
});
- },
+ }.property('model'),
actions: {
- createField: function() {
- this.pushObject(UserField.create({ field_type: 'text' }));
+ createField() {
+ const f = this.store.createRecord('user-field', { field_type: 'text', position: MAX_FIELDS });
+ this.get('model').pushObject(f);
},
- destroy: function(f) {
- var model = this.get('model'),
- self = this;
+ moveUp(f) {
+ const idx = this.get('arrangedContent').indexOf(f);
+ if (idx) {
+ const prev = this.get('arrangedContent').objectAt(idx-1);
+ const prevPos = prev.get('position');
+
+ prev.update({ position: f.get('position') });
+ f.update({ position: prevPos });
+ }
+ },
+
+ moveDown(f) {
+ const idx = this.get('arrangedContent').indexOf(f);
+ if (idx > -1) {
+ const next = this.get('arrangedContent').objectAt(idx+1);
+ const nextPos = next.get('position');
+
+ next.update({ position: f.get('position') });
+ f.update({ position: nextPos });
+ }
+ },
+
+ destroy(f) {
+ const model = this.get('model');
// Only confirm if we already been saved
if (f.get('id')) {
bootbox.confirm(I18n.t("admin.user_fields.delete_confirm"), function(result) {
- if (result) { self._performDestroy(f, model); }
+ if (result) {
+ f.destroyRecord().then(function() {
+ model.removeObject(f);
+ }).catch(popupAjaxError);
+ }
});
} else {
- self._performDestroy(f, model);
+ model.removeObject(f);
}
}
}
diff --git a/app/assets/javascripts/admin/models/site_customization.js b/app/assets/javascripts/admin/models/site_customization.js
index f7b6346a95..19fcbad266 100644
--- a/app/assets/javascripts/admin/models/site_customization.js
+++ b/app/assets/javascripts/admin/models/site_customization.js
@@ -44,7 +44,7 @@ Discourse.SiteCustomization = Discourse.Model.extend({
this.set('originals', originals);
}.on('init'),
- previewUrl: function() { return "/?preview-style=" + this.get('key'); }.property('key'),
+ previewUrl: function() { return Discourse.getURL("/?preview-style=" + this.get('key')); }.property('key'),
disableSave: function() { return !this.get('changed') || this.get('saving'); }.property('changed'),
save: function() {
diff --git a/app/assets/javascripts/admin/models/user-field.js.es6 b/app/assets/javascripts/admin/models/user-field.js.es6
index e5c3348efd..c6a1a1f6be 100644
--- a/app/assets/javascripts/admin/models/user-field.js.es6
+++ b/app/assets/javascripts/admin/models/user-field.js.es6
@@ -1,54 +1,25 @@
-var UserField = Ember.Object.extend({
- destroy: function() {
- var self = this;
- return new Ember.RSVP.Promise(function(resolve) {
- var id = self.get('id');
- if (id) {
- return Discourse.ajax("/admin/customize/user_fields/" + id, { type: 'DELETE' }).then(function() {
- resolve();
- });
- }
- resolve();
- });
- },
+import RestModel from 'discourse/models/rest';
- save: function(attrs) {
- var id = this.get('id');
- if (!id) {
- return Discourse.ajax("/admin/customize/user_fields", {
- type: "POST",
- data: { user_field: attrs }
- });
- } else {
- return Discourse.ajax("/admin/customize/user_fields/" + id, {
- type: "PUT",
- data: { user_field: attrs }
- });
- }
- }
+const UserField = RestModel.extend();
+
+const UserFieldType = Ember.Object.extend({
+ name: Discourse.computed.i18n('id', 'admin.user_fields.field_types.%@')
});
UserField.reopenClass({
- findAll: function() {
- return Discourse.ajax("/admin/customize/user_fields").then(function(result) {
- return result.user_fields.map(function(uf) {
- return UserField.create(uf);
- });
- });
- },
-
- fieldTypes: function() {
+ fieldTypes() {
if (!this._fieldTypes) {
this._fieldTypes = [
- Ember.Object.create({id: 'text', name: I18n.t('admin.user_fields.field_types.text') }),
- Ember.Object.create({id: 'confirm', name: I18n.t('admin.user_fields.field_types.confirm') })
+ UserFieldType.create({ id: 'text' }),
+ UserFieldType.create({ id: 'confirm' }),
+ UserFieldType.create({ id: 'dropdown', hasOptions: true })
];
}
return this._fieldTypes;
},
- fieldTypeById: function(id) {
+ fieldTypeById(id) {
return this.fieldTypes().findBy('id', id);
}
});
diff --git a/app/assets/javascripts/admin/routes/admin-user-fields.js.es6 b/app/assets/javascripts/admin/routes/admin-user-fields.js.es6
index a540f343cc..5770165ff7 100644
--- a/app/assets/javascripts/admin/routes/admin-user-fields.js.es6
+++ b/app/assets/javascripts/admin/routes/admin-user-fields.js.es6
@@ -2,13 +2,10 @@ import UserField from 'admin/models/user-field';
export default Discourse.Route.extend({
model: function() {
- return UserField.findAll();
+ return this.store.findAll('user-field');
},
setupController: function(controller, model) {
- controller.setProperties({
- model: model,
- fieldTypes: UserField.fieldTypes()
- });
+ controller.setProperties({ model, fieldTypes: UserField.fieldTypes() });
}
});
diff --git a/app/assets/javascripts/admin/templates/backups_index.hbs b/app/assets/javascripts/admin/templates/backups_index.hbs
index ca8830a0e8..c6a6598be6 100644
--- a/app/assets/javascripts/admin/templates/backups_index.hbs
+++ b/app/assets/javascripts/admin/templates/backups_index.hbs
@@ -21,10 +21,10 @@
{{fa-icon "download"}}{{i18n 'admin.backups.operations.download.label'}}
{{#if isOperationRunning}}
- {{d-button icon="trash-o" action="destroyBackup" actionParam=backup class="btn-danger no-text" disabled="true" title="admin.backups.operations.is_running"}}
+ {{d-button icon="trash-o" action="destroyBackup" actionParam=backup class="btn-danger" disabled="true" title="admin.backups.operations.is_running"}}
{{d-button icon="play" action="startRestore" actionParam=backup disabled=restoreDisabled title=restoreTitle label="admin.backups.operations.restore.label"}}
{{else}}
- {{d-button icon="trash-o" action="destroyBackup" actionParam=backup class="btn-danger no-text" title="admin.backups.operations.destroy.title"}}
+ {{d-button icon="trash-o" action="destroyBackup" actionParam=backup class="btn-danger" title="admin.backups.operations.destroy.title"}}
{{d-button icon="play" action="startRestore" actionParam=backup disabled=restoreDisabled title=restoreTitle label="admin.backups.operations.restore.label"}}
{{/if}}
diff --git a/app/assets/javascripts/admin/templates/components/admin-form-row.hbs b/app/assets/javascripts/admin/templates/components/admin-form-row.hbs
new file mode 100644
index 0000000000..aee95b6701
--- /dev/null
+++ b/app/assets/javascripts/admin/templates/components/admin-form-row.hbs
@@ -0,0 +1,14 @@
+
+ {{#if label}}
+
+ {{else}}
+
+ {{/if}}
+
+
+ {{#if wrapLabel}}
+
+ {{else}}
+ {{yield}}
+ {{/if}}
+
diff --git a/app/assets/javascripts/admin/templates/components/admin-user-field-item.hbs b/app/assets/javascripts/admin/templates/components/admin-user-field-item.hbs
index 8e1bd8c176..36fa8c3b81 100644
--- a/app/assets/javascripts/admin/templates/components/admin-user-field-item.hbs
+++ b/app/assets/javascripts/admin/templates/components/admin-user-field-item.hbs
@@ -1,45 +1,51 @@
{{#if editing}}
-
-
- {{input value=buffered.name class="user-field-name" placeholder=userFieldsName}}
-
-
- {{input value=buffered.description class="user-field-desc" placeholder=userFieldsDescription}}
-
-
- {{combo-box content=fieldTypes valueAttribute="id" value=buffered.field_type}}
-
-
- {{d-button action="save" class="btn-primary" icon="check" label="admin.user_fields.save"}}
- {{d-button action="cancel" class="btn-danger" icon="times" label="admin.user_fields.cancel"}}
-
-
-
-
-
-
-
-
-
-
-
-
-
+ {{#admin-form-row label="admin.user_fields.type"}}
+ {{combo-box content=fieldTypes valueAttribute="id" value=buffered.field_type}}
+ {{/admin-form-row}}
+
+ {{#admin-form-row label="admin.user_fields.name"}}
+ {{input value=buffered.name class="user-field-name"}}
+ {{/admin-form-row}}
+
+ {{#admin-form-row label="admin.user_fields.description"}}
+ {{input value=buffered.description class="user-field-desc"}}
+ {{/admin-form-row}}
+
+ {{#if bufferedFieldType.hasOptions}}
+ {{#admin-form-row label="admin.user_fields.options"}}
+ {{value-list values=buffered.options inputType="array"}}
+ {{/admin-form-row}}
+ {{/if}}
+
+ {{#admin-form-row wrapLabel="true"}}
+ {{input type="checkbox" checked=buffered.editable}} {{i18n 'admin.user_fields.editable.title'}}
+ {{/admin-form-row}}
+
+ {{#admin-form-row wrapLabel="true"}}
+ {{input type="checkbox" checked=buffered.required}} {{i18n 'admin.user_fields.required.title'}}
+ {{/admin-form-row}}
+
+ {{#admin-form-row wrapLabel="true"}}
+ {{input type="checkbox" checked=buffered.show_on_profile}} {{i18n 'admin.user_fields.show_on_profile.title'}}
+ {{/admin-form-row}}
+
+ {{#admin-form-row}}
+ {{d-button action="save" class="btn-primary" icon="check" label="admin.user_fields.save"}}
+ {{d-button action="cancel" class="btn-danger" icon="times" label="admin.user_fields.cancel"}}
+ {{/admin-form-row}}
{{else}}
-
{{userField.name}}
-
{{{userField.description}}}
+
+ {{userField.name}}
+
+ {{{userField.description}}}
+
{{fieldName}}
-
{{d-button action="edit" class="btn-default" icon="pencil" label="admin.user_fields.edit"}}
{{d-button action="destroy" class="btn-danger" icon="trash-o" label="admin.user_fields.delete"}}
+ {{d-button action="moveUp" icon="arrow-up" disabled=cantMoveUp}}
+ {{d-button action="moveDown" icon="arrow-down" disabled=cantMoveDown}}
{{flags}}
diff --git a/app/assets/javascripts/admin/templates/components/site-setting.hbs b/app/assets/javascripts/admin/templates/components/site-setting.hbs
index eb117c0f4d..15821600ee 100644
--- a/app/assets/javascripts/admin/templates/components/site-setting.hbs
+++ b/app/assets/javascripts/admin/templates/components/site-setting.hbs
@@ -6,8 +6,8 @@
{{#if dirty}}
- {{d-button class="ok no-text" action="save" icon="check"}}
- {{d-button class="cancel no-text" action="cancel" icon="times"}}
+ {{d-button class="ok" action="save" icon="check"}}
+ {{d-button class="cancel" action="cancel" icon="times"}}
{{else if setting.overridden}}
{{d-button action="resetDefault" icon="undo" label="admin.site_settings.reset"}}
diff --git a/app/assets/javascripts/admin/templates/components/value-list.hbs b/app/assets/javascripts/admin/templates/components/value-list.hbs
index 74904f92e8..6f6e1aa194 100644
--- a/app/assets/javascripts/admin/templates/components/value-list.hbs
+++ b/app/assets/javascripts/admin/templates/components/value-list.hbs
@@ -1,11 +1,11 @@
{{#if collection}}
- {{#each collection as |value|}}
-
+ {{#each collection as |value index|}}
+
{{d-button action="removeValue"
actionParam=value
icon="times"
- class="btn-small no-text"}}
+ class="btn-small"}}
{{value}}
{{/each}}
@@ -14,5 +14,5 @@
{{text-field value=newValue placeholderKey=addKey}}
- {{d-button action="addValue" icon="plus" class="btn-primary btn-small no-text" disabled=inputInvalid}}
+ {{d-button action="addValue" icon="plus" class="btn-primary btn-small" disabled=inputInvalid}}
diff --git a/app/assets/javascripts/admin/templates/customize_css_html.hbs b/app/assets/javascripts/admin/templates/customize_css_html.hbs
index e2cc7951a0..f4dce62b09 100644
--- a/app/assets/javascripts/admin/templates/customize_css_html.hbs
+++ b/app/assets/javascripts/admin/templates/customize_css_html.hbs
@@ -65,9 +65,9 @@
{{#unless selectedItem.changed}}
{{i18n 'admin.customize.preview'}}
|
-
{{i18n 'admin.customize.undo_preview'}}
+
{{i18n 'admin.customize.undo_preview'}}
|
-
{{i18n 'admin.customize.rescue_preview'}}
+
{{i18n 'admin.customize.rescue_preview'}}
{{/unless}}
diff --git a/app/assets/javascripts/admin/templates/permalinks.hbs b/app/assets/javascripts/admin/templates/permalinks.hbs
index 47717627b6..7d6e1ff8fc 100644
--- a/app/assets/javascripts/admin/templates/permalinks.hbs
+++ b/app/assets/javascripts/admin/templates/permalinks.hbs
@@ -10,9 +10,9 @@
{{i18n 'admin.permalink.url'}}
-
{{i18n 'admin.permalink.topic_id'}}
-
{{i18n 'admin.permalink.post_id'}}
-
{{i18n 'admin.permalink.category_id'}}
+
{{i18n 'admin.permalink.topic_title'}}
+
{{i18n 'admin.permalink.post_title'}}
+
{{i18n 'admin.permalink.category_title'}}
{{i18n 'admin.permalink.external_url'}}
diff --git a/app/assets/javascripts/admin/templates/permalinks_list_item.hbs b/app/assets/javascripts/admin/templates/permalinks_list_item.hbs
index 4fbf2db55e..2a143e07ed 100644
--- a/app/assets/javascripts/admin/templates/permalinks_list_item.hbs
+++ b/app/assets/javascripts/admin/templates/permalinks_list_item.hbs
@@ -1,7 +1,23 @@
{{url}}
-
{{topic_id}}
-
{{post_id}}
-
{{category_id}}
-
{{external_url}}
+
+
+
+
diff --git a/app/assets/javascripts/admin/templates/user-fields.hbs b/app/assets/javascripts/admin/templates/user-fields.hbs
index 7f6bab02f5..3a89a0e06e 100644
--- a/app/assets/javascripts/admin/templates/user-fields.hbs
+++ b/app/assets/javascripts/admin/templates/user-fields.hbs
@@ -4,13 +4,21 @@
{{i18n 'admin.user_fields.help'}}
{{#if model}}
- {{#each model as |uf|}}
- {{admin-user-field-item userField=uf fieldTypes=fieldTypes destroyAction="destroy"}}
+ {{#each arrangedContent as |uf|}}
+ {{admin-user-field-item userField=uf
+ fieldTypes=fieldTypes
+ firstField=arrangedContent.firstObject
+ lastField=arrangedContent.lastObject
+ destroyAction="destroy"
+ moveUpAction="moveUp"
+ moveDownAction="moveDown"}}
{{/each}}
{{/if}}
-
+ {{d-button disabled=createDisabled
+ class="btn-primary"
+ action="createField"
+ label="admin.user_fields.create"
+ icon="plus"}}
+
diff --git a/app/assets/javascripts/admin/templates/user_index.hbs b/app/assets/javascripts/admin/templates/user_index.hbs
index 9d2c510b31..302db5f4d0 100644
--- a/app/assets/javascripts/admin/templates/user_index.hbs
+++ b/app/assets/javascripts/admin/templates/user_index.hbs
@@ -350,8 +350,8 @@
{{combo-box content=model.customGroups value=model.primary_group_id nameProperty="name" none="admin.groups.no_primary"}}
{{/if}}
{{#if primaryGroupDirty}}
- {{d-button icon="check" class="ok no-text" action="savePrimaryGroup"}}
- {{d-button icon="times" class="cancel no-text" action="resetPrimaryGroup"}}
+ {{d-button icon="check" class="ok" action="savePrimaryGroup"}}
+ {{d-button icon="times" class="cancel" action="resetPrimaryGroup"}}
{{/if}}
diff --git a/app/assets/javascripts/discourse/adapters/rest.js.es6 b/app/assets/javascripts/discourse/adapters/rest.js.es6
index ec25f2d134..5a64730aba 100644
--- a/app/assets/javascripts/discourse/adapters/rest.js.es6
+++ b/app/assets/javascripts/discourse/adapters/rest.js.es6
@@ -17,10 +17,14 @@ function rethrow(error) {
}
export default Ember.Object.extend({
- pathFor(store, type, findArgs) {
- let path = "/" + Ember.String.underscore(store.pluralize(type));
- if (ADMIN_MODELS.indexOf(type) !== -1) { path = "/admin" + path; }
+ basePath(store, type) {
+ if (ADMIN_MODELS.indexOf(type) !== -1) { return "/admin/"; }
+ return "/";
+ },
+
+ pathFor(store, type, findArgs) {
+ let path = this.basePath(store, type, findArgs) + Ember.String.underscore(store.pluralize(type));
if (findArgs) {
if (typeof findArgs === "object") {
@@ -51,9 +55,10 @@ export default Ember.Object.extend({
update(store, type, id, attrs) {
const data = {};
- data[Ember.String.underscore(type)] = attrs;
+ const typeField = Ember.String.underscore(type);
+ data[typeField] = attrs;
return ajax(this.pathFor(store, type, id), { method: 'PUT', data }).then(function(json) {
- return new Result(json[type], json);
+ return new Result(json[typeField], json);
});
},
diff --git a/app/assets/javascripts/discourse/adapters/topic-list.js.es6 b/app/assets/javascripts/discourse/adapters/topic-list.js.es6
index 46a3a56683..bb7375597d 100644
--- a/app/assets/javascripts/discourse/adapters/topic-list.js.es6
+++ b/app/assets/javascripts/discourse/adapters/topic-list.js.es6
@@ -1,6 +1,6 @@
import RestAdapter from 'discourse/adapters/rest';
-function finderFor(filter, params) {
+export function finderFor(filter, params) {
return function() {
let url = Discourse.getURL("/") + filter + ".json";
@@ -9,7 +9,7 @@ function finderFor(filter, params) {
encoded = [];
keys.forEach(function(p) {
- const value = params[p];
+ const value = encodeURI(params[p]);
if (typeof value !== 'undefined') {
encoded.push(p + "=" + value);
}
diff --git a/app/assets/javascripts/discourse/adapters/user-field.js.es6 b/app/assets/javascripts/discourse/adapters/user-field.js.es6
new file mode 100644
index 0000000000..f57240a116
--- /dev/null
+++ b/app/assets/javascripts/discourse/adapters/user-field.js.es6
@@ -0,0 +1,7 @@
+import RestAdapter from 'discourse/adapters/rest';
+
+export default RestAdapter.extend({
+ basePath() {
+ return "/admin/customize/";
+ }
+});
diff --git a/app/assets/javascripts/discourse/components/combo-box.js.es6 b/app/assets/javascripts/discourse/components/combo-box.js.es6
index 5fdd914e82..d74410e89b 100644
--- a/app/assets/javascripts/discourse/components/combo-box.js.es6
+++ b/app/assets/javascripts/discourse/components/combo-box.js.es6
@@ -3,8 +3,9 @@ export default Ember.Component.extend({
attributeBindings: ['tabindex'],
classNames: ['combobox'],
valueAttribute: 'id',
+ nameProperty: 'name',
- buildData(o) {
+ _buildData(o) {
let result = "";
if (this.resultAttributes) {
this.resultAttributes.forEach(function(a) {
@@ -14,19 +15,15 @@ export default Ember.Component.extend({
return result;
},
- realNameProperty: function() {
- return this.get('nameProperty') || 'name';
- }.property('nameProperty'),
-
render(buffer) {
- const nameProperty = this.get('realNameProperty'),
- none = this.get('none');
+ const nameProperty = this.get('nameProperty');
+ const none = this.get('none');
// Add none option if required
if (typeof none === "string") {
buffer.push('");
} else if (typeof none === "object") {
- buffer.push("");
+ buffer.push("");
}
let selected = this.get('value');
@@ -35,18 +32,20 @@ export default Ember.Component.extend({
if (this.get('content')) {
const self = this;
this.get('content').forEach(function(o) {
- let val = o[self.get('valueAttribute')];
+ let val = o[self.get('valueAttribute')] || o;
if (!Em.isNone(val)) { val = val.toString(); }
const selectedText = (val === selected) ? "selected" : "";
- buffer.push("");
+ const name = Ember.get(o, nameProperty) || o;
+ buffer.push("");
});
}
},
valueChanged: function() {
const $combo = this.$(),
- val = this.get('value');
+ val = this.get('value');
+
if (val !== undefined && val !== null) {
$combo.select2('val', val.toString());
} else {
@@ -54,13 +53,11 @@ export default Ember.Component.extend({
}
}.observes('value'),
- contentChanged: function() {
+ _rerenderOnChange: function() {
this.rerender();
}.observes('content.@each'),
_initializeCombo: function() {
- const $elem = this.$(),
- self = this;
// Workaround for https://github.com/emberjs/ember.js/issues/9813
// Can be removed when fixed. Without it, the wrong option is selected
@@ -70,12 +67,14 @@ export default Ember.Component.extend({
// observer for item names changing (optional)
if (this.get('nameChanges')) {
- this.addObserver('content.@each.' + this.get('realNameProperty'), this.rerender);
+ this.addObserver('content.@each.' + this.get('nameProperty'), this.rerender);
}
+ const $elem = this.$();
$elem.select2({formatResult: this.comboTemplate, minimumResultsForSearch: 5, width: 'resolve'});
const castInteger = this.get('castInteger');
+ const self = this;
$elem.on("change", function (e) {
let val = $(e.target).val();
if (val && val.length && castInteger) {
diff --git a/app/assets/javascripts/discourse/components/d-button.js.es6 b/app/assets/javascripts/discourse/components/d-button.js.es6
index 7673e60ac4..45fe5effce 100644
--- a/app/assets/javascripts/discourse/components/d-button.js.es6
+++ b/app/assets/javascripts/discourse/components/d-button.js.es6
@@ -2,9 +2,11 @@ import { iconHTML } from 'discourse/helpers/fa-icon';
export default Ember.Component.extend({
tagName: 'button',
- classNameBindings: [':btn'],
+ classNameBindings: [':btn', 'noText'],
attributeBindings: ['disabled', 'translatedTitle:title'],
+ noText: Ember.computed.empty('translatedLabel'),
+
translatedTitle: function() {
const title = this.get('title');
return title ? I18n.t(title) : this.get('translatedLabel');
diff --git a/app/assets/javascripts/discourse/components/highlight-text.js.es6 b/app/assets/javascripts/discourse/components/highlight-text.js.es6
new file mode 100644
index 0000000000..ca783a23b7
--- /dev/null
+++ b/app/assets/javascripts/discourse/components/highlight-text.js.es6
@@ -0,0 +1,13 @@
+export default Ember.Component.extend({
+ tagName: 'span',
+
+ _highlightOnInsert: function() {
+ const term = this.get('highlight');
+ const self = this;
+
+ if(!_.isEmpty(term)) {
+ self.$().highlight(term.split(/\s+/), {className: 'search-highlight'});
+ }
+ }.observes('highlight').on('didInsertElement')
+
+});
diff --git a/app/assets/javascripts/admin/components/nav-item.js.es6 b/app/assets/javascripts/discourse/components/nav-item.js.es6
similarity index 100%
rename from app/assets/javascripts/admin/components/nav-item.js.es6
rename to app/assets/javascripts/discourse/components/nav-item.js.es6
diff --git a/app/assets/javascripts/discourse/components/post-menu.js.es6 b/app/assets/javascripts/discourse/components/post-menu.js.es6
index 89417192ed..33800387f2 100644
--- a/app/assets/javascripts/discourse/components/post-menu.js.es6
+++ b/app/assets/javascripts/discourse/components/post-menu.js.es6
@@ -85,8 +85,10 @@ const PostMenuComponent = Ember.Component.extend(StringBuffer, {
// Delegate click actions
click(e) {
- const $target = $(e.target),
- action = $target.data('action') || $target.parent().data('action');
+ const $target = $(e.target);
+ const action = $target.data('action') || $target.parent().data('action');
+
+ if ($target.prop('disabled') || $target.parent().prop('disabled')) { return; }
if (!action) return;
const handler = this["click" + action.classify()];
diff --git a/app/assets/javascripts/discourse/components/small-action.js.es6 b/app/assets/javascripts/discourse/components/small-action.js.es6
new file mode 100644
index 0000000000..50e42f19ab
--- /dev/null
+++ b/app/assets/javascripts/discourse/components/small-action.js.es6
@@ -0,0 +1,52 @@
+const icons = {
+ 'closed.enabled': 'lock',
+ 'closed.disabled': 'unlock-alt',
+ 'autoclosed.enabled': 'lock',
+ 'autoclosed.disabled': 'unlock-alt',
+ 'archived.enabled': 'folder',
+ 'archived.disabled': 'folder-open',
+ 'pinned.enabled': 'thumb-tack',
+ 'pinned.disabled': 'thumb-tack unpinned',
+ 'pinned_globally.enabled': 'thumb-tack',
+ 'pinned_globally.disabled': 'thumb-tack unpinned',
+ 'visible.enabled': 'eye',
+ 'visible.disabled': 'eye-slash'
+};
+
+export default Ember.Component.extend({
+ layoutName: 'components/small-action', // needed because `time-gap` inherits from this
+ classNames: ['small-action'],
+
+ description: function() {
+ const actionCode = this.get('actionCode');
+ if (actionCode) {
+ const dt = new Date(this.get('post.created_at'));
+ const when = Discourse.Formatter.relativeAge(dt, {format: 'medium-with-ago'});
+ var result = I18n.t(`action_codes.${actionCode}`, {when});
+ var cooked = this.get('post.cooked');
+
+ result = "" + result + "
";
+
+ if (!Em.isEmpty(cooked)) {
+ result += "" + cooked + "
";
+ }
+
+ return result;
+ }
+ }.property('actionCode', 'post.created_at', 'post.cooked'),
+
+ icon: function() {
+ return icons[this.get('actionCode')] || 'exclamation';
+ }.property('actionCode'),
+
+ actions: {
+ edit: function() {
+ this.sendAction('editPost', this.get('post'));
+ },
+
+ delete: function() {
+ this.sendAction('deletePost', this.get('post'));
+ }
+ }
+
+});
diff --git a/app/assets/javascripts/discourse/components/time-gap.js.es6 b/app/assets/javascripts/discourse/components/time-gap.js.es6
index 7b7b9a6c49..3cd887ec43 100644
--- a/app/assets/javascripts/discourse/components/time-gap.js.es6
+++ b/app/assets/javascripts/discourse/components/time-gap.js.es6
@@ -1,22 +1,19 @@
-export default Ember.Component.extend({
- classNameBindings: [':time-gap'],
+import SmallActionComponent from 'discourse/components/small-action';
- render(buffer) {
- const gapDays = this.get('gapDays');
+export default SmallActionComponent.extend({
+ classNames: ['time-gap'],
+ icon: 'clock-o',
- buffer.push("
");
-
- let timeGapWords;
+ description: function() {
+ const gapDays = this.get('daysAgo');
if (gapDays < 30) {
- timeGapWords = I18n.t('dates.later.x_days', {count: gapDays});
+ return I18n.t('dates.later.x_days', {count: gapDays});
} else if (gapDays < 365) {
const gapMonths = Math.floor(gapDays / 30);
- timeGapWords = I18n.t('dates.later.x_months', {count: gapMonths});
+ return I18n.t('dates.later.x_months', {count: gapMonths});
} else {
const gapYears = Math.floor(gapDays / 365);
- timeGapWords = I18n.t('dates.later.x_years', {count: gapYears});
+ return I18n.t('dates.later.x_years', {count: gapYears});
}
-
- buffer.push("" + timeGapWords + "
");
- }
+ }.property(),
});
diff --git a/app/assets/javascripts/discourse/components/user-field.js.es6 b/app/assets/javascripts/discourse/components/user-field.js.es6
index a1c6f56b4d..625067622e 100644
--- a/app/assets/javascripts/discourse/components/user-field.js.es6
+++ b/app/assets/javascripts/discourse/components/user-field.js.es6
@@ -1,6 +1,10 @@
export default Ember.Component.extend({
classNameBindings: [':user-field', 'field.field_type'],
- layoutName: function() {
- return "components/user-fields/" + this.get('field.field_type');
- }.property('field.field_type')
+ layoutName: Discourse.computed.fmt('field.field_type', 'components/user-fields/%@'),
+
+ noneLabel: function() {
+ if (!this.get('field.required')) {
+ return 'user_fields.none';
+ }
+ }.property('field.required')
});
diff --git a/app/assets/javascripts/discourse/controllers/composer.js.es6 b/app/assets/javascripts/discourse/controllers/composer.js.es6
index 75695d8055..8ad012dd28 100644
--- a/app/assets/javascripts/discourse/controllers/composer.js.es6
+++ b/app/assets/javascripts/discourse/controllers/composer.js.es6
@@ -168,15 +168,15 @@ export default Ember.ObjectController.extend(Presence, {
}.property('model.loading'),
save(force) {
- const composer = this.get('model'),
- self = this;
+ const composer = this.get('model');
+ const self = this;
// Clear the warning state if we're not showing the checkbox anymore
if (!this.get('showWarning')) {
this.set('model.isWarning', false);
}
- if(composer.get('cantSubmitPost')) {
+ if (composer.get('cantSubmitPost')) {
const now = Date.now();
this.setProperties({
'view.showTitleTip': now,
diff --git a/app/assets/javascripts/discourse/controllers/create-account.js.es6 b/app/assets/javascripts/discourse/controllers/create-account.js.es6
index b5812eb6a9..a668d4cc88 100644
--- a/app/assets/javascripts/discourse/controllers/create-account.js.es6
+++ b/app/assets/javascripts/discourse/controllers/create-account.js.es6
@@ -394,11 +394,8 @@ export default DiscourseController.extend(ModalFunctionality, {
let userFields = this.site.get('user_fields');
if (userFields) {
- userFields = userFields.map(function(f) {
- return Ember.Object.create({
- value: null,
- field: f
- });
+ userFields = _.sortBy(userFields, 'position').map(function(f) {
+ return Ember.Object.create({ value: null, field: f });
});
}
this.set('userFields', userFields);
diff --git a/app/assets/javascripts/discourse/controllers/discovery/topics.js.es6 b/app/assets/javascripts/discourse/controllers/discovery/topics.js.es6
index a69daece73..71a316ef02 100644
--- a/app/assets/javascripts/discourse/controllers/discovery/topics.js.es6
+++ b/app/assets/javascripts/discourse/controllers/discovery/topics.js.es6
@@ -16,37 +16,15 @@ const controllerOpts = {
expandGloballyPinned: false,
expandAllPinned: false,
- isSearch: Em.computed.equal('model.filter', 'search'),
-
- searchTerm: function(){
- return this.get('model.params.q');
- }.property('isSearch,model.params,model'),
-
actions: {
changeSort(sortBy) {
- if (this.get('isSearch')) {
- let term = this.get('searchTerm');
- let order;
-
- if (sortBy === 'activity') { order = 'latest'; }
- if (sortBy === 'views') { order = 'views'; }
-
- if (order && term.indexOf("order:" + order) === -1) {
- term = term.replace(/order:[a-z]+/, '');
- term = term.trim() + " order:" + order;
- this.set('model.params.q', term);
- this.get('model').refreshSort();
- }
-
+ if (sortBy === this.get('order')) {
+ this.toggleProperty('ascending');
} else {
- if (sortBy === this.get('order')) {
- this.toggleProperty('ascending');
- } else {
- this.setProperties({ order: sortBy, ascending: false });
- }
- this.get('model').refreshSort(sortBy, this.get('ascending'));
+ this.setProperties({ order: sortBy, ascending: false });
}
+ this.get('model').refreshSort(sortBy, this.get('ascending'));
},
// Show newly inserted topics
@@ -128,6 +106,7 @@ const controllerOpts = {
new: Discourse.computed.endWith('model.filter', 'new'),
top: Em.computed.notEmpty('period'),
yearly: Em.computed.equal('period', 'yearly'),
+ quarterly: Em.computed.equal('period', 'quarterly'),
monthly: Em.computed.equal('period', 'monthly'),
weekly: Em.computed.equal('period', 'weekly'),
daily: Em.computed.equal('period', 'daily'),
diff --git a/app/assets/javascripts/discourse/controllers/feature-topic.js.es6 b/app/assets/javascripts/discourse/controllers/feature-topic.js.es6
index a00c79a5b3..990d2b83ae 100644
--- a/app/assets/javascripts/discourse/controllers/feature-topic.js.es6
+++ b/app/assets/javascripts/discourse/controllers/feature-topic.js.es6
@@ -10,15 +10,23 @@ export default ObjectController.extend(ModalFunctionality, {
pinnedGloballyCount: 0,
bannerCount: 0,
+ reset: function() {
+ this.set("model.pinnedInCategoryUntil", null);
+ this.set("model.pinnedGloballyUntil", null);
+ },
+
categoryLink: function() {
return categoryLinkHTML(this.get("model.category"), { allowUncategorized: true });
}.property("model.category"),
unPinMessage: function() {
- return this.get("model.pinned_globally") ?
- I18n.t("topic.feature_topic.unpin_globally") :
- I18n.t("topic.feature_topic.unpin", { categoryLink: this.get("categoryLink") });
- }.property("categoryLink", "model.pinned_globally"),
+ let name = "topic.feature_topic.unpin";
+ if (this.get("model.pinned_globally")) name += "_globally";
+ if (moment(this.get("model.pinned_until")) > moment()) name += "_until";
+ const until = moment(this.get("model.pinned_until")).format("LL");
+
+ return I18n.t(name, { categoryLink: this.get("categoryLink"), until: until });
+ }.property("categoryLink", "model.{pinned_globally,pinned_until}"),
pinMessage: function() {
return I18n.t("topic.feature_topic.pin", { categoryLink: this.get("categoryLink") });
@@ -28,6 +36,30 @@ export default ObjectController.extend(ModalFunctionality, {
return I18n.t("topic.feature_topic.already_pinned", { categoryLink: this.get("categoryLink"), count: this.get("pinnedInCategoryCount") });
}.property("categoryLink", "pinnedInCategoryCount"),
+ pinDisabled: function() {
+ return !this._isDateValid(this.get("parsedPinnedInCategoryUntil"));
+ }.property("parsedPinnedInCategoryUntil"),
+
+ pinGloballyDisabled: function() {
+ return !this._isDateValid(this.get("parsedPinnedGloballyUntil"));
+ }.property("pinnedGloballyUntil"),
+
+ parsedPinnedInCategoryUntil: function() {
+ return this._parseDate(this.get("model.pinnedInCategoryUntil"));
+ }.property("model.pinnedInCategoryUntil"),
+
+ parsedPinnedGloballyUntil: function() {
+ return this._parseDate(this.get("model.pinnedGloballyUntil"));
+ }.property("model.pinnedGloballyUntil"),
+
+ _parseDate(date) {
+ return moment(date, ["YYYY-MM-DD", "YYYY-MM-DD HH:mm"]);
+ },
+
+ _isDateValid(parsedDate) {
+ return parsedDate.isValid() && parsedDate > moment();
+ },
+
onShow() {
this.set("loading", true);
diff --git a/app/assets/javascripts/discourse/controllers/full-page-search.js.es6 b/app/assets/javascripts/discourse/controllers/full-page-search.js.es6
new file mode 100644
index 0000000000..a763a94c3d
--- /dev/null
+++ b/app/assets/javascripts/discourse/controllers/full-page-search.js.es6
@@ -0,0 +1,33 @@
+import DiscourseController from 'discourse/controllers/controller';
+import { translateResults } from 'discourse/lib/search-for-term';
+
+export default DiscourseController.extend({
+ loading: Em.computed.not('model'),
+ queryParams: ['q'],
+ q: null,
+ modelChanged: function(){
+ if (this.get('searchTerm') !== this.get('q')) {
+ this.set('searchTerm', this.get('q'));
+ }
+ }.observes('model'),
+
+ qChanged: function(){
+ var model = this.get('model');
+ if (model && this.get('model.q') !== this.get('q')){
+ this.set('searchTerm', this.get('q'));
+ this.send('search');
+ }
+ }.observes('q'),
+ actions: {
+ search: function(){
+ var self = this;
+ this.set('q', this.get('searchTerm'));
+ this.set('model', null);
+
+ Discourse.ajax('/search', {data: {q: this.get('searchTerm')}}).then(function(results) {
+ self.set('model', translateResults(results) || {});
+ self.set('model.q', self.get('q'));
+ });
+ }
+ }
+});
diff --git a/app/assets/javascripts/discourse/controllers/navigation/default.js.es6 b/app/assets/javascripts/discourse/controllers/navigation/default.js.es6
index 79fb9561bf..7832d4ab51 100644
--- a/app/assets/javascripts/discourse/controllers/navigation/default.js.es6
+++ b/app/assets/javascripts/discourse/controllers/navigation/default.js.es6
@@ -9,18 +9,6 @@ export default DiscourseController.extend({
navItems: function() {
return Discourse.NavItem.buildList(null, {filterMode: this.get('filterMode')});
- }.property('filterMode'),
+ }.property('filterMode')
- isSearch: Em.computed.equal('filterMode', 'search'),
-
- searchTerm: Em.computed.alias('controllers.discovery/topics.model.params.q'),
-
- actions: {
- search: function(){
- var discovery = this.get('controllers.discovery/topics');
- var model = discovery.get('model');
- discovery.set('q', this.get('searchTerm'));
- model.refreshSort();
- }
- }
});
diff --git a/app/assets/javascripts/discourse/controllers/topic.js.es6 b/app/assets/javascripts/discourse/controllers/topic.js.es6
index 066b000a26..aace6f8e0f 100644
--- a/app/assets/javascripts/discourse/controllers/topic.js.es6
+++ b/app/assets/javascripts/discourse/controllers/topic.js.es6
@@ -99,19 +99,6 @@ export default ObjectController.extend(SelectedPostsCount, BufferedContent, {
this.set('selectedReplies', []);
}.on('init'),
- _togglePinnedStates(property) {
- const value = this.get('model.pinned_at') ? false : true,
- topic = this.get('content');
-
- // optimistic update
- topic.setProperties({
- pinned_at: value,
- pinned_globally: value
- });
-
- return topic.saveStatus(property, value);
- },
-
actions: {
deleteTopic() {
this.deleteTopic();
@@ -371,27 +358,31 @@ export default ObjectController.extend(SelectedPostsCount, BufferedContent, {
togglePinned() {
const value = this.get('model.pinned_at') ? false : true,
- topic = this.get('content');
+ topic = this.get('content'),
+ until = this.get('model.pinnedInCategoryUntil');
// optimistic update
topic.setProperties({
pinned_at: value ? moment() : null,
- pinned_globally: false
+ pinned_globally: false,
+ pinned_until: value ? until : null
});
- return topic.saveStatus("pinned", value);
+ return topic.saveStatus("pinned", value, until);
},
pinGlobally() {
- const topic = this.get('content');
+ const topic = this.get('content'),
+ until = this.get('model.pinnedGloballyUntil');
// optimistic update
topic.setProperties({
pinned_at: moment(),
- pinned_globally: true
+ pinned_globally: true,
+ pinned_until: until
});
- return topic.saveStatus("pinned_globally", true);
+ return topic.saveStatus("pinned_globally", true, until);
},
toggleArchived() {
diff --git a/app/assets/javascripts/discourse/dialects/dialect.js b/app/assets/javascripts/discourse/dialects/dialect.js
index e5b9048401..62febca1a7 100644
--- a/app/assets/javascripts/discourse/dialects/dialect.js
+++ b/app/assets/javascripts/discourse/dialects/dialect.js
@@ -475,7 +475,7 @@ Discourse.Dialect = {
**/
replaceBlock: function(args) {
- this.registerBlock(args.start.toString(), function(block, next) {
+ var fn = function(block, next) {
var linebreaks = dialect.options.traditional_markdown_linebreaks ||
Discourse.SiteSettings.traditional_markdown_linebreaks;
@@ -565,7 +565,13 @@ Discourse.Dialect = {
var emitterResult = args.emitter.call(this, contentBlocks, match, dialect.options);
if (emitterResult) { result.push(emitterResult); }
return result;
- });
+ };
+
+ if (args.priority) {
+ fn.priority = args.priority;
+ }
+
+ this.registerBlock(args.start.toString(), fn);
},
/**
diff --git a/app/assets/javascripts/discourse/dialects/table_dialect.js b/app/assets/javascripts/discourse/dialects/table_dialect.js
new file mode 100644
index 0000000000..ad1bb375df
--- /dev/null
+++ b/app/assets/javascripts/discourse/dialects/table_dialect.js
@@ -0,0 +1,52 @@
+var flattenBlocks = function(blocks) {
+ var result = "";
+ blocks.forEach(function(b) {
+ result += b;
+ if (b.trailing) { result += b.trailing; }
+ });
+
+ // bypass newline insertion
+ return result.replace(/[\n\r]/g, " ");
+};
+
+var emitter = function(contents) {
+ // TODO event should be fired when sanitizer loads
+ if (window.html4 && window.html4.ELEMENTS.td !== 1) {
+ window.html4.ELEMENTS.table = 0;
+ window.html4.ELEMENTS.tbody = 1;
+ window.html4.ELEMENTS.td = 1;
+ window.html4.ELEMENTS.thead = 1;
+ window.html4.ELEMENTS.th = 1;
+ window.html4.ELEMENTS.tr = 1;
+ }
+ return ['table', {"class": "md-table"}, flattenBlocks.apply(this, [contents])];
+};
+
+var tableBlock = {
+ start: /(]*>)([\S\s]*)/igm,
+ stop: /<\/table>/igm,
+ rawContents: true,
+ emitter: emitter,
+ priority: 1
+};
+
+var init = function(){
+ if (Discourse.SiteSettings.allow_html_tables) {
+ Discourse.Markdown.whiteListTag("table");
+ Discourse.Markdown.whiteListTag("table", "class", "md-table");
+ Discourse.Markdown.whiteListTag("tbody");
+ Discourse.Markdown.whiteListTag("thead");
+ Discourse.Markdown.whiteListTag("tr");
+ Discourse.Markdown.whiteListTag("th");
+ Discourse.Markdown.whiteListTag("td");
+ Discourse.Dialect.replaceBlock(tableBlock);
+
+ }
+};
+
+if (Discourse.SiteSettings) {
+ init();
+} else {
+ Discourse.initializer({initialize: init, name: 'enable-html-tables'});
+}
+
diff --git a/app/assets/javascripts/discourse/helpers/period-title.js.es6 b/app/assets/javascripts/discourse/helpers/period-title.js.es6
index 82c1e6ba58..d8267547a7 100644
--- a/app/assets/javascripts/discourse/helpers/period-title.js.es6
+++ b/app/assets/javascripts/discourse/helpers/period-title.js.es6
@@ -1,6 +1,7 @@
const TITLE_SUBS = {
all: 'all_time',
yearly: 'this_year',
+ quarterly: 'this_quarter',
monthly: 'this_month',
daily: 'today',
};
@@ -13,6 +14,9 @@ export default Ember.Handlebars.makeBoundHelper(function (period, options) {
case 'yearly':
dateString = moment().subtract(1, 'year').format(I18n.t('dates.long_with_year_no_time')) + " - " + moment().format(I18n.t('dates.long_with_year_no_time'));
break;
+ case 'quarterly':
+ dateString = moment().subtract(3, 'month').format(I18n.t('dates.long_no_year_no_time')) + " - " + moment().format(I18n.t('dates.long_no_year_no_time'));
+ break;
case 'weekly':
dateString = moment().subtract(1, 'week').format(I18n.t('dates.long_no_year_no_time')) + " - " + moment().format(I18n.t('dates.long_no_year_no_time'));
break;
diff --git a/app/assets/javascripts/discourse/helpers/topic-link.js.es6 b/app/assets/javascripts/discourse/helpers/topic-link.js.es6
index 105ec123a9..9c2a73870e 100644
--- a/app/assets/javascripts/discourse/helpers/topic-link.js.es6
+++ b/app/assets/javascripts/discourse/helpers/topic-link.js.es6
@@ -3,5 +3,7 @@ import registerUnbound from 'discourse/helpers/register-unbound';
registerUnbound('topic-link', function(topic) {
var title = topic.get('fancyTitle');
var url = topic.linked_post_number ? topic.urlForPostNumber(topic.linked_post_number) : topic.get('lastUnreadUrl');
- return new Handlebars.SafeString("" + title + "");
+
+ var extraClass = topic.get('last_read_post_number') === topic.get('highest_post_number') ? " visited" : "";
+ return new Handlebars.SafeString("");
});
diff --git a/app/assets/javascripts/discourse/initializers/url-redirects.js.es6 b/app/assets/javascripts/discourse/initializers/url-redirects.js.es6
index 2164be0256..fa8514e6d5 100644
--- a/app/assets/javascripts/discourse/initializers/url-redirects.js.es6
+++ b/app/assets/javascripts/discourse/initializers/url-redirects.js.es6
@@ -6,5 +6,6 @@ export default {
Discourse.URL.rewrite(/^\/category\//, "/c/");
Discourse.URL.rewrite(/^\/group\//, "/groups/");
Discourse.URL.rewrite(/\/private-messages\/$/, "/messages/");
+ Discourse.URL.rewrite(/^\/users\/([^\/]+)\/?$/, "/users/$1/activity");
}
};
diff --git a/app/assets/javascripts/discourse/lib/copy-text.js.es6 b/app/assets/javascripts/discourse/lib/copy-text.js.es6
new file mode 100644
index 0000000000..8730362339
--- /dev/null
+++ b/app/assets/javascripts/discourse/lib/copy-text.js.es6
@@ -0,0 +1,34 @@
+/**
+ * Copy text to the clipboard. Must be called from within a user gesture (Chrome).
+ */
+export default function(text, element) {
+ let supported = false;
+ try {
+ // Chrome: This only returns true within a user gesture.
+ // Chrome: queryCommandEnabled() only returns true if a selection is
+ // present, so we use queryCommandSupported() instead for the fail-fast.
+ if (document.queryCommandSupported('copy')) {
+ supported = true;
+ }
+ } catch (e) {
+ // Ignore
+ }
+ if (!supported) {
+ return;
+ }
+
+ let newRange = document.createRange();
+ newRange.selectNode(element);
+ const selection = window.getSelection();
+ selection.removeAllRanges();
+ selection.addRange(newRange);
+
+ try {
+ if (document.execCommand("copy")) {
+ return true;
+ }
+ } catch (e) {
+ // Ignore
+ }
+ return false;
+}
diff --git a/app/assets/javascripts/discourse/lib/emoji/emoji-toolbar.js.es6 b/app/assets/javascripts/discourse/lib/emoji/emoji-toolbar.js.es6
index b9c7a5656c..00287b21a5 100644
--- a/app/assets/javascripts/discourse/lib/emoji/emoji-toolbar.js.es6
+++ b/app/assets/javascripts/discourse/lib/emoji/emoji-toolbar.js.es6
@@ -1,27 +1,48 @@
+// note that these categories are copied from Slack
+// be careful, there are ~20 differences in synonyms, e.g. :boom: vs. :collision:
+// a few Emoji are actually missing from the Slack categories as well (?), and were added
var groups = [
{
- name: "emoticons",
- icons: ["smile","smiley","grinning","blush","relaxed","wink","heart_eyes","kissing_heart","kissing_closed_eyes","kissing","kissing_smiling_eyes","stuck_out_tongue_winking_eye","stuck_out_tongue_closed_eyes","stuck_out_tongue","flushed","grin","pensive","relieved","unamused","disappointed","persevere","cry","joy","sob","sleepy","disappointed_relieved","cold_sweat","sweat_smile","sweat","weary","tired_face","fearful","scream","angry","rage","triumph","confounded","laughing","yum","mask","sunglasses","sleeping","dizzy_face","astonished","worried","frowning","anguished","smiling_imp","imp","open_mouth","grimacing","neutral_face","confused","hushed","no_mouth","innocent","smirk","expressionless","man_with_gua_pi_mao","man_with_turban","cop","construction_worker","guardsman","baby","boy","girl","man","woman","older_man","older_woman","person_with_blond_hair","angel","princess","smiley_cat","smile_cat","heart_eyes_cat","kissing_cat","smirk_cat","scream_cat","crying_cat_face","joy_cat","pouting_cat","japanese_ogre","japanese_goblin","see_no_evil","hear_no_evil","speak_no_evil","skull","alien","poop","fire","sparkles","star2","dizzy","boom","anger","sweat_drops","droplet","zzz","dash","ear","eyes","nose","tongue","lips","thumbsup","thumbsdown","ok_hand","punch","fist","v","wave","raised_hand","open_hands","point_up_2","point_down","point_right","point_left","raised_hands","pray","point_up","clap","muscle","walking","runner","dancer","couple","family","two_men_holding_hands","two_women_holding_hands","couplekiss","couple_with_heart","dancers","ok_woman","no_good","information_desk_person","raising_hand","massage","haircut","nail_care","bride_with_veil","person_with_pouting_face","person_frowning","bow","tophat","crown","womans_hat","athletic_shoe","mans_shoe","sandal","high_heel","boot","shirt","necktie","womans_clothes","dress","running_shirt_with_sash","jeans","kimono","bikini","briefcase","handbag","pouch","purse","eyeglasses","ribbon","closed_umbrella","lipstick","yellow_heart","blue_heart","purple_heart","green_heart","heart","broken_heart","heartpulse","heartbeat","two_hearts","sparkling_heart","revolving_hearts","cupid","love_letter","kiss","ring","gem","bust_in_silhouette","busts_in_silhouette","speech_balloon","footprints","thought_balloon"]
+ name: "people",
+ fullname: "People",
+ tabicon: "grinning",
+ icons: ["grinning", "grin", "joy", "smiley", "smile", "sweat_smile", "laughing", "innocent", "smiling_imp", "imp", "wink", "blush", "relaxed", "yum", "relieved", "heart_eyes", "sunglasses", "smirk", "neutral_face", "expressionless", "unamused", "sweat", "pensive", "confused", "confounded", "kissing", "kissing_heart", "kissing_smiling_eyes", "kissing_closed_eyes", "stuck_out_tongue", "stuck_out_tongue_winking_eye", "stuck_out_tongue_closed_eyes", "disappointed", "worried", "angry", "rage", "cry", "persevere", "triumph", "disappointed_relieved", "frowning", "anguished", "fearful", "weary", "sleepy", "tired_face", "grimacing", "sob", "open_mouth", "hushed", "cold_sweat", "scream", "astonished", "flushed", "sleeping", "dizzy_face", "no_mouth", "mask", "smile_cat", "joy_cat", "smiley_cat", "heart_eyes_cat", "smirk_cat", "kissing_cat", "pouting_cat", "crying_cat_face", "scream_cat", "footprints", "bust_in_silhouette", "busts_in_silhouette", "baby", "boy", "girl", "man", "woman", "family", "couple", "two_men_holding_hands", "two_women_holding_hands", "dancers", "bride_with_veil", "person_with_blond_hair", "man_with_gua_pi_mao", "man_with_turban", "older_man", "older_woman", "cop", "construction_worker", "princess", "guardsman", "angel", "santa", "ghost", "japanese_ogre", "japanese_goblin", "hankey", "skull", "alien", "space_invader", "bow", "information_desk_person", "no_good", "ok_woman", "raising_hand", "person_with_pouting_face", "person_frowning", "massage", "haircut", "couple_with_heart", "couplekiss", "raised_hands", "clap", "hand", "ear", "eyes", "nose", "lips", "kiss", "tongue", "nail_care", "wave", "+1", "-1", "point_up", "point_up_2", "point_down", "point_left", "point_right", "ok_hand", "v", "facepunch", "fist", "raised_hand", "muscle", "open_hands", "pray"]
},
{
name: "nature",
- icons: ["dog","wolf","cat","mouse","hamster","rabbit","frog","tiger","koala","bear","pig","pig_nose","cow","boar","monkey_face","monkey","horse","sheep","elephant","panda_face","penguin","bird","baby_chick","hatched_chick","hatching_chick","chicken","snake","turtle","bug","bee","ant","beetle","snail","octopus","shell","tropical_fish","fish","dolphin","whale","whale2","cow2","ram","rat","water_buffalo","tiger2","rabbit2","dragon","racehorse","goat","rooster","dog2","pig2","mouse2","ox","dragon_face","blowfish","crocodile","camel","dromedary_camel","leopard","cat2","poodle","feet","bouquet","cherry_blossom","tulip","four_leaf_clover","rose","sunflower","hibiscus","maple_leaf","leaves","fallen_leaf","herb","ear_of_rice","mushroom","cactus","palm_tree","evergreen_tree","deciduous_tree","chestnut","seedling","blossom","globe_with_meridians","sun_with_face","full_moon_with_face","new_moon_with_face","new_moon","waxing_crescent_moon","first_quarter_moon","waxing_gibbous_moon","full_moon","waning_gibbous_moon","last_quarter_moon","waning_crescent_moon","last_quarter_moon_with_face","first_quarter_moon_with_face","crescent_moon","earth_africa","earth_americas","earth_asia","volcano","milky_way","stars","star","sunny","partly_sunny","cloud","zap","umbrella","snowflake","snowman","cyclone","foggy","rainbow","ocean"]
+ fullname: "Nature",
+ tabicon: "leaves",
+ icons: ["seedling", "evergreen_tree", "deciduous_tree", "palm_tree", "cactus", "tulip", "cherry_blossom", "rose", "hibiscus", "sunflower", "blossom", "bouquet", "ear_of_rice", "herb", "four_leaf_clover", "maple_leaf", "fallen_leaf", "leaves", "mushroom", "chestnut", "rat", "mouse2", "mouse", "hamster", "ox", "water_buffalo", "cow2", "cow", "tiger2", "leopard", "tiger", "rabbit2", "rabbit", "cat2", "cat", "racehorse", "horse", "ram", "sheep", "goat", "rooster", "chicken", "baby_chick", "hatching_chick", "hatched_chick", "bird", "penguin", "elephant", "dromedary_camel", "camel", "boar", "pig2", "pig", "pig_nose", "dog2", "poodle", "dog", "wolf", "bear", "koala", "panda_face", "monkey_face", "see_no_evil", "hear_no_evil", "speak_no_evil", "monkey", "dragon", "dragon_face", "crocodile", "snake", "turtle", "frog", "whale2", "whale", "dolphin", "octopus", "fish", "tropical_fish", "blowfish", "shell", "snail", "bug", "ant", "bee", "beetle", "feet", "zap", "fire", "crescent_moon", "sunny", "partly_sunny", "cloud", "droplet", "sweat_drops", "umbrella", "dash", "snowflake", "star2", "star", "stars", "sunrise_over_mountains", "sunrise", "rainbow", "ocean", "volcano", "milky_way", "mount_fuji", "japan", "globe_with_meridians", "earth_africa", "earth_americas", "earth_asia", "new_moon", "waxing_crescent_moon", "first_quarter_moon", "moon", "full_moon", "waning_gibbous_moon", "last_quarter_moon", "waning_crescent_moon", "new_moon_with_face", "full_moon_with_face", "first_quarter_moon_with_face", "last_quarter_moon_with_face", "sun_with_face"]
+ },
+ {
+ name: "food",
+ fullname: "Food & Drink",
+ tabicon: "hamburger",
+ icons: ["tomato", "eggplant", "corn", "sweet_potato", "grapes", "melon", "watermelon", "tangerine", "lemon", "banana", "pineapple", "apple", "green_apple", "pear", "peach", "cherries", "strawberry", "hamburger", "pizza", "meat_on_bone", "poultry_leg", "rice_cracker", "rice_ball", "rice", "curry", "ramen", "spaghetti", "bread", "fries", "dango", "oden", "sushi", "fried_shrimp", "fish_cake", "icecream", "shaved_ice", "ice_cream", "doughnut", "cookie", "chocolate_bar", "candy", "lollipop", "custard", "honey_pot", "cake", "bento", "stew", "egg", "fork_and_knife", "tea", "coffee", "sake", "wine_glass", "cocktail", "tropical_drink", "beer", "beers", "baby_bottle"]
+ },
+ {
+ name: "celebration",
+ fullname: "Celebration",
+ tabicon: "gift",
+ icons: ["ribbon", "gift", "birthday", "jack_o_lantern", "christmas_tree", "tanabata_tree", "bamboo", "rice_scene", "fireworks", "sparkler", "tada", "confetti_ball", "balloon", "dizzy", "sparkles", "boom", "mortar_board", "crown", "dolls", "flags", "wind_chime", "crossed_flags", "izakaya_lantern", "ring", "heart", "broken_heart", "love_letter", "two_hearts", "revolving_hearts", "heartbeat", "heartpulse", "sparkling_heart", "cupid", "gift_heart", "heart_decoration", "purple_heart", "yellow_heart", "green_heart", "blue_heart"]
+ },
+ {
+ name: "activity",
+ fullname: "Activities",
+ tabicon: "soccer",
+ icons: ["runner", "walking", "dancer", "rowboat", "swimmer", "surfer", "bath", "snowboarder", "ski", "snowman", "bicyclist", "mountain_bicyclist", "horse_racing", "tent", "fishing_pole_and_fish", "soccer", "basketball", "football", "baseball", "tennis", "rugby_football", "golf", "trophy", "running_shirt_with_sash", "checkered_flag", "musical_keyboard", "guitar", "violin", "saxophone", "trumpet", "musical_note", "notes", "musical_score", "headphones", "microphone", "performing_arts", "ticket", "tophat", "circus_tent", "clapper", "art", "dart", "8ball", "bowling", "slot_machine", "game_die", "video_game", "flower_playing_cards", "black_joker", "mahjong", "carousel_horse", "ferris_wheel", "roller_coaster"]
+ },
+ {
+ name: "travel",
+ fullname: "Travel & Places",
+ tabicon: "airplane",
+ icons: ["train", "mountain_railway", "railway_car", "steam_locomotive", "monorail", "bullettrain_side", "bullettrain_front", "train2", "metro", "light_rail", "station", "tram", "bus", "oncoming_bus", "trolleybus", "minibus", "ambulance", "fire_engine", "police_car", "oncoming_police_car", "rotating_light", "taxi", "oncoming_taxi", "car", "oncoming_automobile", "blue_car", "truck", "articulated_lorry", "tractor", "bike", "busstop", "fuelpump", "construction", "vertical_traffic_light", "traffic_light", "rocket", "helicopter", "airplane", "seat", "anchor", "ship", "speedboat", "boat", "aerial_tramway", "mountain_cableway", "suspension_railway", "passport_control", "customs", "baggage_claim", "left_luggage", "yen", "euro", "pound", "dollar", "statue_of_liberty", "moyai", "foggy", "tokyo_tower", "fountain", "european_castle", "japanese_castle", "city_sunrise", "city_sunset", "night_with_stars", "bridge_at_night", "house", "house_with_garden", "office", "department_store", "factory", "post_office", "european_post_office", "hospital", "bank", "hotel", "love_hotel", "wedding", "church", "convenience_store", "school", "cn", "de", "es", "fr", "gb", "it", "jp", "kr", "ru", "us"]
},
{
name: "objects",
- icons: ["bamboo","gift_heart","dolls","school_satchel","mortar_board","flags","fireworks","sparkler","wind_chime","rice_scene","jack_o_lantern","ghost","santa","christmas_tree","gift","tanabata_tree","tada","confetti_ball","balloon","crossed_flags","crystal_ball","movie_camera","camera","video_camera","vhs","cd","dvd","minidisc","floppy_disk","computer","iphone","telephone","telephone_receiver","pager","fax","satellite","tv","radio","loud_sound","sound","speaker","mute","bell","no_bell","loudspeaker","mega","hourglass_flowing_sand","hourglass","alarm_clock","watch","unlock","lock","lock_with_ink_pen","closed_lock_with_key","key","mag_right","bulb","flashlight","high_brightness","low_brightness","electric_plug","battery","mag","bathtub","bath","shower","toilet","wrench","nut_and_bolt","hammer","door","smoking","bomb","gun","knife","pill","syringe","moneybag","yen","dollar","pound","euro","credit_card","money_with_wings","calling","e-mail","inbox_tray","outbox_tray","envelope","envelope_with_arrow","incoming_envelope","postal_horn","mailbox","mailbox_closed","mailbox_with_mail","mailbox_with_no_mail","postbox","package","pencil","page_facing_up","page_with_curl","bookmark_tabs","bar_chart","chart_with_upwards_trend","chart_with_downwards_trend","scroll","clipboard","date","calendar","card_index","file_folder","open_file_folder","scissors","pushpin","paperclip","black_nib","pencil2","straight_ruler","triangular_ruler","closed_book","green_book","blue_book","orange_book","notebook","notebook_with_decorative_cover","ledger","books","book","bookmark","name_badge","microscope","telescope","newspaper","art","clapper","microphone","headphones","musical_score","musical_note","notes","musical_keyboard","violin","trumpet","saxophone","guitar","space_invader","video_game","black_joker","flower_playing_cards","mahjong","game_die","dart","football","basketball","soccer","baseball","tennis","8ball","rugby_football","bowling","golf","mountain_bicyclist","bicyclist","checkered_flag","horse_racing","trophy","ski","snowboarder","swimmer","surfer","fishing_pole_and_fish"]
- },
- {
- name: "foods",
- icons: ["coffee","tea","sake","baby_bottle","beer","beers","cocktail","tropical_drink","wine_glass","fork_and_knife","pizza","hamburger","fries","poultry_leg","meat_on_bone","spaghetti","curry","fried_shrimp","bento","sushi","fish_cake","rice_ball","rice_cracker","rice","ramen","stew","oden","dango","egg","bread","doughnut","custard","icecream","ice_cream","shaved_ice","birthday","cake","cookie","chocolate_bar","candy","lollipop","honey_pot","apple","green_apple","tangerine","lemon","cherries","grapes","watermelon","strawberry","peach","melon","banana","pear","pineapple","sweet_potato","eggplant","tomato","corn"]
- },
- {
- name: "places",
- icons: ["house","house_with_garden","school","office","post_office","hospital","bank","convenience_store","love_hotel","hotel","wedding","church","department_store","european_post_office","city_sunset","city_dusk","japanese_castle","european_castle","tent","factory","tokyo_tower","japan","mount_fuji","sunrise_over_mountains","sunrise","night_with_stars","statue_of_liberty","bridge_at_night","carousel_horse","ferris_wheel","fountain","roller_coaster","ship","sailboat","speedboat","rowboat","anchor","rocket","airplane","seat","helicopter","steam_locomotive","tram","station","mountain_railway","train2","bullettrain_side","bullettrain_front","light_rail","metro","monorail","train","railway_car","trolleybus","bus","oncoming_bus","blue_car","oncoming_automobile","red_car","taxi","oncoming_taxi","articulated_lorry","truck","rotating_light","police_car","oncoming_police_car","fire_engine","ambulance","minibus","bike","aerial_tramway","suspension_railway","mountain_cableway","tractor","barber","busstop","ticket","vertical_traffic_light","traffic_light","warning","construction","beginner","fuelpump","izakaya_lantern","slot_machine","hotsprings","moyai","circus_tent","performing_arts","round_pushpin","triangular_flag_on_post","cn","us","in","jp","br","ru","de","ng","gb","fr","mx","kr","id","ph","eg","vn","tr","it","es","ca","pl","ar","co","ir","za","my","pk","au","th","ma","tw","nl","ua","sa","ke","ve","pe","ro","cl","uz","bd","kz","be","se","cz","sd","hu","pt","ch","at","tz"]
- },
- {
- name: "symbols",
- icons: ["hash","one","two","three","four","five","six","seven","eight","nine","zero","keycap_ten","1234","symbols","arrow_up","arrow_down","arrow_left","arrow_right","capital_abcd","abcd","abc","arrow_upper_right","arrow_upper_left","arrow_lower_right","arrow_lower_left","left_right_arrow","arrow_up_down","arrows_counterclockwise","arrow_backward","arrow_forward","arrow_up_small","arrow_down_small","leftwards_arrow_with_hook","arrow_right_hook","information_source","rewind","fast_forward","arrow_double_up","arrow_double_down","arrow_heading_down","arrow_heading_up","ok","twisted_rightwards_arrows","repeat","repeat_one","new","up","cool","free","ng","signal_strength","cinema","koko","u6307","u7a7a","u6e80","u5408","u7981","ideograph_advantage","u5272","u55b6","u6709","u7121","restroom","mens","womens","baby_symbol","wc","potable_water","put_litter_in_its_place","parking","wheelchair","no_smoking","u6708","u7533","sa","m","passport_control","baggage_claim","left_luggage","customs","accept","secret","congratulations","cl","sos","id","no_entry_sign","underage","no_mobile_phones","do_not_litter","non-potable_water","no_bicycles","no_pedestrians","children_crossing","no_entry","eight_spoked_asterisk","sparkle","negative_squared_cross_mark","white_check_mark","eight_pointed_black_star","heart_decoration","vs","vibration_mode","mobile_phone_off","a","b","ab","o2","diamond_shape_with_a_dot_inside","loop","recycle","aries","taurus","gemini","cancer","leo","virgo","libra","scorpius","sagittarius","capricorn","aquarius","pisces","ophiuchus","six_pointed_star","atm","chart","heavy_dollar_sign","currency_exchange","copyright","registered","tm","part_alternation_mark","wavy_dash","top","end","back","on","soon","x","o","exclamation","question","grey_exclamation","grey_question","bangbang","interrobang","arrows_clockwise","clock12","clock1230","clock1","clock130","clock2","clock230","clock3","clock330","clock4","clock430","clock5","clock530","clock6","clock7","clock8","clock9","clock10","clock11","clock630","clock730","clock830","clock930","clock1030","clock1130","heavy_multiplication_x","heavy_plus_sign","heavy_minus_sign","heavy_division_sign","spades","hearts","clubs","diamonds","white_flower","100","heavy_check_mark","ballot_box_with_check","radio_button","link","curly_loop","trident","black_square_button","white_square_button","black_medium_square","white_medium_square","black_medium_small_square","white_medium_small_square","black_small_square","white_small_square","small_red_triangle","white_large_square","black_large_square","black_circle","white_circle","red_circle","large_blue_circle","small_red_triangle_down","large_orange_diamond","large_blue_diamond","small_orange_diamond","small_blue_diamond"]
+ fullname: "Objects & Symbols",
+ tabicon: "eyeglasses",
+ icons: ["watch", "iphone", "calling", "computer", "alarm_clock", "hourglass_flowing_sand", "hourglass", "camera", "video_camera", "movie_camera", "tv", "radio", "pager", "telephone_receiver", "phone", "fax", "minidisc", "floppy_disk", "cd", "dvd", "vhs", "battery", "electric_plug", "bulb", "flashlight", "satellite", "credit_card", "money_with_wings", "moneybag", "gem", "closed_umbrella", "pouch", "purse", "handbag", "briefcase", "school_satchel", "lipstick", "eyeglasses", "womans_hat", "sandal", "high_heel", "boot", "mans_shoe", "athletic_shoe", "bikini", "dress", "kimono", "womans_clothes", "shirt", "necktie", "jeans", "door", "shower", "bathtub", "toilet", "barber", "syringe", "pill", "microscope", "telescope", "crystal_ball", "wrench", "hocho", "nut_and_bolt", "hammer", "bomb", "smoking", "gun", "bookmark", "newspaper", "key", "email", "envelope_with_arrow", "incoming_envelope", "e-mail", "inbox_tray", "outbox_tray", "package", "postal_horn", "postbox", "mailbox_closed", "mailbox", "mailbox_with_mail", "mailbox_with_no_mail", "page_facing_up", "page_with_curl", "bookmark_tabs", "chart_with_upwards_trend", "chart_with_downwards_trend", "bar_chart", "date", "calendar", "low_brightness", "high_brightness", "scroll", "clipboard", "book", "notebook", "notebook_with_decorative_cover", "ledger", "closed_book", "green_book", "blue_book", "orange_book", "books", "card_index", "link", "paperclip", "pushpin", "scissors", "triangular_ruler", "round_pushpin", "straight_ruler", "triangular_flag_on_post", "file_folder", "open_file_folder", "black_nib", "pencil2", "memo", "lock_with_ink_pen", "closed_lock_with_key", "lock", "unlock", "mega", "loudspeaker", "sound", "loud_sound", "speaker", "mute", "zzz", "bell", "no_bell", "thought_balloon", "speech_balloon", "children_crossing", "mag", "mag_right", "no_entry_sign", "no_entry", "name_badge", "no_pedestrians", "do_not_litter", "no_bicycles", "non-potable_water", "no_mobile_phones", "underage", "accept", "ideograph_advantage", "white_flower", "secret", "congratulations", "u5408", "u6e80", "u7981", "u6709", "u7121", "u7533", "u55b6", "u6708", "u5272", "u7a7a", "sa", "koko", "u6307", "chart", "sparkle", "eight_spoked_asterisk", "negative_squared_cross_mark", "white_check_mark", "eight_pointed_black_star", "vibration_mode", "mobile_phone_off", "vs", "a", "b", "ab", "cl", "o2", "sos", "id", "parking", "wc", "cool", "free", "new", "ng", "ok", "up", "atm", "aries", "taurus", "gemini", "cancer", "leo", "virgo", "libra", "scorpius", "sagittarius", "capricorn", "aquarius", "pisces", "restroom", "mens", "womens", "baby_symbol", "wheelchair", "potable_water", "no_smoking", "put_litter_in_its_place", "arrow_forward", "arrow_backward", "arrow_up_small", "arrow_down_small", "fast_forward", "rewind", "arrow_double_up", "arrow_double_down", "arrow_right", "arrow_left", "arrow_up", "arrow_down", "arrow_upper_right", "arrow_lower_right", "arrow_lower_left", "arrow_upper_left", "arrow_up_down", "left_right_arrow", "arrows_counterclockwise", "arrow_right_hook", "leftwards_arrow_with_hook", "arrow_heading_up", "arrow_heading_down", "twisted_rightwards_arrows", "repeat", "repeat_one", "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "keycap_ten", "1234", "hash", "abc", "abcd", "capital_abcd", "information_source", "signal_strength", "cinema", "symbols", "heavy_plus_sign", "heavy_minus_sign", "wavy_dash", "heavy_division_sign", "heavy_multiplication_x", "heavy_check_mark", "arrows_clockwise", "tm", "copyright", "registered", "currency_exchange", "heavy_dollar_sign", "curly_loop", "loop", "part_alternation_mark", "exclamation", "bangbang", "question", "grey_exclamation", "grey_question", "interrobang", "x", "o", "100", "end", "back", "on", "top", "soon", "cyclone", "m", "ophiuchus", "six_pointed_star", "beginner", "trident", "warning", "hotsprings", "recycle", "anger", "diamond_shape_with_a_dot_inside", "spades", "clubs", "hearts", "diamonds", "ballot_box_with_check", "white_circle", "black_circle", "radio_button", "red_circle", "large_blue_circle", "small_red_triangle", "small_red_triangle_down", "small_orange_diamond", "small_blue_diamond", "large_orange_diamond", "large_blue_diamond", "black_small_square", "white_small_square", "black_large_square", "white_large_square", "black_medium_square", "white_medium_square", "black_medium_small_square", "white_medium_small_square", "black_square_button", "white_square_button", "clock1", "clock2", "clock3", "clock4", "clock5", "clock6", "clock7", "clock8", "clock9", "clock10", "clock11", "clock12", "clock130", "clock230", "clock330", "clock430", "clock530", "clock630", "clock730", "clock830", "clock930", "clock1030", "clock1130", "clock1230"]
}
];
@@ -67,7 +88,7 @@ var initializeUngroupedIcons = function(){
try {
if (localStorage && !localStorage.emojiUsage) { localStorage.emojiUsage = "{}"; }
} catch(e){
-/* localStorage can be disabled, or cookies disabled, do not crash script here
+/* localStorage can be disabled, or cookies disabled, do not crash script here
* TODO introduce a global wrapper for dealing with local storage
* */
}
@@ -120,8 +141,16 @@ var toolbar = function(selected){
if (!recentlyUsedIcons) { initializeRecentlyUsedIcons(); }
return _.map(groups, function(g, i){
- var icon = g.name === "recent" ? "star2" : g.icons[0];
- var row = {src: Discourse.Emoji.urlFor(icon), groupId: i};
+ var icon = g.tabicon;
+ var title = g.fullname;
+ if (g.name === "recent") {
+ icon = "star2";
+ title = "Recent";
+ } else if (g.name === "ungrouped") {
+ icon = g.icons[0];
+ title = "Custom";
+ }
+ var row = {src: Discourse.Emoji.urlFor(icon), title: title, groupId: i};
if(i === selected){
row.selected = true;
}
diff --git a/app/assets/javascripts/discourse/lib/emoji/emoji.js.erb b/app/assets/javascripts/discourse/lib/emoji/emoji.js.erb
index 3358a665a7..ef7856a748 100644
--- a/app/assets/javascripts/discourse/lib/emoji/emoji.js.erb
+++ b/app/assets/javascripts/discourse/lib/emoji/emoji.js.erb
@@ -99,6 +99,7 @@ var translations = {
':|' : 'expressionless',
':-|' : 'expressionless',
':/' : 'confused',
+ '8-)' : 'sunglasses',
";P" : 'stuck_out_tongue_winking_eye',
";-P" : 'stuck_out_tongue_winking_eye',
":$" : 'blush',
diff --git a/app/assets/javascripts/discourse/lib/search-for-term.js.es6 b/app/assets/javascripts/discourse/lib/search-for-term.js.es6
index a63eacad71..e5348a6a13 100644
--- a/app/assets/javascripts/discourse/lib/search-for-term.js.es6
+++ b/app/assets/javascripts/discourse/lib/search-for-term.js.es6
@@ -1,5 +1,67 @@
import Topic from 'discourse/models/topic';
+export function translateResults(results, opts) {
+ if (!opts) opts = {};
+
+ // Topics might not be included
+ if (!results.topics) { results.topics = []; }
+ if (!results.users) { results.users = []; }
+ if (!results.posts) { results.posts = []; }
+ if (!results.categories) { results.categories = []; }
+
+ const topicMap = {};
+ results.topics = results.topics.map(function(topic){
+ topic = Topic.create(topic);
+ topicMap[topic.id] = topic;
+ return topic;
+ });
+
+ results.posts = results.posts.map(function(post){
+ post = Discourse.Post.create(post);
+ post.set('topic', topicMap[post.topic_id]);
+ return post;
+ });
+
+ results.users = results.users.map(function(user){
+ user = Discourse.User.create(user);
+ return user;
+ });
+
+ results.categories = results.categories.map(function(category){
+ return Discourse.Category.list().findProperty('id', category.id);
+ }).compact();
+
+ const r = results.grouped_search_result;
+ results.resultTypes = [];
+
+ // TODO: consider refactoring front end to take a better structure
+ [['topic','posts'],['user','users'],['category','categories']].forEach(function(pair){
+ const type = pair[0], name = pair[1];
+ if (results[name].length > 0) {
+ var result = {
+ results: results[name],
+ componentName: "search-result-" + ((opts.searchContext && opts.searchContext.type === 'topic' && type === 'topic') ? 'post' : type),
+ type,
+ more: r['more_' + name]
+ };
+
+ if (result.more && name === "posts" && opts.fullSearchUrl) {
+ result.more = false;
+ result.moreUrl = opts.fullSearchUrl;
+ }
+
+ results.resultTypes.push(result);
+ }
+ });
+
+ const noResults = !!(results.topics.length === 0 &&
+ results.posts.length === 0 &&
+ results.users.length === 0 &&
+ results.categories.length === 0);
+
+ return noResults ? null : Em.Object.create(results);
+}
+
function searchForTerm(term, opts) {
if (!opts) opts = {};
@@ -16,63 +78,7 @@ function searchForTerm(term, opts) {
}
return Discourse.ajax('/search/query', { data: data }).then(function(results){
- // Topics might not be included
- if (!results.topics) { results.topics = []; }
- if (!results.users) { results.users = []; }
- if (!results.posts) { results.posts = []; }
- if (!results.categories) { results.categories = []; }
-
- const topicMap = {};
- results.topics = results.topics.map(function(topic){
- topic = Topic.create(topic);
- topicMap[topic.id] = topic;
- return topic;
- });
-
- results.posts = results.posts.map(function(post){
- post = Discourse.Post.create(post);
- post.set('topic', topicMap[post.topic_id]);
- return post;
- });
-
- results.users = results.users.map(function(user){
- user = Discourse.User.create(user);
- return user;
- });
-
- results.categories = results.categories.map(function(category){
- return Discourse.Category.list().findProperty('id', category.id);
- }).compact();
-
- const r = results.grouped_search_result;
- results.resultTypes = [];
-
- // TODO: consider refactoring front end to take a better structure
- [['topic','posts'],['user','users'],['category','categories']].forEach(function(pair){
- const type = pair[0], name = pair[1];
- if (results[name].length > 0) {
- var result = {
- results: results[name],
- componentName: "search-result-" + ((opts.searchContext && opts.searchContext.type === 'topic' && type === 'topic') ? 'post' : type),
- type,
- more: r['more_' + name]
- };
-
- if (result.more && name === "posts" && opts.fullSearchUrl) {
- result.more = false;
- result.moreUrl = opts.fullSearchUrl;
- }
-
- results.resultTypes.push(result);
- }
- });
-
- const noResults = !!(results.topics.length === 0 &&
- results.posts.length === 0 &&
- results.users.length === 0 &&
- results.categories.length === 0);
-
- return noResults ? null : Em.Object.create(results);
+ return translateResults(results, opts);
});
}
diff --git a/app/assets/javascripts/discourse/models/composer.js.es6 b/app/assets/javascripts/discourse/models/composer.js.es6
index 6148efa04c..49838fdf72 100644
--- a/app/assets/javascripts/discourse/models/composer.js.es6
+++ b/app/assets/javascripts/discourse/models/composer.js.es6
@@ -114,6 +114,7 @@ const Composer = RestModel.extend({
// whether to disable the post button
cantSubmitPost: function() {
+
// can't submit while loading
if (this.get('loading')) return true;
@@ -199,12 +200,9 @@ const Composer = RestModel.extend({
}
}.property('privateMessage'),
- /**
- Number of missing characters in the reply until valid.
-
- @property missingReplyCharacters
- **/
missingReplyCharacters: function() {
+ const postType = this.get('post.post_type');
+ if (postType === this.site.get('post_types.small_action')) { return 0; }
return this.get('minimumPostLength') - this.get('replyLength');
}.property('minimumPostLength', 'replyLength'),
diff --git a/app/assets/javascripts/discourse/models/post-stream.js.es6 b/app/assets/javascripts/discourse/models/post-stream.js.es6
index 8052ee4048..5a45ddcc59 100644
--- a/app/assets/javascripts/discourse/models/post-stream.js.es6
+++ b/app/assets/javascripts/discourse/models/post-stream.js.es6
@@ -767,14 +767,12 @@ const PostStream = RestModel.extend({
// If the result was 404 the post is not found
if (status === 404) {
- topic.set('errorTitle', I18n.t('topic.not_found.title'));
topic.set('notFoundHtml', result.jqXHR.responseText);
return;
}
// If the result is 403 it means invalid access
if (status === 403) {
- topic.set('errorTitle', I18n.t('topic.invalid_access.title'));
topic.set('noRetry', true);
if (Discourse.User.current()) {
topic.set('message', I18n.t('topic.invalid_access.description'));
@@ -785,7 +783,6 @@ const PostStream = RestModel.extend({
}
// Otherwise supply a generic error message
- topic.set('errorTitle', I18n.t('topic.server_error.title'));
topic.set('message', I18n.t('topic.server_error.description'));
}
diff --git a/app/assets/javascripts/discourse/models/rest.js.es6 b/app/assets/javascripts/discourse/models/rest.js.es6
index 843e22eb1b..6396f8e548 100644
--- a/app/assets/javascripts/discourse/models/rest.js.es6
+++ b/app/assets/javascripts/discourse/models/rest.js.es6
@@ -18,7 +18,6 @@ const RestModel = Ember.Object.extend(Presence, {
const self = this;
self.set('isSaving', true);
return store.update(type, this.get('id'), props).then(function(res) {
-
const payload = self.__munge(res.payload || res.responseJson);
if (payload.success === "OK") {
diff --git a/app/assets/javascripts/discourse/models/topic.js.es6 b/app/assets/javascripts/discourse/models/topic.js.es6
index 2481f2574b..0f06dc4847 100644
--- a/app/assets/javascripts/discourse/models/topic.js.es6
+++ b/app/assets/javascripts/discourse/models/topic.js.es6
@@ -2,7 +2,6 @@ import RestModel from 'discourse/models/rest';
const Topic = RestModel.extend({
message: null,
- errorTitle: null,
errorLoading: false,
fancyTitle: function() {
@@ -154,13 +153,17 @@ const Topic = RestModel.extend({
this.saveStatus(property, !!this.get(property));
},
- saveStatus(property, value) {
+ saveStatus(property, value, until) {
if (property === 'closed' && value === true) {
this.set('details.auto_close_at', null);
}
return Discourse.ajax(this.get('url') + "/status", {
type: 'PUT',
- data: { status: property, enabled: !!value }
+ data: {
+ status: property,
+ enabled: !!value,
+ until: until
+ }
});
},
diff --git a/app/assets/javascripts/discourse/models/user-stream.js.es6 b/app/assets/javascripts/discourse/models/user-stream.js.es6
index ac3883ba38..1870d484d6 100644
--- a/app/assets/javascripts/discourse/models/user-stream.js.es6
+++ b/app/assets/javascripts/discourse/models/user-stream.js.es6
@@ -68,7 +68,7 @@ export default RestModel.extend({
if (result && result.user_actions) {
const copy = Em.A();
result.user_actions.forEach(function(action) {
- action.title = Discourse.Emoji.unescape(action.title);
+ action.title = Discourse.Emoji.unescape(Handlebars.Utils.escapeExpression(action.title));
copy.pushObject(Discourse.UserAction.create(action));
});
diff --git a/app/assets/javascripts/discourse/models/user.js.es6 b/app/assets/javascripts/discourse/models/user.js.es6
index aecb9cbc4c..8e9c8e5350 100644
--- a/app/assets/javascripts/discourse/models/user.js.es6
+++ b/app/assets/javascripts/discourse/models/user.js.es6
@@ -395,7 +395,7 @@ const User = RestModel.extend({
}.observes("watched_category_ids"),
canDeleteAccount: function() {
- return this.get('can_delete_account') && ((this.get('reply_count')||0) + (this.get('topic_count')||0)) <= 1;
+ return !Discourse.SiteSettings.enable_sso && this.get('can_delete_account') && ((this.get('reply_count')||0) + (this.get('topic_count')||0)) <= 1;
}.property('can_delete_account', 'reply_count', 'topic_count'),
"delete": function() {
diff --git a/app/assets/javascripts/discourse/routes/app-route-map.js.es6 b/app/assets/javascripts/discourse/routes/app-route-map.js.es6
index 98e1194df9..779822ee39 100644
--- a/app/assets/javascripts/discourse/routes/app-route-map.js.es6
+++ b/app/assets/javascripts/discourse/routes/app-route-map.js.es6
@@ -99,4 +99,6 @@ export default function() {
});
this.resource('queued-posts', { path: '/queued-posts' });
+
+ this.route('full-page-search', {path: '/search'});
}
diff --git a/app/assets/javascripts/discourse/routes/full-page-search.js.es6 b/app/assets/javascripts/discourse/routes/full-page-search.js.es6
new file mode 100644
index 0000000000..b092136ee5
--- /dev/null
+++ b/app/assets/javascripts/discourse/routes/full-page-search.js.es6
@@ -0,0 +1,18 @@
+import { translateResults } from 'discourse/lib/search-for-term';
+
+export default Discourse.Route.extend({
+ queryParams: {
+ q: {
+ }
+ },
+ model: function(params) {
+ return PreloadStore.getAndRemove("search", function() {
+ return Discourse.ajax('/search', {data: {q: params.q}});
+ }).then(function(results){
+ var model = translateResults(results) || {};
+ model.q = params.q;
+ return model;
+ });
+ }
+
+});
diff --git a/app/assets/javascripts/discourse/routes/topic.js.es6 b/app/assets/javascripts/discourse/routes/topic.js.es6
index 963afc35ce..63762c7b55 100644
--- a/app/assets/javascripts/discourse/routes/topic.js.es6
+++ b/app/assets/javascripts/discourse/routes/topic.js.es6
@@ -61,6 +61,7 @@ const TopicRoute = Discourse.Route.extend(ShowFooter, {
showFeatureTopic() {
showModal('featureTopic', { model: this.modelFor('topic'), title: 'topic.feature_topic.title' });
this.controllerFor('modal').set('modalClass', 'feature-topic-modal');
+ this.controllerFor('feature_topic').reset();
},
showInvite() {
diff --git a/app/assets/javascripts/discourse/routes/user-invited-index.js.es6 b/app/assets/javascripts/discourse/routes/user-invited-index.js.es6
index e11dea722e..df9e7fadcf 100644
--- a/app/assets/javascripts/discourse/routes/user-invited-index.js.es6
+++ b/app/assets/javascripts/discourse/routes/user-invited-index.js.es6
@@ -1,5 +1,5 @@
export default Discourse.Route.extend({
beforeModel: function() {
- this.replaceWith('userInvited.show', 'redeemed');
+ this.replaceWith('userInvited.show', 'pending');
}
});
diff --git a/app/assets/javascripts/discourse/routes/user-invited-show.js.es6 b/app/assets/javascripts/discourse/routes/user-invited-show.js.es6
index a5e391093f..6c42105dd6 100644
--- a/app/assets/javascripts/discourse/routes/user-invited-show.js.es6
+++ b/app/assets/javascripts/discourse/routes/user-invited-show.js.es6
@@ -8,6 +8,12 @@ export default Discourse.Route.extend(ShowFooter, {
return Discourse.Invite.findInvitedBy(this.modelFor('user'), params.filter);
},
+ afterModel: function(model) {
+ if (!model.can_see_invite_details) {
+ this.replaceWith('userInvited.show', 'redeemed');
+ }
+ },
+
setupController(controller, model) {
controller.setProperties({
model: model,
diff --git a/app/assets/javascripts/discourse/templates/application.hbs b/app/assets/javascripts/discourse/templates/application.hbs
index 6b9b133219..23822e0f40 100644
--- a/app/assets/javascripts/discourse/templates/application.hbs
+++ b/app/assets/javascripts/discourse/templates/application.hbs
@@ -1,6 +1,6 @@
{{render "header"}}
-
+
{{outlet}}
{{render "user-card"}}
diff --git a/app/assets/javascripts/discourse/templates/components/basic-topic-list.hbs b/app/assets/javascripts/discourse/templates/components/basic-topic-list.hbs
index 341d355d47..6d323d7aab 100644
--- a/app/assets/javascripts/discourse/templates/components/basic-topic-list.hbs
+++ b/app/assets/javascripts/discourse/templates/components/basic-topic-list.hbs
@@ -5,7 +5,6 @@
hideCategory=hideCategory
topics=topics
expandExcerpts=expandExcerpts
- searchTerm=searchTerm
}}
{{else}}
diff --git a/app/assets/javascripts/discourse/templates/components/bread-crumbs.hbs b/app/assets/javascripts/discourse/templates/components/bread-crumbs.hbs
index 902c468486..756b976c33 100644
--- a/app/assets/javascripts/discourse/templates/components/bread-crumbs.hbs
+++ b/app/assets/javascripts/discourse/templates/components/bread-crumbs.hbs
@@ -4,4 +4,6 @@
{{category-drop category=secondCategory parentCategory=firstCategory categories=childCategories subCategory="true" noSubcategories=noSubcategories}}
{{/if}}
+{{plugin-outlet "bread-crumbs-right" tagName="li"}}
+
diff --git a/app/assets/javascripts/discourse/templates/components/bulk-select-button.hbs b/app/assets/javascripts/discourse/templates/components/bulk-select-button.hbs
index 30d9bc49fe..4dad7efb0c 100644
--- a/app/assets/javascripts/discourse/templates/components/bulk-select-button.hbs
+++ b/app/assets/javascripts/discourse/templates/components/bulk-select-button.hbs
@@ -1,5 +1,5 @@
{{#if selected}}
- {{d-button action="showBulkActions" icon="wrench" class="no-text"}}
+ {{d-button action="showBulkActions" icon="wrench"}}
{{/if}}
diff --git a/app/assets/javascripts/discourse/templates/components/header-extra-info.hbs b/app/assets/javascripts/discourse/templates/components/header-extra-info.hbs
index 2f6b95c498..614e48e38b 100644
--- a/app/assets/javascripts/discourse/templates/components/header-extra-info.hbs
+++ b/app/assets/javascripts/discourse/templates/components/header-extra-info.hbs
@@ -9,10 +9,6 @@
{{#if topic.details.loaded}}
{{topic-status topic=topic}}
{{{topic.fancyTitle}}}
- {{else}}
- {{#if topic.errorLoading}}
-
{{topic.errorTitle}}
- {{/if}}
{{/if}}
{{#if topic.details.loaded}}
diff --git a/app/assets/javascripts/admin/templates/components/nav-item.hbs b/app/assets/javascripts/discourse/templates/components/nav-item.hbs
similarity index 100%
rename from app/assets/javascripts/admin/templates/components/nav-item.hbs
rename to app/assets/javascripts/discourse/templates/components/nav-item.hbs
diff --git a/app/assets/javascripts/discourse/templates/components/small-action.hbs b/app/assets/javascripts/discourse/templates/components/small-action.hbs
new file mode 100644
index 0000000000..b05c658088
--- /dev/null
+++ b/app/assets/javascripts/discourse/templates/components/small-action.hbs
@@ -0,0 +1,15 @@
+
{{fa-icon icon}}
+
+ {{#if post}}
+ {{#if post.can_delete}}
+
+ {{/if}}
+ {{#if post.can_edit}}
+
+ {{/if}}
+
+ {{avatar post imageSize="small"}}
+
+ {{/if}}
+ {{{description}}}
+
diff --git a/app/assets/javascripts/discourse/templates/components/topic-map.hbs b/app/assets/javascripts/discourse/templates/components/topic-map.hbs
index 8043f66028..b851f03bcd 100644
--- a/app/assets/javascripts/discourse/templates/components/topic-map.hbs
+++ b/app/assets/javascripts/discourse/templates/components/topic-map.hbs
@@ -69,7 +69,7 @@
{{#each infoLinks as |link|}}
|
- {{link.clicks}}
+ {{link.clicks}}
|
diff --git a/app/assets/javascripts/discourse/templates/components/user-fields/dropdown.hbs b/app/assets/javascripts/discourse/templates/components/user-fields/dropdown.hbs
new file mode 100644
index 0000000000..9c5d37ec0b
--- /dev/null
+++ b/app/assets/javascripts/discourse/templates/components/user-fields/dropdown.hbs
@@ -0,0 +1,6 @@
+
+
+ {{combo-box content=field.options value=value none=noneLabel}}
+ {{#if field.required}} *{{/if}}
+ {{{field.description}}}
+
diff --git a/app/assets/javascripts/discourse/templates/discovery/topics.hbs b/app/assets/javascripts/discourse/templates/discovery/topics.hbs
index 0b6f9735df..f3e452a4ab 100644
--- a/app/assets/javascripts/discourse/templates/discovery/topics.hbs
+++ b/app/assets/javascripts/discourse/templates/discovery/topics.hbs
@@ -46,8 +46,6 @@
selected=selected
expandGloballyPinned=expandGloballyPinned
expandAllPinned=expandAllPinned
- expandExcerpts=isSearch
- searchTerm=searchTerm
topics=model.topics}}
{{/if}}
diff --git a/app/assets/javascripts/discourse/templates/emoji-toolbar.raw.hbs b/app/assets/javascripts/discourse/templates/emoji-toolbar.raw.hbs
index 8a519786e8..e7ed62a92a 100644
--- a/app/assets/javascripts/discourse/templates/emoji-toolbar.raw.hbs
+++ b/app/assets/javascripts/discourse/templates/emoji-toolbar.raw.hbs
@@ -1,6 +1,6 @@
diff --git a/app/assets/javascripts/discourse/templates/full-page-search.hbs b/app/assets/javascripts/discourse/templates/full-page-search.hbs
new file mode 100644
index 0000000000..848215c73e
--- /dev/null
+++ b/app/assets/javascripts/discourse/templates/full-page-search.hbs
@@ -0,0 +1,45 @@
+
+ {{input type="text" value=searchTerm class="input-xxlarge search no-blur" action="search"}}
+
+
+
+{{#conditional-loading-spinner condition=loading}}
+
+{{#unless model.posts}}
+
+{{/unless}}
+
+{{#each model.posts as |result|}}
+
+
+
+
+ {{format-age result.created_at}}
+ {{#if result.blurb}}
+ -
+ {{/if}}
+
+ {{#if result.blurb}}
+ {{#highlight-text highlight=controller.q}}
+ {{{unbound result.blurb}}}
+ {{/highlight-text}}
+ {{/if}}
+
+
+{{/each}}
+
+{{#if model.posts}}
+
+{{/if}}
+
+{{/conditional-loading-spinner}}
+
diff --git a/app/assets/javascripts/discourse/templates/header.hbs b/app/assets/javascripts/discourse/templates/header.hbs
index 4d71d1eb22..3a8bd239ed 100644
--- a/app/assets/javascripts/discourse/templates/header.hbs
+++ b/app/assets/javascripts/discourse/templates/header.hbs
@@ -1,4 +1,4 @@
-
+
{{home-logo minimized=showExtraInfo}}
diff --git a/app/assets/javascripts/discourse/templates/modal/avatar_selector.hbs b/app/assets/javascripts/discourse/templates/modal/avatar_selector.hbs
index be857517d0..12f0309d6c 100644
--- a/app/assets/javascripts/discourse/templates/modal/avatar_selector.hbs
+++ b/app/assets/javascripts/discourse/templates/modal/avatar_selector.hbs
@@ -7,7 +7,7 @@
- {{d-button action="refreshGravatar" title="user.change_avatar.refresh_gravatar_title" disabled=gravatarRefreshDisabled class="no-text" icon="refresh"}}
+ {{d-button action="refreshGravatar" title="user.change_avatar.refresh_gravatar_title" disabled=gravatarRefreshDisabled icon="refresh"}}
{{#if allowImageUpload}}
diff --git a/app/assets/javascripts/discourse/templates/modal/feature-topic.hbs b/app/assets/javascripts/discourse/templates/modal/feature-topic.hbs
index 73e0176055..07b6430795 100644
--- a/app/assets/javascripts/discourse/templates/modal/feature-topic.hbs
+++ b/app/assets/javascripts/discourse/templates/modal/feature-topic.hbs
@@ -1,80 +1,94 @@
{{#if model.pinned_at}}
-
- {{d-button action="unpin" icon="thumb-tack" label="topic.feature.unpin" class="btn-primary"}}
-
- {{{unPinMessage}}}
{{#if model.pinned_globally}}
- {{i18n "topic.feature_topic.global_pin_note"}}
{{#conditional-loading-spinner size="small" condition=loading}}
{{{i18n "topic.feature_topic.already_pinned_globally" count=pinnedGloballyCount}}}
{{/conditional-loading-spinner}}
+ {{i18n "topic.feature_topic.global_pin_note"}}
{{else}}
- {{i18n "topic.feature_topic.pin_note"}}
{{#conditional-loading-spinner size="small" condition=loading}}
{{{alreadyPinnedMessage}}}
{{/conditional-loading-spinner}}
+ {{i18n "topic.feature_topic.pin_note"}}
{{/if}}
+ {{{unPinMessage}}}
+ {{d-button action="unpin" icon="thumb-tack" label="topic.feature.unpin" class="btn-primary"}}
{{else}}
-
- {{d-button action="pin" icon="thumb-tack" label="topic.feature.pin" class="btn-primary"}}
-
- {{{pinMessage}}}
- {{i18n "topic.feature_topic.pin_note"}}
{{#conditional-loading-spinner size="small" condition=loading}}
{{{alreadyPinnedMessage}}}
{{/conditional-loading-spinner}}
+
+ {{i18n "topic.feature_topic.pin_note"}}
+
+
+ {{{pinMessage}}}
+ {{fa-icon "clock-o"}}
+ {{input type="date" value=model.pinnedInCategoryUntil}}
+
+
+ {{d-button action="pin" icon="thumb-tack" label="topic.feature.pin" class="btn-primary" disabled=pinDisabled}}
+
-
- {{d-button action="pinGlobally" icon="thumb-tack" label="topic.feature.pin_globally" class="btn-primary"}}
-
- {{i18n "topic.feature_topic.pin_globally"}}
- {{i18n "topic.feature_topic.global_pin_note"}}
{{#conditional-loading-spinner size="small" condition=loading}}
{{{i18n "topic.feature_topic.already_pinned_globally" count=pinnedGloballyCount}}}
{{/conditional-loading-spinner}}
+
+ {{i18n "topic.feature_topic.global_pin_note"}}
+
+
+ {{i18n "topic.feature_topic.pin_globally"}}
+ {{fa-icon "clock-o"}}
+ {{input type="date" value=model.pinnedGloballyUntil}}
+
+
+ {{d-button action="pinGlobally" icon="thumb-tack" label="topic.feature.pin_globally" class="btn-primary" disabled=pinGloballyDisabled}}
+
{{/if}}
-
- {{#if model.isBanner}}
- {{d-button action="removeBanner" icon="thumb-tack" label="topic.feature.remove_banner" class="btn-primary"}}
- {{else}}
- {{d-button action="makeBanner" icon="thumb-tack" label="topic.feature.make_banner" class="btn-primary"}}
- {{/if}}
-
- {{#if model.isBanner}}
- {{i18n "topic.feature_topic.remove_banner"}}
- {{else}}
- {{i18n "topic.feature_topic.make_banner"}}
- {{/if}}
- {{i18n "topic.feature_topic.banner_note"}}
{{#conditional-loading-spinner size="small" condition=loading}}
{{{i18n "topic.feature_topic.already_banner" count=bannerCount}}}
{{/conditional-loading-spinner}}
+
+ {{i18n "topic.feature_topic.banner_note"}}
+
+
+ {{#if model.isBanner}}
+ {{i18n "topic.feature_topic.remove_banner"}}
+ {{else}}
+ {{i18n "topic.feature_topic.make_banner"}}
+ {{/if}}
+
+
+ {{#if model.isBanner}}
+ {{d-button action="removeBanner" icon="thumb-tack" label="topic.feature.remove_banner" class="btn-primary"}}
+ {{else}}
+ {{d-button action="makeBanner" icon="thumb-tack" label="topic.feature.make_banner" class="btn-primary"}}
+ {{/if}}
+
diff --git a/app/assets/javascripts/discourse/templates/modal/upload_selector.hbs b/app/assets/javascripts/discourse/templates/modal/upload_selector.hbs
index 394153f71b..4423fe5f72 100644
--- a/app/assets/javascripts/discourse/templates/modal/upload_selector.hbs
+++ b/app/assets/javascripts/discourse/templates/modal/upload_selector.hbs
@@ -38,7 +38,7 @@
|