Version bump
This commit is contained in:
commit
ac079e6240
14
Gemfile.lock
14
Gemfile.lock
@ -147,13 +147,13 @@ GEM
|
||||
thor (~> 0.15)
|
||||
libv8 (3.16.14.7)
|
||||
listen (0.7.3)
|
||||
logster (0.8.1)
|
||||
lru_redux (0.8.4)
|
||||
logster (0.8.2)
|
||||
lru_redux (1.1.0)
|
||||
mail (2.5.4)
|
||||
mime-types (~> 1.16)
|
||||
treetop (~> 1.4.8)
|
||||
memory_profiler (0.9.0)
|
||||
message_bus (1.0.12)
|
||||
memory_profiler (0.9.3)
|
||||
message_bus (1.0.13)
|
||||
rack (>= 1.1.3)
|
||||
redis
|
||||
metaclass (0.0.4)
|
||||
@ -182,7 +182,7 @@ GEM
|
||||
multi_json (~> 1.3)
|
||||
multi_xml (~> 0.5)
|
||||
rack (~> 1.2)
|
||||
oj (2.12.0)
|
||||
oj (2.12.9)
|
||||
omniauth (1.2.2)
|
||||
hashie (>= 1.2, < 4)
|
||||
rack (~> 1.0)
|
||||
@ -208,7 +208,7 @@ GEM
|
||||
omniauth-twitter (1.0.1)
|
||||
multi_json (~> 1.3)
|
||||
omniauth-oauth (~> 1.0)
|
||||
onebox (1.5.19)
|
||||
onebox (1.5.20)
|
||||
moneta (~> 0.7)
|
||||
multi_json (~> 1.7)
|
||||
mustache (~> 0.99)
|
||||
@ -486,4 +486,4 @@ DEPENDENCIES
|
||||
unicorn
|
||||
|
||||
BUNDLED WITH
|
||||
1.10.2
|
||||
1.10.3
|
||||
|
||||
@ -2,7 +2,7 @@ import BufferedContent from 'discourse/mixins/buffered-content';
|
||||
import ScrollTop from 'discourse/mixins/scroll-top';
|
||||
import SiteSetting from 'admin/models/site-setting';
|
||||
|
||||
const CustomTypes = ['bool', 'enum', 'list', 'url_list'];
|
||||
const CustomTypes = ['bool', 'enum', 'list', 'url_list', 'host_list'];
|
||||
|
||||
export default Ember.Component.extend(BufferedContent, ScrollTop, {
|
||||
classNameBindings: [':row', ':setting', 'setting.overridden', 'typeClass'],
|
||||
|
||||
@ -1,32 +0,0 @@
|
||||
export default Ember.Component.extend({
|
||||
_setupUrls: function() {
|
||||
const value = this.get('value');
|
||||
this.set('urls', (value && value.length) ? value.split("\n") : []);
|
||||
}.on('init').observes('value'),
|
||||
|
||||
_urlsChanged: function() {
|
||||
this.set('value', this.get('urls').join("\n"));
|
||||
}.observes('urls.@each'),
|
||||
|
||||
urlInvalid: Ember.computed.empty('newUrl'),
|
||||
|
||||
keyDown(e) {
|
||||
if (e.keyCode === 13) {
|
||||
this.send('addUrl');
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
addUrl() {
|
||||
if (this.get('urlInvalid')) { return; }
|
||||
|
||||
this.get('urls').addObject(this.get('newUrl'));
|
||||
this.set('newUrl', '');
|
||||
},
|
||||
|
||||
removeUrl(url) {
|
||||
const urls = this.get('urls');
|
||||
urls.removeObject(url);
|
||||
}
|
||||
}
|
||||
});
|
||||
34
app/assets/javascripts/admin/components/value-list.js.es6
Normal file
34
app/assets/javascripts/admin/components/value-list.js.es6
Normal file
@ -0,0 +1,34 @@
|
||||
export default Ember.Component.extend({
|
||||
classNameBindings: [':value-list'],
|
||||
|
||||
_setupCollection: function() {
|
||||
const values = this.get('values');
|
||||
this.set('collection', (values && values.length) ? values.split("\n") : []);
|
||||
}.on('init').observes('values'),
|
||||
|
||||
_collectionChanged: function() {
|
||||
this.set('values', this.get('collection').join("\n"));
|
||||
}.observes('collection.@each'),
|
||||
|
||||
inputInvalid: Ember.computed.empty('newValue'),
|
||||
|
||||
keyDown(e) {
|
||||
if (e.keyCode === 13) {
|
||||
this.send('addValue');
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
addValue() {
|
||||
if (this.get('inputInvalid')) { return; }
|
||||
|
||||
this.get('collection').addObject(this.get('newValue'));
|
||||
this.set('newValue', '');
|
||||
},
|
||||
|
||||
removeValue(value) {
|
||||
const collection = this.get('collection');
|
||||
collection.removeObject(value);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -39,6 +39,11 @@ export default Ember.ArrayController.extend(Presence, {
|
||||
});
|
||||
if (matches.length > 0) {
|
||||
matchesGroupedByCategory[0].siteSettings.pushObjects(matches);
|
||||
matchesGroupedByCategory.pushObject({
|
||||
nameKey: settingsCategory.nameKey,
|
||||
name: I18n.t('admin.site_settings.categories.' + settingsCategory.nameKey),
|
||||
siteSettings: matches
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -1,18 +0,0 @@
|
||||
{{#if urls}}
|
||||
<div class='urls'>
|
||||
{{#each url in urls}}
|
||||
<div class='url'>
|
||||
{{d-button action="removeUrl"
|
||||
actionParam=url
|
||||
icon="times"
|
||||
class="btn-small no-text"}}
|
||||
<a href="{{unbound url}}" target="_blank">{{url}}</a>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class='input'>
|
||||
{{text-field value=newUrl placeholderKey="admin.site_settings.add_url"}}
|
||||
{{d-button action="addUrl" icon="plus" class="btn-primary btn-small no-text" disabled=urlInvalid}}
|
||||
</div>
|
||||
@ -0,0 +1,18 @@
|
||||
{{#if collection}}
|
||||
<div class='values'>
|
||||
{{#each collection as |value|}}
|
||||
<div class='value'>
|
||||
{{d-button action="removeValue"
|
||||
actionParam=value
|
||||
icon="times"
|
||||
class="btn-small no-text"}}
|
||||
{{value}}
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class='input'>
|
||||
{{text-field value=newValue placeholderKey=addKey}}
|
||||
{{d-button action="addValue" icon="plus" class="btn-primary btn-small no-text" disabled=inputInvalid}}
|
||||
</div>
|
||||
@ -0,0 +1,3 @@
|
||||
{{value-list values=buffered.value addKey="admin.site_settings.add_host"}}
|
||||
{{setting-validation-message message=validationMessage}}
|
||||
<div class='desc'>{{{unbound setting.description}}}</div>
|
||||
@ -1,3 +1,3 @@
|
||||
{{url-list value=buffered.value}}
|
||||
{{value-list value=buffered.value addKey="admin.site_settings.add_url"}}
|
||||
{{setting-validation-message message=validationMessage}}
|
||||
<div class='desc'>{{{unbound setting.description}}}</div>
|
||||
|
||||
@ -27,7 +27,7 @@ export default DiscourseController.extend({
|
||||
|
||||
const selection = window.getSelection();
|
||||
// no selections
|
||||
if (selection.rangeCount === 0) return;
|
||||
if (selection.isCollapsed) return;
|
||||
|
||||
// retrieve the selected range
|
||||
const range = selection.getRangeAt(0),
|
||||
|
||||
@ -166,7 +166,7 @@ Discourse.Emoji.search = function(term, options) {
|
||||
var maxResults = (options && options["maxResults"]) || -1;
|
||||
if (maxResults === 0) { return []; }
|
||||
|
||||
toSearch = toSearch || _.merge(_.keys(emojiHash), _.keys(extendedEmoji));
|
||||
toSearch = toSearch || _.union(_.keys(emojiHash), _.keys(extendedEmoji)).sort();
|
||||
|
||||
var i, results = [];
|
||||
|
||||
|
||||
43
app/assets/javascripts/discourse/lib/link-mentions.js.es6
Normal file
43
app/assets/javascripts/discourse/lib/link-mentions.js.es6
Normal file
@ -0,0 +1,43 @@
|
||||
function replaceSpan($e, username) {
|
||||
$e.replaceWith("<a href='" +
|
||||
Discourse.getURL("/users/") + username.toLowerCase() +
|
||||
"' class='mention'>@" + username + "</a>");
|
||||
}
|
||||
|
||||
const found = [];
|
||||
const checked = [];
|
||||
|
||||
function updateFound($mentions, usernames) {
|
||||
Ember.run.scheduleOnce('afterRender', function() {
|
||||
$mentions.each((i, e) => {
|
||||
const $e = $(e);
|
||||
const username = usernames[i];
|
||||
if (found.indexOf(username) !== -1) {
|
||||
replaceSpan($e, username);
|
||||
} else if (checked.indexOf(username) !== -1) {
|
||||
$e.addClass('mention-tested');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function linkSeenMentions($elem, siteSettings) {
|
||||
const $mentions = $('span.mention:not(.mention-tested)', $elem);
|
||||
if ($mentions.length) {
|
||||
const usernames = $mentions.map((_, e) => $(e).text().substr(1).toLowerCase());
|
||||
const unseen = _.uniq(usernames).filter((u) => {
|
||||
return u.length >= siteSettings.min_username_length && checked.indexOf(u) === -1;
|
||||
});
|
||||
updateFound($mentions, usernames);
|
||||
return unseen;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
export function fetchUnseenMentions($elem, usernames) {
|
||||
return Discourse.ajax("/users/is_local_username", { data: { usernames } }).then(function(r) {
|
||||
found.push.apply(found, r.valid);
|
||||
checked.push.apply(checked, usernames);
|
||||
});
|
||||
}
|
||||
@ -1,59 +0,0 @@
|
||||
|
||||
// A local cache lookup
|
||||
var localCache = [];
|
||||
|
||||
|
||||
/**
|
||||
Lookup a username and return whether it is exists or not.
|
||||
|
||||
@function lookup
|
||||
@param {String} username to look up
|
||||
@return {Promise} promise that results to whether the name was found or not
|
||||
**/
|
||||
function lookup(username) {
|
||||
return new Em.RSVP.Promise(function (resolve) {
|
||||
var cached = localCache[username];
|
||||
|
||||
// If we have a cached answer, return it
|
||||
if (typeof cached !== "undefined") {
|
||||
resolve(cached);
|
||||
} else {
|
||||
Discourse.ajax("/users/is_local_username", { data: { username: username } }).then(function(r) {
|
||||
localCache[username] = r.valid;
|
||||
resolve(r.valid);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
Help us link directly to a mentioned user's profile if the username exists.
|
||||
|
||||
@class Mention
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.Mention = {
|
||||
|
||||
/**
|
||||
Paints an element in the DOM with the appropriate classes and markup if the username
|
||||
it is mentioning exists.
|
||||
|
||||
@method paint
|
||||
@param {Element} the element in the DOM to decorate
|
||||
**/
|
||||
paint: function(e) {
|
||||
var $elem = $(e);
|
||||
if ($elem.data('mention-tested')) return;
|
||||
var username = $elem.text().substr(1);
|
||||
|
||||
$elem.addClass('mention-loading');
|
||||
lookup(username).then(function(found) {
|
||||
if (found) {
|
||||
$elem.replaceWith("<a href='" + Discourse.getURL("/users/") + username.toLowerCase() + "' class='mention'>@" + username + "</a>");
|
||||
} else {
|
||||
$elem.removeClass('mention-loading').addClass('mention-tested');
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -75,7 +75,8 @@ Discourse.Category = Discourse.Model.extend({
|
||||
parent_category_id: this.get('parent_category_id'),
|
||||
logo_url: this.get('logo_url'),
|
||||
background_url: this.get('background_url'),
|
||||
allow_badges: this.get('allow_badges')
|
||||
allow_badges: this.get('allow_badges'),
|
||||
custom_fields: this.get('custom_fields')
|
||||
},
|
||||
type: this.get('id') ? 'PUT' : 'POST'
|
||||
});
|
||||
|
||||
@ -713,7 +713,7 @@ const PostStream = RestModel.extend({
|
||||
// If the result was 404 the post is not found
|
||||
if (status === 404) {
|
||||
topic.set('errorTitle', I18n.t('topic.not_found.title'));
|
||||
topic.set('notFoundHtml', result.responseText);
|
||||
topic.set('notFoundHtml', result.jqXHR.responseText);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -3,6 +3,9 @@
|
||||
<div id="banner-content">
|
||||
<div class="close" {{action "dismiss"}}><i class="fa fa-times" title="{{i18n 'banner.close'}}"></i></div>
|
||||
{{{banner.html}}}
|
||||
{{#if currentUser.staff}}
|
||||
<p><a href="{{banner.url}}">{{{i18n "banner.edit"}}}</a></p>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -43,3 +43,5 @@
|
||||
<a href="/admin/site_settings/category/basic">{{i18n 'category.position_disabled_click'}}</a>
|
||||
{{/if}}
|
||||
</section>
|
||||
|
||||
{{plugin-outlet "category-custom-settings"}}
|
||||
|
||||
@ -125,7 +125,7 @@
|
||||
<div class='container'>
|
||||
{{#conditional-loading-spinner condition=noErrorYet}}
|
||||
{{#if model.notFoundHtml}}
|
||||
{{{model.notFoundHtml}}}
|
||||
<div class='not-found'>{{{model.notFoundHtml}}}</div>
|
||||
{{else}}
|
||||
<div class="topic-error">
|
||||
<div>{{model.message}}</div>
|
||||
|
||||
@ -63,6 +63,7 @@
|
||||
<h3><span class='desc'>{{i18n 'last_post'}}</span> {{format-date user.last_posted_at leaveAgo="true"}}</h3>
|
||||
{{/if}}
|
||||
<h3><span class='desc'>{{i18n 'joined'}}</span> {{format-date user.created_at leaveAgo="true"}}</h3>
|
||||
{{plugin-outlet "user-card-metadata"}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@ import afterTransition from 'discourse/lib/after-transition';
|
||||
import loadScript from 'discourse/lib/load-script';
|
||||
import avatarTemplate from 'discourse/lib/avatar-template';
|
||||
import positioningWorkaround from 'discourse/lib/safari-hacks';
|
||||
import { linkSeenMentions, fetchUnseenMentions } from 'discourse/lib/link-mentions';
|
||||
|
||||
const ComposerView = Discourse.View.extend(Ember.Evented, {
|
||||
_lastKeyTimeout: null,
|
||||
@ -175,13 +176,22 @@ const ComposerView = Discourse.View.extend(Ember.Evented, {
|
||||
$('a.onebox', $wmdPreview).each(function(i, e) {
|
||||
Discourse.Onebox.load(e, refresh);
|
||||
});
|
||||
$('span.mention', $wmdPreview).each(function(i, e) {
|
||||
Discourse.Mention.paint(e);
|
||||
});
|
||||
|
||||
const unseen = linkSeenMentions($wmdPreview, this.siteSettings);
|
||||
if (unseen.length) {
|
||||
Ember.run.debounce(this, this._renderUnseen, $wmdPreview, unseen, 500);
|
||||
}
|
||||
|
||||
this.trigger('previewRefreshed', $wmdPreview);
|
||||
},
|
||||
|
||||
_renderUnseen: function($wmdPreview, unseen) {
|
||||
fetchUnseenMentions($wmdPreview, unseen, this.siteSettings).then(() => {
|
||||
linkSeenMentions($wmdPreview, this.siteSettings);
|
||||
this.trigger('previewRefreshed', $wmdPreview);
|
||||
});
|
||||
},
|
||||
|
||||
_applyEmojiAutocomplete() {
|
||||
if (!this.siteSettings.enable_emoji) { return; }
|
||||
|
||||
|
||||
@ -50,7 +50,9 @@ export default Discourse.View.extend({
|
||||
|
||||
// deselects only when the user left click
|
||||
// (allows anyone to `extend` their selection using shift+click)
|
||||
if (e.which === 1 && !e.shiftKey) controller.deselectText();
|
||||
if (!window.getSelection().isCollapsed &&
|
||||
e.which === 1 &&
|
||||
!e.shiftKey) controller.deselectText();
|
||||
})
|
||||
.on('mouseup.quote-button', function(e) {
|
||||
view.selectText(e.target, controller);
|
||||
|
||||
@ -48,6 +48,7 @@
|
||||
//= require ./discourse/components/dropdown-button
|
||||
//= require ./discourse/components/notifications-button
|
||||
//= require ./discourse/components/topic-notifications-button
|
||||
//= require ./discourse/lib/link-mentions
|
||||
//= require ./discourse/views/composer
|
||||
//= require ./discourse/lib/show-modal
|
||||
//= require ./discourse/routes/discourse
|
||||
|
||||
@ -1449,8 +1449,8 @@ table#user-badges {
|
||||
}
|
||||
}
|
||||
|
||||
.url-list {
|
||||
.url {
|
||||
.value-list {
|
||||
.value {
|
||||
border-bottom: 1px solid #ddd;
|
||||
padding: 3px;
|
||||
margin-right: 10px;
|
||||
@ -1460,7 +1460,7 @@ table#user-badges {
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.urls {
|
||||
.values {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
|
||||
@ -68,7 +68,7 @@ aside.quote {
|
||||
}
|
||||
|
||||
.quote-controls, .quote-controls .back:before, .quote-controls .quote-other-topic:before {
|
||||
color: scale-color($primary, $lightness: 60%);
|
||||
color: scale-color($primary, $lightness: 70%);
|
||||
}
|
||||
|
||||
.cooked .highlight {
|
||||
|
||||
@ -234,12 +234,18 @@ nav.post-controls {
|
||||
// WARNING: overflow hide is required for quoted / embedded images
|
||||
// which expect "normal" post width, but expansions are narrower
|
||||
overflow: hidden;
|
||||
padding: 10px 15px 15px 15px;
|
||||
padding: 15px 15px 0 15px;
|
||||
}
|
||||
|
||||
// this is covered by .topic-body .regular on a normal post
|
||||
// but no such class structure exists for an embedded, expanded post
|
||||
.cooked {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.topic-avatar {
|
||||
padding-left: 15px;
|
||||
padding-top: 0;
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
// bottom means "reply expansion" below a post
|
||||
@ -266,7 +272,7 @@ nav.post-controls {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.post-date { color: scale-color($primary, $lightness: 50%); }
|
||||
.post-date { color: scale-color($primary, $lightness: 60%); }
|
||||
.fa-arrow-up, .fa-arrow-down { margin-left: 5px; }
|
||||
.reply:first-of-type .row { border-top: none; }
|
||||
|
||||
@ -282,7 +288,7 @@ nav.post-controls {
|
||||
color: scale-color($primary, $lightness: 30%);
|
||||
}
|
||||
}
|
||||
.arrow {color: scale-color($primary, $lightness: 50%);}
|
||||
.arrow {color: scale-color($primary, $lightness: 60%);}
|
||||
}
|
||||
|
||||
.post-action {
|
||||
|
||||
@ -212,6 +212,10 @@ class ApplicationController < ActionController::Base
|
||||
@guardian ||= Guardian.new(current_user)
|
||||
end
|
||||
|
||||
def current_homepage
|
||||
current_user ? SiteSetting.homepage : SiteSetting.anonymous_homepage
|
||||
end
|
||||
|
||||
def serialize_data(obj, serializer, opts=nil)
|
||||
# If it's an array, apply the serializer as an each_serializer to the elements
|
||||
serializer_opts = {scope: guardian}.merge!(opts || {})
|
||||
|
||||
@ -24,6 +24,10 @@ class CategoriesController < ApplicationController
|
||||
|
||||
discourse_expires_in 1.minute
|
||||
|
||||
unless current_homepage == 'categories'
|
||||
@title = I18n.t('js.filters.categories.title')
|
||||
end
|
||||
|
||||
store_preloaded("categories_list", MultiJson.dump(CategoryListSerializer.new(@list, scope: guardian)))
|
||||
respond_to do |format|
|
||||
format.html { render }
|
||||
@ -142,6 +146,7 @@ class CategoriesController < ApplicationController
|
||||
:background_url,
|
||||
:allow_badges,
|
||||
:slug,
|
||||
:custom_fields => [params[:custom_fields].try(:keys)],
|
||||
:permissions => [*p.try(:keys)])
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,13 +1,18 @@
|
||||
class EmbedController < ApplicationController
|
||||
skip_before_filter :check_xhr, :preload_json, :verify_authenticity_token
|
||||
|
||||
before_filter :ensure_embeddable
|
||||
|
||||
layout 'embed'
|
||||
|
||||
def comments
|
||||
embed_url = params.require(:embed_url)
|
||||
topic_id = TopicEmbed.topic_id_for_embed(embed_url)
|
||||
embed_url = params[:embed_url]
|
||||
|
||||
topic_id = nil
|
||||
if embed_url.present?
|
||||
topic_id = TopicEmbed.topic_id_for_embed(embed_url)
|
||||
else
|
||||
topic_id = params[:topic_id].to_i
|
||||
end
|
||||
|
||||
if topic_id
|
||||
@topic_view = TopicView.new(topic_id,
|
||||
@ -21,7 +26,8 @@ class EmbedController < ApplicationController
|
||||
if @topic_view && @topic_view.posts.size == SiteSetting.embed_post_limit
|
||||
@posts_left = @topic_view.topic.posts_count - SiteSetting.embed_post_limit - 1
|
||||
end
|
||||
else
|
||||
|
||||
elsif embed_url.present?
|
||||
Jobs.enqueue(:retrieve_topic, user_id: current_user.try(:id), embed_url: embed_url)
|
||||
render 'loading'
|
||||
end
|
||||
@ -30,7 +36,6 @@ class EmbedController < ApplicationController
|
||||
end
|
||||
|
||||
def count
|
||||
|
||||
embed_urls = params[:embed_url]
|
||||
by_url = {}
|
||||
|
||||
@ -53,8 +58,8 @@ class EmbedController < ApplicationController
|
||||
def ensure_embeddable
|
||||
|
||||
if !(Rails.env.development? && current_user.try(:admin?))
|
||||
raise Discourse::InvalidAccess.new('embeddable host not set') if SiteSetting.normalized_embeddable_host.blank?
|
||||
raise Discourse::InvalidAccess.new('invalid referer host') if URI(request.referer || '').host != SiteSetting.normalized_embeddable_host
|
||||
raise Discourse::InvalidAccess.new('embeddable hosts not set') if SiteSetting.embeddable_hosts.blank?
|
||||
raise Discourse::InvalidAccess.new('invalid referer host') unless SiteSetting.allows_embeddable_host?(request.referer)
|
||||
end
|
||||
|
||||
response.headers['X-Frame-Options'] = "ALLOWALL"
|
||||
|
||||
@ -20,7 +20,7 @@ class ExportCsvController < ApplicationController
|
||||
export_initiated_by_user_id = UserExport.where(id: export_id)[0].user_id unless UserExport.where(id: export_id).empty?
|
||||
export_csv_path = UserExport.get_download_path(filename)
|
||||
|
||||
if export_csv_path && export_initiated_by_user_id == current_user.id
|
||||
if export_csv_path && current_user.present? && export_initiated_by_user_id == current_user.id
|
||||
send_file export_csv_path
|
||||
else
|
||||
render nothing: true, status: 404
|
||||
|
||||
@ -60,7 +60,6 @@ class ListController < ApplicationController
|
||||
list_opts[:no_definitions] = true
|
||||
end
|
||||
|
||||
|
||||
list = TopicQuery.new(user, list_opts).public_send("list_#{filter}")
|
||||
list.more_topics_url = construct_url_with(:next, list_opts)
|
||||
list.prev_topics_url = construct_url_with(:prev, list_opts)
|
||||
@ -69,7 +68,7 @@ class ListController < ApplicationController
|
||||
@rss = filter
|
||||
|
||||
# Note the first is the default and we don't add a title
|
||||
if idx > 0 && use_crawler_layout?
|
||||
if (filter.to_s != current_homepage) && use_crawler_layout?
|
||||
filter_title = I18n.t("js.filters.#{filter.to_s}.title")
|
||||
if list_opts[:category]
|
||||
@title = I18n.t('js.filters.with_category', filter: filter_title, category: Category.find(list_opts[:category]).name)
|
||||
|
||||
@ -9,7 +9,7 @@ class PostsController < ApplicationController
|
||||
# Need to be logged in for all actions here
|
||||
before_filter :ensure_logged_in, except: [:show, :replies, :by_number, :short_link, :reply_history, :revisions, :latest_revision, :expand_embed, :markdown_id, :markdown_num, :cooked, :latest]
|
||||
|
||||
skip_before_filter :preload_json, :check_xhr, only: [:markdown_id, :markdown_num, :short_link]
|
||||
skip_before_filter :preload_json, :check_xhr, only: [:markdown_id, :markdown_num, :short_link, :latest]
|
||||
|
||||
def markdown_id
|
||||
markdown Post.find(params[:id].to_i)
|
||||
@ -42,16 +42,26 @@ class PostsController < ApplicationController
|
||||
# Remove posts the user doesn't have permission to see
|
||||
# This isn't leaking any information we weren't already through the post ID numbers
|
||||
posts = posts.reject { |post| !guardian.can_see?(post) }
|
||||
|
||||
counts = PostAction.counts_for(posts, current_user)
|
||||
|
||||
render_json_dump(serialize_data(posts,
|
||||
PostSerializer,
|
||||
scope: guardian,
|
||||
root: 'latest_posts',
|
||||
add_raw: true,
|
||||
all_post_actions: counts)
|
||||
)
|
||||
respond_to do |format|
|
||||
format.rss do
|
||||
@posts = posts
|
||||
@title = "#{SiteSetting.title} - #{I18n.t("rss_description.posts")}"
|
||||
@link = Discourse.base_url
|
||||
@description = I18n.t("rss_description.posts")
|
||||
render 'posts/latest', formats: [:rss]
|
||||
end
|
||||
format.json do
|
||||
render_json_dump(serialize_data(posts,
|
||||
PostSerializer,
|
||||
scope: guardian,
|
||||
root: 'latest_posts',
|
||||
add_raw: true,
|
||||
all_post_actions: counts)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def cooked
|
||||
|
||||
@ -175,10 +175,12 @@ class UsersController < ApplicationController
|
||||
end
|
||||
|
||||
def is_local_username
|
||||
params.require(:username)
|
||||
u = params[:username].downcase
|
||||
r = User.exec_sql('select 1 from users where username_lower = ?', u).values
|
||||
render json: {valid: r.length == 1}
|
||||
users = params[:usernames]
|
||||
users = [params[:username]] if users.blank?
|
||||
users.each(&:downcase!)
|
||||
|
||||
result = User.where(username_lower: users).pluck(:username_lower)
|
||||
render json: {valid: result}
|
||||
end
|
||||
|
||||
def render_available_true
|
||||
@ -345,7 +347,7 @@ class UsersController < ApplicationController
|
||||
end
|
||||
|
||||
def admin_login
|
||||
unless SiteSetting.enable_sso && !current_user
|
||||
if current_user
|
||||
return redirect_to path("/")
|
||||
end
|
||||
|
||||
|
||||
20
app/helpers/list_helper.rb
Normal file
20
app/helpers/list_helper.rb
Normal file
@ -0,0 +1,20 @@
|
||||
module ListHelper
|
||||
def page_links(topic)
|
||||
posts = topic.posts_count
|
||||
max_pages = 10
|
||||
total_pages = (posts / TopicView.chunk_size) + (posts == TopicView.chunk_size ? 0 : 1)
|
||||
|
||||
return if total_pages < 2
|
||||
|
||||
page = [total_pages - (max_pages+1), 2].max
|
||||
|
||||
result = "("
|
||||
while page <= total_pages
|
||||
result << " <a href='#{topic.relative_url}?page=#{page}'>#{page}</a> "
|
||||
page += 1
|
||||
end
|
||||
|
||||
result << ")"
|
||||
result.html_safe
|
||||
end
|
||||
end
|
||||
@ -11,7 +11,7 @@ module Jobs
|
||||
|
||||
# remove old unmatched IP addresses
|
||||
ScreenedIpAddress.where(action_type: ScreenedIpAddress.actions[:block])
|
||||
.where("last_match_at < ?", last_match_threshold)
|
||||
.where("last_match_at < ? OR (last_match_at IS NULL AND created_at < ?)", last_match_threshold, last_match_threshold)
|
||||
.destroy_all
|
||||
end
|
||||
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
require_dependency 'pinned_check'
|
||||
|
||||
class CategoryList
|
||||
include ActiveModel::Serialization
|
||||
|
||||
@ -17,6 +19,8 @@ class CategoryList
|
||||
|
||||
prune_empty
|
||||
find_user_data
|
||||
sort_unpinned
|
||||
trim_results
|
||||
end
|
||||
|
||||
private
|
||||
@ -151,4 +155,27 @@ class CategoryList
|
||||
@all_topics.each { |ft| ft.user_data = topic_lookup[ft.id] }
|
||||
end
|
||||
end
|
||||
|
||||
def sort_unpinned
|
||||
if @guardian.current_user && @all_topics.present?
|
||||
# Put unpinned topics at the end of the list
|
||||
@categories.each do |c|
|
||||
next if c.displayable_topics.blank? || c.displayable_topics.size <= latest_posts_count
|
||||
unpinned = []
|
||||
c.displayable_topics.each do |t|
|
||||
unpinned << t if t.pinned_at && PinnedCheck.unpinned?(t, t.user_data)
|
||||
end
|
||||
unless unpinned.empty?
|
||||
c.displayable_topics = (c.displayable_topics - unpinned) + unpinned
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def trim_results
|
||||
@categories.each do |c|
|
||||
next if c.displayable_topics.blank?
|
||||
c.displayable_topics = c.displayable_topics[0,latest_posts_count]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -8,6 +8,7 @@ class OptimizedImage < ActiveRecord::Base
|
||||
|
||||
def self.create_for(upload, width, height, opts={})
|
||||
return unless width > 0 && height > 0
|
||||
return if upload.try(:sha1).blank?
|
||||
|
||||
DistributedMutex.synchronize("optimized_image_#{upload.id}_#{width}_#{height}") do
|
||||
# do we already have that thumbnail?
|
||||
@ -25,7 +26,7 @@ class OptimizedImage < ActiveRecord::Base
|
||||
# create the thumbnail otherwise
|
||||
original_path = Discourse.store.path_for(upload)
|
||||
if original_path.blank?
|
||||
external_copy = Discourse.store.download(upload)
|
||||
external_copy = Discourse.store.download(upload) rescue nil
|
||||
original_path = external_copy.try(:path)
|
||||
end
|
||||
|
||||
@ -159,7 +160,7 @@ class OptimizedImage < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def self.convert_with(instructions, to)
|
||||
`convert #{instructions.join(" ")}`
|
||||
`convert #{instructions.join(" ")} &> /dev/null`
|
||||
return false if $?.exitstatus != 0
|
||||
|
||||
ImageOptim.new.optimize_image!(to)
|
||||
|
||||
@ -6,6 +6,7 @@ require_dependency 'enum'
|
||||
require_dependency 'post_analyzer'
|
||||
require_dependency 'validators/post_validator'
|
||||
require_dependency 'plugin/filter'
|
||||
require_dependency 'email_cook'
|
||||
|
||||
require 'archetype'
|
||||
require 'digest/sha1'
|
||||
@ -76,7 +77,7 @@ class Post < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def self.cook_methods
|
||||
@cook_methods ||= Enum.new(:regular, :raw_html)
|
||||
@cook_methods ||= Enum.new(:regular, :raw_html, :email)
|
||||
end
|
||||
|
||||
def self.find_by_detail(key, value)
|
||||
@ -161,16 +162,20 @@ class Post < ActiveRecord::Base
|
||||
# case we can skip the rendering pipeline.
|
||||
return raw if cook_method == Post.cook_methods[:raw_html]
|
||||
|
||||
# Default is to cook posts
|
||||
cooked = if !self.user || SiteSetting.tl3_links_no_follow || !self.user.has_trust_level?(TrustLevel[3])
|
||||
post_analyzer.cook(*args)
|
||||
else
|
||||
# At trust level 3, we don't apply nofollow to links
|
||||
cloned = args.dup
|
||||
cloned[1] ||= {}
|
||||
cloned[1][:omit_nofollow] = true
|
||||
post_analyzer.cook(*cloned)
|
||||
end
|
||||
cooked = nil
|
||||
if cook_method == Post.cook_methods[:email]
|
||||
cooked = EmailCook.new(raw).cook
|
||||
else
|
||||
cooked = if !self.user || SiteSetting.tl3_links_no_follow || !self.user.has_trust_level?(TrustLevel[3])
|
||||
post_analyzer.cook(*args)
|
||||
else
|
||||
# At trust level 3, we don't apply nofollow to links
|
||||
cloned = args.dup
|
||||
cloned[1] ||= {}
|
||||
cloned[1][:omit_nofollow] = true
|
||||
post_analyzer.cook(*cloned)
|
||||
end
|
||||
end
|
||||
|
||||
new_cooked = Plugin::Filter.apply(:after_post_cook, self, cooked)
|
||||
|
||||
|
||||
@ -68,9 +68,16 @@ class SiteSetting < ActiveRecord::Base
|
||||
@anonymous_menu_items ||= Set.new Discourse.anonymous_filters.map(&:to_s)
|
||||
end
|
||||
|
||||
def self.normalized_embeddable_host
|
||||
return embeddable_host if embeddable_host.blank?
|
||||
embeddable_host.sub(/^https?\:\/\//, '')
|
||||
def self.allows_embeddable_host?(host)
|
||||
return false if embeddable_hosts.blank?
|
||||
uri = URI(host) rescue nil
|
||||
|
||||
return false unless uri.present?
|
||||
|
||||
host = uri.host
|
||||
return false unless host.present?
|
||||
|
||||
!!embeddable_hosts.split("\n").detect {|h| h.sub(/^https?\:\/\//, '') == host }
|
||||
end
|
||||
|
||||
def self.anonymous_homepage
|
||||
|
||||
@ -662,7 +662,8 @@ class Topic < ActiveRecord::Base
|
||||
|
||||
{
|
||||
html: post.cooked,
|
||||
key: self.id
|
||||
key: self.id,
|
||||
url: self.url
|
||||
}
|
||||
end
|
||||
|
||||
@ -842,7 +843,7 @@ class Topic < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def expandable_first_post?
|
||||
SiteSetting.embeddable_host.present? && SiteSetting.embed_truncate? && has_topic_embed?
|
||||
SiteSetting.embeddable_hosts.present? && SiteSetting.embed_truncate? && has_topic_embed?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@ -41,7 +41,6 @@ class UserEmailObserver < ActiveRecord::Observer
|
||||
def enqueue(type)
|
||||
return unless notification.user.email_direct?
|
||||
|
||||
|
||||
Jobs.enqueue_in(delay,
|
||||
:user_email,
|
||||
type: type,
|
||||
@ -50,7 +49,8 @@ class UserEmailObserver < ActiveRecord::Observer
|
||||
end
|
||||
|
||||
def enqueue_private(type)
|
||||
return unless (notification.user.email_direct? && notification.user.email_private_messages?)
|
||||
return unless notification.user.email_private_messages?
|
||||
|
||||
Jobs.enqueue_in(delay,
|
||||
:user_email,
|
||||
type: type,
|
||||
|
||||
@ -10,7 +10,8 @@ class CategorySerializer < BasicCategorySerializer
|
||||
:email_in_allow_strangers,
|
||||
:can_delete,
|
||||
:cannot_delete_reason,
|
||||
:allow_badges
|
||||
:allow_badges,
|
||||
:custom_fields
|
||||
|
||||
def group_permissions
|
||||
@group_permissions ||= begin
|
||||
|
||||
@ -32,7 +32,8 @@ class TopicViewSerializer < ApplicationSerializer
|
||||
:category_id,
|
||||
:word_count,
|
||||
:deleted_at,
|
||||
:pending_posts_count
|
||||
:pending_posts_count,
|
||||
:user_id
|
||||
|
||||
attributes :draft,
|
||||
:draft_key,
|
||||
|
||||
@ -23,4 +23,4 @@
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<% content_for :title do %><%= I18n.t('js.filters.categories.title') %><% end %>
|
||||
<% content_for :title do %><%= @title %><% end %>
|
||||
|
||||
@ -21,10 +21,11 @@
|
||||
<a href='<%= t.relative_url %>' itemprop='item'>
|
||||
<span itemprop='name'><%= t.title %></span>
|
||||
</a>
|
||||
<%= page_links(t) %>
|
||||
<% if !@category && t.category %>
|
||||
<span>[<a href='<%= t.category.url %>'><%= t.category.name %></a>]</span>
|
||||
<% end %>
|
||||
<span title='<%= t 'posts' %>'>(<%= t.posts_count %>)</span>
|
||||
<span title='<%= t 'posts' %>'>(<a href="<%=t.last_post_url%>"><%= t.posts_count %></a>)</span>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
@ -40,6 +41,7 @@
|
||||
|
||||
<% if @rss %>
|
||||
<% content_for :head do %>
|
||||
<%= auto_discovery_link_tag(:rss, "#{Discourse.base_url}/posts.rss", title: I18n.t("rss_description.posts")) %>
|
||||
<%= auto_discovery_link_tag(:rss, { action: "#{@rss}_feed" }, title: I18n.t("rss_description.#{@rss}")) %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<rss version="2.0" xmlns:discourse="http://www.discourse.org/" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
<rss version="2.0" xmlns:discourse="http://www.discourse.org/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<channel>
|
||||
<% lang = SiteSetting.find_by_name('default_locale').try(:value) %>
|
||||
<% site_email = SiteSetting.find_by_name('contact_email').try(:value) %>
|
||||
@ -14,7 +14,7 @@
|
||||
<% topic_url = topic.url -%>
|
||||
<item>
|
||||
<title><%= topic.title %></title>
|
||||
<author><%= "no-reply@example.com (@#{topic.user.username}#{" #{topic.user.name}" if (topic.user.name.present? && SiteSetting.enable_names?)})" -%></author>
|
||||
<dc:creator><![CDATA[<%= "@#{topic.user.username}#{" #{topic.user.name}" if (topic.user.name.present? && SiteSetting.enable_names?)}" -%>]]></dc:creator>
|
||||
<category><%= topic.category.name %></category>
|
||||
<description><![CDATA[
|
||||
<p><%= t('author_wrote', author: link_to("@#{topic.user.username}", "#{Discourse.base_url}/users/#{topic.user.username_lower}")).html_safe %></p>
|
||||
|
||||
21
app/views/posts/latest.rss.erb
Normal file
21
app/views/posts/latest.rss.erb
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<rss version="2.0" xmlns:discourse="http://www.discourse.org/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<channel>
|
||||
<% lang = SiteSetting.find_by_name('default_locale').try(:value) %>
|
||||
<% site_email = SiteSetting.find_by_name('contact_email').try(:value) %>
|
||||
<title><%= @title %></title>
|
||||
<link><%= @link %></link>
|
||||
<description><%= @description %></description>
|
||||
<% @posts.each do |post| %>
|
||||
<% next unless post.user %>
|
||||
<item>
|
||||
<title><%= post.topic.title %></title>
|
||||
<dc:creator><![CDATA[<%= "@#{post.user.username}#{" #{post.user.name}" if (post.user.name.present? && SiteSetting.enable_names?)}" -%>]]></dc:creator>
|
||||
<description><![CDATA[ <%= post.cooked.html_safe %> ]]></description>
|
||||
<link><%= Discourse.base_url + post.url %></link>
|
||||
<pubDate><%= post.created_at.rfc2822 %></pubDate>
|
||||
<guid isPermaLink="false">post-<%= post.id %></guid>
|
||||
</item>
|
||||
<% end %>
|
||||
</channel>
|
||||
</rss>
|
||||
@ -4,14 +4,16 @@
|
||||
|
||||
<% @breadcrumbs = categories_breadcrumb(@topic_view.topic)
|
||||
if @breadcrumbs.present? %>
|
||||
<div itemscope itemtype='http://schema.org/BreadcrumbList'>
|
||||
<div id='breadcrumbs'>
|
||||
<% @breadcrumbs.each_with_index do |c,i| %>
|
||||
<span itemprop='itemListElement' itemscope itemtype='http://schema.org/ListItem'>
|
||||
<%= link_to c[:url], itemprop: 'item' do %>
|
||||
<span itemprop='name'><%= c[:name] %></span>
|
||||
<% end %>
|
||||
</span>
|
||||
<meta itemprop='position' content='<%= i + 1 %>'>
|
||||
<div id="breadcrumb-<%=i%>" itemscope itemtype="http://data-vocabulary.org/Breadcrumb"
|
||||
<%-if (i+1) < @breadcrumbs.length%>
|
||||
itemref="breadcrumb-<%=(i+1)%>"
|
||||
<%-end%>>
|
||||
<a href="<%=c[:url]%>" itemprop="url">
|
||||
<span itemprop="title"><%=c[:name]%></span>
|
||||
</a>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<channel>
|
||||
<% topic_url = @topic_view.absolute_url %>
|
||||
<% lang = SiteSetting.find_by_name('default_locale').try(:value) %>
|
||||
@ -15,7 +15,7 @@
|
||||
<% next unless post.user %>
|
||||
<item>
|
||||
<title><%= @topic_view.title %></title>
|
||||
<author><%= "no-reply@example.com (@#{post.user.username}#{" #{post.user.name}" if (post.user.name.present? && SiteSetting.enable_names?)})" -%></author>
|
||||
<dc:creator><![CDATA[<%= "@#{post.user.username}#{" #{post.user.name}" if (post.user.name.present? && SiteSetting.enable_names?)}" -%>]]></dc:creator>
|
||||
<description><, just start typing `:` or the traditional smileys `:)` :smile:
|
||||
<img src="/images/welcome/username-completion-2x.png" width="191" height="125">
|
||||
|
||||
## What else can I do?
|
||||
To use [standard Emoji](http://www.emoji.codes/), just type `:` to match by name, or use the traditional smileys `;)`
|
||||
|
||||
There are action buttons at the bottom of each post.
|
||||
<img src="/images/welcome/emoji-completion-2x.png" width="144" height="153">
|
||||
|
||||
<img src="/images/welcome/like-link-flag-bookmark.png" width="169" height="46">
|
||||
## Actions
|
||||
|
||||
To let someone know that you enjoyed their post, use the **like** button. If you see a problem with a post, privately let them, or our staff, know about it with the **flag** button.
|
||||
There are action buttons at the bottom of each post:
|
||||
|
||||
You can also **share** a link to a post, or **bookmark** it for later reference on your user page.
|
||||
<img src="/images/welcome/like-link-flag-bookmark-2x.png" width="162" height="42">
|
||||
|
||||
## Who is talking to me?
|
||||
To let someone know that you enjoyed and appreciated their post, use the **like** button. Share the love!
|
||||
|
||||
When someone replies to your post, quotes your post, or mentions your `@username`, a number will immediately appear at the top right of the page. Use it access your **notifications**.
|
||||
If you see a problem with someone's post, privately let them, or [our staff](/about), know about it with the **flag** button. You can also **share** a link to a post, or **bookmark** it for later reference on your user page.
|
||||
|
||||
<img src="/images/welcome/notification-panel.png" width="178" height="59">
|
||||
## Notifications
|
||||
|
||||
Don't worry about missing a reply – you'll be emailed replies (and messages) if you aren't online when they arrive.
|
||||
When someone replies to you, quotes your post, or mentions your `@username`, a number will immediately appear at the top right of the page. Use it to access your **notifications**.
|
||||
|
||||
## When are conversations new?
|
||||
<img src="/images/welcome/notification-panel-2x.png" width="160" height="54">
|
||||
|
||||
By default all conversations less than two days old are considered new, and any conversation you've participated in (replied to, created, or read for an extended period) will automatically be tracked.
|
||||
Don't worry about missing a reply – you'll be emailed any notifications that arrive when you are away.
|
||||
|
||||
You will see the blue new and number indicators next to these topics:
|
||||
## Your Preferences
|
||||
|
||||
<img src="/images/welcome/topics-new-unread.png" width="368" height="89">
|
||||
- All topics less than **two days old** are considered new.
|
||||
|
||||
You can change the individual notification state of a topic via the control at the bottom of the topic (this can also be set per category). To change how you track topics, or the definition of new, see [your user preferences](%{base_url}/my/preferences).
|
||||
- Any topic you've **actively participated in** (by creating it, replying, or reading for an extended period) will be automatically tracked.
|
||||
|
||||
## Why can't I do certain things?
|
||||
You will see the blue new and unread number indicators next to these topics:
|
||||
|
||||
New users are somewhat limited for safety reasons. As you participate here, you'll gain the trust of the community, become a full citizen, and those limitations will automatically be removed. At a high enough [trust level](https://meta.discourse.org/t/what-do-user-trust-levels-do/4924), you'll gain even more abilities to help us manage our community together.
|
||||
<img src="/images/welcome/topics-new-unread-2x.png" width="341" height="106">
|
||||
|
||||
You can change your notifications for any topic via the notification control at the bottom of the topic. You can also set notification state per category.
|
||||
|
||||
<img src="/images/welcome/topic-notification-control-2x.png" width="608" height="312">
|
||||
|
||||
To change any of these settings, see [your user preferences](%{base_url}/my/preferences).
|
||||
|
||||
## Community Trust
|
||||
|
||||
New users are somewhat limited for safety reasons. As you participate here, over time you'll gain the trust of the community, become a full citizen, and those limitations will be lifted. At a high enough [trust level](https://meta.discourse.org/t/what-do-user-trust-levels-do/4924), you'll gain new abilities to help us manage our community together.
|
||||
|
||||
welcome_user:
|
||||
subject_template: "Welcome to %{site_name}!"
|
||||
|
||||
@ -133,6 +133,7 @@ es:
|
||||
rss_description:
|
||||
latest: "Temas recientes"
|
||||
hot: "Temas calientes"
|
||||
posts: "Últimos posts"
|
||||
too_late_to_edit: "Ese post fue publicado hace demasiado tiempo. No puede ser editado ni eliminado."
|
||||
excerpt_image: "imagen"
|
||||
queue:
|
||||
@ -959,7 +960,7 @@ es:
|
||||
warn_reviving_old_topic_age: "Cuando alguien publica en un tema cuya última respuesta fue hace este número de días o más, se le mostrará un aviso para desalentar el hecho de resucitar una antigua discusión. Deshabilita esta opción introduciendo el valor 0."
|
||||
autohighlight_all_code: "Forzar el resaltado de código a los bloques de código preformateado cuando no se especifique el lenguaje del código."
|
||||
highlighted_languages: "Incluye reglas resaltadas de sintaxis. (Advertencia: incluyendo demasiadas lenguages puede afectar al rendimiento) ver: https://highlightjs.org/static/demo/ para una demostración"
|
||||
embeddable_host: "Host que puede incrustar los comentarios de este foro Discourse. Nombre de host solamente, no comienzan con http://"
|
||||
embeddable_hosts: "Host(s) que puede incrustar los comentarios de este foro Discourse. Nombre de host solamente, no comienzan con http://"
|
||||
feed_polling_enabled: "SOLO PARA EMBEBER: embeber feeds RSS/ATOM como posts."
|
||||
feed_polling_url: "SOLO PARA EMBEBER: URL de los feeds RSS/ATOM a embeber."
|
||||
embed_by_username: "Nombre de usuario en Discourse del que crea los temas embebidos."
|
||||
|
||||
@ -916,7 +916,7 @@ fa_IR:
|
||||
warn_reviving_old_topic_age: "وقتی کسی شروع می کند با پاسخ دادن به جستاری که آخرین پاسخ برمی گردد به خیلی قبل یک هشدار نمایش داده می شود. نمایش با تنظیمات تا 0. "
|
||||
autohighlight_all_code: "اعمال زور برای برجسته کردن کد به تمام بلاک های کد تنظیم نشده حتی وقتی به صراحت زبان را مشخص نمی کنند. "
|
||||
highlighted_languages: "شامل نحوه قوانین برجسته شده. ( اخطار: شامل زبان های متفاوت ممکن است در نحوه اجرا تاثیر گذار باشد) ببین: https://highlightjs.org/static/demo/ برای دمو"
|
||||
embeddable_host: "سروری که می تونه در نوشته ها جا سازی بشه از انجمن دیسکورس. فقط نام سروری که با http:// شروع می شود"
|
||||
embeddable_hosts: "سروری که می تونه در نوشته ها جا سازی بشه از انجمن دیسکورس. فقط نام سروری که با http:// شروع می شود"
|
||||
feed_polling_enabled: "فقط جاسازی: چه جاسازی RSS/ATOM feed به عنوان نوشته"
|
||||
feed_polling_url: "فقط جاسازی: URL of RSS/ATOM feed to embed."
|
||||
embed_by_username: "نام کاربری دیسکورس کاربری که نوشته های جاسازی شده را ساخته."
|
||||
|
||||
@ -962,7 +962,7 @@ fi:
|
||||
warn_reviving_old_topic_age: "Kun käyttäjä alkaa kirjoittamaan vastausta ketjuun, jonka uusin viesti on tätä vanhempi päivissä, näytetään varoitus. Poista käytöstä asettamalla arvoksi 0."
|
||||
autohighlight_all_code: "Pakota koodin korostus kaikkiin esimuotoiltuihin tekstiblokkeihin, vaikka käyttäjä ei määrittelisi kieltä."
|
||||
highlighted_languages: "Syntaksin korostamisen säännöt. (Varoitus: liian monen kielen sisällyttäminen voi vaikuttaa suorituskykyyn) katso demo: https://highlightjs.org/static/demo/"
|
||||
embeddable_host: "Isäntä, joka voi upottaa kommentteja tältä palstalta. Pelkkä isäntänimi, älä aloita http://"
|
||||
embeddable_hosts: "Isäntä, joka voi upottaa kommentteja tältä palstalta. Pelkkä isäntänimi, älä aloita http://"
|
||||
feed_polling_enabled: "VAIN UPOTUS: Upotetaanko RSS/ATOM syöte viesteinä."
|
||||
feed_polling_url: "VAIN UPOTUS: RSS/ATOM syötteen URL."
|
||||
embed_by_username: "Sen käyttäjän Discourse käyttäjänimi, joka luo upotetut ketjut."
|
||||
|
||||
@ -133,6 +133,7 @@ fr:
|
||||
rss_description:
|
||||
latest: "Sujets récents"
|
||||
hot: "Sujets populaires"
|
||||
posts: "Messages récents"
|
||||
too_late_to_edit: "Ce message a été créé il y a trop longtemps. Il ne peut plus être modifié ou supprimé."
|
||||
excerpt_image: "image"
|
||||
queue:
|
||||
@ -963,7 +964,7 @@ fr:
|
||||
warn_reviving_old_topic_age: "Lorsque quelqu'un commence à répondre à un sujet dont la dernière réponse est vielle de plusieurs jours, un avertissement sera affiché. Désactiver la fonctionnalité en indiquant: 0."
|
||||
autohighlight_all_code: "Forcer la mise en évidence de tout les textes dans les balises code, même si ils ne correspondent à aucun langage de programmation."
|
||||
highlighted_languages: "Include les règles de mise en surbrillance de syntaxe. (Avertissement: l'ajout de trop de langages peut impacter les performances) voir l'exemple https://highlightjs.org/static/demo/"
|
||||
embeddable_host: "Hôte qui peut incorporer des messages de ce forum Discourse. Nom d'hôte seulement, sans http://"
|
||||
embeddable_hosts: "Hôte(s) qui peuvent incorporer des messages de ce forum Discourse. Nom d'hôte seulement, sans http://"
|
||||
feed_polling_enabled: "EMBARQUER UNIQUEMENT: Embarqué le flux RSS/ATOM en tant que messages."
|
||||
feed_polling_url: "EMBARQUER UNIQUEMENT: Url du flux RSS/ATOM à embarqué."
|
||||
embed_by_username: "Pseudo de l'utilisateur Discourse qui crée les sujets embarqués."
|
||||
@ -1265,10 +1266,10 @@ fr:
|
||||
Afin d'être guider, merci de vous référer à notre [guide de bonne conduite](%{base_url}/guidelines).
|
||||
|
||||
usage_tips:
|
||||
text_body_template: "Quelques astuces pour vous aider à démarrer rapidement.\n\n## Continuez de descendre\n\nIl n'y a pas de bouton Page Suivante ni de numéro de page – pour en lire plus, **il vous suffit de descendre !**\n\nLorsque de nouvelles réponses arrivent, elles apparaissent automatiquement. \n\n## Où suis-je ?\n\n- Pour la recherche, votre page d'utilisateur, ou le menu, utiliser les **boutons icônes <kbd>☰</kbd> en haut à droite**.\n\n- Dans la liste des sujets, le titre vous emmènera toujours vers le prochain message non lu. Utiliser les colonnes Activité ou Messages pour allez au premier ou au dernier message. \n\n- Lorsque vous lisez un sujet, retournez en haut de celui ↑ en cliquant sur le titre. Cliquez sur la barre de progression en bas à droite pour avoir une navigation complète, ou utilisez les touches <kbd>Home</kbd> et <kbd>Fin</kbd>. \n\n <img src=\"/images/welcome/progress-bar.png\" width=\"143\" height=\"39\">\n\n## Comment je réponds ?\n\n- Pour répondre au sujet dans sa globalité, utilisez le bouton \"Répondre\" <img src=\"/images/welcome/reply-topic.png\" width=\"29\" height=\"25\"> tout en bas de la page.\n\n- Pour répondre à un message spécifique, utilisez le bouton \"Répondre\" <img src=\"/images/welcome/reply-post.png\" width=\"29\" height=\"25\">\
|
||||
\ sur le message.\n\n - Si vous voulez continuer le sujet dans une section différente, mais garder le lien entre votre sujet et le message qui vous l'a inspiré, utilisez la fonction Répondre en créant un nouveau sujet <img src=\"/images/welcome/reply-as-linked-topic.png\" width=\"15\" height=\"25\"> à droite de chaque message.\n\nPour citer quelqu'un dans votre message, sélectionnez le texte que vous voulez citer et appuyez sur un des boutons Répondre.\n\n<img src=\"/images/welcome/quote-reply.png\" width=\"350\" height=\"129\">\n\nPour mentionner le pseudo d'un utilisateur, commencez à taper `@` et une liste d'auto-complétion apparaîtra.\n\n<img src=\"/images/welcome/username-completion.png\" width=\"230\" height=\"131\">\n\nConcernant les [icones Emoji](http://www.emoji.codes/), commencez par écrire `:` ou le traditionnel smiley `:)` :smile: \n\n## Que puis-je faire d'autre ?\n\nÀ la fin de chaque message il y a un ensemble de boutons pour les différentes actions possibles.\n\n<img src=\"/images/welcome/like-link-flag-bookmark.png\" width=\"169\" height=\"46\">\n\nPour faire savoir à quelqu'un que vous avez apprécié son message, cliquez sur le bouton *j'aime** en bas du message. Si vous voyez un problème avec un message, n'hésitez pas à cliquer sur le bouton **signaler** pour avertir\
|
||||
\ en privé l'auteur ou les modérateurs du contenu de ce message.\n\nVous pouvez aussi **partager** un liens vers un message ou l'ajouter à vos **signets** pour le retrouver sur votre page d'utilisateur. \n\n## Qui me parle ?\n\nQuand quelqu'un répond à votre message, vous cite, ou mentionne votre `@pseudo`, un nombre apparaitra immédiatement en haut à droite de la page. Utilisez-le pour consulter vos **notifications**.\n\n<img src=\"/images/welcome/notification-panel.png\" width=\"178\" height=\"59\">\n\nNe soyez pas inquiêt de manquer des notifications – \nLes réponses (et les messages directs) vous seront envoyés par courriel si vous n'êtes pas présent sur le site lorsqu'ils sont envoyés. \n\n## Quand est-ce qu'une conversation est nouvelle ?\n\nPar défaut, toutes les conversations de moins de deux jours sont considérées comme nouvelles, et chaque sujets auxquels vous avez participé (répondus, créés ou lu pendant un long moment) seront automatiquements suivis. \n\nVous verrez le badge bleu de nouveauté et le nombre de nouveaux messages au côté de ces discussions:\n\n<img src=\"/images/welcome/topics-new-unread.png\" width=\"368\" height=\"89\">\n\nVous pouvez modifier l'état du suivi des notifications d'une discussion en utilisant les boutons en bas de la discussion (vous pouvez également\
|
||||
\ le modifier au niveau des catégories). Pour changer la façon dont vous suivez les discussions, ou pour définir de nouveaux suivis, consultez [vos préférences utilisateurs](%{base_url}/my/preferences). \n\n## Quelles sont les choses que je ne peux pas faire ?\n\nLes nouveaux utilisateurs sont limités pour des raisons de sécurité. Plus vous participerez, plus vous gagnerez la confiance de la communauté et les restrictions disparaîtrons. A plus haut [niveau de confiance](https://meta.discourse.org/t/what-do-user-trust-levels-do/4924), vous gagnerez de nouvelles compétences pour nous aider à gérer la communauté.\n"
|
||||
text_body_template: "Voilà quelques astuces pour vous aider à démarrer :\n\n## Continuez de descendre\n\nPour en lire plus, **il vous suffit de descendre !** Lorsque de nouvelles réponses arrivent, elles apparaissent automatiquement. \n\n## Navigation\n\n- Pour la recherche, votre page d'utilisateur, ou le menu, utiliser les **boutons icônes <kbd>☰</kbd> en haut à droite**. \n\n- Dans la liste des sujets, le titre vous emmènera toujours vers le prochain message non lu. Cliquer sur la date ou le nombre de messages pour allez au premier ou au dernier message. \n\n- Lorsque vous lisez un sujet, retournez en haut ↑ en cliquant sur le titre. Cliquez sur la barre de progression en bas à droite pour avoir une navigation complète, ou utilisez les touches <kbd>Home</kbd> et <kbd>Fin</kbd>. \n\n<img src=\"/images/welcome/progress-bar.png\" width=\"143\" height=\"39\"> \n\n## Réponses\n\nPour répondre …\n\n- au **sujet dans sa globalité**, utilisez le bouton \"Répondre\" <img src=\"/images/welcome/reply-topic.png\" width=\"29\" height=\"25\"> tout en bas de la page. \n\n- à une **personne spécifique**, utilisez le bouton \"Répondre\" <img src=\"/images/welcome/reply-post.png\" width=\"29\" height=\"25\"> sur leur message. \n\n- avec **un sujet lié**, utilisez le bouton \"Répondre en créant un\
|
||||
\ sujet lié\" <img src=\"/images/welcome/reply-as-linked-topic.png\" width=\"15\" height=\"25\"> à droite de chaque message. \n\nPour citer, sélectionnez le texte que vous voulez citer et appuyez sur un des boutons Répondre. \n\n<img src=\"/images/welcome/quote-reply.png\" width=\"350\" height=\"129\"> \n\nPour alerter un utilisateur dans votre message, commencez à taper `@` pour pouvoir sélectionner un pseudo.\n\n<img src=\"/images/welcome/username-completion.png\" width=\"230\" height=\"131\">\n\nConcernant les [icones Emoji](http://www.emoji.codes/), commencez par écrire `:` ou le traditionnel smiley `:)` :smile: \n\n## Actions \n\nÀ la fin de chaque message il y a des boutons pour les différentes actions possibles. \n\n<img src=\"/images/welcome/like-link-flag-bookmark.png\" width=\"169\" height=\"46\"> \n\nPour faire savoir à quelqu'un que vous avez apprécié son message, cliquez sur le bouton **j'aime** en bas du message. Si vous voyez un problème avec un message, n'hésitez pas à cliquer sur le bouton **signaler** pour avertir en privé l'auteur ou les modérateurs du contenu de ce message. \n\nVous pouvez aussi **partager** un liens vers un message ou l'ajouter à vos **signets** pour le retrouver sur votre page d'utilisateur. \n\n## Notifications\n\nQuand quelqu'un répond à votre\
|
||||
\ message, vous cite, ou mentionne votre `@pseudo`, un nombre apparaitra immédiatement en haut à droite de la page. Utilisez-le pour consulter vos **notifications**. \n\n<img src=\"/images/welcome/notification-panel.png\" width=\"178\" height=\"59\"> \n\nNe soyez pas inquiêt de manquer une réponse – vos notifications vous seront envoyées par courriel si vous n'êtes pas présent sur le site lorsqu'ils sont envoyés. \n\n## Vos préférences \n\nPar défaut, toutes les conversations de moins de deux jours sont considérées comme nouvelles, et chaque sujets auxquels vous avez participé (répondus, créés ou lu pendant un long moment) seront automatiquements suivis. \n\nVous verrez le badge bleu de nouveauté et le nombre de nouveaux messages au côté de ces discussions: \n\n<img src=\"/images/welcome/topics-new-unread.png\" width=\"368\" height=\"89\"> \n\nVous pouvez modifier l'état du suivi des notifications d'une discussion en utilisant les boutons en bas de la discussion (vous pouvez également le modifier au niveau des catégories). Pour changer la façon dont vous suivez les discussions, ou pour définir de nouveaux suivis, consultez [vos préférences utilisateurs](%{base_url}/my/preferences). \n\n## Confiance de la communauté\n\nLes nouveaux utilisateurs sont limités pour des raisons de sécurité.\
|
||||
\ Plus vous participerez, plus vous gagnerez la confiance de la communauté et les restrictions disparaîtront. A plus haut [niveau de confiance](https://meta.discourse.org/t/what-do-user-trust-levels-do/4924), vous gagnerez de nouvelles compétences pour nous aider à gérer la communauté.\n"
|
||||
welcome_user:
|
||||
subject_template: "Bienvenue sur %{site_name} !"
|
||||
text_body_template: "Merci d'avoir rejoint %{site_name} et bienvenue ! \n\n%{new_user_tips}\n\nNous croyons au [comportement communautaire civilisé](%{base_url}/guidelines) en tous temps. \n\nAmusez-vous bien ! \n\n(Si, en tant que nouvel utilisateur, vous avez besoin de communiquer avec un [responsable](%{base_url}/about), répondez simplement à ce message.)\n"
|
||||
|
||||
@ -553,7 +553,7 @@ he:
|
||||
http_background_reqs:
|
||||
title: "רקע"
|
||||
xaxis: "יום"
|
||||
yaxis: "בשקות שמשמשות לעדכונים חיים ולמעקב"
|
||||
yaxis: "בקשות שמשמשות לעדכונים חיים ולמעקב"
|
||||
http_2xx_reqs:
|
||||
title: "סטטוס 2xx (OK)"
|
||||
xaxis: "יום"
|
||||
@ -925,7 +925,7 @@ he:
|
||||
warn_reviving_old_topic_age: "כאשר מישהו/מישהי מתחילים להגיב לנושא שבו התגובה האחרונה היא בת יותר מכמה ימים, אזהרה תוצג. בטלו אפשרות זו באמצעות הזנה של 0."
|
||||
autohighlight_all_code: "לחייב שימוש בקוד הדגשה לכל קוד מעוצב מראש (preformatted code blocks) אפילו אם הם אינם מציינים את השפה."
|
||||
highlighted_languages: "הכללת הדגשת שגיאות תחביר. (אזהרה: הכללת שפות רבות מידי עשוי להשפיע על הביצוע) ראו: https://highlightjs.org/static/demo/ להדגמה"
|
||||
embeddable_host: "מארח (Host) אשר יכול להטמיע את התגובות מפורום Discourse זה. שם מארח בלבד, ללא http:// בהתחלה"
|
||||
embeddable_hosts: "מארח (Host) אשר יכול להטמיע את התגובות מפורום Discourse זה. שם מארח בלבד, ללא http:// בהתחלה"
|
||||
feed_polling_enabled: "הטמעה בלבד: האם לעמבד פידים של RSS/ATOM כפרסומים."
|
||||
feed_polling_url: "הטמעה בלבד: URL של פיד RSS/ATOM להטמעה."
|
||||
embed_by_username: "שם המשתמש של המשתמש/ת שיוצר את הנושאים המוטמעים."
|
||||
@ -1084,7 +1084,7 @@ he:
|
||||
>
|
||||
> %{site_description}
|
||||
|
||||
אם אינכם מעוניינים לחצו על הקישור הבא:
|
||||
אם הינכם מעוניינים לחצו על הקישור הבא:
|
||||
|
||||
%{invite_link}
|
||||
|
||||
|
||||
@ -340,6 +340,7 @@ it:
|
||||
one: "quasi 1 anno fa"
|
||||
other: "quasi %{count} anni fa"
|
||||
password_reset:
|
||||
no_token: "Siamo spiacenti ma il collegamento per il cambio password è troppo vecchio. Collegati di nuovo e seleziona 'Ho dimenticato la password' per avere un nuovo collegamento."
|
||||
choose_new: "Per favore scegli una nuova password."
|
||||
choose: "Per favore scegli una password"
|
||||
update: 'Aggiorna Password'
|
||||
@ -359,6 +360,7 @@ it:
|
||||
continue_button: "Procedi su %{site_name}."
|
||||
welcome_to: "Benvenuto su %{site_name}!"
|
||||
approval_required: "Un moderatore deve approvare manualmente il tuo account prima che tu possa accedere al forum. Riceverai un'email quando il tuo account sarà approvato!"
|
||||
missing_session: "Non possiamo verificare che il tuo account sia stato creato, per favore assicurati di avere i cookie abilitati nelle impostazioni del tuo browser."
|
||||
post_action_types:
|
||||
off_topic:
|
||||
title: 'Fuori Tema'
|
||||
|
||||
@ -837,7 +837,7 @@ ja:
|
||||
warn_reviving_old_topic_age: "最後の返信がこの設定よりも古いトピックに返信すると、警告を表示します。0を設定すると無効になります"
|
||||
autohighlight_all_code: "明示的に言語を指定しなくても、全てのコードブロックにコードハイライトを強制的に適用する"
|
||||
highlighted_languages: "適用するシンタックスハイライトルール(警告: あまりに多くの言語を含むとパフォーマンスに影響を与えます) デモ: https://highlightjs.org/static/demo/ "
|
||||
embeddable_host: "このDiscourseフォーラムのコメントを埋め込む事が出来るHost。Host名のみ、http:// で始めない"
|
||||
embeddable_hosts: "このDiscourseフォーラムのコメントを埋め込む事が出来るHost。Host名のみ、http:// で始めない"
|
||||
feed_polling_enabled: "EMBEDDING ONLY: ポストとしてRSS/Atomフィードを埋め込むかどうか"
|
||||
feed_polling_url: "EMBEDDING ONLY: RSS/ATOMフィードのURLを埋め込む事が出来ます"
|
||||
embed_by_username: "embedされたトピックの作成者として表示されるDiscourseユーザー名"
|
||||
|
||||
@ -950,7 +950,7 @@ pt:
|
||||
warn_reviving_old_topic_age: "Quando alguém começa a responder a um tópico em que a última resposta é mais antiga que estes dias, um aviso será exibido. Desativar ao configurar para 0."
|
||||
autohighlight_all_code: "Forçar o destaque do código a todos os blocos de código pré-formatados mesmo quando não se especifica a linguagem."
|
||||
highlighted_languages: "Incluídas regras de destaque da sintaxe (Aviso: incluir demasiadas linguagens pode impactar o desempenho) ver: https://highlightjs.org/static/demo/ para uma demonstração"
|
||||
embeddable_host: "Servidor que pode incorporar os comentários deste fórum Discourse. Apenas o nome do servidor, não começar com http://"
|
||||
embeddable_hosts: "Servidor que pode incorporar os comentários deste fórum Discourse. Apenas o nome do servidor, não começar com http://"
|
||||
feed_polling_enabled: "INCORPORAR APENAS: incorporar feeds RSS/ATOM como mensagens."
|
||||
feed_polling_url: "INCORPORAR APENAS: URL dos feeds de RSS/ATOM para embutir."
|
||||
embed_by_username: "Nome de utilizador Discourse do utilizador que cria tópicos embebidos."
|
||||
|
||||
@ -950,7 +950,7 @@ pt_BR:
|
||||
warn_reviving_old_topic_age: "Quando alguém começa a responder a um tópico mais velho do que este número de dias, um aviso será exibido para desencorajar o usuário de reviver uma velha discussão. Desabilite definindo para 0."
|
||||
autohighlight_all_code: "Aplicar código destacando todos os blocos de código pré-formatados, mesmo quando não for específica o idioma"
|
||||
highlighted_languages: "Incluir regras de destaque de sintaxe. (AVISO: incluir muitas linguagens podem afetar a performance) veja: https://highlightjs.org/static/demo/ para uma demonstração"
|
||||
embeddable_host: "Servidor que pode incorporar os comentários deste forum Discourse. Apenas o Nome do Servidor, não começar com http://"
|
||||
embeddable_hosts: "Servidor que pode incorporar os comentários deste forum Discourse. Apenas o Nome do Servidor, não começar com http://"
|
||||
feed_polling_enabled: "Se um feed RSS / ATOM são importados como mensagens"
|
||||
feed_polling_url: "URL do feed RSS / ATOM para importar"
|
||||
embed_by_username: "Nome de usuário Discourse para o usuário que cria os tópicos"
|
||||
|
||||
@ -874,6 +874,7 @@ ru:
|
||||
s3_access_key_id: "Amazon S3 access key для загрузки и хранения изображений"
|
||||
s3_secret_access_key: "Amazon S3 secret key для загрузки и хранения изображений"
|
||||
s3_region: "Географический регион Amazon S3, который будет использоваться для хранения изображений"
|
||||
avatar_sizes: "Список автоматически сгенерированных размеров аватар."
|
||||
enable_flash_video_onebox: "Разрешить умную вставку ссылок sqf и flv (Adobe Flash). ВНИМАНИЕ: повышает риски безопасности сайта."
|
||||
default_invitee_trust_level: "Уровень доверия приглашенных пользователей по-умолчанию (от 0 до 4)."
|
||||
default_trust_level: "Уровень доверия по умолчанию для всех новых пользователей (0-4). ВНИМАНИЕ! Повышая уровень доверия для новых пользователей, Вы можете столкнуться с большим колличеством спама."
|
||||
@ -1005,7 +1006,7 @@ ru:
|
||||
warn_reviving_old_topic_age: "Показывать предупреждение, когда кто-то пытается ответить в очень старую тему. Указывается в днях. Чтобы отключить 0."
|
||||
autohighlight_all_code: "Принудительно использовать подсветку кода для всех отформатированных блоков кода, даже когда они явно не указан язык."
|
||||
highlighted_languages: "Включить правила подсветки синтаксиса. (ВНИМАНИЕ: включение для многих языков может вызвать проблемы с производительностью) пример можно посмотреть на: https://highlightjs.org/static/demo/ "
|
||||
embeddable_host: "Имя хоста которому разрешено использовать комменты с данного форума. Не указывайте http://"
|
||||
embeddable_hosts: "Имя хоста которому разрешено использовать комменты с данного форума. Не указывайте http://"
|
||||
feed_polling_enabled: "ТОЛЬКО ДЛЯ ВЛОЖЕННЫХ: Встраивать ли вложенные сообщения в RSS/ATOM ленту"
|
||||
feed_polling_url: "URL адрес импорта RSS/ATOM ленты"
|
||||
embed_by_username: "Имя пользователя который созал вложенную тему"
|
||||
@ -1018,6 +1019,7 @@ ru:
|
||||
notify_about_flags_after: "Если есть жалобы, которые не были обработаны после этого колличества часов, отправить уведомление по электронной почте на contact_email. Значение 0, чтобы отключить."
|
||||
enable_cdn_js_debugging: "Добавить права crossorigin для всех js включений для правильного отображения ошибок в /logs "
|
||||
show_create_topics_notice: "Если общее количество тем меньше 5, показывать персоналу соощбение с просьбой создать новые темы."
|
||||
delete_drafts_older_than_n_days: Удалять черновики, которые старше (n) дней.
|
||||
vacuum_db_days: "Запускать VACUUM FULL ANALYZE для восстановления базы данных после миграций ( 0 отключает)"
|
||||
prevent_anons_from_downloading_files: "Запретить анонимным пользователям скачивать вложенные файлы. ВНИМАНИЕ: при этом будут недоступны любые вложения кроме картинок."
|
||||
slug_generation_method: "Выберите метод генерации URL. 'encoded' будет изпользовать русские буквы в URL закодированные через проценты. 'none' не будет использовать перегенирацию."
|
||||
@ -1142,6 +1144,7 @@ ru:
|
||||
not_allowed_from_ip_address: "Вход с этого IP в качестве пользователя %{username} запрещен."
|
||||
admin_not_allowed_from_ip_address: "Вход с этого IP в качестве администратора запрещен."
|
||||
suspended: "Вы не можете войти до %{date}."
|
||||
suspended_with_reason: "Учетная запись заблокирована до %{date}: %{reason}"
|
||||
errors: "%{errors}"
|
||||
not_available: "Недоступно. Попробуйте %{suggestion}?"
|
||||
something_already_taken: "Что-то пошло не так, возможно, имя пользователя или электронный ящик уже используются. Попробуйте восстановить ваш пароль."
|
||||
@ -1163,6 +1166,9 @@ ru:
|
||||
email:
|
||||
not_allowed: "недопустимый почтовый домен. Пожалуйста, используйте другой адрес."
|
||||
blocked: "не разрешено."
|
||||
ip_address:
|
||||
blocked: "Новые регистрации запрещены с вашего IP-адреса."
|
||||
max_new_accounts_per_registration_ip: "Новые регистрации запрещены с вашего IP-адреса (достигнут лимит регистраций). Свяжитесь с администрацией."
|
||||
invite_mailer:
|
||||
subject_template: "%{invitee_name} приглашает вас присоединиться к '%{topic_title}' на сайте %{site_domain_name}"
|
||||
text_body_template: |
|
||||
@ -2124,3 +2130,5 @@ ru:
|
||||
error: "Ошибка!"
|
||||
email_input: "Email Администратора"
|
||||
submit_button: "отправить письмо"
|
||||
discourse_hub:
|
||||
access_token_problem: "Пожалуйста, обновите настройки сайта, чтобы задать верный discourse_org_access_key."
|
||||
|
||||
@ -938,7 +938,7 @@ sq:
|
||||
warn_reviving_old_topic_age: "When someone starts replying to a topic where the last reply is older than this many days, a warning will be displayed. Disable by setting to 0."
|
||||
autohighlight_all_code: "Force apply code highlighting to all preformatted code blocks even when they didn't explicitly specify the language."
|
||||
highlighted_languages: "Included syntax highlighting rules. (Warning: including too many langauges may impact performance) see: https://highlightjs.org/static/demo/ for a demo"
|
||||
embeddable_host: "Host that can embed the comments from this Discourse forum. Hostname only, do not begin with http://"
|
||||
embeddable_hosts: "Host that can embed the comments from this Discourse forum. Hostname only, do not begin with http://"
|
||||
feed_polling_enabled: "EMBEDDING ONLY: Whether to embed a RSS/ATOM feed as posts."
|
||||
feed_polling_url: "EMBEDDING ONLY: URL of RSS/ATOM feed to embed."
|
||||
embed_by_username: "Discourse username of the user who creates the embedded topics."
|
||||
|
||||
@ -903,7 +903,7 @@ tr_TR:
|
||||
warn_reviving_old_topic_age: "Herhangi bir kullanıcı, son cevabın burada belirtilen gün sayısından daha önce yazıldığı bir konuya cevap yazmaya başladığında, bir uyarı mesajı çıkacak. Bu özelliği devre dışı bırakmak için 0 girin. "
|
||||
autohighlight_all_code: "Tüm önceden formatlanan kod bloklarına, açıkça dil seçimi yapılmamış olsa da, zorla kod vurgulaması uygula."
|
||||
highlighted_languages: "Dahil edilen sözdizimi vurgulama kuralları. (Dikkat: çok fazla dili dahil etmek performansı etkileyebilir) Demo için https://highlightjs.org/static/demo/ adresine bakınız"
|
||||
embeddable_host: "Bu Discourse forumundan yorumların yerleştirilebileceği sunucu. Sadece sunucu, http:// ile başlamayın"
|
||||
embeddable_hosts: "Bu Discourse forumundan yorumların yerleştirilebileceği sunucu. Sadece sunucu, http:// ile başlamayın"
|
||||
feed_polling_enabled: "SADECE YERLEŞTİRME İÇİN: RSS/ATOM beslemesinin gönderi olarak yerleştirilip yerleştirilemeyeceği."
|
||||
feed_polling_url: "SADECE YERLEŞTİRME İÇİN: Yerleştirilecek RSS/ATOM beslemesinin URL'i."
|
||||
embed_by_username: "Yerleştirilmiş konuları oluşturan kullanıcıya ait Discourse kullanıcı adı. "
|
||||
@ -916,6 +916,7 @@ tr_TR:
|
||||
notify_about_flags_after: "Bu kadar saat geçmesine rağmen hala ilgilenilmemiş bayraklar varsa, contact_email adresine e-posta gönder. Devre dışı bırakmak için 0 girin. "
|
||||
enable_cdn_js_debugging: "/logs 'ların asli hataları tüm js içeriklerine crossorigin izinleri ekleyerek göstermesine izin ver."
|
||||
show_create_topics_notice: "Eğer sitede herkese açık konu sayısı 5'den az ise, adminden yeni konular oluşturmasını isteyen bir uyarı mesajı göster. "
|
||||
delete_drafts_older_than_n_days: (n) günden eski taslakları sil.
|
||||
vacuum_db_days: "Geçiş sonra DB alanı geri kazanmak için TAM VAKUM ANALİZİ'ni çalıştırın (devre dışı bırakmak için 0 girin)"
|
||||
prevent_anons_from_downloading_files: "Anonim kullanıcıların eklenti indirebilmesini önle. DİKKAT: Bu ayar, eklenti olarak gönderilen resim-dışı site içeriklerinin de çalışmasını engelleyebilir."
|
||||
slug_generation_method: "Slug üretim yöntemi seçin. 'kodlanmış' seçeneği yüzde kodlamalı metin oluşturur. 'hiçbiri' seçeneği slug'ı devre dışı bırakır."
|
||||
@ -1016,6 +1017,7 @@ tr_TR:
|
||||
not_allowed_from_ip_address: "Bu IP adresinden %{username} kullanıcı adı ile giriş yapamazsınız."
|
||||
admin_not_allowed_from_ip_address: "Bu IP adresinden admin olarak giriş yapamazsınız."
|
||||
suspended: "%{date} tarihine kadar giriş yapamazsınız."
|
||||
suspended_with_reason: "Hesap %{date} tarihine kadar yasaklanmıştır. Sebep: %{reason}"
|
||||
errors: "%{errors}"
|
||||
not_available: "Uygun değil. Bunu denemeye ne dersiniz? %{suggestion}"
|
||||
something_already_taken: "Bir şeyler ters gitti. Kullanıcı adı veya e-posta zaten kayıtlı olabilir. Parolamı Unuttum bağlantısını deneyin."
|
||||
@ -1037,6 +1039,9 @@ tr_TR:
|
||||
email:
|
||||
not_allowed: "için o e-posta sağlayıcısına izin verilmiyor. Lütfen başka bir email adresi kullanın. "
|
||||
blocked: "için izin yok."
|
||||
ip_address:
|
||||
blocked: "Mevcut IP adresiniz üzerinden yeni kayıt işlemine izin verilmiyor."
|
||||
max_new_accounts_per_registration_ip: "Mevcut IP adresiniz üzerinden yeni kayıt işlemine izin verilmiyor. (Belirlenen maksimum limit aşıldı.) Bir yetkili ile iletişime geçin."
|
||||
invite_mailer:
|
||||
subject_template: "%{invitee_name} sizi %{site_domain_name} sitesindeki '%{topic_title}' adlı konuya davet etti. "
|
||||
text_body_template: "%{invitee_name} sizi \n\n> %{site_title} -- %{site_description} \n\nsitesindeki\n\n> **%{topic_title}**\n>\n> %{topic_excerpt}\n\ntartışmasına davet ediyor.\n\nEğer ilgileniyorsanız, aşağıdaki bağlantıya tıklayın:\n\n%{invite_link}\n\nBu davet güvenilir bir kullanıcı tarafından gönderilmiştir, cevap yazarak tartışmaya hemen katılabilirsiniz.\n"
|
||||
|
||||
@ -934,7 +934,7 @@ zh_CN:
|
||||
warn_reviving_old_topic_age: "当有人开始回复最后一贴超过一定天数前的主题时,将有一个警告显示,不鼓励他们复活一个老的讨论。将其设置为 0 以禁用。"
|
||||
autohighlight_all_code: "即使未显式设定语言,仍为所有预编排代码块应用语法高亮。"
|
||||
highlighted_languages: "包含语法高亮规则。(警告:包含太多的语言可能会印象性能)见:https://highlightjs.org/static/demo/ 查看演示"
|
||||
embeddable_host: "能从这个 Discourse 论坛嵌入评论的主机。\n仅主机名,不要以 http:// 开头"
|
||||
embeddable_hosts: "能从这个 Discourse 论坛嵌入评论的主机。\n仅主机名,不要以 http:// 开头"
|
||||
feed_polling_enabled: "仅用于嵌入:是否将 RSS/ATOM 订阅为帖子。"
|
||||
feed_polling_url: "仅用于嵌入:RSS/ATOM 订阅的 URL。"
|
||||
embed_by_username: "创建嵌入主题的 Discourse 的用户名。"
|
||||
@ -1004,9 +1004,9 @@ zh_CN:
|
||||
not_seen_in_a_month: "欢迎回来!我们已经好久没见到你了。这些是你不在时的最热门主题。"
|
||||
move_posts:
|
||||
new_topic_moderator_post:
|
||||
other: "我已经移动了 %{conut} 个帖子到新的主题:%{topic_link}"
|
||||
other: "我已经移动了 %{count} 个帖子到新的主题:%{topic_link}"
|
||||
existing_topic_moderator_post:
|
||||
other: "我已经移动了 %{conut} 个帖子到了一个已存在的主题:%{topic_link}"
|
||||
other: "我已经移动了 %{count} 个帖子到了一个已存在的主题:%{topic_link}"
|
||||
change_owner:
|
||||
post_revision_text: "所有权从 %{old_user} 转移至 %{new_user}"
|
||||
emoji:
|
||||
|
||||
@ -1120,3 +1120,8 @@ zh_TW:
|
||||
title: "服務條款"
|
||||
privacy_topic:
|
||||
title: "隱私政策"
|
||||
admin_login:
|
||||
success: "已發送郵件"
|
||||
error: "錯誤"
|
||||
email_input: "管理者郵件"
|
||||
submit_button: "送出電子郵件"
|
||||
|
||||
@ -729,9 +729,9 @@ developer:
|
||||
client: true
|
||||
|
||||
embedding:
|
||||
embeddable_host:
|
||||
default: ''
|
||||
regex: "^(?!http).+" # don't allow this to start with http:// or https://
|
||||
embeddable_hosts:
|
||||
default: ''
|
||||
type: host_list
|
||||
feed_polling_enabled: false
|
||||
feed_polling_url: ''
|
||||
embed_by_username:
|
||||
|
||||
@ -54,6 +54,15 @@ if seed_welcome_topics
|
||||
post.topic.update_pinned(true)
|
||||
end
|
||||
|
||||
welcome = File.read(Rails.root + 'docs/ADMIN-QUICK-START-GUIDE.md')
|
||||
PostCreator.create(Discourse.system_user, raw: welcome, title: "READ ME FIRST: Admin Quick Start Guide", skip_validations: true, category: staff ? staff.name : nil)
|
||||
filename = DiscoursePluginRegistry.seed_data["admin_quick_start_filename"]
|
||||
if filename.nil? || !File.exists?(filename)
|
||||
filename = Rails.root + 'docs/ADMIN-QUICK-START-GUIDE.md'
|
||||
end
|
||||
|
||||
welcome = File.read(filename)
|
||||
PostCreator.create( Discourse.system_user,
|
||||
raw: welcome,
|
||||
title: DiscoursePluginRegistry.seed_data["admin_quick_start_title"] || "READ ME FIRST: Admin Quick Start Guide",
|
||||
skip_validations: true,
|
||||
category: staff ? staff.name : nil)
|
||||
end
|
||||
|
||||
5
db/migrate/20150609163211_migrate_embeddable_host.rb
Normal file
5
db/migrate/20150609163211_migrate_embeddable_host.rb
Normal file
@ -0,0 +1,5 @@
|
||||
class MigrateEmbeddableHost < ActiveRecord::Migration
|
||||
def change
|
||||
execute "UPDATE site_settings SET name = 'embeddable_hosts', data_type = 9 WHERE name = 'embeddable_host'"
|
||||
end
|
||||
end
|
||||
@ -1,6 +1,6 @@
|
||||
module BackupRestore
|
||||
|
||||
class RestoreDisabledError < RuntimeError; end
|
||||
class RestoreDisabledError < RuntimeError; end
|
||||
class FilenameMissingError < RuntimeError; end
|
||||
|
||||
class Restorer
|
||||
@ -74,7 +74,7 @@ module BackupRestore
|
||||
protected
|
||||
|
||||
def ensure_restore_is_enabled
|
||||
raise Restore::RestoreDisabledError unless Rails.env.development? || SiteSetting.allow_restore?
|
||||
raise BackupRestore::RestoreDisabledError unless Rails.env.development? || SiteSetting.allow_restore?
|
||||
end
|
||||
|
||||
def ensure_no_operation_is_running
|
||||
@ -89,7 +89,7 @@ module BackupRestore
|
||||
end
|
||||
|
||||
def ensure_we_have_a_filename
|
||||
raise Restore::FilenameMissingError if @filename.nil?
|
||||
raise BackupRestore::FilenameMissingError if @filename.nil?
|
||||
end
|
||||
|
||||
def initialize_state
|
||||
|
||||
@ -207,7 +207,7 @@ class CookedPostProcessor
|
||||
def update_topic_image(images)
|
||||
if @post.is_first_post?
|
||||
img = images.first
|
||||
@post.topic.update_column(:image_url, img["src"]) if img["src"].present?
|
||||
@post.topic.update_column(:image_url, img["src"][0...255]) if img["src"].present?
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -13,6 +13,7 @@ class DiscoursePluginRegistry
|
||||
attr_writer :sass_variables
|
||||
attr_writer :handlebars
|
||||
attr_writer :serialized_current_user_fields
|
||||
attr_writer :seed_data
|
||||
|
||||
attr_accessor :custom_html
|
||||
|
||||
@ -56,6 +57,10 @@ class DiscoursePluginRegistry
|
||||
def serialized_current_user_fields
|
||||
@serialized_current_user_fields ||= Set.new
|
||||
end
|
||||
|
||||
def seed_data
|
||||
@seed_data ||= HashWithIndifferentAccess.new({})
|
||||
end
|
||||
end
|
||||
|
||||
def register_js(filename, options={})
|
||||
@ -104,6 +109,10 @@ class DiscoursePluginRegistry
|
||||
end
|
||||
end
|
||||
|
||||
def self.register_seed_data(key, value)
|
||||
self.seed_data[key] = value
|
||||
end
|
||||
|
||||
def javascripts
|
||||
self.class.javascripts
|
||||
end
|
||||
|
||||
@ -31,7 +31,7 @@ class DistributedCache
|
||||
hash = current.hash(message.site_id)
|
||||
|
||||
case payload["op"]
|
||||
when "set" then hash[payload["key"]] = payload["value"]
|
||||
when "set" then hash[payload["key"]] = payload["marshalled"] ? Marshal.load(payload["value"]) : payload["value"]
|
||||
when "delete" then hash.delete(payload["key"])
|
||||
when "clear" then hash.clear
|
||||
end
|
||||
@ -69,7 +69,10 @@ class DistributedCache
|
||||
end
|
||||
|
||||
def self.set(hash, key, value)
|
||||
publish(hash, { op: :set, key: key, value: value })
|
||||
# special support for set
|
||||
marshal = Set === value
|
||||
value = Marshal.dump(value) if marshal
|
||||
publish(hash, { op: :set, key: key, value: value, marshalled: marshal })
|
||||
end
|
||||
|
||||
def self.delete(hash, key)
|
||||
|
||||
36
lib/email_cook.rb
Normal file
36
lib/email_cook.rb
Normal file
@ -0,0 +1,36 @@
|
||||
# A very simple formatter for imported emails
|
||||
class EmailCook
|
||||
|
||||
def initialize(raw)
|
||||
@raw = raw
|
||||
end
|
||||
|
||||
def cook
|
||||
result = ""
|
||||
|
||||
in_quote = false
|
||||
quote_buffer = ""
|
||||
@raw.each_line do |l|
|
||||
|
||||
if l =~ /^\s*>/
|
||||
in_quote = true
|
||||
quote_buffer << l.sub(/^[\s>]*/, '') << "<br>"
|
||||
elsif in_quote
|
||||
result << "<blockquote>#{quote_buffer}</blockquote>"
|
||||
quote_buffer = ""
|
||||
in_quote = false
|
||||
else
|
||||
result << l << "<br>"
|
||||
end
|
||||
end
|
||||
|
||||
if in_quote
|
||||
result << "<blockquote>#{quote_buffer}</blockquote>"
|
||||
end
|
||||
|
||||
result.gsub!(/(<br>){3,10}/, '<br><br>')
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
end
|
||||
@ -200,7 +200,7 @@ class Guardian
|
||||
|
||||
def can_invite_to_forum?(groups=nil)
|
||||
authenticated? &&
|
||||
SiteSetting.max_invites_per_day.to_i > 0 &&
|
||||
(SiteSetting.max_invites_per_day.to_i > 0 || is_staff?) &&
|
||||
!SiteSetting.enable_sso &&
|
||||
SiteSetting.enable_local_logins &&
|
||||
(
|
||||
@ -213,8 +213,8 @@ class Guardian
|
||||
def can_invite_to?(object, group_ids=nil)
|
||||
return false if ! authenticated?
|
||||
return false unless ( SiteSetting.enable_local_logins && (!SiteSetting.must_approve_users? || is_staff?) )
|
||||
return false if SiteSetting.max_invites_per_day.to_i == 0
|
||||
return true if is_admin?
|
||||
return false if (SiteSetting.max_invites_per_day.to_i == 0 && !is_staff?)
|
||||
return false if ! can_see?(object)
|
||||
return false if group_ids.present?
|
||||
|
||||
|
||||
@ -17,6 +17,10 @@ class Plugin::Instance
|
||||
}
|
||||
end
|
||||
|
||||
def seed_data
|
||||
@seed_data ||= {}
|
||||
end
|
||||
|
||||
def self.find_all(parent_path)
|
||||
[].tap { |plugins|
|
||||
# also follows symlinks - http://stackoverflow.com/q/357754
|
||||
@ -166,7 +170,11 @@ class Plugin::Instance
|
||||
|
||||
def register_color_scheme(name, colors)
|
||||
color_schemes << {name: name, colors: colors}
|
||||
end
|
||||
end
|
||||
|
||||
def register_seed_data(key, value)
|
||||
seed_data[key] = value
|
||||
end
|
||||
|
||||
def automatic_assets
|
||||
css = styles.join("\n")
|
||||
@ -224,6 +232,10 @@ class Plugin::Instance
|
||||
|
||||
register_assets! unless assets.blank?
|
||||
|
||||
seed_data.each do |key, value|
|
||||
DiscoursePluginRegistry.register_seed_data(key, value)
|
||||
end
|
||||
|
||||
# TODO: possibly amend this to a rails engine
|
||||
|
||||
# Automatically include assets
|
||||
|
||||
@ -28,6 +28,7 @@ class PostCreator
|
||||
# cook_method - Method of cooking the post.
|
||||
# :regular - Pass through Markdown parser and strip bad HTML
|
||||
# :raw_html - Perform no processing
|
||||
# :raw_email - Imported from an email
|
||||
# via_email - Mark this post as arriving via email
|
||||
# raw_email - Full text of arriving email (to store)
|
||||
#
|
||||
|
||||
@ -224,6 +224,7 @@ module PrettyText
|
||||
|
||||
def self.add_s3_cdn(doc)
|
||||
doc.css("img").each do |img|
|
||||
next unless img["src"]
|
||||
img["src"] = img["src"].sub(Discourse.store.absolute_base_url, SiteSetting.s3_cdn_url)
|
||||
end
|
||||
end
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user