mirror of
https://github.com/discourse/discourse.git
synced 2024-11-22 09:42:02 +08:00
Staff can enter and view deleted topics
This commit is contained in:
parent
eba662b988
commit
19c169540c
|
@ -60,6 +60,8 @@ Discourse.TopicController = Discourse.ObjectController.extend(Discourse.Selected
|
|||
return canDelete;
|
||||
}.property('selectedPostsCount'),
|
||||
|
||||
hasError: Ember.computed.or('errorBodyHtml', 'message'),
|
||||
|
||||
streamPercentage: function() {
|
||||
if (!this.get('postStream.loaded')) { return 0; }
|
||||
if (this.get('postStream.filteredPostsCount') === 0) { return 0; }
|
||||
|
@ -248,12 +250,8 @@ Discourse.TopicController = Discourse.ObjectController.extend(Discourse.Selected
|
|||
}.property('isPrivateMessage'),
|
||||
|
||||
deleteTopic: function() {
|
||||
var topicController = this;
|
||||
this.unsubscribe();
|
||||
this.get('content').destroy().then(function() {
|
||||
topicController.set('message', I18n.t('topic.deleted'));
|
||||
topicController.set('loaded', false);
|
||||
});
|
||||
this.get('content').destroy(Discourse.User.current());
|
||||
},
|
||||
|
||||
toggleVisibility: function() {
|
||||
|
|
|
@ -258,7 +258,7 @@ Discourse.PostStream = Em.Object.extend({
|
|||
|
||||
Discourse.URL.set('queryParams', postStream.get('streamFilters'));
|
||||
}, function(result) {
|
||||
postStream.errorLoading(result.status);
|
||||
postStream.errorLoading(result);
|
||||
});
|
||||
},
|
||||
hasLoadedData: Em.computed.and('hasPosts', 'hasStream'),
|
||||
|
@ -612,7 +612,8 @@ Discourse.PostStream = Em.Object.extend({
|
|||
@param {Integer} status the HTTP status code
|
||||
@param {Discourse.Topic} topic The topic instance we were trying to load
|
||||
**/
|
||||
errorLoading: function(status) {
|
||||
errorLoading: function(result) {
|
||||
var status = result.status;
|
||||
|
||||
var topic = this.get('topic');
|
||||
topic.set('loadingFilter', false);
|
||||
|
@ -621,7 +622,7 @@ Discourse.PostStream = Em.Object.extend({
|
|||
// If the result was 404 the post is not found
|
||||
if (status === 404) {
|
||||
topic.set('errorTitle', I18n.t('topic.not_found.title'));
|
||||
topic.set('message', I18n.t('topic.not_found.description'));
|
||||
topic.set('errorBodyHtml', result.responseText);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ Discourse.Topic = Discourse.Model.extend({
|
|||
}.property(),
|
||||
|
||||
invisible: Em.computed.not('visible'),
|
||||
deleted: Em.computed.notEmpty('deleted_at'),
|
||||
|
||||
canConvertToRegular: function() {
|
||||
var a = this.get('archetype');
|
||||
|
@ -142,13 +143,13 @@ Discourse.Topic = Discourse.Model.extend({
|
|||
});
|
||||
},
|
||||
|
||||
favoriteTooltipKey: (function() {
|
||||
favoriteTooltipKey: function() {
|
||||
return this.get('starred') ? 'favorite.help.unstar' : 'favorite.help.star';
|
||||
}).property('starred'),
|
||||
}.property('starred'),
|
||||
|
||||
favoriteTooltip: (function() {
|
||||
favoriteTooltip: function() {
|
||||
return I18n.t(this.get('favoriteTooltipKey'));
|
||||
}).property('favoriteTooltipKey'),
|
||||
}.property('favoriteTooltipKey'),
|
||||
|
||||
toggleStar: function() {
|
||||
var topic = this;
|
||||
|
@ -181,22 +182,26 @@ Discourse.Topic = Discourse.Model.extend({
|
|||
|
||||
// Reset our read data for this topic
|
||||
resetRead: function() {
|
||||
return Discourse.ajax("/t/" + (this.get('id')) + "/timings", {
|
||||
return Discourse.ajax("/t/" + this.get('id') + "/timings", {
|
||||
type: 'DELETE'
|
||||
});
|
||||
},
|
||||
|
||||
// Invite a user to this topic
|
||||
inviteUser: function(user) {
|
||||
return Discourse.ajax("/t/" + (this.get('id')) + "/invite", {
|
||||
return Discourse.ajax("/t/" + this.get('id') + "/invite", {
|
||||
type: 'POST',
|
||||
data: { user: user }
|
||||
});
|
||||
},
|
||||
|
||||
// Delete this topic
|
||||
destroy: function() {
|
||||
return Discourse.ajax("/t/" + (this.get('id')), { type: 'DELETE' });
|
||||
destroy: function(deleted_by) {
|
||||
this.setProperties({
|
||||
deleted_at: new Date(),
|
||||
deleted_by: deleted_by
|
||||
});
|
||||
return Discourse.ajax("/t/" + this.get('id'), { type: 'DELETE' });
|
||||
},
|
||||
|
||||
// Update our attributes from a JSON result
|
||||
|
|
|
@ -107,15 +107,18 @@
|
|||
</div>
|
||||
|
||||
{{else}}
|
||||
{{#if message}}
|
||||
{{#if hasError}}
|
||||
<div class='container'>
|
||||
<div class='message'>
|
||||
{{#if errorBodyHtml}}
|
||||
{{{errorBodyHtml}}}
|
||||
{{/if}}
|
||||
|
||||
<h2>{{message}}</h2>
|
||||
{{#if message}}
|
||||
<div class="message">
|
||||
<h2>{{message}}</h2>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<p>
|
||||
{{#linkTo list.latest}}{{i18n topic.back_to_list}}{{/linkTo}}
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class='container'>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
Discourse.ButtonView = Discourse.View.extend({
|
||||
tagName: 'button',
|
||||
classNameBindings: [':btn', ':standard', 'dropDownToggle'],
|
||||
attributeBindings: ['data-not-implemented', 'title', 'data-toggle', 'data-share-url'],
|
||||
attributeBindings: ['title', 'data-toggle', 'data-share-url'],
|
||||
|
||||
title: function() {
|
||||
return I18n.t(this.get('helpKey') || this.get('textKey'));
|
||||
|
|
|
@ -7,9 +7,7 @@
|
|||
@module Discourse
|
||||
**/
|
||||
Discourse.DropdownButtonView = Discourse.View.extend({
|
||||
classNames: ['btn-group'],
|
||||
attributeBindings: ['data-not-implemented'],
|
||||
|
||||
classNameBindings: [':btn-group', 'hidden'],
|
||||
shouldRerender: Discourse.View.renderIfChanged('text', 'longDescription'),
|
||||
|
||||
didInsertElement: function(e) {
|
||||
|
|
|
@ -8,9 +8,10 @@
|
|||
**/
|
||||
Discourse.FavoriteButton = Discourse.ButtonView.extend({
|
||||
textKey: 'favorite.title',
|
||||
helpKeyBinding: 'controller.content.favoriteTooltipKey',
|
||||
helpKeyBinding: 'controller.favoriteTooltipKey',
|
||||
attributeBindings: ['disabled'],
|
||||
|
||||
shouldRerender: Discourse.View.renderIfChanged('controller.content.starred'),
|
||||
shouldRerender: Discourse.View.renderIfChanged('controller.starred'),
|
||||
|
||||
click: function() {
|
||||
this.get('controller').toggleStar();
|
||||
|
@ -18,7 +19,7 @@ Discourse.FavoriteButton = Discourse.ButtonView.extend({
|
|||
|
||||
renderIcon: function(buffer) {
|
||||
buffer.push("<i class='icon-star " +
|
||||
(this.get('controller.content.starred') ? ' starred' : '') +
|
||||
(this.get('controller.starred') ? ' starred' : '') +
|
||||
"'></i>");
|
||||
}
|
||||
});
|
||||
|
|
|
@ -10,7 +10,7 @@ Discourse.InviteReplyButton = Discourse.ButtonView.extend({
|
|||
textKey: 'topic.invite_reply.title',
|
||||
helpKey: 'topic.invite_reply.help',
|
||||
attributeBindings: ['disabled'],
|
||||
disabled: Em.computed.or('controller.content.archived', 'controller.content.closed'),
|
||||
disabled: Em.computed.or('controller.archived', 'controller.closed', 'controller.deleted'),
|
||||
|
||||
renderIcon: function(buffer) {
|
||||
buffer.push("<i class='icon icon-group'></i>");
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
Discourse.NotificationsButton = Discourse.DropdownButtonView.extend({
|
||||
title: I18n.t('topic.notifications.title'),
|
||||
longDescriptionBinding: 'topic.details.notificationReasonText',
|
||||
topic: Em.computed.alias('controller.model'),
|
||||
hidden: Em.computed.alias('topic.deleted'),
|
||||
|
||||
dropDownContent: [
|
||||
[Discourse.Topic.NotificationLevel.WATCHING, 'topic.notifications.watching'],
|
||||
|
|
|
@ -10,6 +10,7 @@ Discourse.ShareButton = Discourse.ButtonView.extend({
|
|||
textKey: 'topic.share.title',
|
||||
helpKey: 'topic.share.help',
|
||||
'data-share-url': Em.computed.alias('topic.shareUrl'),
|
||||
topic: Em.computed.alias('controller.model'),
|
||||
|
||||
renderIcon: function(buffer) {
|
||||
buffer.push("<i class='icon icon-share'></i>");
|
||||
|
|
|
@ -26,13 +26,13 @@ Discourse.TopicFooterButtonsView = Discourse.ContainerView.extend({
|
|||
this.attachViewClass(Discourse.InviteReplyButton);
|
||||
}
|
||||
this.attachViewClass(Discourse.FavoriteButton);
|
||||
this.attachViewWithArgs({topic: topic}, Discourse.ShareButton);
|
||||
this.attachViewClass(Discourse.ShareButton);
|
||||
this.attachViewClass(Discourse.ClearPinButton);
|
||||
}
|
||||
this.attachViewClass(Discourse.ReplyButton);
|
||||
|
||||
if (!topic.get('isPrivateMessage')) {
|
||||
this.attachViewWithArgs({topic: topic}, Discourse.NotificationsButton);
|
||||
this.attachViewClass(Discourse.NotificationsButton);
|
||||
}
|
||||
this.trigger('additionalButtons', this);
|
||||
} else {
|
||||
|
|
|
@ -9,15 +9,17 @@
|
|||
**/
|
||||
Discourse.TopicView = Discourse.View.extend(Discourse.Scrolling, {
|
||||
templateName: 'topic',
|
||||
topicBinding: 'controller.content',
|
||||
topicBinding: 'controller.model',
|
||||
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',
|
||||
'topic.deleted:deleted-topic'],
|
||||
menuVisible: true,
|
||||
SHORT_POST: 1200,
|
||||
|
||||
postStream: Em.computed.alias('controller.postStream'),
|
||||
|
||||
|
||||
updateBar: function() {
|
||||
var $topicProgress = $('#topic-progress');
|
||||
if (!$topicProgress.length) return;
|
||||
|
@ -168,7 +170,6 @@ Discourse.TopicView = Discourse.View.extend(Discourse.Scrolling, {
|
|||
|
||||
// Called for every post seen, returns the post number
|
||||
postSeen: function($post) {
|
||||
|
||||
var post = this.getPost($post);
|
||||
|
||||
if (post) {
|
||||
|
|
|
@ -777,7 +777,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Private messages
|
||||
// Custom Gutter Glyphs
|
||||
// --------------------------------------------------
|
||||
|
||||
.private_message .gutter {
|
||||
|
@ -794,6 +794,20 @@
|
|||
}
|
||||
|
||||
|
||||
.deleted-topic .gutter {
|
||||
position: relative;
|
||||
&:before {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
color: rgba($black, 0.05);
|
||||
font: 90px/1 FontAwesome;
|
||||
content: "\f05c";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.secure_category .gutter {
|
||||
position: relative;
|
||||
&:before {
|
||||
|
|
|
@ -74,9 +74,9 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
def rescue_discourse_actions(message, error)
|
||||
if request.format && request.format.json?
|
||||
render status: error, layout: false, text: message
|
||||
render status: error, layout: false, text: (error == 404) ? build_not_found_page(error) : message
|
||||
else
|
||||
render_not_found_page(error)
|
||||
render text: build_not_found_page(error, 'no_js')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -123,7 +123,6 @@ class ApplicationController < ActionController::Base
|
|||
@guardian ||= Guardian.new(current_user)
|
||||
end
|
||||
|
||||
|
||||
def serialize_data(obj, serializer, opts={})
|
||||
# If it's an array, apply the serializer as an each_serializer to the elements
|
||||
serializer_opts = {scope: guardian}.merge!(opts)
|
||||
|
@ -261,13 +260,13 @@ class ApplicationController < ActionController::Base
|
|||
redirect_to :login if SiteSetting.login_required? && !current_user
|
||||
end
|
||||
|
||||
def render_not_found_page(status=404)
|
||||
def build_not_found_page(status=404, layout=false)
|
||||
@top_viewed = TopicQuery.top_viewed(10)
|
||||
@recent = TopicQuery.recent(10)
|
||||
@slug = params[:slug].class == String ? params[:slug] : ''
|
||||
@slug = (params[:id].class == String ? params[:id] : '') if @slug.blank?
|
||||
@slug.gsub!('-',' ')
|
||||
render status: status, layout: 'no_js', formats: [:html], template: '/exceptions/not_found'
|
||||
render_to_string status: status, layout: layout, formats: [:html], template: '/exceptions/not_found'
|
||||
end
|
||||
|
||||
protected
|
||||
|
|
|
@ -25,6 +25,7 @@ class TopicsController < ApplicationController
|
|||
caches_action :avatar, cache_path: Proc.new {|c| "#{c.params[:post_number]}-#{c.params[:topic_id]}" }
|
||||
|
||||
def show
|
||||
|
||||
# We'd like to migrate the wordpress feed to another url. This keeps up backwards compatibility with
|
||||
# existing installs.
|
||||
return wordpress if params[:best].present?
|
||||
|
@ -33,6 +34,7 @@ class TopicsController < ApplicationController
|
|||
begin
|
||||
@topic_view = TopicView.new(params[:id] || params[:topic_id], current_user, opts)
|
||||
rescue Discourse::NotFound
|
||||
Rails.logger.info ">>>> B"
|
||||
topic = Topic.where(slug: params[:id]).first if params[:id]
|
||||
raise Discourse::NotFound unless topic
|
||||
return redirect_to(topic.relative_url)
|
||||
|
|
|
@ -19,7 +19,8 @@ class TopicViewSerializer < ApplicationSerializer
|
|||
:has_best_of,
|
||||
:archetype,
|
||||
:slug,
|
||||
:category_id]
|
||||
:category_id,
|
||||
:deleted_at]
|
||||
end
|
||||
|
||||
attributes :draft,
|
||||
|
|
|
@ -1,31 +1,43 @@
|
|||
<% local_domain = "#{request.protocol}#{request.host_with_port}" %>
|
||||
|
||||
<p><%= t 'page_not_found.title' %></p>
|
||||
<table>
|
||||
<tr>
|
||||
<td style="vertical-align:top; padding:0 20px 20px 0;">
|
||||
<h2><%= t 'page_not_found.popular_topics' %></h2>
|
||||
<% @top_viewed.each do |t| %>
|
||||
<%= link_to t.title, t.relative_url %><br/>
|
||||
<% end %>
|
||||
<br/>
|
||||
<a href="/latest" class="btn"><%= t 'page_not_found.see_more' %>…</a>
|
||||
</td>
|
||||
<td style="vertical-align:top; padding:0 0 20px 0;">
|
||||
<h2><%= t 'page_not_found.recent_topics' %></h2>
|
||||
<% @recent.each do |t| %>
|
||||
<%= link_to t.title, t.relative_url %><br/>
|
||||
<% end %>
|
||||
<br/>
|
||||
<a href="/latest" class="btn"><%= t 'page_not_found.see_more' %>…</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<h2><%= t 'page_not_found.search_title' %></h2>
|
||||
<p>
|
||||
<form action='http://google.com'>
|
||||
<input type="text" name='q' value="site:<%= local_domain %> <%= @slug %>">
|
||||
<!--<input type="button" class="btn" value="Search Here" onclick="alert('single page search results not implemented yet');" />-->
|
||||
<input type="submit" class="btn btn-primary" value="<%= t 'page_not_found.search_google' %>" />
|
||||
</form>
|
||||
</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="span8">
|
||||
<h2><%= t 'page_not_found.popular_topics' %></h2>
|
||||
<% @top_viewed.each do |t| %>
|
||||
<%= link_to t.title, t.relative_url %><br/>
|
||||
<% end %>
|
||||
<br/>
|
||||
<a href="/latest" class="btn"><%= t 'page_not_found.see_more' %>…</a>
|
||||
</div>
|
||||
<div class="span8">
|
||||
<h2><%= t 'page_not_found.recent_topics' %></h2>
|
||||
<% @recent.each do |t| %>
|
||||
<%= link_to t.title, t.relative_url %><br/>
|
||||
<% end %>
|
||||
<br/>
|
||||
<a href="/latest" class="btn"><%= t 'page_not_found.see_more' %>…</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="span10" style='padding-top: 20px'>
|
||||
<h2><%= t 'page_not_found.search_title' %></h2>
|
||||
<p>
|
||||
<form action='http://google.com' id='google-search' onsubmit="return google_button_clicked()">
|
||||
<input type="text" id='user-query' value="<%= @slug %>">
|
||||
<input type='hidden' id='google-query' name="q">
|
||||
<button class="btn btn-primary"><%= t 'page_not_found.search_google' %></button>
|
||||
</form>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script language="Javascript">
|
||||
function google_button_clicked(e) {
|
||||
var searchValue = document.getElementById('user-query').value;
|
||||
document.getElementById('google-query').value = 'site:<%= local_domain %> ' + searchValue;
|
||||
return true;
|
||||
}
|
||||
</script>
|
|
@ -1052,7 +1052,7 @@ en:
|
|||
access_token_problem: "Tell an admin: Please update the site settings to include the correct discourse_org_access_key."
|
||||
|
||||
page_not_found:
|
||||
title: "The page you requested doesn't exist on this discussion forum. Perhaps we can help find it, or another topic like it:"
|
||||
title: "The page you requested doesn't exist or may have been deleted by a moderator."
|
||||
popular_topics: "Popular topics"
|
||||
recent_topics: "Recent topics"
|
||||
see_more: "See More"
|
||||
|
|
|
@ -242,7 +242,11 @@ class Guardian
|
|||
end
|
||||
|
||||
def can_create_post_on_topic?(topic)
|
||||
is_staff? || (not(topic.closed? || topic.archived?) && can_create_post?(topic))
|
||||
|
||||
# No users can create posts on deleted topics
|
||||
return false if topic.trashed?
|
||||
|
||||
is_staff? || (not(topic.closed? || topic.archived? || topic.trashed?) && can_create_post?(topic))
|
||||
end
|
||||
|
||||
# Editing Methods
|
||||
|
@ -283,7 +287,9 @@ class Guardian
|
|||
end
|
||||
|
||||
def can_delete_topic?(topic)
|
||||
is_staff? && not(Category.exists?(topic_id: topic.id))
|
||||
!topic.trashed? &&
|
||||
is_staff? &&
|
||||
!(Category.exists?(topic_id: topic.id))
|
||||
end
|
||||
|
||||
def can_delete_post_action?(post_action)
|
||||
|
|
|
@ -8,17 +8,18 @@ class TopicView
|
|||
attr_accessor :draft, :draft_key, :draft_sequence
|
||||
|
||||
def initialize(topic_id, user=nil, options={})
|
||||
@user = user
|
||||
@topic = find_topic(topic_id)
|
||||
|
||||
raise Discourse::NotFound if @topic.blank?
|
||||
|
||||
@guardian = Guardian.new(user)
|
||||
@guardian = Guardian.new(@user)
|
||||
|
||||
# Special case: If the topic is private and the user isn't logged in, ask them
|
||||
# to log in!
|
||||
if @topic.present? && @topic.private_message? && user.blank?
|
||||
if @topic.present? && @topic.private_message? && @user.blank?
|
||||
raise Discourse::NotLoggedIn.new
|
||||
end
|
||||
|
||||
guardian.ensure_can_see!(@topic)
|
||||
|
||||
@post_number, @page = options[:post_number], options[:page].to_i
|
||||
|
@ -36,14 +37,13 @@ class TopicView
|
|||
@filtered_posts = @filtered_posts.where('post_number = 1 or user_id in (select u.id from users u where username_lower in (?))', usernames)
|
||||
end
|
||||
|
||||
@user = user
|
||||
@initial_load = true
|
||||
@index_reverse = false
|
||||
|
||||
filter_posts(options)
|
||||
|
||||
@draft_key = @topic.draft_key
|
||||
@draft_sequence = DraftSequence.current(user, @draft_key)
|
||||
@draft_sequence = DraftSequence.current(@user, @draft_key)
|
||||
end
|
||||
|
||||
def canonical_path
|
||||
|
@ -317,6 +317,8 @@ class TopicView
|
|||
end
|
||||
|
||||
def find_topic(topic_id)
|
||||
Topic.where(id: topic_id).includes(:category).first
|
||||
finder = Topic.where(id: topic_id).includes(:category)
|
||||
finder = finder.with_deleted if @user.try(:staff?)
|
||||
finder.first
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,7 +5,6 @@ module Trashable
|
|||
default_scope where(with_deleted_scope_sql)
|
||||
|
||||
# scope unscoped does not work
|
||||
|
||||
belongs_to :deleted_by, class_name: 'User'
|
||||
end
|
||||
|
||||
|
@ -26,6 +25,10 @@ module Trashable
|
|||
end
|
||||
end
|
||||
|
||||
def trashed?
|
||||
deleted_at.present?
|
||||
end
|
||||
|
||||
def trash!(trashed_by=nil)
|
||||
# note, an argument could be made that the column should probably called trashed_at
|
||||
# however, deleted_at is the terminology used in the UI
|
||||
|
|
|
@ -318,7 +318,6 @@ describe Guardian do
|
|||
end
|
||||
|
||||
context 'regular users' do
|
||||
|
||||
it "doesn't allow new posts from regular users" do
|
||||
Guardian.new(coding_horror).can_create?(Post, topic).should be_false
|
||||
end
|
||||
|
@ -326,7 +325,6 @@ describe Guardian do
|
|||
it 'allows editing of posts' do
|
||||
Guardian.new(coding_horror).can_edit?(post).should be_false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
it "allows new posts from moderators" do
|
||||
|
@ -338,6 +336,26 @@ describe Guardian do
|
|||
end
|
||||
end
|
||||
|
||||
context "trashed topic" do
|
||||
before do
|
||||
topic.deleted_at = Time.now
|
||||
end
|
||||
|
||||
it "doesn't allow new posts from regular users" do
|
||||
Guardian.new(coding_horror).can_create?(Post, topic).should be_false
|
||||
end
|
||||
|
||||
it "doesn't allow new posts from moderators users" do
|
||||
Guardian.new(moderator).can_create?(Post, topic).should be_false
|
||||
end
|
||||
|
||||
it "doesn't allow new posts from admins" do
|
||||
Guardian.new(admin).can_create?(Post, topic).should be_false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -18,6 +18,14 @@ describe TopicView do
|
|||
lambda { topic_view }.should raise_error(Discourse::InvalidAccess)
|
||||
end
|
||||
|
||||
it "handles deleted topics" do
|
||||
topic.trash!(coding_horror)
|
||||
lambda { TopicView.new(topic.id, coding_horror) }.should raise_error(Discourse::NotFound)
|
||||
coding_horror.stubs(:staff?).returns(true)
|
||||
lambda { TopicView.new(topic.id, coding_horror) }.should_not raise_error
|
||||
end
|
||||
|
||||
|
||||
context "with a few sample posts" do
|
||||
let!(:p1) { Fabricate(:post, topic: topic, user: first_poster, percent_rank: 1 )}
|
||||
let!(:p2) { Fabricate(:post, topic: topic, user: coding_horror, percent_rank: 0.5 )}
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
module("Discourse.Topic");
|
||||
|
||||
test("defaults", function() {
|
||||
var topic = Discourse.Topic.create({id: 1234});
|
||||
blank(topic.get('deleted_at'), 'deleted_at defaults to blank');
|
||||
blank(topic.get('deleted_by'), 'deleted_by defaults to blank');
|
||||
});
|
||||
|
||||
test('has details', function() {
|
||||
var topic = Discourse.Topic.create({id: 1234});
|
||||
var topicDetails = topic.get('details');
|
||||
|
@ -36,4 +42,16 @@ test("updateFromJson", function() {
|
|||
equal(topic.get('details.hello'), 'world', 'it updates the details');
|
||||
equal(topic.get('cool'), "property", "it updates other properties");
|
||||
equal(topic.get('category'), category);
|
||||
});
|
||||
|
||||
test("destroy", function() {
|
||||
var topic = Discourse.Topic.create({id: 1234});
|
||||
var user = Discourse.User.create({username: 'eviltrout'});
|
||||
|
||||
this.stub(Discourse, 'ajax');
|
||||
|
||||
topic.destroy(user);
|
||||
present(topic.get('deleted_at'), 'deleted at is set');
|
||||
equal(topic.get('deleted_by'), user, 'deleted by is set');
|
||||
ok(Discourse.ajax.calledOnce, "it called delete over the wire");
|
||||
});
|
Loading…
Reference in New Issue
Block a user