mirror of
https://github.com/discourse/discourse.git
synced 2025-02-07 03:48:23 +00:00
Merge branch 'master' into fixed_modals
Conflicts: app/assets/javascripts/discourse/templates/modal/modal.js.handlebars app/assets/stylesheets/application/modal.css.scss
This commit is contained in:
commit
5e08427dd3
1
.gitignore
vendored
1
.gitignore
vendored
@ -25,6 +25,7 @@ dump.rdb
|
||||
config/database.yml
|
||||
config/redis.yml
|
||||
config/discourse.pill
|
||||
config/environments/production.rb
|
||||
|
||||
# Ignore the default SQLite database and db dumps
|
||||
/db/*.sqlite3
|
||||
|
4
Gemfile
4
Gemfile
@ -5,8 +5,8 @@ gem 'active_model_serializers', git: 'https://github.com/rails-api/active_model_
|
||||
# we had issues with latest, stick to the rev till we figure this out
|
||||
# PR that makes it all hang together welcome
|
||||
gem 'ember-rails'
|
||||
gem 'ember-source', '1.0.0.rc5' # or the version you need
|
||||
gem 'handlebars-source', '1.0.0.rc4' # or the version you need
|
||||
gem 'ember-source', '1.0.0.rc6.2'
|
||||
gem 'handlebars-source', '1.0.12'
|
||||
gem 'barber'
|
||||
|
||||
gem 'vestal_versions', git: 'https://github.com/zhangyuan/vestal_versions'
|
||||
|
10
Gemfile.lock
10
Gemfile.lock
@ -169,8 +169,8 @@ GEM
|
||||
barber
|
||||
execjs (>= 1.2)
|
||||
railties (>= 3.1)
|
||||
ember-source (1.0.0.rc5)
|
||||
handlebars-source (= 1.0.0.rc4)
|
||||
ember-source (1.0.0.rc6.2)
|
||||
handlebars-source (= 1.0.12)
|
||||
erubis (2.7.0)
|
||||
eventmachine (1.0.3)
|
||||
excon (0.20.1)
|
||||
@ -211,7 +211,7 @@ GEM
|
||||
childprocess (>= 0.2.3)
|
||||
guard (>= 1.1)
|
||||
spork (>= 0.8.4)
|
||||
handlebars-source (1.0.0.rc4)
|
||||
handlebars-source (1.0.12)
|
||||
hashie (2.0.4)
|
||||
highline (1.6.18)
|
||||
hike (1.2.2)
|
||||
@ -482,7 +482,7 @@ DEPENDENCIES
|
||||
em-redis
|
||||
email_reply_parser!
|
||||
ember-rails
|
||||
ember-source (= 1.0.0.rc5)
|
||||
ember-source (= 1.0.0.rc6.2)
|
||||
eventmachine
|
||||
fabrication
|
||||
fakeweb (~> 1.3.0)
|
||||
@ -493,7 +493,7 @@ DEPENDENCIES
|
||||
fog
|
||||
guard-rspec
|
||||
guard-spork
|
||||
handlebars-source (= 1.0.0.rc4)
|
||||
handlebars-source (= 1.0.12)
|
||||
highline
|
||||
hiredis
|
||||
image_optim
|
||||
|
@ -238,9 +238,9 @@ Discourse.AdminUser = Discourse.User.extend({
|
||||
|
||||
loadDetails: function() {
|
||||
var model = this;
|
||||
if (model.get('loadedDetails')) { return; }
|
||||
if (model.get('loadedDetails')) { return Ember.RSVP.resolve(model); }
|
||||
|
||||
Discourse.AdminUser.find(model.get('username_lower')).then(function (result) {
|
||||
return Discourse.AdminUser.find(model.get('username_lower')).then(function (result) {
|
||||
model.setProperties(result);
|
||||
model.set('loadedDetails', true);
|
||||
});
|
||||
|
@ -11,14 +11,16 @@ var oneWeekAgo = function() {
|
||||
return moment().subtract('days',7).format('YYYY-MM-DD');
|
||||
};
|
||||
|
||||
Discourse.AdminEmailPreviewDigestRoute = Discourse.Route.extend(Discourse.ModelReady, {
|
||||
Discourse.AdminEmailPreviewDigestRoute = Discourse.Route.extend({
|
||||
|
||||
model: function() {
|
||||
return Discourse.EmailPreview.findDigest(oneWeekAgo());
|
||||
},
|
||||
|
||||
modelReady: function(controller, model) {
|
||||
afterModel: function(model) {
|
||||
var controller = this.controllerFor('adminEmailPreviewDigest');
|
||||
controller.setProperties({
|
||||
model: model,
|
||||
lastSeen: oneWeekAgo(),
|
||||
showHtml: true
|
||||
});
|
||||
|
@ -6,7 +6,7 @@
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.AdminUserRoute = Discourse.Route.extend(Discourse.ModelReady, {
|
||||
Discourse.AdminUserRoute = Discourse.Route.extend({
|
||||
|
||||
serialize: function(params) {
|
||||
return { username: Em.get(params, 'username').toLowerCase() };
|
||||
@ -20,10 +20,14 @@ Discourse.AdminUserRoute = Discourse.Route.extend(Discourse.ModelReady, {
|
||||
this.render({into: 'admin/templates/admin'});
|
||||
},
|
||||
|
||||
modelReady: function(controller, adminUser) {
|
||||
adminUser.loadDetails();
|
||||
controller.set('model', adminUser);
|
||||
adminUser.setOriginalTrustLevel();
|
||||
afterModel: function(adminUser) {
|
||||
var controller = this.controllerFor('adminUser');
|
||||
|
||||
adminUser.loadDetails().then(function () {
|
||||
adminUser.setOriginalTrustLevel();
|
||||
controller.set('model', adminUser);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
@ -1,9 +1,9 @@
|
||||
<div class='admin-controls'>
|
||||
<div class='span15'>
|
||||
<ul class="nav nav-pills">
|
||||
<li>{{#linkTo adminEmail.index}}{{i18n admin.email.settings}}{{/linkTo}}</li>
|
||||
<li>{{#linkTo adminEmail.logs}}{{i18n admin.email.logs}}{{/linkTo}}</li>
|
||||
<li>{{#linkTo adminEmail.previewDigest}}{{i18n admin.email.preview_digest}}{{/linkTo}}</li>
|
||||
<li>{{#linkTo 'adminEmail.index'}}{{i18n admin.email.settings}}{{/linkTo}}</li>
|
||||
<li>{{#linkTo 'adminEmail.logs'}}{{i18n admin.email.logs}}{{/linkTo}}</li>
|
||||
<li>{{#linkTo 'adminEmail.previewDigest'}}{{i18n admin.email.preview_digest}}{{/linkTo}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,8 +1,8 @@
|
||||
<div class='admin-controls'>
|
||||
<div class='span15'>
|
||||
<ul class="nav nav-pills">
|
||||
<li>{{#linkTo adminFlags.active}}{{i18n admin.flags.active}}{{/linkTo}}</li>
|
||||
<li>{{#linkTo adminFlags.old}}{{i18n admin.flags.old}}{{/linkTo}}</li>
|
||||
<li>{{#linkTo 'adminFlags.active'}}{{i18n admin.flags.active}}{{/linkTo}}</li>
|
||||
<li>{{#linkTo 'adminFlags.old'}}{{i18n admin.flags.old}}{{/linkTo}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,15 +1,15 @@
|
||||
<div class='admin-controls'>
|
||||
<div class='span15'>
|
||||
<ul class="nav nav-pills">
|
||||
<li>{{#linkTo adminUsersList.active}}{{i18n admin.users.nav.active}}{{/linkTo}}</li>
|
||||
<li>{{#linkTo adminUsersList.new}}{{i18n admin.users.nav.new}}{{/linkTo}}</li>
|
||||
<li>{{#linkTo 'adminUsersList.active'}}{{i18n admin.users.nav.active}}{{/linkTo}}</li>
|
||||
<li>{{#linkTo 'adminUsersList.new'}}{{i18n admin.users.nav.new}}{{/linkTo}}</li>
|
||||
{{#if Discourse.SiteSettings.must_approve_users}}
|
||||
<li>{{#linkTo adminUsersList.pending}}{{i18n admin.users.nav.pending}}{{/linkTo}}</li>
|
||||
<li>{{#linkTo 'adminUsersList.pending'}}{{i18n admin.users.nav.pending}}{{/linkTo}}</li>
|
||||
{{/if}}
|
||||
<li>{{#linkTo adminUsersList.admins}}{{i18n admin.users.nav.admins}}{{/linkTo}}</li>
|
||||
<li>{{#linkTo adminUsersList.moderators}}{{i18n admin.users.nav.moderators}}{{/linkTo}}</li>
|
||||
<li>{{#linkTo adminUsersList.banned}}{{i18n admin.users.nav.banned}}{{/linkTo}}</li>
|
||||
<li>{{#linkTo adminUsersList.blocked}}{{i18n admin.users.nav.blocked}}{{/linkTo}}</li>
|
||||
<li>{{#linkTo 'adminUsersList.admins'}}{{i18n admin.users.nav.admins}}{{/linkTo}}</li>
|
||||
<li>{{#linkTo 'adminUsersList.moderators'}}{{i18n admin.users.nav.moderators}}{{/linkTo}}</li>
|
||||
<li>{{#linkTo 'adminUsersList.banned'}}{{i18n admin.users.nav.banned}}{{/linkTo}}</li>
|
||||
<li>{{#linkTo 'adminUsersList.blocked'}}{{i18n admin.users.nav.blocked}}{{/linkTo}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class='span5 username controls'>
|
||||
|
@ -12,7 +12,7 @@
|
||||
// Externals we need to load first
|
||||
//= require ./external/jquery-1.9.1.js
|
||||
//= require ./external/jquery.ui.widget.js
|
||||
//= require ./external/handlebars-1.0.rc.4.js
|
||||
//= require ./external/handlebars.js
|
||||
<%
|
||||
if Rails.env.development?
|
||||
require_asset ("./external_development/ember.js")
|
||||
|
@ -250,9 +250,7 @@ Discourse = Ember.Application.createWithMixins({
|
||||
// If we have URL_FIXTURES, load from there instead (testing)
|
||||
var fixture = Discourse.URL_FIXTURES && Discourse.URL_FIXTURES[url];
|
||||
if (fixture) {
|
||||
return Ember.Deferred.promise(function(promise) {
|
||||
promise.resolve(fixture);
|
||||
});
|
||||
return Ember.RSVP.resolve(fixture);
|
||||
}
|
||||
|
||||
return Ember.Deferred.promise(function (promise) {
|
||||
|
@ -254,7 +254,7 @@ Discourse.BBCode = {
|
||||
|
||||
// Arguments for formatting
|
||||
args = {
|
||||
username: username,
|
||||
username: I18n.t('user.said',{username: username}),
|
||||
params: params,
|
||||
quote: content,
|
||||
avatarImg: opts.lookupAvatar ? opts.lookupAvatar(username) : void 0
|
||||
|
@ -101,14 +101,12 @@ Discourse.EditCategoryController = Discourse.ObjectController.extend(Discourse.M
|
||||
return false;
|
||||
},
|
||||
|
||||
addGroup: function(){
|
||||
this.get('model').addGroup(this.get("selectedGroup"));
|
||||
addPermission: function(group, permission_id){
|
||||
this.get('model').addPermission({group_name: group + "", permission: Discourse.PermissionType.create({id: permission_id})});
|
||||
},
|
||||
|
||||
removeGroup: function(group){
|
||||
// OBVIOUS, Ember treats this as Ember.String, we need a real string here
|
||||
group = group + "";
|
||||
this.get('model').removeGroup(group);
|
||||
removePermission: function(permission){
|
||||
this.get('model').removePermission(permission);
|
||||
},
|
||||
|
||||
saveCategory: function() {
|
||||
|
@ -100,11 +100,16 @@ Discourse.QuoteButtonController = Discourse.Controller.extend({
|
||||
var post = this.get('post');
|
||||
var composerController = this.get('controllers.composer');
|
||||
var composerOpts = {
|
||||
post: post,
|
||||
action: Discourse.Composer.REPLY,
|
||||
draftKey: this.get('post.topic.draft_key')
|
||||
};
|
||||
|
||||
if(post.get('post_number') === 1) {
|
||||
composerOpts.topic = post.get("topic");
|
||||
} else {
|
||||
composerOpts.post = post;
|
||||
}
|
||||
|
||||
// If the composer is associated with a different post, we don't change it.
|
||||
var composerPost = composerController.get('content.post');
|
||||
if (composerPost && (composerPost.get('id') !== this.get('post.id'))) {
|
||||
|
@ -20,7 +20,7 @@ Discourse.StaticController = Discourse.Controller.extend({
|
||||
text = text[1];
|
||||
this.set('content', text);
|
||||
} else {
|
||||
return Discourse.ajax(path).then(function (result) {
|
||||
return Discourse.ajax(path, {dataType: 'html'}).then(function (result) {
|
||||
staticController.set('content', result);
|
||||
});
|
||||
}
|
||||
|
@ -198,41 +198,6 @@ Discourse.TopicController = Discourse.ObjectController.extend(Discourse.Selected
|
||||
Discourse.URL.routeTo(this.get('lastPostUrl'));
|
||||
},
|
||||
|
||||
replyAsNewTopic: function(post) {
|
||||
// TODO shut down topic draft cleanly if it exists ...
|
||||
var composerController = this.get('controllers.composer');
|
||||
var promise = composerController.open({
|
||||
action: Discourse.Composer.CREATE_TOPIC,
|
||||
draftKey: Discourse.Composer.REPLY_AS_NEW_TOPIC_KEY
|
||||
});
|
||||
var postUrl = "" + location.protocol + "//" + location.host + (post.get('url'));
|
||||
var postLink = "[" + (this.get('title')) + "](" + postUrl + ")";
|
||||
|
||||
promise.then(function() {
|
||||
Discourse.Post.loadQuote(post.get('id')).then(function(q) {
|
||||
composerController.appendText("" + (I18n.t("post.continue_discussion", {
|
||||
postLink: postLink
|
||||
})) + "\n\n" + q);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// Topic related
|
||||
reply: function() {
|
||||
var composerController = this.get('controllers.composer');
|
||||
if (composerController.get('content.topic.id') === this.get('content.id') &&
|
||||
composerController.get('content.action') === Discourse.Composer.REPLY) {
|
||||
composerController.set('content.post', null);
|
||||
composerController.set('content.composeState', Discourse.Composer.OPEN);
|
||||
} else {
|
||||
composerController.open({
|
||||
topic: this.get('content'),
|
||||
action: Discourse.Composer.REPLY,
|
||||
draftKey: this.get('content.draft_key'),
|
||||
draftSequence: this.get('content.draft_sequence')
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
Toggle a participant for filtering
|
||||
@ -336,25 +301,60 @@ Discourse.TopicController = Discourse.ObjectController.extend(Discourse.Selected
|
||||
var composerController = this.get('controllers.composer');
|
||||
var quoteController = this.get('controllers.quoteButton');
|
||||
var quotedText = Discourse.BBCode.buildQuoteBBCode(quoteController.get('post'), quoteController.get('buffer'));
|
||||
|
||||
var topic = post ? post.get('topic') : this.get('model');
|
||||
|
||||
quoteController.set('buffer', '');
|
||||
|
||||
if (composerController.get('content.topic.id') === post.get('topic.id') &&
|
||||
if (composerController.get('content.topic.id') === topic.get('id') &&
|
||||
composerController.get('content.action') === Discourse.Composer.REPLY) {
|
||||
composerController.set('content.post', post);
|
||||
composerController.set('content.composeState', Discourse.Composer.OPEN);
|
||||
composerController.appendText(quotedText);
|
||||
} else {
|
||||
var promise = composerController.open({
|
||||
post: post,
|
||||
|
||||
var opts = {
|
||||
action: Discourse.Composer.REPLY,
|
||||
draftKey: post.get('topic.draft_key'),
|
||||
draftSequence: post.get('topic.draft_sequence')
|
||||
});
|
||||
draftKey: topic.get('draft_key'),
|
||||
draftSequence: topic.get('draft_sequence')
|
||||
};
|
||||
|
||||
if(post && post.get("post_number") !== 1){
|
||||
opts.post = post;
|
||||
} else {
|
||||
opts.topic = topic;
|
||||
}
|
||||
|
||||
var promise = composerController.open(opts);
|
||||
promise.then(function() { composerController.appendText(quotedText); });
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
replyAsNewTopic: function(post) {
|
||||
// TODO shut down topic draft cleanly if it exists ...
|
||||
var composerController = this.get('controllers.composer');
|
||||
var promise = composerController.open({
|
||||
action: Discourse.Composer.CREATE_TOPIC,
|
||||
draftKey: Discourse.Composer.REPLY_AS_NEW_TOPIC_KEY
|
||||
});
|
||||
var postUrl = "" + location.protocol + "//" + location.host + (post.get('url'));
|
||||
var postLink = "[" + (this.get('title')) + "](" + postUrl + ")";
|
||||
|
||||
promise.then(function() {
|
||||
Discourse.Post.loadQuote(post.get('id')).then(function(q) {
|
||||
composerController.appendText("" + (I18n.t("post.continue_discussion", {
|
||||
postLink: postLink
|
||||
})) + "\n\n" + q);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// Topic related
|
||||
reply: function() {
|
||||
this.replyToPost();
|
||||
},
|
||||
|
||||
// Edits a post
|
||||
editPost: function(post) {
|
||||
this.get('controllers.composer').open({
|
||||
|
@ -1,29 +0,0 @@
|
||||
/**
|
||||
Until the fully async router is merged into Ember, it is healthy to do some extra checking
|
||||
that setupController is not passed a promise instead of the model we want.
|
||||
|
||||
This mixin handles that case, and calls modelReady instead.
|
||||
|
||||
@class Discourse.ModelReady
|
||||
@extends Ember.Mixin
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.ModelReady = Em.Mixin.create({
|
||||
|
||||
setupController: function(controller, model) {
|
||||
var route = this;
|
||||
if (model.then) {
|
||||
model.then(function (m) {
|
||||
controller.set('model', m);
|
||||
if (route.modelReady) { route.modelReady(controller, m); }
|
||||
});
|
||||
} else {
|
||||
controller.set('model', model);
|
||||
if (route.modelReady) { route.modelReady(controller, model); }
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
@ -11,9 +11,21 @@ Discourse.Category = Discourse.Model.extend({
|
||||
init: function() {
|
||||
this._super();
|
||||
this.set("availableGroups", Em.A(this.get("available_groups")));
|
||||
this.set("groups", Em.A(this.groups));
|
||||
this.set("permissions", Em.A(_.map(this.group_permissions, function(elem){
|
||||
return {
|
||||
group_name: elem.group_name,
|
||||
permission: Discourse.PermissionType.create({id: elem.permission_type})
|
||||
};
|
||||
})));
|
||||
},
|
||||
|
||||
availablePermissions: function(){
|
||||
return [ Discourse.PermissionType.create({id: Discourse.PermissionType.FULL}),
|
||||
Discourse.PermissionType.create({id: Discourse.PermissionType.CREATE_POST}),
|
||||
Discourse.PermissionType.create({id: Discourse.PermissionType.READONLY})
|
||||
];
|
||||
}.property(),
|
||||
|
||||
searchContext: function() {
|
||||
return ({ type: 'category', id: this.get('id'), category: this });
|
||||
}.property('id'),
|
||||
@ -43,33 +55,49 @@ Discourse.Category = Discourse.Model.extend({
|
||||
text_color: this.get('text_color'),
|
||||
hotness: this.get('hotness'),
|
||||
secure: this.get('secure'),
|
||||
group_names: this.get('groups').join(","),
|
||||
permissions: this.get('permissionsForUpdate'),
|
||||
auto_close_days: this.get('auto_close_days')
|
||||
},
|
||||
type: this.get('id') ? 'PUT' : 'POST'
|
||||
});
|
||||
},
|
||||
|
||||
permissionsForUpdate: function(){
|
||||
var rval = {};
|
||||
_.each(this.get("permissions"),function(p){
|
||||
rval[p.group_name] = p.permission.id;
|
||||
});
|
||||
return rval;
|
||||
}.property("permissions"),
|
||||
|
||||
destroy: function(callback) {
|
||||
return Discourse.ajax("/categories/" + (this.get('slug') || this.get('id')), { type: 'DELETE' });
|
||||
},
|
||||
|
||||
addGroup: function(group){
|
||||
this.get("groups").addObject(group);
|
||||
this.get("availableGroups").removeObject(group);
|
||||
addPermission: function(permission){
|
||||
this.get("permissions").addObject(permission);
|
||||
this.get("availableGroups").removeObject(permission.group_name);
|
||||
},
|
||||
|
||||
|
||||
removeGroup: function(group){
|
||||
this.get("groups").removeObject(group);
|
||||
this.get("availableGroups").addObject(group);
|
||||
removePermission: function(permission){
|
||||
this.get("permissions").removeObject(permission);
|
||||
this.get("availableGroups").addObject(permission.group_name);
|
||||
},
|
||||
|
||||
// note, this is used in a data attribute, data attributes get downcased
|
||||
// to avoid confusion later on using this naming here.
|
||||
description_text: function(){
|
||||
return $("<div>" + this.get("description") + "</div>").text();
|
||||
}.property("description")
|
||||
}.property("description"),
|
||||
|
||||
permissions: function(){
|
||||
return Em.A([
|
||||
{group_name: "everyone", permission: Discourse.PermissionType.create({id: 1})},
|
||||
{group_name: "admins", permission: Discourse.PermissionType.create({id: 2}) },
|
||||
{group_name: "crap", permission: Discourse.PermissionType.create({id: 3}) }
|
||||
]);
|
||||
}.property()
|
||||
|
||||
});
|
||||
|
||||
|
23
app/assets/javascripts/discourse/models/permission_type.js
Normal file
23
app/assets/javascripts/discourse/models/permission_type.js
Normal file
@ -0,0 +1,23 @@
|
||||
|
||||
Discourse.PermissionType = Discourse.Model.extend({
|
||||
description: function(){
|
||||
var key = "";
|
||||
|
||||
switch(this.get("id")){
|
||||
case 1:
|
||||
key = "full";
|
||||
break;
|
||||
case 2:
|
||||
key = "create_post";
|
||||
break;
|
||||
case 3:
|
||||
key = "readonly";
|
||||
break;
|
||||
}
|
||||
return I18n.t("permission_types." + key);
|
||||
}.property("id")
|
||||
});
|
||||
|
||||
Discourse.PermissionType.FULL = 1;
|
||||
Discourse.PermissionType.CREATE_POST = 2;
|
||||
Discourse.PermissionType.READONLY = 3;
|
@ -9,9 +9,14 @@
|
||||
Discourse.Post = Discourse.Model.extend({
|
||||
|
||||
shareUrl: function() {
|
||||
if (this.get('firstPost')) return this.get('topic.url');
|
||||
var user = Discourse.User.current();
|
||||
return this.get('url') + (user ? '?u=' + user.get('username_lower') : '');
|
||||
var userSuffix = user ? '?u=' + user.get('username_lower') : '';
|
||||
|
||||
if (this.get('firstPost')) {
|
||||
return this.get('topic.url') + userSuffix;
|
||||
} else {
|
||||
return this.get('url') + userSuffix ;
|
||||
}
|
||||
}.property('url'),
|
||||
|
||||
new_user: Em.computed.equal('trust_level', 0),
|
||||
|
@ -8,7 +8,7 @@
|
||||
**/
|
||||
Discourse.RestrictedUserRoute = Discourse.Route.extend({
|
||||
|
||||
redirect: function() {
|
||||
afterModel: function() {
|
||||
var user = this.modelFor('user');
|
||||
if (!user.get('can_edit')) {
|
||||
this.transitionTo('user.activity', user);
|
||||
|
@ -6,13 +6,16 @@
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.ListCategoriesRoute = Discourse.Route.extend(Discourse.ModelReady, {
|
||||
Discourse.ListCategoriesRoute = Discourse.Route.extend({
|
||||
|
||||
redirect: function() { Discourse.redirectIfLoginRequired(this); },
|
||||
|
||||
events: {
|
||||
createCategory: function() {
|
||||
Discourse.Route.showModal(this, 'editCategory', Discourse.Category.create({ color: 'AB9364', text_color: 'FFFFFF', hotness: 5 }));
|
||||
Discourse.Route.showModal(this, 'editCategory', Discourse.Category.create({
|
||||
color: 'AB9364', text_color: 'FFFFFF', hotness: 5, group_permissions: [{group_name: "everyone", permission_type: 1}],
|
||||
available_groups: Discourse.Site.instance().group_names
|
||||
}));
|
||||
this.controllerFor('editCategory').set('selectedTab', 'general');
|
||||
}
|
||||
},
|
||||
@ -28,9 +31,11 @@ Discourse.ListCategoriesRoute = Discourse.Route.extend(Discourse.ModelReady, {
|
||||
this.controllerFor('list').set('canCreateCategory', false);
|
||||
},
|
||||
|
||||
modelReady: function(controller, categoryList) {
|
||||
renderTemplate: function() {
|
||||
this.render('listCategories', { into: 'list', outlet: 'listView' });
|
||||
},
|
||||
|
||||
afterModel: function(categoryList) {
|
||||
this.controllerFor('list').setProperties({
|
||||
canCreateCategory: categoryList.get('can_create_category'),
|
||||
canCreateTopic: categoryList.get('can_create_topic'),
|
||||
|
@ -9,9 +9,11 @@
|
||||
Discourse.StaticController.pages.forEach(function(page) {
|
||||
|
||||
Discourse[(page.capitalize()) + "Route"] = Discourse.Route.extend({
|
||||
|
||||
renderTemplate: function() {
|
||||
this.render('static');
|
||||
},
|
||||
|
||||
setupController: function() {
|
||||
var config_key = Discourse.StaticController.configs[page];
|
||||
if (config_key && Discourse.SiteSettings[config_key].length > 0) {
|
||||
@ -20,6 +22,7 @@ Discourse.StaticController.pages.forEach(function(page) {
|
||||
this.controllerFor('static').loadPath("/" + page);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -6,7 +6,7 @@
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.UserInvitedRoute = Discourse.Route.extend(Discourse.ModelReady, {
|
||||
Discourse.UserInvitedRoute = Discourse.Route.extend({
|
||||
|
||||
renderTemplate: function() {
|
||||
this.render({ into: 'user', outlet: 'userOutlet' });
|
||||
|
@ -85,7 +85,7 @@
|
||||
</li>
|
||||
<li class='current-user'>
|
||||
{{#if currentUser}}
|
||||
{{#titledLinkTo user.activity currentUser titleKey="current_user" class="icon"}}{{avatar currentUser imageSize="medium" }}{{/titledLinkTo}}
|
||||
{{#titledLinkTo 'user.activity' currentUser titleKey="current_user" class="icon"}}{{avatar currentUser imageSize="medium" }}{{/titledLinkTo}}
|
||||
{{else}}
|
||||
<div class="icon not-logged-in-avatar" {{action showLogin}}><i class='icon-user'></i></div>
|
||||
{{/if}}
|
||||
@ -110,7 +110,7 @@
|
||||
</section>
|
||||
|
||||
<section class='d-dropdown' id='site-map-dropdown'>
|
||||
<ul>
|
||||
<ul class="location-links">
|
||||
{{#if currentUser.staff}}
|
||||
<li><a href="/admin"><i class='icon icon-wrench'></i>{{i18n admin_title}}</a></li>
|
||||
<li><a href="/admin/flags/active"><i class='icon icon-flag'></i>{{i18n flags_title}}</a>
|
||||
@ -123,8 +123,10 @@
|
||||
{{#titledLinkTo "list.latest" titleKey="filters.latest.help"}}{{i18n filters.latest.title}}{{/titledLinkTo}}
|
||||
</li>
|
||||
<li>{{faqLink}}</li>
|
||||
</ul>
|
||||
|
||||
{{#if categories}}
|
||||
{{#if categories}}
|
||||
<ul class="category-links">
|
||||
<li class='heading' title="{{i18n filters.categories.help}}">
|
||||
{{#linkTo "list.categories"}}{{i18n filters.categories.title}}{{/linkTo}}
|
||||
</li>
|
||||
@ -135,9 +137,9 @@
|
||||
<b>{{unbound topic_count}}</b></a>
|
||||
</li>
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
</ul>
|
||||
{{/if}}
|
||||
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
@ -62,7 +62,7 @@
|
||||
<a href='#' {{action createTopic}}>{{i18n topic.suggest_create_topic}}</a>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
{{#linkTo list.categories}}{{i18n topic.browse_all_categories}}{{/linkTo}} {{i18n or}} {{#linkTo list.latest}}{{i18n topic.view_latest_topics}}{{/linkTo}}
|
||||
{{#linkTo 'list.categories'}}{{i18n topic.browse_all_categories}}{{/linkTo}} {{i18n or}} {{#linkTo 'list.latest'}}{{i18n topic.view_latest_topics}}{{/linkTo}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</h3>
|
||||
|
@ -60,25 +60,18 @@
|
||||
{{#unless isUncategorized}}
|
||||
<div {{bindAttr class=":modal-tab :options-tab securitySelected::invisible"}}>
|
||||
<section class='field'>
|
||||
<label>
|
||||
{{input type="checkbox" checked=secure}}
|
||||
{{i18n category.is_secure}}
|
||||
</label>
|
||||
{{#if secure}}
|
||||
<div class="secure-category-options">
|
||||
<label>{{i18n category.allowed_groups}}</label>
|
||||
<ul class="badge-list">
|
||||
{{#each groups}}
|
||||
<li class="badge-group">
|
||||
{{this}}
|
||||
<a {{action removeGroup this}}><i class="icon icon-remove-sign"></i></a>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{view Ember.Select contentBinding="availableGroups" valueBinding="selectedGroup"}}
|
||||
<button {{action addGroup}} class="btn btn-small">{{i18n category.add_group}}</button>
|
||||
</div>
|
||||
{{/if}}
|
||||
<ul class='permission-list'>
|
||||
{{#each permissions}}
|
||||
<li>
|
||||
<span class="name"><span class="badge-group">{{group_name}}</span></span>
|
||||
<span class="permission">{{permission.description}}</span>
|
||||
<a {{action removePermission this}}><i class="icon icon-remove-sign"></i></a>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{view Ember.Select contentBinding="availableGroups" valueBinding="selectedGroup"}}
|
||||
{{view Ember.Select class="permission-selector" optionValuePath="content.id" optionLabelPath="content.description" contentBinding="availablePermissions" valueBinding="selectedPermission"}}
|
||||
<button {{action addPermission selectedGroup selectedPermission}} class="btn btn-small">{{i18n category.add_group}}</button>
|
||||
</section>
|
||||
</div>
|
||||
<div {{bindAttr class=":modal-tab :options-tab settingsSelected::invisible"}}>
|
||||
|
@ -3,7 +3,6 @@
|
||||
<div class='quote-controls'></div>
|
||||
{{{avatarImg}}}
|
||||
{{username}}
|
||||
said:
|
||||
</div>
|
||||
<blockquote>{{{quote}}}</blockquote>
|
||||
</aside>
|
||||
|
@ -35,7 +35,7 @@
|
||||
<dt>{{i18n user.last_seen}}:</dt><dd>{{date last_seen_at}}</dd>
|
||||
{{/if}}
|
||||
{{#if invited_by}}
|
||||
<dt>{{i18n user.invited_by}}:</dt><dd>{{#linkTo user.activity invited_by}}{{invited_by.username}}{{/linkTo}}</dd>
|
||||
<dt>{{i18n user.invited_by}}:</dt><dd>{{#linkTo 'user.activity' invited_by}}{{invited_by.username}}{{/linkTo}}</dd>
|
||||
{{/if}}
|
||||
{{#if email}}
|
||||
<dt>{{i18n user.email.title}}:</dt><dd {{bindAttr title="email"}}>{{email}}</dd>
|
||||
|
@ -14,7 +14,10 @@ Discourse.CategoryChooserView = Discourse.ComboboxView.extend({
|
||||
|
||||
init: function() {
|
||||
this._super();
|
||||
this.set('content', Discourse.Category.list());
|
||||
// TODO perhaps allow passing a param in to select if we need full or not
|
||||
this.set('content', _.filter(Discourse.Category.list(), function(c){
|
||||
return c.permission === Discourse.PermissionType.FULL;
|
||||
}));
|
||||
},
|
||||
|
||||
none: function() {
|
||||
|
@ -37,11 +37,13 @@ Discourse.ActivityFilterView = Discourse.View.extend({
|
||||
}
|
||||
|
||||
var icon = this.get('icon');
|
||||
|
||||
buffer.push("<a href='#'>");
|
||||
if(icon) {
|
||||
buffer.push("<i class='glyph icon icon-" + icon + "'></i>");
|
||||
buffer.push("<i class='glyph icon icon-" + icon + "'></i> ");
|
||||
}
|
||||
|
||||
buffer.push("<a href='#'>" + description +
|
||||
buffer.push(description +
|
||||
" <span class='count'>(" + count + ")</span>");
|
||||
|
||||
|
||||
|
155
app/assets/javascripts/external/handlebars-1.0.rc.4.js → app/assets/javascripts/external/handlebars.js
vendored
Executable file → Normal file
155
app/assets/javascripts/external/handlebars-1.0.rc.4.js → app/assets/javascripts/external/handlebars.js
vendored
Executable file → Normal file
@ -29,13 +29,14 @@ var Handlebars = {};
|
||||
;
|
||||
// lib/handlebars/base.js
|
||||
|
||||
Handlebars.VERSION = "1.0.0-rc.4";
|
||||
Handlebars.COMPILER_REVISION = 3;
|
||||
Handlebars.VERSION = "1.0.0";
|
||||
Handlebars.COMPILER_REVISION = 4;
|
||||
|
||||
Handlebars.REVISION_CHANGES = {
|
||||
1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it
|
||||
2: '== 1.0.0-rc.3',
|
||||
3: '>= 1.0.0-rc.4'
|
||||
3: '== 1.0.0-rc.4',
|
||||
4: '>= 1.0.0'
|
||||
};
|
||||
|
||||
Handlebars.helpers = {};
|
||||
@ -67,7 +68,7 @@ Handlebars.registerHelper('helperMissing', function(arg) {
|
||||
if(arguments.length === 2) {
|
||||
return undefined;
|
||||
} else {
|
||||
throw new Error("Could not find property '" + arg + "'");
|
||||
throw new Error("Missing helper: '" + arg + "'");
|
||||
}
|
||||
});
|
||||
|
||||
@ -124,6 +125,9 @@ Handlebars.registerHelper('each', function(context, options) {
|
||||
var fn = options.fn, inverse = options.inverse;
|
||||
var i = 0, ret = "", data;
|
||||
|
||||
var type = toString.call(context);
|
||||
if(type === functionType) { context = context.call(this); }
|
||||
|
||||
if (options.data) {
|
||||
data = Handlebars.createFrame(options.data);
|
||||
}
|
||||
@ -152,22 +156,25 @@ Handlebars.registerHelper('each', function(context, options) {
|
||||
return ret;
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('if', function(context, options) {
|
||||
var type = toString.call(context);
|
||||
if(type === functionType) { context = context.call(this); }
|
||||
Handlebars.registerHelper('if', function(conditional, options) {
|
||||
var type = toString.call(conditional);
|
||||
if(type === functionType) { conditional = conditional.call(this); }
|
||||
|
||||
if(!context || Handlebars.Utils.isEmpty(context)) {
|
||||
if(!conditional || Handlebars.Utils.isEmpty(conditional)) {
|
||||
return options.inverse(this);
|
||||
} else {
|
||||
return options.fn(this);
|
||||
}
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('unless', function(context, options) {
|
||||
return Handlebars.helpers['if'].call(this, context, {fn: options.inverse, inverse: options.fn});
|
||||
Handlebars.registerHelper('unless', function(conditional, options) {
|
||||
return Handlebars.helpers['if'].call(this, conditional, {fn: options.inverse, inverse: options.fn});
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('with', function(context, options) {
|
||||
var type = toString.call(context);
|
||||
if(type === functionType) { context = context.call(this); }
|
||||
|
||||
if (!Handlebars.Utils.isEmpty(context)) return options.fn(context);
|
||||
});
|
||||
|
||||
@ -181,9 +188,9 @@ Handlebars.registerHelper('log', function(context, options) {
|
||||
var handlebars = (function(){
|
||||
var parser = {trace: function trace() { },
|
||||
yy: {},
|
||||
symbols_: {"error":2,"root":3,"program":4,"EOF":5,"simpleInverse":6,"statements":7,"statement":8,"openInverse":9,"closeBlock":10,"openBlock":11,"mustache":12,"partial":13,"CONTENT":14,"COMMENT":15,"OPEN_BLOCK":16,"inMustache":17,"CLOSE":18,"OPEN_INVERSE":19,"OPEN_ENDBLOCK":20,"path":21,"OPEN":22,"OPEN_UNESCAPED":23,"OPEN_PARTIAL":24,"partialName":25,"params":26,"hash":27,"DATA":28,"param":29,"STRING":30,"INTEGER":31,"BOOLEAN":32,"hashSegments":33,"hashSegment":34,"ID":35,"EQUALS":36,"PARTIAL_NAME":37,"pathSegments":38,"SEP":39,"$accept":0,"$end":1},
|
||||
terminals_: {2:"error",5:"EOF",14:"CONTENT",15:"COMMENT",16:"OPEN_BLOCK",18:"CLOSE",19:"OPEN_INVERSE",20:"OPEN_ENDBLOCK",22:"OPEN",23:"OPEN_UNESCAPED",24:"OPEN_PARTIAL",28:"DATA",30:"STRING",31:"INTEGER",32:"BOOLEAN",35:"ID",36:"EQUALS",37:"PARTIAL_NAME",39:"SEP"},
|
||||
productions_: [0,[3,2],[4,2],[4,3],[4,2],[4,1],[4,1],[4,0],[7,1],[7,2],[8,3],[8,3],[8,1],[8,1],[8,1],[8,1],[11,3],[9,3],[10,3],[12,3],[12,3],[13,3],[13,4],[6,2],[17,3],[17,2],[17,2],[17,1],[17,1],[26,2],[26,1],[29,1],[29,1],[29,1],[29,1],[29,1],[27,1],[33,2],[33,1],[34,3],[34,3],[34,3],[34,3],[34,3],[25,1],[21,1],[38,3],[38,1]],
|
||||
symbols_: {"error":2,"root":3,"program":4,"EOF":5,"simpleInverse":6,"statements":7,"statement":8,"openInverse":9,"closeBlock":10,"openBlock":11,"mustache":12,"partial":13,"CONTENT":14,"COMMENT":15,"OPEN_BLOCK":16,"inMustache":17,"CLOSE":18,"OPEN_INVERSE":19,"OPEN_ENDBLOCK":20,"path":21,"OPEN":22,"OPEN_UNESCAPED":23,"CLOSE_UNESCAPED":24,"OPEN_PARTIAL":25,"partialName":26,"params":27,"hash":28,"dataName":29,"param":30,"STRING":31,"INTEGER":32,"BOOLEAN":33,"hashSegments":34,"hashSegment":35,"ID":36,"EQUALS":37,"DATA":38,"pathSegments":39,"SEP":40,"$accept":0,"$end":1},
|
||||
terminals_: {2:"error",5:"EOF",14:"CONTENT",15:"COMMENT",16:"OPEN_BLOCK",18:"CLOSE",19:"OPEN_INVERSE",20:"OPEN_ENDBLOCK",22:"OPEN",23:"OPEN_UNESCAPED",24:"CLOSE_UNESCAPED",25:"OPEN_PARTIAL",31:"STRING",32:"INTEGER",33:"BOOLEAN",36:"ID",37:"EQUALS",38:"DATA",40:"SEP"},
|
||||
productions_: [0,[3,2],[4,2],[4,3],[4,2],[4,1],[4,1],[4,0],[7,1],[7,2],[8,3],[8,3],[8,1],[8,1],[8,1],[8,1],[11,3],[9,3],[10,3],[12,3],[12,3],[13,3],[13,4],[6,2],[17,3],[17,2],[17,2],[17,1],[17,1],[27,2],[27,1],[30,1],[30,1],[30,1],[30,1],[30,1],[28,1],[34,2],[34,1],[35,3],[35,3],[35,3],[35,3],[35,3],[26,1],[26,1],[26,1],[29,2],[21,1],[39,3],[39,1]],
|
||||
performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {
|
||||
|
||||
var $0 = $$.length - 1;
|
||||
@ -224,7 +231,10 @@ case 17: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]);
|
||||
break;
|
||||
case 18: this.$ = $$[$0-1];
|
||||
break;
|
||||
case 19: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]);
|
||||
case 19:
|
||||
// Parsing out the '&' escape token at this level saves ~500 bytes after min due to the removal of one parser node.
|
||||
this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], $$[$0-2][2] === '&');
|
||||
|
||||
break;
|
||||
case 20: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], true);
|
||||
break;
|
||||
@ -242,7 +252,7 @@ case 26: this.$ = [[$$[$0-1]], $$[$0]];
|
||||
break;
|
||||
case 27: this.$ = [[$$[$0]], null];
|
||||
break;
|
||||
case 28: this.$ = [[new yy.DataNode($$[$0])], null];
|
||||
case 28: this.$ = [[$$[$0]], null];
|
||||
break;
|
||||
case 29: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
|
||||
break;
|
||||
@ -256,7 +266,7 @@ case 33: this.$ = new yy.IntegerNode($$[$0]);
|
||||
break;
|
||||
case 34: this.$ = new yy.BooleanNode($$[$0]);
|
||||
break;
|
||||
case 35: this.$ = new yy.DataNode($$[$0]);
|
||||
case 35: this.$ = $$[$0];
|
||||
break;
|
||||
case 36: this.$ = new yy.HashNode($$[$0]);
|
||||
break;
|
||||
@ -272,20 +282,26 @@ case 41: this.$ = [$$[$0-2], new yy.IntegerNode($$[$0])];
|
||||
break;
|
||||
case 42: this.$ = [$$[$0-2], new yy.BooleanNode($$[$0])];
|
||||
break;
|
||||
case 43: this.$ = [$$[$0-2], new yy.DataNode($$[$0])];
|
||||
case 43: this.$ = [$$[$0-2], $$[$0]];
|
||||
break;
|
||||
case 44: this.$ = new yy.PartialNameNode($$[$0]);
|
||||
break;
|
||||
case 45: this.$ = new yy.IdNode($$[$0]);
|
||||
case 45: this.$ = new yy.PartialNameNode(new yy.StringNode($$[$0]));
|
||||
break;
|
||||
case 46: $$[$0-2].push($$[$0]); this.$ = $$[$0-2];
|
||||
case 46: this.$ = new yy.PartialNameNode(new yy.IntegerNode($$[$0]));
|
||||
break;
|
||||
case 47: this.$ = [$$[$0]];
|
||||
case 47: this.$ = new yy.DataNode($$[$0]);
|
||||
break;
|
||||
case 48: this.$ = new yy.IdNode($$[$0]);
|
||||
break;
|
||||
case 49: $$[$0-2].push({part: $$[$0], separator: $$[$0-1]}); this.$ = $$[$0-2];
|
||||
break;
|
||||
case 50: this.$ = [{part: $$[$0]}];
|
||||
break;
|
||||
}
|
||||
},
|
||||
table: [{3:1,4:2,5:[2,7],6:3,7:4,8:6,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,5],22:[1,14],23:[1,15],24:[1,16]},{1:[3]},{5:[1,17]},{5:[2,6],7:18,8:6,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,19],20:[2,6],22:[1,14],23:[1,15],24:[1,16]},{5:[2,5],6:20,8:21,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,5],20:[2,5],22:[1,14],23:[1,15],24:[1,16]},{17:23,18:[1,22],21:24,28:[1,25],35:[1,27],38:26},{5:[2,8],14:[2,8],15:[2,8],16:[2,8],19:[2,8],20:[2,8],22:[2,8],23:[2,8],24:[2,8]},{4:28,6:3,7:4,8:6,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,5],20:[2,7],22:[1,14],23:[1,15],24:[1,16]},{4:29,6:3,7:4,8:6,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,5],20:[2,7],22:[1,14],23:[1,15],24:[1,16]},{5:[2,12],14:[2,12],15:[2,12],16:[2,12],19:[2,12],20:[2,12],22:[2,12],23:[2,12],24:[2,12]},{5:[2,13],14:[2,13],15:[2,13],16:[2,13],19:[2,13],20:[2,13],22:[2,13],23:[2,13],24:[2,13]},{5:[2,14],14:[2,14],15:[2,14],16:[2,14],19:[2,14],20:[2,14],22:[2,14],23:[2,14],24:[2,14]},{5:[2,15],14:[2,15],15:[2,15],16:[2,15],19:[2,15],20:[2,15],22:[2,15],23:[2,15],24:[2,15]},{17:30,21:24,28:[1,25],35:[1,27],38:26},{17:31,21:24,28:[1,25],35:[1,27],38:26},{17:32,21:24,28:[1,25],35:[1,27],38:26},{25:33,37:[1,34]},{1:[2,1]},{5:[2,2],8:21,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,19],20:[2,2],22:[1,14],23:[1,15],24:[1,16]},{17:23,21:24,28:[1,25],35:[1,27],38:26},{5:[2,4],7:35,8:6,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,19],20:[2,4],22:[1,14],23:[1,15],24:[1,16]},{5:[2,9],14:[2,9],15:[2,9],16:[2,9],19:[2,9],20:[2,9],22:[2,9],23:[2,9],24:[2,9]},{5:[2,23],14:[2,23],15:[2,23],16:[2,23],19:[2,23],20:[2,23],22:[2,23],23:[2,23],24:[2,23]},{18:[1,36]},{18:[2,27],21:41,26:37,27:38,28:[1,45],29:39,30:[1,42],31:[1,43],32:[1,44],33:40,34:46,35:[1,47],38:26},{18:[2,28]},{18:[2,45],28:[2,45],30:[2,45],31:[2,45],32:[2,45],35:[2,45],39:[1,48]},{18:[2,47],28:[2,47],30:[2,47],31:[2,47],32:[2,47],35:[2,47],39:[2,47]},{10:49,20:[1,50]},{10:51,20:[1,50]},{18:[1,52]},{18:[1,53]},{18:[1,54]},{18:[1,55],21:56,35:[1,27],38:26},{18:[2,44],35:[2,44]},{5:[2,3],8:21,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,19],20:[2,3],22:[1,14],23:[1,15],24:[1,16]},{14:[2,17],15:[2,17],16:[2,17],19:[2,17],20:[2,17],22:[2,17],23:[2,17],24:[2,17]},{18:[2,25],21:41,27:57,28:[1,45],29:58,30:[1,42],31:[1,43],32:[1,44],33:40,34:46,35:[1,47],38:26},{18:[2,26]},{18:[2,30],28:[2,30],30:[2,30],31:[2,30],32:[2,30],35:[2,30]},{18:[2,36],34:59,35:[1,60]},{18:[2,31],28:[2,31],30:[2,31],31:[2,31],32:[2,31],35:[2,31]},{18:[2,32],28:[2,32],30:[2,32],31:[2,32],32:[2,32],35:[2,32]},{18:[2,33],28:[2,33],30:[2,33],31:[2,33],32:[2,33],35:[2,33]},{18:[2,34],28:[2,34],30:[2,34],31:[2,34],32:[2,34],35:[2,34]},{18:[2,35],28:[2,35],30:[2,35],31:[2,35],32:[2,35],35:[2,35]},{18:[2,38],35:[2,38]},{18:[2,47],28:[2,47],30:[2,47],31:[2,47],32:[2,47],35:[2,47],36:[1,61],39:[2,47]},{35:[1,62]},{5:[2,10],14:[2,10],15:[2,10],16:[2,10],19:[2,10],20:[2,10],22:[2,10],23:[2,10],24:[2,10]},{21:63,35:[1,27],38:26},{5:[2,11],14:[2,11],15:[2,11],16:[2,11],19:[2,11],20:[2,11],22:[2,11],23:[2,11],24:[2,11]},{14:[2,16],15:[2,16],16:[2,16],19:[2,16],20:[2,16],22:[2,16],23:[2,16],24:[2,16]},{5:[2,19],14:[2,19],15:[2,19],16:[2,19],19:[2,19],20:[2,19],22:[2,19],23:[2,19],24:[2,19]},{5:[2,20],14:[2,20],15:[2,20],16:[2,20],19:[2,20],20:[2,20],22:[2,20],23:[2,20],24:[2,20]},{5:[2,21],14:[2,21],15:[2,21],16:[2,21],19:[2,21],20:[2,21],22:[2,21],23:[2,21],24:[2,21]},{18:[1,64]},{18:[2,24]},{18:[2,29],28:[2,29],30:[2,29],31:[2,29],32:[2,29],35:[2,29]},{18:[2,37],35:[2,37]},{36:[1,61]},{21:65,28:[1,69],30:[1,66],31:[1,67],32:[1,68],35:[1,27],38:26},{18:[2,46],28:[2,46],30:[2,46],31:[2,46],32:[2,46],35:[2,46],39:[2,46]},{18:[1,70]},{5:[2,22],14:[2,22],15:[2,22],16:[2,22],19:[2,22],20:[2,22],22:[2,22],23:[2,22],24:[2,22]},{18:[2,39],35:[2,39]},{18:[2,40],35:[2,40]},{18:[2,41],35:[2,41]},{18:[2,42],35:[2,42]},{18:[2,43],35:[2,43]},{5:[2,18],14:[2,18],15:[2,18],16:[2,18],19:[2,18],20:[2,18],22:[2,18],23:[2,18],24:[2,18]}],
|
||||
defaultActions: {17:[2,1],25:[2,28],38:[2,26],57:[2,24]},
|
||||
table: [{3:1,4:2,5:[2,7],6:3,7:4,8:6,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,5],22:[1,14],23:[1,15],25:[1,16]},{1:[3]},{5:[1,17]},{5:[2,6],7:18,8:6,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,19],20:[2,6],22:[1,14],23:[1,15],25:[1,16]},{5:[2,5],6:20,8:21,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,5],20:[2,5],22:[1,14],23:[1,15],25:[1,16]},{17:23,18:[1,22],21:24,29:25,36:[1,28],38:[1,27],39:26},{5:[2,8],14:[2,8],15:[2,8],16:[2,8],19:[2,8],20:[2,8],22:[2,8],23:[2,8],25:[2,8]},{4:29,6:3,7:4,8:6,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,5],20:[2,7],22:[1,14],23:[1,15],25:[1,16]},{4:30,6:3,7:4,8:6,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,5],20:[2,7],22:[1,14],23:[1,15],25:[1,16]},{5:[2,12],14:[2,12],15:[2,12],16:[2,12],19:[2,12],20:[2,12],22:[2,12],23:[2,12],25:[2,12]},{5:[2,13],14:[2,13],15:[2,13],16:[2,13],19:[2,13],20:[2,13],22:[2,13],23:[2,13],25:[2,13]},{5:[2,14],14:[2,14],15:[2,14],16:[2,14],19:[2,14],20:[2,14],22:[2,14],23:[2,14],25:[2,14]},{5:[2,15],14:[2,15],15:[2,15],16:[2,15],19:[2,15],20:[2,15],22:[2,15],23:[2,15],25:[2,15]},{17:31,21:24,29:25,36:[1,28],38:[1,27],39:26},{17:32,21:24,29:25,36:[1,28],38:[1,27],39:26},{17:33,21:24,29:25,36:[1,28],38:[1,27],39:26},{21:35,26:34,31:[1,36],32:[1,37],36:[1,28],39:26},{1:[2,1]},{5:[2,2],8:21,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,19],20:[2,2],22:[1,14],23:[1,15],25:[1,16]},{17:23,21:24,29:25,36:[1,28],38:[1,27],39:26},{5:[2,4],7:38,8:6,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,19],20:[2,4],22:[1,14],23:[1,15],25:[1,16]},{5:[2,9],14:[2,9],15:[2,9],16:[2,9],19:[2,9],20:[2,9],22:[2,9],23:[2,9],25:[2,9]},{5:[2,23],14:[2,23],15:[2,23],16:[2,23],19:[2,23],20:[2,23],22:[2,23],23:[2,23],25:[2,23]},{18:[1,39]},{18:[2,27],21:44,24:[2,27],27:40,28:41,29:48,30:42,31:[1,45],32:[1,46],33:[1,47],34:43,35:49,36:[1,50],38:[1,27],39:26},{18:[2,28],24:[2,28]},{18:[2,48],24:[2,48],31:[2,48],32:[2,48],33:[2,48],36:[2,48],38:[2,48],40:[1,51]},{21:52,36:[1,28],39:26},{18:[2,50],24:[2,50],31:[2,50],32:[2,50],33:[2,50],36:[2,50],38:[2,50],40:[2,50]},{10:53,20:[1,54]},{10:55,20:[1,54]},{18:[1,56]},{18:[1,57]},{24:[1,58]},{18:[1,59],21:60,36:[1,28],39:26},{18:[2,44],36:[2,44]},{18:[2,45],36:[2,45]},{18:[2,46],36:[2,46]},{5:[2,3],8:21,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,19],20:[2,3],22:[1,14],23:[1,15],25:[1,16]},{14:[2,17],15:[2,17],16:[2,17],19:[2,17],20:[2,17],22:[2,17],23:[2,17],25:[2,17]},{18:[2,25],21:44,24:[2,25],28:61,29:48,30:62,31:[1,45],32:[1,46],33:[1,47],34:43,35:49,36:[1,50],38:[1,27],39:26},{18:[2,26],24:[2,26]},{18:[2,30],24:[2,30],31:[2,30],32:[2,30],33:[2,30],36:[2,30],38:[2,30]},{18:[2,36],24:[2,36],35:63,36:[1,64]},{18:[2,31],24:[2,31],31:[2,31],32:[2,31],33:[2,31],36:[2,31],38:[2,31]},{18:[2,32],24:[2,32],31:[2,32],32:[2,32],33:[2,32],36:[2,32],38:[2,32]},{18:[2,33],24:[2,33],31:[2,33],32:[2,33],33:[2,33],36:[2,33],38:[2,33]},{18:[2,34],24:[2,34],31:[2,34],32:[2,34],33:[2,34],36:[2,34],38:[2,34]},{18:[2,35],24:[2,35],31:[2,35],32:[2,35],33:[2,35],36:[2,35],38:[2,35]},{18:[2,38],24:[2,38],36:[2,38]},{18:[2,50],24:[2,50],31:[2,50],32:[2,50],33:[2,50],36:[2,50],37:[1,65],38:[2,50],40:[2,50]},{36:[1,66]},{18:[2,47],24:[2,47],31:[2,47],32:[2,47],33:[2,47],36:[2,47],38:[2,47]},{5:[2,10],14:[2,10],15:[2,10],16:[2,10],19:[2,10],20:[2,10],22:[2,10],23:[2,10],25:[2,10]},{21:67,36:[1,28],39:26},{5:[2,11],14:[2,11],15:[2,11],16:[2,11],19:[2,11],20:[2,11],22:[2,11],23:[2,11],25:[2,11]},{14:[2,16],15:[2,16],16:[2,16],19:[2,16],20:[2,16],22:[2,16],23:[2,16],25:[2,16]},{5:[2,19],14:[2,19],15:[2,19],16:[2,19],19:[2,19],20:[2,19],22:[2,19],23:[2,19],25:[2,19]},{5:[2,20],14:[2,20],15:[2,20],16:[2,20],19:[2,20],20:[2,20],22:[2,20],23:[2,20],25:[2,20]},{5:[2,21],14:[2,21],15:[2,21],16:[2,21],19:[2,21],20:[2,21],22:[2,21],23:[2,21],25:[2,21]},{18:[1,68]},{18:[2,24],24:[2,24]},{18:[2,29],24:[2,29],31:[2,29],32:[2,29],33:[2,29],36:[2,29],38:[2,29]},{18:[2,37],24:[2,37],36:[2,37]},{37:[1,65]},{21:69,29:73,31:[1,70],32:[1,71],33:[1,72],36:[1,28],38:[1,27],39:26},{18:[2,49],24:[2,49],31:[2,49],32:[2,49],33:[2,49],36:[2,49],38:[2,49],40:[2,49]},{18:[1,74]},{5:[2,22],14:[2,22],15:[2,22],16:[2,22],19:[2,22],20:[2,22],22:[2,22],23:[2,22],25:[2,22]},{18:[2,39],24:[2,39],36:[2,39]},{18:[2,40],24:[2,40],36:[2,40]},{18:[2,41],24:[2,41],36:[2,41]},{18:[2,42],24:[2,42],36:[2,42]},{18:[2,43],24:[2,43],36:[2,43]},{5:[2,18],14:[2,18],15:[2,18],16:[2,18],19:[2,18],20:[2,18],22:[2,18],23:[2,18],25:[2,18]}],
|
||||
defaultActions: {17:[2,1]},
|
||||
parseError: function parseError(str, hash) {
|
||||
throw new Error(str);
|
||||
},
|
||||
@ -584,7 +600,7 @@ case 3:
|
||||
break;
|
||||
case 4: yy_.yytext = yy_.yytext.substr(0, yy_.yyleng-4); this.popState(); return 15;
|
||||
break;
|
||||
case 5: this.begin("par"); return 24;
|
||||
case 5: return 25;
|
||||
break;
|
||||
case 6: return 16;
|
||||
break;
|
||||
@ -596,7 +612,7 @@ case 9: return 19;
|
||||
break;
|
||||
case 10: return 23;
|
||||
break;
|
||||
case 11: return 23;
|
||||
case 11: return 22;
|
||||
break;
|
||||
case 12: this.popState(); this.begin('com');
|
||||
break;
|
||||
@ -604,48 +620,44 @@ case 13: yy_.yytext = yy_.yytext.substr(3,yy_.yyleng-5); this.popState(); return
|
||||
break;
|
||||
case 14: return 22;
|
||||
break;
|
||||
case 15: return 36;
|
||||
case 15: return 37;
|
||||
break;
|
||||
case 16: return 35;
|
||||
case 16: return 36;
|
||||
break;
|
||||
case 17: return 35;
|
||||
case 17: return 36;
|
||||
break;
|
||||
case 18: return 39;
|
||||
case 18: return 40;
|
||||
break;
|
||||
case 19: /*ignore whitespace*/
|
||||
break;
|
||||
case 20: this.popState(); return 18;
|
||||
case 20: this.popState(); return 24;
|
||||
break;
|
||||
case 21: this.popState(); return 18;
|
||||
break;
|
||||
case 22: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 30;
|
||||
case 22: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 31;
|
||||
break;
|
||||
case 23: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\'/g,"'"); return 30;
|
||||
case 23: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\'/g,"'"); return 31;
|
||||
break;
|
||||
case 24: yy_.yytext = yy_.yytext.substr(1); return 28;
|
||||
case 24: return 38;
|
||||
break;
|
||||
case 25: return 32;
|
||||
case 25: return 33;
|
||||
break;
|
||||
case 26: return 32;
|
||||
case 26: return 33;
|
||||
break;
|
||||
case 27: return 31;
|
||||
case 27: return 32;
|
||||
break;
|
||||
case 28: return 35;
|
||||
case 28: return 36;
|
||||
break;
|
||||
case 29: yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 35;
|
||||
case 29: yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 36;
|
||||
break;
|
||||
case 30: return 'INVALID';
|
||||
break;
|
||||
case 31: /*ignore whitespace*/
|
||||
break;
|
||||
case 32: this.popState(); return 37;
|
||||
break;
|
||||
case 33: return 5;
|
||||
case 31: return 5;
|
||||
break;
|
||||
}
|
||||
};
|
||||
lexer.rules = [/^(?:\\\\(?=(\{\{)))/,/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|$)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\{\{>)/,/^(?:\{\{#)/,/^(?:\{\{\/)/,/^(?:\{\{\^)/,/^(?:\{\{\s*else\b)/,/^(?:\{\{\{)/,/^(?:\{\{&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{)/,/^(?:=)/,/^(?:\.(?=[}/ ]))/,/^(?:\.\.)/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}\}\})/,/^(?:\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@[a-zA-Z]+)/,/^(?:true(?=[}\s]))/,/^(?:false(?=[}\s]))/,/^(?:-?[0-9]+(?=[}\s]))/,/^(?:[a-zA-Z0-9_$:\-]+(?=[=}\s\/.]))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:\s+)/,/^(?:[a-zA-Z0-9_$\-\/]+)/,/^(?:$)/];
|
||||
lexer.conditions = {"mu":{"rules":[5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,33],"inclusive":false},"emu":{"rules":[3],"inclusive":false},"com":{"rules":[4],"inclusive":false},"par":{"rules":[31,32],"inclusive":false},"INITIAL":{"rules":[0,1,2,33],"inclusive":true}};
|
||||
lexer.rules = [/^(?:\\\\(?=(\{\{)))/,/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|$)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\{\{>)/,/^(?:\{\{#)/,/^(?:\{\{\/)/,/^(?:\{\{\^)/,/^(?:\{\{\s*else\b)/,/^(?:\{\{\{)/,/^(?:\{\{&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{)/,/^(?:=)/,/^(?:\.(?=[}\/ ]))/,/^(?:\.\.)/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}\}\})/,/^(?:\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=[}\s]))/,/^(?:false(?=[}\s]))/,/^(?:-?[0-9]+(?=[}\s]))/,/^(?:[^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=[=}\s\/.]))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:$)/];
|
||||
lexer.conditions = {"mu":{"rules":[5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31],"inclusive":false},"emu":{"rules":[3],"inclusive":false},"com":{"rules":[4],"inclusive":false},"INITIAL":{"rules":[0,1,2,31],"inclusive":true}};
|
||||
return lexer;})()
|
||||
parser.lexer = lexer;
|
||||
function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser;
|
||||
@ -731,21 +743,24 @@ Handlebars.AST.HashNode = function(pairs) {
|
||||
|
||||
Handlebars.AST.IdNode = function(parts) {
|
||||
this.type = "ID";
|
||||
this.original = parts.join(".");
|
||||
|
||||
var dig = [], depth = 0;
|
||||
var original = "",
|
||||
dig = [],
|
||||
depth = 0;
|
||||
|
||||
for(var i=0,l=parts.length; i<l; i++) {
|
||||
var part = parts[i];
|
||||
var part = parts[i].part;
|
||||
original += (parts[i].separator || '') + part;
|
||||
|
||||
if (part === ".." || part === "." || part === "this") {
|
||||
if (dig.length > 0) { throw new Handlebars.Exception("Invalid path: " + this.original); }
|
||||
if (dig.length > 0) { throw new Handlebars.Exception("Invalid path: " + original); }
|
||||
else if (part === "..") { depth++; }
|
||||
else { this.isScoped = true; }
|
||||
}
|
||||
else { dig.push(part); }
|
||||
}
|
||||
|
||||
this.original = original;
|
||||
this.parts = dig;
|
||||
this.string = dig.join('.');
|
||||
this.depth = depth;
|
||||
@ -759,7 +774,7 @@ Handlebars.AST.IdNode = function(parts) {
|
||||
|
||||
Handlebars.AST.PartialNameNode = function(name) {
|
||||
this.type = "PARTIAL_NAME";
|
||||
this.name = name;
|
||||
this.name = name.original;
|
||||
};
|
||||
|
||||
Handlebars.AST.DataNode = function(id) {
|
||||
@ -769,13 +784,15 @@ Handlebars.AST.DataNode = function(id) {
|
||||
|
||||
Handlebars.AST.StringNode = function(string) {
|
||||
this.type = "STRING";
|
||||
this.string = string;
|
||||
this.stringModeValue = string;
|
||||
this.original =
|
||||
this.string =
|
||||
this.stringModeValue = string;
|
||||
};
|
||||
|
||||
Handlebars.AST.IntegerNode = function(integer) {
|
||||
this.type = "INTEGER";
|
||||
this.integer = integer;
|
||||
this.original =
|
||||
this.integer = integer;
|
||||
this.stringModeValue = Number(integer);
|
||||
};
|
||||
|
||||
@ -1162,7 +1179,15 @@ Compiler.prototype = {
|
||||
|
||||
DATA: function(data) {
|
||||
this.options.data = true;
|
||||
this.opcode('lookupData', data.id);
|
||||
if (data.id.isScoped || data.id.depth) {
|
||||
throw new Handlebars.Exception('Scoped data references are not supported: ' + data.original);
|
||||
}
|
||||
|
||||
this.opcode('lookupData');
|
||||
var parts = data.id.parts;
|
||||
for(var i=0, l=parts.length; i<l; i++) {
|
||||
this.opcode('lookup', parts[i]);
|
||||
}
|
||||
},
|
||||
|
||||
STRING: function(string) {
|
||||
@ -1361,8 +1386,9 @@ JavaScriptCompiler.prototype = {
|
||||
|
||||
if (!this.isChild) {
|
||||
var namespace = this.namespace;
|
||||
var copies = "helpers = helpers || " + namespace + ".helpers;";
|
||||
if (this.environment.usePartial) { copies = copies + " partials = partials || " + namespace + ".partials;"; }
|
||||
|
||||
var copies = "helpers = this.merge(helpers, " + namespace + ".helpers);";
|
||||
if (this.environment.usePartial) { copies = copies + " partials = this.merge(partials, " + namespace + ".partials);"; }
|
||||
if (this.options.data) { copies = copies + " data = data || {};"; }
|
||||
out.push(copies);
|
||||
} else {
|
||||
@ -1391,7 +1417,9 @@ JavaScriptCompiler.prototype = {
|
||||
// Generate minimizer alias mappings
|
||||
if (!this.isChild) {
|
||||
for (var alias in this.context.aliases) {
|
||||
this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias];
|
||||
if (this.context.aliases.hasOwnProperty(alias)) {
|
||||
this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1610,7 +1638,7 @@ JavaScriptCompiler.prototype = {
|
||||
//
|
||||
// Push the result of looking up `id` on the current data
|
||||
lookupData: function(id) {
|
||||
this.push(this.nameLookup('data', id, 'data'));
|
||||
this.push('data');
|
||||
},
|
||||
|
||||
// [pushStringParam]
|
||||
@ -1717,8 +1745,9 @@ JavaScriptCompiler.prototype = {
|
||||
this.context.aliases.helperMissing = 'helpers.helperMissing';
|
||||
|
||||
var helper = this.lastHelper = this.setupHelper(paramSize, name, true);
|
||||
var nonHelper = this.nameLookup('depth' + this.lastContext, name, 'context');
|
||||
|
||||
this.push(helper.name);
|
||||
this.push(helper.name + ' || ' + nonHelper);
|
||||
this.replaceStack(function(name) {
|
||||
return name + ' ? ' + name + '.call(' +
|
||||
helper.callParams + ") " + ": helperMissing.call(" +
|
||||
@ -2163,6 +2192,16 @@ Handlebars.VM = {
|
||||
}
|
||||
return programWrapper;
|
||||
},
|
||||
merge: function(param, common) {
|
||||
var ret = param || common;
|
||||
|
||||
if (param && common) {
|
||||
ret = {};
|
||||
Handlebars.Utils.extend(ret, common);
|
||||
Handlebars.Utils.extend(ret, param);
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
programWithDepth: Handlebars.VM.programWithDepth,
|
||||
noop: Handlebars.VM.noop,
|
||||
compilerInfo: null
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -32,30 +32,31 @@ PreloadStore = {
|
||||
**/
|
||||
getAndRemove: function(key, finder) {
|
||||
var preloadStore = this;
|
||||
return Ember.Deferred.promise(function(promise) {
|
||||
if (preloadStore.data[key]) {
|
||||
promise.resolve(preloadStore.data[key]);
|
||||
delete preloadStore.data[key];
|
||||
} else {
|
||||
|
||||
if (finder) {
|
||||
var result = finder();
|
||||
if (preloadStore.data[key]) {
|
||||
var promise = Ember.RSVP.resolve(preloadStore.data[key]);
|
||||
delete preloadStore.data[key];
|
||||
return promise;
|
||||
}
|
||||
|
||||
// If the finder returns a promise, we support that too
|
||||
if (result.then) {
|
||||
result.then(function(result) {
|
||||
return promise.resolve(result);
|
||||
}, function(result) {
|
||||
return promise.reject(result);
|
||||
});
|
||||
} else {
|
||||
promise.resolve(result);
|
||||
}
|
||||
if (finder) {
|
||||
return Ember.Deferred.promise(function(promise) {
|
||||
var result = finder();
|
||||
|
||||
// If the finder returns a promise, we support that too
|
||||
if (result.then) {
|
||||
result.then(function(result) {
|
||||
return promise.resolve(result);
|
||||
}, function(result) {
|
||||
return promise.reject(result);
|
||||
});
|
||||
} else {
|
||||
promise.resolve(null);
|
||||
promise.resolve(result);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return Ember.RSVP.resolve(null);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -82,7 +82,7 @@
|
||||
|
||||
.badge-group {
|
||||
@extend %badge;
|
||||
padding: 3px 2px 3px 8px;
|
||||
padding: 3px 5px;
|
||||
color: $black;
|
||||
text-shadow: 0 1px 0 rgba($white, 0.2);
|
||||
background-color: #ddd;
|
||||
|
@ -55,6 +55,7 @@
|
||||
// --------------------------------------------------
|
||||
|
||||
.nav-stacked {
|
||||
position: relative;
|
||||
@extend %nav;
|
||||
border: 1px solid $nav-stacked-border-color;
|
||||
padding: 0;
|
||||
@ -69,7 +70,7 @@
|
||||
}
|
||||
> a {
|
||||
margin: 0;
|
||||
padding: 13px;
|
||||
padding: 13px 13px 13px 30px;
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
line-height: 20px;
|
||||
@ -94,17 +95,11 @@
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.no-glyph {
|
||||
a {
|
||||
padding-left: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.glyph {
|
||||
font-size: 12px;
|
||||
margin: 15px 0 0 0;
|
||||
width: 30px;
|
||||
text-align: center;
|
||||
float: left;
|
||||
position: absolute;
|
||||
left: 2px;
|
||||
}
|
||||
}
|
||||
|
@ -102,7 +102,7 @@ class ApplicationController < ActionController::Base
|
||||
guardian.current_user.sync_notification_channel_position
|
||||
end
|
||||
|
||||
store_preloaded("site", Site.cached_json(current_user))
|
||||
store_preloaded("site", Site.cached_json(guardian))
|
||||
|
||||
if current_user.present?
|
||||
store_preloaded("currentUser", MultiJson.dump(CurrentUserSerializer.new(current_user, root: false)))
|
||||
|
@ -52,16 +52,18 @@ class CategoriesController < ApplicationController
|
||||
[:name, :color, :text_color]
|
||||
end
|
||||
|
||||
def category_param_keys
|
||||
[required_param_keys, :hotness, :secure, :group_names, :auto_close_days].flatten!
|
||||
end
|
||||
|
||||
def category_params
|
||||
required_param_keys.each do |key|
|
||||
params.require(key)
|
||||
end
|
||||
|
||||
params.permit(*category_param_keys)
|
||||
if p = params[:permissions]
|
||||
p.each do |k,v|
|
||||
p[k] = v.to_i
|
||||
end
|
||||
end
|
||||
|
||||
params.permit(*required_param_keys, :hotness, :auto_close_days, :permissions => [*p.try(:keys)])
|
||||
end
|
||||
|
||||
def fetch_category
|
||||
|
@ -27,6 +27,10 @@ class StaticController < ApplicationController
|
||||
file = "static/#{page}.en"
|
||||
end
|
||||
|
||||
if not lookup_context.find_all("#{file}.html").any?
|
||||
file = "static/#{page}"
|
||||
end
|
||||
|
||||
if lookup_context.find_all("#{file}.html").any?
|
||||
render file, layout: !request.xhr?, formats: [:html]
|
||||
return
|
||||
|
@ -60,22 +60,7 @@ class Users::OmniauthCallbacksController < ApplicationController
|
||||
auth_provider: "Twitter"
|
||||
}
|
||||
|
||||
if user_info
|
||||
if user_info.user.active?
|
||||
if Guardian.new(user_info.user).can_access_forum?
|
||||
log_on_user(user_info.user)
|
||||
@data[:authenticated] = true
|
||||
else
|
||||
@data[:awaiting_approval] = true
|
||||
end
|
||||
else
|
||||
@data[:awaiting_activation] = true
|
||||
# send another email ?
|
||||
end
|
||||
else
|
||||
@data[:name] = screen_name
|
||||
end
|
||||
|
||||
process_user_info(user_info, screen_name)
|
||||
end
|
||||
|
||||
def create_or_sign_on_user_using_facebook(auth_token)
|
||||
@ -265,24 +250,7 @@ class Users::OmniauthCallbacksController < ApplicationController
|
||||
auth_provider: "Github"
|
||||
}
|
||||
|
||||
if user_info
|
||||
if user_info.user.active?
|
||||
|
||||
if Guardian.new(user_info.user).can_access_forum?
|
||||
log_on_user(user_info.user)
|
||||
@data[:authenticated] = true
|
||||
else
|
||||
@data[:awaiting_approval] = true
|
||||
end
|
||||
|
||||
else
|
||||
@data[:awaiting_activation] = true
|
||||
# send another email ?
|
||||
end
|
||||
else
|
||||
@data[:name] = screen_name
|
||||
end
|
||||
|
||||
process_user_info(user_info, screen_name)
|
||||
end
|
||||
|
||||
def create_or_sign_on_user_using_persona(auth_token)
|
||||
@ -319,6 +287,26 @@ class Users::OmniauthCallbacksController < ApplicationController
|
||||
|
||||
private
|
||||
|
||||
def process_user_info(user_info, screen_name)
|
||||
if user_info
|
||||
if user_info.user.active?
|
||||
|
||||
if Guardian.new(user_info.user).can_access_forum?
|
||||
log_on_user(user_info.user)
|
||||
@data[:authenticated] = true
|
||||
else
|
||||
@data[:awaiting_approval] = true
|
||||
end
|
||||
|
||||
else
|
||||
@data[:awaiting_activation] = true
|
||||
# send another email ?
|
||||
end
|
||||
else
|
||||
@data[:name] = screen_name
|
||||
end
|
||||
end
|
||||
|
||||
def invite_only?
|
||||
SiteSetting.invite_only? && !@data[:authenticated]
|
||||
end
|
||||
|
@ -22,6 +22,7 @@ class Category < ActiveRecord::Base
|
||||
|
||||
before_validation :ensure_slug
|
||||
after_save :invalidate_site_cache
|
||||
before_save :apply_permissions
|
||||
after_create :create_category_definition
|
||||
after_create :publish_categories_list
|
||||
after_destroy :invalidate_site_cache
|
||||
@ -34,15 +35,52 @@ class Category < ActiveRecord::Base
|
||||
scope :secured, ->(guardian = nil) {
|
||||
ids = guardian.secure_category_ids if guardian
|
||||
if ids.present?
|
||||
where("NOT categories.secure or categories.id in (:cats)", cats: ids)
|
||||
where("NOT categories.read_restricted or categories.id in (:cats)", cats: ids)
|
||||
else
|
||||
where("NOT categories.secure")
|
||||
where("NOT categories.read_restricted")
|
||||
end
|
||||
}
|
||||
|
||||
scope :topic_create_allowed, ->(guardian) {
|
||||
scoped_to_permissions(guardian, [:full])
|
||||
}
|
||||
|
||||
scope :post_create_allowed, ->(guardian) {
|
||||
scoped_to_permissions(guardian, [:create_post, :full])
|
||||
}
|
||||
delegate :post_template, to: 'self.class'
|
||||
|
||||
attr_accessor :displayable_topics
|
||||
# permission is just used by serialization
|
||||
# we may consider wrapping this in another spot
|
||||
attr_accessor :displayable_topics, :permission
|
||||
|
||||
|
||||
def self.scoped_to_permissions(guardian, permission_types)
|
||||
if guardian && guardian.is_staff?
|
||||
scoped
|
||||
else
|
||||
permission_types = permission_types.map{ |permission_type|
|
||||
CategoryGroup.permission_types[permission_type]
|
||||
}
|
||||
where("categories.id in (
|
||||
SELECT c.id FROM categories c
|
||||
WHERE (
|
||||
NOT c.read_restricted AND
|
||||
(
|
||||
NOT EXISTS(
|
||||
SELECT 1 FROM category_groups cg WHERE cg.category_id = categories.id )
|
||||
) OR EXISTS(
|
||||
SELECT 1 FROM category_groups cg
|
||||
WHERE permission_type in (?) AND
|
||||
cg.category_id = categories.id AND
|
||||
group_id IN (
|
||||
SELECT g.group_id FROM group_users g where g.user_id = ? UNION SELECT ?
|
||||
)
|
||||
)
|
||||
)
|
||||
)", permission_types,(!guardian || guardian.user.blank?) ? -1 : guardian.user.id, Group[:everyone].id)
|
||||
end
|
||||
end
|
||||
|
||||
# Internal: Update category stats: # of topics in past year, month, week for
|
||||
# all categories.
|
||||
@ -119,28 +157,69 @@ class Category < ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
|
||||
def deny(group)
|
||||
if group == :all
|
||||
self.secure = true
|
||||
end
|
||||
# will reset permission on a topic to a particular
|
||||
# set.
|
||||
#
|
||||
# Available permissions are, :full, :create_post, :readonly
|
||||
# hash can be:
|
||||
#
|
||||
# :everyone => :full - everyone has everything
|
||||
# :everyone => :readonly, :staff => :full
|
||||
# 7 => 1 # you can pass a group_id and permission id
|
||||
def set_permissions(permissions)
|
||||
self.read_restricted, @permissions = Category.resolve_permissions(permissions)
|
||||
|
||||
# Ideally we can just call .clear here, but it runs SQL, we only want to run it
|
||||
# on save.
|
||||
end
|
||||
|
||||
def allow(group)
|
||||
if group == :all
|
||||
self.secure = false
|
||||
# this is kind of annoying, there should be a clean way of queuing this stuff
|
||||
category_groups.destroy_all unless new_record?
|
||||
else
|
||||
groups.push(group)
|
||||
def permissions=(permissions)
|
||||
set_permissions(permissions)
|
||||
end
|
||||
|
||||
def apply_permissions
|
||||
if @permissions
|
||||
category_groups.destroy_all
|
||||
@permissions.each do |group_id, permission_type|
|
||||
category_groups.build(group_id: group_id, permission_type: permission_type)
|
||||
end
|
||||
@permissions = nil
|
||||
end
|
||||
end
|
||||
|
||||
def secure_group_ids
|
||||
if self.secure
|
||||
if self.read_restricted?
|
||||
groups.pluck("groups.id")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def self.resolve_permissions(permissions)
|
||||
read_restricted = true
|
||||
|
||||
everyone = Group::AUTO_GROUPS[:everyone]
|
||||
full = CategoryGroup.permission_types[:full]
|
||||
|
||||
mapped = permissions.map do |group,permission|
|
||||
group = group.id if Group === group
|
||||
|
||||
# subtle, using Group[] ensures the group exists in the DB
|
||||
group = Group[group.to_sym].id unless Fixnum === group
|
||||
permission = CategoryGroup.permission_types[permission] unless Fixnum === permission
|
||||
|
||||
[group, permission]
|
||||
end
|
||||
|
||||
mapped.each do |group, permission|
|
||||
if group == everyone && permission == full
|
||||
return [false, []]
|
||||
end
|
||||
|
||||
read_restricted = false if group == everyone
|
||||
end
|
||||
|
||||
[read_restricted, mapped]
|
||||
end
|
||||
end
|
||||
|
||||
# == Schema Information
|
||||
@ -162,7 +241,7 @@ end
|
||||
# description :text
|
||||
# text_color :string(6) default("FFFFFF"), not null
|
||||
# hotness :float default(5.0), not null
|
||||
# secure :boolean default(FALSE), not null
|
||||
# read_restricted :boolean default(FALSE), not null
|
||||
# auto_close_days :float
|
||||
#
|
||||
# Indexes
|
||||
|
@ -1,16 +1,22 @@
|
||||
class CategoryGroup < ActiveRecord::Base
|
||||
belongs_to :category
|
||||
belongs_to :group
|
||||
|
||||
def self.permission_types
|
||||
@permission_types ||= Enum.new(:full, :create_post, :readonly)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: category_groups
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# category_id :integer not null
|
||||
# group_id :integer not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# id :integer not null, primary key
|
||||
# category_id :integer not null
|
||||
# group_id :integer not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# permission_type :integer default(1)
|
||||
#
|
||||
|
||||
|
@ -10,6 +10,7 @@ class Group < ActiveRecord::Base
|
||||
validate :name_format_validator
|
||||
|
||||
AUTO_GROUPS = {
|
||||
:everyone => 0,
|
||||
:admins => 1,
|
||||
:moderators => 2,
|
||||
:staff => 3,
|
||||
@ -34,6 +35,10 @@ class Group < ActiveRecord::Base
|
||||
group.save!
|
||||
end
|
||||
|
||||
# the everyone group is special, it can include non-users so there is no
|
||||
# way to have the membership in a table
|
||||
return group if name == :everyone
|
||||
|
||||
group.name = I18n.t("groups.default_names.#{name}")
|
||||
|
||||
# don't allow shoddy localization to break this
|
||||
|
@ -59,6 +59,7 @@ end
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# deleted_at :datetime
|
||||
# deleted_by_id :integer
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
|
@ -412,6 +412,8 @@ end
|
||||
# percent_rank :float default(1.0)
|
||||
# notify_user_count :integer default(0), not null
|
||||
# like_score :integer default(0), not null
|
||||
# deleted_by_id :integer
|
||||
# nuked_user :boolean default(FALSE)
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
|
@ -63,9 +63,14 @@ class PostAnalyzer
|
||||
|
||||
@linked_hosts = {}
|
||||
raw_links.each do |u|
|
||||
uri = URI.parse(u)
|
||||
host = uri.host
|
||||
@linked_hosts[host] ||= 1
|
||||
begin
|
||||
uri = URI.parse(u)
|
||||
host = uri.host
|
||||
@linked_hosts[host] ||= 1
|
||||
rescue URI::InvalidURIError
|
||||
# An invalid URI does not count as a raw link.
|
||||
next
|
||||
end
|
||||
end
|
||||
@linked_hosts
|
||||
end
|
||||
|
@ -25,11 +25,24 @@ class Site
|
||||
TrustLevel.all
|
||||
end
|
||||
|
||||
def group_names
|
||||
@group_name ||= Group.pluck(:name)
|
||||
end
|
||||
|
||||
def categories
|
||||
Category
|
||||
.secured(@guardian)
|
||||
.latest
|
||||
.includes(:topic_only_relative_url)
|
||||
@categories ||= begin
|
||||
categories = Category
|
||||
.secured(@guardian)
|
||||
.latest
|
||||
.includes(:topic_only_relative_url).to_a
|
||||
|
||||
allowed_topic_create = Set.new(Category.topic_create_allowed(@guardian).pluck(:id))
|
||||
|
||||
categories.each do |category|
|
||||
category.permission = CategoryGroup.permission_types[:full] if allowed_topic_create.include?(category.id)
|
||||
end
|
||||
categories
|
||||
end
|
||||
end
|
||||
|
||||
def archetypes
|
||||
|
@ -103,9 +103,9 @@ class Topic < ActiveRecord::Base
|
||||
# Query conditions
|
||||
condition =
|
||||
if ids.present?
|
||||
["NOT c.secure or c.id in (:cats)", cats: ids]
|
||||
["NOT c.read_restricted or c.id in (:cats)", cats: ids]
|
||||
else
|
||||
["NOT c.secure"]
|
||||
["NOT c.read_restricted"]
|
||||
end
|
||||
|
||||
where("category_id IS NULL OR category_id IN (
|
||||
@ -629,8 +629,8 @@ class Topic < ActiveRecord::Base
|
||||
self
|
||||
end
|
||||
|
||||
def secure_category?
|
||||
category && category.secure
|
||||
def read_restricted_category?
|
||||
category && category.read_restricted
|
||||
end
|
||||
|
||||
private
|
||||
@ -692,6 +692,7 @@ end
|
||||
# auto_close_at :datetime
|
||||
# auto_close_user_id :integer
|
||||
# auto_close_started_at :datetime
|
||||
# deleted_by_id :integer
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
|
@ -113,11 +113,11 @@ class TopicTrackingState
|
||||
((#{unread}) OR (#{new})) AND
|
||||
(topics.visible OR u.admin OR u.moderator) AND
|
||||
topics.deleted_at IS NULL AND
|
||||
( category_id IS NULL OR NOT c.secure OR category_id IN (
|
||||
( category_id IS NULL OR NOT c.read_restricted OR category_id IN (
|
||||
SELECT c2.id FROM categories c2
|
||||
JOIN category_groups cg ON cg.category_id = c2.id
|
||||
JOIN group_users gu ON gu.user_id = u.id AND cg.group_id = gu.group_id
|
||||
WHERE c2.secure )
|
||||
WHERE c2.read_restricted )
|
||||
)
|
||||
|
||||
SQL
|
||||
|
@ -486,10 +486,14 @@ class User < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def secure_category_ids
|
||||
cats = self.staff? ? Category.select(:id).where(secure: true) : secure_categories.select('categories.id')
|
||||
cats = self.staff? ? Category.select(:id).where(read_restricted: true) : secure_categories.select('categories.id')
|
||||
cats.map { |c| c.id }.sort
|
||||
end
|
||||
|
||||
def topic_create_allowed_category_ids
|
||||
Category.topic_create_allowed(self.id).select(:id)
|
||||
end
|
||||
|
||||
# Flag all posts from a user as spam
|
||||
def flag_linked_posts_as_spam
|
||||
admin = Discourse.system_user
|
||||
@ -660,6 +664,7 @@ end
|
||||
# topic_reply_count :integer default(0), not null
|
||||
# blocked :boolean default(FALSE)
|
||||
# dynamic_favicon :boolean default(FALSE), not null
|
||||
# title :string(255)
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
|
@ -179,7 +179,7 @@ ORDER BY p.created_at desc
|
||||
|
||||
# move into Topic perhaps
|
||||
group_ids = nil
|
||||
if topic && topic.category && topic.category.secure
|
||||
if topic && topic.category && topic.category.read_restricted
|
||||
group_ids = topic.category.groups.pluck("groups.id")
|
||||
end
|
||||
|
||||
@ -232,11 +232,11 @@ ORDER BY p.created_at desc
|
||||
unless guardian.is_staff?
|
||||
allowed = guardian.secure_category_ids
|
||||
if allowed.present?
|
||||
builder.where("( c.secure IS NULL OR
|
||||
c.secure = 'f' OR
|
||||
(c.secure = 't' and c.id in (:cats)) )", cats: guardian.secure_category_ids )
|
||||
builder.where("( c.read_restricted IS NULL OR
|
||||
NOT c.read_restricted OR
|
||||
(c.read_restricted and c.id in (:cats)) )", cats: guardian.secure_category_ids )
|
||||
else
|
||||
builder.where("(c.secure IS NULL OR c.secure = 'f')")
|
||||
builder.where("(c.read_restricted IS NULL OR NOT c.read_restricted)")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -9,6 +9,7 @@ class BasicCategorySerializer < ApplicationSerializer
|
||||
:description,
|
||||
:topic_url,
|
||||
:hotness,
|
||||
:secure
|
||||
:read_restricted,
|
||||
:permission
|
||||
|
||||
end
|
||||
|
@ -1,13 +1,24 @@
|
||||
class CategorySerializer < BasicCategorySerializer
|
||||
|
||||
attributes :secure, :groups, :available_groups, :auto_close_days
|
||||
attributes :read_restricted, :available_groups, :auto_close_days, :group_permissions
|
||||
|
||||
def groups
|
||||
@groups ||= object.groups.order("name").all.map(&:name)
|
||||
def group_permissions
|
||||
@group_permissions ||= begin
|
||||
perms = object.category_groups.joins(:group).includes(:group).order("groups.name").map do |cg|
|
||||
{
|
||||
permission_type: cg.permission_type,
|
||||
group_name: cg.group.name
|
||||
}
|
||||
end
|
||||
if perms.length == 0 && !object.read_restricted
|
||||
perms << {permission_type: CategoryGroup.permission_types[:full], group_name: :everyone}
|
||||
end
|
||||
perms
|
||||
end
|
||||
end
|
||||
|
||||
def available_groups
|
||||
Group.order("name").map(&:name) - groups
|
||||
Group.order(:name).pluck(:name) - group_permissions.map{|g| g[:group_name]}
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -3,7 +3,8 @@ class SiteSerializer < ApplicationSerializer
|
||||
attributes :default_archetype,
|
||||
:notification_types,
|
||||
:post_types,
|
||||
:uncategorized_slug
|
||||
:uncategorized_slug,
|
||||
:group_names
|
||||
|
||||
|
||||
has_many :categories, serializer: BasicCategorySerializer, embed: :objects
|
||||
|
@ -112,7 +112,7 @@ module Discourse
|
||||
# ember stuff only used for asset precompliation, production variant plays up
|
||||
config.ember.variant = :development
|
||||
config.ember.ember_location = "#{Rails.root}/app/assets/javascripts/external_production/ember.js"
|
||||
config.ember.handlebars_location = "#{Rails.root}/app/assets/javascripts/external/handlebars-1.0.rc.4.js"
|
||||
config.ember.handlebars_location = "#{Rails.root}/app/assets/javascripts/external/handlebars.js"
|
||||
|
||||
# Since we are using strong_parameters, we can disable and remove
|
||||
# attr_accessible.
|
||||
|
@ -88,9 +88,8 @@ predef:
|
||||
- visit
|
||||
- count
|
||||
- exists
|
||||
- asyncTest
|
||||
- asyncTestDiscourse
|
||||
- find
|
||||
- resolvingPromise
|
||||
- sinon
|
||||
- controllerFor
|
||||
|
||||
|
@ -12,7 +12,10 @@ cs:
|
||||
storage_units:
|
||||
format: ! '%n %u'
|
||||
units:
|
||||
byte: B
|
||||
byte:
|
||||
one: bajt
|
||||
few: bajty
|
||||
other: bajtů
|
||||
gb: GB
|
||||
kb: KB
|
||||
mb: MB
|
||||
@ -666,6 +669,7 @@ cs:
|
||||
description: "nebudete vůbec dostávat oznámení o tomto tématu a nebude se zobrazovat v seznamu nepřečtených témat."
|
||||
|
||||
actions:
|
||||
recover: "Vrátit téma"
|
||||
delete: "Odstranit téma"
|
||||
open: "Otevřít téma"
|
||||
close: "Zavřít téma"
|
||||
@ -775,6 +779,7 @@ cs:
|
||||
continue_discussion: "Pokračující diskuze z {{postLink}}:"
|
||||
follow_quote: "přejít na citovaný příspěvek"
|
||||
deleted_by_author: "(příspěvek odstraněn autorem)"
|
||||
deleted_by: "odstranil"
|
||||
expand_collapse: "rozbalit/sbalit"
|
||||
|
||||
has_replies:
|
||||
@ -789,6 +794,7 @@ cs:
|
||||
upload_too_large: "Soubor, který se snažíte nahrát je bohužel příliš velký (maximální velikost je {{max_size_kb}}kb). Prosím zmenšete ho zkuste to znovu."
|
||||
too_many_uploads: "Bohužel, najednou smíte nahrát jen jeden soubor."
|
||||
upload_not_authorized: "Bohužel, soubor, který se snažíte nahrát, není povolený (povolené přípony: {{authorized_extensions}})."
|
||||
upload_not_allowed_for_new_user: "Bohužel, noví uživatelé nemohou nahrávat obrázky."
|
||||
|
||||
abandon: "Opravdu chcete opustit váš příspěvek?"
|
||||
|
||||
@ -998,6 +1004,7 @@ cs:
|
||||
views_long: "toto téma bylo zobrazeno {{number}}krát"
|
||||
activity: "Aktivita"
|
||||
likes: "Líbí se"
|
||||
likes_long: "je zde {{number}} 'líbí se' v tomto tématu"
|
||||
top_contributors: "Účastníci"
|
||||
category_title: "Kategorie"
|
||||
history: "Historie"
|
||||
@ -1064,6 +1071,8 @@ cs:
|
||||
critical_available: "Je k dispozici důležitá aktualizace."
|
||||
updates_available: "Jsou k dispozici aktualizace."
|
||||
please_upgrade: "Prosím aktualizujte!"
|
||||
no_check_performed: "Kontrola na aktualizace nebyla provedena. Ujistěte se, že běží služby clockword a sidekiq."
|
||||
stale_data: "V poslední době neproběhal kontrola aktualizací. Ujistěte se, že běží služby clockword a sidekiq."
|
||||
installed_version: "Nainstalováno"
|
||||
latest_version: "Poslední verze"
|
||||
problems_found: "Byly nalezeny problémy s vaší instalací systému Discourse:"
|
||||
@ -1073,6 +1082,7 @@ cs:
|
||||
moderators: 'Moderátoři:'
|
||||
admins: 'Administrátoři:'
|
||||
blocked: 'Blokováno:'
|
||||
banned: 'Zakázáno:'
|
||||
private_messages_short: "SZ"
|
||||
private_messages_title: "Soukromé zprávy"
|
||||
|
||||
@ -1176,6 +1186,7 @@ cs:
|
||||
settings: "Nastavení"
|
||||
logs: "Záznamy"
|
||||
sent_at: "Odesláno"
|
||||
user: "Uživatel"
|
||||
email_type: "Typ emailu"
|
||||
to_address: "Komu"
|
||||
test_email_address: "testovací emailová adresa"
|
||||
@ -1205,9 +1216,13 @@ cs:
|
||||
not_found: "Bohužel uživatel s tímto jménem není v našem systému."
|
||||
active: "Aktivní"
|
||||
nav:
|
||||
new: "Noví"
|
||||
active: "Aktivní"
|
||||
new: "Nový"
|
||||
pending: "Čeká na schválení"
|
||||
admins: "Administrátoři"
|
||||
moderators: "Moderátoři"
|
||||
banned: "Zakázaní"
|
||||
blocked: "Blokovaní"
|
||||
approved: "Schválen?"
|
||||
approved_selected:
|
||||
one: "schválit uživatele"
|
||||
@ -1225,6 +1240,7 @@ cs:
|
||||
admins: 'Admininstrátoři'
|
||||
moderators: 'Moderátoři'
|
||||
blocked: 'Blokovaní uživatelé'
|
||||
banned: "Zakázaní uživatelé"
|
||||
|
||||
user:
|
||||
ban_failed: "Nastala chyba při zakazování uživatele {{error}}"
|
||||
@ -1281,7 +1297,7 @@ cs:
|
||||
deactivate_explanation: "Deaktivovaný uživatel musí znovu validovat svoji emailovou adresu než se bude moci znovu přihlásit."
|
||||
banned_explanation: "Zakázaný uživatel se nemůže přihlásit."
|
||||
block_explanation: "Zablokovaný uživatel nemůže přispívat nebo vytvářet nová témata."
|
||||
|
||||
trust_level_change_failed: "Nastal problém při změně důveryhodnosti uživatele."
|
||||
|
||||
site_content:
|
||||
none: "Zvolte typ obsahu a můžete začít editovat."
|
||||
|
@ -168,6 +168,7 @@ en:
|
||||
"13": "Inbox"
|
||||
|
||||
user:
|
||||
said: "{{username}} said:"
|
||||
profile: Profile
|
||||
mute: Mute
|
||||
edit: Edit Preferences
|
||||
@ -999,6 +1000,11 @@ en:
|
||||
|
||||
browser_update: 'Unfortunately, <a href="http://www.discourse.org/faq/#browser">your browser is too old to work on this Discourse forum</a>. Please <a href="http://browsehappy.com">upgrade your browser</a>.'
|
||||
|
||||
permission_types:
|
||||
full: "Create Topics, Create Posts and Read"
|
||||
create_post: "Create Posts and Read"
|
||||
readonly: "Read Only"
|
||||
|
||||
# This section is exported to the javascript for i18n in the admin section
|
||||
admin_js:
|
||||
type_to_filter: "type to filter..."
|
||||
@ -1245,3 +1251,4 @@ en:
|
||||
title: 'Settings'
|
||||
reset: 'reset to default'
|
||||
none: 'none'
|
||||
|
||||
|
@ -60,6 +60,9 @@ cs:
|
||||
rss_topics_in_category: "RSS feed témat z kategorie '%{category}'"
|
||||
author_wrote: "%{author} napsal:"
|
||||
private_message_abbrev: "SZ"
|
||||
rss_description:
|
||||
latest: "Poslední témata"
|
||||
hot: "Populární témata"
|
||||
|
||||
groups:
|
||||
errors:
|
||||
@ -139,6 +142,8 @@ cs:
|
||||
title: "vůdce"
|
||||
elder:
|
||||
title: "starší"
|
||||
change_failed_explanation: "Pokusili jste se změnil důvěryhodnost uživatele %{user_name} na '%{new_trust_level}'. Jejich důvěryhodnost je ale již '%{current_trust_level}'. %{user_name} zůstané na důvěryhodnosti '%{current_trust_level}'"
|
||||
|
||||
|
||||
rate_limiter:
|
||||
too_many_requests: "Děláte tuto akci příliš často. Prosím počkejte %{time_left} a zkuste to znovu."
|
||||
@ -501,6 +506,7 @@ cs:
|
||||
apple_touch_icon_url: "Ikona používaná pro doteková zařízení od firmy Apple. Doporučené rozměry jsou 144px krát 144px."
|
||||
|
||||
notification_email: "Návratová emailová adresa, která se použije u systémových emailů, jako jsou notifikace o zapomenutém heslu, nových účtech, atd."
|
||||
email_custom_headers: "Seznam vlastních hlaviček emailů, oddělený svislítkem"
|
||||
use_ssl: "Má být web přístupný přes SSL? (NEPODPOROVÁNO, EXPERIMENTÁLNÍ FUNKCE)"
|
||||
best_of_score_threshold: "Minimální skóre příspěvku, aby byl zařazen mezi 'nejlepší'"
|
||||
best_of_posts_required: "Minimální počet příspěvků v tématu, aby byl povolen mód 'nejlepší příspěvky'"
|
||||
@ -527,6 +533,8 @@ cs:
|
||||
must_approve_users: "Administrátoři musí schválit všechny uživatele, než získají přístup"
|
||||
ga_tracking_code: "Kód pro sledování přes 'Google analytics', např. UA-12345678-9; viz http://google.com/analytics"
|
||||
ga_domain_name: "Doménové jméno pro Google analytics, např. mysite.com; viz http://google.com/analytics"
|
||||
enable_escaped_fragments: "Povolit alternativní řešení, které pomůže starým webovým robotům indexovat váš web. VAROVÁNÍ: povolte pouze v případě, že to opravdu potřebujete."
|
||||
enable_noscript_support: "Povolit podporu <noscipt> tagu"
|
||||
top_menu: "Určuje, které položky se zobrazí v navigaci na hlavní stránce a v jakém pořadí. Příklad: latest|hot|read|favorited|unread|new|posted|categories"
|
||||
post_menu: "Určuje, které položky se zobrazí v menu u příspěvku a v jakém pořadí. Příklad: like|edit|flag|delete|share|bookmark|reply"
|
||||
share_links: "Určuje, které položky se zobrazí ve sdílecím dialogu a v jakém pořadí. Příklad: twitter|facebook|google+"
|
||||
@ -536,6 +544,8 @@ cs:
|
||||
system_username: "Uživatelské jméno, za které se zasílají automatické soukromé zprávy"
|
||||
send_welcome_message: "Mají noví uživatelé obdržet uvítací soukromou zprávu?"
|
||||
suppress_reply_directly_below: "Nezobrazovat počet odpovědí, pokud existuje jen jediná odpověď hned pod příspěvkem"
|
||||
suppress_reply_directly_above: "Nezobrazovat informaci 'je odpověď na' u příspěvku, jehož odpověď je přímo nad ním"
|
||||
|
||||
allow_index_in_robots_txt: "Povolit robotům indexaci tohoto webu (aktualizuje robots.txt)"
|
||||
email_domains_blacklist: "Seznam domén, jejichž emailové adresy nebudou přijímány, oddělený znakem '|'. Příklad: mailinator.com|trashmail.net"
|
||||
email_domains_whitelist: "Seznam domén, ze kterých bude povolena registrace, oddělený znamek '|'. POZOR: Emailové adresy z jiných než z těchto domén nebudou přijímány."
|
||||
@ -603,6 +613,8 @@ cs:
|
||||
s3_secret_access_key: "Hodnota 'server access key' služby Amazon S3, která se použije pro nahrávání obrázku"
|
||||
s3_region: "Hodnota 'region' služby Amazon S3, která se použije pro nahrávání obrázku"
|
||||
|
||||
enable_flash_video_onebox: "Povolit embedování SWF a FLV odkazů do oneboxu (může znamenat bezpečnostní riziko, doporučujeme opatrnost)"
|
||||
|
||||
default_invitee_trust_level: "Výchozí věrohodnost pozvaných uživatelů (0-4)"
|
||||
default_trust_level: "Výchozí věrohodnost uživatelů (0-4)"
|
||||
|
||||
@ -663,7 +675,9 @@ cs:
|
||||
pop3s_polling_username: "Uživatelské jméno pro dotazování přes POP3S"
|
||||
pop3s_polling_password: "Heslo pro dotazování přes POP3S"
|
||||
|
||||
minimum_topics_similar: "How many topics need to exist in the database before similar topics are presented."
|
||||
minimum_topics_similar: "Kolik témat musí v databázi existovat, než se začne nabízet menu s podobnými tématy."
|
||||
|
||||
relative_date_duration: "Počet dní od zaslání příspěvku, po které se budou datumy zobrazovat relativně namísto absolutně. Příklady: relativně: 7d, absolutně: 20. února"
|
||||
|
||||
|
||||
notification_types:
|
||||
|
@ -436,6 +436,9 @@ en:
|
||||
tos_miscellaneous:
|
||||
title: "Terms of Service: Miscellaneous"
|
||||
description: "The text for the Miscellaneous section of the Terms of Service."
|
||||
login_required:
|
||||
title: "Login Required: Homepage"
|
||||
description: "The text displayed for unauthorized users when login is required on the site."
|
||||
|
||||
site_settings:
|
||||
default_locale: "The default language of this Discourse instance (ISO 639-1 Code)"
|
||||
@ -458,7 +461,7 @@ en:
|
||||
company_short_name: "The short name of the company that runs this site, used in legal documents like the /tos"
|
||||
company_domain: "The domain name owned by the company that runs this site, used in legal documents like the /tos"
|
||||
api_key: "The secure API key used to create and update topics, use the /admin/api section to set it up"
|
||||
queue_jobs: "DEVELOPER ONLY! WARNING! Queue various jobs in sidekiq, if false queues are inline"
|
||||
queue_jobs: "DEVELOPER ONLY! WARNING! By default, queue jobs in sidekiq. If disabled, your site will be broken."
|
||||
crawl_images: "Enable retrieving images from third party sources to insert width and height dimensions"
|
||||
ninja_edit_window: "Number of seconds after posting where edits do not create a new version"
|
||||
max_image_width: "Maximum allowed width of images in a post"
|
||||
|
@ -0,0 +1,9 @@
|
||||
class AddPermissionTypeToCategoryGroups < ActiveRecord::Migration
|
||||
def change
|
||||
# 1 is full permissions
|
||||
add_column :category_groups, :permission_type, :integer, default: 1
|
||||
|
||||
# secure is ambiguous after this change, it should be read_restricted
|
||||
rename_column :categories, :secure, :read_restricted
|
||||
end
|
||||
end
|
@ -1,6 +1,6 @@
|
||||
<table cellspacing="0" cellpadding="0" style="border: 1px solid #eee; -webkit-border-radius: 10px;">
|
||||
<tr>
|
||||
<th style="text-align:left; background-color: #eee; padding: 5px">{{{avatarImg}}} {{username}} said:</th>
|
||||
<th style="text-align:left; background-color: #eee; padding: 5px">{{{avatarImg}}} {{username}}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 10px; background-color: #f9f9f9">{{{quote}}}</td>
|
||||
|
@ -7,6 +7,7 @@ class Guardian
|
||||
def staff?; false; end
|
||||
def approved?; false; end
|
||||
def secure_category_ids; []; end
|
||||
def topic_create_allowed_category_ids; []; end
|
||||
def has_trust_level?(level); false; end
|
||||
end
|
||||
|
||||
@ -237,8 +238,19 @@ class Guardian
|
||||
can_create_post?(parent)
|
||||
end
|
||||
|
||||
def can_create_topic_on_category?(category)
|
||||
can_create_post?(nil) && (
|
||||
!category ||
|
||||
Category.topic_create_allowed(self).where(:id => category.id).count == 1
|
||||
)
|
||||
end
|
||||
|
||||
def can_create_post?(parent)
|
||||
!SpamRulesEnforcer.block?(@user)
|
||||
!SpamRulesEnforcer.block?(@user) && (
|
||||
!parent ||
|
||||
!parent.category ||
|
||||
Category.post_create_allowed(self).where(:id => parent.category.id).count == 1
|
||||
)
|
||||
end
|
||||
|
||||
def can_create_post_on_topic?(topic)
|
||||
@ -328,7 +340,7 @@ class Guardian
|
||||
topic.deleted_at.nil? &&
|
||||
|
||||
# not secure, or I can see it
|
||||
(not(topic.secure_category?) || can_see_category?(topic.category)) &&
|
||||
(not(topic.read_restricted_category?) || can_see_category?(topic.category)) &&
|
||||
|
||||
# not private, or I am allowed (or an admin)
|
||||
(not(topic.private_message?) || authenticated? && (topic.all_allowed_users.where(id: @user.id).exists? || is_admin?))
|
||||
@ -340,7 +352,7 @@ class Guardian
|
||||
end
|
||||
|
||||
def can_see_category?(category)
|
||||
not(category.secure) || secure_category_ids.include?(category.id)
|
||||
not(category.read_restricted) || secure_category_ids.include?(category.id)
|
||||
end
|
||||
|
||||
def can_vote?(post, opts={})
|
||||
@ -378,6 +390,10 @@ class Guardian
|
||||
@secure_category_ids ||= @user.secure_category_ids
|
||||
end
|
||||
|
||||
def topic_create_allowed_category_ids
|
||||
@topic_create_allowed_category_ids ||= @user.topic_create_allowed_category_ids
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def is_my_own?(obj)
|
||||
|
@ -19,9 +19,9 @@ module Oneboxer
|
||||
case route[:controller]
|
||||
when 'users'
|
||||
user = User.where(username_lower: route[:username].downcase).first
|
||||
return nil unless user
|
||||
return nil unless user
|
||||
|
||||
Guardian.new.ensure_can_see!(user)
|
||||
return @url unless Guardian.new.can_see?(user)
|
||||
|
||||
args.merge! avatar: PrettyText.avatar_img(user.username, 'tiny'), username: user.username
|
||||
args[:bio] = user.bio_cooked if user.bio_cooked.present?
|
||||
@ -33,7 +33,7 @@ module Oneboxer
|
||||
post = Post.where(topic_id: route[:topic_id], post_number: route[:post_number].to_i).first
|
||||
return nil unless post
|
||||
|
||||
Guardian.new.ensure_can_see!(post)
|
||||
return @url unless Guardian.new.can_see?(post)
|
||||
|
||||
topic = post.topic
|
||||
slug = Slug.for(topic.title)
|
||||
@ -52,7 +52,8 @@ module Oneboxer
|
||||
topic = Topic.where(id: route[:topic_id].to_i).includes(:user).first
|
||||
return nil unless topic
|
||||
|
||||
Guardian.new.ensure_can_see!(topic)
|
||||
return @url unless Guardian.new.can_see?(topic)
|
||||
|
||||
post = topic.posts.first
|
||||
|
||||
posters = topic.posters_summary.map do |p|
|
||||
|
@ -116,7 +116,7 @@ class PostCreator
|
||||
protected
|
||||
|
||||
def secure_group_ids(topic)
|
||||
@secure_group_ids ||= if topic.category && topic.category.secure?
|
||||
@secure_group_ids ||= if topic.category && topic.category.read_restricted?
|
||||
topic.category.secure_group_ids
|
||||
end
|
||||
end
|
||||
|
@ -46,6 +46,19 @@ module PrettyText
|
||||
|
||||
|
||||
class Helpers
|
||||
|
||||
def t(key, opts)
|
||||
str = I18n.t("js." + key)
|
||||
if opts
|
||||
# TODO: server localisation has no parity with client
|
||||
# should be fixed
|
||||
opts.each do |k,v|
|
||||
str.gsub!("{{#{k}}}", v)
|
||||
end
|
||||
end
|
||||
str
|
||||
end
|
||||
|
||||
# function here are available to v8
|
||||
def avatar_template(username)
|
||||
return "" unless username
|
||||
@ -90,6 +103,7 @@ module PrettyText
|
||||
|
||||
@ctx.eval("var Discourse = {}; Discourse.SiteSettings = #{SiteSetting.client_settings_json};")
|
||||
@ctx.eval("var window = {}; window.devicePixelRatio = 2;") # hack to make code think stuff is retina
|
||||
@ctx.eval("var I18n = {}; I18n.t = function(a,b){ return helpers.t(a,b); }");
|
||||
|
||||
ctx_load( "app/assets/javascripts/discourse/components/bbcode.js",
|
||||
"app/assets/javascripts/discourse/components/utilities.js",
|
||||
|
@ -160,9 +160,9 @@ class Search
|
||||
.order("topics.bumped_at DESC")
|
||||
|
||||
if secure_category_ids.present?
|
||||
posts = posts.where("(categories.id IS NULL) OR (NOT categories.secure) OR (categories.id IN (?))", secure_category_ids)
|
||||
posts = posts.where("(categories.id IS NULL) OR (NOT categories.read_restricted) OR (categories.id IN (?))", secure_category_ids)
|
||||
else
|
||||
posts = posts.where("(categories.id IS NULL) OR (NOT categories.secure)")
|
||||
posts = posts.where("(categories.id IS NULL) OR (NOT categories.read_restricted)")
|
||||
end
|
||||
posts.limit(limit)
|
||||
end
|
||||
|
@ -18,9 +18,9 @@ class SqlBuilder
|
||||
|
||||
def secure_category(secure_category_ids, category_alias = 'c')
|
||||
if secure_category_ids.present?
|
||||
where("NOT COALESCE(" << category_alias << ".secure, false) OR " << category_alias << ".id IN (:secure_category_ids)", secure_category_ids: secure_category_ids)
|
||||
where("NOT COALESCE(" << category_alias << ".read_restricted, false) OR " << category_alias << ".id IN (:secure_category_ids)", secure_category_ids: secure_category_ids)
|
||||
else
|
||||
where("NOT COALESCE(" << category_alias << ".secure, false)")
|
||||
where("NOT COALESCE(" << category_alias << ".read_restricted, false)")
|
||||
end
|
||||
self
|
||||
end
|
||||
|
@ -27,9 +27,9 @@ class TopicCreator
|
||||
topic_params[:archetype] = @opts[:archetype] if @opts[:archetype].present?
|
||||
topic_params[:subtype] = @opts[:subtype] if @opts[:subtype].present?
|
||||
|
||||
@guardian.ensure_can_create!(Topic)
|
||||
|
||||
category = Category.where(name: @opts[:category]).first
|
||||
|
||||
@guardian.ensure_can_create!(Topic,category)
|
||||
topic_params[:category_id] = category.id if category.present?
|
||||
topic_params[:meta_data] = @opts[:meta_data] if @opts[:meta_data].present?
|
||||
topic_params[:created_at] = Time.zone.parse(@opts[:created_at].to_s) if @opts[:created_at].present?
|
||||
|
@ -239,9 +239,9 @@ class TopicQuery
|
||||
unless @user && @user.moderator?
|
||||
category_ids = @user.secure_category_ids if @user
|
||||
if category_ids.present?
|
||||
result = result.where('categories.secure IS NULL OR categories.secure = ? OR categories.id IN (?)', false, category_ids)
|
||||
result = result.where('categories.read_restricted IS NULL OR categories.read_restricted = ? OR categories.id IN (?)', false, category_ids)
|
||||
else
|
||||
result = result.where('categories.secure IS NULL OR categories.secure = ?', false)
|
||||
result = result.where('categories.read_restricted IS NULL OR categories.read_restricted = ?', false)
|
||||
end
|
||||
end
|
||||
|
||||
|
129
script/require_profiler.rb
Normal file
129
script/require_profiler.rb
Normal file
@ -0,0 +1,129 @@
|
||||
# Some based on : https://gist.github.com/277289
|
||||
#
|
||||
# This is a rudimentary script that allows us to
|
||||
# quickly determine if any gems are slowing down startup
|
||||
|
||||
require 'benchmark'
|
||||
require 'fileutils'
|
||||
|
||||
module RequireProfiler
|
||||
class << self
|
||||
|
||||
attr_accessor :stats
|
||||
|
||||
def profiling_enabled?
|
||||
@profiling_enabled
|
||||
end
|
||||
|
||||
def profile
|
||||
start
|
||||
yield
|
||||
stop
|
||||
end
|
||||
|
||||
def start(tmp_options={})
|
||||
@start_time = Time.now
|
||||
[ ::Kernel, (class << ::Kernel; self; end) ].each do |klass|
|
||||
klass.class_eval do
|
||||
def require_with_profiling(path, *args)
|
||||
RequireProfiler.measure(path, caller, :require) { require_without_profiling(path, *args) }
|
||||
end
|
||||
alias require_without_profiling require
|
||||
alias require require_with_profiling
|
||||
|
||||
def load_with_profiling(path, *args)
|
||||
RequireProfiler.measure(path, caller, :load) { load_without_profiling(path, *args) }
|
||||
end
|
||||
alias load_without_profiling load
|
||||
alias load load_with_profiling
|
||||
end
|
||||
end
|
||||
# This is necessary so we don't clobber Bundler.require on Rails 3
|
||||
Kernel.class_eval { private :require, :load }
|
||||
@profiling_enabled = true
|
||||
end
|
||||
|
||||
def stop
|
||||
@stop_time = Time.now
|
||||
[ ::Kernel, (class << ::Kernel; self; end) ].each do |klass|
|
||||
klass.class_eval do
|
||||
alias require require_without_profiling
|
||||
alias load load_without_profiling
|
||||
end
|
||||
end
|
||||
@profiling_enabled = false
|
||||
end
|
||||
|
||||
def measure(path, full_backtrace, mechanism, &block)
|
||||
# Path may be a Pathname, convert to a String
|
||||
path = path.to_s
|
||||
|
||||
@stack ||= []
|
||||
self.stats ||= {}
|
||||
|
||||
stat = self.stats.fetch(path){|key| self.stats[key] = {calls: 0, time: 0, parent_time: 0} }
|
||||
|
||||
@stack << stat
|
||||
|
||||
time = Time.now
|
||||
begin
|
||||
output = yield # do the require or load here
|
||||
ensure
|
||||
delta = Time.now - time
|
||||
stat[:time] += delta
|
||||
stat[:calls] += 1
|
||||
@stack.pop
|
||||
@stack.each do |frame|
|
||||
frame[:parent_time] += delta
|
||||
end
|
||||
end
|
||||
|
||||
output
|
||||
end
|
||||
|
||||
def time_block
|
||||
start = Time.now
|
||||
yield
|
||||
Time.now - start
|
||||
end
|
||||
|
||||
def gc_analyze
|
||||
ObjectSpace.garbage_collect
|
||||
gc_duration_start = time_block { ObjectSpace.garbage_collect }
|
||||
old_objs = ObjectSpace.count_objects
|
||||
yield
|
||||
ObjectSpace.garbage_collect
|
||||
gc_duration_finish = time_block { ObjectSpace.garbage_collect }
|
||||
new_objs = ObjectSpace.count_objects
|
||||
|
||||
puts "New objects: #{(new_objs[:TOTAL] - new_objs[:FREE]) - (old_objs[:TOTAL] - old_objs[:FREE])}"
|
||||
puts "GC duration: #{gc_duration_finish}"
|
||||
puts "GC impact: #{gc_duration_finish - gc_duration_start}"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
# RequireProfiler.gc_analyze do
|
||||
# # require 'mime-types'
|
||||
# require 'highline'
|
||||
# end
|
||||
# exit
|
||||
|
||||
RequireProfiler.profile do
|
||||
Bundler.definition.dependencies.each do |dep|
|
||||
begin
|
||||
require dep.name unless dep.name =~ /timecop/
|
||||
rescue Exception
|
||||
# don't care
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sorted = RequireProfiler.stats.to_a.sort{|a,b| b[1][:time] - b[1][:parent_time] <=> a[1][:time] - a[1][:parent_time]}
|
||||
|
||||
sorted[0..120].each do |k, v|
|
||||
puts "#{k} : time #{v[:time] - v[:parent_time]} "
|
||||
end
|
@ -41,8 +41,7 @@ describe CategoryList do
|
||||
|
||||
cat = Fabricate(:category)
|
||||
topic = Fabricate(:topic, category: cat)
|
||||
cat.deny(:all)
|
||||
cat.allow(Group[:admins])
|
||||
cat.set_permissions(:admins => :full)
|
||||
cat.save
|
||||
|
||||
CategoryList.new(Guardian.new admin).categories.count.should == 1
|
||||
|
@ -215,8 +215,9 @@ describe Guardian do
|
||||
|
||||
it 'correctly handles groups' do
|
||||
group = Fabricate(:group)
|
||||
category = Fabricate(:category, secure: true)
|
||||
category.allow(group)
|
||||
category = Fabricate(:category, read_restricted: true)
|
||||
category.set_permissions(group => :full)
|
||||
category.save
|
||||
|
||||
topic = Fabricate(:topic, category: category)
|
||||
|
||||
@ -275,8 +276,27 @@ describe Guardian do
|
||||
end
|
||||
end
|
||||
|
||||
describe 'a Topic' do
|
||||
it 'should check for full permissions' do
|
||||
category = Fabricate(:category)
|
||||
category.set_permissions(:everyone => :create_post)
|
||||
category.save
|
||||
Guardian.new(user).can_create?(Topic,category).should be_false
|
||||
end
|
||||
end
|
||||
|
||||
describe 'a Post' do
|
||||
|
||||
it "is false on readonly categories" do
|
||||
category = Fabricate(:category)
|
||||
topic.category = category
|
||||
category.set_permissions(:everyone => :readonly)
|
||||
category.save
|
||||
|
||||
Guardian.new(topic.user).can_create?(Post, topic).should be_false
|
||||
|
||||
end
|
||||
|
||||
it "is false when not logged in" do
|
||||
Guardian.new.can_create?(Post, topic).should be_false
|
||||
end
|
||||
|
@ -62,8 +62,7 @@ describe PostCreator do
|
||||
admin = Fabricate(:admin)
|
||||
|
||||
cat = Fabricate(:category)
|
||||
cat.deny(:all)
|
||||
cat.allow(Group[:admins])
|
||||
cat.set_permissions(:admins => :full)
|
||||
cat.save
|
||||
|
||||
created_post = nil
|
||||
|
@ -15,15 +15,15 @@ test
|
||||
end
|
||||
|
||||
it "produces a quote even with new lines in it" do
|
||||
PrettyText.cook("[quote=\"EvilTrout, post:123, topic:456, full:true\"]ddd\n[/quote]").should match_html "<p></p><aside class=\"quote\" data-post=\"123\" data-topic=\"456\" data-full=\"true\"><div class=\"title\">\n <div class=\"quote-controls\"></div>\n <img width=\"20\" height=\"20\" src=\"/users/eviltrout/avatar/40?__ws=http%3A%2F%2Ftest.localhost\" class=\"avatar \" title=\"\">\n EvilTrout\n said:\n </div>\n <blockquote>ddd</blockquote>\n</aside><p></p>"
|
||||
PrettyText.cook("[quote=\"EvilTrout, post:123, topic:456, full:true\"]ddd\n[/quote]").should match_html "<p></p><aside class=\"quote\" data-post=\"123\" data-topic=\"456\" data-full=\"true\"><div class=\"title\">\n <div class=\"quote-controls\"></div>\n <img width=\"20\" height=\"20\" src=\"/users/eviltrout/avatar/40?__ws=http%3A%2F%2Ftest.localhost\" class=\"avatar \" title=\"\">\n EvilTrout said:\n </div>\n <blockquote>ddd</blockquote>\n</aside><p></p>"
|
||||
end
|
||||
|
||||
it "should produce a quote" do
|
||||
PrettyText.cook("[quote=\"EvilTrout, post:123, topic:456, full:true\"]ddd[/quote]").should match_html "<p></p><aside class=\"quote\" data-post=\"123\" data-topic=\"456\" data-full=\"true\"><div class=\"title\">\n <div class=\"quote-controls\"></div>\n <img width=\"20\" height=\"20\" src=\"/users/eviltrout/avatar/40?__ws=http%3A%2F%2Ftest.localhost\" class=\"avatar \" title=\"\">\n EvilTrout\n said:\n </div>\n <blockquote>ddd</blockquote>\n</aside><p></p>"
|
||||
PrettyText.cook("[quote=\"EvilTrout, post:123, topic:456, full:true\"]ddd[/quote]").should match_html "<p></p><aside class=\"quote\" data-post=\"123\" data-topic=\"456\" data-full=\"true\"><div class=\"title\">\n <div class=\"quote-controls\"></div>\n <img width=\"20\" height=\"20\" src=\"/users/eviltrout/avatar/40?__ws=http%3A%2F%2Ftest.localhost\" class=\"avatar \" title=\"\">\n EvilTrout said:\n </div>\n <blockquote>ddd</blockquote>\n</aside><p></p>"
|
||||
end
|
||||
|
||||
it "trims spaces on quote params" do
|
||||
PrettyText.cook("[quote=\"EvilTrout, post:555, topic: 666\"]ddd[/quote]").should match_html "<p></p><aside class=\"quote\" data-post=\"555\" data-topic=\"666\"><div class=\"title\">\n <div class=\"quote-controls\"></div>\n <img width=\"20\" height=\"20\" src=\"/users/eviltrout/avatar/40?__ws=http%3A%2F%2Ftest.localhost\" class=\"avatar \" title=\"\">\n EvilTrout\n said:\n </div>\n <blockquote>ddd</blockquote>\n</aside><p></p>"
|
||||
PrettyText.cook("[quote=\"EvilTrout, post:555, topic: 666\"]ddd[/quote]").should match_html "<p></p><aside class=\"quote\" data-post=\"555\" data-topic=\"666\"><div class=\"title\">\n <div class=\"quote-controls\"></div>\n <img width=\"20\" height=\"20\" src=\"/users/eviltrout/avatar/40?__ws=http%3A%2F%2Ftest.localhost\" class=\"avatar \" title=\"\">\n EvilTrout said:\n </div>\n <blockquote>ddd</blockquote>\n</aside><p></p>"
|
||||
end
|
||||
|
||||
|
||||
|
@ -171,8 +171,7 @@ describe Search do
|
||||
topic.category_id = category.id
|
||||
topic.save
|
||||
|
||||
category.deny(:all)
|
||||
category.allow(Group[:staff])
|
||||
category.set_permissions(:staff => :full)
|
||||
category.save
|
||||
|
||||
result(nil).should_not be_present
|
||||
@ -211,7 +210,7 @@ describe Search do
|
||||
r[:title].should == category.name
|
||||
r[:url].should == "/category/#{category.slug}"
|
||||
|
||||
category.deny(:all)
|
||||
category.set_permissions({})
|
||||
category.save
|
||||
|
||||
result.should_not be_present
|
||||
|
@ -14,9 +14,8 @@ describe TopicQuery do
|
||||
context 'secure category' do
|
||||
it "filters categories out correctly" do
|
||||
category = Fabricate(:category)
|
||||
category.deny(:all)
|
||||
group = Fabricate(:group)
|
||||
category.allow(group)
|
||||
category.set_permissions(group => :full)
|
||||
category.save
|
||||
|
||||
topic = Fabricate(:topic, category: category)
|
||||
|
@ -1,6 +1,7 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe Admin::GroupsController do
|
||||
|
||||
it "is a subclass of AdminController" do
|
||||
(Admin::GroupsController < Admin::AdminController).should be_true
|
||||
end
|
||||
@ -13,7 +14,7 @@ describe Admin::GroupsController do
|
||||
|
||||
xhr :get, :index
|
||||
response.status.should == 200
|
||||
::JSON.parse(response.body).should == [{
|
||||
::JSON.parse(response.body).keep_if{|r| r["id"] == group.id}.should == [{
|
||||
"id"=>group.id,
|
||||
"name"=>group.name,
|
||||
"user_count"=>1,
|
||||
@ -36,7 +37,7 @@ describe Admin::GroupsController do
|
||||
xhr :delete, :destroy, id: group.id
|
||||
response.status.should == 200
|
||||
|
||||
Group.count.should == 0
|
||||
Group.where(id: group.id).count.should == 0
|
||||
end
|
||||
|
||||
it "is able to create a group" do
|
||||
@ -49,7 +50,7 @@ describe Admin::GroupsController do
|
||||
|
||||
response.status.should == 200
|
||||
|
||||
groups = Group.all.to_a
|
||||
groups = Group.where(name: "bob").to_a
|
||||
|
||||
groups.count.should == 1
|
||||
groups[0].usernames.should == a.username
|
||||
|
@ -1,13 +1,13 @@
|
||||
require 'spec_helper'
|
||||
require "spec_helper"
|
||||
|
||||
describe CategoriesController do
|
||||
describe 'create' do
|
||||
describe "create" do
|
||||
|
||||
it 'requires the user to be logged in' do
|
||||
it "requires the user to be logged in" do
|
||||
lambda { xhr :post, :create }.should raise_error(Discourse::NotLoggedIn)
|
||||
end
|
||||
|
||||
describe 'logged in' do
|
||||
describe "logged in" do
|
||||
before do
|
||||
@user = log_in(:moderator)
|
||||
end
|
||||
@ -18,55 +18,66 @@ describe CategoriesController do
|
||||
response.should be_forbidden
|
||||
end
|
||||
|
||||
it 'raises an exception when the name is missing' do
|
||||
lambda { xhr :post, :create, color: 'ff0', text_color: 'fff' }.should raise_error(ActionController::ParameterMissing)
|
||||
it "raises an exception when the name is missing" do
|
||||
lambda { xhr :post, :create, color: "ff0", text_color: "fff" }.should raise_error(ActionController::ParameterMissing)
|
||||
end
|
||||
|
||||
it 'raises an exception when the color is missing' do
|
||||
lambda { xhr :post, :create, name: 'hello', text_color: 'fff' }.should raise_error(ActionController::ParameterMissing)
|
||||
it "raises an exception when the color is missing" do
|
||||
lambda { xhr :post, :create, name: "hello", text_color: "fff" }.should raise_error(ActionController::ParameterMissing)
|
||||
end
|
||||
|
||||
it 'raises an exception when the text color is missing' do
|
||||
lambda { xhr :post, :create, name: 'hello', color: 'ff0' }.should raise_error(ActionController::ParameterMissing)
|
||||
it "raises an exception when the text color is missing" do
|
||||
lambda { xhr :post, :create, name: "hello", color: "ff0" }.should raise_error(ActionController::ParameterMissing)
|
||||
end
|
||||
|
||||
describe 'failure' do
|
||||
describe "failure" do
|
||||
before do
|
||||
@category = Fabricate(:category, user: @user)
|
||||
xhr :post, :create, name: @category.name, color: 'ff0', text_color: 'fff'
|
||||
xhr :post, :create, name: @category.name, color: "ff0", text_color: "fff"
|
||||
end
|
||||
|
||||
it { should_not respond_with(:success) }
|
||||
|
||||
it 'returns errors on a duplicate category name' do
|
||||
response.code.to_i.should == 422
|
||||
it "returns errors on a duplicate category name" do
|
||||
response.status.should == 422
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
describe 'success' do
|
||||
before do
|
||||
xhr :post, :create, name: 'hello', color: 'ff0', text_color: 'fff'
|
||||
describe "success" do
|
||||
it "works" do
|
||||
readonly = CategoryGroup.permission_types[:readonly]
|
||||
create_post = CategoryGroup.permission_types[:create_post]
|
||||
|
||||
xhr :post, :create, name: "hello", color: "ff0", text_color: "fff",
|
||||
hotness: 2,
|
||||
auto_close_days: 3,
|
||||
permissions: {
|
||||
"everyone" => readonly,
|
||||
"staff" => create_post
|
||||
}
|
||||
|
||||
response.status.should == 200
|
||||
category = Category.first
|
||||
category.category_groups.map{|g| [g.group_id, g.permission_type]}.sort.should == [
|
||||
[Group[:everyone].id, readonly],[Group[:staff].id,create_post]
|
||||
]
|
||||
category.name.should == "hello"
|
||||
category.color.should == "ff0"
|
||||
category.hotness.should == 2
|
||||
category.auto_close_days.should == 3
|
||||
end
|
||||
|
||||
it 'creates a category' do
|
||||
Category.count.should == 1
|
||||
end
|
||||
|
||||
it { should respond_with(:success) }
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
describe 'destroy' do
|
||||
describe "destroy" do
|
||||
|
||||
it 'requires the user to be logged in' do
|
||||
lambda { xhr :delete, :destroy, id: 'category'}.should raise_error(Discourse::NotLoggedIn)
|
||||
it "requires the user to be logged in" do
|
||||
lambda { xhr :delete, :destroy, id: "category"}.should raise_error(Discourse::NotLoggedIn)
|
||||
end
|
||||
|
||||
describe 'logged in' do
|
||||
describe "logged in" do
|
||||
before do
|
||||
@user = log_in
|
||||
@category = Fabricate(:category, user: @user)
|
||||
@ -86,14 +97,14 @@ describe CategoriesController do
|
||||
|
||||
end
|
||||
|
||||
describe 'update' do
|
||||
describe "update" do
|
||||
|
||||
it 'requires the user to be logged in' do
|
||||
it "requires the user to be logged in" do
|
||||
lambda { xhr :put, :update, id: 'category'}.should raise_error(Discourse::NotLoggedIn)
|
||||
end
|
||||
|
||||
|
||||
describe 'logged in' do
|
||||
describe "logged in" do
|
||||
before do
|
||||
@user = log_in(:moderator)
|
||||
@category = Fabricate(:category, user: @user)
|
||||
@ -117,37 +128,46 @@ describe CategoriesController do
|
||||
lambda { xhr :put, :update, id: @category.slug, name: 'asdf', color: 'fff' }.should raise_error(ActionController::ParameterMissing)
|
||||
end
|
||||
|
||||
describe 'failure' do
|
||||
describe "failure" do
|
||||
before do
|
||||
@other_category = Fabricate(:category, name: 'Other', user: @user )
|
||||
xhr :put, :update, id: @category.id, name: @other_category.name, color: 'ff0', text_color: 'fff'
|
||||
@other_category = Fabricate(:category, name: "Other", user: @user )
|
||||
xhr :put, :update, id: @category.id, name: @other_category.name, color: "ff0", text_color: "fff"
|
||||
end
|
||||
|
||||
it 'returns errors on a duplicate category name' do
|
||||
it "returns errors on a duplicate category name" do
|
||||
response.should_not be_success
|
||||
end
|
||||
|
||||
it 'returns errors on a duplicate category name' do
|
||||
it "returns errors on a duplicate category name" do
|
||||
response.code.to_i.should == 422
|
||||
end
|
||||
end
|
||||
|
||||
describe 'success' do
|
||||
before do
|
||||
# might as well test this as well
|
||||
@category.allow(Group[:admins])
|
||||
@category.save
|
||||
describe "success" do
|
||||
|
||||
xhr :put, :update, id: @category.id, name: 'science', color: '000', text_color: '0ff', group_names: Group[:staff].name, secure: 'true'
|
||||
it "updates the group correctly" do
|
||||
|
||||
readonly = CategoryGroup.permission_types[:readonly]
|
||||
create_post = CategoryGroup.permission_types[:create_post]
|
||||
|
||||
xhr :put, :update, id: @category.id, name: "hello", color: "ff0", text_color: "fff",
|
||||
hotness: 2,
|
||||
auto_close_days: 3,
|
||||
permissions: {
|
||||
"everyone" => readonly,
|
||||
"staff" => create_post
|
||||
}
|
||||
|
||||
response.status.should == 200
|
||||
@category.reload
|
||||
end
|
||||
@category.category_groups.map{|g| [g.group_id, g.permission_type]}.sort.should == [
|
||||
[Group[:everyone].id, readonly],[Group[:staff].id,create_post]
|
||||
]
|
||||
@category.name.should == "hello"
|
||||
@category.color.should == "ff0"
|
||||
@category.hotness.should == 2
|
||||
@category.auto_close_days.should == 3
|
||||
|
||||
it 'updates the group correctly' do
|
||||
@category.name.should == 'science'
|
||||
@category.color.should == '000'
|
||||
@category.text_color.should == '0ff'
|
||||
@category.secure?.should be_true
|
||||
@category.groups.count.should == 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -15,8 +15,7 @@ describe CategoryFeaturedTopic do
|
||||
# so much dancing, I am thinking fixures make sense here.
|
||||
user.change_trust_level!(:basic)
|
||||
|
||||
category.deny(:all)
|
||||
category.allow(Group[:trust_level_1])
|
||||
category.set_permissions(:trust_level_1 => :full)
|
||||
category.save
|
||||
|
||||
uncategorized_post = PostCreator.create(user, raw: "this is my new post 123 post", title: "hello world")
|
||||
|
@ -18,6 +18,63 @@ describe Category do
|
||||
it { should have_many :category_featured_topics }
|
||||
it { should have_many :featured_topics }
|
||||
|
||||
|
||||
describe "resolve_permissions" do
|
||||
it "can determine read_restricted" do
|
||||
read_restricted, resolved = Category.resolve_permissions(:everyone => :full)
|
||||
|
||||
read_restricted.should be_false
|
||||
resolved.should == []
|
||||
end
|
||||
end
|
||||
|
||||
describe "topic_create_allowed and post_create_allowed" do
|
||||
it "works" do
|
||||
default_category = Fabricate(:category)
|
||||
full_category = Fabricate(:category)
|
||||
can_post_category = Fabricate(:category)
|
||||
can_read_category = Fabricate(:category)
|
||||
|
||||
|
||||
user = Fabricate(:user)
|
||||
group = Fabricate(:group)
|
||||
group.add(user)
|
||||
group.save
|
||||
|
||||
admin = Fabricate(:admin)
|
||||
|
||||
full_category.set_permissions(group => :full)
|
||||
full_category.save
|
||||
|
||||
can_post_category.set_permissions(group => :create_post)
|
||||
can_post_category.save
|
||||
|
||||
can_read_category.set_permissions(group => :readonly)
|
||||
can_read_category.save
|
||||
|
||||
guardian = Guardian.new(admin)
|
||||
Category.topic_create_allowed(guardian).count.should == 4
|
||||
Category.post_create_allowed(guardian).count.should == 4
|
||||
Category.secured(guardian).count.should == 4
|
||||
|
||||
guardian = Guardian.new(user)
|
||||
Category.secured(guardian).count.should == 4
|
||||
Category.post_create_allowed(guardian).count.should == 3
|
||||
Category.topic_create_allowed(guardian).count.should == 2 # explicitly allowed once, default allowed once
|
||||
|
||||
# everyone has special semantics, test it as well
|
||||
can_post_category.set_permissions(:everyone => :create_post)
|
||||
can_post_category.save
|
||||
|
||||
Category.post_create_allowed(guardian).count.should == 3
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "post_create_allowed" do
|
||||
|
||||
end
|
||||
|
||||
describe "security" do
|
||||
let(:category) { Fabricate(:category) }
|
||||
let(:category_2) { Fabricate(:category) }
|
||||
@ -25,20 +82,20 @@ describe Category do
|
||||
let(:group) { Fabricate(:group) }
|
||||
|
||||
it "secures categories correctly" do
|
||||
category.secure?.should be_false
|
||||
category.read_restricted?.should be_false
|
||||
|
||||
category.deny(:all)
|
||||
category.secure?.should be_true
|
||||
category.set_permissions({})
|
||||
category.read_restricted?.should be_true
|
||||
|
||||
category.allow(:all)
|
||||
category.secure?.should be_false
|
||||
category.set_permissions(:everyone => :full)
|
||||
category.read_restricted?.should be_false
|
||||
|
||||
user.secure_categories.should be_empty
|
||||
|
||||
group.add(user)
|
||||
group.save
|
||||
|
||||
category.allow(group)
|
||||
category.set_permissions(group.id => :full)
|
||||
category.save
|
||||
|
||||
user.reload
|
||||
@ -47,13 +104,13 @@ describe Category do
|
||||
|
||||
it "lists all secured categories correctly" do
|
||||
group.add(user)
|
||||
category.allow(group)
|
||||
category.set_permissions(group.id => :full)
|
||||
category.save
|
||||
category_2.set_permissions(group.id => :full)
|
||||
category_2.save
|
||||
|
||||
Category.secured.should == [category]
|
||||
|
||||
category_2.allow(group)
|
||||
|
||||
Category.secured.should =~ [category, category_2]
|
||||
Category.secured.should =~ []
|
||||
Category.secured(Guardian.new(user)).should =~ [category, category_2]
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -80,6 +80,11 @@ describe PostAnalyzer do
|
||||
post_analyzer = PostAnalyzer.new(raw_three_links, default_topic_id)
|
||||
post_analyzer.linked_hosts.should == {"discourse.org" => 1, "www.imdb.com" => 1}
|
||||
end
|
||||
|
||||
it 'returns blank for ipv6 output' do
|
||||
post_analyzer = PostAnalyzer.new('PING www.google.com(lb-in-x93.1e100.net) 56 data bytes', default_topic_id)
|
||||
post_analyzer.linked_hosts.should be_blank
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
17
spec/models/site_spec.rb
Normal file
17
spec/models/site_spec.rb
Normal file
@ -0,0 +1,17 @@
|
||||
require 'spec_helper'
|
||||
require_dependency 'site'
|
||||
|
||||
describe Site do
|
||||
it "omits categories users can not write to from the category list" do
|
||||
category = Fabricate(:category)
|
||||
user = Fabricate(:user)
|
||||
|
||||
Site.new(Guardian.new(user)).categories.count.should == 1
|
||||
|
||||
category.set_permissions(:everyone => :create_post)
|
||||
category.save
|
||||
|
||||
# TODO clean up querying so we can make sure we have the correct permission set
|
||||
Site.new(Guardian.new(user)).categories[0].permission.should_not == CategoryGroup.permission_types[:full]
|
||||
end
|
||||
end
|
@ -258,8 +258,7 @@ describe TopicLink do
|
||||
TopicLink.topic_summary(Guardian.new, post.topic_id).count.should == 1
|
||||
TopicLink.counts_for(Guardian.new, post.topic, [post]).length.should == 1
|
||||
|
||||
category.deny(:all)
|
||||
category.allow(Group[:staff])
|
||||
category.set_permissions(:staff => :full)
|
||||
category.save
|
||||
|
||||
admin = Fabricate(:admin)
|
||||
|
@ -192,7 +192,7 @@ describe Topic do
|
||||
context "secure categories" do
|
||||
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:category) { Fabricate(:category, secure: true) }
|
||||
let(:category) { Fabricate(:category, read_restricted: true) }
|
||||
|
||||
before do
|
||||
topic.category = category
|
||||
@ -1263,7 +1263,7 @@ describe Topic do
|
||||
|
||||
describe 'secured' do
|
||||
it 'can remove secure groups' do
|
||||
category = Fabricate(:category, secure: true)
|
||||
category = Fabricate(:category, read_restricted: true)
|
||||
topic = Fabricate(:topic, category: category)
|
||||
|
||||
Topic.secured(Guardian.new(nil)).count.should == 0
|
||||
@ -1280,17 +1280,17 @@ describe Topic do
|
||||
let(:category){ Category.new }
|
||||
|
||||
it "is true if the category is secure" do
|
||||
category.stubs(:secure).returns(true)
|
||||
Topic.new(:category => category).should be_secure_category
|
||||
category.stubs(:read_restricted).returns(true)
|
||||
Topic.new(:category => category).should be_read_restricted_category
|
||||
end
|
||||
|
||||
it "is false if the category is not secure" do
|
||||
category.stubs(:secure).returns(false)
|
||||
Topic.new(:category => category).should_not be_secure_category
|
||||
category.stubs(:read_restricted).returns(false)
|
||||
Topic.new(:category => category).should_not be_read_restricted_category
|
||||
end
|
||||
|
||||
it "is false if there is no category" do
|
||||
Topic.new(:category => nil).should_not be_secure_category
|
||||
Topic.new(:category => nil).should_not be_read_restricted_category
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -51,7 +51,7 @@ describe TopicTrackingState do
|
||||
row.user_id.should == post.user_id
|
||||
|
||||
# when we have no permission to see a category, don't show its stats
|
||||
category = Fabricate(:category, secure: true)
|
||||
category = Fabricate(:category, read_restricted: true)
|
||||
|
||||
post.topic.category_id = category.id
|
||||
post.topic.save
|
||||
|
@ -68,7 +68,7 @@ describe UserAction do
|
||||
|
||||
# groups
|
||||
|
||||
category = Fabricate(:category, secure: true)
|
||||
category = Fabricate(:category, read_restricted: true)
|
||||
|
||||
public_topic.recover!
|
||||
public_topic.category = category
|
||||
@ -82,7 +82,7 @@ describe UserAction do
|
||||
group.add(u)
|
||||
group.save
|
||||
|
||||
category.allow(group)
|
||||
category.set_permissions(group => :full)
|
||||
category.save
|
||||
|
||||
stats_for_user(u).should == [UserAction::NEW_TOPIC]
|
||||
|
@ -77,21 +77,21 @@ test("quote formatting", function() {
|
||||
// TODO: This HTML matching is quite ugly.
|
||||
format("[quote=\"eviltrout, post:1, topic:1\"]abc[/quote]",
|
||||
"</p><aside class='quote' data-post=\"1\" data-topic=\"1\" >\n <div class='title'>\n " +
|
||||
"<div class='quote-controls'></div>\n \n eviltrout\n said:\n </div>\n <blockquote>abc</blockquote>\n</aside>\n<p>",
|
||||
"<div class='quote-controls'></div>\n \n eviltrout said:\n </div>\n <blockquote>abc</blockquote>\n</aside>\n<p>",
|
||||
"renders quotes properly");
|
||||
|
||||
format("[quote=\"eviltrout, post:1, topic:1\"]abc[quote=\"eviltrout, post:2, topic:2\"]nested[/quote][/quote]",
|
||||
"</p><aside class='quote' data-post=\"1\" data-topic=\"1\" >\n <div class='title'>\n <div " +
|
||||
"class='quote-controls'></div>\n \n eviltrout\n said:\n </div>\n <blockquote>abc</p><aside " +
|
||||
"class='quote-controls'></div>\n \n eviltrout said:\n </div>\n <blockquote>abc</p><aside " +
|
||||
"class='quote' data-post=\"2\" data-topic=\"2\" >\n <div class='title'>\n <div class='quote-" +
|
||||
"controls'></div>\n \n eviltrout\n said:\n </div>\n <blockquote>nested</blockquote>\n</aside>\n<p></blockquote>\n</aside>\n<p>",
|
||||
"controls'></div>\n \n eviltrout said:\n </div>\n <blockquote>nested</blockquote>\n</aside>\n<p></blockquote>\n</aside>\n<p>",
|
||||
"can nest quotes");
|
||||
|
||||
format("before[quote=\"eviltrout, post:1, topic:1\"]first[/quote]middle[quote=\"eviltrout, post:2, topic:2\"]second[/quote]after",
|
||||
"before</p><aside class='quote' data-post=\"1\" data-topic=\"1\" >\n <div class='title'>\n <div class='quote-cont" +
|
||||
"rols'></div>\n \n eviltrout\n said:\n </div>\n <blockquote>first</blockquote>\n</aside>\n<p>middle</p><aside cla" +
|
||||
"rols'></div>\n \n eviltrout said:\n </div>\n <blockquote>first</blockquote>\n</aside>\n<p>middle</p><aside cla" +
|
||||
"ss='quote' data-post=\"2\" data-topic=\"2\" >\n <div class='title'>\n <div class='quote-controls'></div>\n \n " +
|
||||
"eviltrout\n said:\n </div>\n <blockquote>second</blockquote>\n</aside>\n<p>after",
|
||||
"eviltrout said:\n </div>\n <blockquote>second</blockquote>\n</aside>\n<p>after",
|
||||
"can handle more than one quote");
|
||||
|
||||
});
|
||||
|
@ -69,13 +69,13 @@ test("Quotes", function() {
|
||||
cookedOptions("1[quote=\"bob, post:1\"]my quote[/quote]2",
|
||||
{ topicId: 2, lookupAvatar: function(name) { return "" + name; } },
|
||||
"<p>1</p><aside class='quote' data-post=\"1\" >\n <div class='title'>\n <div class='quote-controls'></div>\n" +
|
||||
" bob\n bob\n said:\n </div>\n <blockquote>my quote</blockquote>\n</aside>\n<p></p>\n\n<p>2</p>",
|
||||
" bob\n bob said:\n </div>\n <blockquote>my quote</blockquote>\n</aside>\n<p></p>\n\n<p>2</p>",
|
||||
"handles quotes properly");
|
||||
|
||||
cookedOptions("1[quote=\"bob, post:1\"]my quote[/quote]2",
|
||||
{ topicId: 2, lookupAvatar: function(name) { } },
|
||||
"<p>1</p><aside class='quote' data-post=\"1\" >\n <div class='title'>\n <div class='quote-controls'></div>\n" +
|
||||
" \n bob\n said:\n </div>\n <blockquote>my quote</blockquote>\n</aside>\n<p></p>\n\n<p>2</p>",
|
||||
" \n bob said:\n </div>\n <blockquote>my quote</blockquote>\n</aside>\n<p></p>\n\n<p>2</p>",
|
||||
"includes no avatar if none is found");
|
||||
});
|
||||
|
||||
|
@ -4,17 +4,20 @@ module("Discourse.Onebox", {
|
||||
}
|
||||
});
|
||||
|
||||
test("Stops rapid calls with cache true", function() {
|
||||
this.stub(Discourse, "ajax").returns(resolvingPromise);
|
||||
asyncTestDiscourse("Stops rapid calls with cache true", function() {
|
||||
this.stub(Discourse, "ajax").returns(Ember.RSVP.resolve());
|
||||
Discourse.Onebox.load(this.anchor, true);
|
||||
Discourse.Onebox.load(this.anchor, true);
|
||||
|
||||
Discourse.Onebox.load(this.anchor, true);
|
||||
Discourse.Onebox.load(this.anchor, true);
|
||||
start();
|
||||
ok(Discourse.ajax.calledOnce);
|
||||
});
|
||||
|
||||
test("Stops rapid calls with cache false", function() {
|
||||
this.stub(Discourse, "ajax").returns(resolvingPromise);
|
||||
asyncTestDiscourse("Stops rapid calls with cache true", function() {
|
||||
this.stub(Discourse, "ajax").returns(Ember.RSVP.resolve());
|
||||
Discourse.Onebox.load(this.anchor, false);
|
||||
Discourse.Onebox.load(this.anchor, false);
|
||||
|
||||
start();
|
||||
ok(Discourse.ajax.calledOnce);
|
||||
});
|
||||
});
|
||||
|
@ -14,7 +14,7 @@ test("remove", function() {
|
||||
blank(PreloadStore.get('bane'), "removes the value if the key exists");
|
||||
});
|
||||
|
||||
asyncTest("getAndRemove returns a promise that resolves to null", function() {
|
||||
asyncTestDiscourse("getAndRemove returns a promise that resolves to null", function() {
|
||||
expect(1);
|
||||
|
||||
PreloadStore.getAndRemove('joker').then(function(result) {
|
||||
@ -23,7 +23,7 @@ asyncTest("getAndRemove returns a promise that resolves to null", function() {
|
||||
});
|
||||
});
|
||||
|
||||
asyncTest("getAndRemove returns a promise that resolves to the result of the finder", function() {
|
||||
asyncTestDiscourse("getAndRemove returns a promise that resolves to the result of the finder", function() {
|
||||
expect(1);
|
||||
|
||||
var finder = function() { return 'batdance'; };
|
||||
@ -34,7 +34,7 @@ asyncTest("getAndRemove returns a promise that resolves to the result of the fin
|
||||
|
||||
});
|
||||
|
||||
asyncTest("getAndRemove returns a promise that resolves to the result of the finder's promise", function() {
|
||||
asyncTestDiscourse("getAndRemove returns a promise that resolves to the result of the finder's promise", function() {
|
||||
expect(1);
|
||||
|
||||
var finder = function() {
|
||||
@ -47,7 +47,7 @@ asyncTest("getAndRemove returns a promise that resolves to the result of the fin
|
||||
});
|
||||
});
|
||||
|
||||
asyncTest("returns a promise that rejects with the result of the finder's rejected promise", function() {
|
||||
asyncTestDiscourse("returns a promise that rejects with the result of the finder's rejected promise", function() {
|
||||
expect(1);
|
||||
|
||||
var finder = function() {
|
||||
@ -61,7 +61,7 @@ asyncTest("returns a promise that rejects with the result of the finder's reject
|
||||
|
||||
});
|
||||
|
||||
asyncTest("returns a promise that resolves to 'evil'", function() {
|
||||
asyncTestDiscourse("returns a promise that resolves to 'evil'", function() {
|
||||
expect(1);
|
||||
|
||||
PreloadStore.getAndRemove('bane').then(function(result) {
|
||||
|
@ -1,11 +1,11 @@
|
||||
// Test helpers
|
||||
var resolvingPromise = Ember.Deferred.promise(function (p) {
|
||||
p.resolve();
|
||||
});
|
||||
// var resolvingPromise = Ember.Deferred.promise(function (p) {
|
||||
// p.resolve();
|
||||
// });
|
||||
|
||||
var resolvingPromiseWith = function(result) {
|
||||
return Ember.Deferred.promise(function (p) { p.resolve(result); });
|
||||
};
|
||||
// var resolvingPromiseWith = function(result) {
|
||||
// return Ember.Deferred.promise(function (p) { p.resolve(result); });
|
||||
// };
|
||||
|
||||
function exists(selector) {
|
||||
return !!count(selector);
|
||||
|
@ -18,4 +18,15 @@ function controllerFor(controller, model) {
|
||||
var controller = Discourse.__container__.lookup('controller:' + controller);
|
||||
if (model) { controller.set('model', model ); }
|
||||
return controller;
|
||||
}
|
||||
|
||||
function asyncTestDiscourse(text, func) {
|
||||
|
||||
asyncTest(text, function () {
|
||||
|
||||
var qunitContext = this;
|
||||
Ember.run(function () {
|
||||
func.call(qunitContext);
|
||||
});
|
||||
});
|
||||
}
|
@ -8,7 +8,7 @@ var qHint = function(name, sourceFile, options, globals) {
|
||||
sourceFile = name;
|
||||
}
|
||||
|
||||
return asyncTest(name, function() {
|
||||
return asyncTestDiscourse(name, function() {
|
||||
qHint.sendRequest(sourceFile, function(req) {
|
||||
start();
|
||||
|
||||
@ -113,9 +113,8 @@ var jsHintOpts = {
|
||||
"visit",
|
||||
"count",
|
||||
"exists",
|
||||
"asyncTest",
|
||||
"asyncTestDiscourse",
|
||||
"find",
|
||||
"resolvingPromise",
|
||||
"sinon",
|
||||
"moment",
|
||||
"start",
|
||||
@ -125,7 +124,6 @@ var jsHintOpts = {
|
||||
"controllerFor",
|
||||
"containsInstance",
|
||||
"deepEqual",
|
||||
"resolvingPromiseWith",
|
||||
"Blob",
|
||||
"File"],
|
||||
"node" : false,
|
||||
|
@ -130,7 +130,7 @@ test('editingFirstPost', function() {
|
||||
|
||||
});
|
||||
|
||||
asyncTest('importQuote with a post', function() {
|
||||
asyncTestDiscourse('importQuote with a post', function() {
|
||||
expect(1);
|
||||
|
||||
this.stub(Discourse.Post, 'load').withArgs(123).returns(Em.Deferred.promise(function (p) {
|
||||
@ -144,7 +144,7 @@ asyncTest('importQuote with a post', function() {
|
||||
});
|
||||
});
|
||||
|
||||
asyncTest('importQuote with no post', function() {
|
||||
asyncTestDiscourse('importQuote with no post', function() {
|
||||
expect(1);
|
||||
|
||||
this.stub(Discourse.Post, 'load').withArgs(4).returns(Em.Deferred.promise(function (p) {
|
||||
|
@ -213,7 +213,7 @@ test("identity map", function() {
|
||||
deepEqual(postStream.listUnloadedIds([1, 2, 3, 4]), [2, 4], "it only returns unloaded posts");
|
||||
});
|
||||
|
||||
asyncTest("loadIntoIdentityMap with no data", function() {
|
||||
asyncTestDiscourse("loadIntoIdentityMap with no data", function() {
|
||||
var postStream = buildStream(1234);
|
||||
expect(1);
|
||||
|
||||
@ -224,11 +224,11 @@ asyncTest("loadIntoIdentityMap with no data", function() {
|
||||
});
|
||||
});
|
||||
|
||||
asyncTest("loadIntoIdentityMap with post ids", function() {
|
||||
asyncTestDiscourse("loadIntoIdentityMap with post ids", function() {
|
||||
var postStream = buildStream(1234);
|
||||
expect(1);
|
||||
|
||||
this.stub(Discourse, "ajax").returns(resolvingPromiseWith({
|
||||
this.stub(Discourse, "ajax").returns(Ember.RSVP.resolve({
|
||||
post_stream: {
|
||||
posts: [{id: 10, post_number: 10}]
|
||||
}
|
||||
|
@ -11,7 +11,7 @@
|
||||
// Externals we need to load first
|
||||
//= require ../../app/assets/javascripts/external/jquery-1.9.1.js
|
||||
//= require ../../app/assets/javascripts/external/jquery.ui.widget.js
|
||||
//= require ../../app/assets/javascripts/external/handlebars-1.0.rc.4.js
|
||||
//= require ../../app/assets/javascripts/external/handlebars.js
|
||||
//= require ../../app/assets/javascripts/external_development/ember.js
|
||||
//= require ../../app/assets/javascripts/external_development/group-helper.js
|
||||
|
||||
@ -46,11 +46,11 @@
|
||||
|
||||
// sinon settings
|
||||
sinon.config = {
|
||||
injectIntoThis: true,
|
||||
injectInto: null,
|
||||
properties: ["spy", "stub", "mock", "clock", "sandbox"],
|
||||
useFakeTimers: false,
|
||||
useFakeServer: false
|
||||
injectIntoThis: true,
|
||||
injectInto: null,
|
||||
properties: ["spy", "stub", "mock", "clock", "sandbox"],
|
||||
useFakeTimers: false,
|
||||
useFakeServer: false
|
||||
};
|
||||
|
||||
window.assetPath = function() { return null };
|
||||
|
Loading…
x
Reference in New Issue
Block a user