1
0
mirror of https://github.com/flarum/core.git synced 2025-07-24 02:01:19 +02:00

Implement hard deletion and rename soft delete to hide

This commit is contained in:
Toby Zerner
2015-02-12 14:35:24 +10:30
parent c33697654a
commit 36787bcf45
18 changed files with 90 additions and 50 deletions

View File

@@ -21,8 +21,8 @@ export default Ember.Component.extend(FadeIn, HasItemLists, UseComposer, {
tagName: 'article', tagName: 'article',
classNames: ['post', 'post-comment'], classNames: ['post', 'post-comment'],
classNameBindings: [ classNameBindings: [
'post.isHidden:deleted', 'post.isHidden:is-hidden',
'post.isEdited:edited', 'post.isEdited:is-edited',
'revealContent:reveal-content' 'revealContent:reveal-content'
], ],
itemLists: ['controls', 'header', 'footer'], itemLists: ['controls', 'header', 'footer'],
@@ -35,7 +35,7 @@ export default Ember.Component.extend(FadeIn, HasItemLists, UseComposer, {
populateControls: function(items) { populateControls: function(items) {
if (this.get('post.isHidden')) { if (this.get('post.isHidden')) {
this.addActionItem(items, 'restore', 'Restore', 'reply', 'post.canEdit'); this.addActionItem(items, 'restore', 'Restore', 'reply', 'post.canEdit');
this.addActionItem(items, 'delete', 'Delete', 'times', 'post.canDelete'); this.addActionItem(items, 'delete', 'Delete Forever', 'times', 'post.canDelete');
} else { } else {
this.addActionItem(items, 'edit', 'Edit', 'pencil', 'post.canEdit'); this.addActionItem(items, 'edit', 'Edit', 'pencil', 'post.canEdit');
this.addActionItem(items, 'hide', 'Delete', 'times', 'post.canEdit'); this.addActionItem(items, 'hide', 'Delete', 'times', 'post.canEdit');
@@ -88,8 +88,8 @@ export default Ember.Component.extend(FadeIn, HasItemLists, UseComposer, {
var post = this.get('post'); var post = this.get('post');
post.setProperties({ post.setProperties({
isHidden: true, isHidden: true,
deleteTime: new Date, hideTime: new Date,
deleteUser: this.get('session.user') hideUser: this.get('session.user')
}); });
post.save(); post.save();
}, },
@@ -98,10 +98,16 @@ export default Ember.Component.extend(FadeIn, HasItemLists, UseComposer, {
var post = this.get('post'); var post = this.get('post');
post.setProperties({ post.setProperties({
isHidden: false, isHidden: false,
deleteTime: null, hideTime: null,
deleteUser: null hideUser: null
}); });
post.save(); post.save();
},
delete: function() {
var post = this.get('post');
post.destroyRecord();
this.sendAction('postRemoved', post);
} }
} }
}); });

View File

@@ -288,6 +288,10 @@ export default Ember.Component.extend({
loadRange: function(start, end, backwards) { loadRange: function(start, end, backwards) {
this.get('stream').loadRange(start, end, backwards); this.get('stream').loadRange(start, end, backwards);
},
postRemoved: function(post) {
this.sendAction('postRemoved', post);
} }
} }
}); });

View File

@@ -98,6 +98,10 @@ export default Ember.Controller.extend(Ember.Evented, UseComposerMixin, {
discussion.set('readNumber', endNumber); discussion.set('readNumber', endNumber);
discussion.save(); discussion.save();
} }
},
postRemoved: function(post) {
this.get('stream').removePost(post);
} }
} }
}); });

View File

