mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
Merge branch 'master' of github.com:discourse/discourse
This commit is contained in:
commit
f4640e3bff
@ -12,13 +12,20 @@ Discourse.Search = {
|
|||||||
|
|
||||||
@method forTerm
|
@method forTerm
|
||||||
@param {String} term The term to search for
|
@param {String} term The term to search for
|
||||||
@param {String} typeFilter An optional filter to restrict the search by type
|
@param {Object} opts Options for searching
|
||||||
|
@param {String} opts.typeFilter Filter our results to one type only
|
||||||
|
@param {Ember.Object} opts.searchContext data to help searching within a context (say, a category or user)
|
||||||
@return {Promise} a promise that resolves the search results
|
@return {Promise} a promise that resolves the search results
|
||||||
**/
|
**/
|
||||||
forTerm: function(term, typeFilter) {
|
forTerm: function(term, opts) {
|
||||||
return Discourse.ajax('/search', {
|
if (!opts) opts = {};
|
||||||
data: { term: term, type_filter: typeFilter }
|
|
||||||
});
|
// Only include the data we have
|
||||||
|
var data = { term: term }
|
||||||
|
if (opts.typeFilter) data.typeFilter = opts.typeFilter;
|
||||||
|
if (opts.searchContext) data.search_context = opts.searchContext;
|
||||||
|
|
||||||
|
return Discourse.ajax('/search', { data: data });
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,10 @@ Discourse.ComposerController = Discourse.Controller.extend({
|
|||||||
if (c) return c.appendText(text);
|
if (c) return c.appendText(text);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
categories: function() {
|
||||||
|
return Discourse.Category.list();
|
||||||
|
}.property(),
|
||||||
|
|
||||||
save: function(force) {
|
save: function(force) {
|
||||||
var composer,
|
var composer,
|
||||||
_this = this,
|
_this = this,
|
||||||
@ -35,6 +39,13 @@ Discourse.ComposerController = Discourse.Controller.extend({
|
|||||||
buttons;
|
buttons;
|
||||||
|
|
||||||
composer = this.get('content');
|
composer = this.get('content');
|
||||||
|
|
||||||
|
if( composer.get('cantSubmitPost') ) {
|
||||||
|
this.set('view.showTitleTip', true);
|
||||||
|
this.set('view.showReplyTip', true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
composer.set('disableDrafts', true);
|
composer.set('disableDrafts', true);
|
||||||
|
|
||||||
// for now handle a very narrow use case
|
// for now handle a very narrow use case
|
||||||
@ -324,6 +335,8 @@ Discourse.ComposerController = Discourse.Controller.extend({
|
|||||||
close: function() {
|
close: function() {
|
||||||
this.set('content', null);
|
this.set('content', null);
|
||||||
this.set('view.content', null);
|
this.set('view.content', null);
|
||||||
|
this.set('view.showTitleTip', false);
|
||||||
|
this.set('view.showReplyTip', false);
|
||||||
},
|
},
|
||||||
|
|
||||||
closeIfCollapsed: function() {
|
closeIfCollapsed: function() {
|
||||||
|
@ -14,7 +14,11 @@ Discourse.HeaderController = Discourse.Controller.extend({
|
|||||||
var topic = this.get('topic');
|
var topic = this.get('topic');
|
||||||
if (topic) topic.toggleStar();
|
if (topic) topic.toggleStar();
|
||||||
return false;
|
return false;
|
||||||
}
|
},
|
||||||
|
|
||||||
|
categories: function() {
|
||||||
|
return Discourse.Category.list();
|
||||||
|
}.property()
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -8,21 +8,17 @@
|
|||||||
**/
|
**/
|
||||||
Discourse.ListController = Discourse.Controller.extend({
|
Discourse.ListController = Discourse.Controller.extend({
|
||||||
currentUserBinding: 'Discourse.currentUser',
|
currentUserBinding: 'Discourse.currentUser',
|
||||||
categoriesBinding: 'Discourse.site.categories',
|
|
||||||
categoryBinding: 'topicList.category',
|
categoryBinding: 'topicList.category',
|
||||||
canCreateCategory: false,
|
canCreateCategory: false,
|
||||||
canCreateTopic: false,
|
canCreateTopic: false,
|
||||||
needs: ['composer', 'modal', 'listTopics'],
|
needs: ['composer', 'modal', 'listTopics'],
|
||||||
|
|
||||||
availableNavItems: function() {
|
availableNavItems: function() {
|
||||||
var hasCategories, loggedOn, summary;
|
var summary = this.get('filterSummary');
|
||||||
summary = this.get('filterSummary');
|
var loggedOn = !!Discourse.get('currentUser');
|
||||||
loggedOn = !!Discourse.get('currentUser');
|
|
||||||
hasCategories = !!this.get('categories');
|
|
||||||
return Discourse.SiteSettings.top_menu.split("|").map(function(i) {
|
return Discourse.SiteSettings.top_menu.split("|").map(function(i) {
|
||||||
return Discourse.NavItem.fromText(i, {
|
return Discourse.NavItem.fromText(i, {
|
||||||
loggedOn: loggedOn,
|
loggedOn: loggedOn,
|
||||||
hasCategories: hasCategories,
|
|
||||||
countSummary: summary
|
countSummary: summary
|
||||||
});
|
});
|
||||||
}).filter(function(i) {
|
}).filter(function(i) {
|
||||||
|
@ -25,7 +25,12 @@ Discourse.SearchController = Em.ArrayController.extend(Discourse.Presence, {
|
|||||||
var searchController = this;
|
var searchController = this;
|
||||||
this.set('count', 0);
|
this.set('count', 0);
|
||||||
|
|
||||||
return Discourse.Search.forTerm(term, typeFilter).then(function(results) {
|
var searcher = Discourse.Search.forTerm(term, {
|
||||||
|
typeFilter: typeFilter,
|
||||||
|
searchContext: searchController.get('searchContext')
|
||||||
|
});
|
||||||
|
|
||||||
|
return searcher.then(function(results) {
|
||||||
searchController.set('results', results);
|
searchController.set('results', results);
|
||||||
if (results) {
|
if (results) {
|
||||||
searchController.set('noResults', results.length === 0);
|
searchController.set('noResults', results.length === 0);
|
||||||
|
@ -29,6 +29,9 @@ Discourse.TopicController = Discourse.ObjectController.extend(Discourse.Selected
|
|||||||
return (this.get('selectedPostsCount') > 0);
|
return (this.get('selectedPostsCount') > 0);
|
||||||
}.property('selectedPostsCount'),
|
}.property('selectedPostsCount'),
|
||||||
|
|
||||||
|
categories: function() {
|
||||||
|
return Discourse.Category.list();
|
||||||
|
}.property(),
|
||||||
|
|
||||||
canSelectAll: Em.computed.not('allPostsSelected'),
|
canSelectAll: Em.computed.not('allPostsSelected'),
|
||||||
|
|
||||||
|
@ -98,6 +98,21 @@ Ember.Handlebars.registerHelper('inputTip', function(options) {
|
|||||||
return Ember.Handlebars.helpers.view.call(this, Discourse.InputTipView, options);
|
return Ember.Handlebars.helpers.view.call(this, Discourse.InputTipView, options);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
Inserts a Discourse.PopupInputTipView
|
||||||
|
|
||||||
|
@method popupInputTip
|
||||||
|
@for Handlebars
|
||||||
|
**/
|
||||||
|
Ember.Handlebars.registerHelper('popupInputTip', function(options) {
|
||||||
|
var hash = options.hash,
|
||||||
|
types = options.hashTypes;
|
||||||
|
|
||||||
|
normalizeHash(hash, types);
|
||||||
|
|
||||||
|
return Ember.Handlebars.helpers.view.call(this, Discourse.PopupInputTipView, options);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Produces a bound link to a category
|
Produces a bound link to a category
|
||||||
|
@ -20,6 +20,10 @@ Discourse.Category = Discourse.Model.extend({
|
|||||||
this.set("groups", Em.A(this.groups));
|
this.set("groups", Em.A(this.groups));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
searchContext: function() {
|
||||||
|
return ({ type: 'category', id: this.get('id') });
|
||||||
|
}.property('id'),
|
||||||
|
|
||||||
url: function() {
|
url: function() {
|
||||||
return Discourse.getURL("/category/") + (this.get('slug'));
|
return Discourse.getURL("/category/") + (this.get('slug'));
|
||||||
}.property('name'),
|
}.property('name'),
|
||||||
|
@ -276,7 +276,9 @@ Discourse.Composer = Discourse.Model.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
save: function(opts) {
|
save: function(opts) {
|
||||||
return this.get('editingPost') ? this.editPost(opts) : this.createPost(opts);
|
if( !this.get('cantSubmitPost') ) {
|
||||||
|
return this.get('editingPost') ? this.editPost(opts) : this.createPost(opts);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// When you edit a post
|
// When you edit a post
|
||||||
|
@ -34,7 +34,7 @@ Discourse.NavItem.reopenClass({
|
|||||||
testName = name.split("/")[0];
|
testName = name.split("/")[0];
|
||||||
|
|
||||||
if (!opts.loggedOn && !validAnon.contains(testName)) return null;
|
if (!opts.loggedOn && !validAnon.contains(testName)) return null;
|
||||||
if (!opts.hasCategories && testName === "categories") return null;
|
if (!Discourse.Category.list() && testName === "categories") return null;
|
||||||
if (!validNavNames.contains(testName)) return null;
|
if (!validNavNames.contains(testName)) return null;
|
||||||
|
|
||||||
opts = {
|
opts = {
|
||||||
|
@ -128,7 +128,7 @@ Discourse.Post = Discourse.Model.extend({
|
|||||||
return _this.get("actionByName." + (item.get('name_key')) + ".can_act");
|
return _this.get("actionByName." + (item.get('name_key')) + ".can_act");
|
||||||
});
|
});
|
||||||
return flags;
|
return flags;
|
||||||
}.property('Discourse.site.flagTypes', 'actions_summary.@each.can_act'),
|
}.property('actions_summary.@each.can_act'),
|
||||||
|
|
||||||
actionsHistory: function() {
|
actionsHistory: function() {
|
||||||
if (!this.present('actions_summary')) return null;
|
if (!this.present('actions_summary')) return null;
|
||||||
@ -220,7 +220,7 @@ Discourse.Post = Discourse.Model.extend({
|
|||||||
obj.actions_summary.each(function(a) {
|
obj.actions_summary.each(function(a) {
|
||||||
var actionSummary;
|
var actionSummary;
|
||||||
a.post = post;
|
a.post = post;
|
||||||
a.actionType = Discourse.get("site").postActionTypeById(a.id);
|
a.actionType = Discourse.Site.instance().postActionTypeById(a.id);
|
||||||
actionSummary = Discourse.ActionSummary.create(a);
|
actionSummary = Discourse.ActionSummary.create(a);
|
||||||
post.get('actions_summary').pushObject(actionSummary);
|
post.get('actions_summary').pushObject(actionSummary);
|
||||||
lookup.set(a.actionType.get('name_key'), actionSummary);
|
lookup.set(a.actionType.get('name_key'), actionSummary);
|
||||||
@ -278,10 +278,9 @@ Discourse.Post.reopenClass({
|
|||||||
if (result.actions_summary) {
|
if (result.actions_summary) {
|
||||||
lookup = Em.Object.create();
|
lookup = Em.Object.create();
|
||||||
result.actions_summary = result.actions_summary.map(function(a) {
|
result.actions_summary = result.actions_summary.map(function(a) {
|
||||||
var actionSummary;
|
|
||||||
a.post = result;
|
a.post = result;
|
||||||
a.actionType = Discourse.get("site").postActionTypeById(a.id);
|
a.actionType = Discourse.Site.instance().postActionTypeById(a.id);
|
||||||
actionSummary = Discourse.ActionSummary.create(a);
|
var actionSummary = Discourse.ActionSummary.create(a);
|
||||||
lookup.set(a.actionType.get('name_key'), actionSummary);
|
lookup.set(a.actionType.get('name_key'), actionSummary);
|
||||||
return actionSummary;
|
return actionSummary;
|
||||||
});
|
});
|
||||||
@ -290,8 +289,7 @@ Discourse.Post.reopenClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
create: function(obj, topic) {
|
create: function(obj, topic) {
|
||||||
var result;
|
var result = this._super(obj);
|
||||||
result = this._super(obj);
|
|
||||||
this.createActionSummary(result);
|
this.createActionSummary(result);
|
||||||
if (obj.reply_to_user) {
|
if (obj.reply_to_user) {
|
||||||
result.set('reply_to_user', Discourse.User.create(obj.reply_to_user));
|
result.set('reply_to_user', Discourse.User.create(obj.reply_to_user));
|
||||||
|
@ -7,17 +7,16 @@
|
|||||||
@module Discourse
|
@module Discourse
|
||||||
**/
|
**/
|
||||||
Discourse.Topic = Discourse.Model.extend({
|
Discourse.Topic = Discourse.Model.extend({
|
||||||
categoriesBinding: 'Discourse.site.categories',
|
|
||||||
|
|
||||||
fewParticipants: (function() {
|
fewParticipants: function() {
|
||||||
if (!this.present('participants')) return null;
|
if (!this.present('participants')) return null;
|
||||||
return this.get('participants').slice(0, 3);
|
return this.get('participants').slice(0, 3);
|
||||||
}).property('participants'),
|
}.property('participants'),
|
||||||
|
|
||||||
canConvertToRegular: (function() {
|
canConvertToRegular: function() {
|
||||||
var a = this.get('archetype');
|
var a = this.get('archetype');
|
||||||
return a !== 'regular' && a !== 'private_message';
|
return a !== 'regular' && a !== 'private_message';
|
||||||
}).property('archetype'),
|
}.property('archetype'),
|
||||||
|
|
||||||
convertArchetype: function(archetype) {
|
convertArchetype: function(archetype) {
|
||||||
var a = this.get('archetype');
|
var a = this.get('archetype');
|
||||||
@ -30,11 +29,13 @@ Discourse.Topic = Discourse.Model.extend({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
searchContext: function() {
|
||||||
|
return ({ type: 'topic', id: this.get('id') });
|
||||||
|
}.property('id'),
|
||||||
|
|
||||||
category: function() {
|
category: function() {
|
||||||
if (this.get('categories')) {
|
return Discourse.Category.list().findProperty('name', this.get('categoryName'));
|
||||||
return this.get('categories').findProperty('name', this.get('categoryName'));
|
}.property('categoryName'),
|
||||||
}
|
|
||||||
}.property('categoryName', 'categories'),
|
|
||||||
|
|
||||||
shareUrl: function(){
|
shareUrl: function(){
|
||||||
var user = Discourse.get('currentUser');
|
var user = Discourse.get('currentUser');
|
||||||
@ -220,6 +221,7 @@ Discourse.Topic = Discourse.Model.extend({
|
|||||||
|
|
||||||
// If loading the topic succeeded...
|
// If loading the topic succeeded...
|
||||||
var afterTopicLoaded = function(result) {
|
var afterTopicLoaded = function(result) {
|
||||||
|
|
||||||
var closestPostNumber, lastPost, postDiff;
|
var closestPostNumber, lastPost, postDiff;
|
||||||
|
|
||||||
// Update the slug if different
|
// Update the slug if different
|
||||||
@ -246,6 +248,7 @@ Discourse.Topic = Discourse.Model.extend({
|
|||||||
if (result.suggested_topics) {
|
if (result.suggested_topics) {
|
||||||
topic.set('suggested_topics', Em.A());
|
topic.set('suggested_topics', Em.A());
|
||||||
}
|
}
|
||||||
|
|
||||||
topic.mergeAttributes(result, { suggested_topics: Discourse.Topic });
|
topic.mergeAttributes(result, { suggested_topics: Discourse.Topic });
|
||||||
topic.set('posts', Em.A());
|
topic.set('posts', Em.A());
|
||||||
if (opts.trackVisit && result.draft && result.draft.length > 0) {
|
if (opts.trackVisit && result.draft && result.draft.length > 0) {
|
||||||
@ -261,17 +264,18 @@ Discourse.Topic = Discourse.Model.extend({
|
|||||||
// Okay this is weird, but let's store the length of the next post when there
|
// Okay this is weird, but let's store the length of the next post when there
|
||||||
lastPost = null;
|
lastPost = null;
|
||||||
result.posts.each(function(p) {
|
result.posts.each(function(p) {
|
||||||
var post;
|
|
||||||
p.scrollToAfterInsert = opts.nearPost;
|
p.scrollToAfterInsert = opts.nearPost;
|
||||||
post = Discourse.Post.create(p);
|
var post = Discourse.Post.create(p);
|
||||||
post.set('topic', topic);
|
post.set('topic', topic);
|
||||||
topic.get('posts').pushObject(post);
|
topic.get('posts').pushObject(post);
|
||||||
lastPost = post;
|
lastPost = post;
|
||||||
});
|
});
|
||||||
|
|
||||||
topic.set('loaded', true);
|
topic.set('loaded', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
var errorLoadingTopic = function(result) {
|
var errorLoadingTopic = function(result) {
|
||||||
|
|
||||||
topic.set('errorLoading', true);
|
topic.set('errorLoading', true);
|
||||||
|
|
||||||
// If the result was 404 the post is not found
|
// If the result was 404 the post is not found
|
||||||
|
@ -28,6 +28,10 @@ Discourse.User = Discourse.Model.extend({
|
|||||||
return Discourse.Utilities.avatarUrl(this.get('username'), 'small', this.get('avatar_template'));
|
return Discourse.Utilities.avatarUrl(this.get('username'), 'small', this.get('avatar_template'));
|
||||||
}).property('username'),
|
}).property('username'),
|
||||||
|
|
||||||
|
searchContext: function() {
|
||||||
|
return ({ type: 'user', id: this.get('username_lower') });
|
||||||
|
}.property('username_lower'),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
This user's website.
|
This user's website.
|
||||||
|
|
||||||
|
@ -8,8 +8,7 @@
|
|||||||
**/
|
**/
|
||||||
Discourse.ApplicationRoute = Discourse.Route.extend({
|
Discourse.ApplicationRoute = Discourse.Route.extend({
|
||||||
setupController: function(controller) {
|
setupController: function(controller) {
|
||||||
Discourse.set('site', Discourse.Site.instance());
|
//Discourse.set('site', Discourse.Site.instance());
|
||||||
|
|
||||||
var currentUser = PreloadStore.get('currentUser');
|
var currentUser = PreloadStore.get('currentUser');
|
||||||
if (currentUser) {
|
if (currentUser) {
|
||||||
Discourse.set('currentUser', Discourse.User.create(currentUser));
|
Discourse.set('currentUser', Discourse.User.create(currentUser));
|
||||||
|
@ -47,7 +47,7 @@ Discourse.ListCategoryRoute = Discourse.FilteredListRoute.extend({
|
|||||||
this._super();
|
this._super();
|
||||||
|
|
||||||
// Add a search context
|
// Add a search context
|
||||||
this.controllerFor('search').set('searchContext', this.modelFor('listCategory'));
|
this.controllerFor('search').set('searchContext', this.modelFor('listCategory').get('searchContext'));
|
||||||
},
|
},
|
||||||
|
|
||||||
deactivate: function() {
|
deactivate: function() {
|
||||||
|
@ -25,7 +25,7 @@ Discourse.TopicRoute = Discourse.Route.extend({
|
|||||||
Discourse.set('transient.lastTopicIdViewed', parseInt(topic.get('id'), 10));
|
Discourse.set('transient.lastTopicIdViewed', parseInt(topic.get('id'), 10));
|
||||||
|
|
||||||
// Set the search context
|
// Set the search context
|
||||||
this.controllerFor('search').set('searchContext', topic);
|
this.controllerFor('search').set('searchContext', topic.get('searchContext'));
|
||||||
},
|
},
|
||||||
|
|
||||||
deactivate: function() {
|
deactivate: function() {
|
||||||
|
@ -28,7 +28,7 @@ Discourse.UserRoute = Discourse.Route.extend({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Add a search context
|
// Add a search context
|
||||||
this.controllerFor('search').set('searchContext', user);
|
this.controllerFor('search').set('searchContext', user.get('searchContext'));
|
||||||
},
|
},
|
||||||
|
|
||||||
deactivate: function() {
|
deactivate: function() {
|
||||||
|
@ -32,9 +32,14 @@
|
|||||||
{{#if content.creatingPrivateMessage}}
|
{{#if content.creatingPrivateMessage}}
|
||||||
{{view Discourse.UserSelector topicIdBinding="controller.controllers.topic.content.id" excludeCurrentUser="true" id="private-message-users" class="span8" placeholderKey="composer.users_placeholder" tabindex="1" usernamesBinding="content.targetUsernames"}}
|
{{view Discourse.UserSelector topicIdBinding="controller.controllers.topic.content.id" excludeCurrentUser="true" id="private-message-users" class="span8" placeholderKey="composer.users_placeholder" tabindex="1" usernamesBinding="content.targetUsernames"}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{textField value=content.title tabindex="2" id="reply-title" maxlength="255" class="span8" placeholderKey="composer.title_placeholder"}}
|
|
||||||
|
<div class="title-input">
|
||||||
|
{{textField value=content.title tabindex="2" id="reply-title" maxlength="255" class="span8" placeholderKey="composer.title_placeholder"}}
|
||||||
|
{{popupInputTip validation=view.titleValidation show=view.showTitleTip}}
|
||||||
|
</div>
|
||||||
|
|
||||||
{{#unless content.creatingPrivateMessage}}
|
{{#unless content.creatingPrivateMessage}}
|
||||||
{{view Discourse.ComboboxViewCategory valueAttribute="name" contentBinding="Discourse.site.categories" valueBinding="content.categoryName"}}
|
{{view Discourse.ComboboxViewCategory valueAttribute="name" contentBinding="categories" valueBinding="content.categoryName"}}
|
||||||
{{#if content.archetype.hasOptions}}
|
{{#if content.archetype.hasOptions}}
|
||||||
<button class='btn' {{action showOptions}}>{{i18n topic.options}}</button>
|
<button class='btn' {{action showOptions}}>{{i18n topic.options}}</button>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
@ -53,6 +58,7 @@
|
|||||||
<div class='textarea-wrapper'>
|
<div class='textarea-wrapper'>
|
||||||
<div class='wmd-button-bar' id='wmd-button-bar'></div>
|
<div class='wmd-button-bar' id='wmd-button-bar'></div>
|
||||||
{{view Discourse.NotifyingTextArea parentBinding="view" tabindex="3" valueBinding="content.reply" id="wmd-input" placeholderKey="composer.reply_placeholder"}}
|
{{view Discourse.NotifyingTextArea parentBinding="view" tabindex="3" valueBinding="content.reply" id="wmd-input" placeholderKey="composer.reply_placeholder"}}
|
||||||
|
{{popupInputTip validation=view.replyValidation show=view.showReplyTip}}
|
||||||
</div>
|
</div>
|
||||||
<div class='preview-wrapper'>
|
<div class='preview-wrapper'>
|
||||||
<div id='wmd-preview' {{bindAttr class="hidePreview:hidden"}}></div>
|
<div id='wmd-preview' {{bindAttr class="hidePreview:hidden"}}></div>
|
||||||
@ -70,7 +76,7 @@
|
|||||||
|
|
||||||
{{#if Discourse.currentUser}}
|
{{#if Discourse.currentUser}}
|
||||||
<div class='submit-panel'>
|
<div class='submit-panel'>
|
||||||
<button {{action save}} tabindex="4" {{bindAttr disabled="content.cantSubmitPost"}} class='btn btn-primary create'>{{view.content.saveText}}</button>
|
<button {{action save}} tabindex="4" {{bindAttr class=":btn :btn-primary :create content.cantSubmitPost:disabled"}}>{{view.content.saveText}}</button>
|
||||||
<a href='#' {{action cancel}} class='cancel' tabindex="4">{{i18n cancel}}</a>
|
<a href='#' {{action cancel}} class='cancel' tabindex="4">{{i18n cancel}}</a>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
@ -77,12 +77,12 @@
|
|||||||
{{#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>{{#linkTo 'faq'}}{{i18n faq}}{{/linkTo}}</li>
|
<li>{{#linkTo 'faq'}}{{i18n faq}}{{/linkTo}}</li>
|
||||||
{{#if view.categories}}
|
{{#if categories}}
|
||||||
<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>
|
||||||
|
|
||||||
{{#each view.categories}}
|
{{#each categories}}
|
||||||
<li class='category'>
|
<li class='category'>
|
||||||
{{categoryLink this}}
|
{{categoryLink this}}
|
||||||
<b>{{unbound topic_count}}</b></a>
|
<b>{{unbound topic_count}}</b></a>
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
<a href="#" class="close" {{action hide target="view"}}><i class="icon icon-remove-sign"></i></a>
|
||||||
|
{{view.validation.reason}}
|
@ -10,7 +10,7 @@
|
|||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if view.editingTopic}}
|
{{#if view.editingTopic}}
|
||||||
<input id='edit-title' type='text' {{bindAttr value="view.topic.title"}} autofocus>
|
<input id='edit-title' type='text' {{bindAttr value="view.topic.title"}} autofocus>
|
||||||
{{view Discourse.ComboboxViewCategory valueAttribute="name" contentBinding="Discourse.site.categories" sourceBinding="view.topic.categoryName"}}
|
{{view Discourse.ComboboxViewCategory valueAttribute="name" contentBinding="categories" sourceBinding="view.topic.categoryName"}}
|
||||||
<button class='btn btn-primary btn-small' {{action finishedEdit target="view"}}><i class='icon-ok'></i></button>
|
<button class='btn btn-primary btn-small' {{action finishedEdit target="view"}}><i class='icon-ok'></i></button>
|
||||||
<button class='btn btn-small' {{action cancelEdit target="view"}}><i class='icon-remove'></i></button>
|
<button class='btn btn-small' {{action cancelEdit target="view"}}><i class='icon-remove'></i></button>
|
||||||
{{else}}
|
{{else}}
|
||||||
|
@ -26,7 +26,7 @@ Discourse.ChooseTopicView = Discourse.View.extend({
|
|||||||
|
|
||||||
search: Discourse.debounce(function(title) {
|
search: Discourse.debounce(function(title) {
|
||||||
var chooseTopicView = this;
|
var chooseTopicView = this;
|
||||||
Discourse.Search.forTerm(title, 'topic').then(function (facets) {
|
Discourse.Search.forTerm(title, {typeFilter: 'topic'}).then(function (facets) {
|
||||||
if (facets && facets[0] && facets[0].results) {
|
if (facets && facets[0] && facets[0].results) {
|
||||||
chooseTopicView.set('topics', facets[0].results);
|
chooseTopicView.set('topics', facets[0].results);
|
||||||
} else {
|
} else {
|
||||||
|
@ -369,7 +369,33 @@ Discourse.ComposerView = Discourse.View.extend({
|
|||||||
$adminOpts.show();
|
$adminOpts.show();
|
||||||
$wmd.css('top', wmdTop + parseInt($adminOpts.css('height'),10) + 'px' );
|
$wmd.css('top', wmdTop + parseInt($adminOpts.css('height'),10) + 'px' );
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
|
titleValidation: function() {
|
||||||
|
var title = this.get('content.title'), reason;
|
||||||
|
if( !title || title.length < 1 ){
|
||||||
|
reason = Em.String.i18n('composer.error.title_missing');
|
||||||
|
} else if( title.length < Discourse.SiteSettings.min_topic_title_length || title.length > Discourse.SiteSettings.max_topic_title_length ) {
|
||||||
|
reason = Em.String.i18n('composer.error.title_length', {min: Discourse.SiteSettings.min_topic_title_length, max: Discourse.SiteSettings.max_topic_title_length})
|
||||||
|
}
|
||||||
|
|
||||||
|
if( reason ) {
|
||||||
|
return Discourse.InputValidation.create({ failed: true, reason: reason });
|
||||||
|
}
|
||||||
|
}.property('content.title'),
|
||||||
|
|
||||||
|
replyValidation: function() {
|
||||||
|
var reply = this.get('content.reply'), reason;
|
||||||
|
if( !reply || reply.length < 1 ){
|
||||||
|
reason = Em.String.i18n('composer.error.post_missing');
|
||||||
|
} else if( reply.length < Discourse.SiteSettings.min_post_length ) {
|
||||||
|
reason = Em.String.i18n('composer.error.post_length', {min: Discourse.SiteSettings.min_post_length})
|
||||||
|
}
|
||||||
|
|
||||||
|
if( reason ) {
|
||||||
|
return Discourse.InputValidation.create({ failed: true, reason: reason });
|
||||||
|
}
|
||||||
|
}.property('content.reply')
|
||||||
});
|
});
|
||||||
|
|
||||||
// not sure if this is the right way, keeping here for now, we could use a mixin perhaps
|
// not sure if this is the right way, keeping here for now, we could use a mixin perhaps
|
||||||
|
@ -11,9 +11,7 @@ Discourse.HeaderView = Discourse.View.extend({
|
|||||||
classNames: ['d-header', 'clearfix'],
|
classNames: ['d-header', 'clearfix'],
|
||||||
classNameBindings: ['editingTopic'],
|
classNameBindings: ['editingTopic'],
|
||||||
templateName: 'header',
|
templateName: 'header',
|
||||||
siteBinding: 'Discourse.site',
|
|
||||||
currentUserBinding: 'Discourse.currentUser',
|
currentUserBinding: 'Discourse.currentUser',
|
||||||
categoriesBinding: 'site.categories',
|
|
||||||
topicBinding: 'Discourse.router.topicController.content',
|
topicBinding: 'Discourse.router.topicController.content',
|
||||||
|
|
||||||
showDropdown: function($target) {
|
showDropdown: function($target) {
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
@module Discourse
|
@module Discourse
|
||||||
**/
|
**/
|
||||||
Discourse.InputTipView = Discourse.View.extend({
|
Discourse.InputTipView = Discourse.View.extend({
|
||||||
templateName: 'input_tip',
|
|
||||||
classNameBindings: [':tip', 'good', 'bad'],
|
classNameBindings: [':tip', 'good', 'bad'],
|
||||||
|
|
||||||
good: function() {
|
good: function() {
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
Discourse.ListView = Discourse.View.extend({
|
Discourse.ListView = Discourse.View.extend({
|
||||||
templateName: 'list/list',
|
templateName: 'list/list',
|
||||||
composeViewBinding: Ember.Binding.oneWay('Discourse.composeView'),
|
composeViewBinding: Ember.Binding.oneWay('Discourse.composeView'),
|
||||||
categoriesBinding: 'Discourse.site.categories',
|
|
||||||
|
|
||||||
// The window has been scrolled
|
// The window has been scrolled
|
||||||
scrolled: function(e) {
|
scrolled: function(e) {
|
||||||
@ -18,7 +17,7 @@ Discourse.ListView = Discourse.View.extend({
|
|||||||
return currentView ? typeof currentView.scrolled === "function" ? currentView.scrolled(e) : void 0 : void 0;
|
return currentView ? typeof currentView.scrolled === "function" ? currentView.scrolled(e) : void 0 : void 0;
|
||||||
},
|
},
|
||||||
|
|
||||||
createTopicText: (function() {
|
createTopicText: function() {
|
||||||
if (this.get('controller.category.name')) {
|
if (this.get('controller.category.name')) {
|
||||||
return Em.String.i18n("topic.create_in", {
|
return Em.String.i18n("topic.create_in", {
|
||||||
categoryName: this.get('controller.category.name')
|
categoryName: this.get('controller.category.name')
|
||||||
@ -26,7 +25,7 @@ Discourse.ListView = Discourse.View.extend({
|
|||||||
} else {
|
} else {
|
||||||
return Em.String.i18n("topic.create");
|
return Em.String.i18n("topic.create");
|
||||||
}
|
}
|
||||||
}).property('controller.category.name')
|
}.property('controller.category.name')
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -55,16 +55,18 @@ Discourse.EditCategoryView = Discourse.ModalBodyView.extend({
|
|||||||
|
|
||||||
// background colors are available as a pipe-separated string
|
// background colors are available as a pipe-separated string
|
||||||
backgroundColors: function() {
|
backgroundColors: function() {
|
||||||
|
var categories = Discourse.Category.list();
|
||||||
return Discourse.SiteSettings.category_colors.split("|").map(function(i) { return i.toUpperCase(); }).concat(
|
return Discourse.SiteSettings.category_colors.split("|").map(function(i) { return i.toUpperCase(); }).concat(
|
||||||
Discourse.site.categories.map(function(c) { return c.color.toUpperCase(); }) ).uniq();
|
categories.map(function(c) { return c.color.toUpperCase(); }) ).uniq();
|
||||||
}.property('Discourse.SiteSettings.category_colors'),
|
}.property('Discourse.SiteSettings.category_colors'),
|
||||||
|
|
||||||
usedBackgroundColors: function() {
|
usedBackgroundColors: function() {
|
||||||
return Discourse.site.categories.map(function(c) {
|
var categories = Discourse.Category.list();
|
||||||
|
return categories.map(function(c) {
|
||||||
// If editing a category, don't include its color:
|
// If editing a category, don't include its color:
|
||||||
return (this.get('category.id') && this.get('category.color').toUpperCase() === c.color.toUpperCase()) ? null : c.color.toUpperCase();
|
return (this.get('category.id') && this.get('category.color').toUpperCase() === c.color.toUpperCase()) ? null : c.color.toUpperCase();
|
||||||
}, this).compact();
|
}, this).compact();
|
||||||
}.property('Discourse.site.categories', 'category.id', 'category.color'),
|
}.property('category.id', 'category.color'),
|
||||||
|
|
||||||
title: function() {
|
title: function() {
|
||||||
if (this.get('category.id')) return Em.String.i18n("category.edit_long");
|
if (this.get('category.id')) return Em.String.i18n("category.edit_long");
|
||||||
|
@ -0,0 +1,55 @@
|
|||||||
|
/**
|
||||||
|
This view extends the functionality of InputTipView with these extra features:
|
||||||
|
* it can be dismissed
|
||||||
|
* it bounces when it's shown
|
||||||
|
* it's absolutely positioned beside the input element, with the help of
|
||||||
|
extra css you'll need to write to line it up correctly.
|
||||||
|
|
||||||
|
@class PopupInputTipView
|
||||||
|
@extends Discourse.View
|
||||||
|
@namespace Discourse
|
||||||
|
@module Discourse
|
||||||
|
**/
|
||||||
|
Discourse.PopupInputTipView = Discourse.View.extend({
|
||||||
|
templateName: 'popup_input_tip',
|
||||||
|
classNameBindings: [':popup-tip', 'good', 'bad', 'show::hide'],
|
||||||
|
animateAttribute: null,
|
||||||
|
bouncePixels: 6,
|
||||||
|
bounceDelay: 100,
|
||||||
|
|
||||||
|
good: function() {
|
||||||
|
return !this.get('validation.failed');
|
||||||
|
}.property('validation'),
|
||||||
|
|
||||||
|
bad: function() {
|
||||||
|
return this.get('validation.failed');
|
||||||
|
}.property('validation'),
|
||||||
|
|
||||||
|
hide: function() {
|
||||||
|
this.set('show', false);
|
||||||
|
},
|
||||||
|
|
||||||
|
bounce: function() {
|
||||||
|
var $elem = this.$()
|
||||||
|
if( !this.animateAttribute ) {
|
||||||
|
this.animateAttribute = $elem.css('left') === 'auto' ? 'right' : 'left';
|
||||||
|
}
|
||||||
|
if( this.animateAttribute === 'left' ) {
|
||||||
|
this.bounceLeft($elem);
|
||||||
|
} else {
|
||||||
|
this.bounceRight($elem);
|
||||||
|
}
|
||||||
|
}.observes('show'),
|
||||||
|
|
||||||
|
bounceLeft: function($elem) {
|
||||||
|
for( var i = 0; i < 5; i++ ) {
|
||||||
|
$elem.animate({ left: '+=' + this.bouncePixels }, this.bounceDelay).animate({ left: '-=' + this.bouncePixels }, this.bounceDelay);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
bounceRight: function($elem) {
|
||||||
|
for( var i = 0; i < 5; i++ ) {
|
||||||
|
$elem.animate({ right: '-=' + this.bouncePixels }, this.bounceDelay).animate({ right: '+=' + this.bouncePixels }, this.bounceDelay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
@ -12,7 +12,6 @@ Discourse.TopicView = Discourse.View.extend(Discourse.Scrolling, {
|
|||||||
topicBinding: 'controller.content',
|
topicBinding: 'controller.content',
|
||||||
userFiltersBinding: 'controller.userFilters',
|
userFiltersBinding: 'controller.userFilters',
|
||||||
classNameBindings: ['controller.multiSelect:multi-select', 'topic.archetype', 'topic.category.secure:secure_category'],
|
classNameBindings: ['controller.multiSelect:multi-select', 'topic.archetype', 'topic.category.secure:secure_category'],
|
||||||
siteBinding: 'Discourse.site',
|
|
||||||
progressPosition: 1,
|
progressPosition: 1,
|
||||||
menuVisible: true,
|
menuVisible: true,
|
||||||
SHORT_POST: 1200,
|
SHORT_POST: 1200,
|
||||||
|
@ -104,9 +104,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#reply-control {
|
#reply-control {
|
||||||
.requirements-not-met {
|
|
||||||
background-color: rgba(255, 0, 0, 0.12);
|
|
||||||
}
|
|
||||||
.toggle-preview, #draft-status, #image-uploading {
|
.toggle-preview, #draft-status, #image-uploading {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: -31px;
|
bottom: -31px;
|
||||||
@ -325,6 +322,15 @@
|
|||||||
bottom: 8px;
|
bottom: 8px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.title-input {
|
||||||
|
position: relative;
|
||||||
|
display: inline;
|
||||||
|
.popup-tip {
|
||||||
|
width: 300px;
|
||||||
|
left: -8px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.reply-to {
|
.reply-to {
|
||||||
@ -450,6 +456,10 @@ div.ac-wrap {
|
|||||||
.textarea-wrapper {
|
.textarea-wrapper {
|
||||||
padding-right: 5px;
|
padding-right: 5px;
|
||||||
float: left;
|
float: left;
|
||||||
|
.popup-tip {
|
||||||
|
margin-top: 3px;
|
||||||
|
right: 4px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.preview-wrapper {
|
.preview-wrapper {
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
|
29
app/assets/stylesheets/application/input_tip.css.scss
Normal file
29
app/assets/stylesheets/application/input_tip.css.scss
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
@import "foundation/variables";
|
||||||
|
@import "foundation/mixins";
|
||||||
|
|
||||||
|
.popup-tip {
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
padding: 5px 10px;
|
||||||
|
z-index: 101;
|
||||||
|
@include border-radius-all(2px);
|
||||||
|
border: solid 1px #955;
|
||||||
|
&.bad {
|
||||||
|
background-color: #b66;
|
||||||
|
color: white;
|
||||||
|
box-shadow: 1px 1px 5px #777, inset 0 0 9px #b55;
|
||||||
|
}
|
||||||
|
&.hide, &.good {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
a.close {
|
||||||
|
float: right;
|
||||||
|
color: $black;
|
||||||
|
opacity: 0.5;
|
||||||
|
font-size: 15px;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
a.close:hover {
|
||||||
|
opacity: 1.0;
|
||||||
|
}
|
||||||
|
}
|
@ -22,7 +22,7 @@
|
|||||||
&:active {
|
&:active {
|
||||||
text-shadow: none;
|
text-shadow: none;
|
||||||
}
|
}
|
||||||
&[disabled] {
|
&[disabled], &.disabled {
|
||||||
cursor: default;
|
cursor: default;
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
}
|
}
|
||||||
|
@ -338,6 +338,12 @@ en:
|
|||||||
need_more_for_title: "{{n}} to go for the title"
|
need_more_for_title: "{{n}} to go for the title"
|
||||||
need_more_for_reply: "{{n}} to go for the reply"
|
need_more_for_reply: "{{n}} to go for the reply"
|
||||||
|
|
||||||
|
error:
|
||||||
|
title_missing: "Title is required."
|
||||||
|
title_length: "Title needs between {{min}} and {{max}} characters."
|
||||||
|
post_missing: "Post can't be empty."
|
||||||
|
post_length: "Post must be at least {{min}} characters long."
|
||||||
|
|
||||||
save_edit: "Save Edit"
|
save_edit: "Save Edit"
|
||||||
reply_original: "Reply on Original Topic"
|
reply_original: "Reply on Original Topic"
|
||||||
reply_here: "Reply Here"
|
reply_here: "Reply Here"
|
||||||
|
Loading…
Reference in New Issue
Block a user