Version bump

This commit is contained in:
Neil Lalonde 2017-10-30 11:21:04 -04:00
commit 8f77b478e4
453 changed files with 6378 additions and 4056 deletions

View File

@ -46,6 +46,7 @@
"expandSelectBox":true,
"collapseSelectBox":true,
"selectBoxSelectRow":true,
"selectBoxSelectNoneRow":true,
"selectBoxFillInFilter":true,
"asyncTestDiscourse":true,
"fixture":true,

View File

@ -172,6 +172,8 @@ gem 'memory_profiler', require: false, platform: :mri
gem 'cppjieba_rb', require: false
gem 'lograge', require: false
gem 'logstash-logger', require: false
gem 'logster'
gem 'sassc', require: false

View File

@ -150,7 +150,15 @@ GEM
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
ruby_dep (~> 1.2)
logster (1.2.7)
lograge (0.7.1)
actionpack (>= 4, < 5.2)
activesupport (>= 4, < 5.2)
railties (>= 4, < 5.2)
request_store (~> 1.0)
logstash-event (1.2.02)
logstash-logger (0.25.1)
logstash-event (~> 1.2)
logster (1.2.8)
loofah (2.1.1)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
@ -233,7 +241,7 @@ GEM
openid-redis-store (0.0.2)
redis
ruby-openid
parallel (1.11.2)
parallel (1.12.0)
parser (2.4.0.0)
ast (~> 2.2)
pg (0.20.0)
@ -290,6 +298,7 @@ GEM
redis (3.3.5)
redis-namespace (1.5.3)
redis (~> 3.0, >= 3.0.4)
request_store (1.3.2)
rinku (2.0.2)
rspec (3.6.0)
rspec-core (~> 3.6.0)
@ -316,11 +325,11 @@ GEM
rspec-support (~> 3.6.0)
rspec-support (3.6.0)
rtlit (0.0.5)
rubocop (0.49.1)
rubocop (0.51.0)
parallel (~> 1.10)
parser (>= 2.3.3.1, < 3.0)
powerpack (~> 0.1)
rainbow (>= 1.99.1, < 3.0)
rainbow (>= 2.2.2, < 3.0)
ruby-progressbar (~> 1.7)
unicode-display_width (~> 1.0, >= 1.0.1)
ruby-ll (2.1.2)
@ -328,7 +337,7 @@ GEM
ast
ruby-openid (2.7.0)
ruby-prof (0.16.2)
ruby-progressbar (1.8.1)
ruby-progressbar (1.9.0)
ruby-readability (0.7.0)
guess_html_encoding (>= 0.0.4)
nokogiri (>= 1.6.0)
@ -431,6 +440,8 @@ DEPENDENCIES
htmlentities
http_accept_language (~> 2.0.5)
listen
lograge
logstash-logger
logster
lru_redux
mail

View File