@@ -168,6 +168,12 @@ export default Ember.ArrayProxy.extend(Ember.Evented, {
this.get('content').pushObject(this.makeItem(index, index, post)); this.get('content').pushObject(this.makeItem(index, index, post));
}, },
removePost: function(post) {
this.get('ids').removeObject(post.get('id'));
var content = this.get('content');
content.removeObject(content.findBy('content', post));
},
makeItem: function(indexStart, indexEnd, post) { makeItem: function(indexStart, indexEnd, post) {
var item = Ember.Object.create({ var item = Ember.Object.create({
indexStart: indexStart, indexStart: indexStart,

View File

@@ -15,9 +15,9 @@ export default DS.Model.extend({
editUser: DS.belongsTo('user'), editUser: DS.belongsTo('user'),
isEdited: Ember.computed.notEmpty('editTime'), isEdited: Ember.computed.notEmpty('editTime'),
hideTime: DS.attr('date'),
hideUser: DS.belongsTo('user'),
isHidden: DS.attr('boolean'), isHidden: DS.attr('boolean'),
deleteTime: DS.attr('date'),
deleteUser: DS.belongsTo('user'),
canEdit: DS.attr('boolean'), canEdit: DS.attr('boolean'),
canDelete: DS.attr('boolean') canDelete: DS.attr('boolean')

View File

@@ -0,0 +1,16 @@
import ApplicationSerializer from 'flarum/serializers/application';
export default ApplicationSerializer.extend({
attrs: {
number: {serialize: false},
time: {serialize: false},
type: {serialize: false},
contentHtml: {serialize: false},
editTime: {serialize: false},
editUser: {serialize: false},
hideTime: {serialize: false},
hideUser: {serialize: false},
canEdit: {serialize: false},
canDelete: {serialize: false}
}
});

View File

@@ -103,7 +103,6 @@
float: right; float: right;
margin: -2px 0 0 10px; margin: -2px 0 0 10px;
visibility: hidden; visibility: hidden;
z-index: 1;
} }
&:hover .contextual-controls, & .contextual-controls.open { &:hover .contextual-controls, & .contextual-controls.open {
visibility: visible; visibility: visible;
@@ -159,7 +158,7 @@
text-align: center; text-align: center;
font-size: 22px; font-size: 22px;
} }
.post.deleted { .post.is-hidden {
& .post-user, & .post-header > ul, & .post-header > ul a:not(.btn) { & .post-user, & .post-header > ul, & .post-header > ul a:not(.btn) {
color: @fl-body-muted-more-color; color: @fl-body-muted-more-color;
} }
@@ -173,6 +172,10 @@
opacity: 0.5; opacity: 0.5;
} }
} }
& .btn-more {
background: #eee;
color: @fl-body-muted-more-color;
}
} }
.post-meta { .post-meta {
width: 400px; width: 400px;

View File

@@ -1,7 +1,7 @@
{{#each item in stream}} {{#each item in stream}}
{{#discussion/stream-item item=item stream=stream loadRange="loadRange"}} {{#discussion/stream-item item=item stream=stream loadRange="loadRange"}}
{{#if item.content}} {{#if item.content}}
{{component item.component content=item.content}} {{component item.component content=item.content postRemoved="postRemoved"}}
{{/if}} {{/if}}
{{/discussion/stream-item}} {{/discussion/stream-item}}
{{/each}} {{/each}}

View File

@@ -13,5 +13,6 @@
viewName="streamContent" viewName="streamContent"
stream=stream stream=stream
class="discussion-posts posts" class="discussion-posts posts"
positionChanged="positionChanged"}} positionChanged="positionChanged"
postRemoved="postRemoved"}}
</div> </div>

View File

@@ -12,7 +12,7 @@ class Show extends Base
/** /**
* The post repository. * The post repository.
* *
* @var PostRepository * @var PostRepository
*/ */
protected $posts; protected $posts;
@@ -39,7 +39,7 @@ class Show extends Base
$discussion = Discussion::whereCanView()->findOrFail($this->param('id')); $discussion = Discussion::whereCanView()->findOrFail($this->param('id'));
if (in_array('posts', $include)) { if (in_array('posts', $include)) {
$relations = ['user', 'user.groups', 'editUser', 'deleteUser']; $relations = ['user', 'user.groups', 'editUser', 'hideUser'];
$discussion->posts = $this->getPostsForDiscussion($this->posts, $discussion->id, $relations); $discussion->posts = $this->getPostsForDiscussion($this->posts, $discussion->id, $relations);
$include = array_merge($include, array_map(function ($relation) { $include = array_merge($include, array_map(function ($relation) {

View File

@@ -12,7 +12,7 @@ class Index extends Base
/** /**
* The post repository. * The post repository.
* *
* @var PostRepository * @var PostRepository
*/ */
protected $posts; protected $posts;
@@ -35,7 +35,7 @@ class Index extends Base
protected function run() protected function run()
{ {
$postIds = (array) $this->input('ids'); $postIds = (array) $this->input('ids');
$include = ['user', 'user.groups', 'editUser', 'deleteUser']; $include = ['user', 'user.groups', 'editUser', 'hideUser'];
if (count($postIds)) { if (count($postIds)) {
$posts = $this->posts->findMany($postIds, $include); $posts = $this->posts->findMany($postIds, $include);

View File

@@ -23,7 +23,7 @@ class Show extends Base
} }
$include = $this->included(['discussion', 'replyTo']); $include = $this->included(['discussion', 'replyTo']);
$relations = array_merge(['user', 'editUser', 'deleteUser'], $include); $relations = array_merge(['user', 'editUser', 'hideUser'], $include);
$posts->load($relations); $posts->load($relations);
// Finally, we can set up the post serializer and use it to create // Finally, we can set up the post serializer and use it to create

View File

@@ -21,7 +21,7 @@ class PostSerializer extends PostBasicSerializer
* Default relations to include. * Default relations to include.
* @var array * @var array
*/ */
protected $include = ['user', 'editUser', 'deleteUser']; protected $include = ['user', 'editUser', 'hideUser'];
/** /**
* Serialize attributes of a Post model for JSON output. * Serialize attributes of a Post model for JSON output.
@@ -51,9 +51,9 @@ class PostSerializer extends PostBasicSerializer
$attributes['editTime'] = $post->edit_time->toRFC3339String(); $attributes['editTime'] = $post->edit_time->toRFC3339String();
} }
if ($post->delete_time) { if ($post->hide_time) {
$attributes['isHidden'] = true; $attributes['isHidden'] = true;
$attributes['deleteTime'] = $post->delete_time->toRFC3339String(); $attributes['hideTime'] = $post->hide_time->toRFC3339String();
} }
$attributes += [ $attributes += [
@@ -66,7 +66,7 @@ class PostSerializer extends PostBasicSerializer
/** /**
* Get a resource containing a post's user. * Get a resource containing a post's user.
* *
* @param Post $post * @param Post $post
* @param array $relations * @param array $relations
* @return Tobscure\JsonApi\Resource * @return Tobscure\JsonApi\Resource
@@ -78,7 +78,7 @@ class PostSerializer extends PostBasicSerializer
/** /**
* Get a resource containing a post's discussion. * Get a resource containing a post's discussion.
* *
* @param Post $post * @param Post $post
* @param array $relations * @param array $relations
* @return Tobscure\JsonApi\Resource * @return Tobscure\JsonApi\Resource
@@ -90,7 +90,7 @@ class PostSerializer extends PostBasicSerializer
/** /**
* Get a resource containing a post's edit user. * Get a resource containing a post's edit user.
* *
* @param Post $post * @param Post $post
* @param array $relations * @param array $relations
* @return Tobscure\JsonApi\Resource * @return Tobscure\JsonApi\Resource
@@ -101,14 +101,14 @@ class PostSerializer extends PostBasicSerializer
} }
/** /**
* Get a resource containing a post's delete user. * Get a resource containing a post's hide user.
* *
* @param Post $post * @param Post $post
* @param array $relations * @param array $relations
* @return Tobscure\JsonApi\Resource * @return Tobscure\JsonApi\Resource
*/ */
public function includeDeleteUser(Post $post, $relations = []) public function includeHideUser(Post $post, $relations = [])
{ {
return (new UserBasicSerializer($relations))->resource($post->deleteUser); return (new UserBasicSerializer($relations))->resource($post->hideUser);
} }
} }

View File

@@ -111,7 +111,7 @@ class Discussion extends Entity
public function comments() public function comments()
{ {
return $this->posts()->where('type', 'comment')->whereNull('delete_time'); return $this->posts()->where('type', 'comment')->whereNull('hide_time');
} }
public function startPost() public function startPost()

View File

@@ -54,24 +54,24 @@ class CommentPost extends Post
public function hide($user) public function hide($user)
{ {
if ($this->delete_time) { if ($this->hide_time) {
return; return;
} }
$this->delete_time = time(); $this->hide_time = time();
$this->delete_user_id = $user->id; $this->hide_user_id = $user->id;
$this->raise(new Events\PostWasHidden($this)); $this->raise(new Events\PostWasHidden($this));
} }
public function restore($user) public function restore($user)
{ {
if ($this->delete_time === null) { if ($this->hide_time === null) {
return; return;
} }
$this->delete_time = null; $this->hide_time = null;
$this->delete_user_id = null; $this->hide_user_id = null;
$this->raise(new Events\PostWasRestored($this)); $this->raise(new Events\PostWasRestored($this));
} }

View File

@@ -23,8 +23,8 @@ class Post extends Entity
'user_id' => 'integer', 'user_id' => 'integer',
'edit_time' => 'date', 'edit_time' => 'date',
'edit_user_id' => 'integer', 'edit_user_id' => 'integer',
'delete_time' => 'date', 'hide_time' => 'date',
'delete_user_id' => 'integer', 'hide_user_id' => 'integer',
]; ];
public static function boot() public static function boot()
@@ -43,7 +43,7 @@ class Post extends Entity
}); });
static::check('view', function ($check, $user) { static::check('view', function ($check, $user) {
$check->whereNull('delete_user_id') $check->whereNull('hide_user_id')
->orWhereCan('edit'); ->orWhereCan('edit');
}); });
@@ -55,8 +55,8 @@ class Post extends Entity
}); });
static::check('editOwn', function ($check, $user) { static::check('editOwn', function ($check, $user) {
$check->whereNull('delete_user_id') $check->whereNull('hide_user_id')
->orWhere('delete_user_id', $user->id); ->orWhere('hide_user_id', $user->id);
}); });
static::deleted(function ($post) { static::deleted(function ($post) {
@@ -79,14 +79,14 @@ class Post extends Entity
return $this->belongsTo('Flarum\Core\Users\User', 'edit_user_id'); return $this->belongsTo('Flarum\Core\Users\User', 'edit_user_id');
} }
public function deleteUser() public function hideUser()
{ {
return $this->belongsTo('Flarum\Core\Users\User', 'delete_user_id'); return $this->belongsTo('Flarum\Core\Users\User', 'hide_user_id');
} }
public function getDates() public function getDates()
{ {
return ['time', 'edit_time', 'delete_time']; return ['time', 'edit_time', 'hide_time'];
} }
// Terminates the query and returns an array of matching IDs. // Terminates the query and returns an array of matching IDs.
@@ -122,7 +122,7 @@ class Post extends Entity
return $instance; return $instance;
} }
} }
return parent::newFromBuilder($attributes); return parent::newFromBuilder($attributes);
} }
} }

