Merge branch 'master' of github.com:discourse/discourse
This commit is contained in:
commit
4da9a434fd
|
@ -58,6 +58,9 @@ log/
|
|||
# Ignore Eclipse .buildpath file
|
||||
/.buildpath
|
||||
|
||||
# Ignore byebug history
|
||||
/.byebug_history
|
||||
|
||||
# Ignore RubyMine settings
|
||||
/.idea
|
||||
|
||||
|
|
|
@ -1,11 +1,21 @@
|
|||
import IncomingEmail from 'admin/models/incoming-email';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
loadMore() {
|
||||
return IncomingEmail.findAll(this.get("filter"), this.get("model.length"))
|
||||
.then(incoming => {
|
||||
if (incoming.length < 50) { this.get("model").set("allLoaded", true); }
|
||||
this.get("model").addObjects(incoming);
|
||||
});
|
||||
loading: false,
|
||||
|
||||
actions: {
|
||||
|
||||
loadMore() {
|
||||
if (this.get("loading") || this.get("model.allLoaded")) { return; }
|
||||
this.set('loading', true);
|
||||
|
||||
IncomingEmail.findAll(this.get("filter"), this.get("model.length"))
|
||||
.then(incoming => {
|
||||
if (incoming.length < 50) { this.get("model").set("allLoaded", true); }
|
||||
this.get("model").addObjects(incoming);
|
||||
}).finally(() => {
|
||||
this.set('loading', false);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,11 +1,20 @@
|
|||
import EmailLog from 'admin/models/email-log';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
loadMore() {
|
||||
return EmailLog.findAll(this.get("filter"), this.get("model.length"))
|
||||
.then(logs => {
|
||||
if (logs.length < 50) { this.get("model").set("allLoaded", true); }
|
||||
this.get("model").addObjects(logs);
|
||||
});
|
||||
loading: false,
|
||||
|
||||
actions: {
|
||||
loadMore() {
|
||||
if (this.get("loading") || this.get("model.allLoaded")) { return; }
|
||||
|
||||
this.set('loading', true);
|
||||
return EmailLog.findAll(this.get("filter"), this.get("model.length"))
|
||||
.then(logs => {
|
||||
if (logs.length < 50) { this.get("model").set("allLoaded", true); }
|
||||
this.get("model").addObjects(logs);
|
||||
}).finally(() => {
|
||||
this.set('loading', false);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,55 +1,57 @@
|
|||
<table class='table email-list'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{i18n 'admin.email.time'}}</th>
|
||||
<th>{{i18n 'admin.email.incoming_emails.from_address'}}</th>
|
||||
<th>{{i18n 'admin.email.incoming_emails.to_addresses'}}</th>
|
||||
<th>{{i18n 'admin.email.incoming_emails.subject'}}</th>
|
||||
{{#load-more selector=".email-list tr" action="loadMore"}}
|
||||
<table class='table email-list'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{i18n 'admin.email.time'}}</th>
|
||||
<th>{{i18n 'admin.email.incoming_emails.from_address'}}</th>
|
||||
<th>{{i18n 'admin.email.incoming_emails.to_addresses'}}</th>
|
||||
<th>{{i18n 'admin.email.incoming_emails.subject'}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tr class="filters">
|
||||
<td>{{i18n 'admin.email.logs.filters.title'}}</td>
|
||||
<td>{{text-field value=filter.from placeholderKey="admin.email.incoming_emails.filters.from_placeholder"}}</td>
|
||||
<td>{{text-field value=filter.to placeholderKey="admin.email.incoming_emails.filters.to_placeholder"}}</td>
|
||||
<td>{{text-field value=filter.subject placeholderKey="admin.email.incoming_emails.filters.subject_placeholder"}}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tr class="filters">
|
||||
<td>{{i18n 'admin.email.logs.filters.title'}}</td>
|
||||
<td>{{text-field value=filter.from placeholderKey="admin.email.incoming_emails.filters.from_placeholder"}}</td>
|
||||
<td>{{text-field value=filter.to placeholderKey="admin.email.incoming_emails.filters.to_placeholder"}}</td>
|
||||
<td>{{text-field value=filter.subject placeholderKey="admin.email.incoming_emails.filters.subject_placeholder"}}</td>
|
||||
</tr>
|
||||
|
||||
{{#each email in model}}
|
||||
<tr>
|
||||
<td class="time">{{format-date email.created_at}}</td>
|
||||
<td class="username">
|
||||
<div>
|
||||
{{#if email.user}}
|
||||
{{#link-to 'adminUser' email.user}}
|
||||
{{avatar email.user imageSize="tiny"}}
|
||||
{{email.from_address}}
|
||||
{{/link-to}}
|
||||
{{#each email in model}}
|
||||
<tr>
|
||||
<td class="time">{{format-date email.created_at}}</td>
|
||||
<td class="username">
|
||||
<div>
|
||||
{{#if email.user}}
|
||||
{{#link-to 'adminUser' email.user}}
|
||||
{{avatar email.user imageSize="tiny"}}
|
||||
{{email.from_address}}
|
||||
{{/link-to}}
|
||||
{{else}}
|
||||
—
|
||||
{{/if}}
|
||||
</div>
|
||||
</td>
|
||||
<td class="addresses">
|
||||
{{#each to in email.to_addresses}}
|
||||
<p><a href="mailto:{{unbound to}}" title="TO">{{unbound to}}</a></p>
|
||||
{{/each}}
|
||||
{{#each cc in email.cc_addresses}}
|
||||
<p><a href="mailto:{{unbound cc}}" title="CC">{{unbound cc}}</a></p>
|
||||
{{/each}}
|
||||
</td>
|
||||
<td>
|
||||
{{#if email.post_url}}
|
||||
<a href="{{email.post_url}}">{{email.subject}}</a>
|
||||
{{else}}
|
||||
—
|
||||
{{email.subject}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</td>
|
||||
<td class="addresses">
|
||||
{{#each to in email.to_addresses}}
|
||||
<p><a href="mailto:{{unbound to}}" title="TO">{{unbound to}}</a></p>
|
||||
{{/each}}
|
||||
{{#each cc in email.cc_addresses}}
|
||||
<p><a href="mailto:{{unbound cc}}" title="CC">{{unbound cc}}</a></p>
|
||||
{{/each}}
|
||||
</td>
|
||||
<td>
|
||||
{{#if email.post_url}}
|
||||
<a href="{{email.post_url}}">{{email.subject}}</a>
|
||||
{{else}}
|
||||
{{email.subject}}
|
||||
{{/if}}
|
||||
</td>
|
||||
</tr>
|
||||
{{else}}
|
||||
<tr><td colspan="4">{{i18n 'admin.email.incoming_emails.none'}}</td></tr>
|
||||
{{/each}}
|
||||
</td>
|
||||
</tr>
|
||||
{{else}}
|
||||
<tr><td colspan="4">{{i18n 'admin.email.incoming_emails.none'}}</td></tr>
|
||||
{{/each}}
|
||||
|
||||
</table>
|
||||
</table>
|
||||
{{/load-more}}
|
||||
|
||||
{{conditional-loading-spinner condition=view.loading}}
|
||||
{{conditional-loading-spinner condition=loading}}
|
||||
|
|
|
@ -1,54 +1,56 @@
|
|||
<table class='table email-list'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{i18n 'admin.email.time'}}</th>
|
||||
<th>{{i18n 'admin.email.incoming_emails.from_address'}}</th>
|
||||
<th>{{i18n 'admin.email.incoming_emails.to_addresses'}}</th>
|
||||
<th>{{i18n 'admin.email.incoming_emails.subject'}}</th>
|
||||
<th>{{i18n 'admin.email.incoming_emails.error'}}</th>
|
||||
{{#load-more selector=".email-list tr" action="loadMore"}}
|
||||
<table class='table email-list'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{i18n 'admin.email.time'}}</th>
|
||||
<th>{{i18n 'admin.email.incoming_emails.from_address'}}</th>
|
||||
<th>{{i18n 'admin.email.incoming_emails.to_addresses'}}</th>
|
||||
<th>{{i18n 'admin.email.incoming_emails.subject'}}</th>
|
||||
<th>{{i18n 'admin.email.incoming_emails.error'}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tr class="filters">
|
||||
<td>{{i18n 'admin.email.logs.filters.title'}}</td>
|
||||
<td>{{text-field value=filter.from placeholderKey="admin.email.incoming_emails.filters.from_placeholder"}}</td>
|
||||
<td>{{text-field value=filter.to placeholderKey="admin.email.incoming_emails.filters.to_placeholder"}}</td>
|
||||
<td>{{text-field value=filter.subject placeholderKey="admin.email.incoming_emails.filters.subject_placeholder"}}</td>
|
||||
<td>{{text-field value=filter.error placeholderKey="admin.email.incoming_emails.filters.error_placeholder"}}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tr class="filters">
|
||||
<td>{{i18n 'admin.email.logs.filters.title'}}</td>
|
||||
<td>{{text-field value=filter.from placeholderKey="admin.email.incoming_emails.filters.from_placeholder"}}</td>
|
||||
<td>{{text-field value=filter.to placeholderKey="admin.email.incoming_emails.filters.to_placeholder"}}</td>
|
||||
<td>{{text-field value=filter.subject placeholderKey="admin.email.incoming_emails.filters.subject_placeholder"}}</td>
|
||||
<td>{{text-field value=filter.error placeholderKey="admin.email.incoming_emails.filters.error_placeholder"}}</td>
|
||||
</tr>
|
||||
{{#each email in model}}
|
||||
<tr>
|
||||
<td class="time">{{format-date email.created_at}}</td>
|
||||
<td class="username">
|
||||
<div>
|
||||
{{#if email.user}}
|
||||
{{#link-to 'adminUser' email.user}}
|
||||
{{avatar email.user imageSize="tiny"}}
|
||||
{{email.from_address}}
|
||||
{{/link-to}}
|
||||
{{else}}
|
||||
—
|
||||
{{/if}}
|
||||
</div>
|
||||
</td>
|
||||
<td class="addresses">
|
||||
{{#each to in email.to_addresses}}
|
||||
<p><a href="mailto:{{unbound to}}" title="TO">{{unbound to}}</a></p>
|
||||
{{/each}}
|
||||
{{#each cc in email.cc_addresses}}
|
||||
<p><a href="mailto:{{unbound cc}}" title="CC">{{unbound cc}}</a></p>
|
||||
{{/each}}
|
||||
</td>
|
||||
<td>{{email.subject}}</td>
|
||||
<td class="error">
|
||||
<a {{action "showIncomingEmail" email.id}}>{{email.error}}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{{else}}
|
||||
<tr><td colspan="5">{{i18n 'admin.email.incoming_emails.none'}}</td></tr>
|
||||
{{/each}}
|
||||
|
||||
{{#each email in model}}
|
||||
<tr>
|
||||
<td class="time">{{format-date email.created_at}}</td>
|
||||
<td class="username">
|
||||
<div>
|
||||
{{#if email.user}}
|
||||
{{#link-to 'adminUser' email.user}}
|
||||
{{avatar email.user imageSize="tiny"}}
|
||||
{{email.from_address}}
|
||||
{{/link-to}}
|
||||
{{else}}
|
||||
—
|
||||
{{/if}}
|
||||
</div>
|
||||
</td>
|
||||
<td class="addresses">
|
||||
{{#each to in email.to_addresses}}
|
||||
<p><a href="mailto:{{unbound to}}" title="TO">{{unbound to}}</a></p>
|
||||
{{/each}}
|
||||
{{#each cc in email.cc_addresses}}
|
||||
<p><a href="mailto:{{unbound cc}}" title="CC">{{unbound cc}}</a></p>
|
||||
{{/each}}
|
||||
</td>
|
||||
<td>{{email.subject}}</td>
|
||||
<td class="error">
|
||||
<a {{action "showIncomingEmail" email.id}}>{{email.error}}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{{else}}
|
||||
<tr><td colspan="5">{{i18n 'admin.email.incoming_emails.none'}}</td></tr>
|
||||
{{/each}}
|
||||
</table>
|
||||
{{/load-more}}
|
||||
|
||||
</table>
|
||||
|
||||
{{conditional-loading-spinner condition=view.loading}}
|
||||
{{conditional-loading-spinner condition=loading}}
|
||||
|
|
|
@ -1,47 +1,49 @@
|
|||
<table class='table email-list'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{i18n 'admin.email.sent_at'}}</th>
|
||||
<th>{{i18n 'admin.email.user'}}</th>
|
||||
<th>{{i18n 'admin.email.to_address'}}</th>
|
||||
<th>{{i18n 'admin.email.email_type'}}</th>
|
||||
<th>{{i18n 'admin.email.reply_key'}}</th>
|
||||
{{#load-more selector=".email-list tr" action="loadMore"}}
|
||||
<table class='table email-list'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{i18n 'admin.email.sent_at'}}</th>
|
||||
<th>{{i18n 'admin.email.user'}}</th>
|
||||
<th>{{i18n 'admin.email.to_address'}}</th>
|
||||
<th>{{i18n 'admin.email.email_type'}}</th>
|
||||
<th>{{i18n 'admin.email.reply_key'}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tr class="filters">
|
||||
<td>{{i18n 'admin.email.logs.filters.title'}}</td>
|
||||
<td>{{text-field value=filter.user placeholderKey="admin.email.logs.filters.user_placeholder"}}</td>
|
||||
<td>{{text-field value=filter.address placeholderKey="admin.email.logs.filters.address_placeholder"}}</td>
|
||||
<td>{{text-field value=filter.type placeholderKey="admin.email.logs.filters.type_placeholder"}}</td>
|
||||
<td>{{text-field value=filter.reply_key placeholderKey="admin.email.logs.filters.reply_key_placeholder"}}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tr class="filters">
|
||||
<td>{{i18n 'admin.email.logs.filters.title'}}</td>
|
||||
<td>{{text-field value=filter.user placeholderKey="admin.email.logs.filters.user_placeholder"}}</td>
|
||||
<td>{{text-field value=filter.address placeholderKey="admin.email.logs.filters.address_placeholder"}}</td>
|
||||
<td>{{text-field value=filter.type placeholderKey="admin.email.logs.filters.type_placeholder"}}</td>
|
||||
<td>{{text-field value=filter.reply_key placeholderKey="admin.email.logs.filters.reply_key_placeholder"}}</td>
|
||||
</tr>
|
||||
{{#each l in model}}
|
||||
<tr>
|
||||
<td>{{format-date l.created_at}}</td>
|
||||
<td>
|
||||
{{#if l.user}}
|
||||
{{#link-to 'adminUser' l.user}}{{avatar l.user imageSize="tiny"}}{{/link-to}}
|
||||
{{#link-to 'adminUser' l.user}}{{l.user.username}}{{/link-to}}
|
||||
{{else}}
|
||||
—
|
||||
{{/if}}
|
||||
</td>
|
||||
<td><a href='mailto:{{unbound l.to_address}}'>{{l.to_address}}</a></td>
|
||||
<td>{{l.email_type}}</td>
|
||||
<td>
|
||||
{{#if l.post_url}}
|
||||
<a href="{{l.post_url}}">{{l.reply_key}}</a>
|
||||
{{else}}
|
||||
{{l.reply_key}}
|
||||
{{/if}}
|
||||
</td>
|
||||
</tr>
|
||||
{{else}}
|
||||
<tr><td colspan="5">{{i18n 'admin.email.logs.none'}}</td></tr>
|
||||
{{/each}}
|
||||
|
||||
{{#each l in model}}
|
||||
<tr>
|
||||
<td>{{format-date l.created_at}}</td>
|
||||
<td>
|
||||
{{#if l.user}}
|
||||
{{#link-to 'adminUser' l.user}}{{avatar l.user imageSize="tiny"}}{{/link-to}}
|
||||
{{#link-to 'adminUser' l.user}}{{l.user.username}}{{/link-to}}
|
||||
{{else}}
|
||||
—
|
||||
{{/if}}
|
||||
</td>
|
||||
<td><a href='mailto:{{unbound l.to_address}}'>{{l.to_address}}</a></td>
|
||||
<td>{{l.email_type}}</td>
|
||||
<td>
|
||||
{{#if l.post_url}}
|
||||
<a href="{{l.post_url}}">{{l.reply_key}}</a>
|
||||
{{else}}
|
||||
{{l.reply_key}}
|
||||
{{/if}}
|
||||
</td>
|
||||
</tr>
|
||||
{{else}}
|
||||
<tr><td colspan="5">{{i18n 'admin.email.logs.none'}}</td></tr>
|
||||
{{/each}}
|
||||
</table>
|
||||
{{/load-more}}
|
||||
|
||||
</table>
|
||||
|
||||
{{conditional-loading-spinner condition=view.loading}}
|
||||
{{conditional-loading-spinner condition=loading}}
|
||||
|
|
|
@ -1,47 +1,49 @@
|
|||
<table class='table email-list'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{i18n 'admin.email.time'}}</th>
|
||||
<th>{{i18n 'admin.email.user'}}</th>
|
||||
<th>{{i18n 'admin.email.to_address'}}</th>
|
||||
<th>{{i18n 'admin.email.email_type'}}</th>
|
||||
<th>{{i18n 'admin.email.skipped_reason'}}</th>
|
||||
{{#load-more selector=".email-list tr" action="loadMore"}}
|
||||
<table class='table email-list'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{i18n 'admin.email.time'}}</th>
|
||||
<th>{{i18n 'admin.email.user'}}</th>
|
||||
<th>{{i18n 'admin.email.to_address'}}</th>
|
||||
<th>{{i18n 'admin.email.email_type'}}</th>
|
||||
<th>{{i18n 'admin.email.skipped_reason'}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tr class="filters">
|
||||
<td>{{i18n 'admin.email.logs.filters.title'}}</td>
|
||||
<td>{{text-field value=filter.user placeholderKey="admin.email.logs.filters.user_placeholder"}}</td>
|
||||
<td>{{text-field value=filter.address placeholderKey="admin.email.logs.filters.address_placeholder"}}</td>
|
||||
<td>{{text-field value=filter.type placeholderKey="admin.email.logs.filters.type_placeholder"}}</td>
|
||||
<td>{{text-field value=filter.skipped_reason placeholderKey="admin.email.logs.filters.skipped_reason_placeholder"}}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tr class="filters">
|
||||
<td>{{i18n 'admin.email.logs.filters.title'}}</td>
|
||||
<td>{{text-field value=filter.user placeholderKey="admin.email.logs.filters.user_placeholder"}}</td>
|
||||
<td>{{text-field value=filter.address placeholderKey="admin.email.logs.filters.address_placeholder"}}</td>
|
||||
<td>{{text-field value=filter.type placeholderKey="admin.email.logs.filters.type_placeholder"}}</td>
|
||||
<td>{{text-field value=filter.skipped_reason placeholderKey="admin.email.logs.filters.skipped_reason_placeholder"}}</td>
|
||||
</tr>
|
||||
{{#each l in model}}
|
||||
<tr>
|
||||
<td>{{format-date l.created_at}}</td>
|
||||
<td>
|
||||
{{#if l.user}}
|
||||
{{#link-to 'adminUser' l.user}}{{avatar l.user imageSize="tiny"}}{{/link-to}}
|
||||
{{#link-to 'adminUser' l.user}}{{l.user.username}}{{/link-to}}
|
||||
{{else}}
|
||||
—
|
||||
{{/if}}
|
||||
</td>
|
||||
<td><a href='mailto:{{unbound l.to_address}}'>{{l.to_address}}</a></td>
|
||||
<td>{{l.email_type}}</td>
|
||||
<td>
|
||||
{{#if l.post_url}}
|
||||
<a href="{{l.post_url}}">{{l.skipped_reason}}</a>
|
||||
{{else}}
|
||||
{{l.skipped_reason}}
|
||||
{{/if}}
|
||||
</td>
|
||||
</tr>
|
||||
{{else}}
|
||||
<tr><td colspan="5">{{i18n 'admin.email.logs.none'}}</td></tr>
|
||||
{{/each}}
|
||||
|
||||
{{#each l in model}}
|
||||
<tr>
|
||||
<td>{{format-date l.created_at}}</td>
|
||||
<td>
|
||||
{{#if l.user}}
|
||||
{{#link-to 'adminUser' l.user}}{{avatar l.user imageSize="tiny"}}{{/link-to}}
|
||||
{{#link-to 'adminUser' l.user}}{{l.user.username}}{{/link-to}}
|
||||
{{else}}
|
||||
—
|
||||
{{/if}}
|
||||
</td>
|
||||
<td><a href='mailto:{{unbound l.to_address}}'>{{l.to_address}}</a></td>
|
||||
<td>{{l.email_type}}</td>
|
||||
<td>
|
||||
{{#if l.post_url}}
|
||||
<a href="{{l.post_url}}">{{l.skipped_reason}}</a>
|
||||
{{else}}
|
||||
{{l.skipped_reason}}
|
||||
{{/if}}
|
||||
</td>
|
||||
</tr>
|
||||
{{else}}
|
||||
<tr><td colspan="5">{{i18n 'admin.email.logs.none'}}</td></tr>
|
||||
{{/each}}
|
||||
</table>
|
||||
{{/load-more}}
|
||||
|
||||
</table>
|
||||
|
||||
{{conditional-loading-spinner condition=view.loading}}
|
||||
{{conditional-loading-spinner condition=loading}}
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
import LoadMore from "discourse/mixins/load-more";
|
||||
|
||||
export default Ember.View.extend(LoadMore, {
|
||||
loading: false,
|
||||
eyelineSelector: ".email-list tr",
|
||||
|
||||
actions: {
|
||||
loadMore() {
|
||||
if (this.get("loading") || this.get("model.allLoaded")) { return; }
|
||||
this.set("loading", true);
|
||||
return this.get("controller").loadMore().then(() => this.set("loading", false));
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,14 +0,0 @@
|
|||
import LoadMore from "discourse/mixins/load-more";
|
||||
|
||||
export default Ember.View.extend(LoadMore, {
|
||||
loading: false,
|
||||
eyelineSelector: ".email-list tr",
|
||||
|
||||
actions: {
|
||||
loadMore() {
|
||||
if (this.get("loading") || this.get("model.allLoaded")) { return; }
|
||||
this.set("loading", true);
|
||||
return this.get("controller").loadMore().then(() => this.set("loading", false));
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,5 +0,0 @@
|
|||
import AdminEmailIncomingsView from "admin/views/admin-email-incomings";
|
||||
|
||||
export default AdminEmailIncomingsView.extend({
|
||||
templateName: "admin/templates/email-received"
|
||||
});
|
|
@ -1,5 +0,0 @@
|
|||
import AdminEmailIncomingsView from "admin/views/admin-email-incomings";
|
||||
|
||||
export default AdminEmailIncomingsView.extend({
|
||||
templateName: "admin/templates/email-rejected"
|
||||
});
|
|
@ -1,5 +0,0 @@
|
|||
import AdminEmailLogsView from "admin/views/admin-email-logs";
|
||||
|
||||
export default AdminEmailLogsView.extend({
|
||||
templateName: "admin/templates/email-sent"
|
||||
});
|
|
@ -1,5 +0,0 @@
|
|||
import AdminEmailLogsView from "admin/views/admin-email-logs";
|
||||
|
||||
export default AdminEmailLogsView.extend({
|
||||
templateName: "admin/templates/email-skipped"
|
||||
});
|
|
@ -0,0 +1,23 @@
|
|||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
size: 'medium',
|
||||
classNameBindings: [':badge-card', 'size'],
|
||||
|
||||
@computed('count', 'badge.grant_count')
|
||||
displayCount(count, grantCount) {
|
||||
const c = parseInt(count || grantCount || 0);
|
||||
if (c > 1) {
|
||||
return c;
|
||||
}
|
||||
},
|
||||
|
||||
@computed('size')
|
||||
summary(size) {
|
||||
if (size === 'large') {
|
||||
return Discourse.Emoji.unescape(this.get('badge.long_description') || '');
|
||||
}
|
||||
return this.get('badge.displayDescriptionHtml');
|
||||
}
|
||||
|
||||
});
|
|
@ -0,0 +1,16 @@
|
|||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
tagName: 'span',
|
||||
classNameBindings: [':check-display', 'status'],
|
||||
|
||||
@computed('checked')
|
||||
status(checked) {
|
||||
return checked ? 'status-checked' : 'status-unchecked';
|
||||
},
|
||||
|
||||
render(buffer) {
|
||||
const icon = this.get('checked') ? 'check' : 'times';
|
||||
buffer.push(`<i class='fa fa-${icon}'></i>`);
|
||||
}
|
||||
});
|
|
@ -27,9 +27,9 @@ export default Ember.Component.extend(StringBuffer, {
|
|||
|
||||
if (notices.length > 0) {
|
||||
buffer.push(_.map(notices, n => {
|
||||
var html = `<div class='row'><div class='alert alert-info ${n[1]}'>${n[0]}`;
|
||||
var html = `<div class='row'><div class='alert alert-info ${n[1]}'>`;
|
||||
if (n[2]) html += n[2];
|
||||
html += '</div></div>';
|
||||
html += `${n[0]}</div></div>`;
|
||||
return html;
|
||||
}).join(""));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
import LoadMore from "discourse/mixins/load-more";
|
||||
|
||||
export default Ember.Component.extend(LoadMore, {
|
||||
_viaComponent: true,
|
||||
|
||||
init() {
|
||||
this._super();
|
||||
this.set('eyelineSelector', this.get('selector'));
|
||||
},
|
||||
|
||||
actions: {
|
||||
loadMore() {
|
||||
this.sendAction();
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,23 @@
|
|||
import { url } from 'discourse/lib/computed';
|
||||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
|
||||
function normalize(name) {
|
||||
return name.replace(/[\-\_ \.]/g, '').toLowerCase();
|
||||
}
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNameBindings: [':user-info', 'size'],
|
||||
size: 'small',
|
||||
userPath: url('user.username', '/users/%@'),
|
||||
|
||||
// TODO: In later ember releases `hasBlock` works without this
|
||||
hasBlock: Ember.computed.alias('template'),
|
||||
|
||||
@computed('user.name', 'user.username')
|
||||
name(name, username) {
|
||||
if (name && normalize(username) !== normalize(name)) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
|
@ -1,15 +0,0 @@
|
|||
import { url } from 'discourse/lib/computed';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: ['user-small'],
|
||||
|
||||
userPath: url('user.username', '/users/%@'),
|
||||
|
||||
name: function() {
|
||||
const name = this.get('user.name');
|
||||
if (name && this.get('user.username') !== name) {
|
||||
return name;
|
||||
}
|
||||
}.property('user.name')
|
||||
|
||||
});
|
|
@ -1,4 +1,5 @@
|
|||
import UserBadge from 'discourse/models/user-badge';
|
||||
import { default as computed, observes } from 'ember-addons/ember-computed-decorators';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
queryParams: ['username'],
|
||||
|
@ -6,67 +7,43 @@ export default Ember.Controller.extend({
|
|||
userBadges: null,
|
||||
needs: ["application"],
|
||||
|
||||
user: function(){
|
||||
if (this.get("username")) {
|
||||
@computed('username')
|
||||
user(username) {
|
||||
if (username) {
|
||||
return this.get('userBadges')[0].get('user');
|
||||
}
|
||||
}.property("username"),
|
||||
},
|
||||
|
||||
grantCount: function() {
|
||||
if (this.get("username")) {
|
||||
return this.get('userBadges.grant_count');
|
||||
} else {
|
||||
return this.get('model.grant_count');
|
||||
}
|
||||
}.property('username', 'model', 'userBadges'),
|
||||
@computed('username', 'model.grant_count', 'userBadges.grant_count')
|
||||
grantCount(username, modelCount, userCount) {
|
||||
return username ? userCount : modelCount;
|
||||
},
|
||||
|
||||
actions: {
|
||||
loadMore() {
|
||||
const self = this;
|
||||
const userBadges = this.get('userBadges');
|
||||
|
||||
UserBadge.findByBadgeId(this.get('model.id'), {
|
||||
offset: userBadges.length,
|
||||
username: this.get('username'),
|
||||
}).then(function(result) {
|
||||
}).then(result => {
|
||||
userBadges.pushObjects(result);
|
||||
if(userBadges.length === 0){
|
||||
self.set('noMoreBadges', true);
|
||||
if (userBadges.length === 0){
|
||||
this.set('noMoreBadges', true);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
layoutClass: function(){
|
||||
var user = this.get("user") ? " single-user" : "";
|
||||
var ub = this.get("userBadges");
|
||||
if(ub && ub[0] && ub[0].post_id){
|
||||
return "user-badge-with-posts" + user;
|
||||
} else {
|
||||
return "user-badge-no-posts" + user;
|
||||
}
|
||||
}.property("userBadges"),
|
||||
@computed('noMoreBadges', 'grantCount', 'userBadges.length')
|
||||
canLoadMore(noMoreBadges, grantCount, userBadgeLength) {
|
||||
if (noMoreBadges) { return false; }
|
||||
return grantCount > (userBadgeLength || 0);
|
||||
},
|
||||
|
||||
canLoadMore: function() {
|
||||
if (this.get('noMoreBadges')) { return false; }
|
||||
|
||||
if (this.get('userBadges')) {
|
||||
return this.get('grantCount') > this.get('userBadges.length');
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}.property('noMoreBadges', 'model.grant_count', 'userBadges.length'),
|
||||
|
||||
_showFooter: function() {
|
||||
@observes('canLoadMore')
|
||||
_showFooter() {
|
||||
this.set("controllers.application.showFooter", !this.get("canLoadMore"));
|
||||
}.observes("canLoadMore"),
|
||||
|
||||
longDescription: function(){
|
||||
return Discourse.Emoji.unescape(this.get('model.long_description'));
|
||||
}.property('model.long_description'),
|
||||
|
||||
showLongDescription: function(){
|
||||
return this.get('model.long_description');
|
||||
}.property('userBadges')
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
@ -169,11 +169,6 @@ Discourse.BBCode.replaceBBCodeParamsRaw("email", function(param, contents) {
|
|||
return ['a', {href: "mailto:" + param, 'data-bbcode': true}].concat(contents);
|
||||
});
|
||||
|
||||
Discourse.BBCode.register('size', function(contents, params) {
|
||||
return ['span', {'class': "bbcode-size-" + (parseInt(params, 10) || 1)}].concat(contents);
|
||||
});
|
||||
Discourse.Markdown.whiteListTag('span', 'class', /^bbcode-size-\d+$/);
|
||||
|
||||
// Handles `[code] ... [/code]` blocks
|
||||
Discourse.Dialect.replaceBlock({
|
||||
start: /(\[code\])([\s\S]*)/igm,
|
||||
|
|
|
@ -25,3 +25,5 @@ registerUnbound('format-date', function(val, params) {
|
|||
return new Handlebars.SafeString(autoUpdatingRelativeAge(date, {format: format, title: title, leaveAgo: leaveAgo}));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
import { relativeAge } from 'discourse/lib/formatter';
|
||||
|
||||
export default function(dt, params) {
|
||||
dt = params.data.view.getStream(dt).value();
|
||||
return relativeAge(new Date(dt));
|
||||
}
|
|
@ -2,12 +2,20 @@ import Eyeline from 'discourse/lib/eyeline';
|
|||
import Scrolling from 'discourse/mixins/scrolling';
|
||||
import { on } from 'ember-addons/ember-computed-decorators';
|
||||
|
||||
// Provides the ability to load more items for a view which is scrolled to the bottom.
|
||||
// Provides the ability to load more items for a view which is scrolled to the bottom.
|
||||
export default Ember.Mixin.create(Ember.ViewTargetActionSupport, Scrolling, {
|
||||
|
||||
init() {
|
||||
this._super();
|
||||
if (!this._viaComponent) {
|
||||
console.warn('Using `LoadMore` as a view mixin is deprecated. Use `{{load-more}}` instead');
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
scrolled() {
|
||||
const eyeline = this.get('eyeline');
|
||||
if (eyeline) { eyeline.update(); }
|
||||
return eyeline && eyeline.update();
|
||||
},
|
||||
|
||||
loadMoreUnlessFull() {
|
||||
|
|
|
@ -33,13 +33,6 @@ const Badge = RestModel.extend({
|
|||
return I18n.t(i18nKey, {defaultValue: this.get('name')});
|
||||
}.property('name', 'i18nNameKey'),
|
||||
|
||||
/**
|
||||
The i18n translated description for this badge. Returns the null if no
|
||||
translation exists.
|
||||
|
||||
@property translatedDescription
|
||||
@type {String}
|
||||
**/
|
||||
translatedDescription: function() {
|
||||
const i18nKey = "badges.badge." + this.get('i18nNameKey') + ".description";
|
||||
let translation = I18n.t(i18nKey);
|
||||
|
|
|
@ -6,13 +6,8 @@ const UserBadge = Discourse.Model.extend({
|
|||
return "/t/-/" + this.get('topic_id') + "/" + this.get('post_number');
|
||||
}
|
||||
}.property(), // avoid the extra bindings for now
|
||||
/**
|
||||
Revoke this badge.
|
||||
|
||||
@method revoke
|
||||
@returns {Promise} a promise that resolves when the badge has been revoked.
|
||||
**/
|
||||
revoke: function() {
|
||||
revoke() {
|
||||
return Discourse.ajax("/user_badges/" + this.get('id'), {
|
||||
type: "DELETE"
|
||||
});
|
||||
|
|
|
@ -15,10 +15,7 @@ export default Discourse.Route.extend({
|
|||
},
|
||||
|
||||
serialize(model) {
|
||||
return {
|
||||
id: model.get("id"),
|
||||
slug: model.get("slug")
|
||||
};
|
||||
return model.getProperties('id', 'slug');
|
||||
},
|
||||
|
||||
model(params) {
|
||||
|
@ -29,13 +26,12 @@ export default Discourse.Route.extend({
|
|||
}
|
||||
},
|
||||
|
||||
afterModel(model,transition) {
|
||||
afterModel(model, transition) {
|
||||
const username = transition.queryParams && transition.queryParams.username;
|
||||
|
||||
return UserBadge.findByBadgeId(model.get("id"), {username}).then(userBadges => {
|
||||
this.userBadges = userBadges;
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
titleToken() {
|
||||
|
|
|
@ -22,8 +22,8 @@
|
|||
<section class='about admins'>
|
||||
<h3>{{i18n 'about.our_admins'}}</h3>
|
||||
|
||||
{{#each a in model.admins}}
|
||||
{{user-small user=a}}
|
||||
{{#each model.admins as |a|}}
|
||||
{{user-info user=a}}
|
||||
{{/each}}
|
||||
<div class='clearfix'></div>
|
||||
|
||||
|
@ -35,8 +35,8 @@
|
|||
<h3>{{i18n 'about.our_moderators'}}</h3>
|
||||
|
||||
<div class='users'>
|
||||
{{#each m in model.moderators}}
|
||||
{{user-small user=m}}
|
||||
{{#each model.moderators as |m|}}
|
||||
{{user-info user=m}}
|
||||
{{/each}}
|
||||
</div>
|
||||
<div class='clearfix'></div>
|
||||
|
|
|
@ -1,21 +1,19 @@
|
|||
<div class='container badges'>
|
||||
<h1>{{i18n 'badges.title'}}</h1>
|
||||
|
||||
<table class='badges-listing'>
|
||||
<tbody>
|
||||
{{#each bg in badgeGroups}}
|
||||
<tr class='title'>
|
||||
<td colspan=4><h3>{{bg.badgeGrouping.displayName}}</h3></td>
|
||||
</tr>
|
||||
{{#each b in bg.badges}}
|
||||
<tr>
|
||||
<td class='granted'>{{#if b.has_badge}}<i class='fa fa-check'></i>{{/if}}</td>
|
||||
<td class='badge'>{{user-badge badge=b}}</td>
|
||||
<td class='description'>{{{b.displayDescriptionHtml}}}</td>
|
||||
<td class='grant-count'><span title="{{i18n 'badges.granted' count=b.grant_count}}">{{b.grant_count}}</span></td>
|
||||
</tr>
|
||||
<div class='badge-groups'>
|
||||
{{#each bg in badgeGroups}}
|
||||
<div class='badge-grouping'>
|
||||
<div class='title'>
|
||||
<h3>{{bg.badgeGrouping.displayName}}</h3>
|
||||
</div>
|
||||
|
||||
{{#each bg.badges as |b|}}
|
||||
{{#link-to 'badges.show' b.id b.slug}}
|
||||
{{badge-card badge=b}}
|
||||
{{/link-to}}
|
||||
{{/each}}
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -5,66 +5,40 @@
|
|||
{{model.displayName}}
|
||||
</h1>
|
||||
|
||||
{{#if showLongDescription}}
|
||||
<div class='long-description banner'>
|
||||
{{{longDescription}}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class='badges-listing'>
|
||||
<div class='row'>
|
||||
{{#unless user}}
|
||||
<div class='grant-count'>{{i18n 'badges.granted' count=grantCount}}</div>
|
||||
{{/unless}}
|
||||
<div class='info'>{{i18n 'badges.allow_title'}} {{{view.allowTitle}}}<br>{{i18n 'badges.multiple_grant'}} {{{view.multipleGrant}}}
|
||||
<div class='show-badge-details'>
|
||||
{{badge-card badge=model size="large"}}
|
||||
<div class='badge-grant-info'>
|
||||
<div>
|
||||
<div class='grant-info-item'>
|
||||
{{check-mark checked=model.allow_title}} {{i18n 'badges.allow_title'}}
|
||||
</div>
|
||||
<div class='grant-info-item'>
|
||||
{{check-mark checked=model.multiple_grant}} {{i18n 'badges.multiple_grant'}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
{{#if user}}
|
||||
<div class='badge-user-info'>
|
||||
{{#link-to 'user' user}}
|
||||
{{avatar user imageSize="extra_large"}}
|
||||
<div class="details clearfix">
|
||||
<div class='username'>{{user.username}}</div>
|
||||
</div>
|
||||
{{/link-to}}
|
||||
<div class='earned'>
|
||||
{{i18n 'badges.earned_n_times' count=grantCount}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if userBadges}}
|
||||
<div class={{unbound layoutClass}}>
|
||||
{{#each ub in userBadges}}
|
||||
<div class="badge-user">
|
||||
{{#if user}}
|
||||
{{format-date ub.granted_at}}
|
||||
{{else}}
|
||||
{{#link-to 'user' ub.user classNames="badge-info"}}
|
||||
{{avatar ub.user imageSize="large"}}
|
||||
<div class="details">
|
||||
<span class="username">{{ub.user.username}}</span>
|
||||
{{format-date ub.granted_at}}
|
||||
</div>
|
||||
{{/link-to}}
|
||||
{{/if}}
|
||||
|
||||
{{#if ub.post_number}}
|
||||
<a class="post-link" href="{{unbound ub.topic.url}}/{{unbound ub.post_number}}">{{{ub.topic.fancyTitle}}}</a>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/each}}
|
||||
<div class="user-badges">
|
||||
{{#load-more selector=".badge-info" action="loadMore"}}
|
||||
{{#each userBadges as |ub|}}
|
||||
{{#user-info user=ub.user size="medium" class="badge-info" date=ub.granted_at}}
|
||||
<div class="granted-on">{{i18n 'badges.granted_on' date=(inline-date ub.granted_at)}}</div>
|
||||
{{#if ub.post_number}}
|
||||
<a class="post-link" href="{{unbound ub.topic.url}}/{{unbound ub.post_number}}">{{{ub.topic.fancyTitle}}}</a>
|
||||
{{/if}}
|
||||
{{/user-info}}
|
||||
{{/each}}
|
||||
{{/load-more}}
|
||||
|
||||
{{#unless canLoadMore}}
|
||||
{{#if user}}
|
||||
<a class='load-more' href='{{model.url}}'>{{i18n 'badges.more_with_badge'}}</a>
|
||||
<div class='clearfix'>
|
||||
<a class='btn' href='{{model.url}}'>{{i18n 'badges.others_count' count=model.grant_count}}</a>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
|
||||
</div>
|
||||
|
||||
{{conditional-loading-spinner condition=canLoadMore}}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
{{#if displayCount}}
|
||||
<span class='grant-count' title={{i18n 'badges.granted' count=displayCount}}>{{displayCount}}</span>
|
||||
{{/if}}
|
||||
{{#if badge.has_badge}}
|
||||
<span class='check-display status-checked'>{{fa-icon "check"}}</span>
|
||||
{{/if}}
|
||||
<div class='badge-contents'>
|
||||
<div class='badge-icon {{badge.badgeTypeClassName}}'>
|
||||
{{icon-or-image badge.icon}}
|
||||
</div>
|
||||
<div class='badge-info'>
|
||||
<div class='badge-info-item'>
|
||||
<h3>{{badge.displayName}}</h3>
|
||||
<div class='badge-summary'>{{{summary}}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -8,4 +8,11 @@
|
|||
<span class="name">{{unbound name}}</span>
|
||||
</div>
|
||||
<div class="title">{{unbound user.title}}</div>
|
||||
|
||||
{{#if hasBlock}}
|
||||
<div class='details'>
|
||||
{{yield}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
</div>
|
|
@ -18,7 +18,7 @@
|
|||
{{#each model.members as |m|}}
|
||||
<tr>
|
||||
<td class='avatar'>
|
||||
{{user-small user=m}}
|
||||
{{user-info user=m}}
|
||||
{{#if m.owner}}<span class='is-owner'>{{i18n "groups.owner"}}</span>{{/if}}
|
||||
</td>
|
||||
<td>
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
{{#each ic in model itemController="directory-item"}}
|
||||
<div class="user {{if ic.me 'me'}}">
|
||||
{{#with ic.model as |it|}}
|
||||
{{user-small user=it.user}}
|
||||
{{user-info user=it.user}}
|
||||
{{user-stat value=it.likes_received label="directory.likes_received" icon="heart"}}
|
||||
{{user-stat value=it.likes_given label="directory.likes_given" icon="heart"}}
|
||||
{{user-stat value=it.topic_count label="directory.topic_count"}}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
<section class='user-content user-badges-list'>
|
||||
{{#each ub in controller}}
|
||||
{{user-badge badge=ub.badge count=ub.count user=user}}
|
||||
{{#each controller as |ub|}}
|
||||
{{#link-to 'badges.show' ub.badge.id ub.badge.slug (query-params username=user.username_lower)}}
|
||||
{{badge-card badge=ub.badge count=ub.count}}
|
||||
{{/link-to}}
|
||||
{{/each}}
|
||||
</section>
|
||||
|
|
|
@ -278,7 +278,7 @@
|
|||
|
||||
{{plugin-outlet "user-custom-controls"}}
|
||||
|
||||
<div class="control-group">
|
||||
<div class="control-group save-button">
|
||||
<div class="controls">
|
||||
{{partial 'user/preferences/save-button'}}
|
||||
</div>
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
{{#each ic in model itemController="directory-item"}}
|
||||
<tr class="{{if ic.me 'me'}}">
|
||||
{{#with ic.model as |it|}}
|
||||
<td>{{user-small user=it.user}}</td>
|
||||
<td>{{user-info user=it.user}}</td>
|
||||
<td>{{number it.likes_received}}</td>
|
||||
<td>{{number it.likes_given}}</td>
|
||||
<td>{{number it.topic_count}}</td>
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
import LoadMore from "discourse/mixins/load-more";
|
||||
|
||||
export default Ember.View.extend(LoadMore, {
|
||||
eyelineSelector: '.badge-user',
|
||||
tickOrX: function(field){
|
||||
var icon = this.get('controller.model.' + field) ? "fa-check" : "fa-times";
|
||||
return "<i class='fa " + icon + "'></i>";
|
||||
},
|
||||
allowTitle: function() { return this.tickOrX("allow_title"); }.property(),
|
||||
multipleGrant: function() { return this.tickOrX("multiple_grant"); }.property()
|
||||
});
|
|
@ -13,10 +13,4 @@ span {
|
|||
&.bbcode-s {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
// Font sizes
|
||||
@for $i from 4 through 40 {
|
||||
&.bbcode-size-#{$i} {
|
||||
font-size: #{$i}px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
.directory {
|
||||
margin-bottom: 100px;
|
||||
|
||||
.user-info {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.period-chooser {
|
||||
float: left;
|
||||
}
|
||||
|
|
|
@ -32,84 +32,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
/* User badge listing. */
|
||||
.user-badges-list {
|
||||
text-align: center;
|
||||
|
||||
.user-badge {
|
||||
max-width: 80px;
|
||||
text-align: center;
|
||||
vertical-align: top;
|
||||
margin: 10px;
|
||||
border: none;
|
||||
|
||||
.fa {
|
||||
display: block;
|
||||
font-size: 3.571em;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
img {
|
||||
display: block;
|
||||
margin: auto auto 4px;
|
||||
width: 55px;
|
||||
height: 55px;
|
||||
}
|
||||
|
||||
.count {
|
||||
display: block;
|
||||
font-size: 0.8em;
|
||||
color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Badge listing in /badges. */
|
||||
.badges-listing {
|
||||
margin: 20px 0;
|
||||
tr {
|
||||
border-bottom: 1px solid dark-light-diff($primary, $secondary, 90%, -60%);
|
||||
td {
|
||||
padding: 10px 0;
|
||||
}
|
||||
}
|
||||
|
||||
border-bottom: 1px solid dark-light-diff($primary, $secondary, 90%, -60%);
|
||||
width: 90%;
|
||||
padding: 10px;
|
||||
display: table;
|
||||
color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 60%));
|
||||
|
||||
.row {
|
||||
display: table-row;
|
||||
> div {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.user-badge {
|
||||
font-size: $base-font-size;
|
||||
}
|
||||
|
||||
.grant-count {
|
||||
font-size: 120%;
|
||||
}
|
||||
|
||||
.badge, .grant-count {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.info {
|
||||
font-size: 0.9em;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.description {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media all and (max-width: 750px) {
|
||||
.show-badge .user-badge-with-posts .badge-user a.post-link {
|
||||
width: auto;
|
||||
|
@ -135,66 +57,23 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
.user-info.medium.badge-info {
|
||||
min-height: 80px;
|
||||
|
||||
|
||||
/* /badges/:id/:slug page styling. */
|
||||
.show-badge {
|
||||
.badge-user {
|
||||
text-align: center;
|
||||
width: 100px;
|
||||
padding: 5px 10px;
|
||||
margin-bottom: 10px;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
|
||||
.details {
|
||||
margin: 0 10px;
|
||||
padding-top: 3px;
|
||||
color: $primary;
|
||||
}
|
||||
|
||||
.username {
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.date {
|
||||
display: block;
|
||||
color: lighten($primary, 40%);
|
||||
font-size: 0.714em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.show-badge .user-badge-with-posts .badge-user {
|
||||
width: 45%;
|
||||
padding: 0 0 0 4%;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.badge-info {
|
||||
width: 100px;
|
||||
display: block;
|
||||
float: left;
|
||||
.granted-on {
|
||||
color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%));
|
||||
}
|
||||
|
||||
.post-link {
|
||||
width: 250px;
|
||||
display: block;
|
||||
float: left;
|
||||
margin-left: 18px;
|
||||
text-align: left;
|
||||
margin-top: 0.2em;
|
||||
}
|
||||
}
|
||||
|
||||
.show-badge .badge-user-info {
|
||||
margin-left: 2%;
|
||||
.earned {
|
||||
margin-top: 15px;
|
||||
font-size: 1.3em;
|
||||
}
|
||||
.username {
|
||||
margin-top: 5px;
|
||||
display: block;
|
||||
color: dark-light-choose(scale-color($primary, $lightness: 30%), scale-color($secondary, $lightness: 70%));
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -242,3 +121,123 @@
|
|||
margin-bottom: 15px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.badge-card {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
background-color: dark-light-diff($primary, $secondary, 95%, -65%);
|
||||
margin-right: 5px;
|
||||
margin-bottom: 10px;
|
||||
box-shadow: 1px 1px 3px rgba(0.0, 0.0, 0.0, 0.2);
|
||||
|
||||
.check-display {
|
||||
position: absolute;
|
||||
left: 5px;
|
||||
top: 5px;
|
||||
}
|
||||
|
||||
.grant-count {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
font-weight: bold;
|
||||
color: dark-light-diff($primary, $secondary, 50%, -65%);
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.badge-contents {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
min-height: 128px;
|
||||
|
||||
.badge-icon {
|
||||
min-width: 90px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: dark-light-diff($primary, $secondary, 92%, -60%);
|
||||
font-size: 3em;
|
||||
|
||||
&.badge-type-gold .fa {
|
||||
color: #ffd700 !important;
|
||||
}
|
||||
|
||||
&.badge-type-silver .fa {
|
||||
color: #c0c0c0 !important;
|
||||
}
|
||||
|
||||
&.badge-type-bronze .fa {
|
||||
color: #cd7f32 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.badge-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
color: $primary;
|
||||
|
||||
h3 {
|
||||
margin-bottom: 0.25em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.badge-card.medium {
|
||||
width: 350px;
|
||||
}
|
||||
|
||||
.badge-card.large {
|
||||
width: 750px;
|
||||
}
|
||||
|
||||
.badge-groups {
|
||||
margin: 20px 0;
|
||||
color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 60%));
|
||||
h3 {
|
||||
margin-bottom: 1.0em;
|
||||
}
|
||||
}
|
||||
|
||||
.badge-grouping {
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.show-badge-details {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-bottom: 2em;
|
||||
margin-top: 1em;
|
||||
|
||||
.badge-grant-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 1em;
|
||||
}
|
||||
.grant-info-item {
|
||||
margin-bottom: 1em;
|
||||
color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 60%));
|
||||
}
|
||||
}
|
||||
|
||||
.check-display {
|
||||
display: inline-block;
|
||||
width: 18px;
|
||||
border-radius: 10px;
|
||||
text-align: center;
|
||||
.fa {
|
||||
font-size: 0.9em;
|
||||
color: $secondary;
|
||||
}
|
||||
}
|
||||
|
||||
.check-display.status-checked {
|
||||
background-color: $success;
|
||||
}
|
||||
|
||||
.check-display.status-unchecked {
|
||||
background-color: $danger;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,14 @@
|
|||
i.fa-heart {
|
||||
color: $love !important;
|
||||
}
|
||||
.nav-pills {
|
||||
a {
|
||||
color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 40%));
|
||||
}
|
||||
i {
|
||||
color: dark-light-choose(scale-color($primary, $lightness: 55%), scale-color($secondary, $lightness: 55%));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-field {
|
||||
|
@ -92,10 +100,10 @@
|
|||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.user-small {
|
||||
.user-info {
|
||||
display: inline-block;
|
||||
width: 333px;
|
||||
clear: both;
|
||||
margin-bottom: 1em;
|
||||
|
||||
.user-image {
|
||||
float: left;
|
||||
|
@ -128,7 +136,30 @@
|
|||
margin-top: 3px;
|
||||
color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-info.small {
|
||||
width: 333px;
|
||||
}
|
||||
|
||||
.user-info.medium {
|
||||
width: 480px;
|
||||
min-height: 60px;
|
||||
|
||||
.user-image {
|
||||
width: 55px;
|
||||
}
|
||||
.user-detail {
|
||||
width: 380px;
|
||||
}
|
||||
|
||||
.username, .name {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.name {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -181,10 +181,10 @@ input {
|
|||
bottom: 35px;
|
||||
}
|
||||
.submit-panel {
|
||||
width: 50%;
|
||||
// don't specify width; needs to auto-size for smallest phones
|
||||
position: absolute;
|
||||
display: block;
|
||||
bottom: 2px;
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
.category-input {
|
||||
|
|
|
@ -14,11 +14,6 @@
|
|||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.topic-post article {
|
||||
border-top: 1px solid dark-light-diff($primary, $secondary, 90%, -60%);
|
||||
padding: 6px 0;
|
||||
}
|
||||
|
||||
.post-stream {
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
|
@ -287,7 +282,7 @@ a.star {
|
|||
.btn {
|
||||
border: 0;
|
||||
padding: 0 15px;
|
||||
color: $primary;
|
||||
color: dark-light-choose(scale-color($primary, $lightness: 60%), scale-color($secondary, $lightness: 40%));
|
||||
background: blend-primary-secondary(5%);
|
||||
border-left: 1px solid dark-light-diff($primary, $secondary, 90%, -65%);
|
||||
border-top: 1px solid dark-light-diff($primary, $secondary, 90%, -65%);
|
||||
|
|
|
@ -56,6 +56,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
.delete-account {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.checkbox-label {
|
||||
overflow: auto;
|
||||
display: block;
|
||||
|
@ -169,18 +173,15 @@
|
|||
color: $primary;
|
||||
}
|
||||
}
|
||||
.user-small {
|
||||
.user-info {
|
||||
width: 245px;
|
||||
}
|
||||
}
|
||||
|
||||
.user-content {
|
||||
padding: 10px 8px;
|
||||
background-color: $secondary;
|
||||
border: 1px solid dark-light-diff($primary, $secondary, 90%, -60%);
|
||||
margin-bottom: 10px;
|
||||
box-sizing: border-box;
|
||||
margin-top: 20px;
|
||||
margin-top: 10px;
|
||||
|
||||
.btn.right {
|
||||
float: right
|
||||
|
@ -393,7 +394,6 @@
|
|||
|
||||
|
||||
.user-stream {
|
||||
padding: 0 10px;
|
||||
.excerpt {
|
||||
margin: 5px 0;
|
||||
font-size: 0.929em;
|
||||
|
@ -596,3 +596,13 @@
|
|||
.notification-buttons {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
// mobile fixups for badges
|
||||
|
||||
.badge-card.medium {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.show-badge-details .badge-grant-info {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -282,9 +282,13 @@ class Admin::UsersController < Admin::AdminController
|
|||
return render nothing: true, status: 404 unless SiteSetting.enable_sso
|
||||
|
||||
sso = DiscourseSingleSignOn.parse("sso=#{params[:sso]}&sig=#{params[:sig]}")
|
||||
user = sso.lookup_or_create_user
|
||||
|
||||
render_serialized(user, AdminDetailedUserSerializer, root: false)
|
||||
begin
|
||||
user = sso.lookup_or_create_user
|
||||
render_serialized(user, AdminDetailedUserSerializer, root: false)
|
||||
rescue ActiveRecord::RecordInvalid => ex
|
||||
render json: failed_json.merge(message: ex.message), status: 403
|
||||
end
|
||||
end
|
||||
|
||||
def delete_other_accounts_with_same_ip
|
||||
|
|
|
@ -3,7 +3,6 @@ class CategoryHashtagsController < ApplicationController
|
|||
|
||||
def check
|
||||
category_slugs = params[:category_slugs]
|
||||
category_slugs.each(&:downcase!)
|
||||
|
||||
ids = category_slugs.map { |category_slug| Category.query_from_hashtag_slug(category_slug).try(:id) }
|
||||
|
||||
|
|
|
@ -129,7 +129,7 @@ class UserAvatarsController < ApplicationController
|
|||
|
||||
unless File.exist? path
|
||||
FileUtils.mkdir_p PROXY_PATH
|
||||
tmp = FileHelper.download(url, 1.megabyte, filename, true)
|
||||
tmp = FileHelper.download(url, 1.megabyte, filename, true, 10)
|
||||
FileUtils.mv tmp.path, path
|
||||
end
|
||||
|
||||
|
|
|
@ -96,9 +96,12 @@ module Jobs
|
|||
process_popmail(p)
|
||||
end
|
||||
end
|
||||
rescue Net::OpenTimeout => e
|
||||
mark_as_errored!
|
||||
Discourse.handle_job_exception(e, error_context(@args, "Connecting to '#{SiteSetting.pop3_polling_host}' for polling emails."))
|
||||
rescue Net::POPAuthenticationError => e
|
||||
mark_as_errored!
|
||||
Discourse.handle_job_exception(e, error_context(@args, "Signing in to poll incoming email"))
|
||||
Discourse.handle_job_exception(e, error_context(@args, "Signing in to poll incoming emails."))
|
||||
end
|
||||
|
||||
POLL_MAILBOX_ERRORS_KEY ||= "poll_mailbox_errors".freeze
|
||||
|
|
|
@ -173,12 +173,14 @@ class UserNotifications < ActionMailer::Base
|
|||
end
|
||||
|
||||
def user_invited_to_private_message(user, opts)
|
||||
opts[:use_template_html] = true
|
||||
opts[:allow_reply_by_email] = false
|
||||
opts[:use_invite_template] = true
|
||||
notification_email(user, opts)
|
||||
end
|
||||
|
||||
def user_invited_to_topic(user, opts)
|
||||
opts[:use_template_html] = true
|
||||
opts[:allow_reply_by_email] = false
|
||||
opts[:use_invite_template] = true
|
||||
opts[:show_category_in_subject] = true
|
||||
notification_email(user, opts)
|
||||
end
|
||||
|
@ -273,7 +275,7 @@ class UserNotifications < ActionMailer::Base
|
|||
add_re_to_subject: opts[:add_re_to_subject],
|
||||
show_category_in_subject: opts[:show_category_in_subject],
|
||||
notification_type: notification_type,
|
||||
use_template_html: opts[:use_template_html],
|
||||
use_invite_template: opts[:use_invite_template],
|
||||
user: user
|
||||
)
|
||||
end
|
||||
|
@ -322,9 +324,21 @@ class UserNotifications < ActionMailer::Base
|
|||
.where('created_at > ?', 1.day.ago)
|
||||
.count) >= (SiteSetting.max_emails_per_day_per_user-1)
|
||||
|
||||
topic_excerpt = ""
|
||||
if opts[:use_template_html]
|
||||
if opts[:use_invite_template]
|
||||
if post.topic.private_message?
|
||||
invite_template = "user_notifications.invited_to_private_message_body"
|
||||
else
|
||||
invite_template = "user_notifications.invited_to_topic_body"
|
||||
end
|
||||
topic_excerpt = post.excerpt.gsub("\n", " ") if post.is_first_post? && post.excerpt
|
||||
message = I18n.t(invite_template, username: post.username, topic_title: title, topic_excerpt: topic_excerpt, site_title: SiteSetting.title, site_description: SiteSetting.site_description)
|
||||
html = UserNotificationRenderer.new(Rails.configuration.paths["app/views"]).render(
|
||||
template: 'email/invite',
|
||||
format: :html,
|
||||
locals: { message: PrettyText.cook(message, sanitize: false).html_safe,
|
||||
classes: RTL.new(user).css_class
|
||||
}
|
||||
)
|
||||
else
|
||||
in_reply_to_post = post.reply_to_post if user.user_option.email_in_reply_to
|
||||
html = UserNotificationRenderer.new(Rails.configuration.paths["app/views"]).render(
|
||||
|
@ -337,6 +351,7 @@ class UserNotifications < ActionMailer::Base
|
|||
classes: RTL.new(user).css_class
|
||||
}
|
||||
)
|
||||
message = email_post_markdown(post) + (reached_limit ? "\n\n#{I18n.t "user_notifications.reached_limit", count: SiteSetting.max_emails_per_day_per_user}" : "");
|
||||
end
|
||||
|
||||
template = "user_notifications.user_#{notification_type}"
|
||||
|
@ -348,8 +363,7 @@ class UserNotifications < ActionMailer::Base
|
|||
|
||||
email_opts = {
|
||||
topic_title: title,
|
||||
topic_excerpt: topic_excerpt,
|
||||
message: email_post_markdown(post) + (reached_limit ? "\n\n#{I18n.t "user_notifications.reached_limit", count: SiteSetting.max_emails_per_day_per_user}" : ""),
|
||||
message: message,
|
||||
url: post.url,
|
||||
post_id: post.id,
|
||||
topic_id: post.topic_id,
|
||||
|
|
|
@ -339,6 +339,10 @@ class Topic < ActiveRecord::Base
|
|||
|
||||
# Remove muted categories
|
||||
muted_category_ids = CategoryUser.where(user_id: user.id, notification_level: CategoryUser.notification_levels[:muted]).pluck(:category_id)
|
||||
if SiteSetting.digest_suppress_categories.present?
|
||||
muted_category_ids += SiteSetting.digest_suppress_categories.split("|").map(&:to_i)
|
||||
muted_category_ids = muted_category_ids.uniq
|
||||
end
|
||||
if muted_category_ids.present?
|
||||
topics = topics.where("topics.category_id NOT IN (?)", muted_category_ids)
|
||||
end
|
||||
|
|
|
@ -9,4 +9,5 @@ class BadgeIndexSerializer < BadgeSerializer
|
|||
def has_badge
|
||||
@options[:user_badges].include?(object.id)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<div id='main' class=<%= classes %>>
|
||||
|
||||
<div class='header-instructions'>%{header_instructions}</div>
|
||||
|
||||
<% if message.present? %>
|
||||
<div><%= message %></div>
|
||||
<% end %>
|
||||
|
||||
<hr>
|
||||
<div class='footer'>%{respond_instructions}</div>
|
||||
<div class='footer'>%{unsubscribe_link}%{unsubscribe_via_email_link}</div>
|
||||
|
||||
</div>
|
|
@ -64,17 +64,17 @@ store.redis_raw_connection = redis.without_namespace
|
|||
severities = [Logger::WARN, Logger::ERROR, Logger::FATAL, Logger::UNKNOWN]
|
||||
|
||||
RailsMultisite::ConnectionManagement.each_connection do
|
||||
error_rate_per_minute = SiteSetting.alert_admins_if_errors_per_minute
|
||||
error_rate_per_minute = SiteSetting.alert_admins_if_errors_per_minute rescue 0
|
||||
|
||||
if error_rate_per_minute > 0
|
||||
if (error_rate_per_minute || 0) > 0
|
||||
store.register_rate_limit_per_minute(severities, error_rate_per_minute) do |rate|
|
||||
MessageBus.publish("/logs_error_rate_exceeded", { rate: rate, duration: 'minute' })
|
||||
end
|
||||
end
|
||||
|
||||
error_rate_per_hour = SiteSetting.alert_admins_if_errors_per_hour
|
||||
error_rate_per_hour = SiteSetting.alert_admins_if_errors_per_hour rescue 0
|
||||
|
||||
if error_rate_per_hour > 0
|
||||
if (error_rate_per_hour || 0) > 0
|
||||
store.register_rate_limit_per_hour(severities, error_rate_per_hour) do |rate|
|
||||
MessageBus.publish("/logs_error_rate_exceeded", { rate: rate, duration: 'hour' })
|
||||
end
|
||||
|
|
|
@ -2855,10 +2855,11 @@ en:
|
|||
earned_n_times:
|
||||
one: "Earned this badge 1 time"
|
||||
other: "Earned this badge %{count} times"
|
||||
more_with_badge: "Others with this badge"
|
||||
granted_on: "Granted %{date}"
|
||||
others_count: "Others with this badge (%{count})"
|
||||
title: Badges
|
||||
allow_title: "can be used as a title"
|
||||
multiple_grant: "can be awarded multiple times"
|
||||
allow_title: "available title"
|
||||
multiple_grant: "awarded multiple times"
|
||||
badge_count:
|
||||
one: "1 Badge"
|
||||
other: "%{count} Badges"
|
||||
|
|
|
@ -1195,6 +1195,7 @@ en:
|
|||
digest_topics: "The maximum number of topics to display in the email digest."
|
||||
digest_min_excerpt_length: "Minimum post excerpt in the email digest, in characters."
|
||||
delete_digest_email_after_days: "Suppress digest emails for users not seen on the site for more than (n) days."
|
||||
digest_suppress_categories: "Suppress these categories from digest emails."
|
||||
disable_digest_emails: "Disable digest emails for all users."
|
||||
|
||||
detect_custom_avatars: "Whether or not to check that users have uploaded custom profile pictures."
|
||||
|
@ -2067,53 +2068,57 @@ en:
|
|||
|
||||
posted_by: "Posted by %{username} on %{post_date}"
|
||||
|
||||
invited_to_private_message_body: |
|
||||
%{username} invited you to a message
|
||||
|
||||
> **%{topic_title}**
|
||||
>
|
||||
> %{topic_excerpt}
|
||||
|
||||
at
|
||||
|
||||
> %{site_title} -- %{site_description}
|
||||
|
||||
invited_to_topic_body: |
|
||||
%{username} invited you to a discussion
|
||||
|
||||
> **%{topic_title}**
|
||||
>
|
||||
> %{topic_excerpt}
|
||||
|
||||
at
|
||||
|
||||
> %{site_title} -- %{site_description}
|
||||
|
||||
user_invited_to_private_message_pm:
|
||||
subject_template: "[%{site_name}] %{username} invited you to a message '%{topic_title}'"
|
||||
text_body_template: |
|
||||
%{header_instructions}
|
||||
|
||||
%{username} invited you to a message
|
||||
%{message}
|
||||
|
||||
> **%{topic_title}**
|
||||
>
|
||||
> %{topic_excerpt}
|
||||
|
||||
at
|
||||
|
||||
> %{site_title} -- %{site_description}
|
||||
|
||||
Please visit this link to view the message: %{base_url}%{url}
|
||||
---
|
||||
%{respond_instructions}
|
||||
|
||||
user_invited_to_private_message_pm_staged:
|
||||
subject_template: "[%{site_name}] %{username} invited you to a message '%{topic_title}'"
|
||||
text_body_template: |
|
||||
%{header_instructions}
|
||||
|
||||
%{username} invited you to a message
|
||||
%{message}
|
||||
|
||||
> **%{topic_title}**
|
||||
>
|
||||
> %{topic_excerpt}
|
||||
|
||||
at
|
||||
|
||||
> %{site_title} -- %{site_description}
|
||||
|
||||
Please visit this link to view the message: %{base_url}%{url}
|
||||
---
|
||||
%{respond_instructions}
|
||||
|
||||
user_invited_to_topic:
|
||||
subject_template: "[%{site_name}] %{username} invited you to '%{topic_title}'"
|
||||
text_body_template: |
|
||||
%{header_instructions}
|
||||
|
||||
%{username} invited you to a discussion
|
||||
%{message}
|
||||
|
||||
> **%{topic_title}**
|
||||
>
|
||||
> %{topic_excerpt}
|
||||
|
||||
at
|
||||
|
||||
> %{site_title} -- %{site_description}
|
||||
|
||||
Please visit this link to view the message: %{base_url}%{url}
|
||||
---
|
||||
%{respond_instructions}
|
||||
|
||||
user_replied:
|
||||
subject_template: "[%{site_name}] %{topic_title}"
|
||||
|
@ -2770,49 +2775,72 @@ en:
|
|||
read_guidelines: |
|
||||
This badge is granted for <a href="/guidelines">reading the community guidelines</a>. Following and sharing these simple guidelines helps build a safe, fun, and sustainable community for everyone. Always remember there's another human being, one very much like yourself, on the other side of that screen. Be nice!
|
||||
reader: |
|
||||
This badge is granted the first time you read a long topic with more than 100 replies. Reading a conversation closely helps you follow the discussion, understand different viewpoints, and leads to more interesting conversations. The more you read, the better the conversation gets. As we like to say, Reading is Fundamental! :simple_smile:
|
||||
This badge is granted the first time you read a long topic with more than 100 replies. Reading a conversation closely helps you follow the discussion, understand different viewpoints, and leads to more interesting conversations. The more you read, the better the conversation gets. As we like to say, Reading is Fundamental! :slight_smile:
|
||||
editor: |
|
||||
This badge is granted the first time you edit one of your posts. While you won't be able to edit your posts forever, editing is always a good idea -- you can improve your posts, fix small mistakes, or add anything you missed when you originally posted. Edit to make your posts even better!
|
||||
This badge is granted the first time you edit one of your posts. While you won't be able to edit your posts forever, editing is always a good idea — you can improve your posts, fix small mistakes, or add anything you missed when you originally posted. Edit to make your posts even better!
|
||||
first_flag: |
|
||||
This badge is granted the first time you flag a post. Flagging is how we all help keep this a clean, well lit place for everyone. If you notice any posts that require moderator attention for any reason please don't hesitate to flag. You can also use flag to send <b>personal messages</b> to fellow users if you see an issue with their post. If you see a problem, flag it!
|
||||
nice_share: |
|
||||
This badge is granted for sharing a link that was clicked by 25 outside visitors. Thanks for spreading the word about our discussions, and this community.
|
||||
This badge is granted the first time you flag a post. Flagging is how we all help keep this a clean, well lit place for everyone. If you notice any posts that require moderator attention for any reason please don't hesitate to flag. You can also flag to send <b>personal messages</b> to fellow users if you see an issue with their post. If you see a problem, :flag_black: flag it!
|
||||
welcome: |
|
||||
This badge is granted when you receive your first like on a post. Congratulations, you've posted something that your fellow community members found interesting, cool, or useful! Now keep going!
|
||||
This badge is granted when you receive your first like on a post. Congratulations, you've posted something that your fellow community members found interesting, cool, or useful!
|
||||
anniversary: |
|
||||
This badge is granted when you've been a member for a year with at least one post in that year. Thank you for sticking around and contributing to our community. We couldn't do it without you.
|
||||
nice_share: |
|
||||
This badge is granted for sharing a link that was clicked by 25 outside visitors. Thanks for spreading the word about our discussions, and this community.
|
||||
good_share: |
|
||||
This badge is granted for sharing a link that was clicked by 300 outside visitors. Good work! You've shown off a great discussion to a bunch of new people and helped this community grow.
|
||||
great_share: |
|
||||
This badge is granted for sharing a link that was clicked by 1000 outside visitors. Wow! You've promoted an interesting discussion to a huge new audience, and helped us grow our community in a big way!
|
||||
nice_topic: |
|
||||
This badge is granted when your topic gets 10 likes. You started an interesting conversation that the community enjoyed.
|
||||
This badge is granted when your topic gets 10 likes. Hey, you started an interesting conversation that the community enjoyed!
|
||||
nice_post: |
|
||||
This badge is granted when your reply gets 10 likes. Your reply made an impression on the community and helped move the conversation forward.
|
||||
This badge is granted when your reply gets 10 likes. Your reply really made an impression on the community and helped move the conversation forward!
|
||||
good_topic: |
|
||||
This badge is granted when your topic gets 25 likes. You launched a vibrant conversation that the community really responded to.
|
||||
This badge is granted when your topic gets 25 likes. You launched a vibrant conversation that the community rallied around and loved!
|
||||
good_post: |
|
||||
This badge is granted when your reply gets 25 likes. Your reply was exceptional and it made the conversation a whole lot better for everyone.
|
||||
This badge is granted when your reply gets 25 likes. Your reply was exceptional and made the conversation a whole lot better for everyone!
|
||||
great_topic: |
|
||||
This badge is granted when your topic gets 50 likes. Wow! You kicked off a fascinating conversation and the community loved the dynamic discussion that resulted.
|
||||
This badge is granted when your topic gets 50 likes. You kicked off a fascinating conversation and the community enjoyed the dynamic discussion that resulted!
|
||||
great_post: |
|
||||
This badge is granted when your reply gets 50 likes. Wow! Your reply was inspiring, fascinating, hilarious, or insightful and the community loved it.
|
||||
basic: |
|
||||
This badge is granted when you reach trust level 1. Thanks for sticking around a little while and reading a few topics to learn what our community is about. Your new user restrictions have been lifted; you've been granted all essential community abilities, such as personal messaging, flagging, wiki editing, and the ability to post multiple images and links.
|
||||
appreciated: |
|
||||
This badge is granted when you receive at least one like on 20 different posts. The community is enjoying your contributions to the conversations here, so keep them coming!
|
||||
This badge is granted when you receive at least one like on 20 different posts. The community is enjoying your contributions to the conversations here!
|
||||
respected: |
|
||||
This badge is granted when you receive at least 2 likes on 100 different posts. The community is growing to respect your many contributions to the conversations here.
|
||||
admired: |
|
||||
This badge is granted when you receive at least 5 likes on 300 different posts. Wow! The community admires your frequent, high quality contributions to the conversations here.
|
||||
out_of_love: |
|
||||
This badge is granted when you use all 50 of your daily likes. Letting the community know what's great by regularly liking those posts you enjoy and appreciate is the best way to encourage people to create even more great discussions in the future.
|
||||
This badge is granted when you use all 50 of your daily likes. Remembering to take a moment and like the posts you enjoy and appreciate encourages your fellow community members to create even more great discussions in the future.
|
||||
higher_love: |
|
||||
This badge is granted when you use all 50 of your daily likes for 5 days. Thanks for taking the time actively encouraging the best conversations every day!
|
||||
crazy_in_love: |
|
||||
This badge is granted when you use all 50 of your daily likes for 20 days. Wow! You're a model of regularly encouraging your fellow community members!
|
||||
promoter: |
|
||||
This badge is granted when you invite someone to join the community via the invite button on your user page, or at the bottom of a topic. Inviting friends who might be interested in specific discussions is an great way to introduce new people to our community, so thanks!
|
||||
campaigner: |
|
||||
This badge is granted when you've invited 3 people who subsequently spent enough time on the site to become basic users. A vibrant community needs a regular infusion of newcomers who regularly participate and add new voices to the conversationss.
|
||||
champion: |
|
||||
This badge is granted when you've invited 5 people who subsequently spent enough time on the site to become full members. Wow! Thanks for expanding the diversity of our community with new members!
|
||||
thank_you: |
|
||||
This badge is granted when you have at least 20 liked posts.
|
||||
This badge is granted when you've received 20 likes on your posts and have given 10 or more likes in return. When someone likes your posts, you find the time to like what other people are posting in return.
|
||||
gives_back: |
|
||||
This badge is granted when you've received 100 likes and have given 100 or more likes in return. Thanks for paying it forward, and liking in return!
|
||||
empathetic: |
|
||||
This badge is granted when you've received 500 likes and have given 1000 or more likes in return. Wow! You're a model of generosity and mutual love :two_hearts:.
|
||||
basic_user: |
|
||||
This badge is granted when you reach trust level 1. Thanks for sticking around a little while and reading a few topics to learn what our community is about. Your new user restrictions have been lifted; you've been granted all essential community abilities, such as personal messaging, flagging, wiki editing, and the ability to post multiple images and links.
|
||||
member: |
|
||||
This badge is granted when you reach trust level 2. Thanks for participating over a period of weeks to truly join our community. You can now send invitations from your user page or individual topics, create group personal messages, and have a few more likes per day.
|
||||
regular: |
|
||||
This badge is granted when you reach trust level 3. Thanks for being a regular part of our community over a period of months. You're now one of the most active readers, and a reliable contributor that makes our community great. You can now recategorize and rename topics, take advantage of more powerful spam flags, access a private lounge area, and you'll also get lots more likes per day.
|
||||
leader: |
|
||||
This badge is granted when you reach trust level 4. You're a leader in this community as selected by staff, and you set a positive example for the rest of the community in your actions and words here. You have the ability to edit all posts, take common topic moderator actions such as pin, close, unlist, archive, split, and merge, and you have tons of likes per day.
|
||||
popular_link: |
|
||||
This badge is granted when a link you shared gets 50 clicks. Thanks for posting a useful link that added interesting context to the conversation!
|
||||
hot_link: |
|
||||
This badge is granted when a link you shared gets 300 clicks. Thanks for posting a fascinating link that drove the conversation forward and illuminated the discussion!
|
||||
famous_link: |
|
||||
This badge is granted when a link you shared gets 1000 clicks. Wow! You posted a link that significantly improved the conversation by addding essential detail, context, and information. Great work!
|
||||
|
||||
|
||||
admin_login:
|
||||
success: "Email Sent"
|
||||
|
|
|
@ -252,15 +252,15 @@ Discourse::Application.routes.draw do
|
|||
end
|
||||
|
||||
resources :static
|
||||
post "login" => "static#enter"
|
||||
get "login" => "static#show", id: "login"
|
||||
get "password-reset" => "static#show", id: "password_reset"
|
||||
get "faq" => "static#show", id: "faq"
|
||||
get "guidelines" => "static#show", id: "guidelines", as: 'guidelines'
|
||||
get "tos" => "static#show", id: "tos", as: 'tos'
|
||||
get "privacy" => "static#show", id: "privacy", as: 'privacy'
|
||||
get "signup" => "static#show", id: "signup"
|
||||
get "login-preferences" => "static#show", id: "login"
|
||||
post "login" => "static#enter", constraints: { format: /(json|html)/ }
|
||||
get "login" => "static#show", id: "login", constraints: { format: /(json|html)/ }
|
||||
get "password-reset" => "static#show", id: "password_reset", constraints: { format: /(json|html)/ }
|
||||
get "faq" => "static#show", id: "faq", constraints: { format: /(json|html)/ }
|
||||
get "guidelines" => "static#show", id: "guidelines", as: 'guidelines', constraints: { format: /(json|html)/ }
|
||||
get "tos" => "static#show", id: "tos", as: 'tos', constraints: { format: /(json|html)/ }
|
||||
get "privacy" => "static#show", id: "privacy", as: 'privacy', constraints: { format: /(json|html)/ }
|
||||
get "signup" => "static#show", id: "signup", constraints: { format: /(json|html)/ }
|
||||
get "login-preferences" => "static#show", id: "login", constraints: { format: /(json|html)/ }
|
||||
|
||||
get "users/admin-login" => "users#admin_login"
|
||||
put "users/admin-login" => "users#admin_login"
|
||||
|
|
|
@ -512,6 +512,9 @@ email:
|
|||
digest_min_excerpt_length: 100
|
||||
digest_topics: 20
|
||||
delete_digest_email_after_days: 365
|
||||
digest_suppress_categories:
|
||||
type: category_list
|
||||
default: ''
|
||||
disable_digest_emails:
|
||||
default: false
|
||||
client: true
|
||||
|
|
|
@ -134,7 +134,8 @@ Badge.seed do |b|
|
|||
b.target_posts = true
|
||||
b.show_posts = false
|
||||
b.query = Badge::Queries::FirstFlag
|
||||
b.default_badge_grouping_id = BadgeGrouping::Community
|
||||
b.badge_grouping_id = BadgeGrouping::GettingStarted
|
||||
b.default_badge_grouping_id = BadgeGrouping::GettingStarted
|
||||
b.trigger = Badge::Trigger::PostAction
|
||||
b.auto_revoke = false
|
||||
b.system = true
|
||||
|
@ -228,7 +229,8 @@ Badge.seed do |b|
|
|||
b.badge_type_id = BadgeType::Bronze
|
||||
b.multiple_grant = false
|
||||
b.query = Badge::Queries::Editor
|
||||
b.default_badge_grouping_id = BadgeGrouping::Community
|
||||
b.badge_grouping_id = BadgeGrouping::GettingStarted
|
||||
b.default_badge_grouping_id = BadgeGrouping::GettingStarted
|
||||
b.trigger = Badge::Trigger::PostRevision
|
||||
b.system = true
|
||||
end
|
||||
|
@ -285,7 +287,8 @@ end
|
|||
b.target_posts = true
|
||||
b.show_posts = true
|
||||
b.query = Badge::Queries.linking_badge(count)
|
||||
b.default_badge_grouping_id = BadgeGrouping::Community
|
||||
b.badge_grouping_id = BadgeGrouping::Posting
|
||||
b.default_badge_grouping_id = BadgeGrouping::Posting
|
||||
# don't trigger for now, its too expensive
|
||||
b.trigger = Badge::Trigger::None
|
||||
b.system = true
|
||||
|
|
|
@ -6,7 +6,7 @@ class FileHelper
|
|||
filename =~ images_regexp
|
||||
end
|
||||
|
||||
def self.download(url, max_file_size, tmp_file_name, follow_redirect=false)
|
||||
def self.download(url, max_file_size, tmp_file_name, follow_redirect=false, read_timeout=5)
|
||||
raise Discourse::InvalidParameters.new(:url) unless url =~ /^https?:\/\//
|
||||
|
||||
uri = parse_url(url)
|
||||
|
@ -14,7 +14,7 @@ class FileHelper
|
|||
tmp = Tempfile.new([tmp_file_name, extension])
|
||||
|
||||
File.open(tmp.path, "wb") do |f|
|
||||
downloaded = uri.open("rb", read_timeout: 5, redirect: follow_redirect, allow_redirections: :all)
|
||||
downloaded = uri.open("rb", read_timeout: read_timeout, redirect: follow_redirect, allow_redirections: :all)
|
||||
while f.size <= max_file_size && data = downloaded.read(512.kilobytes)
|
||||
f.write(data)
|
||||
end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module MobileDetection
|
||||
def self.mobile_device?(user_agent)
|
||||
user_agent =~ /Mobile|Android|webOS/ && !(user_agent =~ /iPad|Nexus (9|10)/)
|
||||
user_agent =~ /Mobile/ && !(user_agent =~ /iPad/)
|
||||
end
|
||||
|
||||
# we need this as a reusable chunk that is called from the cache
|
||||
|
|
|
@ -22,5 +22,13 @@ describe CategoryHashtag do
|
|||
it "should return nil for incorrect parent and child category slug" do
|
||||
expect(Category.query_from_hashtag_slug("random-slug#{CategoryHashtag::SEPARATOR}random-slug")).to eq(nil)
|
||||
end
|
||||
|
||||
it "should be case sensitive" do
|
||||
parent_category.update_attributes!(slug: "ApPlE")
|
||||
child_category.update_attributes!(slug: "OraNGE")
|
||||
|
||||
expect(Category.query_from_hashtag_slug("apple")).to eq(nil)
|
||||
expect(Category.query_from_hashtag_slug("apple:orange")).to eq(nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -507,52 +507,66 @@ describe Admin::UsersController do
|
|||
|
||||
end
|
||||
|
||||
it 'can sync up sso' do
|
||||
log_in(:admin)
|
||||
|
||||
SiteSetting.enable_sso = true
|
||||
SiteSetting.sso_overrides_email = true
|
||||
SiteSetting.sso_overrides_name = true
|
||||
SiteSetting.sso_overrides_username = true
|
||||
context '#sync_sso' do
|
||||
let(:sso) { SingleSignOn.new }
|
||||
let(:sso_secret) { "sso secret" }
|
||||
|
||||
SiteSetting.sso_secret = "sso secret"
|
||||
before do
|
||||
log_in(:admin)
|
||||
|
||||
sso = SingleSignOn.new
|
||||
sso.sso_secret = "sso secret"
|
||||
sso.name = "Bob The Bob"
|
||||
sso.username = "bob"
|
||||
sso.email = "bob@bob.com"
|
||||
sso.external_id = "1"
|
||||
SiteSetting.enable_sso = true
|
||||
SiteSetting.sso_overrides_email = true
|
||||
SiteSetting.sso_overrides_name = true
|
||||
SiteSetting.sso_overrides_username = true
|
||||
SiteSetting.sso_secret = sso_secret
|
||||
sso.sso_secret = sso_secret
|
||||
end
|
||||
|
||||
user = DiscourseSingleSignOn.parse(sso.payload)
|
||||
.lookup_or_create_user
|
||||
|
||||
sso.name = "Bill"
|
||||
sso.username = "Hokli$$!!"
|
||||
sso.email = "bob2@bob.com"
|
||||
it 'can sync up with the sso' do
|
||||
sso.name = "Bob The Bob"
|
||||
sso.username = "bob"
|
||||
sso.email = "bob@bob.com"
|
||||
sso.external_id = "1"
|
||||
|
||||
xhr :post, :sync_sso, Rack::Utils.parse_query(sso.payload)
|
||||
expect(response).to be_success
|
||||
user = DiscourseSingleSignOn.parse(sso.payload)
|
||||
.lookup_or_create_user
|
||||
|
||||
user.reload
|
||||
expect(user.email).to eq("bob2@bob.com")
|
||||
expect(user.name).to eq("Bill")
|
||||
expect(user.username).to eq("Hokli")
|
||||
sso.name = "Bill"
|
||||
sso.username = "Hokli$$!!"
|
||||
sso.email = "bob2@bob.com"
|
||||
|
||||
# It can also create new users
|
||||
sso = SingleSignOn.new
|
||||
sso.sso_secret = "sso secret"
|
||||
sso.name = "Dr. Claw"
|
||||
sso.username = "dr_claw"
|
||||
sso.email = "dr@claw.com"
|
||||
sso.external_id = "2"
|
||||
xhr :post, :sync_sso, Rack::Utils.parse_query(sso.payload)
|
||||
expect(response).to be_success
|
||||
xhr :post, :sync_sso, Rack::Utils.parse_query(sso.payload)
|
||||
expect(response).to be_success
|
||||
|
||||
user = User.where(email: 'dr@claw.com').first
|
||||
expect(user).to be_present
|
||||
expect(user.ip_address).to be_blank
|
||||
user.reload
|
||||
expect(user.email).to eq("bob2@bob.com")
|
||||
expect(user.name).to eq("Bill")
|
||||
expect(user.username).to eq("Hokli")
|
||||
end
|
||||
|
||||
it 'should create new users' do
|
||||
sso.name = "Dr. Claw"
|
||||
sso.username = "dr_claw"
|
||||
sso.email = "dr@claw.com"
|
||||
sso.external_id = "2"
|
||||
xhr :post, :sync_sso, Rack::Utils.parse_query(sso.payload)
|
||||
expect(response).to be_success
|
||||
|
||||
user = User.where(email: 'dr@claw.com').first
|
||||
expect(user).to be_present
|
||||
expect(user.ip_address).to be_blank
|
||||
end
|
||||
|
||||
it 'should return the right message if the record is invalid' do
|
||||
sso.email = ""
|
||||
sso.name = ""
|
||||
sso.external_id = "1"
|
||||
|
||||
xhr :post, :sync_sso, Rack::Utils.parse_query(sso.payload)
|
||||
expect(response.status).to eq(403)
|
||||
expect(JSON.parse(response.body)["message"]).to include("Email can't be blank")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -29,28 +29,38 @@ describe ApplicationHelper do
|
|||
|
||||
context "mobile_view is not set" do
|
||||
it "is false if user agent is not mobile" do
|
||||
controller.request.stubs(:user_agent).returns('Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.17 Safari/537.36')
|
||||
controller.request.stubs(:user_agent).returns('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36')
|
||||
expect(helper.mobile_view?).to be_falsey
|
||||
end
|
||||
|
||||
it "is true for iPhone" do
|
||||
controller.request.stubs(:user_agent).returns('Mozilla/5.0 (iPhone; U; ru; CPU iPhone OS 4_2_1 like Mac OS X; ru) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148a Safari/6533.18.5')
|
||||
controller.request.stubs(:user_agent).returns('Mozilla/5.0 (iPhone; CPU iPhone OS 9_2_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13D15 Safari/601.1')
|
||||
expect(helper.mobile_view?).to eq(true)
|
||||
end
|
||||
|
||||
it "is true for Android Samsung Galaxy" do
|
||||
controller.request.stubs(:user_agent).returns('Mozilla/5.0 (Linux; Android 5.0.2; SAMSUNG SM-G925F Build/LRX22G) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/4.0 Chrome/44.0.2403.133 Mobile Safari/537.36')
|
||||
expect(helper.mobile_view?).to eq(true)
|
||||
end
|
||||
|
||||
it "is true for Android Google Nexus 5X" do
|
||||
controller.request.stubs(:user_agent).returns('Mozilla/5.0 (Linux; Android 6.0; Nexus 5X Build/MDB08I) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.43 Mobile Safari/537.36')
|
||||
expect(helper.mobile_view?).to eq(true)
|
||||
end
|
||||
|
||||
it "is false for iPad" do
|
||||
controller.request.stubs(:user_agent).returns("Mozilla/5.0 (iPad; CPU OS 5_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9B176 Safari/7534.48.3")
|
||||
controller.request.stubs(:user_agent).returns("Mozilla/5.0 (iPad; CPU OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B14 3 Safari/601.1")
|
||||
expect(helper.mobile_view?).to eq(false)
|
||||
end
|
||||
|
||||
it "is false for Nexus 10 tablet" do
|
||||
controller.request.stubs(:user_agent).returns("Mozilla/5.0 (Linux; Android 4.2.1; Nexus 10 Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19")
|
||||
controller.request.stubs(:user_agent).returns("Mozilla/5.0 (Linux; Android 5.1.1; Nexus 10 Build/LMY49G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.91 Safari/537.36")
|
||||
expect(helper.mobile_view?).to be_falsey
|
||||
end
|
||||
|
||||
it "is true for Nexus 7 tablet" do
|
||||
controller.request.stubs(:user_agent).returns("Mozilla/5.0 (Linux; Android 4.1.2; Nexus 7 Build/JZ054K) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19")
|
||||
expect(helper.mobile_view?).to eq(true)
|
||||
it "is false for Nexus 7 tablet" do
|
||||
controller.request.stubs(:user_agent).returns("Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MMB29Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.91 Safari/537.36")
|
||||
expect(helper.mobile_view?).to be_falsey
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1296,6 +1296,16 @@ describe Topic do
|
|||
expect(Topic.for_digest(user, 1.year.ago, top_order: true)).to be_blank
|
||||
end
|
||||
|
||||
it "doesn't return topics from suppressed categories" do
|
||||
user = Fabricate(:user)
|
||||
category = Fabricate(:category)
|
||||
Fabricate(:topic, category: category)
|
||||
|
||||
SiteSetting.digest_suppress_categories = "#{category.id}"
|
||||
|
||||
expect(Topic.for_digest(user, 1.year.ago, top_order: true)).to be_blank
|
||||
end
|
||||
|
||||
it "doesn't return topics from TL0 users" do
|
||||
new_user = Fabricate(:user, trust_level: 0)
|
||||
Fabricate(:topic, user_id: new_user.id)
|
||||
|
@ -1607,7 +1617,7 @@ describe Topic do
|
|||
|
||||
topic.update_status('closed', true, user)
|
||||
topic.reload
|
||||
|
||||
|
||||
expect(@topic_status_event_triggered).to eq(true)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,8 +4,8 @@ acceptance("About");
|
|||
test("viewing", () => {
|
||||
visit("/about");
|
||||
andThen(() => {
|
||||
ok(exists('.about.admins .user-small'), 'has admins');
|
||||
ok(exists('.about.moderators .user-small'), 'has moderators');
|
||||
ok(exists('.about.admins .user-info'), 'has admins');
|
||||
ok(exists('.about.moderators .user-info'), 'has moderators');
|
||||
ok(exists('.about.stats tr td'), 'has stats');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,12 +5,12 @@ acceptance("Badges");
|
|||
test("Visit Badge Pages", () => {
|
||||
visit("/badges");
|
||||
andThen(() => {
|
||||
ok(exists('.badges-listing tr'), "has a list of badges");
|
||||
ok(exists('.badge-groups .badge-card'), "has a list of badges");
|
||||
});
|
||||
|
||||
visit("/badges/9/autobiographer");
|
||||
andThen(() => {
|
||||
ok(exists('.badges-listing div'), "has the badge in the listing");
|
||||
ok(exists('.badge-user'), "has the list of users with that badge");
|
||||
ok(exists('.badge-card'), "has the badge in the listing");
|
||||
ok(exists('.user-info'), "has the list of users with that badge");
|
||||
});
|
||||
});
|
||||
|
|
|
@ -62,20 +62,6 @@ test('tags with arguments', function() {
|
|||
format("[b]first[/b] [b]second[/b]", "<span class=\"bbcode-b\">first</span> <span class=\"bbcode-b\">second</span>", "can bold two things on the same line");
|
||||
});
|
||||
|
||||
test("size tags", function() {
|
||||
format("[size=35]BIG [b]whoop[/b][/size]",
|
||||
"<span class=\"bbcode-size-35\">BIG <span class=\"bbcode-b\">whoop</span></span>",
|
||||
"supports [size=]");
|
||||
format("[size=asdf]regular[/size]",
|
||||
"<span class=\"bbcode-size-1\">regular</span>",
|
||||
"it only supports numbers in bbcode");
|
||||
format("[size=35]NEWLINE\n\ntest[/size]",
|
||||
"<span class=\"bbcode-size-35\"><p>NEWLINE</p><p>test</p></span>",
|
||||
"works with newlines");
|
||||
format("[size=35][quote=\"user\"]quote[/quote][/size]",
|
||||
"<span class=\"bbcode-size-35\"><aside class=\"quote\"><div class=\"title\"><div class=\"quote-controls\"></div>user:</div><blockquote><p>quote</p></blockquote></aside></span>",
|
||||
"works with nested complex blocks");
|
||||
});
|
||||
|
||||
test("quotes", function() {
|
||||
|
||||
|
|
|
@ -510,9 +510,9 @@ test("sanitize", function() {
|
|||
|
||||
cooked("<i class=\"fa fa-bug fa-spin\" style=\"font-size:600%\"></i>\n<!-- -->", "<p><i></i><br/></p>", "it doesn't circumvent XSS with comments");
|
||||
|
||||
cooked("<span class=\"-bbcode-size-0 fa fa-spin\">a</span>", "<p><span>a</span></p>", "it sanitizes spans");
|
||||
cooked("<span class=\"fa fa-spin -bbcode-size-0\">a</span>", "<p><span>a</span></p>", "it sanitizes spans");
|
||||
cooked("<span class=\"bbcode-size-10\">a</span>", "<p><span class=\"bbcode-size-10\">a</span></p>", "it sanitizes spans");
|
||||
cooked("<span class=\"-bbcode-s fa fa-spin\">a</span>", "<p><span>a</span></p>", "it sanitizes spans");
|
||||
cooked("<span class=\"fa fa-spin -bbcode-s\">a</span>", "<p><span>a</span></p>", "it sanitizes spans");
|
||||
cooked("<span class=\"bbcode-s\">a</span>", "<p><span class=\"bbcode-s\">a</span></p>", "it sanitizes spans");
|
||||
});
|
||||
|
||||
test("URLs in BBCode tags", function() {
|
||||
|
|
Loading…
Reference in New Issue