@ -1,13 +1,14 @@
import loadScript from 'discourse/lib/load-script';
import { observes } from 'ember-addons/ember-computed-decorators';
const LOAD_ASYNC = !Ember.Test;
const LOAD_ASYNC = !Ember.testing;
export default Ember.Component.extend({
mode: 'css',
classNames: ['ace-wrapper'],
_editor: null,
_skipContentChangeEvent: null,
disabled: false,
@observes('editorId')
editorIdChanged() {
@ -30,6 +31,24 @@ export default Ember.Component.extend({
}
},
@observes('disabled')
disabledStateChanged() {
this.changeDisabledState();
},
changeDisabledState() {
const editor = this._editor;
if (editor) {
const disabled = this.get('disabled');
editor.setOptions({
readOnly: disabled,
highlightActiveLine: !disabled,
highlightGutterLine: !disabled
});
editor.container.parentNode.setAttribute("data-disabled", disabled);
}
},
_destroyEditor: function() {
if (this._editor) {
this._editor.destroy();
@ -76,6 +95,7 @@ export default Ember.Component.extend({
this.$().data('editor', editor);
this._editor = editor;
this.changeDisabledState();
$(window).off('ace:resize').on('ace:resize', ()=>{
this.appEvents.trigger('ace:resize');

View File

@ -1,10 +0,0 @@
import computed from 'ember-addons/ember-computed-decorators';
export default Ember.Component.extend({
classNames: ['flag-counts'],
@computed('details.flag_type_id')
title(id) {
return I18n.t(`admin.flags.summary.action_type_${id}`, { count: 1 });
}
});

View File

@ -50,5 +50,3 @@ export default Ember.Component.extend({
});
}
});

View File

@ -0,0 +1,12 @@
function postActionTitle([id, nameKey]) {
let title = I18n.t(`admin.flags.short_names.${nameKey}`, { defaultValue: null });
// TODO: We can remove this once other translations have been updated
if (!title) {
return I18n.t(`admin.flags.summary.action_type_${id}`, { count: 1 });
}
return title;
}
export default Ember.Helper.helper(postActionTitle);

View File

@ -1,5 +1,6 @@
export default Discourse.Route.extend({
redirect() {
this.replaceWith('adminFlags.postsActive');
let segment = this.siteSettings.flags_default_topics ? 'topics' : 'postsActive';
this.replaceWith(`adminFlags.${segment}`);
}
});

View File

@ -26,9 +26,7 @@
{{combo-box name="badge_type_id"
value=buffered.badge_type_id
content=badgeTypes
optionValuePath="content.id"
optionLabelPath="content.name"
disabled=readOnly}}
isDisabled=readOnly}}
</div>
<div>
@ -36,8 +34,7 @@
{{combo-box name="badge_grouping_id"
value=buffered.badge_grouping_id
content=badgeGroupings
optionValuePath="content.id"
optionLabelPath="content.displayName"}}
nameProperty="name"}}
&nbsp;<button {{action "editGroupings"}} class='btn'>{{d-icon 'pencil'}}</button>
</div>
@ -63,7 +60,7 @@
{{#if siteSettings.enable_badge_sql}}
<div>
<label for="query">{{i18n 'admin.badges.query'}}</label>
{{textarea name="query" value=buffered.query disabled=readOnly}}
{{ace-editor content=buffered.query mode="sql" disabled=readOnly}}
</div>
{{#if hasQuery}}

View File

@ -1,6 +1,6 @@
{{#if editing}}
{{#admin-form-row label="admin.user_fields.type"}}
{{combo-box content=fieldTypes valueAttribute="id" value=buffered.field_type}}
{{combo-box content=fieldTypes value=buffered.field_type}}
{{/admin-form-row}}
{{#admin-form-row label="admin.user_fields.name"}}

View File

@ -9,7 +9,7 @@
{{input value=buffered.path_whitelist placeholder="/blog/.*" enter="save" class="path-whitelist"}}
</td>
<td>
{{category-select-box value=categoryId class="small"}}
{{category-chooser value=categoryId class="small"}}
</td>
<td>
{{d-button icon="check" action="save" class="btn-primary" disabled=cantSave}}

View File

@ -1,2 +0,0 @@
<span class='type-name'>{{title}}</span>
<span class='type-count'>x{{details.count}}</span>

View File

@ -73,7 +73,7 @@
{{#each flaggedPost.post_actions as |postAction|}}
{{#flag-user user=postAction.user date=postAction.created_at}}
<div class='flagger-flag-type'>
{{i18n (concat "admin.flags.summary.action_type_" postAction.post_action_type_id) count=1}}
{{post-action-title postAction.post_action_type_id postAction.name_key}}
</div>
{{/flag-user}}
{{/each}}

View File

@ -36,8 +36,7 @@
<h3>{{i18n "admin.customize.theme.color_scheme"}}</h3>
<p>{{i18n "admin.customize.theme.color_scheme_select"}}</p>
<p>{{select-box content=colorSchemes
textKey="name"
<p>{{combo-box content=colorSchemes
filterable=true
value=colorSchemeId
icon="paint-brush"}}
@ -123,11 +122,8 @@
</ul>
{{/unless}}
{{#if selectableChildThemes}}
<p>{{combo-box content=selectableChildThemes
nameProperty="name"
value=selectedChildThemeId
valueAttribute="id"}}
<p>
{{combo-box content=selectableChildThemes value=selectedChildThemeId}}
{{#d-button action="addChildTheme" icon="plus"}}{{i18n "admin.customize.theme.add"}}{{/d-button}}
</p>
{{/if}}

View File

@ -19,7 +19,10 @@
</td>
<td>
{{#each ft.flag_counts as |fc|}}
{{flag-counts details=fc}}
<div class='flag-counts'>
<span class='type-name'>{{post-action-title fc.post_action_type_id fc.name_key}}</span>
<span class='type-count'>x{{fc.count}}</span>
</div>
{{/each}}
</td>
<td class='flagged-topic-users'>

View File

@ -1,6 +1,12 @@
{{#admin-nav}}
{{nav-item route='adminFlags.postsActive' label='admin.flags.active_posts'}}
{{nav-item route='adminFlags.topics' label='admin.flags.topics'}}
{{#if siteSettings.flags_default_topics}}
{{nav-item route='adminFlags.topics' label='admin.flags.topics'}}
{{nav-item route='adminFlags.postsActive' label='admin.flags.active_posts'}}
{{else}}
{{nav-item route='adminFlags.postsActive' label='admin.flags.active_posts'}}
{{nav-item route='adminFlags.topics' label='admin.flags.topics'}}
{{/if}}
{{nav-item route='adminFlags.postsOld' label='admin.flags.old_posts' class='right'}}
{{/admin-nav}}

View File

@ -6,7 +6,7 @@
</div>
<div class='control'>
{{combo-box content=groups valueAttribute="id" value=groupId none="admin.groups.bulk_select"}}
{{combo-box filterable=true content=groups value=groupId none="admin.groups.bulk_select"}}
</div>
<div class='control'>

View File

@ -30,7 +30,7 @@
{{/if}}
</div>
{{else}}
{{i18n "admin.logs.staff_actions.filter"}} {{combo-box content=userHistoryActions nameProperty="name" value=filterActionId none="admin.logs.staff_actions.all"}}
{{i18n "admin.logs.staff_actions.filter"}} {{combo-box content=userHistoryActions value=filterActionId none="admin.logs.staff_actions.all"}}
{{/if}}
<div class="pull-right">

View File

@ -2,7 +2,6 @@
{{#d-modal-body title="admin.customize.colors.select_base.title"}}
{{i18n "admin.customize.colors.select_base.description"}}
{{combo-box content=model
nameProperty="name"
value=selectedBaseThemeId
valueAttribute="base_scheme_id"}}
{{/d-modal-body}}

View File

@ -7,7 +7,7 @@
{{future-date-input
class="suspend-until"
label="admin.user.suspend_duration"
includeForever=true
includeFarFuture=true
input=suspendUntil}}
</label>
</div>

View File

@ -4,10 +4,10 @@
{{i18n 'admin.dashboard.reports.start_date'}} {{date-picker-past value=startDate defaultDate=startDate}}
{{i18n 'admin.dashboard.reports.end_date'}} {{date-picker-past value=endDate defaultDate=endDate}}
{{#if showCategoryOptions}}
{{combo-box valueAttribute="value" content=categoryOptions value=categoryId}}
{{combo-box filterable=true valueAttribute="value" content=categoryOptions value=categoryId}}
{{/if}}
{{#if showGroupOptions}}
{{combo-box valueAttribute="value" content=groupOptions value=groupId}}
{{combo-box filterable=true valueAttribute="value" content=groupOptions value=groupId}}
{{/if}}
{{d-button action="refreshReport" class="btn-primary" label="admin.dashboard.reports.refresh_report" icon="refresh"}}
{{d-button action="exportCsv" label="admin.export_csv.button_text" icon="download"}}

View File

@ -16,7 +16,7 @@
<form class="form-horizontal">
<div>
<label>{{i18n 'admin.badges.badge'}}</label>
{{combo-box valueAttribute="id" value=selectedBadgeId content=grantableBadges nameProperty="name"}}
{{combo-box filterable=true value=selectedBadgeId content=grantableBadges}}
</div>
<label>
<label>{{i18n 'admin.badges.reason'}}</label>

View File

@ -1,5 +1,4 @@
<section class="details {{unless model.active 'not-activated'}}">
<div class='user-controls'>
{{#if model.canViewProfile}}
{{#link-to 'user' model class="btn"}}
@ -379,7 +378,7 @@
<div class='controls'>
{{#if model.customGroups}}
{{i18n 'admin.groups.primary'}}
{{combo-box content=model.customGroups value=model.primary_group_id nameProperty="name" none="admin.groups.no_primary"}}
{{combo-box content=model.customGroups value=model.primary_group_id none="admin.groups.no_primary"}}
{{/if}}
{{#if primaryGroupDirty}}
{{d-button icon="check" class="ok" action="savePrimaryGroup"}}

View File

@ -6,15 +6,13 @@
{{/if}}
<div class="admin-title">
<div class="pull-left">
<h2>{{title}}</h2>
</div>
<h2>{{title}}</h2>
</div>
<div class='username controls'>
{{text-field value=listFilter placeholder=searchHint}}
{{#unless showEmails}}
<div class="pull-right">
<button {{action "showEmails"}} class="btn">{{i18n 'admin.users.show_emails'}}</button>
<button {{action "showEmails"}} class="show-emails btn">{{i18n 'admin.users.show_emails'}}</button>
</div>
{{/unless}}
</div>
@ -28,7 +26,7 @@
{{/if}}
<th>&nbsp;</th>
<th>{{i18n 'username'}}</th>
<th>{{i18n 'email'}}</th>
<th class='email-heading'>{{i18n 'email'}}</th>
<th>{{i18n 'admin.users.last_emailed'}}</th>
{{admin-directory-toggle field="seen" i18nKey='last_seen' order=order ascending=ascending}}
{{admin-directory-toggle field="topics_viewed" i18nKey="admin.user.topics_entered" order=order ascending=ascending}}

View File

@ -16,8 +16,6 @@
<label for='content-type'>{{i18n 'admin.web_hooks.content_type'}}</label>
{{combo-box content=contentTypes
name="content-type"
nameProperty="name"
valueAttribute="id"
value=model.content_type}}
</div>

View File

@ -4,6 +4,7 @@
//= require ./ember-addons/ember-computed-decorators
//= require ./ember-addons/fmt
//= require_tree ./discourse-common
//= require_tree ./select-box-kit
//= require ./discourse
//= require ./deprecated

View File

@ -1,146 +0,0 @@
import { bufferedRender } from 'discourse-common/lib/buffered-render';
import { on, observes } from 'ember-addons/ember-computed-decorators';
import { iconHTML } from 'discourse-common/lib/icon-library';
export default Ember.Component.extend(bufferedRender({
tagName: 'select',
attributeBindings: ['tabindex', 'disabled'],
classNames: ['combobox'],
valueAttribute: 'id',
nameProperty: 'name',
buildBuffer(buffer) {
const nameProperty = this.get('nameProperty');
const none = this.get('none');
let noneValue = null;
// Add none option if required
if (typeof none === "string") {
buffer.push('<option value="">' + I18n.t(none) + "</option>");
} else if (typeof none === "object") {
noneValue = Em.get(none, this.get('valueAttribute'));
buffer.push(`<option value="${noneValue}">${Em.get(none, nameProperty)}</option>`);
}
let selected = this.get('value');
if (!Em.isNone(selected)) { selected = selected.toString(); }
let selectedFound = false;
let firstVal = undefined;
const content = this.get('content');
if (content) {
let first = true;
content.forEach(o => {
let val = o[this.get('valueAttribute')];
if (typeof val === "undefined") { val = o; }
if (!Em.isNone(val)) { val = val.toString(); }
const selectedText = (val === selected) ? "selected" : "";
const name = Handlebars.Utils.escapeExpression(Ember.get(o, nameProperty) || o);
if (val === selected) {
selectedFound = true;
}
if (first) {
firstVal = val;
first = false;
}
buffer.push(`<option ${selectedText} value="${val}">${name}</option>`);
});
}
if (!selectedFound && !noneValue) {
if (none) {
this.set('value', null);
} else {
this.set('value', firstVal);
}
}
Ember.run.scheduleOnce('afterRender', this, this._updateSelect2);
},
@observes('value')
valueChanged() {
const $combo = this.$(),
val = this.get('value');
if (val !== undefined && val !== null) {
$combo.select2('val', val.toString());
} else {
$combo.select2('val', null);
}
},
@observes('content.[]')
_rerenderOnChange() {
this.rerenderBuffer();
},
didInsertElement() {
this._super();
// Workaround for https://github.com/emberjs/ember.js/issues/9813
// Can be removed when fixed. Without it, the wrong option is selected
this.$('option').each((i, o) => o.selected = !!$(o).attr('selected'));
// observer for item names changing (optional)
if (this.get('nameChanges')) {
this.addObserver('content.@each.' + this.get('nameProperty'), this.rerenderBuffer);
}
const $elem = this.$();
const caps = this.capabilities;
const minimumResultsForSearch = this.get('minimumResultsForSearch') || ((caps && caps.isIOS) ? -1 : 5);
if (!this.get("selectionTemplate") && this.get("selectionIcon")) {
this.selectionTemplate = (item) => {
let name = Em.get(item, 'text');
name = Handlebars.escapeExpression(name);
return iconHTML(this.get('selectionIcon')) + name;
};
}
const options = {
minimumResultsForSearch,
width: this.get('width') || 'resolve',
allowClear: true
};
if (this.comboTemplate) {
options.formatResult = this.comboTemplate.bind(this);
}
if (this.selectionTemplate) {
options.formatSelection = this.selectionTemplate.bind(this);
}
$elem.select2(options);
const castInteger = this.get('castInteger');
$elem.on("change", e => {
let val = $(e.target).val();
if (val && val.length && castInteger) {
val = parseInt(val, 10);
}
Ember.run(() => this.set('value', val));
});
Ember.run.scheduleOnce('afterRender', this, this._triggerChange);
},
_updateSelect2() {
this.$().trigger('change.select2');
},
_triggerChange() {
this.$().trigger('change');
},
@on('willDestroyElement')
_destroyDropdown() {
this.$().select2('destroy');
}
}));

View File

@ -1,6 +1,8 @@
import showModal from 'discourse/lib/show-modal';
export default Ember.Component.extend({
classNames: ['bulk-select-container'],
actions: {
showBulkActions() {
const controller = showModal('topic-bulk-actions', {

View File

@ -1,49 +0,0 @@
import DropdownSelectBoxComponent from "discourse/components/dropdown-select-box";
import { iconHTML } from "discourse-common/lib/icon-library";
import computed from "ember-addons/ember-computed-decorators";
export default DropdownSelectBoxComponent.extend({
classNames: ["categories-admin-dropdown"],
icon: `${iconHTML('bars')}${iconHTML('caret-down')}`.htmlSafe(),
generatedHeadertext: null,
@computed
content() {
const items = [
{
id: "create",
text: I18n.t("category.create"),
description: I18n.t("category.create_long"),
icon: "plus"
}
];
const includeReorder = this.get("siteSettings.fixed_category_positions");
if (includeReorder) {
items.push({
id: "reorder",
text: I18n.t("categories.reorder.title"),
description: I18n.t("categories.reorder.title_long"),
icon: "random"
});
}
return items;
},
actionNames: {
create: "createCategory",
reorder: "reorderCategories"
},
actions: {
onSelectRow(content) {
this._super(content);
this.sendAction(`actionNames.${this.get("value")}`);
this.set("value", null);
}
}
});

View File

@ -1,88 +0,0 @@
import Combobox from 'discourse-common/components/combo-box';
import { categoryBadgeHTML } from 'discourse/helpers/category-link';
import computed from 'ember-addons/ember-computed-decorators';
import { observes, on } from 'ember-addons/ember-computed-decorators';
import PermissionType from 'discourse/models/permission-type';
import Category from 'discourse/models/category';
export default Combobox.extend({
classNames: ['combobox category-combobox'],
dataAttributes: ['id', 'description_text'],
overrideWidths: true,
castInteger: true,
@computed("scopedCategoryId", "categories")
content(scopedCategoryId, categories) {
// Always scope to the parent of a category, if present
if (scopedCategoryId) {
const scopedCat = Category.findById(scopedCategoryId);
scopedCategoryId = scopedCat.get('parent_category_id') || scopedCat.get('id');
}
const excludeCategoryId = this.get('excludeCategoryId');
return categories.filter(c => {
const categoryId = c.get('id');
if (scopedCategoryId && categoryId !== scopedCategoryId && c.get('parent_category_id') !== scopedCategoryId) { return false; }
if (c.get('isUncategorizedCategory') || excludeCategoryId === categoryId) { return false; }
return c.get('permission') === PermissionType.FULL;
});
},
@on("init")
@observes("site.sortedCategories")
_updateCategories() {
if (!this.get('categories')) {
const categories = Discourse.SiteSettings.fixed_category_positions_on_create ?
Category.list() :
Category.listByActivity();
this.set('categories', categories);
}
},
@computed("rootNone", "rootNoneLabel")
none(rootNone, rootNoneLabel) {
if (this.siteSettings.allow_uncategorized_topics || this.get('allowUncategorized')) {
if (rootNone) {
return rootNoneLabel || "category.none";
} else {
return Category.findUncategorized();
}
} else {
return 'category.choose';
}
},
comboTemplate(item) {
let category;
// If we have no id, but text with the uncategorized name, we can use that badge.
if (Ember.isEmpty(item.id)) {
const uncat = Category.findUncategorized();
if (uncat && uncat.get('name') === item.text) {
category = uncat;
}
} else {
category = Category.findById(parseInt(item.id,10));
}
if (!category) return item.text;
let result = categoryBadgeHTML(category, {link: false, allowUncategorized: true, hideParent: true});
const parentCategoryId = category.get('parent_category_id');
if (parentCategoryId) {
result = categoryBadgeHTML(Category.findById(parentCategoryId), {link: false}) + "&nbsp;" + result;
}
result += ` <span class='topic-count'>&times; ${category.get('topic_count')}</span>`;
const description = category.get('description');
// TODO wtf how can this be null?;
if (description && description !== 'null') {
result += `<div class="category-desc">${description.substr(0, 200)}${description.length > 200 ? '&hellip;' : ''}</div>`;
}
return result;
}
});

View File

@ -1,28 +0,0 @@
import NotificationOptionsComponent from "discourse/components/notifications-button";
import computed from "ember-addons/ember-computed-decorators";
import { iconHTML } from "discourse-common/lib/icon-library";
export default NotificationOptionsComponent.extend({
classNames: ["category-notifications-button"],
hidden: Ember.computed.or("category.deleted", "site.isMobileDevice"),
i18nPrefix: "category.notifications",
value: Em.computed.alias("category.notification_level"),
@computed("value")
icon() {
return `${this._super()}${iconHTML("caret-down")}`.htmlSafe();
},
generatedHeadertext: null,
actions: {
onSelectRow(content) {
this._super(content);
this.get("category").setNotification(this.get("value"));
}
}
});

View File

@ -1,162 +0,0 @@
import SelectBoxComponent from "discourse/components/select-box";
import { categoryBadgeHTML } from "discourse/helpers/category-link";
import { observes, on } from "ember-addons/ember-computed-decorators";
import PermissionType from "discourse/models/permission-type";
import Category from "discourse/models/category";
export default SelectBoxComponent.extend({
classNames: ["category-select-box"],
selectBoxRowComponent: "category-select-box/category-select-box-row",
textKey: "name",
filterable: true,
castInteger: true,
clearable: true,
allowUncategorized: null,
init() {
this._super();
if (!Ember.isNone(this.get("categories"))) {
this.set("content", this.get("categories"));
this._scopeCategories();
}
if (Ember.isNone(this.get("value"))) {
if (this.siteSettings.allow_uncategorized_topics && this.get("allowUncategorized") !== false) {
this.set("value", Category.findUncategorized().id);
}
}
},
filterFunction: function(content) {
const _matchFunction = (filter, text) => {
return text.toLowerCase().indexOf(filter) > -1;
};
return (selectBox) => {
const filter = selectBox.get("filter").toLowerCase();
return _.filter(content, (c) => {
const category = Category.findById(c[selectBox.get("idKey")]);
const text = c[selectBox.get("textKey")];
if (category && category.get("parentCategory")) {
const categoryName = category.get("parentCategory.name");
return _matchFunction(filter, text) || _matchFunction(filter, categoryName);
} else {
return _matchFunction(filter, text);
}
});
};
},
@on("init")
@observes("selectedContent")
_setHeaderText: function() {
let headerText;
if (Ember.isNone(this.get("selectedContent"))) {
if (this.siteSettings.allow_uncategorized_topics) {
headerText = Ember.get(Category.findUncategorized(), this.get("textKey"));
} else {
headerText = I18n.t("category.choose").htmlSafe();
}
} else {
headerText = this.get("selectedContent.text");
}
this.set("headerText", headerText);
},
templateForRow: function() {
return (rowComponent) => this.rowContentTemplate(rowComponent.get("content"));
}.property(),
@observes("scopedCategoryId", "categories")
_scopeCategories() {
let scopedCategoryId = this.get("scopedCategoryId");
const categories = this.get("categories");
// Always scope to the parent of a category, if present
if (scopedCategoryId) {
const scopedCat = Category.findById(scopedCategoryId);
scopedCategoryId = scopedCat.get("parent_category_id") || scopedCat.get("id");
}
const excludeCategoryId = this.get("excludeCategoryId");
const filteredCategories = categories.filter(c => {
const categoryId = c.get("id");
if (scopedCategoryId && categoryId !== scopedCategoryId && c.get("parent_category_id") !== scopedCategoryId) { return false; }
if (excludeCategoryId === categoryId) { return false; }
if (this.get("allowUncategorized") === false && c.get("isUncategorizedCategory")) { return false; }
if (this.get("allowUncategorized") !== true) {
if (!this.siteSettings.allow_uncategorized_topics && c.get("isUncategorizedCategory")) {
return false;
}
}
return c.get("permission") === PermissionType.FULL;
});
this.set("content", filteredCategories);
},
@on("didRender")
_bindComposerResizing() {
this.appEvents.on("composer:resized", this, this.applyDirection);
},
@on("willDestroyElement")
_unbindComposerResizing() {
this.appEvents.off("composer:resized");
},
@on("init")
@observes("site.sortedCategories")
_updateCategories() {
if (!this.get("categories")) {
const categories = Discourse.SiteSettings.fixed_category_positions_on_create ?
Category.list() :
Category.listByActivity();
this.set("categories", categories);
}
},
rowContentTemplate(item) {
let category;
// If we have no id, but text with the uncategorized name, we can use that badge.
if (Ember.isEmpty(item.id)) {
const uncat = Category.findUncategorized();
if (uncat && uncat.get("name") === item.text) {
category = uncat;
}
} else {
category = Category.findById(parseInt(item.id,10));
}
if (!category) return item.text;
let result = categoryBadgeHTML(category, {link: false, allowUncategorized: true, hideParent: true});
const parentCategoryId = category.get("parent_category_id");
if (parentCategoryId) {
result = `<div class="category-status">${categoryBadgeHTML(Category.findById(parentCategoryId), {link: false})}&nbsp;${result}`;
} else {
result = `<div class="category-status">${result}`;
}
result += ` <span class="topic-count">&times; ${category.get("topic_count")}</span></div>`;
const description = category.get("description");
// TODO wtf how can this be null?;
if (description && description !== "null") {
result += `<div class="category-desc">${description.substr(0, 200)}${description.length > 200 ? '&hellip;' : ''}</div>`;
}
return result;
}
});

View File

@ -1,13 +0,0 @@
import computed from 'ember-addons/ember-computed-decorators';
import SelectBoxRowComponent from "discourse/components/select-box/select-box-row";
import Category from "discourse/models/category";
export default SelectBoxRowComponent.extend({
classNameBindings: ["isUncategorized"],
@computed("content")
isUncategorized(content) {
const category = Category.findById(content.id);
return category.get("isUncategorizedCategory");
}
});

View File

@ -101,24 +101,6 @@ class Toolbar {
perform: e => e.applyList(i => !i ? "1. " : `${parseInt(i) + 1}. `, 'list_item')
});
this.addButton({
id: 'heading',
group: 'extras',
icon: 'header',
label: getButtonLabel('composer.heading_label', 'H'),
shortcut: 'Alt+1',
perform: e => e.applyList('## ', 'heading_text')
});
this.addButton({
id: 'rule',
group: 'extras',
icon: 'minus',
shortcut: 'Alt+R',
title: 'composer.hr_title',
perform: e => e.addText("\n\n----------\n")
});
if (site.mobileView) {
this.groups.push({group: 'mobileExtras', buttons: []});
}
@ -233,7 +215,7 @@ export default Ember.Component.extend({
const shortcuts = this.get('toolbar.shortcuts');
// for some reason I am having trouble bubbling this so hack it in
mouseTrap.bind(['ctrl+shift+s','command+shift+s'], (event) =>{
mouseTrap.bind(['ctrl+alt+f'], (event) =>{
this.appEvents.trigger('header:keyboard-trigger', {type: 'search', event});
return true;
});

View File

@ -1,33 +0,0 @@
import computed from "ember-addons/ember-computed-decorators";
import SelectBoxComponent from "discourse/components/select-box";
export default SelectBoxComponent.extend({
classNames: ["dropdown-select-box"],
wrapper: false,
verticalOffset: 3,
collectionHeight: "auto",
fullWidthOnMobile: true,
selectBoxHeaderComponent: "dropdown-select-box/dropdown-header",
@computed
templateForRow: function() {
return (rowComponent) => {
let template = "";
const content = rowComponent.get("content");
const icon = rowComponent.icon();
if (icon) {
template += `<div class="icons">${icon}</div>`;
}
template += `
<div class="texts">
<span class="title">${Handlebars.escapeExpression(Ember.get(content, this.get("textKey")))}</span>
<span class="desc">${Handlebars.escapeExpression(content.description)}</span>
</div>
`;
return template;
};
}
});

View File

@ -1,7 +0,0 @@
import SelectBoxHeaderComponent from "discourse/components/select-box/select-box-header";
export default SelectBoxHeaderComponent.extend({
layoutName: "components/dropdown-select-box/dropdown-header",
classNames: ["dropdown-header"]
});

View File

@ -20,6 +20,8 @@ export default buildCategoryPanel('security', {
permission: PermissionType.create({ id: parseInt(id) })
});
}
this.set('selectedGroup', this.get('category.availableGroups.firstObject'));
},
removePermission(permission) {

View File

@ -2,6 +2,7 @@ import { MAX_MESSAGE_LENGTH } from 'discourse/models/post-action-type';
import computed from 'ember-addons/ember-computed-decorators';
export default Ember.Component.extend({
classNames: ['flag-action-type'],
@computed('flag.name_key')
customPlaceholder(nameKey) {

View File

@ -1,207 +0,0 @@
import { iconHTML } from 'discourse-common/lib/icon-library';
import { default as computed, observes } from "ember-addons/ember-computed-decorators";
import Combobox from 'discourse-common/components/combo-box';
import { CLOSE_STATUS_TYPE } from 'discourse/controllers/edit-topic-timer';
const LATER_TODAY = 'later_today';
const TOMORROW = 'tomorrow';
const LATER_THIS_WEEK = 'later_this_week';
const THIS_WEEKEND = 'this_weekend';
const NEXT_WEEK = 'next_week';
const TWO_WEEKS = 'two_weeks';
const NEXT_MONTH = 'next_month';
const FOREVER = 'forever';
export const PICK_DATE_AND_TIME = 'pick_date_and_time';
export const SET_BASED_ON_LAST_POST = 'set_based_on_last_post';
export const FORMAT = 'YYYY-MM-DD HH:mm';
export default Combobox.extend({
classNames: ['future-date-input-selector'],
isCustom: Ember.computed.equal("value", PICK_DATE_AND_TIME),
@computed()
content() {
const selections = [];
const now = moment();
const canScheduleToday = (24 - now.hour()) > 6;
const day = now.day();
if (canScheduleToday) {
selections.push({
id: LATER_TODAY,
name: I18n.t('topic.auto_update_input.later_today')
});
}
selections.push({
id: TOMORROW,
name: I18n.t('topic.auto_update_input.tomorrow')
});
if (!canScheduleToday && day < 4) {
selections.push({
id: LATER_THIS_WEEK,
name: I18n.t('topic.auto_update_input.later_this_week')
});
}
if (day < 5 && this.get('includeWeekend')) {
selections.push({
id: THIS_WEEKEND,
name: I18n.t('topic.auto_update_input.this_weekend')
});
}
if (day !== 7) {
selections.push({
id: NEXT_WEEK,
name: I18n.t('topic.auto_update_input.next_week')
});
}
selections.push({
id: TWO_WEEKS,
name: I18n.t('topic.auto_update_input.two_weeks')
});
if (moment().endOf('month').date() !== now.date()) {
selections.push({
id: NEXT_MONTH,
name: I18n.t('topic.auto_update_input.next_month')
});
}
if (this.get('includeForever')) {
selections.push({
id: FOREVER,
name: I18n.t('topic.auto_update_input.forever')
});
}
selections.push({
id: PICK_DATE_AND_TIME,
name: I18n.t('topic.auto_update_input.pick_date_and_time')
});
if (this.get('statusType') === CLOSE_STATUS_TYPE) {
selections.push({
id: SET_BASED_ON_LAST_POST,
name: I18n.t('topic.auto_update_input.set_based_on_last_post')
});
}
return selections;
},
@observes('value')
_updateInput() {
if (this.get('isCustom')) return;
let input = null;
const { time } = this.get('updateAt');
if (time && !Ember.isEmpty(this.get('value'))) {
input = time.format(FORMAT);
}
this.set('input', input);
},
@computed('value')
updateAt(value) {
return this._updateAt(value);
},
comboTemplate(state) {
return this._format(state);
},
selectionTemplate(state) {
return this._format(state);
},
_format(state) {
let { time, icon } = this._updateAt(state.id);
let icons;
if (icon) {
icons = icon.split(',').map(i => iconHTML(i)).join(" ");
}
if (time) {
if (state.id === LATER_TODAY) {
time = time.format('h a');
} else if (state.id === NEXT_MONTH || state.id === TWO_WEEKS) {
time = time.format('MMM D');
} else {
time = time.format('ddd, h a');
}
}
let output = "";
if (!Ember.isEmpty(icons)) {
output += `<span class='future-date-input-selector-icons'>${icons}</span>`;
}
output += `<span>${state.text}</span>`;
if (time && state.id !== FOREVER) {
output += `<span class='future-date-input-selector-datetime'>${time}</span>`;
}
return output;
},
_updateAt(selection) {
let time = moment();
let icon;
const timeOfDay = this.get('statusType') !== CLOSE_STATUS_TYPE ? 8 : 18;
switch(selection) {
case LATER_TODAY:
time = time.hour(18).minute(0);
icon = 'moon-o';
break;
case TOMORROW:
time = time.add(1, 'day').hour(timeOfDay).minute(0);
icon = 'sun-o';
break;
case LATER_THIS_WEEK:
time = time.add(2, 'day').hour(timeOfDay).minute(0);
icon = 'briefcase';
break;
case THIS_WEEKEND:
time = time.day(6).hour(timeOfDay).minute(0);
icon = 'bed';
break;
case NEXT_WEEK:
time = time.add(1, 'week').day(1).hour(timeOfDay).minute(0);
icon = 'briefcase';
break;
case TWO_WEEKS:
time = time.add(2, 'week').hour(timeOfDay).minute(0);
icon = 'briefcase';
break;
case NEXT_MONTH:
time = time.add(1, 'month').startOf('month').hour(timeOfDay).minute(0);
icon = 'briefcase';
break;
case FOREVER:
time = time.add(1000, 'year').hour(timeOfDay).minute(0);
icon = 'gavel';
break;
case PICK_DATE_AND_TIME:
time = null;
icon = 'calendar-plus-o';
break;
case SET_BASED_ON_LAST_POST:
time = null;
icon = 'clock-o';
break;
}
return { time, icon };
},
});

View File

@ -1,9 +1,7 @@
import { default as computed, observes } from "ember-addons/ember-computed-decorators";
import {
FORMAT,
PICK_DATE_AND_TIME,
SET_BASED_ON_LAST_POST
} from "discourse/components/future-date-input-selector";
} from "select-box-kit/components/future-date-input-selector";
import { PUBLISH_TO_CATEGORY_STATUS_TYPE } from 'discourse/controllers/edit-topic-timer';
@ -11,8 +9,8 @@ export default Ember.Component.extend({
selection: null,
date: null,
time: null,
isCustom: Ember.computed.equal('selection', PICK_DATE_AND_TIME),
isBasedOnLastPost: Ember.computed.equal('selection', SET_BASED_ON_LAST_POST),
isCustom: Ember.computed.equal('selection', 'pick_date_and_time'),
isBasedOnLastPost: Ember.computed.equal('selection', 'set_based_on_last_post'),
displayLabel: null,
init() {
@ -22,9 +20,9 @@ export default Ember.Component.extend({
if (input) {
if (this.get('basedOnLastPost')) {
this.set('selection', SET_BASED_ON_LAST_POST);
this.set('selection', 'set_based_on_last_post');
} else {
this.set('selection', PICK_DATE_AND_TIME);
this.set('selection', 'pick_date_and_time');
const datetime = moment(input);
this.set('date', datetime.toDate());
this.set('time', datetime.format("HH:mm"));

View File

@ -1,17 +0,0 @@
import NotificationOptionsComponent from "discourse/components/notifications-button";
export default NotificationOptionsComponent.extend({
classNames: ["group-notifications-button"],
value: Em.computed.alias("group.group_user.notification_level"),
i18nPrefix: "groups.notifications",
actions: {
onSelectRow(content) {
this._super(content);
this.get("group").setNotification(this.get("value"), this.get("user.id"));
}
}
});

View File

@ -1,68 +0,0 @@
import DropdownSelectBoxComponent from "discourse/components/dropdown-select-box";
import { iconHTML } from "discourse-common/lib/icon-library";
import computed from "ember-addons/ember-computed-decorators";
import { buttonDetails } from "discourse/lib/notification-levels";
import { allLevels } from "discourse/lib/notification-levels";
export default DropdownSelectBoxComponent.extend({
classNames: ["notifications-button"],
i18nPrefix: "",
i18nPostfix: "",
textKey: "key",
showFullTitle: true,
fullWidthOnMobile: true,
content: allLevels,
value: Em.computed.alias("notificationLevel"),
@computed("selectedDetails")
icon(details) {
return iconHTML(details.icon, {class: details.key}).htmlSafe();
},
@computed("selectedDetails.key", "i18nPrefix")
selectedTitle(key, prefix) {
return I18n.t(`${prefix}.${key}.title`);
},
@computed("value")
selectedDetails(value) {
return buttonDetails(value);
},
@computed("selectedTitle", "showFullTitle")
generatedHeadertext(selectedTitle, showFullTitle) {
return showFullTitle ? selectedTitle : null;
},
@computed
titleForRow: function() {
return (rowComponent) => {
const notificationLevel = rowComponent.get(`content.${this.get("idKey")}`);
const details = buttonDetails(notificationLevel);
return I18n.t(`${this.get("i18nPrefix")}.${details.key}.title`);
};
},
@computed
templateForRow: function() {
return (rowComponent) => {
const content = rowComponent.get("content");
const start = `${this.get("i18nPrefix")}.${content.key}${this.get("i18nPostfix")}`;
const title = I18n.t(`${start}.title`);
const description = I18n.t(`${start}.description`);
return `
<div class="icons">
<span class="selection-indicator"></span>
${iconHTML(content.icon, { class: content.key.dasherize() })}
</div>
<div class="texts">
<span class="title">${Handlebars.escapeExpression(title)}</span>
<span class="desc">${Handlebars.escapeExpression(description)}</span>
</div>
`;
};
}
});

View File

@ -1,73 +0,0 @@
import DropdownSelectBoxComponent from "discourse/components/dropdown-select-box";
import computed from "ember-addons/ember-computed-decorators";
import { observes } from "ember-addons/ember-computed-decorators";
import { iconHTML } from "discourse-common/lib/icon-library";
export default DropdownSelectBoxComponent.extend({
classNames: ["pinned-options"],
@computed("topic.pinned")
value(pinned) {
return pinned ? "pinned" : "unpinned";
},
@observes("topic.pinned")
_pinnedChanged() {
this.set("value", this.get("topic.pinned") ? "pinned" : "unpinned");
},
@computed("topic.pinned_globally")
content(pinnedGlobally) {
const globally = pinnedGlobally ? "_globally" : "";
return [
{
id: "pinned",
text: I18n.t("topic_statuses.pinned" + globally + ".title"),
description: I18n.t('topic_statuses.pinned' + globally + '.help'),
icon: "thumb-tack"
},
{
id: "unpinned",
text: I18n.t("topic_statuses.unpinned.title"),
icon: "thumb-tack",
description: I18n.t('topic_statuses.unpinned.help'),
iconClass: "unpinned"
}
];
},
@computed("topic.pinned", "topic.pinned_globally")
icon(pinned, pinnedGlobally) {
const globally = pinnedGlobally ? "_globally" : "";
const state = pinned ? `pinned${globally}` : "unpinned";
return iconHTML(
"thumb-tack",
{ class: (state === "unpinned" ? "unpinned" : null) }
);
},
@computed("topic.pinned", "topic.pinned_globally")
generatedHeadertext(pinned, pinnedGlobally) {
const globally = pinnedGlobally ? "_globally" : "";
const state = pinned ? `pinned${globally}` : "unpinned";
const title = I18n.t(`topic_statuses.${state}.title`);
return `${title}${iconHTML("caret-down")}`.htmlSafe();
},
actions: {
onSelectRow(content) {
this._super(content);
const topic = this.get("topic");
if (this.get("value") === "unpinned") {
topic.clearPin();
} else {
topic.rePin();
}
}
}
});

View File

@ -1,485 +0,0 @@
import { on, observes } from "ember-addons/ember-computed-decorators";
import computed from "ember-addons/ember-computed-decorators";
export default Ember.Component.extend({
layoutName: "components/select-box",
classNames: "select-box",
classNameBindings: ["expanded:is-expanded", "hidden:is-hidden"],
expanded: false,
focused: false,
filterFocused: false,
renderBody: false,
wrapper: true,
hidden: false,
tabindex: 0,
scrollableParentSelector: ".modal-body",
caretUpIcon: "caret-up",
caretDownIcon: "caret-down",
headerText: I18n.t("select_box.default_header_text"),
dynamicHeaderText: true,
icon: null,
clearable: false,
value: null,
highlightedValue: null,
selectedContent: null,
noContentLabel: I18n.t("select_box.no_content"),
clearSelectionLabel: null,
idKey: "id",
textKey: "text",
iconKey: "icon",
filterable: false,
filter: "",
filterPlaceholder: I18n.t("select_box.filter_placeholder"),
filterIcon: "search",
selectBoxRowComponent: "select-box/select-box-row",
selectBoxFilterComponent: "select-box/select-box-filter",
selectBoxHeaderComponent: "select-box/select-box-header",
selectBoxCollectionComponent: "select-box/select-box-collection",
collectionHeight: 200,
verticalOffset: 0,
horizontalOffset: 0,
fullWidthOnMobile: false,
castInteger: false,
click(event) {
event.stopPropagation();
},
filterFunction: function(content) {
return (selectBox) => {
const filter = selectBox.get("filter").toLowerCase();
return _.filter(content, (c) => {
return c[selectBox.get("textKey")].toLowerCase().indexOf(filter) > -1;
});
};
},
@computed("textKey")
titleForRow(textKey) {
return (rowComponent) => {
return rowComponent.get(`content.${textKey}`);
};
},
@computed("idKey")
idForRow(idKey) {
return (rowComponent) => {
return rowComponent.get(`content.${idKey}`);
};
},
@computed
shouldHighlightRow: function() {
return (rowComponent) => {
const id = this._castInteger(rowComponent.get(`content.${this.get("idKey")}`));
return id === this.get("highlightedValue");
};
},
@computed("value", "idKey")
shouldSelectRow(value, idKey) {
return (rowComponent) => {
const id = this._castInteger(rowComponent.get(`content.${idKey}`));
return id === value;
};
},
@computed
templateForRow: function() {
return (rowComponent) => {
let template = "";
const icon = rowComponent.icon();
if (icon) {
template += icon;
}
const text = rowComponent.get(`content.${this.get("textKey")}`);
template += `<p class="text">${Handlebars.escapeExpression(text)}</p>`;
return template;
};
},
applyDirection() {
this.$().removeClass("is-above is-below is-left-aligned is-right-aligned");
let options = { left: "auto", bottom: "auto", left: "auto", top: "auto" };
const headerHeight = this.$(".select-box-header").outerHeight(false);
const filterHeight = this.$(".select-box-filter").outerHeight(false);
const bodyHeight = this.$(".select-box-body").outerHeight(false);
const windowWidth = $(window).width();
const windowHeight = $(window).height();
const boundingRect = this.$()[0].getBoundingClientRect();
const offsetTop = boundingRect.top;
if (this.get("fullWidthOnMobile") && this.site.isMobileDevice) {
const margin = 10;
const relativeLeft = this.$().offset().left - $(window).scrollLeft();
options.left = margin - relativeLeft;
options.width = windowWidth - margin * 2;
options.maxWidth = options.minWidth = "unset";
} else {
const offsetLeft = boundingRect.left;
const bodyWidth = this.$(".select-box-body").outerWidth(false);
const hasRightSpace = (windowWidth - (this.get("horizontalOffset") + offsetLeft + filterHeight + bodyWidth) > 0);
if (hasRightSpace) {
this.$().addClass("is-left-aligned");
options.left = this.get("horizontalOffset");
} else {
this.$().addClass("is-right-aligned");
options.right = this.get("horizontalOffset");
}
}
const componentHeight = this.get("verticalOffset") + bodyHeight + headerHeight;
const hasBelowSpace = windowHeight - offsetTop - componentHeight > 0;
if (hasBelowSpace) {
this.$().addClass("is-below");
options.top = headerHeight + this.get("verticalOffset");
} else {
this.$().addClass("is-above");
options.bottom = headerHeight + this.get("verticalOffset");
}
this.$(".select-box-body").css(options);
},
init() {
this._super();
const content = this.getWithDefault("content", []);
this.set("content", content);
if (this.site.isMobileDevice) {
this.set("filterable", false);
}
this.setProperties({
value: this._castInteger(this.get("value")),
componentId: this.elementId
});
},
@on("willDestroyElement")
_removeDocumentListeners: function() {
$(document).off("click.select-box");
$(window).off("resize.select-box");
},
@on("willDestroyElement")
_unbindEvents: function() {
this.$(".select-box-offscreen").off(
"focusin.select-box",
"focusout.select-box",
"keydown.select-box"
);
this.$(".filter-query").off("focusin.select-box", "focusout.select-box");
},
@on("didRender")
_configureSelectBoxDOM: function() {
if (this.get("scrollableParent").length === 1) {
this._removeFixedPosition();
}
const computedWidth = this.$().outerWidth(false);
const computedHeight = this.$().outerHeight(false);
this.$(".select-box-filter").css("height", computedHeight);
if (this.get("expanded")) {
if (this.get("scrollableParent").length === 1) {
this._applyFixedPosition(computedWidth, computedHeight);
}
this.$(".select-box-collection").css("max-height", this.get("collectionHeight"));
Ember.run.schedule("afterRender", () => {
this.applyDirection();
if (this.get("wrapper")) {
this._positionSelectBoxWrapper();
}
});
} else {
if (this.get("wrapper")) {
this.$(".select-box-wrapper").hide();
}
}
},
keyDown(event) {
const keyCode = event.keyCode || event.which;
if (this.get("expanded")) {
if ((keyCode === 13 || keyCode === 9) && Ember.isPresent(this.get("highlightedValue"))) {
event.preventDefault();
this.send("onSelectRow", this.get("highlightedContent"));
}
if (keyCode === 9) {
this.set("expanded", false);
}
if (keyCode === 27) {
this.set("expanded", false);
event.stopPropagation();
}
if (keyCode === 38) {
event.preventDefault();
const self = this;
Ember.run.throttle(self, this._handleUpArrow, 50);
}
if (keyCode === 40) {
event.preventDefault();
const self = this;
Ember.run.throttle(self, this._handleDownArrow, 50);
}
}
},
@on("didRender")
_setupDocumentListeners: function() {
$(document).off("click.select-box");
$(document)
.on("click.select-box", (event) => {
if (this.isDestroying || this.isDestroyed) { return; }
const $element = this.$();
const $target = $(event.target);
if (!$target.closest($element).length) {
this.set("expanded", false);
}
});
$(window).on("resize.select-box", () => this.set("expanded", false) );
},
@on("didInsertElement")
_bindEvents: function() {
this.$(".select-box-offscreen")
.on("focusin.select-box", () => this.set("focused", true) )
.on("focusout.select-box", () => this.set("focused", false) );
this.$(".filter-query")
.on("focusin.select-box", () => this.set("filterFocused", true) )
.on("focusout.select-box", () => this.set("filterFocused", false) );
this.$(".select-box-offscreen").on("keydown.select-box", (event) => {
const keyCode = event.keyCode || event.which;
if (keyCode === 13 || keyCode === 40) {
this.setProperties({ expanded: true, focused: false });
event.stopPropagation();
}
if (keyCode >= 65 && keyCode <= 90) {
this.setProperties({ expanded: true, focused: false });
Ember.run.schedule("afterRender", () => {
this.$(".filter-query").focus().val(String.fromCharCode(keyCode));
});
}
});
},
@observes("expanded")
_expandedChanged: function() {
if (this.get("expanded")) {
this.setProperties({ highlightedValue: null, renderBody: true, focused: false });
if (this.get("filterable")) {
Ember.run.schedule("afterRender", () => this.$(".filter-query").focus());
}
};
},
@computed("value", "content.[]", "idKey")
selectedContent(value, content, idKey) {
if (Ember.isNone(value)) {
return null;
}
return content.find((c) => {
return this._castInteger(Ember.get(c, idKey)) === value;
});
},
@computed("highlightedValue", "content.[]", "idKey")
highlightedContent(highlightedValue, content, idKey) {
if (Ember.isNone(highlightedValue)) {
return null;
}
return content.find((c) => {
return this._castInteger(Ember.get(c, idKey)) === highlightedValue;
});
},
@computed("headerText", "selectedContent", "textKey")
selectedTitle(headerText, selectedContent, textKey) {
if (Ember.isNone(selectedContent)) {
return headerText;
}
return selectedContent[textKey];
},
@computed("headerText", "dynamicHeaderText", "selectedContent", "textKey", "clearSelectionLabel")
generatedHeadertext(headerText, dynamic, selectedContent, textKey, clearSelectionLabel) {
if (dynamic && !Ember.isNone(selectedContent)) {
return selectedContent[textKey];
}
if (dynamic && Ember.isNone(selectedContent) && !Ember.isNone(clearSelectionLabel)) {
return I18n.t(clearSelectionLabel);
}
return headerText;
},
@computed("content.[]", "filter", "idKey")
filteredContent(content, filter, idKey) {
let filteredContent;
if (Ember.isEmpty(filter)) {
filteredContent = content;
} else {
filteredContent = this.filterFunction(content)(this);
if (!Ember.isEmpty(filteredContent)) {
this.set("highlightedValue", filteredContent[0][idKey]);
}
}
return filteredContent;
},
@computed("scrollableParentSelector")
scrollableParent(scrollableParentSelector) {
return this.$().parents(scrollableParentSelector).first();
},
actions: {
onToggle() {
this.toggleProperty("expanded");
},
onFilterChange(filter) {
this.set("filter", filter);
},
onHoverRow(content) {
const id = this._castInteger(Ember.get(content, this.get("idKey")));
this.set("highlightedValue", id);
},
onSelectRow(content) {
this.setProperties({
value: this._castInteger(Ember.get(content, this.get("idKey"))),
expanded: false
});
},
onClearSelection() {
this.setProperties({ value: null, expanded: false });
}
},
_positionSelectBoxWrapper() {
const headerHeight = this.$(".select-box-header").outerHeight(false);
this.$(".select-box-wrapper").css({
width: this.$().width(),
display: "block",
height: headerHeight + this.$(".select-box-body").outerHeight(false)
});
},
_castInteger(id) {
if (this.get("castInteger") === true && Ember.isPresent(id)) {
return parseInt(id, 10);
}
return id;
},
_applyFixedPosition(width, height) {
const $placeholder = $(`<div class='select-box-fixed-placeholder-${this.get("componentId")}' style='vertical-align: middle; height: ${height}px; width: ${width}px; line-height: ${height}px;display:inline-block'></div>`);
this.$()
.before($placeholder)
.css({
width,
position: "fixed",
"margin-top": -this.get("scrollableParent").scrollTop(),
"margin-left": -width
});
this.get("scrollableParent").on("scroll.select-box", () => this.set("expanded", false) );
},
_removeFixedPosition() {
$(`.select-box-fixed-placeholder-${this.get("componentId")}`).remove();
this.$().css({
top: "auto",
left: "auto",
"margin-left": "auto",
"margin-top": "auto",
position: "relative"
});
this.get("scrollableParent").off("scroll.select-box");
},
_handleDownArrow() {
this._handleArrow("down");
},
_handleUpArrow() {
this._handleArrow("up");
},
_handleArrow(direction) {
const content = this.get("filteredContent");
const idKey = this.get("idKey");
const selectedContent = content.findBy(idKey, this.get("highlightedValue"));
const currentIndex = content.indexOf(selectedContent);
if (direction === "down") {
if (currentIndex < 0) {
this.set("highlightedValue", this._castInteger(Ember.get(content[0], idKey)));
} else if(currentIndex + 1 < content.length) {
this.set("highlightedValue", this._castInteger(Ember.get(content[currentIndex + 1], idKey)));
}
} else {
if (currentIndex <= 0) {
this.set("highlightedValue", this._castInteger(Ember.get(content[0], idKey)));
} else if(currentIndex - 1 < content.length) {
this.set("highlightedValue", this._castInteger(Ember.get(content[currentIndex - 1], idKey)));
}
}
Ember.run.schedule("afterRender", () => {
const $highlightedRow = this.$(".select-box-row.is-highlighted");
if ($highlightedRow.length === 0) { return; }
const $collection = this.$(".select-box-collection");
const rowOffset = $highlightedRow.offset();
const bodyOffset = $collection.offset();
$collection.scrollTop(rowOffset.top - bodyOffset.top);
});
}
});

View File

@ -1,9 +0,0 @@
export default Ember.Component.extend({
classNames: "select-box-collection",
actions: {
onClearSelection() {
this.sendAction("onClearSelection");
}
}
});

View File

@ -1,5 +0,0 @@
export default Ember.Component.extend({
classNames: "select-box-filter",
classNameBindings: ["focused:is-focused"]
});

View File

@ -1,24 +0,0 @@
export default Ember.Component.extend({
classNames: "select-box-header",
classNameBindings: ["focused:is-focused"],
didReceiveAttrs() {
this._super();
this._setCaretIcon();
},
click(event) {
this.sendAction("onToggle");
event.stopPropagation();
},
_setCaretIcon() {
if(this.get("expanded")) {
this.set("caretIcon", this.get("caretUpIcon"));
} else {
this.set("caretIcon", this.get("caretDownIcon"));
}
}
});

View File

@ -1,47 +0,0 @@
import computed from 'ember-addons/ember-computed-decorators';
import { iconHTML } from "discourse-common/lib/icon-library";
export default Ember.Component.extend({
layoutName: "components/select-box/select-box-row",
classNames: "select-box-row",
tagName: "li",
attributeBindings: ["title", "id:data-id"],
classNameBindings: ["isHighlighted:is-highlighted", "isSelected:is-selected"],
@computed("titleForRow")
title(titleForRow) { return titleForRow(this); },
@computed("idForRow")
id(idForRow) { return idForRow(this); },
@computed("templateForRow")
template(templateForRow) { return templateForRow(this); },
@computed("shouldHighlightRow", "highlightedValue")
isHighlighted(shouldHighlightRow) { return shouldHighlightRow(this); },
@computed("shouldSelectRow", "value")
isSelected(shouldSelectRow) { return shouldSelectRow(this); },
icon() {
if (this.get("content.icon")) {
const iconName = this.get("content.icon");
const iconClass = this.get("content.iconClass");
return iconHTML(iconName, { class: iconClass });
}
return null;
},
mouseEnter() {
this.sendAction("onHover", this.get("content"));
},
click() {
this.sendAction("onSelect", this.get("content"));
}
});

View File

@ -1,24 +0,0 @@
import NotificationOptionsComponent from "discourse/components/notifications-button";
import computed from "ember-addons/ember-computed-decorators";
import { iconHTML } from "discourse-common/lib/icon-library";
export default NotificationOptionsComponent.extend({
classNames: ["tag-notifications-button"],
i18nPrefix: "tagging.notifications",
@computed("value")
icon() {
return `${this._super()}${iconHTML("caret-down")}`.htmlSafe();
},
generatedHeadertext: null,
actions: {
onSelectRow(content) {
this._super(content);
this.sendAction("action", this.get("value"));
}
}
});

View File

@ -1,78 +0,0 @@
import { observes } from 'ember-addons/ember-computed-decorators';
import SelectBoxComponent from "discourse/components/select-box";
export default SelectBoxComponent.extend({
textKey: "name",
headerText: I18n.t("topic.controls"),
dynamicHeaderText: false,
collectionHeight: 300,
init() {
this._super();
this._createContent();
},
_createContent() {
const content = [];
const topic = this.get('topic');
const details = topic.get('details');
if (details.get('can_invite_to')) {
content.push({ id: 'invite', icon: 'users', name: I18n.t('topic.invite_reply.title') });
}
if (topic.get('bookmarked')) {
content.push({ id: 'bookmark', icon: 'bookmark', name: I18n.t('bookmarked.clear_bookmarks') });
} else {
content.push({ id: 'bookmark', icon: 'bookmark', name: I18n.t('bookmarked.title') });
}
content.push({ id: 'share', icon: 'link', name: I18n.t('topic.share.title') });
if (details.get('can_flag_topic')) {
content.push({ id: 'flag', icon: 'flag', name: I18n.t('topic.flag_topic.title') });
}
this.set('content', content);
},
@observes('value')
_valueChanged() {
this._super();
const value = this.get('value');
const topic = this.get('topic');
// In case it's not a valid topic
if (!topic.get('id')) {
return;
}
const refresh = () => {
this._createContent();
this.set('value', null);
};
switch(value) {
case 'invite':
this.attrs.showInvite();
refresh();
break;
case 'bookmark':
topic.toggleBookmark().then(() => refresh());
break;
case 'share':
this.appEvents.trigger('share:url', topic.get('shareUrl'), $('#topic-footer-buttons'));
refresh();
break;
case 'flag':
this.attrs.showFlagTopic();
refresh();
break;
}
}
});

View File

@ -1,9 +0,0 @@
export default Ember.Component.extend({
layoutName: "components/topic-notifications-button",
classNames: ["topic-notifications-button"],
showFullTitle: true,
appendReason: true,
});

View File

@ -1,52 +0,0 @@
import NotificationOptionsComponent from "discourse/components/notifications-button";
import { on } from "ember-addons/ember-computed-decorators";
import computed from "ember-addons/ember-computed-decorators";
import { topicLevels, buttonDetails } from "discourse/lib/notification-levels";
export default NotificationOptionsComponent.extend({
classNames: ["topic-notifications-options"],
content: topicLevels,
i18nPrefix: "topic.notifications",
value: Ember.computed.alias("topic.details.notification_level"),
@on("didInsertElement")
_bindGlobalLevelChanged() {
this.appEvents.on("topic-notifications-button:changed", (msg) => {
if (msg.type === "notification") {
if (this.get("value") !== msg.id) {
this.get("topic.details").updateNotifications(msg.id);
}
}
});
},
@on("willDestroyElement")
_unbindGlobalLevelChanged() {
this.appEvents.off("topic-notifications-button:changed");
},
@computed("value", "showFullTitle")
generatedHeadertext(value, showFullTitle) {
if (showFullTitle) {
const details = buttonDetails(value);
return I18n.t(`topic.notifications.${details.key}.title`);
} else {
return null;
}
},
actions: {
onSelectRow(content) {
const notificationLevelId = Ember.get(content, this.get("idKey"));
if (notificationLevelId !== this.get("value")) {
this.get("topic.details").updateNotifications(notificationLevelId);
}
this._super(content);
}
}
});

View File

@ -1,3 +1,5 @@
export default Ember.Component.extend({
classNames: ['user-stat']
classNames: ['user-stat'],
type: 'number',
isNumber: Ember.computed.equal('type', 'number')
});

View File

@ -0,0 +1,3 @@
export default Ember.Component.extend({
classNames: ['top-sub-section']
});

View File

@ -0,0 +1,3 @@
export default Ember.Component.extend({
tagName: 'li'
});

View File

@ -0,0 +1,13 @@
import computed from 'ember-addons/ember-computed-decorators';
// should be kept in sync with 'UserSummary::MAX_SUMMARY_RESULTS'
const MAX_SUMMARY_RESULTS = 6;
export default Ember.Component.extend({
tagName: '',
@computed('items.length')
hasMore(length) {
return length >= MAX_SUMMARY_RESULTS;
}
});

View File

@ -0,0 +1,3 @@
export default Ember.Component.extend({
tagName: 'li'
});

View File

@ -25,9 +25,9 @@ export default Ember.Controller.extend(BadgeSelectController, {
return username ? userCount : modelCount;
},
@computed("model.has_title_badges")
canSelectTitle(hasTitleBadges) {
return this.siteSettings.enable_badges && hasTitleBadges;
@computed("model.allow_title", "model.has_badge", "model")
canSelectTitle(hasTitleBadges, hasBadge) {
return this.siteSettings.enable_badges && hasTitleBadges && hasBadge;
},
actions: {

View File

@ -1,4 +1,3 @@
import { iconHTML } from 'discourse-common/lib/icon-library';
import ModalFunctionality from 'discourse/mixins/modal-functionality';
import ActionSummary from 'discourse/models/action-summary';
import { MAX_MESSAGE_LENGTH } from 'discourse/models/post-action-type';
@ -54,19 +53,17 @@ export default Ember.Controller.extend(ModalFunctionality, {
return flagsAvailable;
} else {
// flagging topic
const self = this,
lookup = Em.Object.create();
_.each(this.get("model.actions_summary"),function(a) {
a.flagTopic = self.get('model');
a.actionType = self.site.topicFlagTypeById(a.id);
const actionSummary = ActionSummary.create(a);
lookup.set(a.actionType.get('name_key'), actionSummary);
let lookup = Em.Object.create();
let model = this.get('model');
model.get('actions_summary').forEach(a => {
a.flagTopic = model;
a.actionType = this.site.topicFlagTypeById(a.id);
lookup.set(a.actionType.get('name_key'), ActionSummary.create(a));
});
this.set('topicActionByName', lookup);
return this.site.get('topic_flag_types').filter(function(item) {
return _.any(self.get("model.actions_summary"), function(a) {
return this.site.get('topic_flag_types').filter(item => {
return _.any(this.get("model.actions_summary"), a => {
return (a.id === item.get('id') && a.can_act);
});
});
@ -97,13 +94,18 @@ export default Ember.Controller.extend(ModalFunctionality, {
return !flagTopic && !isCustomFlag && this.currentUser.get('staff');
},
submitText: function(){
if (this.get('selected.is_custom_flag')) {
return iconHTML('envelope') + (I18n.t(this.get('flagTopic') ? "flagging_topic.notify_action" : "flagging.notify_action"));
} else {
return iconHTML('flag') + (I18n.t(this.get('flagTopic') ? "flagging_topic.action" : "flagging.action"));
@computed('selected.is_custom_flag')
submitIcon(isCustomFlag) {
return isCustomFlag ? "envelope" : "flag";
},
@computed('selected.is_custom_flag', 'flagTopic')
submitLabel(isCustomFlag, flagTopic) {
if (isCustomFlag) {
return flagTopic ? "flagging_topic.notify_action" : "flagging.notify_action";
}
}.property('selected.is_custom_flag'),
return flagTopic ? "flagging_topic.action" : "flagging.action";
},
actions: {
deleteSpammer() {

View File

@ -13,7 +13,7 @@ export default Ember.Controller.extend(PreferencesTabController, {
'dynamic_favicon',
'enable_quoting',
'disable_jump_reply',
'automatically_unpin_topics'
'automatically_unpin_topics',
];
if (makeDefault) {

View File

@ -10,7 +10,8 @@ export default Ember.Controller.extend(PreferencesTabController, {
'new_topic_duration_minutes',
'auto_track_topics_after_msecs',
'notification_level_when_replying',
'like_notification_frequency'
'like_notification_frequency',
'allow_private_messages',
],
@computed("model.watchedCategories", "model.trackedCategories", "model.mutedCategories")

View File

@ -231,7 +231,11 @@ export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, {
},
removeAllowedUser(user) {
return this.get('model.details').removeAllowedUser(user);
return this.get('model.details').removeAllowedUser(user).then(() => {
if (this.currentUser.id === user.id) {
this.transitionToRoute("userPrivateMessages", user);
}
});
},
removeAllowedGroup(group) {
@ -536,7 +540,7 @@ export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, {
},
expandHidden(post) {
post.expandHidden();
return post.expandHidden();
},
toggleVisibility() {

View File

@ -1,7 +1,5 @@
import computed from 'ember-addons/ember-computed-decorators';
// should be kept in sync with 'UserSummary::MAX_SUMMARY_RESULTS'
const MAX_SUMMARY_RESULTS = 6;
// should be kept in sync with 'UserSummary::MAX_BADGES'
const MAX_BADGES = 6;
@ -9,12 +7,6 @@ export default Ember.Controller.extend({
userController: Ember.inject.controller('user'),
user: Ember.computed.alias('userController.model'),
@computed("model.topics.length")
moreTopics(topicsLength) { return topicsLength >= MAX_SUMMARY_RESULTS; },
@computed("model.replies.length")
moreReplies(repliesLength) { return repliesLength >= MAX_SUMMARY_RESULTS; },
@computed("model.badges.length")
moreBadges(badgesLength) { return badgesLength >= MAX_BADGES; },
});

View File

@ -33,7 +33,7 @@ export default {
}
bus.subscribe(`/notification/${user.get('id')}`, data => {
const store = container.lookup('store:main');
const store = container.lookup('service:store');
const oldUnread = user.get('unread_notifications');
const oldPM = user.get('unread_private_messages');

View File

@ -6,8 +6,7 @@ const bindings = {
'!': {postAction: 'showFlags'},
'#': {handler: 'goToPost', anonymous: true},
'/': {handler: 'toggleSearch', anonymous: true},
'ctrl+shift+s': {handler: 'toggleSearch', anonymous: true},
'command+shift+s': {handler: 'toggleSearch', anonymous: true},
'ctrl+alt+f': {handler: 'toggleSearch', anonymous: true},
'=': {handler: 'toggleHamburgerMenu', anonymous: true},
'?': {handler: 'showHelpModal', anonymous: true},
'.': {click: '.alert.alert-info.clickable', anonymous: true}, // show incoming/updated topics

View File

@ -441,7 +441,7 @@ class PluginApi {
* will issue a request to `/mice.json`
**/
addStorePluralization(thing, plural) {
this.container.lookup("store:main").addPluralization(thing, plural);
this.container.lookup("service:store").addPluralization(thing, plural);
}
/**

View File

@ -147,7 +147,7 @@ function positioningWorkaround($fixedElement) {
fixedElement.style.top = '0px';
composingTopic = $('#reply-control .category-select-box').length > 0;
composingTopic = $('#reply-control .category-chooser').length > 0;
const height = calcHeight(composingTopic);
fixedElement.style.height = height + "px";

View File

@ -127,6 +127,7 @@ export default function transformPost(currentUser, site, post, prevPost, nextPos
postAtts.allowedGroups = details.allowed_groups;
postAtts.allowedUsers = details.allowed_users;
postAtts.canRemoveAllowedUsers = details.can_remove_allowed_users;
postAtts.canRemoveSelfId = details.can_remove_self_id;
postAtts.canInvite = details.can_invite_to;
}

View File

@ -122,7 +122,7 @@ NavItem.reopenClass({
_.merge(args, extra);
});
const store = Discourse.__container__.lookup('store:main');
const store = Discourse.__container__.lookup('service:store');
return store.createRecord('nav-item', args);
},

View File

@ -1,9 +1,7 @@
import RestModel from 'discourse/models/rest';
const PostActionType = RestModel.extend({
notCustomFlag: Em.computed.not('is_custom_flag')
});
export const MAX_MESSAGE_LENGTH = 500;
export default PostActionType;
export default RestModel.extend({
notCustomFlag: Em.computed.not('is_custom_flag')
});

View File

@ -84,7 +84,7 @@ RestModel.reopenClass({
if (!args.store) {
const container = Discourse.__container__;
// Ember.warn('Use `store.createRecord` to create records instead of `.create()`');
args.store = container.lookup('store:main');
args.store = container.lookup('service:store');
}
args.__munge = this.munge;

View File

@ -94,7 +94,7 @@ Site.reopenClass(Singleton, {
// The current singleton will retrieve its attributes from the `PreloadStore`.
createCurrent() {
const store = Discourse.__container__.lookup('store:main');
const store = Discourse.__container__.lookup('service:store');
return store.createRecord('site', PreloadStore.get('site'));
},

View File

@ -140,7 +140,7 @@ TopicList.reopenClass({
},
find(filter, params) {
const store = Discourse.__container__.lookup('store:main');
const store = Discourse.__container__.lookup('service:store');
return store.findFiltered('topicList', {filter, params});
},

View File

@ -11,7 +11,6 @@ import UserBadge from 'discourse/models/user-badge';
import UserActionStat from 'discourse/models/user-action-stat';
import UserAction from 'discourse/models/user-action';
import Group from 'discourse/models/group';
import Topic from 'discourse/models/topic';
import { emojiUnescape } from 'discourse/lib/text';
import PreloadStore from 'preload-store';
import { defaultHomepage } from 'discourse/lib/utilities';
@ -248,7 +247,8 @@ const User = RestModel.extend({
'notification_level_when_replying',
'like_notification_frequency',
'include_tl0_in_digests',
'theme_key'
'theme_key',
'allow_private_messages',
];
if (fields) {
@ -492,38 +492,39 @@ const User = RestModel.extend({
},
summary() {
return ajax(userPath(`${this.get("username_lower")}/summary.json`))
.then(json => {
const summary = json["user_summary"];
const topicMap = {};
const badgeMap = {};
let { store } = this;
json.topics.forEach(t => topicMap[t.id] = Topic.create(t));
Badge.createFromJson(json).forEach(b => badgeMap[b.id] = b );
return ajax(userPath(`${this.get("username_lower")}/summary.json`)).then(json => {
const summary = json.user_summary;
const topicMap = {};
const badgeMap = {};
summary.topics = summary.topic_ids.map(id => topicMap[id]);
json.topics.forEach(t => topicMap[t.id] = store.createRecord('topic', t));
Badge.createFromJson(json).forEach(b => badgeMap[b.id] = b );
summary.replies.forEach(r => {
r.topic = topicMap[r.topic_id];
r.url = r.topic.urlForPostNumber(r.post_number);
r.createdAt = new Date(r.created_at);
});
summary.topics = summary.topic_ids.map(id => topicMap[id]);
summary.links.forEach(l => {
l.topic = topicMap[l.topic_id];
l.post_url = l.topic.urlForPostNumber(l.post_number);
});
summary.replies.forEach(r => {
r.topic = topicMap[r.topic_id];
r.url = r.topic.urlForPostNumber(r.post_number);
r.createdAt = new Date(r.created_at);
});
if (summary.badges) {
summary.badges = summary.badges.map(ub => {
const badge = badgeMap[ub.badge_id];
badge.count = ub.count;
return badge;
});
}
summary.links.forEach(l => {
l.topic = topicMap[l.topic_id];
l.post_url = l.topic.urlForPostNumber(l.post_number);
});
return summary;
});
if (summary.badges) {
summary.badges = summary.badges.map(ub => {
const badge = badgeMap[ub.badge_id];
badge.count = ub.count;
return badge;
});
}
return summary;
});
},
canManageGroup(group) {
@ -543,7 +544,7 @@ User.reopenClass(Singleton, {
createCurrent() {
const userJson = PreloadStore.get('currentUser');
if (userJson) {
const store = Discourse.__container__.lookup('store:main');
const store = Discourse.__container__.lookup('service:store');
return store.createRecord('user', userJson);
}
return null;

View File

@ -8,17 +8,7 @@ import SearchService from 'discourse/services/search';
import { startTracking, default as TopicTrackingState } from 'discourse/models/topic-tracking-state';
import ScreenTrack from 'discourse/lib/screen-track';
function inject() {
const app = arguments[0],
name = arguments[1],
singletonName = Ember.String.underscore(name).replace(/_/g, '-') + ':main';
Array.prototype.slice.call(arguments, 2).forEach(dest => app.inject(dest, name, singletonName));
}
function injectAll(app, name) {
inject(app, name, 'controller', 'component', 'route', 'model', 'adapter');
}
const ALL_TARGETS = ['controller', 'component', 'route', 'model', 'adapter'];
export default {
name: "inject-discourse-objects",
@ -26,51 +16,62 @@ export default {
initialize(container, app) {
const appEvents = AppEvents.create();
app.register('app-events:main', appEvents, { instantiate: false });
injectAll(app, 'appEvents');
ALL_TARGETS.forEach(t => app.inject(t, 'appEvents', 'app-events:main'));
DiscourseURL.appEvents = appEvents;
// backwards compatibility: remove when plugins have updated
app.register('store:main', Store);
inject(app, 'store', 'route', 'controller', 'service');
app.register('service:store', Store);
ALL_TARGETS.forEach(t => app.inject(t, 'store', 'service:store'));
const messageBus = window.MessageBus;
app.register('message-bus:main', messageBus, { instantiate: false });
injectAll(app, 'messageBus');
ALL_TARGETS.forEach(t => app.inject(t, 'messageBus', 'message-bus:main'));
const currentUser = Discourse.User.current();
app.register('current-user:main', currentUser, { instantiate: false });
const topicTrackingState = TopicTrackingState.create({ messageBus, currentUser });
app.register('topic-tracking-state:main', topicTrackingState, { instantiate: false });
injectAll(app, 'topicTrackingState');
ALL_TARGETS.forEach(t => app.inject(t, 'topicTrackingState', 'topic-tracking-state:main'));
const site = Discourse.Site.current();
app.register('site:main', site, { instantiate: false });
injectAll(app, 'site');
ALL_TARGETS.forEach(t => app.inject(t, 'site', 'site:main'));
const siteSettings = Discourse.SiteSettings;
app.register('site-settings:main', siteSettings, { instantiate: false });
injectAll(app, 'siteSettings');
ALL_TARGETS.forEach(t => app.inject(t, 'siteSettings', 'site-settings:main'));
app.register('search-service:main', SearchService);
injectAll(app, 'searchService');
ALL_TARGETS.forEach(t => app.inject(t, 'searchService', 'search-service:main'));
const session = Session.current();
app.register('session:main', session, { instantiate: false });
injectAll(app, 'session');
ALL_TARGETS.forEach(t => app.inject(t, 'session', 'session:main'));
const screenTrack = new ScreenTrack(
topicTrackingState,
siteSettings,
session,
currentUser
);
const screenTrack = new ScreenTrack(topicTrackingState, siteSettings, session, currentUser);
app.register('screen-track:main', screenTrack, { instantiate: false });
inject(app, 'screenTrack', 'component', 'route');
['component', 'route'].forEach(t => app.inject(t, 'screenTrack', 'screen-track:main'));
if (currentUser) {
inject(app, 'currentUser', 'component', 'route', 'controller');
['component', 'route', 'controller'].forEach(t => {
app.inject(t, 'currentUser', 'current-user:main');
});
}
app.register('location:discourse-location', DiscourseLocation);
const keyValueStore = new KeyValueStore("discourse_");
app.register('key-value-store:main', keyValueStore, { instantiate: false });
injectAll(app, 'keyValueStore');
ALL_TARGETS.forEach(t => app.inject(t, 'keyValueStore', 'key-value-store:main'));
startTracking(topicTrackingState);
}

View File

@ -2,12 +2,13 @@ import { ajax } from 'discourse/lib/ajax';
import { translateResults, getSearchKey, isValidSearchTerm } from "discourse/lib/search";
import PreloadStore from 'preload-store';
import { getTransient, setTransient } from 'discourse/lib/page-tracker';
import { escapeExpression } from 'discourse/lib/utilities';
export default Discourse.Route.extend({
queryParams: { q: {}, expanded: false, context_id: {}, context: {}, skip_context: {} },
titleToken() {
return I18n.t('search.results_page');
return I18n.t('search.results_page', { term: escapeExpression(this.controllerFor("full-page-search").get('searchTerm')) });
},
model(params) {

View File

@ -9,17 +9,13 @@
{{badge-card badge=model size="large" count=userBadges.grant_count}}
<div class='badge-grant-info {{if hiddenSetTitle '' 'hidden'}}'>
<div>
{{#if model.allow_title}}
{{#if canSelectTitle}}
<div class='grant-info-item'>
{{i18n 'badges.allow_title'}}
{{#if userBadges}}
{{#if model.allow_title}}
{{d-button
{{d-button
class='btn btn-small pad-left no-text'
action='toggleSetUserTitle'
icon='pencil'}}
{{/if}}
{{/if}}
</div>
{{/if}}
{{#if model.multiple_grant}}
@ -29,10 +25,13 @@
{{/if}}
</div>
</div>
<div class='badge-set-title {{if hiddenSetTitle 'hidden' ''}}'>
{{badge-title selectableUserBadges=selectableUserBadges user=user}}
<button class='btn btn-default close-btn' {{action "toggleSetUserTitle"}}>{{i18n 'close'}}</button>
</div>
{{#if canSelectTitle}}
<div class='badge-set-title {{if hiddenSetTitle 'hidden' ''}}'>
{{badge-title selectableUserBadges=selectableUserBadges user=user}}
<button class='btn btn-default close-btn' {{action "toggleSetUserTitle"}}>{{i18n 'close'}}</button>
</div>
{{/if}}
</div>
{{#if userBadges}}

View File

@ -10,7 +10,7 @@
<div class="control-group">
<label class="control-label"></label>
<div class="controls">
{{combo-box valueAttribute="id" value=selectedUserBadgeId nameProperty="badge.name" content=selectableUserBadges}}
{{combo-box value=selectedUserBadgeId nameProperty="badge.name" content=selectableUserBadges}}
</div>
</div>

View File

@ -1,12 +0,0 @@
<button
class="btn {{if text 'btn-icon-text' 'no-text btn-icon'}}"
aria-label="{{selectedTitle}}"
type="button"
title="{{selectedTitle}}">
{{{icon}}}
{{#if text}}
<span class="d-button-label">{{{text}}}</span>
{{/if}}
</button>

View File

@ -19,8 +19,8 @@
{{/each}}
{{else}}
<label>{{i18n 'category.parent'}}</label>
{{category-select-box
clearSelectionLabel="category.none"
{{category-chooser
none="category.none"
value=category.parent_category_id
categories=parentCategories
allowUncategorized=false}}

View File

@ -61,11 +61,13 @@
<section class="field">
<label>
{{i18n "category.sort_order"}}
{{combo-box valueAttribute="value" content=availableSorts value=category.sort_order none="category.sort_options.default"}}
{{#unless isDefaultSortOrder}}
{{combo-box valueAttribute="value" content=sortAscendingOptions value=category.sort_ascending none="category.sort_options.default"}}
{{/unless}}
</label>
<div class="controls">
{{combo-box valueAttribute="value" content=availableSorts value=category.sort_order none="category.sort_options.default"}}
{{#unless isDefaultSortOrder}}
{{combo-box valueAttribute="value" content=sortAscendingOptions value=category.sort_ascending none="category.sort_options.default"}}
{{/unless}}
</div>
</section>
<section class="field num-featured-topics-fields">

View File

@ -13,7 +13,7 @@
{{else if publishToCategory}}
<div class="control-group">
<label>{{i18n 'topic.topic_status_update.publish_to'}}</label>
{{category-select-box
{{category-chooser
value=topicTimer.category_id
excludeCategoryId=excludeCategoryId}}
</div>

View File

@ -1,11 +1,17 @@
{{#if isNotifyUser}}
<h3>{{formattedName}}</h3>
<div class='controls'>
<label class='radio'><input type='radio' id="radio_{{unbound flag.name_key}}" {{action "changePostActionType" flag}} name='post_action_type_index'> <span class='description'>{{{flag.description}}}</span></label>
{{#if showMessageInput}}
{{textarea name="message" class="flag-message" placeholder=customPlaceholder value=message}}
<div class="custom-message-length {{customMessageLengthClasses}}">{{customMessageLength}}</div>
{{/if}}
<label class='radio'>
<input type='radio' id="radio_{{unbound flag.name_key}}" {{action "changePostActionType" flag}} name='post_action_type_index'>
<div class='flag-action-type-details'>
<span class='description'>{{{flag.description}}}</span>
{{#if showMessageInput}}
{{textarea name="message" class="flag-message" placeholder=customPlaceholder value=message}}
<div class="custom-message-length {{customMessageLengthClasses}}">{{customMessageLength}}</div>
{{/if}}
</div>
</label>
</div>
{{#if staffFlagsAvailable}}
<hr>
@ -14,14 +20,17 @@
{{else}}
<div class='controls'>
<label class='radio'>
<input type='radio' id="radio_{{unbound flag.name_key}}" {{action "changePostActionType" flag}} name='post_action_type_index'> <strong>{{formattedName}}</strong>
{{#if showDescription}}
<div class='description'>{{{description}}}</div>
{{/if}}
<input type='radio' id="radio_{{unbound flag.name_key}}" {{action "changePostActionType" flag}} name='post_action_type_index'>
<div class='flag-action-type-details'>
<strong>{{formattedName}}</strong>
{{#if showDescription}}
<div class='description'>{{{description}}}</div>
{{/if}}
{{#if showMessageInput}}
{{textarea name="message" class="flag-message" placeholder=customPlaceholder value=message}}
<div class="custom-message-length {{customMessageLengthClasses}}">{{customMessageLength}}</div>
{{/if}}
</div>
</label>
{{#if showMessageInput}}
{{textarea name="message" class="flag-message" placeholder=customPlaceholder value=message}}
<div class="custom-message-length {{customMessageLengthClasses}}">{{customMessageLength}}</div>
{{/if}}
</div>
{{/if}}

View File

@ -1,16 +1,13 @@
<div class="future-date-input">
<div class="control-group">
<label>{{displayLabel}}</label>
{{future-date-input-selector
valueAttribute="id"
minimumResultsForSearch=-1
statusType=statusType
value=selection
input=input
includeWeekend=includeWeekend
includeForever=includeForever
width="50%"
includeFarFuture=includeFarFuture
none="topic.auto_update_input.none"}}
</div>

View File

@ -24,7 +24,7 @@
<span class="edit-title">
{{text-field value=buffered.title maxlength=siteSettings.max_topic_title_length}}
</span>
{{category-select-box value=buffered.category_id}}
{{category-chooser value=buffered.category_id}}
{{else}}
<span class='post-title'>
{{i18n "queue.topic"}}

View File

@ -1,55 +0,0 @@
<input
class="select-box-offscreen"
type="text"
aria-haspopup="true"
role="button"
aria-labelledby="select-box-input-{{componentId}}"
tabindex={{tabindex}}
/>
{{component selectBoxHeaderComponent
text=generatedHeadertext
selectedTitle=selectedTitle
focused=focused
caretUpIcon=caretUpIcon
caretDownIcon=caretDownIcon
onToggle=(action "onToggle")
icon=icon
expanded=expanded
value=value
}}
<div class="select-box-body">
{{#if renderBody}}
{{#if filterable}}
{{component selectBoxFilterComponent
onFilterChange=(action "onFilterChange")
icon=filterIcon
focused=filterFocused
placeholder=filterPlaceholder
tabindex=tabindex
}}
{{/if}}
{{component selectBoxCollectionComponent
clearSelectionLabel=clearSelectionLabel
filteredContent=filteredContent
selectBoxRowComponent=selectBoxRowComponent
templateForRow=templateForRow
shouldHighlightRow=shouldHighlightRow
shouldSelectRow=shouldSelectRow
titleForRow=titleForRow
idForRow=idForRow
onSelectRow=(action "onSelectRow")
onHoverRow=(action "onHoverRow")
onClearSelection=(action "onClearSelection")
noContentLabel=noContentLabel
highlightedValue=highlightedValue
value=value
}}
{{/if}}
</div>
{{#if wrapper}}
<div class="select-box-wrapper"></div>
{{/if}}

View File

@ -1,28 +0,0 @@
<ul class="collection">
{{#if clearSelectionLabel}}
<li {{action "onClearSelection" on="click"}} class="select-box-row clear-selection">
{{i18n clearSelectionLabel}}
</li>
{{/if}}
{{#each filteredContent as |content|}}
{{component selectBoxRowComponent
content=content
templateForRow=templateForRow
idForRow=idForRow
titleForRow=titleForRow
shouldHighlightRow=shouldHighlightRow
shouldSelectRow=shouldSelectRow
highlightedValue=highlightedValue
onSelect=onSelectRow
onHover=onHoverRow
value=value
}}
{{else}}
{{#if noContentLabel}}
<li class="select-box-row no-content">
{{noContentLabel}}
</li>
{{/if}}
{{/each}}
</ul>

View File

@ -1,9 +0,0 @@
{{#if icon}}
{{d-icon icon class="icon"}}
{{/if}}
<span class="current-selection" title={{selectedTitle}}>
{{text}}
</span>
{{d-icon caretIcon class="caret-icon"}}

View File

@ -1,5 +1,11 @@
<span class='value'>
{{#if icon}}{{d-icon icon}}{{/if}}
{{number value}}
{{#if isNumber}}
{{number value}}
{{else}}
{{value}}
{{/if}}
</span>
<span class='label'>
{{#if icon}}{{d-icon icon}}{{/if}}
{{{i18n label count=value}}}
</span>
<span class='label'>{{{i18n label count=value}}}</span>

View File

@ -0,0 +1,2 @@
<h3 class='stats-title'>{{i18n (concat "user.summary." title)}}</h3>
{{yield}}

View File

@ -0,0 +1,9 @@
<span class='topic-info'>
{{format-date createdAt format="tiny" noTitle="true"}}
{{#if likes}}
&middot;
{{d-icon 'heart'}}&nbsp;<span class='like-count'>{{number likes}}</span>
{{/if}}
</span>
<br>
<a href="{{unbound url}}">{{{topic.fancyTitle}}}</a>

View File

@ -0,0 +1,16 @@
{{#if items}}
<ul>
{{#each items as |item|}}
{{yield item}}
{{/each}}
</ul>
{{#if hasMore}}
<p>
{{#link-to (concat "userActivity." type) user class="more"}}
{{i18n (concat "user.summary.more_" type)}}
{{/link-to}}
</p>
{{/if}}
{{else}}
<p>{{i18n (concat "user.summary.no_" type)}}</p>
{{/if}}

View File

@ -0,0 +1,4 @@
{{#user-info user=user}}
{{d-icon icon}}
<span class={{countClass}}>{{number user.count}}</span>
{{/user-info}}

View File

@ -0,0 +1,9 @@
{{#if users}}
<ul>
{{#each users as |user|}}
{{yield user}}
{{/each}}
</ul>
{{else}}
<p>{{i18n (concat "user.summary." none)}}</p>
{{/if}}

View File

@ -72,7 +72,7 @@
{{#if model.showCategoryChooser}}
<div class="category-input">
{{category-select-box value=model.categoryId scopedCategoryId=scopedCategoryId tabindex="3"}}
{{category-chooser value=model.categoryId scopedCategoryId=scopedCategoryId tabindex="3"}}
{{popup-input-tip validation=categoryValidation}}
</div>
{{#if model.archetype.hasOptions}}

View File

@ -57,7 +57,7 @@
<span class='desc'>
{{i18n "search.sort_by"}}
</span>
{{combo-box value=sortOrder content=sortOrders castInteger="true"}}
{{combo-box value=sortOrder content=sortOrders castInteger=true}}
</div>
</div>
{{/if}}

View File

@ -1,6 +1,6 @@
<p>{{i18n "topics.bulk.choose_new_category"}}</p>
<p>{{category-select-box value=newCategoryId}}</p>
<p>{{category-chooser value=newCategoryId}}</p>
{{#conditional-loading-spinner condition=loading}}
{{d-button action="changeCategory" label="topics.bulk.change_category"}}

View File

@ -18,7 +18,8 @@
action=(action "createFlag")
disabled=submitDisabled
title="flagging.submit_tooltip"
translatedLabel=submitText}}
icon=submitIcon
label=submitLabel}}
{{#if canSendWarning}}
{{d-button

View File

@ -6,7 +6,7 @@
{{text-field value=topicName placeholderKey="composer.title_placeholder" elementId='split-topic-name'}}
<label>{{i18n 'categories.category'}}</label>
{{category-select-box value=categoryId class="small"}}
{{category-chooser value=categoryId class="small"}}
</form>
{{/d-modal-body}}

Some files were not shown because too many files have changed in this diff Show More