mirror of
https://github.com/flarum/framework.git
synced 2024-12-02 06:53:47 +08:00
New tag selection modal when composing a discussion
Also numerous bug fixes. Still WIP
This commit is contained in:
parent
c9a03d9d8a
commit
e3b26b48a9
8
extensions/tags/js/bootstrap.js
vendored
8
extensions/tags/js/bootstrap.js
vendored
|
@ -8,6 +8,8 @@ import TagsPage from 'flarum-tags/components/tags-page';
|
|||
import addTagList from 'flarum-tags/add-tag-list';
|
||||
import addTagFilter from 'flarum-tags/add-tag-filter';
|
||||
import addTagLabels from 'flarum-tags/add-tag-labels';
|
||||
import addTagDiscussionControl from 'flarum-tags/add-tag-discussion-control';
|
||||
import addTagComposer from 'flarum-tags/add-tag-composer';
|
||||
|
||||
app.initializers.add('flarum-tags', function() {
|
||||
// Register routes.
|
||||
|
@ -17,7 +19,7 @@ app.initializers.add('flarum-tags', function() {
|
|||
// Register models.
|
||||
app.store.models['tags'] = Tag;
|
||||
Discussion.prototype.tags = Model.many('tags');
|
||||
Discussion.prototype.canMove = Model.prop('canMove');
|
||||
Discussion.prototype.canTag = Model.prop('canTag');
|
||||
|
||||
// Add a list of tags to the index navigation.
|
||||
addTagList();
|
||||
|
@ -28,7 +30,7 @@ app.initializers.add('flarum-tags', function() {
|
|||
// Add tags to the discussion list and discussion hero.
|
||||
addTagLabels();
|
||||
|
||||
// addMoveDiscussionControl();
|
||||
addTagDiscussionControl();
|
||||
|
||||
// addDiscussionComposer();
|
||||
addTagComposer();
|
||||
});
|
||||
|
|
54
extensions/tags/js/src/add-tag-composer.js
Normal file
54
extensions/tags/js/src/add-tag-composer.js
Normal file
|
@ -0,0 +1,54 @@
|
|||
import { extend, override } from 'flarum/extension-utils';
|
||||
import IndexPage from 'flarum/components/index-page';
|
||||
import DiscussionComposer from 'flarum/components/discussion-composer';
|
||||
import icon from 'flarum/helpers/icon';
|
||||
|
||||
import TagDiscussionModal from 'flarum-tags/components/tag-discussion-modal';
|
||||
import tagsLabel from 'flarum-tags/helpers/tags-label';
|
||||
|
||||
export default function() {
|
||||
override(IndexPage.prototype, 'composeNewDiscussion', function(original, deferred) {
|
||||
var tag = app.store.getBy('tags', 'slug', this.params().tags);
|
||||
|
||||
app.modal.show(
|
||||
new TagDiscussionModal({
|
||||
selectedTags: tag ? [tag] : [],
|
||||
onsubmit: tags => {
|
||||
original(deferred).then(component => component.tags(tags));
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
return deferred.promise;
|
||||
});
|
||||
|
||||
// Add tag-selection abilities to the discussion composer.
|
||||
DiscussionComposer.prototype.tags = m.prop([]);
|
||||
DiscussionComposer.prototype.chooseTags = function() {
|
||||
app.modal.show(
|
||||
new TagDiscussionModal({
|
||||
selectedTags: this.tags().slice(0),
|
||||
onsubmit: tags => {
|
||||
this.tags(tags);
|
||||
this.$('textarea').focus();
|
||||
}
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
// Add a tag-selection menu to the discussion composer's header, after the
|
||||
// title.
|
||||
extend(DiscussionComposer.prototype, 'headerItems', function(items) {
|
||||
var tags = this.tags();
|
||||
|
||||
items.add('tags', m('a[href=javascript:;][tabindex=-1].control-change-tags', {onclick: this.chooseTags.bind(this)}, [
|
||||
tagsLabel(tags)
|
||||
]));
|
||||
});
|
||||
|
||||
// Add the selected tags as data to submit to the server.
|
||||
extend(DiscussionComposer.prototype, 'data', function(data) {
|
||||
data.links = data.links || {};
|
||||
data.links.tags = this.tags();
|
||||
});
|
||||
};
|
18
extensions/tags/js/src/add-tag-discussion-control.js
Normal file
18
extensions/tags/js/src/add-tag-discussion-control.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { extend } from 'flarum/extension-utils';
|
||||
import Discussion from 'flarum/models/discussion';
|
||||
import ActionButton from 'flarum/components/action-button';
|
||||
|
||||
import TagDiscussionModal from 'flarum-tags/components/tag-discussion-modal';
|
||||
|
||||
export default function() {
|
||||
// Add a control allowing the discussion to be moved to another category.
|
||||
extend(Discussion.prototype, 'controls', function(items) {
|
||||
if (this.canTag()) {
|
||||
items.add('tags', ActionButton.component({
|
||||
label: 'Edit Tags',
|
||||
icon: 'tag',
|
||||
onclick: () => app.modal.show(new TagDiscussionModal({ discussion: this }))
|
||||
}), {after: 'rename'});
|
||||
}
|
||||
});
|
||||
};
|
|
@ -4,12 +4,13 @@ import DiscussionPage from 'flarum/components/discussion-page';
|
|||
import DiscussionHero from 'flarum/components/discussion-hero';
|
||||
|
||||
import tagsLabel from 'flarum-tags/helpers/tags-label';
|
||||
import sortTags from 'flarum-tags/utils/sort-tags';
|
||||
|
||||
export default function() {
|
||||
// Add tag labels to each discussion in the discussion list.
|
||||
extend(DiscussionList.prototype, 'infoItems', function(items, discussion) {
|
||||
var tags = discussion.tags();
|
||||
if (tags) {
|
||||
if (tags && tags.length) {
|
||||
items.add('tags', tagsLabel(tags.filter(tag => tag.slug() !== this.props.params.tags)), {first: true});
|
||||
}
|
||||
});
|
||||
|
@ -21,8 +22,8 @@ export default function() {
|
|||
|
||||
// Restyle a discussion's hero to use its first tag's color.
|
||||
extend(DiscussionHero.prototype, 'view', function(view) {
|
||||
var tags = this.props.discussion.tags();
|
||||
if (tags) {
|
||||
var tags = sortTags(this.props.discussion.tags());
|
||||
if (tags && tags.length) {
|
||||
view.attrs.style = 'color: #fff; background-color: '+tags[0].color();
|
||||
}
|
||||
});
|
||||
|
@ -31,7 +32,7 @@ export default function() {
|
|||
// before the title. Put the title on its own line.
|
||||
extend(DiscussionHero.prototype, 'items', function(items) {
|
||||
var tags = this.props.discussion.tags();
|
||||
if (tags) {
|
||||
if (tags && tags.length) {
|
||||
items.add('tags', tagsLabel(tags, {link: true}), {before: 'title'});
|
||||
|
||||
items.title.content.wrapperClass = 'block-item';
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
import Component from 'flarum/component';
|
||||
import DiscussionPage from 'flarum/components/discussion-page';
|
||||
import icon from 'flarum/helpers/icon';
|
||||
import categoryLabel from 'flarum-categories/helpers/category-label';
|
||||
|
||||
export default class MoveDiscussionModal extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.categories = m.prop(app.store.all('categories'));
|
||||
}
|
||||
|
||||
view() {
|
||||
var discussion = this.props.discussion;
|
||||
var discussionCategory = discussion && discussion.category();
|
||||
|
||||
return m('div.modal-dialog.modal-move-discussion', [
|
||||
m('div.modal-content', [
|
||||
m('button.btn.btn-icon.btn-link.close.back-control', {onclick: app.modal.close.bind(app.modal)}, icon('times')),
|
||||
m('div.modal-header', m('h3.title-control', discussion
|
||||
? ['Move ', m('em', discussion.title()), ' from ', categoryLabel(discussionCategory), ' to...']
|
||||
: ['Start a Discussion In...'])),
|
||||
m('div', [
|
||||
m('ul.category-list', [
|
||||
this.categories().map(category =>
|
||||
(discussion && discussionCategory && category.id() === discussionCategory.id()) ? '' : m('li.category-tile', {style: 'background-color: '+category.color()}, [
|
||||
m('a[href=javascript:;]', {onclick: this.save.bind(this, category)}, [
|
||||
m('h3.title', category.title()),
|
||||
m('p.description', category.description()),
|
||||
m('span.count', category.discussionsCount()+' discussions'),
|
||||
])
|
||||
])
|
||||
)
|
||||
])
|
||||
])
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
save(category) {
|
||||
var discussion = this.props.discussion;
|
||||
|
||||
if (discussion) {
|
||||
discussion.save({links: {category}}).then(discussion => {
|
||||
if (app.current instanceof DiscussionPage) {
|
||||
app.current.stream.sync();
|
||||
}
|
||||
m.redraw();
|
||||
});
|
||||
}
|
||||
|
||||
this.props.onchange && this.props.onchange(category);
|
||||
|
||||
app.modal.close();
|
||||
|
||||
m.redraw.strategy('none');
|
||||
}
|
||||
}
|
234
extensions/tags/js/src/components/tag-discussion-modal.js
Normal file
234
extensions/tags/js/src/components/tag-discussion-modal.js
Normal file
|
@ -0,0 +1,234 @@
|
|||
import FormModal from 'flarum/components/form-modal';
|
||||
import DiscussionPage from 'flarum/components/discussion-page';
|
||||
import highlight from 'flarum/helpers/highlight';
|
||||
import classList from 'flarum/utils/class-list';
|
||||
|
||||
import tagLabel from 'flarum-tags/helpers/tag-label';
|
||||
import tagIcon from 'flarum-tags/helpers/tag-icon';
|
||||
import sortTags from 'flarum-tags/utils/sort-tags';
|
||||
|
||||
export default class TagDiscussionModal extends FormModal {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.tags = sortTags(app.store.all('tags'));
|
||||
|
||||
this.selected = m.prop([]);
|
||||
if (this.props.selectedTags) {
|
||||
this.props.selectedTags.map(this.addTag.bind(this));
|
||||
} else if (this.props.discussion) {
|
||||
this.props.discussion.tags().map(this.addTag.bind(this));
|
||||
}
|
||||
|
||||
this.filter = m.prop('');
|
||||
|
||||
this.index = m.prop(this.tags[0].id());
|
||||
|
||||
this.focused = m.prop(false);
|
||||
}
|
||||
|
||||
addTag(tag) {
|
||||
var selected = this.selected();
|
||||
var parent = tag.parent();
|
||||
if (parent) {
|
||||
var index = selected.indexOf(parent);
|
||||
if (index === -1) {
|
||||
selected.push(parent);
|
||||
}
|
||||
}
|
||||
selected.push(tag);
|
||||
}
|
||||
|
||||
removeTag(tag) {
|
||||
var selected = this.selected();
|
||||
var index = selected.indexOf(tag);
|
||||
selected.splice(index, 1);
|
||||
selected.filter(selected => selected.parent() && selected.parent() === tag).forEach(child => {
|
||||
var index = selected.indexOf(child);
|
||||
selected.splice(index, 1);
|
||||
});
|
||||
}
|
||||
|
||||
view() {
|
||||
var discussion = this.props.discussion;
|
||||
var selected = this.selected();
|
||||
|
||||
var tags = this.tags;
|
||||
var filter = this.filter().toLowerCase();
|
||||
|
||||
if (filter) {
|
||||
tags = tags.filter(tag => tag.name().substr(0, filter.length).toLowerCase() === filter);
|
||||
}
|
||||
|
||||
if (tags.indexOf(this.index()) === -1) {
|
||||
this.index(tags[0]);
|
||||
}
|
||||
|
||||
return super.view({
|
||||
className: 'tag-discussion-modal',
|
||||
title: discussion
|
||||
? ['Edit Tags for ', m('em', discussion.title())]
|
||||
: 'Start a Discussion About...',
|
||||
body: [
|
||||
m('div.tags-form', [
|
||||
m('div.tags-input.form-control', {className: this.focused() ? 'focus' : ''}, [
|
||||
m('span.tags-input-selected', selected.map(tag =>
|
||||
m('span.remove-tag', {onclick: () => {
|
||||
this.removeTag(tag);
|
||||
this.ready();
|
||||
}}, tagLabel(tag))
|
||||
)),
|
||||
m('input.form-control', {
|
||||
placeholder: !selected.length ? 'Choose one or more topics' : '',
|
||||
value: this.filter(),
|
||||
oninput: m.withAttr('value', this.filter),
|
||||
onkeydown: this.onkeydown.bind(this),
|
||||
onfocus: () => this.focused(true),
|
||||
onblur: () => this.focused(false)
|
||||
})
|
||||
]),
|
||||
m('button[type=submit].btn.btn-primary', {disabled: !selected.length}, 'Confirm')
|
||||
])
|
||||
],
|
||||
footer: [
|
||||
m('ul.tags-select', tags.map(tag =>
|
||||
filter || !tag.parent() || selected.indexOf(tag.parent()) !== -1
|
||||
? m('li', {
|
||||
'data-index': tag.id(),
|
||||
className: classList({
|
||||
category: tag.position() !== null,
|
||||
selected: selected.indexOf(tag) !== -1,
|
||||
active: this.index() == tag
|
||||
}),
|
||||
style: {
|
||||
color: tag.color()
|
||||
},
|
||||
onmouseover: () => {
|
||||
this.index(tag);
|
||||
},
|
||||
onclick: () => {
|
||||
var selected = this.selected();
|
||||
var index = selected.indexOf(tag);
|
||||
if (index !== -1) {
|
||||
this.removeTag(tag);
|
||||
} else {
|
||||
this.addTag(tag);
|
||||
}
|
||||
if (this.filter()) {
|
||||
this.filter('');
|
||||
this.index(this.tags[0]);
|
||||
}
|
||||
this.ready();
|
||||
}
|
||||
}, [
|
||||
tagIcon(tag),
|
||||
m('span.name', highlight(tag.name(), filter)),
|
||||
tag.description() ? m('span.description', tag.description()) : ''
|
||||
])
|
||||
: ''
|
||||
))
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
onkeydown(e) {
|
||||
switch (e.which) {
|
||||
case 40:
|
||||
case 38: // Down/Up
|
||||
e.preventDefault();
|
||||
this.setIndex(this.getCurrentNumericIndex() + (e.which === 40 ? 1 : -1), true);
|
||||
break;
|
||||
|
||||
case 13: // Return
|
||||
e.preventDefault();
|
||||
if (e.metaKey || e.ctrlKey || this.selected().indexOf(this.index()) !== -1) {
|
||||
if (this.selected().length) {
|
||||
this.$('form').submit();
|
||||
}
|
||||
} else {
|
||||
this.getItem(this.index())[0].dispatchEvent(new Event('click'));
|
||||
}
|
||||
break;
|
||||
|
||||
case 8: // Backspace
|
||||
if (e.target.selectionStart == 0 && e.target.selectionEnd == 0) {
|
||||
e.preventDefault();
|
||||
var selected = this.selected();
|
||||
selected.splice(selected.length - 1, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
selectableItems() {
|
||||
return this.$('.tags-select > li');
|
||||
}
|
||||
|
||||
getCurrentNumericIndex() {
|
||||
return this.selectableItems().index(
|
||||
this.getItem(this.index())
|
||||
);
|
||||
}
|
||||
|
||||
getItem(index) {
|
||||
var $items = this.selectableItems();
|
||||
return $items.filter('[data-index='+index.id()+']');
|
||||
}
|
||||
|
||||
setIndex(index, scrollToItem) {
|
||||
var $items = this.selectableItems();
|
||||
var $dropdown = $items.parent();
|
||||
|
||||
if (index < 0) {
|
||||
index = $items.length - 1;
|
||||
} else if (index >= $items.length) {
|
||||
index = 0;
|
||||
}
|
||||
|
||||
var $item = $items.eq(index);
|
||||
|
||||
this.index(app.store.getById('tags', $item.attr('data-index')));
|
||||
|
||||
m.redraw();
|
||||
|
||||
if (scrollToItem) {
|
||||
var dropdownScroll = $dropdown.scrollTop();
|
||||
var dropdownTop = $dropdown.offset().top;
|
||||
var dropdownBottom = dropdownTop + $dropdown.outerHeight();
|
||||
var itemTop = $item.offset().top;
|
||||
var itemBottom = itemTop + $item.outerHeight();
|
||||
|
||||
var scrollTop;
|
||||
if (itemTop < dropdownTop) {
|
||||
scrollTop = dropdownScroll - dropdownTop + itemTop - parseInt($dropdown.css('padding-top'));
|
||||
} else if (itemBottom > dropdownBottom) {
|
||||
scrollTop = dropdownScroll - dropdownBottom + itemBottom + parseInt($dropdown.css('padding-bottom'));
|
||||
}
|
||||
|
||||
if (typeof scrollTop !== 'undefined') {
|
||||
$dropdown.stop(true).animate({scrollTop}, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onsubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var discussion = this.props.discussion;
|
||||
var tags = this.selected();
|
||||
|
||||
if (discussion) {
|
||||
discussion.save({links: {tags}}).then(discussion => {
|
||||
if (app.current instanceof DiscussionPage) {
|
||||
app.current.stream.sync();
|
||||
}
|
||||
m.redraw();
|
||||
});
|
||||
}
|
||||
|
||||
this.props.onsubmit && this.props.onsubmit(tags);
|
||||
|
||||
app.modal.close();
|
||||
|
||||
m.redraw.strategy('none');
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import tagLabel from 'flarum-tags/helpers/tag-label';
|
||||
import sortTags from 'flarum-tags/utils/sort-tags';
|
||||
|
||||
export default function tagsLabel(tags, attrs) {
|
||||
attrs = attrs || {};
|
||||
|
@ -8,7 +9,7 @@ export default function tagsLabel(tags, attrs) {
|
|||
delete attrs.link;
|
||||
|
||||
if (tags) {
|
||||
tags.forEach(tag => {
|
||||
sortTags(tags).forEach(tag => {
|
||||
children.push(tagLabel(tag, {link}));
|
||||
});
|
||||
} else {
|
||||
|
|
25
extensions/tags/js/src/utils/sort-tags.js
Normal file
25
extensions/tags/js/src/utils/sort-tags.js
Normal file
|
@ -0,0 +1,25 @@
|
|||
export default function sortTags(tags) {
|
||||
return tags.slice(0).sort((a, b) => {
|
||||
var aPos = a.position();
|
||||
var bPos = b.position();
|
||||
|
||||
var aParent = a.parent();
|
||||
var bParent = b.parent();
|
||||
|
||||
if (aPos === null && bPos === null) {
|
||||
return b.discussionsCount() - a.discussionsCount();
|
||||
} else if (bPos === null) {
|
||||
return -1;
|
||||
} else if (aPos === null) {
|
||||
return 1;
|
||||
} else if (aParent === bParent) {
|
||||
return aPos - bPos;
|
||||
} else if (aParent) {
|
||||
return aParent.position() - bPos;
|
||||
} else if (bParent) {
|
||||
return aPos - bParent.position();
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
};
|
|
@ -5,6 +5,7 @@
|
|||
padding: 0.2em 0.55em;
|
||||
border-radius: @border-radius-base;
|
||||
background: @fl-body-secondary-color;
|
||||
color: @fl-body-muted-color;
|
||||
|
||||
&.untagged {
|
||||
background: transparent;
|
||||
|
@ -91,3 +92,122 @@
|
|||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.tag-discussion-modal {
|
||||
& .modal-header {
|
||||
background: @fl-body-secondary-color;
|
||||
padding: 20px;
|
||||
|
||||
& h3 {
|
||||
text-align: left;
|
||||
color: @fl-body-muted-color;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
& .modal-body {
|
||||
padding: 0 20px 20px;
|
||||
}
|
||||
& .modal-footer {
|
||||
padding: 1px 0 0;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
.tags-form {
|
||||
padding-right: 100px;
|
||||
overflow: hidden;
|
||||
|
||||
& .tags-input {
|
||||
float: left;
|
||||
}
|
||||
& .btn {
|
||||
margin-right: -100px;
|
||||
float: right;
|
||||
width: 85px;
|
||||
}
|
||||
}
|
||||
.tags-input {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
||||
& input {
|
||||
display: inline;
|
||||
outline: none;
|
||||
margin-top: -2px;
|
||||
border: 0 !important;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
margin-right: -100%;
|
||||
}
|
||||
& .remove-tag {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
.tags-input-selected {
|
||||
& .tag-label {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.tags-select {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
overflow: auto;
|
||||
max-height: 50vh;
|
||||
|
||||
& > li {
|
||||
padding: 7px 20px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
cursor: pointer;
|
||||
|
||||
&.category {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
|
||||
& .name {
|
||||
font-size: 16px;
|
||||
}
|
||||
&.selected .tag-icon:before {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
&.active {
|
||||
background: @fl-body-secondary-color;
|
||||
}
|
||||
& .name {
|
||||
display: inline-block;
|
||||
width: 150px;
|
||||
margin-right: 10px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
& .description {
|
||||
color: @fl-body-muted-color;
|
||||
font-size: 12px;
|
||||
}
|
||||
&.selected {
|
||||
& .tag-icon {
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
.fa();
|
||||
content: @fa-var-check;
|
||||
color: @fl-body-muted-color;
|
||||
position: absolute;
|
||||
font-size: 14px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
padding-top: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
& mark {
|
||||
font-weight: bold;
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,13 +17,20 @@ class CreateTagsTable extends Migration
|
|||
$table->string('name', 100);
|
||||
$table->string('slug', 100);
|
||||
$table->text('description')->nullable();
|
||||
|
||||
$table->string('color', 50)->nullable();
|
||||
$table->string('background_path', 100)->nullable();
|
||||
$table->string('background_mode', 100)->nullable();
|
||||
$table->string('icon_path', 100)->nullable();
|
||||
$table->integer('discussions_count')->unsigned()->default(0);
|
||||
|
||||
$table->integer('position')->nullable();
|
||||
$table->integer('parent_id')->unsigned()->nullable();
|
||||
$table->string('default_sort', 50)->nullable();
|
||||
|
||||
$table->integer('discussions_count')->unsigned()->default(0);
|
||||
$table->integer('last_time')->unsigned()->nullable();
|
||||
$table->integer('last_discussion_id')->unsigned()->nullable();
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<?php namespace Flarum\Categories\Events;
|
||||
<?php namespace Flarum\Tags\Events;
|
||||
|
||||
use Flarum\Core\Models\Discussion;
|
||||
use Flarum\Core\Models\User;
|
||||
|
||||
class DiscussionWasMoved
|
||||
class DiscussionWasTagged
|
||||
{
|
||||
/**
|
||||
* @var \Flarum\Core\Models\Discussion
|
||||
|
@ -16,19 +16,19 @@ class DiscussionWasMoved
|
|||
public $user;
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
* @var array
|
||||
*/
|
||||
public $oldCategoryId;
|
||||
public $oldTags;
|
||||
|
||||
/**
|
||||
* @param \Flarum\Core\Models\Discussion $discussion
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @param \Flarum\Categories\Category $oldCategory
|
||||
*/
|
||||
public function __construct(Discussion $discussion, User $user, $oldCategoryId)
|
||||
public function __construct(Discussion $discussion, User $user, array $oldTags)
|
||||
{
|
||||
$this->discussion = $discussion;
|
||||
$this->user = $user;
|
||||
$this->oldCategoryId = $oldCategoryId;
|
||||
$this->oldTags = $oldTags;
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
<?php namespace Flarum\Categories\Handlers;
|
||||
|
||||
use Flarum\Categories\Events\DiscussionWasMoved;
|
||||
use Flarum\Core\Events\DiscussionWillBeSaved;
|
||||
|
||||
class CategorySaver
|
||||
{
|
||||
public function subscribe($events)
|
||||
{
|
||||
$events->listen('Flarum\Core\Events\DiscussionWillBeSaved', __CLASS__.'@whenDiscussionWillBeSaved');
|
||||
}
|
||||
|
||||
public function whenDiscussionWillBeSaved(DiscussionWillBeSaved $event)
|
||||
{
|
||||
if (isset($event->command->data['links']['category']['linkage'])) {
|
||||
$linkage = $event->command->data['links']['category']['linkage'];
|
||||
|
||||
$categoryId = (int) $linkage['id'];
|
||||
$discussion = $event->discussion;
|
||||
$user = $event->command->user;
|
||||
|
||||
$oldCategoryId = (int) $discussion->category_id;
|
||||
|
||||
if ($oldCategoryId === $categoryId) {
|
||||
return;
|
||||
}
|
||||
|
||||
$discussion->category_id = $categoryId;
|
||||
|
||||
if ($discussion->exists) {
|
||||
$discussion->raise(new DiscussionWasMoved($discussion, $user, $oldCategoryId));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
56
extensions/tags/src/Handlers/TagSaver.php
Executable file
56
extensions/tags/src/Handlers/TagSaver.php
Executable file
|
@ -0,0 +1,56 @@
|
|||
<?php namespace Flarum\Tags\Handlers;
|
||||
|
||||
use Flarum\Tags\Events\DiscussionWasTagged;
|
||||
use Flarum\Core\Events\DiscussionWillBeSaved;
|
||||
use Flarum\Core\Events\DiscussionWasDeleted;
|
||||
use Flarum\Core\Models\Discussion;
|
||||
|
||||
class TagSaver
|
||||
{
|
||||
public function subscribe($events)
|
||||
{
|
||||
$events->listen('Flarum\Core\Events\DiscussionWillBeSaved', __CLASS__.'@whenDiscussionWillBeSaved');
|
||||
$events->listen('Flarum\Core\Events\DiscussionWasDeleted', __CLASS__.'@whenDiscussionWasDeleted');
|
||||
}
|
||||
|
||||
public function whenDiscussionWillBeSaved(DiscussionWillBeSaved $event)
|
||||
{
|
||||
if (isset($event->command->data['links']['tags']['linkage'])) {
|
||||
$discussion = $event->discussion;
|
||||
$user = $event->command->user;
|
||||
$linkage = (array) $event->command->data['links']['tags']['linkage'];
|
||||
|
||||
$newTagIds = [];
|
||||
foreach ($linkage as $link) {
|
||||
$newTagIds[] = (int) $link['id'];
|
||||
}
|
||||
|
||||
$oldTags = [];
|
||||
|
||||
if ($discussion->exists) {
|
||||
$oldTags = $discussion->tags()->get();
|
||||
$oldTagIds = $oldTags->lists('id');
|
||||
|
||||
if ($oldTagIds == $newTagIds) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// @todo is there a better (safer) way to do this?
|
||||
// maybe store some info on the discussion model and then use the
|
||||
// DiscussionWasTagged event to actually save the data?
|
||||
Discussion::saved(function ($discussion) use ($newTagIds) {
|
||||
$discussion->tags()->sync($newTagIds);
|
||||
});
|
||||
|
||||
if ($discussion->exists) {
|
||||
$discussion->raise(new DiscussionWasTagged($discussion, $user, $oldTags->all()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function whenDiscussionWasDeleted(DiscussionWasDeleted $event)
|
||||
{
|
||||
$event->discussion->tags()->sync([]);
|
||||
}
|
||||
}
|
|
@ -25,9 +25,9 @@ class TagsServiceProvider extends ServiceProvider
|
|||
]),
|
||||
|
||||
new EventSubscribers([
|
||||
// 'Flarum\Categories\Handlers\DiscussionMovedNotifier',
|
||||
// 'Flarum\Tags\Handlers\DiscussionTaggedNotifier',
|
||||
'Flarum\Tags\Handlers\TagPreloader',
|
||||
// 'Flarum\Categories\Handlers\CategorySaver'
|
||||
'Flarum\Tags\Handlers\TagSaver'
|
||||
]),
|
||||
|
||||
new Relationship('Flarum\Core\Models\Discussion', 'tags', function ($model) {
|
||||
|
@ -38,7 +38,7 @@ class TagsServiceProvider extends ServiceProvider
|
|||
|
||||
new ApiInclude(['discussions.index', 'discussions.show'], 'tags', true),
|
||||
|
||||
(new Permission('discussion.editTags'))
|
||||
(new Permission('discussion.tag'))
|
||||
->serialize()
|
||||
->grant(function ($grant, $user) {
|
||||
$grant->where('start_user_id', $user->id);
|
||||
|
|
Loading…
Reference in New Issue
Block a user