View File

@@ -67,9 +67,9 @@ class DiscussionTableSeeder extends Seeder
]); ]);
} else { } else {
$edited = rand(1, 20) == 1; $edited = rand(1, 20) == 1;
$deleted = rand(1, 100) == 1; $hidden = rand(1, 100) == 1;
if ($deleted) { if ($hidden) {
$discussion->comments_count--; $discussion->comments_count--;
} }
@@ -82,8 +82,8 @@ class DiscussionTableSeeder extends Seeder
'content' => $faker->realText(rand(50, 500)), 'content' => $faker->realText(rand(50, 500)),
'edit_time' => $edited ? $startTime = date_add($startTime, date_interval_create_from_date_string('1 second')) : null, 'edit_time' => $edited ? $startTime = date_add($startTime, date_interval_create_from_date_string('1 second')) : null,
'edit_user_id' => $edited ? rand(1, $users) : null, 'edit_user_id' => $edited ? rand(1, $users) : null,
'delete_time' => $deleted ? $startTime = date_add($startTime, date_interval_create_from_date_string('1 second')) : null, 'hide_time' => $hidden ? $startTime = date_add($startTime, date_interval_create_from_date_string('1 second')) : null,
'delete_user_id' => $deleted ? rand(1, $users) : null, 'hide_user_id' => $hidden ? rand(1, $users) : null,
]); ]);
$posts[] = $post; $posts[] = $post;

View File

@@ -26,8 +26,8 @@ class CreatePostsTable extends Migration {
$table->dateTime('edit_time')->nullable(); $table->dateTime('edit_time')->nullable();
$table->integer('edit_user_id')->unsigned()->nullable(); $table->integer('edit_user_id')->unsigned()->nullable();
$table->dateTime('delete_time')->nullable(); $table->dateTime('hide_time')->nullable();
$table->integer('delete_user_id')->unsigned()->nullable(); $table->integer('hide_user_id')->unsigned()->nullable();
}); });
// add fulltext index to content (and title?) // add fulltext index to content (and title?)