mirror of
https://github.com/discourse/discourse.git
synced 2025-04-26 18:54:31 +08: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/database.yml
|
||||||
config/redis.yml
|
config/redis.yml
|
||||||
config/discourse.pill
|
config/discourse.pill
|
||||||
|
config/environments/production.rb
|
||||||
|
|
||||||
# Ignore the default SQLite database and db dumps
|
# Ignore the default SQLite database and db dumps
|
||||||
/db/*.sqlite3
|
/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
|
# we had issues with latest, stick to the rev till we figure this out
|
||||||
# PR that makes it all hang together welcome
|
# PR that makes it all hang together welcome
|
||||||
gem 'ember-rails'
|
gem 'ember-rails'
|
||||||
gem 'ember-source', '1.0.0.rc5' # or the version you need
|
gem 'ember-source', '1.0.0.rc6.2'
|
||||||
gem 'handlebars-source', '1.0.0.rc4' # or the version you need
|
gem 'handlebars-source', '1.0.12'
|
||||||
gem 'barber'
|
gem 'barber'
|
||||||
|
|
||||||
gem 'vestal_versions', git: 'https://github.com/zhangyuan/vestal_versions'
|
gem 'vestal_versions', git: 'https://github.com/zhangyuan/vestal_versions'
|
||||||
|
10
Gemfile.lock
10
Gemfile.lock
@ -169,8 +169,8 @@ GEM
|
|||||||
barber
|
barber
|
||||||
execjs (>= 1.2)
|
execjs (>= 1.2)
|
||||||
railties (>= 3.1)
|
railties (>= 3.1)
|
||||||
ember-source (1.0.0.rc5)
|
ember-source (1.0.0.rc6.2)
|
||||||
handlebars-source (= 1.0.0.rc4)
|
handlebars-source (= 1.0.12)
|
||||||
erubis (2.7.0)
|
erubis (2.7.0)
|
||||||
eventmachine (1.0.3)
|
eventmachine (1.0.3)
|
||||||
excon (0.20.1)
|
excon (0.20.1)
|
||||||
@ -211,7 +211,7 @@ GEM
|
|||||||
childprocess (>= 0.2.3)
|
childprocess (>= 0.2.3)
|
||||||
guard (>= 1.1)
|
guard (>= 1.1)
|
||||||
spork (>= 0.8.4)
|
spork (>= 0.8.4)
|
||||||
handlebars-source (1.0.0.rc4)
|
handlebars-source (1.0.12)
|
||||||
hashie (2.0.4)
|
hashie (2.0.4)
|
||||||
highline (1.6.18)
|
highline (1.6.18)
|
||||||
hike (1.2.2)
|
hike (1.2.2)
|
||||||
@ -482,7 +482,7 @@ DEPENDENCIES
|
|||||||
em-redis
|
em-redis
|
||||||
email_reply_parser!
|
email_reply_parser!
|
||||||
ember-rails
|
ember-rails
|
||||||
ember-source (= 1.0.0.rc5)
|
ember-source (= 1.0.0.rc6.2)
|
||||||
eventmachine
|
eventmachine
|
||||||
fabrication
|
fabrication
|
||||||
fakeweb (~> 1.3.0)
|
fakeweb (~> 1.3.0)
|
||||||
@ -493,7 +493,7 @@ DEPENDENCIES
|
|||||||
fog
|
fog
|
||||||
guard-rspec
|
guard-rspec
|
||||||
guard-spork
|
guard-spork
|
||||||
handlebars-source (= 1.0.0.rc4)
|
handlebars-source (= 1.0.12)
|
||||||
highline
|
highline
|
||||||
hiredis
|
hiredis
|
||||||
image_optim
|
image_optim
|
||||||
|
@ -238,9 +238,9 @@ Discourse.AdminUser = Discourse.User.extend({
|
|||||||
|
|
||||||
loadDetails: function() {
|
loadDetails: function() {
|
||||||
var model = this;
|
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.setProperties(result);
|
||||||
model.set('loadedDetails', true);
|
model.set('loadedDetails', true);
|
||||||
});
|
});
|
||||||
|
@ -11,14 +11,16 @@ var oneWeekAgo = function() {
|
|||||||
return moment().subtract('days',7).format('YYYY-MM-DD');
|
return moment().subtract('days',7).format('YYYY-MM-DD');
|
||||||
};
|
};
|
||||||
|
|
||||||
Discourse.AdminEmailPreviewDigestRoute = Discourse.Route.extend(Discourse.ModelReady, {
|
Discourse.AdminEmailPreviewDigestRoute = Discourse.Route.extend({
|
||||||
|
|
||||||
model: function() {
|
model: function() {
|
||||||
return Discourse.EmailPreview.findDigest(oneWeekAgo());
|
return Discourse.EmailPreview.findDigest(oneWeekAgo());
|
||||||
},
|
},
|
||||||
|
|
||||||
modelReady: function(controller, model) {
|
afterModel: function(model) {
|
||||||
|
var controller = this.controllerFor('adminEmailPreviewDigest');
|
||||||
controller.setProperties({
|
controller.setProperties({
|
||||||
|
model: model,
|
||||||
lastSeen: oneWeekAgo(),
|
lastSeen: oneWeekAgo(),
|
||||||
showHtml: true
|
showHtml: true
|
||||||
});
|
});
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
@namespace Discourse
|
@namespace Discourse
|
||||||
@module Discourse
|
@module Discourse
|
||||||
**/
|
**/
|
||||||
Discourse.AdminUserRoute = Discourse.Route.extend(Discourse.ModelReady, {
|
Discourse.AdminUserRoute = Discourse.Route.extend({
|
||||||
|
|
||||||
serialize: function(params) {
|
serialize: function(params) {
|
||||||
return { username: Em.get(params, 'username').toLowerCase() };
|
return { username: Em.get(params, 'username').toLowerCase() };
|
||||||
@ -20,10 +20,14 @@ Discourse.AdminUserRoute = Discourse.Route.extend(Discourse.ModelReady, {
|
|||||||
this.render({into: 'admin/templates/admin'});
|
this.render({into: 'admin/templates/admin'});
|
||||||
},
|
},
|
||||||
|
|
||||||
modelReady: function(controller, adminUser) {
|
afterModel: function(adminUser) {
|
||||||
adminUser.loadDetails();
|
var controller = this.controllerFor('adminUser');
|
||||||
controller.set('model', adminUser);
|
|
||||||
adminUser.setOriginalTrustLevel();
|
adminUser.loadDetails().then(function () {
|
||||||
|
adminUser.setOriginalTrustLevel();
|
||||||
|
controller.set('model', adminUser);
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
<div class='admin-controls'>
|
<div class='admin-controls'>
|
||||||
<div class='span15'>
|
<div class='span15'>
|
||||||
<ul class="nav nav-pills">
|
<ul class="nav nav-pills">
|
||||||
<li>{{#linkTo adminEmail.index}}{{i18n admin.email.settings}}{{/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.logs'}}{{i18n admin.email.logs}}{{/linkTo}}</li>
|
||||||
<li>{{#linkTo adminEmail.previewDigest}}{{i18n admin.email.preview_digest}}{{/linkTo}}</li>
|
<li>{{#linkTo 'adminEmail.previewDigest'}}{{i18n admin.email.preview_digest}}{{/linkTo}}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<div class='admin-controls'>
|
<div class='admin-controls'>
|
||||||
<div class='span15'>
|
<div class='span15'>
|
||||||
<ul class="nav nav-pills">
|
<ul class="nav nav-pills">
|
||||||
<li>{{#linkTo adminFlags.active}}{{i18n admin.flags.active}}{{/linkTo}}</li>
|
<li>{{#linkTo 'adminFlags.active'}}{{i18n admin.flags.active}}{{/linkTo}}</li>
|
||||||
<li>{{#linkTo adminFlags.old}}{{i18n admin.flags.old}}{{/linkTo}}</li>
|
<li>{{#linkTo 'adminFlags.old'}}{{i18n admin.flags.old}}{{/linkTo}}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
<div class='admin-controls'>
|
<div class='admin-controls'>
|
||||||
<div class='span15'>
|
<div class='span15'>
|
||||||
<ul class="nav nav-pills">
|
<ul class="nav nav-pills">
|
||||||
<li>{{#linkTo adminUsersList.active}}{{i18n admin.users.nav.active}}{{/linkTo}}</li>
|
<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.new'}}{{i18n admin.users.nav.new}}{{/linkTo}}</li>
|
||||||
{{#if Discourse.SiteSettings.must_approve_users}}
|
{{#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}}
|
{{/if}}
|
||||||
<li>{{#linkTo adminUsersList.admins}}{{i18n admin.users.nav.admins}}{{/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.moderators'}}{{i18n admin.users.nav.moderators}}{{/linkTo}}</li>
|
||||||
<li>{{#linkTo adminUsersList.banned}}{{i18n admin.users.nav.banned}}{{/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.blocked'}}{{i18n admin.users.nav.blocked}}{{/linkTo}}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class='span5 username controls'>
|
<div class='span5 username controls'>
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
// Externals we need to load first
|
// Externals we need to load first
|
||||||
//= require ./external/jquery-1.9.1.js
|
//= require ./external/jquery-1.9.1.js
|
||||||
//= require ./external/jquery.ui.widget.js
|
//= require ./external/jquery.ui.widget.js
|
||||||
//= require ./external/handlebars-1.0.rc.4.js
|
//= require ./external/handlebars.js
|
||||||
<%
|
<%
|
||||||
if Rails.env.development?
|
if Rails.env.development?
|
||||||
require_asset ("./external_development/ember.js")
|
require_asset ("./external_development/ember.js")
|
||||||
|
@ -250,9 +250,7 @@ Discourse = Ember.Application.createWithMixins({
|
|||||||
// If we have URL_FIXTURES, load from there instead (testing)
|
// If we have URL_FIXTURES, load from there instead (testing)
|
||||||
var fixture = Discourse.URL_FIXTURES && Discourse.URL_FIXTURES[url];
|
var fixture = Discourse.URL_FIXTURES && Discourse.URL_FIXTURES[url];
|
||||||
if (fixture) {
|
if (fixture) {
|
||||||
return Ember.Deferred.promise(function(promise) {
|
return Ember.RSVP.resolve(fixture);
|
||||||
promise.resolve(fixture);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ember.Deferred.promise(function (promise) {
|
return Ember.Deferred.promise(function (promise) {
|
||||||
|
@ -254,7 +254,7 @@ Discourse.BBCode = {
|
|||||||
|
|
||||||
// Arguments for formatting
|
// Arguments for formatting
|
||||||
args = {
|
args = {
|
||||||
username: username,
|
username: I18n.t('user.said',{username: username}),
|
||||||
params: params,
|
params: params,
|
||||||
quote: content,
|
quote: content,
|
||||||
avatarImg: opts.lookupAvatar ? opts.lookupAvatar(username) : void 0
|
avatarImg: opts.lookupAvatar ? opts.lookupAvatar(username) : void 0
|
||||||
|
@ -101,14 +101,12 @@ Discourse.EditCategoryController = Discourse.ObjectController.extend(Discourse.M
|
|||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
addGroup: function(){
|
addPermission: function(group, permission_id){
|
||||||
this.get('model').addGroup(this.get("selectedGroup"));
|
this.get('model').addPermission({group_name: group + "", permission: Discourse.PermissionType.create({id: permission_id})});
|
||||||
},
|
},
|
||||||
|
|
||||||
removeGroup: function(group){
|
removePermission: function(permission){
|
||||||
// OBVIOUS, Ember treats this as Ember.String, we need a real string here
|
this.get('model').removePermission(permission);
|
||||||
group = group + "";
|
|
||||||
this.get('model').removeGroup(group);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
saveCategory: function() {
|
saveCategory: function() {
|
||||||
|
@ -100,11 +100,16 @@ Discourse.QuoteButtonController = Discourse.Controller.extend({
|
|||||||
var post = this.get('post');
|
var post = this.get('post');
|
||||||
var composerController = this.get('controllers.composer');
|
var composerController = this.get('controllers.composer');
|
||||||
var composerOpts = {
|
var composerOpts = {
|
||||||
post: post,
|
|
||||||
action: Discourse.Composer.REPLY,
|
action: Discourse.Composer.REPLY,
|
||||||
draftKey: this.get('post.topic.draft_key')
|
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.
|
// If the composer is associated with a different post, we don't change it.
|
||||||
var composerPost = composerController.get('content.post');
|
var composerPost = composerController.get('content.post');
|
||||||
if (composerPost && (composerPost.get('id') !== this.get('post.id'))) {
|
if (composerPost && (composerPost.get('id') !== this.get('post.id'))) {
|
||||||
|
@ -20,7 +20,7 @@ Discourse.StaticController = Discourse.Controller.extend({
|
|||||||
text = text[1];
|
text = text[1];
|
||||||
this.set('content', text);
|
this.set('content', text);
|
||||||
} else {
|
} else {
|
||||||
return Discourse.ajax(path).then(function (result) {
|
return Discourse.ajax(path, {dataType: 'html'}).then(function (result) {
|
||||||
staticController.set('content', result);
|
staticController.set('content', result);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -198,41 +198,6 @@ Discourse.TopicController = Discourse.ObjectController.extend(Discourse.Selected
|
|||||||
Discourse.URL.routeTo(this.get('lastPostUrl'));
|
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
|
Toggle a participant for filtering
|
||||||
@ -336,25 +301,60 @@ Discourse.TopicController = Discourse.ObjectController.extend(Discourse.Selected
|
|||||||
var composerController = this.get('controllers.composer');
|
var composerController = this.get('controllers.composer');
|
||||||
var quoteController = this.get('controllers.quoteButton');
|
var quoteController = this.get('controllers.quoteButton');
|
||||||
var quotedText = Discourse.BBCode.buildQuoteBBCode(quoteController.get('post'), quoteController.get('buffer'));
|
var quotedText = Discourse.BBCode.buildQuoteBBCode(quoteController.get('post'), quoteController.get('buffer'));
|
||||||
|
|
||||||
|
var topic = post ? post.get('topic') : this.get('model');
|
||||||
|
|
||||||
quoteController.set('buffer', '');
|
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.get('content.action') === Discourse.Composer.REPLY) {
|
||||||
composerController.set('content.post', post);
|
composerController.set('content.post', post);
|
||||||
composerController.set('content.composeState', Discourse.Composer.OPEN);
|
composerController.set('content.composeState', Discourse.Composer.OPEN);
|
||||||
composerController.appendText(quotedText);
|
composerController.appendText(quotedText);
|
||||||
} else {
|
} else {
|
||||||
var promise = composerController.open({
|
|
||||||
post: post,
|
var opts = {
|
||||||
action: Discourse.Composer.REPLY,
|
action: Discourse.Composer.REPLY,
|
||||||
draftKey: post.get('topic.draft_key'),
|
draftKey: topic.get('draft_key'),
|
||||||
draftSequence: post.get('topic.draft_sequence')
|
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); });
|
promise.then(function() { composerController.appendText(quotedText); });
|
||||||
}
|
}
|
||||||
return false;
|
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
|
// Edits a post
|
||||||
editPost: function(post) {
|
editPost: function(post) {
|
||||||
this.get('controllers.composer').open({
|
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() {
|
init: function() {
|
||||||
this._super();
|
this._super();
|
||||||
this.set("availableGroups", Em.A(this.get("available_groups")));
|
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() {
|
searchContext: function() {
|
||||||
return ({ type: 'category', id: this.get('id'), category: this });
|
return ({ type: 'category', id: this.get('id'), category: this });
|
||||||
}.property('id'),
|
}.property('id'),
|
||||||
@ -43,33 +55,49 @@ Discourse.Category = Discourse.Model.extend({
|
|||||||
text_color: this.get('text_color'),
|
text_color: this.get('text_color'),
|
||||||
hotness: this.get('hotness'),
|
hotness: this.get('hotness'),
|
||||||
secure: this.get('secure'),
|
secure: this.get('secure'),
|
||||||
group_names: this.get('groups').join(","),
|
permissions: this.get('permissionsForUpdate'),
|
||||||
auto_close_days: this.get('auto_close_days')
|
auto_close_days: this.get('auto_close_days')
|
||||||
},
|
},
|
||||||
type: this.get('id') ? 'PUT' : 'POST'
|
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) {
|
destroy: function(callback) {
|
||||||
return Discourse.ajax("/categories/" + (this.get('slug') || this.get('id')), { type: 'DELETE' });
|
return Discourse.ajax("/categories/" + (this.get('slug') || this.get('id')), { type: 'DELETE' });
|
||||||
},
|
},
|
||||||
|
|
||||||
addGroup: function(group){
|
addPermission: function(permission){
|
||||||
this.get("groups").addObject(group);
|
this.get("permissions").addObject(permission);
|
||||||
this.get("availableGroups").removeObject(group);
|
this.get("availableGroups").removeObject(permission.group_name);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
removeGroup: function(group){
|
removePermission: function(permission){
|
||||||
this.get("groups").removeObject(group);
|
this.get("permissions").removeObject(permission);
|
||||||
this.get("availableGroups").addObject(group);
|
this.get("availableGroups").addObject(permission.group_name);
|
||||||
},
|
},
|
||||||
|
|
||||||
// note, this is used in a data attribute, data attributes get downcased
|
// note, this is used in a data attribute, data attributes get downcased
|
||||||
// to avoid confusion later on using this naming here.
|
// to avoid confusion later on using this naming here.
|
||||||
description_text: function(){
|
description_text: function(){
|
||||||
return $("<div>" + this.get("description") + "</div>").text();
|
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({
|
Discourse.Post = Discourse.Model.extend({
|
||||||
|
|
||||||
shareUrl: function() {
|
shareUrl: function() {
|
||||||
if (this.get('firstPost')) return this.get('topic.url');
|
|
||||||
var user = Discourse.User.current();
|
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'),
|
}.property('url'),
|
||||||
|
|
||||||
new_user: Em.computed.equal('trust_level', 0),
|
new_user: Em.computed.equal('trust_level', 0),
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
**/
|
**/
|
||||||
Discourse.RestrictedUserRoute = Discourse.Route.extend({
|
Discourse.RestrictedUserRoute = Discourse.Route.extend({
|
||||||
|
|
||||||
redirect: function() {
|
afterModel: function() {
|
||||||
var user = this.modelFor('user');
|
var user = this.modelFor('user');
|
||||||
if (!user.get('can_edit')) {
|
if (!user.get('can_edit')) {
|
||||||
this.transitionTo('user.activity', user);
|
this.transitionTo('user.activity', user);
|
||||||
|
@ -6,13 +6,16 @@
|
|||||||
@namespace Discourse
|
@namespace Discourse
|
||||||
@module Discourse
|
@module Discourse
|
||||||
**/
|
**/
|
||||||
Discourse.ListCategoriesRoute = Discourse.Route.extend(Discourse.ModelReady, {
|
Discourse.ListCategoriesRoute = Discourse.Route.extend({
|
||||||
|
|
||||||
redirect: function() { Discourse.redirectIfLoginRequired(this); },
|
redirect: function() { Discourse.redirectIfLoginRequired(this); },
|
||||||
|
|
||||||
events: {
|
events: {
|
||||||
createCategory: function() {
|
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');
|
this.controllerFor('editCategory').set('selectedTab', 'general');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -28,9 +31,11 @@ Discourse.ListCategoriesRoute = Discourse.Route.extend(Discourse.ModelReady, {
|
|||||||
this.controllerFor('list').set('canCreateCategory', false);
|
this.controllerFor('list').set('canCreateCategory', false);
|
||||||
},
|
},
|
||||||
|
|
||||||
modelReady: function(controller, categoryList) {
|
renderTemplate: function() {
|
||||||
this.render('listCategories', { into: 'list', outlet: 'listView' });
|
this.render('listCategories', { into: 'list', outlet: 'listView' });
|
||||||
|
},
|
||||||
|
|
||||||
|
afterModel: function(categoryList) {
|
||||||
this.controllerFor('list').setProperties({
|
this.controllerFor('list').setProperties({
|
||||||
canCreateCategory: categoryList.get('can_create_category'),
|
canCreateCategory: categoryList.get('can_create_category'),
|
||||||
canCreateTopic: categoryList.get('can_create_topic'),
|
canCreateTopic: categoryList.get('can_create_topic'),
|
||||||
|
@ -9,9 +9,11 @@
|
|||||||
Discourse.StaticController.pages.forEach(function(page) {
|
Discourse.StaticController.pages.forEach(function(page) {
|
||||||
|
|
||||||
Discourse[(page.capitalize()) + "Route"] = Discourse.Route.extend({
|
Discourse[(page.capitalize()) + "Route"] = Discourse.Route.extend({
|
||||||
|
|
||||||
renderTemplate: function() {
|
renderTemplate: function() {
|
||||||
this.render('static');
|
this.render('static');
|
||||||
},
|
},
|
||||||
|
|
||||||
setupController: function() {
|
setupController: function() {
|
||||||
var config_key = Discourse.StaticController.configs[page];
|
var config_key = Discourse.StaticController.configs[page];
|
||||||
if (config_key && Discourse.SiteSettings[config_key].length > 0) {
|
if (config_key && Discourse.SiteSettings[config_key].length > 0) {
|
||||||
@ -20,6 +22,7 @@ Discourse.StaticController.pages.forEach(function(page) {
|
|||||||
this.controllerFor('static').loadPath("/" + page);
|
this.controllerFor('static').loadPath("/" + page);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
@namespace Discourse
|
@namespace Discourse
|
||||||
@module Discourse
|
@module Discourse
|
||||||
**/
|
**/
|
||||||
Discourse.UserInvitedRoute = Discourse.Route.extend(Discourse.ModelReady, {
|
Discourse.UserInvitedRoute = Discourse.Route.extend({
|
||||||
|
|
||||||
renderTemplate: function() {
|
renderTemplate: function() {
|
||||||
this.render({ into: 'user', outlet: 'userOutlet' });
|
this.render({ into: 'user', outlet: 'userOutlet' });
|
||||||
|
@ -85,7 +85,7 @@
|
|||||||
</li>
|
</li>
|
||||||
<li class='current-user'>
|
<li class='current-user'>
|
||||||
{{#if currentUser}}
|
{{#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}}
|
{{else}}
|
||||||
<div class="icon not-logged-in-avatar" {{action showLogin}}><i class='icon-user'></i></div>
|
<div class="icon not-logged-in-avatar" {{action showLogin}}><i class='icon-user'></i></div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
@ -110,7 +110,7 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class='d-dropdown' id='site-map-dropdown'>
|
<section class='d-dropdown' id='site-map-dropdown'>
|
||||||
<ul>
|
<ul class="location-links">
|
||||||
{{#if currentUser.staff}}
|
{{#if currentUser.staff}}
|
||||||
<li><a href="/admin"><i class='icon icon-wrench'></i>{{i18n admin_title}}</a></li>
|
<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>
|
<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}}
|
{{#titledLinkTo "list.latest" titleKey="filters.latest.help"}}{{i18n filters.latest.title}}{{/titledLinkTo}}
|
||||||
</li>
|
</li>
|
||||||
<li>{{faqLink}}</li>
|
<li>{{faqLink}}</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
{{#if categories}}
|
{{#if categories}}
|
||||||
|
<ul class="category-links">
|
||||||
<li class='heading' title="{{i18n filters.categories.help}}">
|
<li class='heading' title="{{i18n filters.categories.help}}">
|
||||||
{{#linkTo "list.categories"}}{{i18n filters.categories.title}}{{/linkTo}}
|
{{#linkTo "list.categories"}}{{i18n filters.categories.title}}{{/linkTo}}
|
||||||
</li>
|
</li>
|
||||||
@ -135,9 +137,9 @@
|
|||||||
<b>{{unbound topic_count}}</b></a>
|
<b>{{unbound topic_count}}</b></a>
|
||||||
</li>
|
</li>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{/if}}
|
</ul>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
</ul>
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -62,7 +62,7 @@
|
|||||||
<a href='#' {{action createTopic}}>{{i18n topic.suggest_create_topic}}</a>
|
<a href='#' {{action createTopic}}>{{i18n topic.suggest_create_topic}}</a>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{else}}
|
{{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}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</h3>
|
</h3>
|
||||||
|
@ -60,25 +60,18 @@
|
|||||||
{{#unless isUncategorized}}
|
{{#unless isUncategorized}}
|
||||||
<div {{bindAttr class=":modal-tab :options-tab securitySelected::invisible"}}>
|
<div {{bindAttr class=":modal-tab :options-tab securitySelected::invisible"}}>
|
||||||
<section class='field'>
|
<section class='field'>
|
||||||
<label>
|
<ul class='permission-list'>
|
||||||
{{input type="checkbox" checked=secure}}
|
{{#each permissions}}
|
||||||
{{i18n category.is_secure}}
|
<li>
|
||||||
</label>
|
<span class="name"><span class="badge-group">{{group_name}}</span></span>
|
||||||
{{#if secure}}
|
<span class="permission">{{permission.description}}</span>
|
||||||
<div class="secure-category-options">
|
<a {{action removePermission this}}><i class="icon icon-remove-sign"></i></a>
|
||||||
<label>{{i18n category.allowed_groups}}</label>
|
</li>
|
||||||
<ul class="badge-list">
|
{{/each}}
|
||||||
{{#each groups}}
|
</ul>
|
||||||
<li class="badge-group">
|
{{view Ember.Select contentBinding="availableGroups" valueBinding="selectedGroup"}}
|
||||||
{{this}}
|
{{view Ember.Select class="permission-selector" optionValuePath="content.id" optionLabelPath="content.description" contentBinding="availablePermissions" valueBinding="selectedPermission"}}
|
||||||
<a {{action removeGroup this}}><i class="icon icon-remove-sign"></i></a>
|
<button {{action addPermission selectedGroup selectedPermission}} class="btn btn-small">{{i18n category.add_group}}</button>
|
||||||
</li>
|
|
||||||
{{/each}}
|
|
||||||
</ul>
|
|
||||||
{{view Ember.Select contentBinding="availableGroups" valueBinding="selectedGroup"}}
|
|
||||||
<button {{action addGroup}} class="btn btn-small">{{i18n category.add_group}}</button>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<div {{bindAttr class=":modal-tab :options-tab settingsSelected::invisible"}}>
|
<div {{bindAttr class=":modal-tab :options-tab settingsSelected::invisible"}}>
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
<div class='quote-controls'></div>
|
<div class='quote-controls'></div>
|
||||||
{{{avatarImg}}}
|
{{{avatarImg}}}
|
||||||
{{username}}
|
{{username}}
|
||||||
said:
|
|
||||||
</div>
|
</div>
|
||||||
<blockquote>{{{quote}}}</blockquote>
|
<blockquote>{{{quote}}}</blockquote>
|
||||||
</aside>
|
</aside>
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
<dt>{{i18n user.last_seen}}:</dt><dd>{{date last_seen_at}}</dd>
|
<dt>{{i18n user.last_seen}}:</dt><dd>{{date last_seen_at}}</dd>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if invited_by}}
|
{{#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}}
|
||||||
{{#if email}}
|
{{#if email}}
|
||||||
<dt>{{i18n user.email.title}}:</dt><dd {{bindAttr title="email"}}>{{email}}</dd>
|
<dt>{{i18n user.email.title}}:</dt><dd {{bindAttr title="email"}}>{{email}}</dd>
|
||||||
|
@ -14,7 +14,10 @@ Discourse.CategoryChooserView = Discourse.ComboboxView.extend({
|
|||||||
|
|
||||||
init: function() {
|
init: function() {
|
||||||
this._super();
|
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() {
|
none: function() {
|
||||||
|
@ -37,11 +37,13 @@ Discourse.ActivityFilterView = Discourse.View.extend({
|
|||||||
}
|
}
|
||||||
|
|
||||||
var icon = this.get('icon');
|
var icon = this.get('icon');
|
||||||
|
|
||||||
|
buffer.push("<a href='#'>");
|
||||||
if(icon) {
|
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>");
|
" <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
|
// lib/handlebars/base.js
|
||||||
|
|
||||||
Handlebars.VERSION = "1.0.0-rc.4";
|
Handlebars.VERSION = "1.0.0";
|
||||||
Handlebars.COMPILER_REVISION = 3;
|
Handlebars.COMPILER_REVISION = 4;
|
||||||
|
|
||||||
Handlebars.REVISION_CHANGES = {
|
Handlebars.REVISION_CHANGES = {
|
||||||
1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it
|
1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it
|
||||||
2: '== 1.0.0-rc.3',
|
2: '== 1.0.0-rc.3',
|
||||||
3: '>= 1.0.0-rc.4'
|
3: '== 1.0.0-rc.4',
|
||||||
|
4: '>= 1.0.0'
|
||||||
};
|
};
|
||||||
|
|
||||||
Handlebars.helpers = {};
|
Handlebars.helpers = {};
|
||||||
@ -67,7 +68,7 @@ Handlebars.registerHelper('helperMissing', function(arg) {
|
|||||||
if(arguments.length === 2) {
|
if(arguments.length === 2) {
|
||||||
return undefined;
|
return undefined;
|
||||||
} else {
|
} 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 fn = options.fn, inverse = options.inverse;
|
||||||
var i = 0, ret = "", data;
|
var i = 0, ret = "", data;
|
||||||
|
|
||||||
|
var type = toString.call(context);
|
||||||
|
if(type === functionType) { context = context.call(this); }
|
||||||
|
|
||||||
if (options.data) {
|
if (options.data) {
|
||||||
data = Handlebars.createFrame(options.data);
|
data = Handlebars.createFrame(options.data);
|
||||||
}
|
}
|
||||||
@ -152,22 +156,25 @@ Handlebars.registerHelper('each', function(context, options) {
|
|||||||
return ret;
|
return ret;
|
||||||
});
|
});
|
||||||
|
|
||||||
Handlebars.registerHelper('if', function(context, options) {
|
Handlebars.registerHelper('if', function(conditional, options) {
|
||||||
var type = toString.call(context);
|
var type = toString.call(conditional);
|
||||||
if(type === functionType) { context = context.call(this); }
|
if(type === functionType) { conditional = conditional.call(this); }
|
||||||
|
|
||||||
if(!context || Handlebars.Utils.isEmpty(context)) {
|
if(!conditional || Handlebars.Utils.isEmpty(conditional)) {
|
||||||
return options.inverse(this);
|
return options.inverse(this);
|
||||||
} else {
|
} else {
|
||||||
return options.fn(this);
|
return options.fn(this);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Handlebars.registerHelper('unless', function(context, options) {
|
Handlebars.registerHelper('unless', function(conditional, options) {
|
||||||
return Handlebars.helpers['if'].call(this, context, {fn: options.inverse, inverse: options.fn});
|
return Handlebars.helpers['if'].call(this, conditional, {fn: options.inverse, inverse: options.fn});
|
||||||
});
|
});
|
||||||
|
|
||||||
Handlebars.registerHelper('with', function(context, options) {
|
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);
|
if (!Handlebars.Utils.isEmpty(context)) return options.fn(context);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -181,9 +188,9 @@ Handlebars.registerHelper('log', function(context, options) {
|
|||||||
var handlebars = (function(){
|
var handlebars = (function(){
|
||||||
var parser = {trace: function trace() { },
|
var parser = {trace: function trace() { },
|
||||||
yy: {},
|
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},
|
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:"OPEN_PARTIAL",28:"DATA",30:"STRING",31:"INTEGER",32:"BOOLEAN",35:"ID",36:"EQUALS",37:"PARTIAL_NAME",39:"SEP"},
|
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],[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]],
|
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,$$,_$) {
|
performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {
|
||||||
|
|
||||||
var $0 = $$.length - 1;
|
var $0 = $$.length - 1;
|
||||||
@ -224,7 +231,10 @@ case 17: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]);
|
|||||||
break;
|
break;
|
||||||
case 18: this.$ = $$[$0-1];
|
case 18: this.$ = $$[$0-1];
|
||||||
break;
|
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;
|
break;
|
||||||
case 20: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], true);
|
case 20: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], true);
|
||||||
break;
|
break;
|
||||||
@ -242,7 +252,7 @@ case 26: this.$ = [[$$[$0-1]], $$[$0]];
|
|||||||
break;
|
break;
|
||||||
case 27: this.$ = [[$$[$0]], null];
|
case 27: this.$ = [[$$[$0]], null];
|
||||||
break;
|
break;
|
||||||
case 28: this.$ = [[new yy.DataNode($$[$0])], null];
|
case 28: this.$ = [[$$[$0]], null];
|
||||||
break;
|
break;
|
||||||
case 29: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
|
case 29: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
|
||||||
break;
|
break;
|
||||||
@ -256,7 +266,7 @@ case 33: this.$ = new yy.IntegerNode($$[$0]);
|
|||||||
break;
|
break;
|
||||||
case 34: this.$ = new yy.BooleanNode($$[$0]);
|
case 34: this.$ = new yy.BooleanNode($$[$0]);
|
||||||
break;
|
break;
|
||||||
case 35: this.$ = new yy.DataNode($$[$0]);
|
case 35: this.$ = $$[$0];
|
||||||
break;
|
break;
|
||||||
case 36: this.$ = new yy.HashNode($$[$0]);
|
case 36: this.$ = new yy.HashNode($$[$0]);
|
||||||
break;
|
break;
|
||||||
@ -272,20 +282,26 @@ case 41: this.$ = [$$[$0-2], new yy.IntegerNode($$[$0])];
|
|||||||
break;
|
break;
|
||||||
case 42: this.$ = [$$[$0-2], new yy.BooleanNode($$[$0])];
|
case 42: this.$ = [$$[$0-2], new yy.BooleanNode($$[$0])];
|
||||||
break;
|
break;
|
||||||
case 43: this.$ = [$$[$0-2], new yy.DataNode($$[$0])];
|
case 43: this.$ = [$$[$0-2], $$[$0]];
|
||||||
break;
|
break;
|
||||||
case 44: this.$ = new yy.PartialNameNode($$[$0]);
|
case 44: this.$ = new yy.PartialNameNode($$[$0]);
|
||||||
break;
|
break;
|
||||||
case 45: this.$ = new yy.IdNode($$[$0]);
|
case 45: this.$ = new yy.PartialNameNode(new yy.StringNode($$[$0]));
|
||||||
break;
|
break;
|
||||||
case 46: $$[$0-2].push($$[$0]); this.$ = $$[$0-2];
|
case 46: this.$ = new yy.PartialNameNode(new yy.IntegerNode($$[$0]));
|
||||||
break;
|
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;
|
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]}],
|
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],25:[2,28],38:[2,26],57:[2,24]},
|
defaultActions: {17:[2,1]},
|
||||||
parseError: function parseError(str, hash) {
|
parseError: function parseError(str, hash) {
|
||||||
throw new Error(str);
|
throw new Error(str);
|
||||||
},
|
},
|
||||||
@ -584,7 +600,7 @@ case 3:
|
|||||||
break;
|
break;
|
||||||
case 4: yy_.yytext = yy_.yytext.substr(0, yy_.yyleng-4); this.popState(); return 15;
|
case 4: yy_.yytext = yy_.yytext.substr(0, yy_.yyleng-4); this.popState(); return 15;
|
||||||
break;
|
break;
|
||||||
case 5: this.begin("par"); return 24;
|
case 5: return 25;
|
||||||
break;
|
break;
|
||||||
case 6: return 16;
|
case 6: return 16;
|
||||||
break;
|
break;
|
||||||
@ -596,7 +612,7 @@ case 9: return 19;
|
|||||||
break;
|
break;
|
||||||
case 10: return 23;
|
case 10: return 23;
|
||||||
break;
|
break;
|
||||||
case 11: return 23;
|
case 11: return 22;
|
||||||
break;
|
break;
|
||||||
case 12: this.popState(); this.begin('com');
|
case 12: this.popState(); this.begin('com');
|
||||||
break;
|
break;
|
||||||
@ -604,48 +620,44 @@ case 13: yy_.yytext = yy_.yytext.substr(3,yy_.yyleng-5); this.popState(); return
|
|||||||
break;
|
break;
|
||||||
case 14: return 22;
|
case 14: return 22;
|
||||||
break;
|
break;
|
||||||
case 15: return 36;
|
case 15: return 37;
|
||||||
break;
|
break;
|
||||||
case 16: return 35;
|
case 16: return 36;
|
||||||
break;
|
break;
|
||||||
case 17: return 35;
|
case 17: return 36;
|
||||||
break;
|
break;
|
||||||
case 18: return 39;
|
case 18: return 40;
|
||||||
break;
|
break;
|
||||||
case 19: /*ignore whitespace*/
|
case 19: /*ignore whitespace*/
|
||||||
break;
|
break;
|
||||||
case 20: this.popState(); return 18;
|
case 20: this.popState(); return 24;
|
||||||
break;
|
break;
|
||||||
case 21: this.popState(); return 18;
|
case 21: this.popState(); return 18;
|
||||||
break;
|
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;
|
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;
|
break;
|
||||||
case 24: yy_.yytext = yy_.yytext.substr(1); return 28;
|
case 24: return 38;
|
||||||
break;
|
break;
|
||||||
case 25: return 32;
|
case 25: return 33;
|
||||||
break;
|
break;
|
||||||
case 26: return 32;
|
case 26: return 33;
|
||||||
break;
|
break;
|
||||||
case 27: return 31;
|
case 27: return 32;
|
||||||
break;
|
break;
|
||||||
case 28: return 35;
|
case 28: return 36;
|
||||||
break;
|
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;
|
break;
|
||||||
case 30: return 'INVALID';
|
case 30: return 'INVALID';
|
||||||
break;
|
break;
|
||||||
case 31: /*ignore whitespace*/
|
case 31: return 5;
|
||||||
break;
|
|
||||||
case 32: this.popState(); return 37;
|
|
||||||
break;
|
|
||||||
case 33: return 5;
|
|
||||||
break;
|
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.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,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.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;})()
|
return lexer;})()
|
||||||
parser.lexer = lexer;
|
parser.lexer = lexer;
|
||||||
function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser;
|
function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser;
|
||||||
@ -731,21 +743,24 @@ Handlebars.AST.HashNode = function(pairs) {
|
|||||||
|
|
||||||
Handlebars.AST.IdNode = function(parts) {
|
Handlebars.AST.IdNode = function(parts) {
|
||||||
this.type = "ID";
|
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++) {
|
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 (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 if (part === "..") { depth++; }
|
||||||
else { this.isScoped = true; }
|
else { this.isScoped = true; }
|
||||||
}
|
}
|
||||||
else { dig.push(part); }
|
else { dig.push(part); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.original = original;
|
||||||
this.parts = dig;
|
this.parts = dig;
|
||||||
this.string = dig.join('.');
|
this.string = dig.join('.');
|
||||||
this.depth = depth;
|
this.depth = depth;
|
||||||
@ -759,7 +774,7 @@ Handlebars.AST.IdNode = function(parts) {
|
|||||||
|
|
||||||
Handlebars.AST.PartialNameNode = function(name) {
|
Handlebars.AST.PartialNameNode = function(name) {
|
||||||
this.type = "PARTIAL_NAME";
|
this.type = "PARTIAL_NAME";
|
||||||
this.name = name;
|
this.name = name.original;
|
||||||
};
|
};
|
||||||
|
|
||||||
Handlebars.AST.DataNode = function(id) {
|
Handlebars.AST.DataNode = function(id) {
|
||||||
@ -769,13 +784,15 @@ Handlebars.AST.DataNode = function(id) {
|
|||||||
|
|
||||||
Handlebars.AST.StringNode = function(string) {
|
Handlebars.AST.StringNode = function(string) {
|
||||||
this.type = "STRING";
|
this.type = "STRING";
|
||||||
this.string = string;
|
this.original =
|
||||||
this.stringModeValue = string;
|
this.string =
|
||||||
|
this.stringModeValue = string;
|
||||||
};
|
};
|
||||||
|
|
||||||
Handlebars.AST.IntegerNode = function(integer) {
|
Handlebars.AST.IntegerNode = function(integer) {
|
||||||
this.type = "INTEGER";
|
this.type = "INTEGER";
|
||||||
this.integer = integer;
|
this.original =
|
||||||
|
this.integer = integer;
|
||||||
this.stringModeValue = Number(integer);
|
this.stringModeValue = Number(integer);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1162,7 +1179,15 @@ Compiler.prototype = {
|
|||||||
|
|
||||||
DATA: function(data) {
|
DATA: function(data) {
|
||||||
this.options.data = true;
|
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) {
|
STRING: function(string) {
|
||||||
@ -1361,8 +1386,9 @@ JavaScriptCompiler.prototype = {
|
|||||||
|
|
||||||
if (!this.isChild) {
|
if (!this.isChild) {
|
||||||
var namespace = this.namespace;
|
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 || {};"; }
|
if (this.options.data) { copies = copies + " data = data || {};"; }
|
||||||
out.push(copies);
|
out.push(copies);
|
||||||
} else {
|
} else {
|
||||||
@ -1391,7 +1417,9 @@ JavaScriptCompiler.prototype = {
|
|||||||
// Generate minimizer alias mappings
|
// Generate minimizer alias mappings
|
||||||
if (!this.isChild) {
|
if (!this.isChild) {
|
||||||
for (var alias in this.context.aliases) {
|
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
|
// Push the result of looking up `id` on the current data
|
||||||
lookupData: function(id) {
|
lookupData: function(id) {
|
||||||
this.push(this.nameLookup('data', id, 'data'));
|
this.push('data');
|
||||||
},
|
},
|
||||||
|
|
||||||
// [pushStringParam]
|
// [pushStringParam]
|
||||||
@ -1717,8 +1745,9 @@ JavaScriptCompiler.prototype = {
|
|||||||
this.context.aliases.helperMissing = 'helpers.helperMissing';
|
this.context.aliases.helperMissing = 'helpers.helperMissing';
|
||||||
|
|
||||||
var helper = this.lastHelper = this.setupHelper(paramSize, name, true);
|
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) {
|
this.replaceStack(function(name) {
|
||||||
return name + ' ? ' + name + '.call(' +
|
return name + ' ? ' + name + '.call(' +
|
||||||
helper.callParams + ") " + ": helperMissing.call(" +
|
helper.callParams + ") " + ": helperMissing.call(" +
|
||||||
@ -2163,6 +2192,16 @@ Handlebars.VM = {
|
|||||||
}
|
}
|
||||||
return programWrapper;
|
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,
|
programWithDepth: Handlebars.VM.programWithDepth,
|
||||||
noop: Handlebars.VM.noop,
|
noop: Handlebars.VM.noop,
|
||||||
compilerInfo: null
|
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) {
|
getAndRemove: function(key, finder) {
|
||||||
var preloadStore = this;
|
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) {
|
if (preloadStore.data[key]) {
|
||||||
var result = finder();
|
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 (finder) {
|
||||||
if (result.then) {
|
return Ember.Deferred.promise(function(promise) {
|
||||||
result.then(function(result) {
|
var result = finder();
|
||||||
return promise.resolve(result);
|
|
||||||
}, function(result) {
|
// If the finder returns a promise, we support that too
|
||||||
return promise.reject(result);
|
if (result.then) {
|
||||||
});
|
result.then(function(result) {
|
||||||
} else {
|
return promise.resolve(result);
|
||||||
promise.resolve(result);
|
}, function(result) {
|
||||||
}
|
return promise.reject(result);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
promise.resolve(null);
|
promise.resolve(result);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
|
return Ember.RSVP.resolve(null);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -82,7 +82,7 @@
|
|||||||
|
|
||||||
.badge-group {
|
.badge-group {
|
||||||
@extend %badge;
|
@extend %badge;
|
||||||
padding: 3px 2px 3px 8px;
|
padding: 3px 5px;
|
||||||
color: $black;
|
color: $black;
|
||||||
text-shadow: 0 1px 0 rgba($white, 0.2);
|
text-shadow: 0 1px 0 rgba($white, 0.2);
|
||||||
background-color: #ddd;
|
background-color: #ddd;
|
||||||
|
@ -55,6 +55,7 @@
|
|||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
|
|
||||||
.nav-stacked {
|
.nav-stacked {
|
||||||
|
position: relative;
|
||||||
@extend %nav;
|
@extend %nav;
|
||||||
border: 1px solid $nav-stacked-border-color;
|
border: 1px solid $nav-stacked-border-color;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@ -69,7 +70,7 @@
|
|||||||
}
|
}
|
||||||
> a {
|
> a {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 13px;
|
padding: 13px 13px 13px 30px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
@ -94,17 +95,11 @@
|
|||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-glyph {
|
|
||||||
a {
|
|
||||||
padding-left: 30px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.glyph {
|
.glyph {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
margin: 15px 0 0 0;
|
|
||||||
width: 30px;
|
width: 30px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
float: left;
|
position: absolute;
|
||||||
|
left: 2px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,7 +102,7 @@ class ApplicationController < ActionController::Base
|
|||||||
guardian.current_user.sync_notification_channel_position
|
guardian.current_user.sync_notification_channel_position
|
||||||
end
|
end
|
||||||
|
|
||||||
store_preloaded("site", Site.cached_json(current_user))
|
store_preloaded("site", Site.cached_json(guardian))
|
||||||
|
|
||||||
if current_user.present?
|
if current_user.present?
|
||||||
store_preloaded("currentUser", MultiJson.dump(CurrentUserSerializer.new(current_user, root: false)))
|
store_preloaded("currentUser", MultiJson.dump(CurrentUserSerializer.new(current_user, root: false)))
|
||||||
|
@ -52,16 +52,18 @@ class CategoriesController < ApplicationController
|
|||||||
[:name, :color, :text_color]
|
[:name, :color, :text_color]
|
||||||
end
|
end
|
||||||
|
|
||||||
def category_param_keys
|
|
||||||
[required_param_keys, :hotness, :secure, :group_names, :auto_close_days].flatten!
|
|
||||||
end
|
|
||||||
|
|
||||||
def category_params
|
def category_params
|
||||||
required_param_keys.each do |key|
|
required_param_keys.each do |key|
|
||||||
params.require(key)
|
params.require(key)
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
def fetch_category
|
def fetch_category
|
||||||
|
@ -27,6 +27,10 @@ class StaticController < ApplicationController
|
|||||||
file = "static/#{page}.en"
|
file = "static/#{page}.en"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if not lookup_context.find_all("#{file}.html").any?
|
||||||
|
file = "static/#{page}"
|
||||||
|
end
|
||||||
|
|
||||||
if lookup_context.find_all("#{file}.html").any?
|
if lookup_context.find_all("#{file}.html").any?
|
||||||
render file, layout: !request.xhr?, formats: [:html]
|
render file, layout: !request.xhr?, formats: [:html]
|
||||||
return
|
return
|
||||||
|
@ -60,22 +60,7 @@ class Users::OmniauthCallbacksController < ApplicationController
|
|||||||
auth_provider: "Twitter"
|
auth_provider: "Twitter"
|
||||||
}
|
}
|
||||||
|
|
||||||
if user_info
|
process_user_info(user_info, screen_name)
|
||||||
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
|
end
|
||||||
|
|
||||||
def create_or_sign_on_user_using_facebook(auth_token)
|
def create_or_sign_on_user_using_facebook(auth_token)
|
||||||
@ -265,24 +250,7 @@ class Users::OmniauthCallbacksController < ApplicationController
|
|||||||
auth_provider: "Github"
|
auth_provider: "Github"
|
||||||
}
|
}
|
||||||
|
|
||||||
if user_info
|
process_user_info(user_info, screen_name)
|
||||||
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
|
end
|
||||||
|
|
||||||
def create_or_sign_on_user_using_persona(auth_token)
|
def create_or_sign_on_user_using_persona(auth_token)
|
||||||
@ -319,6 +287,26 @@ class Users::OmniauthCallbacksController < ApplicationController
|
|||||||
|
|
||||||
private
|
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?
|
def invite_only?
|
||||||
SiteSetting.invite_only? && !@data[:authenticated]
|
SiteSetting.invite_only? && !@data[:authenticated]
|
||||||
end
|
end
|
||||||
|
@ -22,6 +22,7 @@ class Category < ActiveRecord::Base
|
|||||||
|
|
||||||
before_validation :ensure_slug
|
before_validation :ensure_slug
|
||||||
after_save :invalidate_site_cache
|
after_save :invalidate_site_cache
|
||||||
|
before_save :apply_permissions
|
||||||
after_create :create_category_definition
|
after_create :create_category_definition
|
||||||
after_create :publish_categories_list
|
after_create :publish_categories_list
|
||||||
after_destroy :invalidate_site_cache
|
after_destroy :invalidate_site_cache
|
||||||
@ -34,15 +35,52 @@ class Category < ActiveRecord::Base
|
|||||||
scope :secured, ->(guardian = nil) {
|
scope :secured, ->(guardian = nil) {
|
||||||
ids = guardian.secure_category_ids if guardian
|
ids = guardian.secure_category_ids if guardian
|
||||||
if ids.present?
|
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
|
else
|
||||||
where("NOT categories.secure")
|
where("NOT categories.read_restricted")
|
||||||
end
|
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'
|
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
|
# Internal: Update category stats: # of topics in past year, month, week for
|
||||||
# all categories.
|
# all categories.
|
||||||
@ -119,28 +157,69 @@ class Category < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def deny(group)
|
# will reset permission on a topic to a particular
|
||||||
if group == :all
|
# set.
|
||||||
self.secure = true
|
#
|
||||||
end
|
# 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
|
end
|
||||||
|
|
||||||
def allow(group)
|
def permissions=(permissions)
|
||||||
if group == :all
|
set_permissions(permissions)
|
||||||
self.secure = false
|
end
|
||||||
# this is kind of annoying, there should be a clean way of queuing this stuff
|
|
||||||
category_groups.destroy_all unless new_record?
|
def apply_permissions
|
||||||
else
|
if @permissions
|
||||||
groups.push(group)
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
def secure_group_ids
|
def secure_group_ids
|
||||||
if self.secure
|
if self.read_restricted?
|
||||||
groups.pluck("groups.id")
|
groups.pluck("groups.id")
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
||||||
# == Schema Information
|
# == Schema Information
|
||||||
@ -162,7 +241,7 @@ end
|
|||||||
# description :text
|
# description :text
|
||||||
# text_color :string(6) default("FFFFFF"), not null
|
# text_color :string(6) default("FFFFFF"), not null
|
||||||
# hotness :float default(5.0), 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
|
# auto_close_days :float
|
||||||
#
|
#
|
||||||
# Indexes
|
# Indexes
|
||||||
|
@ -1,16 +1,22 @@
|
|||||||
class CategoryGroup < ActiveRecord::Base
|
class CategoryGroup < ActiveRecord::Base
|
||||||
belongs_to :category
|
belongs_to :category
|
||||||
belongs_to :group
|
belongs_to :group
|
||||||
|
|
||||||
|
def self.permission_types
|
||||||
|
@permission_types ||= Enum.new(:full, :create_post, :readonly)
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# == Schema Information
|
# == Schema Information
|
||||||
#
|
#
|
||||||
# Table name: category_groups
|
# Table name: category_groups
|
||||||
#
|
#
|
||||||
# id :integer not null, primary key
|
# id :integer not null, primary key
|
||||||
# category_id :integer not null
|
# category_id :integer not null
|
||||||
# group_id :integer not null
|
# group_id :integer not null
|
||||||
# created_at :datetime not null
|
# created_at :datetime not null
|
||||||
# updated_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
|
validate :name_format_validator
|
||||||
|
|
||||||
AUTO_GROUPS = {
|
AUTO_GROUPS = {
|
||||||
|
:everyone => 0,
|
||||||
:admins => 1,
|
:admins => 1,
|
||||||
:moderators => 2,
|
:moderators => 2,
|
||||||
:staff => 3,
|
:staff => 3,
|
||||||
@ -34,6 +35,10 @@ class Group < ActiveRecord::Base
|
|||||||
group.save!
|
group.save!
|
||||||
end
|
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}")
|
group.name = I18n.t("groups.default_names.#{name}")
|
||||||
|
|
||||||
# don't allow shoddy localization to break this
|
# don't allow shoddy localization to break this
|
||||||
|
@ -59,6 +59,7 @@ end
|
|||||||
# created_at :datetime not null
|
# created_at :datetime not null
|
||||||
# updated_at :datetime not null
|
# updated_at :datetime not null
|
||||||
# deleted_at :datetime
|
# deleted_at :datetime
|
||||||
|
# deleted_by_id :integer
|
||||||
#
|
#
|
||||||
# Indexes
|
# Indexes
|
||||||
#
|
#
|
||||||
|
@ -412,6 +412,8 @@ end
|
|||||||
# percent_rank :float default(1.0)
|
# percent_rank :float default(1.0)
|
||||||
# notify_user_count :integer default(0), not null
|
# notify_user_count :integer default(0), not null
|
||||||
# like_score :integer default(0), not null
|
# like_score :integer default(0), not null
|
||||||
|
# deleted_by_id :integer
|
||||||
|
# nuked_user :boolean default(FALSE)
|
||||||
#
|
#
|
||||||
# Indexes
|
# Indexes
|
||||||
#
|
#
|
||||||
|
@ -63,9 +63,14 @@ class PostAnalyzer
|
|||||||
|
|
||||||
@linked_hosts = {}
|
@linked_hosts = {}
|
||||||
raw_links.each do |u|
|
raw_links.each do |u|
|
||||||
uri = URI.parse(u)
|
begin
|
||||||
host = uri.host
|
uri = URI.parse(u)
|
||||||
@linked_hosts[host] ||= 1
|
host = uri.host
|
||||||
|
@linked_hosts[host] ||= 1
|
||||||
|
rescue URI::InvalidURIError
|
||||||
|
# An invalid URI does not count as a raw link.
|
||||||
|
next
|
||||||
|
end
|
||||||
end
|
end
|
||||||
@linked_hosts
|
@linked_hosts
|
||||||
end
|
end
|
||||||
|
@ -25,11 +25,24 @@ class Site
|
|||||||
TrustLevel.all
|
TrustLevel.all
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def group_names
|
||||||
|
@group_name ||= Group.pluck(:name)
|
||||||
|
end
|
||||||
|
|
||||||
def categories
|
def categories
|
||||||
Category
|
@categories ||= begin
|
||||||
.secured(@guardian)
|
categories = Category
|
||||||
.latest
|
.secured(@guardian)
|
||||||
.includes(:topic_only_relative_url)
|
.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
|
end
|
||||||
|
|
||||||
def archetypes
|
def archetypes
|
||||||
|
@ -103,9 +103,9 @@ class Topic < ActiveRecord::Base
|
|||||||
# Query conditions
|
# Query conditions
|
||||||
condition =
|
condition =
|
||||||
if ids.present?
|
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
|
else
|
||||||
["NOT c.secure"]
|
["NOT c.read_restricted"]
|
||||||
end
|
end
|
||||||
|
|
||||||
where("category_id IS NULL OR category_id IN (
|
where("category_id IS NULL OR category_id IN (
|
||||||
@ -629,8 +629,8 @@ class Topic < ActiveRecord::Base
|
|||||||
self
|
self
|
||||||
end
|
end
|
||||||
|
|
||||||
def secure_category?
|
def read_restricted_category?
|
||||||
category && category.secure
|
category && category.read_restricted
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
@ -692,6 +692,7 @@ end
|
|||||||
# auto_close_at :datetime
|
# auto_close_at :datetime
|
||||||
# auto_close_user_id :integer
|
# auto_close_user_id :integer
|
||||||
# auto_close_started_at :datetime
|
# auto_close_started_at :datetime
|
||||||
|
# deleted_by_id :integer
|
||||||
#
|
#
|
||||||
# Indexes
|
# Indexes
|
||||||
#
|
#
|
||||||
|
@ -113,11 +113,11 @@ class TopicTrackingState
|
|||||||
((#{unread}) OR (#{new})) AND
|
((#{unread}) OR (#{new})) AND
|
||||||
(topics.visible OR u.admin OR u.moderator) AND
|
(topics.visible OR u.admin OR u.moderator) AND
|
||||||
topics.deleted_at IS NULL 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
|
SELECT c2.id FROM categories c2
|
||||||
JOIN category_groups cg ON cg.category_id = c2.id
|
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
|
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
|
SQL
|
||||||
|
@ -486,10 +486,14 @@ class User < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def secure_category_ids
|
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
|
cats.map { |c| c.id }.sort
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def topic_create_allowed_category_ids
|
||||||
|
Category.topic_create_allowed(self.id).select(:id)
|
||||||
|
end
|
||||||
|
|
||||||
# Flag all posts from a user as spam
|
# Flag all posts from a user as spam
|
||||||
def flag_linked_posts_as_spam
|
def flag_linked_posts_as_spam
|
||||||
admin = Discourse.system_user
|
admin = Discourse.system_user
|
||||||
@ -660,6 +664,7 @@ end
|
|||||||
# topic_reply_count :integer default(0), not null
|
# topic_reply_count :integer default(0), not null
|
||||||
# blocked :boolean default(FALSE)
|
# blocked :boolean default(FALSE)
|
||||||
# dynamic_favicon :boolean default(FALSE), not null
|
# dynamic_favicon :boolean default(FALSE), not null
|
||||||
|
# title :string(255)
|
||||||
#
|
#
|
||||||
# Indexes
|
# Indexes
|
||||||
#
|
#
|
||||||
|
@ -179,7 +179,7 @@ ORDER BY p.created_at desc
|
|||||||
|
|
||||||
# move into Topic perhaps
|
# move into Topic perhaps
|
||||||
group_ids = nil
|
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")
|
group_ids = topic.category.groups.pluck("groups.id")
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -232,11 +232,11 @@ ORDER BY p.created_at desc
|
|||||||
unless guardian.is_staff?
|
unless guardian.is_staff?
|
||||||
allowed = guardian.secure_category_ids
|
allowed = guardian.secure_category_ids
|
||||||
if allowed.present?
|
if allowed.present?
|
||||||
builder.where("( c.secure IS NULL OR
|
builder.where("( c.read_restricted IS NULL OR
|
||||||
c.secure = 'f' OR
|
NOT c.read_restricted OR
|
||||||
(c.secure = 't' and c.id in (:cats)) )", cats: guardian.secure_category_ids )
|
(c.read_restricted and c.id in (:cats)) )", cats: guardian.secure_category_ids )
|
||||||
else
|
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
|
end
|
||||||
end
|
end
|
||||||
|
@ -9,6 +9,7 @@ class BasicCategorySerializer < ApplicationSerializer
|
|||||||
:description,
|
:description,
|
||||||
:topic_url,
|
:topic_url,
|
||||||
:hotness,
|
:hotness,
|
||||||
:secure
|
:read_restricted,
|
||||||
|
:permission
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -1,13 +1,24 @@
|
|||||||
class CategorySerializer < BasicCategorySerializer
|
class CategorySerializer < BasicCategorySerializer
|
||||||
|
|
||||||
attributes :secure, :groups, :available_groups, :auto_close_days
|
attributes :read_restricted, :available_groups, :auto_close_days, :group_permissions
|
||||||
|
|
||||||
def groups
|
def group_permissions
|
||||||
@groups ||= object.groups.order("name").all.map(&:name)
|
@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
|
end
|
||||||
|
|
||||||
def available_groups
|
def available_groups
|
||||||
Group.order("name").map(&:name) - groups
|
Group.order(:name).pluck(:name) - group_permissions.map{|g| g[:group_name]}
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -3,7 +3,8 @@ class SiteSerializer < ApplicationSerializer
|
|||||||
attributes :default_archetype,
|
attributes :default_archetype,
|
||||||
:notification_types,
|
:notification_types,
|
||||||
:post_types,
|
:post_types,
|
||||||
:uncategorized_slug
|
:uncategorized_slug,
|
||||||
|
:group_names
|
||||||
|
|
||||||
|
|
||||||
has_many :categories, serializer: BasicCategorySerializer, embed: :objects
|
has_many :categories, serializer: BasicCategorySerializer, embed: :objects
|
||||||
|
@ -112,7 +112,7 @@ module Discourse
|
|||||||
# ember stuff only used for asset precompliation, production variant plays up
|
# ember stuff only used for asset precompliation, production variant plays up
|
||||||
config.ember.variant = :development
|
config.ember.variant = :development
|
||||||
config.ember.ember_location = "#{Rails.root}/app/assets/javascripts/external_production/ember.js"
|
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
|
# Since we are using strong_parameters, we can disable and remove
|
||||||
# attr_accessible.
|
# attr_accessible.
|
||||||
|
@ -88,9 +88,8 @@ predef:
|
|||||||
- visit
|
- visit
|
||||||
- count
|
- count
|
||||||
- exists
|
- exists
|
||||||
- asyncTest
|
- asyncTestDiscourse
|
||||||
- find
|
- find
|
||||||
- resolvingPromise
|
|
||||||
- sinon
|
- sinon
|
||||||
- controllerFor
|
- controllerFor
|
||||||
|
|
||||||
|
@ -12,7 +12,10 @@ cs:
|
|||||||
storage_units:
|
storage_units:
|
||||||
format: ! '%n %u'
|
format: ! '%n %u'
|
||||||
units:
|
units:
|
||||||
byte: B
|
byte:
|
||||||
|
one: bajt
|
||||||
|
few: bajty
|
||||||
|
other: bajtů
|
||||||
gb: GB
|
gb: GB
|
||||||
kb: KB
|
kb: KB
|
||||||
mb: MB
|
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."
|
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:
|
actions:
|
||||||
|
recover: "Vrátit téma"
|
||||||
delete: "Odstranit téma"
|
delete: "Odstranit téma"
|
||||||
open: "Otevřít téma"
|
open: "Otevřít téma"
|
||||||
close: "Zavřít téma"
|
close: "Zavřít téma"
|
||||||
@ -775,6 +779,7 @@ cs:
|
|||||||
continue_discussion: "Pokračující diskuze z {{postLink}}:"
|
continue_discussion: "Pokračující diskuze z {{postLink}}:"
|
||||||
follow_quote: "přejít na citovaný příspěvek"
|
follow_quote: "přejít na citovaný příspěvek"
|
||||||
deleted_by_author: "(příspěvek odstraněn autorem)"
|
deleted_by_author: "(příspěvek odstraněn autorem)"
|
||||||
|
deleted_by: "odstranil"
|
||||||
expand_collapse: "rozbalit/sbalit"
|
expand_collapse: "rozbalit/sbalit"
|
||||||
|
|
||||||
has_replies:
|
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."
|
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."
|
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_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?"
|
abandon: "Opravdu chcete opustit váš příspěvek?"
|
||||||
|
|
||||||
@ -998,6 +1004,7 @@ cs:
|
|||||||
views_long: "toto téma bylo zobrazeno {{number}}krát"
|
views_long: "toto téma bylo zobrazeno {{number}}krát"
|
||||||
activity: "Aktivita"
|
activity: "Aktivita"
|
||||||
likes: "Líbí se"
|
likes: "Líbí se"
|
||||||
|
likes_long: "je zde {{number}} 'líbí se' v tomto tématu"
|
||||||
top_contributors: "Účastníci"
|
top_contributors: "Účastníci"
|
||||||
category_title: "Kategorie"
|
category_title: "Kategorie"
|
||||||
history: "Historie"
|
history: "Historie"
|
||||||
@ -1064,6 +1071,8 @@ cs:
|
|||||||
critical_available: "Je k dispozici důležitá aktualizace."
|
critical_available: "Je k dispozici důležitá aktualizace."
|
||||||
updates_available: "Jsou k dispozici aktualizace."
|
updates_available: "Jsou k dispozici aktualizace."
|
||||||
please_upgrade: "Prosím aktualizujte!"
|
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"
|
installed_version: "Nainstalováno"
|
||||||
latest_version: "Poslední verze"
|
latest_version: "Poslední verze"
|
||||||
problems_found: "Byly nalezeny problémy s vaší instalací systému Discourse:"
|
problems_found: "Byly nalezeny problémy s vaší instalací systému Discourse:"
|
||||||
@ -1073,6 +1082,7 @@ cs:
|
|||||||
moderators: 'Moderátoři:'
|
moderators: 'Moderátoři:'
|
||||||
admins: 'Administrátoři:'
|
admins: 'Administrátoři:'
|
||||||
blocked: 'Blokováno:'
|
blocked: 'Blokováno:'
|
||||||
|
banned: 'Zakázáno:'
|
||||||
private_messages_short: "SZ"
|
private_messages_short: "SZ"
|
||||||
private_messages_title: "Soukromé zprávy"
|
private_messages_title: "Soukromé zprávy"
|
||||||
|
|
||||||
@ -1176,6 +1186,7 @@ cs:
|
|||||||
settings: "Nastavení"
|
settings: "Nastavení"
|
||||||
logs: "Záznamy"
|
logs: "Záznamy"
|
||||||
sent_at: "Odesláno"
|
sent_at: "Odesláno"
|
||||||
|
user: "Uživatel"
|
||||||
email_type: "Typ emailu"
|
email_type: "Typ emailu"
|
||||||
to_address: "Komu"
|
to_address: "Komu"
|
||||||
test_email_address: "testovací emailová adresa"
|
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."
|
not_found: "Bohužel uživatel s tímto jménem není v našem systému."
|
||||||
active: "Aktivní"
|
active: "Aktivní"
|
||||||
nav:
|
nav:
|
||||||
|
new: "Noví"
|
||||||
active: "Aktivní"
|
active: "Aktivní"
|
||||||
new: "Nový"
|
|
||||||
pending: "Čeká na schválení"
|
pending: "Čeká na schválení"
|
||||||
|
admins: "Administrátoři"
|
||||||
|
moderators: "Moderátoři"
|
||||||
|
banned: "Zakázaní"
|
||||||
|
blocked: "Blokovaní"
|
||||||
approved: "Schválen?"
|
approved: "Schválen?"
|
||||||
approved_selected:
|
approved_selected:
|
||||||
one: "schválit uživatele"
|
one: "schválit uživatele"
|
||||||
@ -1225,6 +1240,7 @@ cs:
|
|||||||
admins: 'Admininstrátoři'
|
admins: 'Admininstrátoři'
|
||||||
moderators: 'Moderátoři'
|
moderators: 'Moderátoři'
|
||||||
blocked: 'Blokovaní uživatelé'
|
blocked: 'Blokovaní uživatelé'
|
||||||
|
banned: "Zakázaní uživatelé"
|
||||||
|
|
||||||
user:
|
user:
|
||||||
ban_failed: "Nastala chyba při zakazování uživatele {{error}}"
|
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."
|
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."
|
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."
|
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:
|
site_content:
|
||||||
none: "Zvolte typ obsahu a můžete začít editovat."
|
none: "Zvolte typ obsahu a můžete začít editovat."
|
||||||
|
@ -168,6 +168,7 @@ en:
|
|||||||
"13": "Inbox"
|
"13": "Inbox"
|
||||||
|
|
||||||
user:
|
user:
|
||||||
|
said: "{{username}} said:"
|
||||||
profile: Profile
|
profile: Profile
|
||||||
mute: Mute
|
mute: Mute
|
||||||
edit: Edit Preferences
|
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>.'
|
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
|
# This section is exported to the javascript for i18n in the admin section
|
||||||
admin_js:
|
admin_js:
|
||||||
type_to_filter: "type to filter..."
|
type_to_filter: "type to filter..."
|
||||||
@ -1245,3 +1251,4 @@ en:
|
|||||||
title: 'Settings'
|
title: 'Settings'
|
||||||
reset: 'reset to default'
|
reset: 'reset to default'
|
||||||
none: 'none'
|
none: 'none'
|
||||||
|
|
||||||
|
@ -60,6 +60,9 @@ cs:
|
|||||||
rss_topics_in_category: "RSS feed témat z kategorie '%{category}'"
|
rss_topics_in_category: "RSS feed témat z kategorie '%{category}'"
|
||||||
author_wrote: "%{author} napsal:"
|
author_wrote: "%{author} napsal:"
|
||||||
private_message_abbrev: "SZ"
|
private_message_abbrev: "SZ"
|
||||||
|
rss_description:
|
||||||
|
latest: "Poslední témata"
|
||||||
|
hot: "Populární témata"
|
||||||
|
|
||||||
groups:
|
groups:
|
||||||
errors:
|
errors:
|
||||||
@ -139,6 +142,8 @@ cs:
|
|||||||
title: "vůdce"
|
title: "vůdce"
|
||||||
elder:
|
elder:
|
||||||
title: "starší"
|
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:
|
rate_limiter:
|
||||||
too_many_requests: "Děláte tuto akci příliš často. Prosím počkejte %{time_left} a zkuste to znovu."
|
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."
|
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."
|
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)"
|
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_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'"
|
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"
|
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_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"
|
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"
|
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"
|
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+"
|
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"
|
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?"
|
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_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)"
|
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_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."
|
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_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"
|
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_invitee_trust_level: "Výchozí věrohodnost pozvaných uživatelů (0-4)"
|
||||||
default_trust_level: "Výchozí věrohodnost 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_username: "Uživatelské jméno pro dotazování přes POP3S"
|
||||||
pop3s_polling_password: "Heslo 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:
|
notification_types:
|
||||||
|
@ -436,6 +436,9 @@ en:
|
|||||||
tos_miscellaneous:
|
tos_miscellaneous:
|
||||||
title: "Terms of Service: Miscellaneous"
|
title: "Terms of Service: Miscellaneous"
|
||||||
description: "The text for the Miscellaneous section of the Terms of Service."
|
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:
|
site_settings:
|
||||||
default_locale: "The default language of this Discourse instance (ISO 639-1 Code)"
|
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_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"
|
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"
|
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"
|
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"
|
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"
|
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;">
|
<table cellspacing="0" cellpadding="0" style="border: 1px solid #eee; -webkit-border-radius: 10px;">
|
||||||
<tr>
|
<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>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="padding: 10px; background-color: #f9f9f9">{{{quote}}}</td>
|
<td style="padding: 10px; background-color: #f9f9f9">{{{quote}}}</td>
|
||||||
|
@ -7,6 +7,7 @@ class Guardian
|
|||||||
def staff?; false; end
|
def staff?; false; end
|
||||||
def approved?; false; end
|
def approved?; false; end
|
||||||
def secure_category_ids; []; end
|
def secure_category_ids; []; end
|
||||||
|
def topic_create_allowed_category_ids; []; end
|
||||||
def has_trust_level?(level); false; end
|
def has_trust_level?(level); false; end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -237,8 +238,19 @@ class Guardian
|
|||||||
can_create_post?(parent)
|
can_create_post?(parent)
|
||||||
end
|
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)
|
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
|
end
|
||||||
|
|
||||||
def can_create_post_on_topic?(topic)
|
def can_create_post_on_topic?(topic)
|
||||||
@ -328,7 +340,7 @@ class Guardian
|
|||||||
topic.deleted_at.nil? &&
|
topic.deleted_at.nil? &&
|
||||||
|
|
||||||
# not secure, or I can see it
|
# 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 private, or I am allowed (or an admin)
|
||||||
(not(topic.private_message?) || authenticated? && (topic.all_allowed_users.where(id: @user.id).exists? || is_admin?))
|
(not(topic.private_message?) || authenticated? && (topic.all_allowed_users.where(id: @user.id).exists? || is_admin?))
|
||||||
@ -340,7 +352,7 @@ class Guardian
|
|||||||
end
|
end
|
||||||
|
|
||||||
def can_see_category?(category)
|
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
|
end
|
||||||
|
|
||||||
def can_vote?(post, opts={})
|
def can_vote?(post, opts={})
|
||||||
@ -378,6 +390,10 @@ class Guardian
|
|||||||
@secure_category_ids ||= @user.secure_category_ids
|
@secure_category_ids ||= @user.secure_category_ids
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def topic_create_allowed_category_ids
|
||||||
|
@topic_create_allowed_category_ids ||= @user.topic_create_allowed_category_ids
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def is_my_own?(obj)
|
def is_my_own?(obj)
|
||||||
|
@ -19,9 +19,9 @@ module Oneboxer
|
|||||||
case route[:controller]
|
case route[:controller]
|
||||||
when 'users'
|
when 'users'
|
||||||
user = User.where(username_lower: route[:username].downcase).first
|
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.merge! avatar: PrettyText.avatar_img(user.username, 'tiny'), username: user.username
|
||||||
args[:bio] = user.bio_cooked if user.bio_cooked.present?
|
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
|
post = Post.where(topic_id: route[:topic_id], post_number: route[:post_number].to_i).first
|
||||||
return nil unless post
|
return nil unless post
|
||||||
|
|
||||||
Guardian.new.ensure_can_see!(post)
|
return @url unless Guardian.new.can_see?(post)
|
||||||
|
|
||||||
topic = post.topic
|
topic = post.topic
|
||||||
slug = Slug.for(topic.title)
|
slug = Slug.for(topic.title)
|
||||||
@ -52,7 +52,8 @@ module Oneboxer
|
|||||||
topic = Topic.where(id: route[:topic_id].to_i).includes(:user).first
|
topic = Topic.where(id: route[:topic_id].to_i).includes(:user).first
|
||||||
return nil unless topic
|
return nil unless topic
|
||||||
|
|
||||||
Guardian.new.ensure_can_see!(topic)
|
return @url unless Guardian.new.can_see?(topic)
|
||||||
|
|
||||||
post = topic.posts.first
|
post = topic.posts.first
|
||||||
|
|
||||||
posters = topic.posters_summary.map do |p|
|
posters = topic.posters_summary.map do |p|
|
||||||
|
@ -116,7 +116,7 @@ class PostCreator
|
|||||||
protected
|
protected
|
||||||
|
|
||||||
def secure_group_ids(topic)
|
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
|
topic.category.secure_group_ids
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -46,6 +46,19 @@ module PrettyText
|
|||||||
|
|
||||||
|
|
||||||
class Helpers
|
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
|
# function here are available to v8
|
||||||
def avatar_template(username)
|
def avatar_template(username)
|
||||||
return "" unless username
|
return "" unless username
|
||||||
@ -90,6 +103,7 @@ module PrettyText
|
|||||||
|
|
||||||
@ctx.eval("var Discourse = {}; Discourse.SiteSettings = #{SiteSetting.client_settings_json};")
|
@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 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",
|
ctx_load( "app/assets/javascripts/discourse/components/bbcode.js",
|
||||||
"app/assets/javascripts/discourse/components/utilities.js",
|
"app/assets/javascripts/discourse/components/utilities.js",
|
||||||
|
@ -160,9 +160,9 @@ class Search
|
|||||||
.order("topics.bumped_at DESC")
|
.order("topics.bumped_at DESC")
|
||||||
|
|
||||||
if secure_category_ids.present?
|
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
|
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
|
end
|
||||||
posts.limit(limit)
|
posts.limit(limit)
|
||||||
end
|
end
|
||||||
|
@ -18,9 +18,9 @@ class SqlBuilder
|
|||||||
|
|
||||||
def secure_category(secure_category_ids, category_alias = 'c')
|
def secure_category(secure_category_ids, category_alias = 'c')
|
||||||
if secure_category_ids.present?
|
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
|
else
|
||||||
where("NOT COALESCE(" << category_alias << ".secure, false)")
|
where("NOT COALESCE(" << category_alias << ".read_restricted, false)")
|
||||||
end
|
end
|
||||||
self
|
self
|
||||||
end
|
end
|
||||||
|
@ -27,9 +27,9 @@ class TopicCreator
|
|||||||
topic_params[:archetype] = @opts[:archetype] if @opts[:archetype].present?
|
topic_params[:archetype] = @opts[:archetype] if @opts[:archetype].present?
|
||||||
topic_params[:subtype] = @opts[:subtype] if @opts[:subtype].present?
|
topic_params[:subtype] = @opts[:subtype] if @opts[:subtype].present?
|
||||||
|
|
||||||
@guardian.ensure_can_create!(Topic)
|
|
||||||
|
|
||||||
category = Category.where(name: @opts[:category]).first
|
category = Category.where(name: @opts[:category]).first
|
||||||
|
|
||||||
|
@guardian.ensure_can_create!(Topic,category)
|
||||||
topic_params[:category_id] = category.id if category.present?
|
topic_params[:category_id] = category.id if category.present?
|
||||||
topic_params[:meta_data] = @opts[:meta_data] if @opts[:meta_data].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?
|
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?
|
unless @user && @user.moderator?
|
||||||
category_ids = @user.secure_category_ids if @user
|
category_ids = @user.secure_category_ids if @user
|
||||||
if category_ids.present?
|
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
|
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
|
||||||
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)
|
cat = Fabricate(:category)
|
||||||
topic = Fabricate(:topic, category: cat)
|
topic = Fabricate(:topic, category: cat)
|
||||||
cat.deny(:all)
|
cat.set_permissions(:admins => :full)
|
||||||
cat.allow(Group[:admins])
|
|
||||||
cat.save
|
cat.save
|
||||||
|
|
||||||
CategoryList.new(Guardian.new admin).categories.count.should == 1
|
CategoryList.new(Guardian.new admin).categories.count.should == 1
|
||||||
|
@ -215,8 +215,9 @@ describe Guardian do
|
|||||||
|
|
||||||
it 'correctly handles groups' do
|
it 'correctly handles groups' do
|
||||||
group = Fabricate(:group)
|
group = Fabricate(:group)
|
||||||
category = Fabricate(:category, secure: true)
|
category = Fabricate(:category, read_restricted: true)
|
||||||
category.allow(group)
|
category.set_permissions(group => :full)
|
||||||
|
category.save
|
||||||
|
|
||||||
topic = Fabricate(:topic, category: category)
|
topic = Fabricate(:topic, category: category)
|
||||||
|
|
||||||
@ -275,8 +276,27 @@ describe Guardian do
|
|||||||
end
|
end
|
||||||
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
|
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
|
it "is false when not logged in" do
|
||||||
Guardian.new.can_create?(Post, topic).should be_false
|
Guardian.new.can_create?(Post, topic).should be_false
|
||||||
end
|
end
|
||||||
|
@ -62,8 +62,7 @@ describe PostCreator do
|
|||||||
admin = Fabricate(:admin)
|
admin = Fabricate(:admin)
|
||||||
|
|
||||||
cat = Fabricate(:category)
|
cat = Fabricate(:category)
|
||||||
cat.deny(:all)
|
cat.set_permissions(:admins => :full)
|
||||||
cat.allow(Group[:admins])
|
|
||||||
cat.save
|
cat.save
|
||||||
|
|
||||||
created_post = nil
|
created_post = nil
|
||||||
|
@ -15,15 +15,15 @@ test
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "produces a quote even with new lines in it" do
|
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
|
end
|
||||||
|
|
||||||
it "should produce a quote" do
|
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
|
end
|
||||||
|
|
||||||
it "trims spaces on quote params" do
|
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
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -171,8 +171,7 @@ describe Search do
|
|||||||
topic.category_id = category.id
|
topic.category_id = category.id
|
||||||
topic.save
|
topic.save
|
||||||
|
|
||||||
category.deny(:all)
|
category.set_permissions(:staff => :full)
|
||||||
category.allow(Group[:staff])
|
|
||||||
category.save
|
category.save
|
||||||
|
|
||||||
result(nil).should_not be_present
|
result(nil).should_not be_present
|
||||||
@ -211,7 +210,7 @@ describe Search do
|
|||||||
r[:title].should == category.name
|
r[:title].should == category.name
|
||||||
r[:url].should == "/category/#{category.slug}"
|
r[:url].should == "/category/#{category.slug}"
|
||||||
|
|
||||||
category.deny(:all)
|
category.set_permissions({})
|
||||||
category.save
|
category.save
|
||||||
|
|
||||||
result.should_not be_present
|
result.should_not be_present
|
||||||
|
@ -14,9 +14,8 @@ describe TopicQuery do
|
|||||||
context 'secure category' do
|
context 'secure category' do
|
||||||
it "filters categories out correctly" do
|
it "filters categories out correctly" do
|
||||||
category = Fabricate(:category)
|
category = Fabricate(:category)
|
||||||
category.deny(:all)
|
|
||||||
group = Fabricate(:group)
|
group = Fabricate(:group)
|
||||||
category.allow(group)
|
category.set_permissions(group => :full)
|
||||||
category.save
|
category.save
|
||||||
|
|
||||||
topic = Fabricate(:topic, category: category)
|
topic = Fabricate(:topic, category: category)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
describe Admin::GroupsController do
|
describe Admin::GroupsController do
|
||||||
|
|
||||||
it "is a subclass of AdminController" do
|
it "is a subclass of AdminController" do
|
||||||
(Admin::GroupsController < Admin::AdminController).should be_true
|
(Admin::GroupsController < Admin::AdminController).should be_true
|
||||||
end
|
end
|
||||||
@ -13,7 +14,7 @@ describe Admin::GroupsController do
|
|||||||
|
|
||||||
xhr :get, :index
|
xhr :get, :index
|
||||||
response.status.should == 200
|
response.status.should == 200
|
||||||
::JSON.parse(response.body).should == [{
|
::JSON.parse(response.body).keep_if{|r| r["id"] == group.id}.should == [{
|
||||||
"id"=>group.id,
|
"id"=>group.id,
|
||||||
"name"=>group.name,
|
"name"=>group.name,
|
||||||
"user_count"=>1,
|
"user_count"=>1,
|
||||||
@ -36,7 +37,7 @@ describe Admin::GroupsController do
|
|||||||
xhr :delete, :destroy, id: group.id
|
xhr :delete, :destroy, id: group.id
|
||||||
response.status.should == 200
|
response.status.should == 200
|
||||||
|
|
||||||
Group.count.should == 0
|
Group.where(id: group.id).count.should == 0
|
||||||
end
|
end
|
||||||
|
|
||||||
it "is able to create a group" do
|
it "is able to create a group" do
|
||||||
@ -49,7 +50,7 @@ describe Admin::GroupsController do
|
|||||||
|
|
||||||
response.status.should == 200
|
response.status.should == 200
|
||||||
|
|
||||||
groups = Group.all.to_a
|
groups = Group.where(name: "bob").to_a
|
||||||
|
|
||||||
groups.count.should == 1
|
groups.count.should == 1
|
||||||
groups[0].usernames.should == a.username
|
groups[0].usernames.should == a.username
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
require 'spec_helper'
|
require "spec_helper"
|
||||||
|
|
||||||
describe CategoriesController do
|
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)
|
lambda { xhr :post, :create }.should raise_error(Discourse::NotLoggedIn)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'logged in' do
|
describe "logged in" do
|
||||||
before do
|
before do
|
||||||
@user = log_in(:moderator)
|
@user = log_in(:moderator)
|
||||||
end
|
end
|
||||||
@ -18,55 +18,66 @@ describe CategoriesController do
|
|||||||
response.should be_forbidden
|
response.should be_forbidden
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'raises an exception when the name is missing' do
|
it "raises an exception when the name is missing" do
|
||||||
lambda { xhr :post, :create, color: 'ff0', text_color: 'fff' }.should raise_error(ActionController::ParameterMissing)
|
lambda { xhr :post, :create, color: "ff0", text_color: "fff" }.should raise_error(ActionController::ParameterMissing)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'raises an exception when the color is missing' do
|
it "raises an exception when the color is missing" do
|
||||||
lambda { xhr :post, :create, name: 'hello', text_color: 'fff' }.should raise_error(ActionController::ParameterMissing)
|
lambda { xhr :post, :create, name: "hello", text_color: "fff" }.should raise_error(ActionController::ParameterMissing)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'raises an exception when the text color is missing' do
|
it "raises an exception when the text color is missing" do
|
||||||
lambda { xhr :post, :create, name: 'hello', color: 'ff0' }.should raise_error(ActionController::ParameterMissing)
|
lambda { xhr :post, :create, name: "hello", color: "ff0" }.should raise_error(ActionController::ParameterMissing)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'failure' do
|
describe "failure" do
|
||||||
before do
|
before do
|
||||||
@category = Fabricate(:category, user: @user)
|
@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
|
end
|
||||||
|
|
||||||
it { should_not respond_with(:success) }
|
it { should_not respond_with(:success) }
|
||||||
|
|
||||||
it 'returns errors on a duplicate category name' do
|
it "returns errors on a duplicate category name" do
|
||||||
response.code.to_i.should == 422
|
response.status.should == 422
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
describe 'success' do
|
describe "success" do
|
||||||
before do
|
it "works" do
|
||||||
xhr :post, :create, name: 'hello', color: 'ff0', text_color: 'fff'
|
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
|
end
|
||||||
|
|
||||||
it 'creates a category' do
|
|
||||||
Category.count.should == 1
|
|
||||||
end
|
|
||||||
|
|
||||||
it { should respond_with(:success) }
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'destroy' do
|
describe "destroy" do
|
||||||
|
|
||||||
it 'requires the user to be logged in' do
|
it "requires the user to be logged in" do
|
||||||
lambda { xhr :delete, :destroy, id: 'category'}.should raise_error(Discourse::NotLoggedIn)
|
lambda { xhr :delete, :destroy, id: "category"}.should raise_error(Discourse::NotLoggedIn)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'logged in' do
|
describe "logged in" do
|
||||||
before do
|
before do
|
||||||
@user = log_in
|
@user = log_in
|
||||||
@category = Fabricate(:category, user: @user)
|
@category = Fabricate(:category, user: @user)
|
||||||
@ -86,14 +97,14 @@ describe CategoriesController do
|
|||||||
|
|
||||||
end
|
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)
|
lambda { xhr :put, :update, id: 'category'}.should raise_error(Discourse::NotLoggedIn)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
describe 'logged in' do
|
describe "logged in" do
|
||||||
before do
|
before do
|
||||||
@user = log_in(:moderator)
|
@user = log_in(:moderator)
|
||||||
@category = Fabricate(:category, user: @user)
|
@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)
|
lambda { xhr :put, :update, id: @category.slug, name: 'asdf', color: 'fff' }.should raise_error(ActionController::ParameterMissing)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'failure' do
|
describe "failure" do
|
||||||
before do
|
before do
|
||||||
@other_category = Fabricate(:category, name: 'Other', user: @user )
|
@other_category = Fabricate(:category, name: "Other", user: @user )
|
||||||
xhr :put, :update, id: @category.id, name: @other_category.name, color: 'ff0', text_color: 'fff'
|
xhr :put, :update, id: @category.id, name: @other_category.name, color: "ff0", text_color: "fff"
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns errors on a duplicate category name' do
|
it "returns errors on a duplicate category name" do
|
||||||
response.should_not be_success
|
response.should_not be_success
|
||||||
end
|
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
|
response.code.to_i.should == 422
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'success' do
|
describe "success" do
|
||||||
before do
|
|
||||||
# might as well test this as well
|
|
||||||
@category.allow(Group[:admins])
|
|
||||||
@category.save
|
|
||||||
|
|
||||||
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
|
@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
|
end
|
||||||
end
|
end
|
||||||
|
@ -15,8 +15,7 @@ describe CategoryFeaturedTopic do
|
|||||||
# so much dancing, I am thinking fixures make sense here.
|
# so much dancing, I am thinking fixures make sense here.
|
||||||
user.change_trust_level!(:basic)
|
user.change_trust_level!(:basic)
|
||||||
|
|
||||||
category.deny(:all)
|
category.set_permissions(:trust_level_1 => :full)
|
||||||
category.allow(Group[:trust_level_1])
|
|
||||||
category.save
|
category.save
|
||||||
|
|
||||||
uncategorized_post = PostCreator.create(user, raw: "this is my new post 123 post", title: "hello world")
|
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 :category_featured_topics }
|
||||||
it { should have_many :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
|
describe "security" do
|
||||||
let(:category) { Fabricate(:category) }
|
let(:category) { Fabricate(:category) }
|
||||||
let(:category_2) { Fabricate(:category) }
|
let(:category_2) { Fabricate(:category) }
|
||||||
@ -25,20 +82,20 @@ describe Category do
|
|||||||
let(:group) { Fabricate(:group) }
|
let(:group) { Fabricate(:group) }
|
||||||
|
|
||||||
it "secures categories correctly" do
|
it "secures categories correctly" do
|
||||||
category.secure?.should be_false
|
category.read_restricted?.should be_false
|
||||||
|
|
||||||
category.deny(:all)
|
category.set_permissions({})
|
||||||
category.secure?.should be_true
|
category.read_restricted?.should be_true
|
||||||
|
|
||||||
category.allow(:all)
|
category.set_permissions(:everyone => :full)
|
||||||
category.secure?.should be_false
|
category.read_restricted?.should be_false
|
||||||
|
|
||||||
user.secure_categories.should be_empty
|
user.secure_categories.should be_empty
|
||||||
|
|
||||||
group.add(user)
|
group.add(user)
|
||||||
group.save
|
group.save
|
||||||
|
|
||||||
category.allow(group)
|
category.set_permissions(group.id => :full)
|
||||||
category.save
|
category.save
|
||||||
|
|
||||||
user.reload
|
user.reload
|
||||||
@ -47,13 +104,13 @@ describe Category do
|
|||||||
|
|
||||||
it "lists all secured categories correctly" do
|
it "lists all secured categories correctly" do
|
||||||
group.add(user)
|
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.secured.should =~ []
|
||||||
|
Category.secured(Guardian.new(user)).should =~ [category, category_2]
|
||||||
category_2.allow(group)
|
|
||||||
|
|
||||||
Category.secured.should =~ [category, category_2]
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -80,6 +80,11 @@ describe PostAnalyzer do
|
|||||||
post_analyzer = PostAnalyzer.new(raw_three_links, default_topic_id)
|
post_analyzer = PostAnalyzer.new(raw_three_links, default_topic_id)
|
||||||
post_analyzer.linked_hosts.should == {"discourse.org" => 1, "www.imdb.com" => 1}
|
post_analyzer.linked_hosts.should == {"discourse.org" => 1, "www.imdb.com" => 1}
|
||||||
end
|
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
|
||||||
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.topic_summary(Guardian.new, post.topic_id).count.should == 1
|
||||||
TopicLink.counts_for(Guardian.new, post.topic, [post]).length.should == 1
|
TopicLink.counts_for(Guardian.new, post.topic, [post]).length.should == 1
|
||||||
|
|
||||||
category.deny(:all)
|
category.set_permissions(:staff => :full)
|
||||||
category.allow(Group[:staff])
|
|
||||||
category.save
|
category.save
|
||||||
|
|
||||||
admin = Fabricate(:admin)
|
admin = Fabricate(:admin)
|
||||||
|
@ -192,7 +192,7 @@ describe Topic do
|
|||||||
context "secure categories" do
|
context "secure categories" do
|
||||||
|
|
||||||
let(:user) { Fabricate(:user) }
|
let(:user) { Fabricate(:user) }
|
||||||
let(:category) { Fabricate(:category, secure: true) }
|
let(:category) { Fabricate(:category, read_restricted: true) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
topic.category = category
|
topic.category = category
|
||||||
@ -1263,7 +1263,7 @@ describe Topic do
|
|||||||
|
|
||||||
describe 'secured' do
|
describe 'secured' do
|
||||||
it 'can remove secure groups' do
|
it 'can remove secure groups' do
|
||||||
category = Fabricate(:category, secure: true)
|
category = Fabricate(:category, read_restricted: true)
|
||||||
topic = Fabricate(:topic, category: category)
|
topic = Fabricate(:topic, category: category)
|
||||||
|
|
||||||
Topic.secured(Guardian.new(nil)).count.should == 0
|
Topic.secured(Guardian.new(nil)).count.should == 0
|
||||||
@ -1280,17 +1280,17 @@ describe Topic do
|
|||||||
let(:category){ Category.new }
|
let(:category){ Category.new }
|
||||||
|
|
||||||
it "is true if the category is secure" do
|
it "is true if the category is secure" do
|
||||||
category.stubs(:secure).returns(true)
|
category.stubs(:read_restricted).returns(true)
|
||||||
Topic.new(:category => category).should be_secure_category
|
Topic.new(:category => category).should be_read_restricted_category
|
||||||
end
|
end
|
||||||
|
|
||||||
it "is false if the category is not secure" do
|
it "is false if the category is not secure" do
|
||||||
category.stubs(:secure).returns(false)
|
category.stubs(:read_restricted).returns(false)
|
||||||
Topic.new(:category => category).should_not be_secure_category
|
Topic.new(:category => category).should_not be_read_restricted_category
|
||||||
end
|
end
|
||||||
|
|
||||||
it "is false if there is no category" do
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ describe TopicTrackingState do
|
|||||||
row.user_id.should == post.user_id
|
row.user_id.should == post.user_id
|
||||||
|
|
||||||
# when we have no permission to see a category, don't show its stats
|
# 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.category_id = category.id
|
||||||
post.topic.save
|
post.topic.save
|
||||||
|
@ -68,7 +68,7 @@ describe UserAction do
|
|||||||
|
|
||||||
# groups
|
# groups
|
||||||
|
|
||||||
category = Fabricate(:category, secure: true)
|
category = Fabricate(:category, read_restricted: true)
|
||||||
|
|
||||||
public_topic.recover!
|
public_topic.recover!
|
||||||
public_topic.category = category
|
public_topic.category = category
|
||||||
@ -82,7 +82,7 @@ describe UserAction do
|
|||||||
group.add(u)
|
group.add(u)
|
||||||
group.save
|
group.save
|
||||||
|
|
||||||
category.allow(group)
|
category.set_permissions(group => :full)
|
||||||
category.save
|
category.save
|
||||||
|
|
||||||
stats_for_user(u).should == [UserAction::NEW_TOPIC]
|
stats_for_user(u).should == [UserAction::NEW_TOPIC]
|
||||||
|
@ -77,21 +77,21 @@ test("quote formatting", function() {
|
|||||||
// TODO: This HTML matching is quite ugly.
|
// TODO: This HTML matching is quite ugly.
|
||||||
format("[quote=\"eviltrout, post:1, topic:1\"]abc[/quote]",
|
format("[quote=\"eviltrout, post:1, topic:1\"]abc[/quote]",
|
||||||
"</p><aside class='quote' data-post=\"1\" data-topic=\"1\" >\n <div class='title'>\n " +
|
"</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");
|
"renders quotes properly");
|
||||||
|
|
||||||
format("[quote=\"eviltrout, post:1, topic:1\"]abc[quote=\"eviltrout, post:2, topic:2\"]nested[/quote][/quote]",
|
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 " +
|
"</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-" +
|
"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");
|
"can nest quotes");
|
||||||
|
|
||||||
format("before[quote=\"eviltrout, post:1, topic:1\"]first[/quote]middle[quote=\"eviltrout, post:2, topic:2\"]second[/quote]after",
|
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" +
|
"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 " +
|
"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");
|
"can handle more than one quote");
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -69,13 +69,13 @@ test("Quotes", function() {
|
|||||||
cookedOptions("1[quote=\"bob, post:1\"]my quote[/quote]2",
|
cookedOptions("1[quote=\"bob, post:1\"]my quote[/quote]2",
|
||||||
{ topicId: 2, lookupAvatar: function(name) { return "" + name; } },
|
{ 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" +
|
"<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");
|
"handles quotes properly");
|
||||||
|
|
||||||
cookedOptions("1[quote=\"bob, post:1\"]my quote[/quote]2",
|
cookedOptions("1[quote=\"bob, post:1\"]my quote[/quote]2",
|
||||||
{ topicId: 2, lookupAvatar: function(name) { } },
|
{ 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" +
|
"<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");
|
"includes no avatar if none is found");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -4,17 +4,20 @@ module("Discourse.Onebox", {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Stops rapid calls with cache true", function() {
|
asyncTestDiscourse("Stops rapid calls with cache true", function() {
|
||||||
this.stub(Discourse, "ajax").returns(resolvingPromise);
|
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);
|
start();
|
||||||
Discourse.Onebox.load(this.anchor, true);
|
|
||||||
ok(Discourse.ajax.calledOnce);
|
ok(Discourse.ajax.calledOnce);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Stops rapid calls with cache false", function() {
|
asyncTestDiscourse("Stops rapid calls with cache true", function() {
|
||||||
this.stub(Discourse, "ajax").returns(resolvingPromise);
|
this.stub(Discourse, "ajax").returns(Ember.RSVP.resolve());
|
||||||
Discourse.Onebox.load(this.anchor, false);
|
Discourse.Onebox.load(this.anchor, false);
|
||||||
Discourse.Onebox.load(this.anchor, false);
|
Discourse.Onebox.load(this.anchor, false);
|
||||||
|
|
||||||
|
start();
|
||||||
ok(Discourse.ajax.calledOnce);
|
ok(Discourse.ajax.calledOnce);
|
||||||
});
|
});
|
||||||
|
@ -14,7 +14,7 @@ test("remove", function() {
|
|||||||
blank(PreloadStore.get('bane'), "removes the value if the key exists");
|
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);
|
expect(1);
|
||||||
|
|
||||||
PreloadStore.getAndRemove('joker').then(function(result) {
|
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);
|
expect(1);
|
||||||
|
|
||||||
var finder = function() { return 'batdance'; };
|
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);
|
expect(1);
|
||||||
|
|
||||||
var finder = function() {
|
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);
|
expect(1);
|
||||||
|
|
||||||
var finder = function() {
|
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);
|
expect(1);
|
||||||
|
|
||||||
PreloadStore.getAndRemove('bane').then(function(result) {
|
PreloadStore.getAndRemove('bane').then(function(result) {
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
// Test helpers
|
// Test helpers
|
||||||
var resolvingPromise = Ember.Deferred.promise(function (p) {
|
// var resolvingPromise = Ember.Deferred.promise(function (p) {
|
||||||
p.resolve();
|
// p.resolve();
|
||||||
});
|
// });
|
||||||
|
|
||||||
var resolvingPromiseWith = function(result) {
|
// var resolvingPromiseWith = function(result) {
|
||||||
return Ember.Deferred.promise(function (p) { p.resolve(result); });
|
// return Ember.Deferred.promise(function (p) { p.resolve(result); });
|
||||||
};
|
// };
|
||||||
|
|
||||||
function exists(selector) {
|
function exists(selector) {
|
||||||
return !!count(selector);
|
return !!count(selector);
|
||||||
|
@ -18,4 +18,15 @@ function controllerFor(controller, model) {
|
|||||||
var controller = Discourse.__container__.lookup('controller:' + controller);
|
var controller = Discourse.__container__.lookup('controller:' + controller);
|
||||||
if (model) { controller.set('model', model ); }
|
if (model) { controller.set('model', model ); }
|
||||||
return controller;
|
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;
|
sourceFile = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
return asyncTest(name, function() {
|
return asyncTestDiscourse(name, function() {
|
||||||
qHint.sendRequest(sourceFile, function(req) {
|
qHint.sendRequest(sourceFile, function(req) {
|
||||||
start();
|
start();
|
||||||
|
|
||||||
@ -113,9 +113,8 @@ var jsHintOpts = {
|
|||||||
"visit",
|
"visit",
|
||||||
"count",
|
"count",
|
||||||
"exists",
|
"exists",
|
||||||
"asyncTest",
|
"asyncTestDiscourse",
|
||||||
"find",
|
"find",
|
||||||
"resolvingPromise",
|
|
||||||
"sinon",
|
"sinon",
|
||||||
"moment",
|
"moment",
|
||||||
"start",
|
"start",
|
||||||
@ -125,7 +124,6 @@ var jsHintOpts = {
|
|||||||
"controllerFor",
|
"controllerFor",
|
||||||
"containsInstance",
|
"containsInstance",
|
||||||
"deepEqual",
|
"deepEqual",
|
||||||
"resolvingPromiseWith",
|
|
||||||
"Blob",
|
"Blob",
|
||||||
"File"],
|
"File"],
|
||||||
"node" : false,
|
"node" : false,
|
||||||
|
@ -130,7 +130,7 @@ test('editingFirstPost', function() {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
asyncTest('importQuote with a post', function() {
|
asyncTestDiscourse('importQuote with a post', function() {
|
||||||
expect(1);
|
expect(1);
|
||||||
|
|
||||||
this.stub(Discourse.Post, 'load').withArgs(123).returns(Em.Deferred.promise(function (p) {
|
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);
|
expect(1);
|
||||||
|
|
||||||
this.stub(Discourse.Post, 'load').withArgs(4).returns(Em.Deferred.promise(function (p) {
|
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");
|
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);
|
var postStream = buildStream(1234);
|
||||||
expect(1);
|
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);
|
var postStream = buildStream(1234);
|
||||||
expect(1);
|
expect(1);
|
||||||
|
|
||||||
this.stub(Discourse, "ajax").returns(resolvingPromiseWith({
|
this.stub(Discourse, "ajax").returns(Ember.RSVP.resolve({
|
||||||
post_stream: {
|
post_stream: {
|
||||||
posts: [{id: 10, post_number: 10}]
|
posts: [{id: 10, post_number: 10}]
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
// Externals we need to load first
|
// Externals we need to load first
|
||||||
//= require ../../app/assets/javascripts/external/jquery-1.9.1.js
|
//= require ../../app/assets/javascripts/external/jquery-1.9.1.js
|
||||||
//= require ../../app/assets/javascripts/external/jquery.ui.widget.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/ember.js
|
||||||
//= require ../../app/assets/javascripts/external_development/group-helper.js
|
//= require ../../app/assets/javascripts/external_development/group-helper.js
|
||||||
|
|
||||||
@ -46,11 +46,11 @@
|
|||||||
|
|
||||||
// sinon settings
|
// sinon settings
|
||||||
sinon.config = {
|
sinon.config = {
|
||||||
injectIntoThis: true,
|
injectIntoThis: true,
|
||||||
injectInto: null,
|
injectInto: null,
|
||||||
properties: ["spy", "stub", "mock", "clock", "sandbox"],
|
properties: ["spy", "stub", "mock", "clock", "sandbox"],
|
||||||
useFakeTimers: false,
|
useFakeTimers: false,
|
||||||
useFakeServer: false
|
useFakeServer: false
|
||||||
};
|
};
|
||||||
|
|
||||||
window.assetPath = function() { return null };
|
window.assetPath = function() { return null };
|
||||||
|
Loading…
x
Reference in New Issue
Block a user