Merge branch 'discourse:main' into patch-1
This commit is contained in:
commit
eeea2875ea
|
@ -1,4 +1,5 @@
|
|||
3.1.999: 0cbf10b8055370445bd36536e51986bf48bdc57e
|
||||
< 3.2.0.beta2-dev: ac930c509e2a5b0c37b84bcea28d332e686add95
|
||||
3.1.999: a304cd2028ccf1f5b00f5137633aa7027a1fd334
|
||||
3.1.0.beta3: 9c270cac9abc1c2b30574d8c655fb3a90546236b
|
||||
2.9.0.beta8: 28bc8ab78a09551548c87f511ade3d64e1b04bc3
|
||||
2.9.0.beta3: 46f200935dc9e5750c3f2740abd993e27a9b3f6c
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
module.exports = require("@discourse/lint-configs/eslint");
|
|
@ -1,4 +1,3 @@
|
|||
.bundle/
|
||||
auto_generated
|
||||
.DS_Store
|
||||
node_modules/
|
||||
node_modules
|
||||
/gems
|
||||
/auto_generated
|
|
@ -1 +0,0 @@
|
|||
{}
|
|
@ -0,0 +1 @@
|
|||
module.exports = require("@discourse/lint-configs/prettier");
|
|
@ -0,0 +1 @@
|
|||
module.exports = require("@discourse/lint-configs/template-lint");
|
|
@ -1,4 +0,0 @@
|
|||
module.exports = {
|
||||
plugins: ["ember-template-lint-plugin-discourse"],
|
||||
extends: "discourse:recommended",
|
||||
};
|
44
Gemfile.lock
44
Gemfile.lock
|
@ -2,43 +2,45 @@ GEM
|
|||
remote: https://rubygems.org/
|
||||
specs:
|
||||
ast (2.4.2)
|
||||
json (2.6.3)
|
||||
parallel (1.23.0)
|
||||
parser (3.2.2.3)
|
||||
json (2.7.1)
|
||||
language_server-protocol (3.17.0.3)
|
||||
parallel (1.24.0)
|
||||
parser (3.3.0.3)
|
||||
ast (~> 2.4.1)
|
||||
racc
|
||||
prettier_print (1.2.1)
|
||||
racc (1.7.1)
|
||||
racc (1.7.3)
|
||||
rainbow (3.1.1)
|
||||
regexp_parser (2.8.1)
|
||||
rexml (3.2.5)
|
||||
rubocop (1.52.1)
|
||||
regexp_parser (2.9.0)
|
||||
rexml (3.2.6)
|
||||
rubocop (1.59.0)
|
||||
json (~> 2.3)
|
||||
language_server-protocol (>= 3.17.0)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 3.2.2.3)
|
||||
parser (>= 3.2.2.4)
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
regexp_parser (>= 1.8, < 3.0)
|
||||
rexml (>= 3.2.5, < 4.0)
|
||||
rubocop-ast (>= 1.28.0, < 2.0)
|
||||
rubocop-ast (>= 1.30.0, < 2.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (>= 2.4.0, < 3.0)
|
||||
rubocop-ast (1.29.0)
|
||||
rubocop-ast (1.30.0)
|
||||
parser (>= 3.2.1.0)
|
||||
rubocop-capybara (2.18.0)
|
||||
rubocop-capybara (2.20.0)
|
||||
rubocop (~> 1.41)
|
||||
rubocop-discourse (3.2.0)
|
||||
rubocop (>= 1.1.0)
|
||||
rubocop-rspec (>= 2.0.0)
|
||||
rubocop-factory_bot (2.23.1)
|
||||
rubocop (~> 1.33)
|
||||
rubocop-rspec (2.22.0)
|
||||
rubocop (~> 1.33)
|
||||
rubocop-discourse (3.6.0)
|
||||
rubocop (>= 1.59.0)
|
||||
rubocop-rspec (>= 2.25.0)
|
||||
rubocop-factory_bot (2.25.1)
|
||||
rubocop (~> 1.41)
|
||||
rubocop-rspec (2.26.1)
|
||||
rubocop (~> 1.40)
|
||||
rubocop-capybara (~> 2.17)
|
||||
rubocop-factory_bot (~> 2.22)
|
||||
ruby-progressbar (1.13.0)
|
||||
syntax_tree (6.1.1)
|
||||
syntax_tree (6.2.0)
|
||||
prettier_print (>= 1.2.0)
|
||||
unicode-display_width (2.4.2)
|
||||
unicode-display_width (2.5.0)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
@ -48,4 +50,4 @@ DEPENDENCIES
|
|||
syntax_tree
|
||||
|
||||
BUNDLED WITH
|
||||
2.3.4
|
||||
2.5.4
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) Civilized Discourse Construction Kit
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -159,43 +159,6 @@ module DiscourseAssign
|
|||
}
|
||||
end
|
||||
|
||||
def user_menu_assigns
|
||||
assign_notifications =
|
||||
Notification.unread_type(current_user, Notification.types[:assigned], user_menu_limit)
|
||||
|
||||
if assign_notifications.size < user_menu_limit
|
||||
opts = {}
|
||||
ignored_assignment_ids =
|
||||
assign_notifications.filter_map { |notification| notification.data_hash[:assignment_id] }
|
||||
opts[:ignored_assignment_ids] = ignored_assignment_ids if ignored_assignment_ids.present?
|
||||
|
||||
assigns_list =
|
||||
TopicQuery.new(
|
||||
current_user,
|
||||
per_page: user_menu_limit - assign_notifications.size,
|
||||
).list_messages_assigned(current_user, ignored_assignment_ids)
|
||||
end
|
||||
|
||||
if assign_notifications.present?
|
||||
serialized_notifications =
|
||||
ActiveModel::ArraySerializer.new(
|
||||
assign_notifications,
|
||||
each_serializer: NotificationSerializer,
|
||||
scope: guardian,
|
||||
)
|
||||
end
|
||||
|
||||
if assigns_list
|
||||
serialized_assigns =
|
||||
serialize_data(assigns_list, TopicListSerializer, scope: guardian, root: false)[:topics]
|
||||
end
|
||||
|
||||
render json: {
|
||||
notifications: serialized_notifications || [],
|
||||
topics: serialized_assigns || [],
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def translate_failure(reason, assign_to)
|
||||
|
@ -261,10 +224,6 @@ module DiscourseAssign
|
|||
raise Discourse::InvalidAccess.new unless current_user.can_assign?
|
||||
end
|
||||
|
||||
def user_menu_limit
|
||||
UsersController::USER_MENU_LIST_LIMIT
|
||||
end
|
||||
|
||||
def recent_assignees
|
||||
User
|
||||
.where("users.id <> ?", current_user.id)
|
||||
|
|
|
@ -3,79 +3,8 @@
|
|||
module Jobs
|
||||
class AssignNotification < ::Jobs::Base
|
||||
def execute(args)
|
||||
raise Discourse::InvalidParameters.new(:topic_id) if args[:topic_id].nil?
|
||||
raise Discourse::InvalidParameters.new(:post_id) if args[:post_id].nil?
|
||||
raise Discourse::InvalidParameters.new(:assigned_to_id) if args[:assigned_to_id].nil?
|
||||
raise Discourse::InvalidParameters.new(:assigned_to_type) if args[:assigned_to_type].nil?
|
||||
raise Discourse::InvalidParameters.new(:assigned_by_id) if args[:assigned_by_id].nil?
|
||||
raise Discourse::InvalidParameters.new(:assignment_id) if args[:assignment_id].nil?
|
||||
|
||||
if args[:skip_small_action_post].nil?
|
||||
raise Discourse::InvalidParameters.new(:skip_small_action_post)
|
||||
end
|
||||
|
||||
topic = Topic.find(args[:topic_id])
|
||||
post = Post.find(args[:post_id])
|
||||
assigned_by = User.find(args[:assigned_by_id])
|
||||
assigned_to =
|
||||
(
|
||||
if args[:assigned_to_type] == "User"
|
||||
User.find(args[:assigned_to_id])
|
||||
else
|
||||
Group.find(args[:assigned_to_id])
|
||||
end
|
||||
)
|
||||
assigned_to_users = args[:assigned_to_type] == "User" ? [assigned_to] : assigned_to.users
|
||||
|
||||
assigned_to_users.each do |user|
|
||||
Assigner.publish_topic_tracking_state(topic, user.id)
|
||||
|
||||
next if assigned_by == user
|
||||
|
||||
assigned_to_user = args[:assigned_to_type] == "User"
|
||||
|
||||
PostAlerter.new(post).create_notification_alert(
|
||||
user: user,
|
||||
post: post,
|
||||
username: assigned_by.username,
|
||||
notification_type: Notification.types[:assigned] || Notification.types[:custom],
|
||||
excerpt:
|
||||
I18n.t(
|
||||
(
|
||||
if assigned_to_user
|
||||
"discourse_assign.topic_assigned_excerpt"
|
||||
else
|
||||
"discourse_assign.topic_group_assigned_excerpt"
|
||||
end
|
||||
),
|
||||
title: topic.title,
|
||||
group: assigned_to.name,
|
||||
locale: user.effective_locale,
|
||||
),
|
||||
)
|
||||
|
||||
next if args[:skip_small_action_post]
|
||||
Notification.create!(
|
||||
notification_type: Notification.types[:assigned] || Notification.types[:custom],
|
||||
user_id: user.id,
|
||||
topic_id: topic.id,
|
||||
post_number: post.post_number,
|
||||
high_priority: true,
|
||||
data: {
|
||||
message:
|
||||
(
|
||||
if assigned_to_user
|
||||
"discourse_assign.assign_notification"
|
||||
else
|
||||
"discourse_assign.assign_group_notification"
|
||||
end
|
||||
),
|
||||
display_username: assigned_to_user ? assigned_by.username : assigned_to.name,
|
||||
topic_title: topic.title,
|
||||
assignment_id: args[:assignment_id],
|
||||
}.to_json,
|
||||
)
|
||||
end
|
||||
Assignment.find(args[:assignment_id]).create_missing_notifications!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,34 +3,18 @@
|
|||
module Jobs
|
||||
class UnassignNotification < ::Jobs::Base
|
||||
def execute(args)
|
||||
raise Discourse::InvalidParameters.new(:topic_id) if args[:topic_id].nil?
|
||||
raise Discourse::InvalidParameters.new(:assigned_to_id) if args[:assigned_to_id].nil?
|
||||
raise Discourse::InvalidParameters.new(:assigned_to_type) if args[:assigned_to_type].nil?
|
||||
|
||||
topic = Topic.find(args[:topic_id])
|
||||
assigned_to_users =
|
||||
(
|
||||
if args[:assigned_to_type] == "User"
|
||||
[User.find(args[:assigned_to_id])]
|
||||
else
|
||||
Group.find(args[:assigned_to_id]).users
|
||||
end
|
||||
)
|
||||
|
||||
assigned_to_users.each do |user|
|
||||
Assigner.publish_topic_tracking_state(topic, user.id)
|
||||
|
||||
Notification
|
||||
.where(
|
||||
notification_type: Notification.types[:assigned] || Notification.types[:custom],
|
||||
user_id: user.id,
|
||||
topic_id: topic.id,
|
||||
)
|
||||
.where(
|
||||
"data like '%discourse_assign.assign_notification%' OR data like '%discourse_assign.assign_group_notification%'",
|
||||
)
|
||||
.destroy_all
|
||||
%i[topic_id assigned_to_id assigned_to_type assignment_id].each do |argument|
|
||||
raise Discourse::InvalidParameters.new(argument) if args[argument].nil?
|
||||
end
|
||||
|
||||
assignment = Assignment.new(args.slice(:topic_id, :assigned_to_id, :assigned_to_type))
|
||||
assignment.assigned_users.each do |user|
|
||||
Assigner.publish_topic_tracking_state(assignment.topic, user.id)
|
||||
end
|
||||
Notification
|
||||
.for_assignment(args[:assignment_id])
|
||||
.where(user: assignment.assigned_users, topic: assignment.topic)
|
||||
.destroy_all
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,13 +9,15 @@ class Assignment < ActiveRecord::Base
|
|||
belongs_to :target, polymorphic: true
|
||||
|
||||
scope :joins_with_topics,
|
||||
-> {
|
||||
-> do
|
||||
joins(
|
||||
"INNER JOIN topics ON topics.id = assignments.target_id AND assignments.target_type = 'Topic' AND topics.deleted_at IS NULL",
|
||||
)
|
||||
}
|
||||
end
|
||||
|
||||
scope :active_for_group, ->(group) { where(assigned_to: group, active: true) }
|
||||
scope :active_for_group, ->(group) { active.where(assigned_to: group) }
|
||||
scope :active, -> { where(active: true) }
|
||||
scope :inactive, -> { where(active: false) }
|
||||
|
||||
before_validation :default_status
|
||||
|
||||
|
@ -38,11 +40,31 @@ class Assignment < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def assigned_to_user?
|
||||
assigned_to_type == "User"
|
||||
assigned_to.is_a?(User)
|
||||
end
|
||||
|
||||
def assigned_to_group?
|
||||
assigned_to_type == "Group"
|
||||
assigned_to.is_a?(Group)
|
||||
end
|
||||
|
||||
def assigned_users
|
||||
Array.wrap(assigned_to.try(:users) || assigned_to)
|
||||
end
|
||||
|
||||
def post
|
||||
return target.posts.find_by(post_number: 1) if target.is_a?(Topic)
|
||||
target
|
||||
end
|
||||
|
||||
def create_missing_notifications!
|
||||
assigned_users.each do |user|
|
||||
next if user.notifications.for_assignment(self).exists?
|
||||
DiscourseAssign::CreateNotification.call(
|
||||
assignment: self,
|
||||
user: user,
|
||||
mark_as_read: assigned_by_user == user,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -2,7 +2,7 @@ export default {
|
|||
resource: "user.userPrivateMessages",
|
||||
|
||||
map() {
|
||||
this.route("assigned", { path: "/assigned" }, function () {
|
||||
this.route("assigned", function () {
|
||||
this.route("index", { path: "/" });
|
||||
});
|
||||
},
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { action } from "@ember/object";
|
||||
import I18n from "I18n";
|
||||
import DropdownSelectBoxComponent from "select-kit/components/dropdown-select-box";
|
||||
import { action } from "@ember/object";
|
||||
|
||||
export default DropdownSelectBoxComponent.extend({
|
||||
classNames: ["assign-actions-dropdown"],
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
@id="assign-status"
|
||||
@content={{this.availableStatuses}}
|
||||
@value={{this.status}}
|
||||
@onChange={{action (mut @model.status)}}
|
||||
@onChange={{fn (mut @model.status)}}
|
||||
/>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
@ -51,7 +51,6 @@
|
|||
<Textarea
|
||||
id="assign-modal-note"
|
||||
@value={{@model.note}}
|
||||
{{! template-lint-disable no-down-event-binding }}
|
||||
{{on "keydown" this.handleTextAreaKeydown}}
|
||||
/>
|
||||
</div>
|
|
@ -9,8 +9,8 @@ export default class AssignUserForm extends Component {
|
|||
@service capabilities;
|
||||
|
||||
@tracked assigneeError = false;
|
||||
@tracked assigneeName =
|
||||
this.args.model.username || this.args.model.group_name;
|
||||
@tracked
|
||||
assigneeName = this.args.model.username || this.args.model.group_name;
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
|
@ -26,9 +26,7 @@ export default class AssignUserForm extends Component {
|
|||
|
||||
get status() {
|
||||
return (
|
||||
this.args.model.status ||
|
||||
this.args.model.target.assignment_status ||
|
||||
this.siteSettings.assign_statuses.split("|")[0]
|
||||
this.args.model.status || this.siteSettings.assign_statuses.split("|")[0]
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { action } from "@ember/object";
|
||||
import { inject as service } from "@ember/service";
|
||||
import AssignActionsDropdown from "./assign-actions-dropdown";
|
||||
|
||||
export default class AssignedTopicListColumn extends Component {
|
||||
@service taskActions;
|
||||
@service router;
|
||||
|
||||
@action
|
||||
async unassign(targetId, targetType = "Topic") {
|
||||
await this.taskActions.unassign(targetId, targetType);
|
||||
this.router.refresh();
|
||||
}
|
||||
|
||||
@action
|
||||
reassign(topic) {
|
||||
this.taskActions.showAssignModal(topic, {
|
||||
onSuccess: () => this.router.refresh(),
|
||||
});
|
||||
}
|
||||
|
||||
<template>
|
||||
{{#if @topic.assigned_to_user}}
|
||||
<AssignActionsDropdown
|
||||
@topic={{@topic}}
|
||||
@assignee={{@topic.assigned_to_user.username}}
|
||||
@unassign={{this.unassign}}
|
||||
@reassign={{this.reassign}}
|
||||
/>
|
||||
{{else if @topic.assigned_to_group}}
|
||||
<AssignActionsDropdown
|
||||
@topic={{@topic}}
|
||||
@assignee={{@topic.assigned_to_group.name}}
|
||||
@group={{true}}
|
||||
@unassign={{this.unassign}}
|
||||
@reassign={{this.reassign}}
|
||||
/>
|
||||
{{else}}
|
||||
<AssignActionsDropdown @topic={{@topic}} @unassign={{this.unassign}} />
|
||||
{{/if}}
|
||||
</template>
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
{{!
|
||||
The `~` syntax strip spaces between the elements, making it produce
|
||||
`<a class=topic-post-badges>Some text</a><span class=topic-post-badges>`,
|
||||
with no space between them.
|
||||
This causes the topic-post-badge to be considered the same word as "text"
|
||||
at the end of the link, preventing it from line wrapping onto its own line.
|
||||
}}
|
||||
{{#if this.bulkSelectEnabled}}
|
||||
<td class="bulk-select topic-list-data">
|
||||
<input type="checkbox" class="bulk-select" />
|
||||
</td>
|
||||
{{/if}}
|
||||
<td class="main-link clearfix topic-list-data" colspan="1">
|
||||
<span class="link-top-line">
|
||||
{{~raw "topic-status" topic=this.topic}}
|
||||
{{~#if this.isPrivateMessage}}
|
||||
{{~d-icon "envelope" class="private-message-icon"}}
|
||||
{{~/if}}
|
||||
{{~topic-link this.topic class="raw-link raw-topic-link"}}
|
||||
{{~#if this.topic.featured_link}}
|
||||
{{~topic-featured-link this.topic}}
|
||||
{{~/if}}
|
||||
{{~#if this.showTopicPostBadges}}
|
||||
{{~raw
|
||||
"topic-post-badges"
|
||||
unread=this.topic.unread
|
||||
unseen=this.topic.unseen
|
||||
url=this.topic.lastUnreadUrl
|
||||
newDotText=this.newDotText
|
||||
}}
|
||||
{{~/if}}
|
||||
</span>
|
||||
<div class="link-bottom-line">
|
||||
{{#if (or (not this.hideCategory) (not this.topic.isPinnedUncategorized))}}
|
||||
{{category-link this.topic.category}}
|
||||
{{/if}}
|
||||
{{discourse-tags this.topic mode="list" tagsForUser=this.tagsForUser}}
|
||||
{{raw
|
||||
"list/action-list"
|
||||
topic=this.topic
|
||||
postNumbers=this.topic.liked_post_numbers
|
||||
className="likes"
|
||||
icon="heart"
|
||||
}}
|
||||
</div>
|
||||
{{#if this.expandPinned}}
|
||||
{{raw "list/topic-excerpt" topic=this.topic}}
|
||||
{{/if}}
|
||||
</td>
|
||||
|
||||
{{#if this.showPosters}}
|
||||
{{raw "list/posters-column" posters=this.topic.featuredUsers}}
|
||||
{{/if}}
|
||||
|
||||
{{raw "list/posts-count-column" topic=this.topic}}
|
||||
|
||||
<td class="num views {{this.topic.viewsHeat}} topic-list-data">{{number
|
||||
this.topic.views
|
||||
numberKey="views_long"
|
||||
}}</td>
|
||||
{{raw
|
||||
"list/activity-column"
|
||||
topic=this.topic
|
||||
class="num topic-list-data"
|
||||
tagName="td"
|
||||
}}
|
||||
<td class="topic-list-data">
|
||||
{{#if this.topic.assigned_to_user}}
|
||||
<AssignActionsDropdown
|
||||
@topic={{this.topic}}
|
||||
@assignee={{this.topic.assigned_to_user.username}}
|
||||
@unassign={{this.unassign}}
|
||||
@reassign={{this.reassign}}
|
||||
/>
|
||||
{{else if this.topic.assigned_to_group}}
|
||||
<AssignActionsDropdown
|
||||
@topic={{this.topic}}
|
||||
@assignee={{this.topic.assigned_to_group.name}}
|
||||
@group={{true}}
|
||||
@unassign={{this.unassign}}
|
||||
@reassign={{this.reassign}}
|
||||
/>
|
||||
{{else}}
|
||||
<AssignActionsDropdown @topic={{this.topic}} @unassign={{this.unassign}} />
|
||||
{{/if}}
|
||||
</td>
|
|
@ -1,8 +0,0 @@
|
|||
import TopicListItem from "discourse/components/topic-list-item";
|
||||
import { equal } from "@ember/object/computed";
|
||||
|
||||
export default class AssignedTopicListItem extends TopicListItem {
|
||||
classNames = ["assigned-topic-list-item"];
|
||||
|
||||
@equal("topic.archetype", "private_message") isPrivateMessage;
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
{{#unless this.skipHeader}}
|
||||
<thead class="topic-list-header assigned-topic-list-header">
|
||||
{{raw
|
||||
"topic-list-header"
|
||||
canBulkSelect=this.canBulkSelect
|
||||
canDoBulkActions=this.canDoBulkActions
|
||||
toggleInTitle=this.toggleInTitle
|
||||
hideCategory=this.hideCategory
|
||||
showPosters=true
|
||||
showLikes=this.showLikes
|
||||
showOpLikes=this.showOpLikes
|
||||
order=this.order
|
||||
ascending=this.ascending
|
||||
sortable=this.sortable
|
||||
listTitle=this.listTitle
|
||||
bulkSelectEnabled=this.bulkSelectEnabled
|
||||
}}
|
||||
</thead>
|
||||
{{/unless}}
|
||||
|
||||
<tbody class="topic-list-body assigned-topic-list-body">
|
||||
{{#each this.filteredTopics as |topic|}}
|
||||
<AssignedTopicListItem
|
||||
@topic={{topic}}
|
||||
@bulkSelectEnabled={{this.bulkSelectEnabled}}
|
||||
@showTopicPostBadges={{this.showTopicPostBadges}}
|
||||
@hideCategory={{this.hideCategory}}
|
||||
@showPosters={{true}}
|
||||
@showLikes={{this.showLikes}}
|
||||
@showOpLikes={{this.showOpLikes}}
|
||||
@expandGloballyPinned={{this.expandGloballyPinned}}
|
||||
@expandAllPinned={{this.expandAllPinned}}
|
||||
@lastVisitedTopic={{this.lastVisitedTopic}}
|
||||
@selected={{this.selected}}
|
||||
@tagsForUser={{this.tagsForUser}}
|
||||
@unassign={{this.unassign}}
|
||||
@reassign={{this.reassign}}
|
||||
/>
|
||||
|
||||
{{raw
|
||||
"list/visited-line"
|
||||
lastVisitedTopic=this.lastVisitedTopic
|
||||
topic=topic
|
||||
}}
|
||||
{{/each}}
|
||||
</tbody>
|
|
@ -1,3 +0,0 @@
|
|||
import TopicList from "discourse/components/topic-list";
|
||||
|
||||
export default class AssignedTopicList extends TopicList {}
|
|
@ -1,40 +0,0 @@
|
|||
<ConditionalLoadingSpinner @condition={{this.loading}}>
|
||||
{{#if this.hasIncoming}}
|
||||
<div class="show-mores">
|
||||
<a href class="alert alert-info clickable" {{action this.showInserted}}>
|
||||
<CountI18n
|
||||
@key="topic_count_"
|
||||
@suffix="latest"
|
||||
@count={{this.incomingCount}}
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.topics}}
|
||||
<AssignedTopicList
|
||||
@showPosters={{this.showPosters}}
|
||||
@hideCategory={{this.hideCategory}}
|
||||
@topics={{this.topics}}
|
||||
@expandExcerpts={{this.expandExcerpts}}
|
||||
@bulkSelectEnabled={{this.bulkSelectEnabled}}
|
||||
@canBulkSelect={{this.canBulkSelect}}
|
||||
@bulkSelectAction={{this.bulkSelectAction}}
|
||||
@selected={{this.selected}}
|
||||
@skipHeader={{this.skipHeader}}
|
||||
@tagsForUser={{this.tagsForUser}}
|
||||
@changeSort={{this.changeSort}}
|
||||
@toggleBulkSelect={{this.toggleBulkSelect}}
|
||||
@unassign={{this.unassign}}
|
||||
@reassign={{this.reassign}}
|
||||
@onScroll={{this.onScroll}}
|
||||
@scrollOnLoad={{this.scrollOnLoad}}
|
||||
/>
|
||||
{{else}}
|
||||
{{#unless this.loadingMore}}
|
||||
<div class="alert alert-info">
|
||||
{{i18n "choose_topic.none_found"}}
|
||||
</div>
|
||||
{{/unless}}
|
||||
{{/if}}
|
||||
</ConditionalLoadingSpinner>
|
|
@ -1,3 +0,0 @@
|
|||
import BasicTopicList from "discourse/components/basic-topic-list";
|
||||
|
||||
export default class BasicAssignedTopicList extends BasicTopicList {}
|
|
@ -1,8 +1,9 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { action } from "@ember/object";
|
||||
import { TrackedObject } from "@ember-compat/tracked-built-ins";
|
||||
|
||||
export default class AssignUser extends Component {
|
||||
model = {};
|
||||
model = new TrackedObject({});
|
||||
|
||||
// `submit` property will be mutated by the `AssignUserForm` component
|
||||
formApi = {
|
||||
|
@ -14,6 +15,7 @@ export default class AssignUser extends Component {
|
|||
return this.args.performAndRefresh({
|
||||
type: "assign",
|
||||
username: this.model.username,
|
||||
status: this.model.status,
|
||||
note: this.model.note,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<DModal class="assign" @title={{this.title}} @closeModal={{@closeModal}}>
|
||||
<:body>
|
||||
<AssignUserForm
|
||||
@model={{@model}}
|
||||
@model={{this.model}}
|
||||
@onSubmit={{this.onSubmit}}
|
||||
@formApi={{this.formApi}}
|
||||
/>
|
||||
|
@ -12,7 +12,7 @@
|
|||
class="btn-primary"
|
||||
@action={{this.formApi.submit}}
|
||||
@label={{if
|
||||
@model.reassign
|
||||
this.model.reassign
|
||||
"discourse_assign.reassign.title"
|
||||
"discourse_assign.assign_modal.assign"
|
||||
}}
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { action } from "@ember/object";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { TrackedObject } from "@ember-compat/tracked-built-ins";
|
||||
import I18n from "I18n";
|
||||
|
||||
export default class AssignUser extends Component {
|
||||
@service taskActions;
|
||||
|
||||
model = new TrackedObject(this.args.model);
|
||||
|
||||
// `submit` property will be mutated by the `AssignUserForm` component
|
||||
formApi = {
|
||||
submit() {},
|
||||
|
@ -14,7 +17,7 @@ export default class AssignUser extends Component {
|
|||
get title() {
|
||||
let i18nSuffix;
|
||||
|
||||
switch (this.args.model.targetType) {
|
||||
switch (this.model.targetType) {
|
||||
case "Post":
|
||||
i18nSuffix = "_post_modal";
|
||||
break;
|
||||
|
@ -25,7 +28,7 @@ export default class AssignUser extends Component {
|
|||
|
||||
return I18n.t(
|
||||
`discourse_assign.assign${i18nSuffix}.${
|
||||
this.args.model.reassign ? "reassign_title" : "title"
|
||||
this.model.reassign ? "reassign_title" : "title"
|
||||
}`
|
||||
);
|
||||
}
|
||||
|
@ -33,6 +36,6 @@ export default class AssignUser extends Component {
|
|||
@action
|
||||
async onSubmit() {
|
||||
this.args.closeModal();
|
||||
await this.taskActions.assign(this.args.model);
|
||||
await this.taskActions.assign(this.model);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Component from "@ember/component";
|
||||
import I18n from "I18n";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import I18n from "I18n";
|
||||
|
||||
export default class RemindAssignsFrequency extends Component {
|
||||
@discourseComputed(
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
import { set } from "@ember/object";
|
||||
import { sort } from "@ember/object/computed";
|
||||
import UserMenuNotificationsList from "discourse/components/user-menu/notifications-list";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import UserMenuNotificationItem from "discourse/lib/user-menu/notification-item";
|
||||
import UserMenuAssignItem from "../../lib/user-menu/assign-item";
|
||||
import Notification from "discourse/models/notification";
|
||||
import I18n from "I18n";
|
||||
import Topic from "discourse/models/topic";
|
||||
import UserMenuAssignsListEmptyState from "./assigns-list-empty-state";
|
||||
|
||||
export default class UserMenuAssignNotificationsList extends UserMenuNotificationsList {
|
||||
|
@ -56,25 +53,20 @@ export default class UserMenuAssignNotificationsList extends UserMenuNotificatio
|
|||
}
|
||||
|
||||
async fetchItems() {
|
||||
const data = await ajax("/assign/user-menu-assigns.json");
|
||||
const content = [];
|
||||
|
||||
const notifications = data.notifications.map((n) => Notification.create(n));
|
||||
await Notification.applyTransformations(notifications);
|
||||
notifications.forEach((notification) => {
|
||||
content.push(
|
||||
new UserMenuNotificationItem({
|
||||
notification,
|
||||
currentUser: this.currentUser,
|
||||
siteSettings: this.siteSettings,
|
||||
site: this.site,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
const topics = data.topics.map((t) => Topic.create(t));
|
||||
await Topic.applyTransformations(topics);
|
||||
content.push(...topics.map((assign) => new UserMenuAssignItem({ assign })));
|
||||
return content;
|
||||
return new SortedItems(await super.fetchItems()).sortedItems;
|
||||
}
|
||||
}
|
||||
|
||||
class SortedItems {
|
||||
itemsSorting = [
|
||||
"notification.read",
|
||||
"notification.data.message:desc",
|
||||
"notification.created_at:desc",
|
||||
];
|
||||
|
||||
@sort("items", "itemsSorting") sortedItems;
|
||||
|
||||
constructor(items) {
|
||||
set(this, "items", items);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
{{i18n "discourse_assign.admin.groups.manage.interaction.assign"}}
|
||||
</label>
|
||||
|
||||
<label for="visiblity">
|
||||
<label for="visibility">
|
||||
{{i18n
|
||||
"discourse_assign.admin.groups.manage.interaction.assignable_levels.title"
|
||||
}}
|
||||
|
@ -14,7 +14,7 @@
|
|||
@valueProperty="value"
|
||||
@value={{this.assignableLevel}}
|
||||
@content={{this.assignableLevelOptions}}
|
||||
@class="groups-form-assignable-level"
|
||||
@onChange={{action (mut @outletArgs.model.assignable_level)}}
|
||||
class="groups-form-assignable-level"
|
||||
/>
|
||||
</div>
|
|
@ -0,0 +1 @@
|
|||
{{raw "assign-topic-buttons" topic=context.topic}}
|
|
@ -1,13 +1,14 @@
|
|||
import UserTopicsList from "discourse/controllers/user-topics-list";
|
||||
import { alias } from "@ember/object/computed";
|
||||
import discourseDebounce from "discourse-common/lib/debounce";
|
||||
import { INPUT_DELAY } from "discourse-common/config/environment";
|
||||
import { inject as controller } from "@ember/controller";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { action } from "@ember/object";
|
||||
import { alias } from "@ember/object/computed";
|
||||
import { inject as service } from "@ember/service";
|
||||
import UserTopicsList from "discourse/controllers/user-topics-list";
|
||||
import { INPUT_DELAY } from "discourse-common/config/environment";
|
||||
import discourseDebounce from "discourse-common/lib/debounce";
|
||||
|
||||
export default class GroupAssignedShow extends UserTopicsList {
|
||||
@service taskActions;
|
||||
@service router;
|
||||
@controller user;
|
||||
|
||||
queryParams = ["order", "ascending", "search"];
|
||||
|
@ -45,13 +46,13 @@ export default class GroupAssignedShow extends UserTopicsList {
|
|||
@action
|
||||
async unassign(targetId, targetType = "Topic") {
|
||||
await this.taskActions.unassign(targetId, targetType);
|
||||
this.send("changeAssigned");
|
||||
this.router.refresh();
|
||||
}
|
||||
|
||||
@action
|
||||
reassign(topic) {
|
||||
this.taskActions.showAssignModal(topic, {
|
||||
onSuccess: () => this.send("changeAssigned"),
|
||||
onSuccess: () => this.router.refresh(),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { inject as service } from "@ember/service";
|
||||
import Controller, { inject as controller } from "@ember/controller";
|
||||
import { action } from "@ember/object";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import discourseDebounce from "discourse-common/lib/debounce";
|
||||
import { INPUT_DELAY } from "discourse-common/config/environment";
|
||||
import discourseDebounce from "discourse-common/lib/debounce";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
|
||||
export default class GroupAssigned extends Controller {
|
||||
@service router;
|
||||
|
@ -47,7 +47,11 @@ export default class GroupAssigned extends Controller {
|
|||
})
|
||||
.then((result) => {
|
||||
if (this.router.currentRoute.params.filter !== "everyone") {
|
||||
this.transitionToRoute("group.assigned.show", groupName, "everyone");
|
||||
this.router.transitionTo(
|
||||
"group.assigned.show",
|
||||
groupName,
|
||||
"everyone"
|
||||
);
|
||||
}
|
||||
this.set("members", result.members);
|
||||
})
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import UserTopicsList from "discourse/controllers/user-topics-list";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import discourseDebounce from "discourse-common/lib/debounce";
|
||||
import { INPUT_DELAY } from "discourse-common/config/environment";
|
||||
import { inject as controller } from "@ember/controller";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { action } from "@ember/object";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
import UserTopicsList from "discourse/controllers/user-topics-list";
|
||||
import { INPUT_DELAY } from "discourse-common/config/environment";
|
||||
import discourseDebounce from "discourse-common/lib/debounce";
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import { iconHTML } from "discourse-common/lib/icon-library";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import I18n from "I18n";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
|
||||
export default class UserActivityAssigned extends UserTopicsList {
|
||||
@service taskActions;
|
||||
|
@ -56,19 +56,6 @@ export default class UserActivityAssigned extends UserTopicsList {
|
|||
});
|
||||
}
|
||||
|
||||
@action
|
||||
async unassign(targetId, targetType = "Topic") {
|
||||
await this.taskActions.unassign(targetId, targetType);
|
||||
this.send("changeAssigned");
|
||||
}
|
||||
|
||||
@action
|
||||
reassign(topic) {
|
||||
this.taskActions.showAssignModal(topic, {
|
||||
onSuccess: () => this.send("changeAssigned"),
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
changeSort(sortBy) {
|
||||
if (sortBy === this.order) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import I18n from "I18n";
|
||||
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||
import I18n from "I18n";
|
||||
|
||||
export default {
|
||||
name: "assign-extend-user-messages",
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
import { htmlSafe } from "@ember/template";
|
||||
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||
import { emojiUnescape } from "discourse/lib/text";
|
||||
import I18n from "I18n";
|
||||
import UserMenuAssignNotificationsList from "../components/user-menu/assigns-list";
|
||||
|
||||
export default {
|
||||
|
@ -6,16 +9,74 @@ export default {
|
|||
|
||||
initialize(container) {
|
||||
withPluginApi("1.2.0", (api) => {
|
||||
if (api.registerUserMenuTab) {
|
||||
const siteSettings = container.lookup("service:site-settings");
|
||||
if (!siteSettings.assign_enabled) {
|
||||
return;
|
||||
}
|
||||
const siteSettings = container.lookup("service:site-settings");
|
||||
if (!siteSettings.assign_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentUser = api.getCurrentUser();
|
||||
if (!currentUser?.can_assign) {
|
||||
return;
|
||||
}
|
||||
const currentUser = api.getCurrentUser();
|
||||
if (!currentUser?.can_assign) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (api.registerNotificationTypeRenderer) {
|
||||
api.registerNotificationTypeRenderer(
|
||||
"assigned",
|
||||
(NotificationItemBase) => {
|
||||
return class extends NotificationItemBase {
|
||||
get linkTitle() {
|
||||
if (this.isGroup()) {
|
||||
return I18n.t(
|
||||
`user.assigned_to_group.${this.postOrTopic()}`,
|
||||
{
|
||||
group_name: this.notification.data.display_username,
|
||||
}
|
||||
);
|
||||
}
|
||||
return I18n.t(`user.assigned_to_you.${this.postOrTopic()}`);
|
||||
}
|
||||
|
||||
get icon() {
|
||||
return this.isGroup() ? "group-plus" : "user-plus";
|
||||
}
|
||||
|
||||
get label() {
|
||||
if (!this.isGroup()) {
|
||||
return "";
|
||||
}
|
||||
return this.notification.data.display_username;
|
||||
}
|
||||
|
||||
get description() {
|
||||
return htmlSafe(
|
||||
emojiUnescape(
|
||||
I18n.t(
|
||||
`user.assignment_description.${this.postOrTopic()}`,
|
||||
{
|
||||
topic_title: this.notification.fancy_title,
|
||||
post_number: this.notification.post_number,
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
isGroup() {
|
||||
return (
|
||||
this.notification.data.message ===
|
||||
"discourse_assign.assign_group_notification"
|
||||
);
|
||||
}
|
||||
|
||||
postOrTopic() {
|
||||
return this.notification.post_number === 1 ? "topic" : "post";
|
||||
}
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (api.registerUserMenuTab) {
|
||||
api.registerUserMenuTab((UserMenuTab) => {
|
||||
return class extends UserMenuTab {
|
||||
id = "assign-list";
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
import { getOwner } from "@ember/application";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import { h } from "virtual-dom";
|
||||
import SearchAdvancedOptions from "discourse/components/search-advanced-options";
|
||||
import { renderAvatar } from "discourse/helpers/user-avatar";
|
||||
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { iconHTML, iconNode } from "discourse-common/lib/icon-library";
|
||||
import { escapeExpression } from "discourse/lib/utilities";
|
||||
import { h } from "virtual-dom";
|
||||
import { getOwner } from "discourse-common/lib/get-owner";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import SearchAdvancedOptions from "discourse/components/search-advanced-options";
|
||||
import I18n from "I18n";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import { registerTopicFooterDropdown } from "discourse/lib/register-topic-footer-dropdown";
|
||||
import { escapeExpression } from "discourse/lib/utilities";
|
||||
import RawHtml from "discourse/widgets/raw-html";
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import { iconHTML, iconNode } from "discourse-common/lib/icon-library";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import I18n from "I18n";
|
||||
import BulkAssign from "../components/bulk-actions/assign-user";
|
||||
|
||||
const PLUGIN_ID = "discourse-assign";
|
||||
|
@ -409,7 +409,7 @@ function registerTopicFooterButtons(api) {
|
|||
}
|
||||
|
||||
function initialize(api) {
|
||||
const siteSettings = api.container.lookup("site-settings:main");
|
||||
const siteSettings = api.container.lookup("service:site-settings");
|
||||
const currentUser = api.getCurrentUser();
|
||||
|
||||
if (siteSettings.assigns_public || currentUser?.can_assign) {
|
||||
|
@ -616,7 +616,6 @@ function initialize(api) {
|
|||
topicAssignee,
|
||||
assignedToIndirectly.map((assigned) => ({
|
||||
assignee: assigned.assigned_to,
|
||||
status: assigned.assignment_status,
|
||||
note: assigned.assignment_note,
|
||||
}))
|
||||
)
|
||||
|
@ -879,12 +878,12 @@ export default {
|
|||
name: "extend-for-assign",
|
||||
|
||||
initialize(container) {
|
||||
const siteSettings = container.lookup("site-settings:main");
|
||||
const siteSettings = container.lookup("service:site-settings");
|
||||
if (!siteSettings.assign_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentUser = container.lookup("current-user:main");
|
||||
const currentUser = container.lookup("service:current-user");
|
||||
if (currentUser?.can_assign) {
|
||||
SearchAdvancedOptions.reopen({
|
||||
updateSearchTermForAssignedUsername() {
|
||||
|
@ -928,57 +927,23 @@ export default {
|
|||
|
||||
api.addUserSearchOption("assignableGroups");
|
||||
|
||||
if (api.addBulkActionButton) {
|
||||
api.addBulkActionButton({
|
||||
label: "topics.bulk.assign",
|
||||
icon: "user-plus",
|
||||
class: "btn-default assign-topics",
|
||||
action({ setComponent }) {
|
||||
setComponent(BulkAssign);
|
||||
},
|
||||
});
|
||||
api.addBulkActionButton({
|
||||
label: "topics.bulk.assign",
|
||||
icon: "user-plus",
|
||||
class: "btn-default assign-topics",
|
||||
action({ setComponent }) {
|
||||
setComponent(BulkAssign);
|
||||
},
|
||||
});
|
||||
|
||||
api.addBulkActionButton({
|
||||
label: "topics.bulk.unassign",
|
||||
icon: "user-times",
|
||||
class: "btn-default unassign-topics",
|
||||
action({ performAndRefresh }) {
|
||||
performAndRefresh({ type: "unassign" });
|
||||
},
|
||||
});
|
||||
} else {
|
||||
// TODO: Remove this path after core 3.1.0.beta7 is released
|
||||
const {
|
||||
default: TopicButtonAction,
|
||||
addBulkButton,
|
||||
} = require("discourse/controllers/topic-bulk-actions");
|
||||
|
||||
TopicButtonAction.reopen({
|
||||
actions: {
|
||||
showReAssign() {
|
||||
const controller = getOwner(this).lookup(
|
||||
"controller:bulk-assign"
|
||||
);
|
||||
controller.set("model", { username: "", note: "" });
|
||||
this.send("changeBulkTemplate", "modal/bulk-assign");
|
||||
},
|
||||
|
||||
unassignTopics() {
|
||||
this.performAndRefresh({ type: "unassign" });
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
addBulkButton("showReAssign", "assign", {
|
||||
icon: "user-plus",
|
||||
class: "btn-default assign-topics",
|
||||
});
|
||||
|
||||
addBulkButton("unassignTopics", "unassign", {
|
||||
icon: "user-times",
|
||||
class: "btn-default unassign-topics",
|
||||
});
|
||||
}
|
||||
api.addBulkActionButton({
|
||||
label: "topics.bulk.unassign",
|
||||
icon: "user-times",
|
||||
class: "btn-default unassign-topics",
|
||||
action({ performAndRefresh }) {
|
||||
performAndRefresh({ type: "unassign" });
|
||||
},
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
import UserMenuBaseItem from "discourse/lib/user-menu/base-item";
|
||||
import { postUrl } from "discourse/lib/utilities";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
import { emojiUnescape } from "discourse/lib/text";
|
||||
import I18n from "I18n";
|
||||
|
||||
const ICON = "user-plus";
|
||||
const GROUP_ICON = "group-plus";
|
||||
|
||||
export default class UserMenuAssignItem extends UserMenuBaseItem {
|
||||
constructor({ assign }) {
|
||||
super(...arguments);
|
||||
this.assign = assign;
|
||||
}
|
||||
|
||||
get className() {
|
||||
return "assign";
|
||||
}
|
||||
|
||||
get linkHref() {
|
||||
return postUrl(
|
||||
this.assign.slug,
|
||||
this.assign.id,
|
||||
(this.assign.last_read_post_number || 0) + 1
|
||||
);
|
||||
}
|
||||
|
||||
get linkTitle() {
|
||||
if (this.assign.assigned_to_group) {
|
||||
return I18n.t("user.assigned_to_group", {
|
||||
group_name:
|
||||
this.assign.assigned_to_group.full_name ||
|
||||
this.assign.assigned_to_group.name,
|
||||
});
|
||||
} else {
|
||||
return I18n.t("user.assigned_to_you");
|
||||
}
|
||||
}
|
||||
|
||||
get icon() {
|
||||
if (this.assign.assigned_to_group) {
|
||||
return GROUP_ICON;
|
||||
} else {
|
||||
return ICON;
|
||||
}
|
||||
}
|
||||
|
||||
get label() {
|
||||
return null;
|
||||
}
|
||||
|
||||
get description() {
|
||||
return htmlSafe(emojiUnescape(this.assign.fancy_title));
|
||||
}
|
||||
|
||||
get topicId() {
|
||||
return this.assign.id;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import Category from "discourse/models/category";
|
||||
import { computed } from "@ember/object";
|
||||
import Category from "discourse/models/category";
|
||||
|
||||
export default {
|
||||
name: "extend-category-for-assign",
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
{{{view.html}}}
|
|
@ -0,0 +1,21 @@
|
|||
import EmberObject from "@ember/object";
|
||||
import { inject as service } from "@ember/service";
|
||||
import rawRenderGlimmer from "discourse/lib/raw-render-glimmer";
|
||||
import AssignedTopicListColumn from "../components/assigned-topic-list-column";
|
||||
|
||||
const ASSIGN_LIST_ROUTES = ["userActivity.assigned", "group.assigned.show"];
|
||||
|
||||
export default class extends EmberObject {
|
||||
@service router;
|
||||
|
||||
get html() {
|
||||
if (ASSIGN_LIST_ROUTES.includes(this.router.currentRouteName)) {
|
||||
return rawRenderGlimmer(
|
||||
this,
|
||||
"td.assign-topic-buttons",
|
||||
<template><AssignedTopicListColumn @topic={{@data.topic}} /></template>,
|
||||
{ topic: this.topic }
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,13 +1,7 @@
|
|||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import { findOrResetCachedTopicList } from "discourse/lib/cached-topic-list";
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
|
||||
export default class GroupAssignedShow extends DiscourseRoute {
|
||||
beforeModel(transition) {
|
||||
if (transition.from?.localName === "show") {
|
||||
this.session.set("topicListScrollPosition", 1);
|
||||
}
|
||||
}
|
||||
|
||||
model(params) {
|
||||
let filter;
|
||||
if (["everyone", this.modelFor("group").name].includes(params.filter)) {
|
||||
|
@ -36,8 +30,4 @@ export default class GroupAssignedShow extends DiscourseRoute {
|
|||
search: this.currentModel.params.search,
|
||||
});
|
||||
}
|
||||
|
||||
renderTemplate() {
|
||||
this.render("group-topics-list");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { action } from "@ember/object";
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
|
||||
export default class GroupAssigned extends DiscourseRoute {
|
||||
@service router;
|
||||
|
||||
model() {
|
||||
return ajax(`/assign/members/${this.modelFor("group").name}`);
|
||||
}
|
||||
|
@ -22,15 +24,8 @@ export default class GroupAssigned extends DiscourseRoute {
|
|||
}
|
||||
|
||||
redirect(model, transition) {
|
||||
if (transition.to.params.hasOwnProperty("filter")) {
|
||||
this.transitionTo("group.assigned.show", transition.to.params.filter);
|
||||
} else {
|
||||
this.transitionTo("group.assigned.show", "everyone");
|
||||
if (!transition.to.params.hasOwnProperty("filter")) {
|
||||
this.router.transitionTo("group.assigned.show", "everyone");
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
changeAssigned() {
|
||||
this.refresh();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import I18n from "I18n";
|
||||
import UserTopicListRoute from "discourse/routes/user-topic-list";
|
||||
import { inject as service } from "@ember/service";
|
||||
import cookie from "discourse/lib/cookie";
|
||||
import { action } from "@ember/object";
|
||||
import UserTopicListRoute from "discourse/routes/user-topic-list";
|
||||
import I18n from "I18n";
|
||||
|
||||
export default class UserActivityAssigned extends UserTopicListRoute {
|
||||
@service router;
|
||||
templateName = "user-activity-assigned";
|
||||
controllerName = "user-activity-assigned";
|
||||
|
||||
|
@ -13,7 +14,7 @@ export default class UserActivityAssigned extends UserTopicListRoute {
|
|||
beforeModel() {
|
||||
if (!this.currentUser) {
|
||||
cookie("destination_url", window.location.href);
|
||||
this.transitionTo("login");
|
||||
this.router.transitionTo("login");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,9 +35,4 @@ export default class UserActivityAssigned extends UserTopicListRoute {
|
|||
titleToken() {
|
||||
return I18n.t("discourse_assign.assigned");
|
||||
}
|
||||
|
||||
@action
|
||||
changeAssigned() {
|
||||
this.refresh();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import Service, { inject as service } from "@ember/service";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import AssignUser from "../components/modal/assign-user";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import Service, { inject as service } from "@ember/service";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import AssignUser from "../components/modal/assign-user";
|
||||
|
||||
export default class TaskActions extends Service {
|
||||
@service modal;
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
<div class="topic-search-div">
|
||||
<div class="inline-form full-width">
|
||||
<Input
|
||||
class="no-blur"
|
||||
{{on "input" (action "onChangeFilter" value="target.value")}}
|
||||
@value={{readonly this.search}}
|
||||
@type="search"
|
||||
placeholder={{i18n "discourse_assign.topic_search_placeholder"}}
|
||||
autocomplete="off"
|
||||
@type="search"
|
||||
{{on "input" (action "onChangeFilter" value="target.value")}}
|
||||
class="no-blur"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<LoadMore
|
||||
@class="paginated-topics-list"
|
||||
@selector=".paginated-topics-list .topic-list tr"
|
||||
@action={{action "loadMore"}}
|
||||
class="paginated-topics-list"
|
||||
>
|
||||
<BasicAssignedTopicList
|
||||
<BasicTopicList
|
||||
@topicList={{this.model}}
|
||||
@hideCategory={{this.hideCategory}}
|
||||
@showPosters={{this.showPosters}}
|
|
@ -1,18 +1,18 @@
|
|||
<section class="user-secondary-navigation group-assignments">
|
||||
<MobileNav
|
||||
@class="activity-nav"
|
||||
@desktopClass="action-list activity-list nav-stacked"
|
||||
class="activity-nav"
|
||||
>
|
||||
{{#if this.isDesktop}}
|
||||
<div class="search-div">
|
||||
<Input
|
||||
{{on "input" (action this.onChangeFilterName value="target.value")}}
|
||||
@type="text"
|
||||
@value={{readonly this.filterName}}
|
||||
placeholder={{i18n
|
||||
"discourse_assign.sidebar_name_filter_placeholder"
|
||||
}}
|
||||
@value={{readonly this.filterName}}
|
||||
class="search"
|
||||
{{on "input" (action this.onChangeFilterName value="target.value")}}
|
||||
/>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
|
|
@ -7,22 +7,22 @@
|
|||
<div class="topic-search-div">
|
||||
<div class="inline-form full-width">
|
||||
<Input
|
||||
{{on "input" (action "onChangeFilter" value="target.value")}}
|
||||
@value={{readonly this.search}}
|
||||
@type="search"
|
||||
{{on "input" (action "onChangeFilter" value="target.value")}}
|
||||
class="no-blur"
|
||||
placeholder={{i18n "discourse_assign.topic_search_placeholder"}}
|
||||
autocomplete="off"
|
||||
class="no-blur"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<LoadMore
|
||||
@class="paginated-topics-list"
|
||||
@selector=".paginated-topics-list .topic-list tr"
|
||||
@action={{action "loadMore"}}
|
||||
class="paginated-topics-list"
|
||||
>
|
||||
<BasicAssignedTopicList
|
||||
<BasicTopicList
|
||||
@topicList={{this.model}}
|
||||
@hideCategory={{this.hideCategory}}
|
||||
@showPosters={{true}}
|
||||
|
|
|
@ -66,12 +66,8 @@
|
|||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.modal.assign {
|
||||
.modal-inner-container {
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
.d-modal.assign {
|
||||
.d-modal__body {
|
||||
overflow-y: unset;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,11 @@
|
|||
# https://translate.discourse.org/
|
||||
|
||||
ar:
|
||||
admin_js:
|
||||
admin:
|
||||
site_settings:
|
||||
categories:
|
||||
discourse_assign: "تعيين Discourse"
|
||||
js:
|
||||
filters:
|
||||
unassigned:
|
||||
|
@ -35,9 +40,8 @@ ar:
|
|||
assigned_to: "معيَّنة إلى"
|
||||
assigned_topic_to: "تم تعيين الموضوع إلى المستخدم <a href='%{path}'>%{username}</a>"
|
||||
assign_post_to: "تم تعيين المنشور #%{post_number} إلى المستخدم %{username}"
|
||||
assign_post_to_multiple: "#%{post_number} إلى %{username}"
|
||||
assigned_to_w_ellipsis: "معيَّنة إلى..."
|
||||
assign_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
assign_group_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
unassign:
|
||||
title: "إلغاء التعيين"
|
||||
title_w_ellipsis: "إلغاء تعيين..."
|
||||
|
@ -99,13 +103,21 @@ ar:
|
|||
no_assignments_body: >
|
||||
سيتم إدراج الموضوعات والرسائل المعيَّنة إليك هنا. ستتلقى أيضًا إشعار تذكير دوريًا بتعييناتك، والذي يمكنك تعديله في <a href='%{preferencesUrl}'>تفضيلات المستخدم</a>. <br><br> لتعيين موضوع أو رسالة إلى نفسك أو إلى شخص آخر، ابحث عن %{icon} في الأسفل.
|
||||
dismiss_assigned_tooltip: "وضع علامة مقروءة على كل إشعارات التعيين"
|
||||
assigned_to_group: "معيَّن إلى %{group_name}"
|
||||
assigned_to_you: "معيَّنة إليك"
|
||||
assigned_to_group:
|
||||
post: "تم تعيين المنشور إلى %{group_name}"
|
||||
topic: "تم تعيين الموضوع إلى %{group_name}"
|
||||
assigned_to_you:
|
||||
post: "تم تعيين المنشور إليك"
|
||||
topic: "تم تعيين الموضوع إليك"
|
||||
assignment_description:
|
||||
post: "%{topic_title} (#%{post_number})"
|
||||
topic: "%{topic_title}"
|
||||
admin:
|
||||
web_hooks:
|
||||
assign_event:
|
||||
name: "تعيين الحدث"
|
||||
details: "عندما يعيِّن مستخدم موضوعًا أو يلغي تعيينه."
|
||||
group_name: "أحداث التعيين"
|
||||
assigned: "عندما يقوم المستخدم بتعيين موضوع"
|
||||
unassigned: "عندما يلغي المستخدم تعيين موضوع"
|
||||
search:
|
||||
advanced:
|
||||
in:
|
||||
|
|
|
@ -23,6 +23,8 @@ be:
|
|||
user:
|
||||
messages:
|
||||
assigned: "прызначаны"
|
||||
assignment_description:
|
||||
topic: "%{topic_title}"
|
||||
notifications:
|
||||
titles:
|
||||
assigned: "прызначаны"
|
||||
|
|
|
@ -22,8 +22,6 @@ ca:
|
|||
group_everyone: "Tothom"
|
||||
assigned_to: "Assignat a"
|
||||
assigned_to_w_ellipsis: "Assignat a..."
|
||||
assign_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
assign_group_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
unassign:
|
||||
title: "Desassigna"
|
||||
title_w_ellipsis: "Desassigna..."
|
||||
|
@ -60,6 +58,8 @@ ca:
|
|||
messages:
|
||||
assigned_title: "Assignats (%{count})"
|
||||
assigned: "Assignats"
|
||||
assignment_description:
|
||||
topic: "%{topic_title}"
|
||||
search:
|
||||
advanced:
|
||||
assigned:
|
||||
|
|
|
@ -5,13 +5,40 @@
|
|||
# https://translate.discourse.org/
|
||||
|
||||
cs:
|
||||
admin_js:
|
||||
admin:
|
||||
site_settings:
|
||||
categories:
|
||||
discourse_assign: "Discourse Assign"
|
||||
js:
|
||||
filters:
|
||||
unassigned:
|
||||
title: "Nepřiděleno"
|
||||
help: "Témata, která nejsou přidělena"
|
||||
action_codes:
|
||||
assigned: "Zadáno %{who} %{when}"
|
||||
assigned_group: "Zadáno %{who} %{when}"
|
||||
assigned_to_post: "Uživateli %{who} přidělen <a href='%{path}'>příspěvek</a> %{when}"
|
||||
assigned_group_to_post: "Skupině %{who} přidělen <a href='%{path}'>příspěvek</a> %{when}"
|
||||
unassigned: "%{who} zrušeno přidělení %{when}"
|
||||
unassigned_group: "%{who} zrušeno přidělení %{when}"
|
||||
unassigned_from_post: "Uživateli %{who} zrušeno přidělení <a href='%{path}'>příspěvku</a> %{when}"
|
||||
unassigned_group_from_post: "Skupině %{who} zrušeno přidělení <a href='%{path}'>příspěvku</a> %{when}"
|
||||
reassigned: "Uživateli %{who} znovu přiděleno %{when}"
|
||||
reassigned_group: "Skupině %{who} znovu přiděleno %{when}"
|
||||
details_change: "Změněny podrobnosti přidělení pro %{who} %{when}"
|
||||
note_change: "Změněna poznámka přidělení pro %{who} %{when}"
|
||||
status_change: "Změněn stav přidělení pro %{who} %{when}"
|
||||
discourse_assign:
|
||||
add_unassigned_filter: "Přidat do kategorie filtr \"Nepřiděleno\""
|
||||
cant_act: "Nemůžete reagovat na nahlášení, která byla přiřazena jiným uživatelům"
|
||||
cant_act_unclaimed: "Než začnete reagovat na nahlášení, musíte si toto téma nárokovat."
|
||||
topic_search_placeholder: "Hledat témata podle názvu nebo obsahu příspěvku"
|
||||
sidebar_name_filter_placeholder: "Jméno/Uživatelské jméno"
|
||||
assigned: "Zadáno"
|
||||
group_everyone: "Všichni"
|
||||
assigned_to: "Přiděleno"
|
||||
assigned_topic_to: "Téma přiděleno <a href='%{path}'>%{username}</a>"
|
||||
unassign:
|
||||
title: "Odebrat zadání"
|
||||
title_w_ellipsis: "Odebrat zadání..."
|
||||
|
|
|
@ -27,8 +27,6 @@ da:
|
|||
group_everyone: "Alle"
|
||||
assigned_to: "Tildelt til"
|
||||
assigned_to_w_ellipsis: "Tildelt til..."
|
||||
assign_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
assign_group_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
unassign:
|
||||
title: "Fjern tildeling"
|
||||
title_w_ellipsis: "Fjern tildeling..."
|
||||
|
@ -66,11 +64,8 @@ da:
|
|||
messages:
|
||||
assigned_title: "Tildelt (%{count})"
|
||||
assigned: "Tildelt"
|
||||
admin:
|
||||
web_hooks:
|
||||
assign_event:
|
||||
name: "Tildel Begivenhed"
|
||||
details: "Når en bruger tildeler eller fradeler et emne."
|
||||
assignment_description:
|
||||
topic: "%{topic_title}"
|
||||
search:
|
||||
advanced:
|
||||
in:
|
||||
|
|
|
@ -5,6 +5,11 @@
|
|||
# https://translate.discourse.org/
|
||||
|
||||
de:
|
||||
admin_js:
|
||||
admin:
|
||||
site_settings:
|
||||
categories:
|
||||
discourse_assign: "Discourse – Zuordnen"
|
||||
js:
|
||||
filters:
|
||||
unassigned:
|
||||
|
@ -35,9 +40,8 @@ de:
|
|||
assigned_to: "Zugeordnet zu"
|
||||
assigned_topic_to: "Thema zugeordnet zu <a href='%{path}'>%{username}</a>"
|
||||
assign_post_to: "#%{post_number} %{username} zugeordnet"
|
||||
assign_post_to_multiple: "#%{post_number} zu %{username}"
|
||||
assigned_to_w_ellipsis: "Zugeordnet zu …"
|
||||
assign_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
assign_group_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
unassign:
|
||||
title: "Zuordnung aufheben"
|
||||
title_w_ellipsis: "Zuordnung aufheben …"
|
||||
|
@ -99,13 +103,21 @@ de:
|
|||
no_assignments_body: >
|
||||
Deine zugeordneten Themen und Nachrichten werden hier aufgelistet. Außerdem erhältst du regelmäßig eine Erinnerungsbenachrichtigung betreffend deine Zuordnungen, die du in deinen <a href='%{preferencesUrl}'>Benutzereinstellungen</a> anpassen kannst. <br><br> Um dir selbst oder einer anderen Person ein Thema oder eine Nachricht zuzuordnen, verwende die Zuordnungsschaltfläche %{icon} unten.
|
||||
dismiss_assigned_tooltip: "Alle ungelesenen Zuordnungsbenachrichtigungen als gelesen markieren"
|
||||
assigned_to_group: "%{group_name} zugeordnet"
|
||||
assigned_to_you: "dir zugeordnet"
|
||||
assigned_to_group:
|
||||
post: "Beitrag zugeordnet zu %{group_name}"
|
||||
topic: "Thema zugeordnet zu %{group_name}"
|
||||
assigned_to_you:
|
||||
post: "Beitrag wurde dir zugeordnet"
|
||||
topic: "Thema wurde dir zugeordnet"
|
||||
assignment_description:
|
||||
post: "%{topic_title} (#%{post_number})"
|
||||
topic: "%{topic_title}"
|
||||
admin:
|
||||
web_hooks:
|
||||
assign_event:
|
||||
name: "Ereignis zuordnen"
|
||||
details: "Wenn ein Benutzer ein Thema zuordnet oder die Zuordnung aufhebt."
|
||||
group_name: "Ereignisse zuordnen"
|
||||
assigned: "Wenn ein Benutzer ein Thema zuordnet"
|
||||
unassigned: "Wenn ein Benutzer die Zuordnung eines Themas aufhebt"
|
||||
search:
|
||||
advanced:
|
||||
in:
|
||||
|
|
|
@ -22,5 +22,8 @@ el:
|
|||
weekly: "Εβδομαδιαία"
|
||||
monthly: "Μηνιαία"
|
||||
quarterly: "Τριμηνιαία"
|
||||
user:
|
||||
assignment_description:
|
||||
topic: "%{topic_title}"
|
||||
notifications:
|
||||
assigned: "<span>%{username}</span> %{description}"
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
en:
|
||||
admin_js:
|
||||
admin:
|
||||
site_settings:
|
||||
categories:
|
||||
discourse_assign: "Discourse Assign"
|
||||
js:
|
||||
filters:
|
||||
unassigned:
|
||||
|
@ -32,8 +37,6 @@ en:
|
|||
# assign_post_to_multiple used in list form, example: "Assigned topic to username0, [#2 to username1], [#10 to username2]"
|
||||
assign_post_to_multiple: "#%{post_number} to %{username}"
|
||||
assigned_to_w_ellipsis: "Assigned to..."
|
||||
assign_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
assign_group_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
unassign:
|
||||
title: "Unassign"
|
||||
title_w_ellipsis: "Unassign..."
|
||||
|
@ -97,13 +100,21 @@ en:
|
|||
<br><br>
|
||||
To assign a topic or message to yourself or to someone else, look for the %{icon} assign button at the bottom.
|
||||
dismiss_assigned_tooltip: "Mark all unread assign notifications as read"
|
||||
assigned_to_group: "assigned to %{group_name}"
|
||||
assigned_to_you: "assigned to you"
|
||||
assigned_to_group:
|
||||
post: "post assigned to %{group_name}"
|
||||
topic: "topic assigned to %{group_name}"
|
||||
assigned_to_you:
|
||||
post: "post assigned to you"
|
||||
topic: "topic assigned to you"
|
||||
assignment_description:
|
||||
post: "%{topic_title} (#%{post_number})"
|
||||
topic: "%{topic_title}"
|
||||
admin:
|
||||
web_hooks:
|
||||
assign_event:
|
||||
name: "Assign Event"
|
||||
details: "When a user assigns or unassigns a topic."
|
||||
group_name: "Assign Events"
|
||||
assigned: "When an user assigns a topic"
|
||||
unassigned: "When an user unassigns a topic"
|
||||
search:
|
||||
advanced:
|
||||
in:
|
||||
|
|
|
@ -5,6 +5,11 @@
|
|||
# https://translate.discourse.org/
|
||||
|
||||
es:
|
||||
admin_js:
|
||||
admin:
|
||||
site_settings:
|
||||
categories:
|
||||
discourse_assign: "Asignación de Discourse"
|
||||
js:
|
||||
filters:
|
||||
unassigned:
|
||||
|
@ -35,9 +40,8 @@ es:
|
|||
assigned_to: "Asignado a"
|
||||
assigned_topic_to: "Tema asignado a <a href='%{path}'>%{username}</a>"
|
||||
assign_post_to: "Asignado #%{post_number} a %{username}"
|
||||
assign_post_to_multiple: "#%{post_number} a %{username}"
|
||||
assigned_to_w_ellipsis: "Asignado a..."
|
||||
assign_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
assign_group_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
unassign:
|
||||
title: "Anular asignación"
|
||||
title_w_ellipsis: "Anular asignación..."
|
||||
|
@ -99,13 +103,21 @@ es:
|
|||
no_assignments_body: >
|
||||
Tus temas y mensajes asignados aparecerán aquí. También recibirás una notificación periódica de recordatorio de tus asignaciones, que puedes ajustar en tus <a href='%{preferencesUrl}'>preferencias de usuario</a>. <br><br> Para asignar un tema o mensaje a ti mismo o a otra persona, busca el botón %{icon} de asignar en la parte inferior.
|
||||
dismiss_assigned_tooltip: "Marcar todas las notificaciones de asignación no leídas como leídas"
|
||||
assigned_to_group: "asignada a %{group_name}"
|
||||
assigned_to_you: "asignada a ti"
|
||||
assigned_to_group:
|
||||
post: "publicación asignada a %{group_name}"
|
||||
topic: "tema asignado a %{group_name}"
|
||||
assigned_to_you:
|
||||
post: "publicación asignada a ti"
|
||||
topic: "tema asignado a ti"
|
||||
assignment_description:
|
||||
post: "%{topic_title} (#%{post_number})"
|
||||
topic: "%{topic_title}"
|
||||
admin:
|
||||
web_hooks:
|
||||
assign_event:
|
||||
name: "Asignar evento"
|
||||
details: "Cuando un usuario asigna o anula la asignación de un tema."
|
||||
group_name: "Asignar eventos"
|
||||
assigned: "Cuando un usuario asigna un tema"
|
||||
unassigned: "Cuando un usuario anula la asignación de un tema"
|
||||
search:
|
||||
advanced:
|
||||
in:
|
||||
|
|
|
@ -42,6 +42,8 @@ et:
|
|||
user:
|
||||
messages:
|
||||
assigned: "Määratud"
|
||||
assignment_description:
|
||||
topic: "%{topic_title}"
|
||||
search:
|
||||
advanced:
|
||||
assigned:
|
||||
|
|
|
@ -31,5 +31,8 @@ fa_IR:
|
|||
weekly: "هفتگی"
|
||||
monthly: "ماهانه"
|
||||
quarterly: "فصلی"
|
||||
user:
|
||||
assignment_description:
|
||||
topic: "%{topic_title}"
|
||||
notifications:
|
||||
assigned: "<span>%{username}</span> %{description}"
|
||||
|
|
|
@ -5,6 +5,11 @@
|
|||
# https://translate.discourse.org/
|
||||
|
||||
fi:
|
||||
admin_js:
|
||||
admin:
|
||||
site_settings:
|
||||
categories:
|
||||
discourse_assign: "Discourse-osoitus"
|
||||
js:
|
||||
filters:
|
||||
unassigned:
|
||||
|
@ -35,9 +40,8 @@ fi:
|
|||
assigned_to: "Osoitettu käyttäjälle"
|
||||
assigned_topic_to: "Osoitti ketjun käyttäjälle <a href='%{path}'>%{username}</a>"
|
||||
assign_post_to: "Osoitti viestin %{post_number} käyttäjälle %{username}"
|
||||
assign_post_to_multiple: "#%{post_number} käyttäjälle %{username}"
|
||||
assigned_to_w_ellipsis: "Osoitettu käyttäjälle..."
|
||||
assign_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
assign_group_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
unassign:
|
||||
title: "Peru osoitus"
|
||||
title_w_ellipsis: "Peru osoitus..."
|
||||
|
@ -99,13 +103,21 @@ fi:
|
|||
no_assignments_body: >
|
||||
Sinulle osoitetut ketjut ja viestit luetellaan tässä. Saat myös ajoittain muistutusilmoituksia osoituksistasi, joita voit muuttaa <a href='%{preferencesUrl}'>käyttäjäasetuksissasi</a>. <br><br> Jos haluat osoittaa ketjun tai viestin itsellesi tai jollekulle toiselle, etsi osoituspainike %{icon} alaosasta.
|
||||
dismiss_assigned_tooltip: "Merkitse kaikki lukemattomat osoitusilmoitukset luetuiksi"
|
||||
assigned_to_group: "osoitettu ryhmälle %{group_name}"
|
||||
assigned_to_you: "osoitettu sinulle"
|
||||
assigned_to_group:
|
||||
post: "viesti osoitettu ryhmälle %{group_name}"
|
||||
topic: "ketju osoitettu ryhmälle %{group_name}"
|
||||
assigned_to_you:
|
||||
post: "viesti osoitettu sinulle"
|
||||
topic: "ketju osoitettu sinulle"
|
||||
assignment_description:
|
||||
post: "%{topic_title} (#%{post_number})"
|
||||
topic: "%{topic_title}"
|
||||
admin:
|
||||
web_hooks:
|
||||
assign_event:
|
||||
name: "Osoitustapahtuma"
|
||||
details: "Kun käyttäjä osoittaa ketjun tai peruu ketjun osoituksen."
|
||||
group_name: "Osoitustapahtumat"
|
||||
assigned: "Kun käyttäjä osoittaa ketjun"
|
||||
unassigned: "Kun käyttäjä peruu ketjun osoituksen"
|
||||
search:
|
||||
advanced:
|
||||
in:
|
||||
|
|
|
@ -5,6 +5,11 @@
|
|||
# https://translate.discourse.org/
|
||||
|
||||
fr:
|
||||
admin_js:
|
||||
admin:
|
||||
site_settings:
|
||||
categories:
|
||||
discourse_assign: "Affectation de Discourse"
|
||||
js:
|
||||
filters:
|
||||
unassigned:
|
||||
|
@ -35,9 +40,8 @@ fr:
|
|||
assigned_to: "Attribué à"
|
||||
assigned_topic_to: "A attribué le message à <a href='%{path}'>%{username}</a>"
|
||||
assign_post_to: "A attribué %{post_number} à %{username}"
|
||||
assign_post_to_multiple: "#%{post_number} à %{username}"
|
||||
assigned_to_w_ellipsis: "Attribué à…"
|
||||
assign_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
assign_group_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
unassign:
|
||||
title: "Annuler l'attribution"
|
||||
title_w_ellipsis: "Annuler l'attribution…"
|
||||
|
@ -99,13 +103,21 @@ fr:
|
|||
no_assignments_body: >
|
||||
Vos sujets et messages attribués seront répertoriés ici. Vous recevrez également une notification de rappel périodique de vos attributions que vous pouvez ajuster dans vos <a href='%{preferencesUrl}'>préférences d'utilisateur</a>. <br><br> Pour attribuer un sujet ou un message à vous-même ou à quelqu'un d'autre, recherchez le bouton d'attribution %{icon} en bas de l'écran.
|
||||
dismiss_assigned_tooltip: "Marquer toutes les notifications d'attribution non lues comme lues"
|
||||
assigned_to_group: "attribué à %{group_name}"
|
||||
assigned_to_you: "attribué à vous"
|
||||
assigned_to_group:
|
||||
post: "message attribué à %{group_name}"
|
||||
topic: "sujet attribué à %{group_name}"
|
||||
assigned_to_you:
|
||||
post: "message attribué à vous"
|
||||
topic: "sujet attribué à vous"
|
||||
assignment_description:
|
||||
post: "%{topic_title} (#%{post_number})"
|
||||
topic: "%{topic_title}"
|
||||
admin:
|
||||
web_hooks:
|
||||
assign_event:
|
||||
name: "Événement d'attribution"
|
||||
details: "Lorsqu'un utilisateur attribue ou annule l'attribution d'un sujet."
|
||||
group_name: "Événements d'attribution"
|
||||
assigned: "Lorsqu'un utilisateur attribue un sujet"
|
||||
unassigned: "Lorsqu'un utilisateur annule l'attribution d'un sujet"
|
||||
search:
|
||||
advanced:
|
||||
in:
|
||||
|
|
|
@ -26,6 +26,8 @@ gl:
|
|||
user:
|
||||
messages:
|
||||
assigned: "Asignado"
|
||||
assignment_description:
|
||||
topic: "%{topic_title}"
|
||||
notifications:
|
||||
assigned: "<span>%{username}</span> %{description}"
|
||||
titles:
|
||||
|
|
|
@ -5,6 +5,11 @@
|
|||
# https://translate.discourse.org/
|
||||
|
||||
he:
|
||||
admin_js:
|
||||
admin:
|
||||
site_settings:
|
||||
categories:
|
||||
discourse_assign: "Discourse הקצאה"
|
||||
js:
|
||||
filters:
|
||||
unassigned:
|
||||
|
@ -37,8 +42,6 @@ he:
|
|||
assign_post_to: "מס׳ %{post_number} הוקצה לטובת %{username}"
|
||||
assign_post_to_multiple: "מס׳ %{post_number} אל %{username}"
|
||||
assigned_to_w_ellipsis: "מוקצה לטובת..."
|
||||
assign_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
assign_group_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
unassign:
|
||||
title: "ביטול הקצאה"
|
||||
title_w_ellipsis: "ביטול הקצאה..."
|
||||
|
@ -100,13 +103,21 @@ he:
|
|||
no_assignments_body: >
|
||||
הנושאים שמוקצים אליך יופיעו כאן. תישלחנה אליך הודעות תזכורת מדי פעם בפעם כדי להזכיר לך את המטלות שלך, ניתן להגדיר אותן <a href='%{preferencesUrl}'>בהעדפות המשתמש</a> שלך. <br><br> כדי להקצות נושא או הודעה לעצמך או למישהו אחר יש לחפש את הכפתור %{icon} הקצאה למטה.
|
||||
dismiss_assigned_tooltip: "סימון כל התראות ההקצאה שלא נקראו כהתראות שנקראו"
|
||||
assigned_to_group: "מוקצה לטובת %{group_name}"
|
||||
assigned_to_you: "מוקצה לך"
|
||||
assigned_to_group:
|
||||
post: "הפוסט הוקצה לטובת %{group_name}"
|
||||
topic: "הנושא הוקצה לטובת %{group_name}"
|
||||
assigned_to_you:
|
||||
post: "הפוסט הוקצה לך"
|
||||
topic: "הנושא הוקצה לך"
|
||||
assignment_description:
|
||||
post: "%{topic_title} (מס׳ %{post_number})"
|
||||
topic: "%{topic_title}"
|
||||
admin:
|
||||
web_hooks:
|
||||
assign_event:
|
||||
name: "הקצאת אירוע"
|
||||
details: "כאשר משתמש מקצה או מבטל הקצאה של נושא."
|
||||
group_name: "הקצאת אירועים"
|
||||
assigned: "בעת הקצאת נושא על ידי משתמש"
|
||||
unassigned: "בעת ביטול הקצאת נושא על ידי משתמש"
|
||||
search:
|
||||
advanced:
|
||||
in:
|
||||
|
|
|
@ -31,8 +31,6 @@ hu:
|
|||
group_everyone: "Mindenki"
|
||||
assigned_to: "Kiosztva:"
|
||||
assigned_to_w_ellipsis: "Kiosztva…"
|
||||
assign_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
assign_group_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
unassign:
|
||||
title: "Elvétel"
|
||||
title_w_ellipsis: "Elvétel…"
|
||||
|
@ -87,11 +85,8 @@ hu:
|
|||
no_assignments_title: "Még nincsenek kiosztott feladatai"
|
||||
no_assignments_body: >
|
||||
A kiosztott témák és üzenetek itt jelennek meg. Rendszeresen kap egy emlékeztető értesítést is a kiosztott feladatokról, amelyeket a <a href='%{preferencesUrl}'>felhasználói beállításokban</a> módosíthat. <br><br>Téma vagy üzenet saját magának vagy valaki másnak történő kiosztásához keresse a lenti %{icon} kiosztás gombot.
|
||||
admin:
|
||||
web_hooks:
|
||||
assign_event:
|
||||
name: "Esemény kiosztása"
|
||||
details: "Amikor egy felhasználó kioszt vagy elvesz egy témát."
|
||||
assignment_description:
|
||||
topic: "%{topic_title}"
|
||||
search:
|
||||
advanced:
|
||||
assigned:
|
||||
|
|
|
@ -52,6 +52,8 @@ hy:
|
|||
messages:
|
||||
assigned_title: "Վերագրված (%{count})"
|
||||
assigned: "Վերագրված"
|
||||
assignment_description:
|
||||
topic: "%{topic_title}"
|
||||
search:
|
||||
advanced:
|
||||
assigned:
|
||||
|
|
|
@ -5,6 +5,11 @@
|
|||
# https://translate.discourse.org/
|
||||
|
||||
it:
|
||||
admin_js:
|
||||
admin:
|
||||
site_settings:
|
||||
categories:
|
||||
discourse_assign: "Assegnazione Discourse"
|
||||
js:
|
||||
filters:
|
||||
unassigned:
|
||||
|
@ -35,9 +40,8 @@ it:
|
|||
assigned_to: "Assegnato a"
|
||||
assigned_topic_to: "Argomento assegnato a <a href='%{path}'>%{username}</a>"
|
||||
assign_post_to: "#%{post_number} assegnato a %{username}"
|
||||
assign_post_to_multiple: "n°%{post_number} a %{username}"
|
||||
assigned_to_w_ellipsis: "Assegnato a..."
|
||||
assign_notification: "<p> <span>%{username}</span> %{description} </p>"
|
||||
assign_group_notification: "<p> <span>%{username}</span> %{description} </p>"
|
||||
unassign:
|
||||
title: "Annulla assegnazione"
|
||||
title_w_ellipsis: "Annullamento assegnazione..."
|
||||
|
@ -99,13 +103,21 @@ it:
|
|||
no_assignments_body: >
|
||||
Gli argomenti e i messaggi a te assegnati verranno elencati qui. Riceverai anche una notifica di promemoria periodica delle tue assegnazioni, che puoi modificare nelle tue <a href='%{preferencesUrl}'>preferenze utente</a>. <br><br> Per assegnare un argomento o un messaggio a te stesso o a qualcun altro, cerca il pulsante Assegna %{icon} in fondo.
|
||||
dismiss_assigned_tooltip: "Imposta tutte le notifiche di assegnazione non lette come lette"
|
||||
assigned_to_group: "assegnato a %{group_name}"
|
||||
assigned_to_you: "assegnato a te"
|
||||
assigned_to_group:
|
||||
post: "messaggio assegnato a %{group_name}"
|
||||
topic: "argomento assegnato a %{group_name}"
|
||||
assigned_to_you:
|
||||
post: "messaggio assegnato a te"
|
||||
topic: "argomento assegnato a te"
|
||||
assignment_description:
|
||||
post: "%{topic_title} (n°%{post_number})"
|
||||
topic: "%{topic_title}"
|
||||
admin:
|
||||
web_hooks:
|
||||
assign_event:
|
||||
name: "Evento di assegnazione"
|
||||
details: "Quando un utente assegna o annulla l'assegnazione di un argomento."
|
||||
group_name: "Assegna eventi"
|
||||
assigned: "Quando un utente assegna un argomento"
|
||||
unassigned: "Quando un utente rimuove l'assegnazione di un argomento"
|
||||
search:
|
||||
advanced:
|
||||
in:
|
||||
|
|
|
@ -5,6 +5,11 @@
|
|||
# https://translate.discourse.org/
|
||||
|
||||
ja:
|
||||
admin_js:
|
||||
admin:
|
||||
site_settings:
|
||||
categories:
|
||||
discourse_assign: "Discourse 割り当て"
|
||||
js:
|
||||
filters:
|
||||
unassigned:
|
||||
|
@ -35,9 +40,8 @@ ja:
|
|||
assigned_to: "割り当て先"
|
||||
assigned_topic_to: "トピックを <a href='%{path}'>%{username}</a> に割り当てました"
|
||||
assign_post_to: "#%{post_number} を %{username} に割り当てました"
|
||||
assign_post_to_multiple: "#%{post_number} を %{username}"
|
||||
assigned_to_w_ellipsis: "割り当て先..."
|
||||
assign_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
assign_group_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
unassign:
|
||||
title: "割り当て解除"
|
||||
title_w_ellipsis: "割り当て解除..."
|
||||
|
@ -99,13 +103,21 @@ ja:
|
|||
no_assignments_body: >
|
||||
割り当てられたトピックとメッセージがここに一覧表示されます。また、割り当てのリマインダーが定期的に通知されます。この通知頻度は<a href='%{preferencesUrl}'>ユーザー設定</a>で変更できます。 <br><br>トピックまたはメッセージを自分または別のユーザーに割り当てるには、下にある %{icon} 割り当てボタンを探してください。
|
||||
dismiss_assigned_tooltip: "すべての未読の割り当てを既読にします"
|
||||
assigned_to_group: "が %{group_name} に割り当てました"
|
||||
assigned_to_you: "があなたに割り当てました"
|
||||
assigned_to_group:
|
||||
post: "%{group_name} に割り当てられた投稿"
|
||||
topic: "%{group_name} に割り当てられたトピック"
|
||||
assigned_to_you:
|
||||
post: "あなたに割り当てられた投稿"
|
||||
topic: "あなたに割り当てらたトピック"
|
||||
assignment_description:
|
||||
post: "%{topic_title} (#%{post_number})"
|
||||
topic: "%{topic_title}"
|
||||
admin:
|
||||
web_hooks:
|
||||
assign_event:
|
||||
name: "割り当てイベント"
|
||||
details: "ユーザーがトピックを割り当てたか割り当てを解除したとき。"
|
||||
group_name: "割り当てイベント"
|
||||
assigned: "ユーザーがトピックを割り当てたとき"
|
||||
unassigned: "ユーザーがトピックの割り当てを解除したとき"
|
||||
search:
|
||||
advanced:
|
||||
in:
|
||||
|
|
|
@ -59,11 +59,8 @@ ko:
|
|||
messages:
|
||||
assigned_title: "할당됨 (%{count})"
|
||||
assigned: "할당됨"
|
||||
admin:
|
||||
web_hooks:
|
||||
assign_event:
|
||||
name: "이벤트 할당"
|
||||
details: "사용자가 주제를 할당 또는 할당 해제 할 때"
|
||||
assignment_description:
|
||||
topic: "%{topic_title}"
|
||||
search:
|
||||
advanced:
|
||||
assigned:
|
||||
|
|
|
@ -31,6 +31,8 @@ lt:
|
|||
user:
|
||||
messages:
|
||||
assigned: "Priskirta"
|
||||
assignment_description:
|
||||
topic: "%{topic_title}"
|
||||
notifications:
|
||||
assigned: "<span>%{username}</span> %{description}"
|
||||
titles:
|
||||
|
|
|
@ -5,6 +5,11 @@
|
|||
# https://translate.discourse.org/
|
||||
|
||||
nl:
|
||||
admin_js:
|
||||
admin:
|
||||
site_settings:
|
||||
categories:
|
||||
discourse_assign: "Discourse-toewijzing"
|
||||
js:
|
||||
filters:
|
||||
unassigned:
|
||||
|
@ -35,9 +40,8 @@ nl:
|
|||
assigned_to: "Toegewezen aan"
|
||||
assigned_topic_to: "Topic toegewezen aan <a href='%{path}'>%{username}</a>"
|
||||
assign_post_to: "#%{post_number} toegewezen aan %{username}"
|
||||
assign_post_to_multiple: "#%{post_number} aan %{username}"
|
||||
assigned_to_w_ellipsis: "Toegewezen aan..."
|
||||
assign_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
assign_group_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
unassign:
|
||||
title: "Toewijzing opheffen"
|
||||
title_w_ellipsis: "Toewijzing opheffen..."
|
||||
|
@ -99,13 +103,21 @@ nl:
|
|||
no_assignments_body: >
|
||||
Je toegewezen topics en berichten worden hier weergegeven. Ook ontvang je periodiek een herinneringsmelding over je toewijzingen, die je kunt aanpassen in je <a href='%{preferencesUrl}'>gebruikersvoorkeuren</a>. <br><br> Om een topic of bericht aan jezelf of iemand anders toe te wijzen, zoek je de knop %{icon} Toewijzen onderaan.
|
||||
dismiss_assigned_tooltip: "Alle ongelezen toewijzingsmeldingen markeren als gelezen"
|
||||
assigned_to_group: "toegewezen aan %{group_name}"
|
||||
assigned_to_you: "aan jou toegewezen"
|
||||
assigned_to_group:
|
||||
post: "bericht toegewezen aan %{group_name}"
|
||||
topic: "topic toegewezen aan %{group_name}"
|
||||
assigned_to_you:
|
||||
post: "bericht toegewezen aan jou"
|
||||
topic: "topic toegewezen aan jou"
|
||||
assignment_description:
|
||||
post: "%{topic_title} (#%{post_number})"
|
||||
topic: "%{topic_title}"
|
||||
admin:
|
||||
web_hooks:
|
||||
assign_event:
|
||||
name: "Toewijzingsgebeurtenis"
|
||||
details: "Wanneer een gebruiker een topic toewijst of de toewijzing ervan opheft."
|
||||
group_name: "Toewijzingsgebeurtenissen"
|
||||
assigned: "Wanneer een gebruiker een topic toewijst"
|
||||
unassigned: "Wanneer een gebruiker de toewijzing van een topic opheft"
|
||||
search:
|
||||
advanced:
|
||||
in:
|
||||
|
|
|
@ -36,8 +36,6 @@ pl_PL:
|
|||
assigned_topic_to: "Przypisano temat do <a href='%{path}'>%{username}</a>"
|
||||
assign_post_to: "Przypisano #%{post_number} do %{username}"
|
||||
assigned_to_w_ellipsis: "Przypisano do..."
|
||||
assign_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
assign_group_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
unassign:
|
||||
title: "Cofnij przypisanie"
|
||||
title_w_ellipsis: "Cofnij przypisanie..."
|
||||
|
@ -98,13 +96,19 @@ pl_PL:
|
|||
no_assignments_body: >
|
||||
Tutaj zostaną wyświetlone Twoje przypisane tematy i wiadomości. Będziesz również otrzymywać okresowe przypomnienie o swoich przypisach, które możesz dostosować w swoich <a href='%{preferencesUrl}'>preferencjach użytkownika</a>. <br><br> Aby przypisać temat lub wiadomość do siebie lub innej osoby, poszukaj przycisku %{icon} przypisywania na dole.
|
||||
dismiss_assigned_tooltip: "Oznacz wszystkie nieprzeczytane powiadomienia o przypisaniu jako przeczytane"
|
||||
assigned_to_group: "przypisany do %{group_name}"
|
||||
assigned_to_you: "przypisany do Ciebie"
|
||||
assigned_to_group:
|
||||
post: "post przypisany do %{group_name}"
|
||||
topic: "temat przypisany do %{group_name}"
|
||||
assigned_to_you:
|
||||
post: "post przypisany do Ciebie"
|
||||
topic: "temat przypisany do Ciebie"
|
||||
assignment_description:
|
||||
topic: "%{topic_title}"
|
||||
admin:
|
||||
web_hooks:
|
||||
assign_event:
|
||||
name: "Przypisz wydarzenie"
|
||||
details: "Gdy użytkownik przypisuje lub cofa przypisanie tematu."
|
||||
assigned: "Gdy użytkownik przypisze temat"
|
||||
unassigned: "Gdy użytkownik anuluje przypisanie tematu"
|
||||
search:
|
||||
advanced:
|
||||
in:
|
||||
|
@ -147,5 +151,6 @@ pl_PL:
|
|||
titles:
|
||||
assigned: "Przypisany"
|
||||
user_menu:
|
||||
view_all_assigned: "wyświetl wszystkie przypisania"
|
||||
tabs:
|
||||
assign_list: "Lista przypisań"
|
||||
|
|
|
@ -36,8 +36,6 @@ pt:
|
|||
assigned_topic_to: "Tópico atribuído a <a href='%{path}'>%{username}</a>"
|
||||
assign_post_to: "Atribuiu #%{post_number} a %{username}"
|
||||
assigned_to_w_ellipsis: "Atribuído(a) a..."
|
||||
assign_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
assign_group_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
unassign:
|
||||
title: "Cancelar atribuição"
|
||||
title_w_ellipsis: "Cancelar atribuição..."
|
||||
|
@ -99,13 +97,8 @@ pt:
|
|||
no_assignments_body: >
|
||||
Seus tópicos e mensagens atribuídos serão listados aqui. Você receberá uma notificação periódica com um lembrete das suas atribuições, que pode ser ajustada nas suas <a href='%{preferencesUrl}'>preferências do(a) usuário(a)</a>. <br><br> Para atribuir um tópico ou mensagem a si ou a outra pessoa, procure o botão de atribuir %{icon} na parte inferior.
|
||||
dismiss_assigned_tooltip: "Marcar como lidas todas as notificações não atribuídas não lidas"
|
||||
assigned_to_group: "atribuiu a %{group_name}"
|
||||
assigned_to_you: "atribuiu a você"
|
||||
admin:
|
||||
web_hooks:
|
||||
assign_event:
|
||||
name: "Atribuir evento"
|
||||
details: "Quando um(a) usuário(a) atribui ou cancela atribuição de um tópico."
|
||||
assignment_description:
|
||||
topic: "%{topic_title}"
|
||||
search:
|
||||
advanced:
|
||||
in:
|
||||
|
|
|
@ -5,6 +5,11 @@
|
|||
# https://translate.discourse.org/
|
||||
|
||||
pt_BR:
|
||||
admin_js:
|
||||
admin:
|
||||
site_settings:
|
||||
categories:
|
||||
discourse_assign: "Atribuição do Discourse"
|
||||
js:
|
||||
filters:
|
||||
unassigned:
|
||||
|
@ -35,9 +40,8 @@ pt_BR:
|
|||
assigned_to: "Atribuiu a"
|
||||
assigned_topic_to: "Tópico atribuído a <a href='%{path}'>%{username}</a>"
|
||||
assign_post_to: "Atribuiu #%{post_number} a %{username}"
|
||||
assign_post_to_multiple: "#%{post_number} para %{username}"
|
||||
assigned_to_w_ellipsis: "Atribuiu a..."
|
||||
assign_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
assign_group_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
unassign:
|
||||
title: "Remover atribuição"
|
||||
title_w_ellipsis: "Remover atribuição..."
|
||||
|
@ -99,13 +103,21 @@ pt_BR:
|
|||
no_assignments_body: >
|
||||
Seus tópicos e mensagens atribuídas serão listados aqui. Você receberá uma notificação periódica de lembrete das suas atribuições, que você pode ajustar em suas <a href='%{preferencesUrl}'>preferências de usuário(a)/a>. <br><br> Para atribuir uma mensagem ou tópico a si ou a outra pessoa, procure o botão de atribuir %{icon} na parte inferior.
|
||||
dismiss_assigned_tooltip: "Marcar todas as notificações atribuídas não lidas como lidas"
|
||||
assigned_to_group: "atribuiu a %{group_name}"
|
||||
assigned_to_you: "atribuiu a você"
|
||||
assigned_to_group:
|
||||
post: "postagem atribuída a %{group_name}"
|
||||
topic: "tópico atribuído a %{group_name}"
|
||||
assigned_to_you:
|
||||
post: "postagem atribuída a você"
|
||||
topic: "tópico atribuído a você"
|
||||
assignment_description:
|
||||
post: "%{topic_title} (#%{post_number})"
|
||||
topic: "%{topic_title}"
|
||||
admin:
|
||||
web_hooks:
|
||||
assign_event:
|
||||
name: "Evento Atribuir"
|
||||
details: "Quando usuário(a) atribui ou remove atribuição de um tópico."
|
||||
group_name: "Eventos de atribuição"
|
||||
assigned: "Quando um(a) usuário(a) atribuir um tópico"
|
||||
unassigned: "Quando um(a) usuário(a) cancelar a atribuição de um tópico"
|
||||
search:
|
||||
advanced:
|
||||
in:
|
||||
|
|
|
@ -22,5 +22,8 @@ ro:
|
|||
weekly: "Săptămânal"
|
||||
monthly: "Lunar"
|
||||
quarterly: "Trimestrial"
|
||||
user:
|
||||
assignment_description:
|
||||
topic: "%{topic_title}"
|
||||
notifications:
|
||||
assigned: "<span>%{username}</span> %{description}"
|
||||
|
|
|
@ -5,6 +5,11 @@
|
|||
# https://translate.discourse.org/
|
||||
|
||||
ru:
|
||||
admin_js:
|
||||
admin:
|
||||
site_settings:
|
||||
categories:
|
||||
discourse_assign: "Плагин Discourse «Назначение»"
|
||||
js:
|
||||
filters:
|
||||
unassigned:
|
||||
|
@ -35,9 +40,8 @@ ru:
|
|||
assigned_to: "Назначено:"
|
||||
assigned_topic_to: "Назначил(а) тему пользователю <a href='%{path}'>%{username}</a>"
|
||||
assign_post_to: "Назначил(а) ответственным %{username} в сообщение #%{post_number}"
|
||||
assign_post_to_multiple: "#%{post_number} для пользователя %{username}"
|
||||
assigned_to_w_ellipsis: "Назначено..."
|
||||
assign_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
assign_group_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
unassign:
|
||||
title: "Снять ответственного"
|
||||
title_w_ellipsis: "Отменить назначение..."
|
||||
|
@ -99,13 +103,21 @@ ru:
|
|||
no_assignments_body: >
|
||||
Здесь будут перечислены назначенные вами темы и сообщения. Вы также будете получать периодические напоминания о ваших назначениях, которые вы можете изменить в <a href='%{preferencesUrl}'>настройках пользователя</a>. <br><br> Чтобы назначить тему или сообщение себе или кому-то ещё, используйте кнопку %{icon}, расположенную в нижней части темы.
|
||||
dismiss_assigned_tooltip: "Пометить все непрочитанные уведомления о назначении как прочитанные"
|
||||
assigned_to_group: "назначен(а) группе %{group_name}"
|
||||
assigned_to_you: "назначен(а) вам"
|
||||
assigned_to_group:
|
||||
post: "публикация назначена группе %{group_name}"
|
||||
topic: "тема назначена группе %{group_name}"
|
||||
assigned_to_you:
|
||||
post: "публикация, назначенная вам"
|
||||
topic: "тема, назначенная вам"
|
||||
assignment_description:
|
||||
post: "%{topic_title} (#%{post_number})"
|
||||
topic: "%{topic_title}"
|
||||
admin:
|
||||
web_hooks:
|
||||
assign_event:
|
||||
name: "Событие назначения"
|
||||
details: "При назначении или отмене назначения ответственного в тему"
|
||||
group_name: "Назначение событий"
|
||||
assigned: "Когда пользователь назначает тему"
|
||||
unassigned: "Когда пользователь отменяет назначение темы"
|
||||
search:
|
||||
advanced:
|
||||
in:
|
||||
|
|
|
@ -48,6 +48,8 @@ sk:
|
|||
user:
|
||||
messages:
|
||||
assigned: "Priradené"
|
||||
assignment_description:
|
||||
topic: "%{topic_title}"
|
||||
search:
|
||||
advanced:
|
||||
assigned:
|
||||
|
|
|
@ -22,5 +22,8 @@ sl:
|
|||
weekly: "Tedensko"
|
||||
monthly: "Mesečno"
|
||||
quarterly: "V četrtletju"
|
||||
user:
|
||||
assignment_description:
|
||||
topic: "%{topic_title}"
|
||||
notifications:
|
||||
assigned: "<span>%{username}</span> %{description}"
|
||||
|
|
|
@ -19,3 +19,6 @@ sq:
|
|||
weekly: "Javore"
|
||||
monthly: "Mujore"
|
||||
quarterly: "Tremujorsh"
|
||||
user:
|
||||
assignment_description:
|
||||
topic: "%{topic_title}"
|
||||
|
|
|
@ -34,8 +34,6 @@ sv:
|
|||
group_everyone: "Alla"
|
||||
assigned_to: "Tilldelat till"
|
||||
assigned_to_w_ellipsis: "Tilldelad till..."
|
||||
assign_notification: "<p><span>%{username}</span>%{description}</p>"
|
||||
assign_group_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
unassign:
|
||||
title: "Otilldela"
|
||||
title_w_ellipsis: "Ta bort tilldelning..."
|
||||
|
@ -94,13 +92,8 @@ sv:
|
|||
no_assignments_body: >
|
||||
Dina tilldelade ämnen och meddelanden visas här i en lista. Du kommer också att få en periodisk påminnelse om dina tilldelningar, som du kan justera i dina <a href='%{preferencesUrl}'>användarinställningar</a>. <br><br> Om du vill tilldela ett ämne eller ett meddelande till dig själv eller till någon annan letar du efter tilldelningsknappen %{icon} längst ned.
|
||||
dismiss_assigned_tooltip: "Markera alla olästa tilldelningsmeddelanden som lästa"
|
||||
assigned_to_group: "tilldelad till %{group_name}"
|
||||
assigned_to_you: "tilldelad till dig"
|
||||
admin:
|
||||
web_hooks:
|
||||
assign_event:
|
||||
name: "Tilldela händelse"
|
||||
details: "När en användare tilldelar eller återtar tilldelning för ett ämne."
|
||||
assignment_description:
|
||||
topic: "%{topic_title}"
|
||||
search:
|
||||
advanced:
|
||||
in:
|
||||
|
|
|
@ -51,6 +51,8 @@ sw:
|
|||
messages:
|
||||
assigned_title: "Amekabidhiwa (%{count})"
|
||||
assigned: "Imekabidhiwa"
|
||||
assignment_description:
|
||||
topic: "%{topic_title}"
|
||||
search:
|
||||
advanced:
|
||||
assigned:
|
||||
|
|
|
@ -5,6 +5,11 @@
|
|||
# https://translate.discourse.org/
|
||||
|
||||
tr_TR:
|
||||
admin_js:
|
||||
admin:
|
||||
site_settings:
|
||||
categories:
|
||||
discourse_assign: "Discourse Atama"
|
||||
js:
|
||||
filters:
|
||||
unassigned:
|
||||
|
@ -35,9 +40,8 @@ tr_TR:
|
|||
assigned_to: "Şuraya atandı:"
|
||||
assigned_topic_to: "Konu <a href='%{path}'>%{username}</a> adlı kullanıcıya atandı"
|
||||
assign_post_to: "#%{post_number}, %{username} adlı kullanıcıya atandı"
|
||||
assign_post_to_multiple: "%{username} adlı kullanıcıya #%{post_number}"
|
||||
assigned_to_w_ellipsis: "Şuraya atandı..."
|
||||
assign_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
assign_group_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
unassign:
|
||||
title: "Atamayı kaldır"
|
||||
title_w_ellipsis: "Atamayı kaldır..."
|
||||
|
@ -99,13 +103,21 @@ tr_TR:
|
|||
no_assignments_body: >
|
||||
Atanan konularınız ve mesajlarınız burada listelenir. Ayrıca, atamalarınızla ilgili periyodik bir anımsatma bildirimi alırsınız; bunu <a href='%{preferencesUrl}'>kullanıcı tercihlerinizden</a> ayarlayabilirsiniz. <br><br>Kendinize veya bir başkasına bir konu veya mesaj atamak için alttaki %{icon} ata düğmesini kullanın.
|
||||
dismiss_assigned_tooltip: "Okunmamış tüm atama bildirimlerini okundu olarak işaretle"
|
||||
assigned_to_group: "%{group_name} adlı gruba atandı"
|
||||
assigned_to_you: "size atandı"
|
||||
assigned_to_group:
|
||||
post: "gönderi %{group_name} adlı gruba atandı"
|
||||
topic: "konu %{group_name} adlı gruba atandı"
|
||||
assigned_to_you:
|
||||
post: "gönderi size atandı"
|
||||
topic: "konu size atandı"
|
||||
assignment_description:
|
||||
post: "%{topic_title} (#%{post_number})"
|
||||
topic: "%{topic_title}"
|
||||
admin:
|
||||
web_hooks:
|
||||
assign_event:
|
||||
name: "Etkinlik Ata"
|
||||
details: "Bir kullanıcı bir konuyu atadığında veya atamasını kaldırdığında."
|
||||
group_name: "Etkinlik Ata"
|
||||
assigned: "Bir kullanıcı bir konu atadığında"
|
||||
unassigned: "Bir kullanıcı bir konunun atamasını kaldırdığında"
|
||||
search:
|
||||
advanced:
|
||||
in:
|
||||
|
|
|
@ -59,6 +59,8 @@ uk:
|
|||
messages:
|
||||
assigned_title: "Призначено (%{count})"
|
||||
assigned: "Призначено"
|
||||
assignment_description:
|
||||
topic: "%{topic_title}"
|
||||
search:
|
||||
advanced:
|
||||
assigned:
|
||||
|
|
|
@ -31,8 +31,6 @@ ur:
|
|||
group_everyone: "ہر کوئی"
|
||||
assigned_to: "جس کو اسائین کیا گیا"
|
||||
assigned_to_w_ellipsis: "کو تفویض..."
|
||||
assign_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
assign_group_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
unassign:
|
||||
title: "غیر اسائین کریں"
|
||||
title_w_ellipsis: "غیر تفویض کریں..."
|
||||
|
@ -87,11 +85,8 @@ ur:
|
|||
no_assignments_title: "آپ کے پاس ابھی تک کوئی اسائنمنٹ نہیں ہے"
|
||||
no_assignments_body: >
|
||||
آپ کے تفویض کردہ عنوانات اور پیغامات یہاں درج ہوں گے۔ آپ کو اپنے اسائنمنٹس کی وقفے وقفہ سے یاد دہانی کا نوٹیفکیشن بھی ملے گا ، جسے آپ اپنی <a href='%{preferencesUrl}'>صارف ترجیحات</a>میں ایڈجسٹ کرسکتے ہیں۔ <br><br> اپنے آپ کو یا کسی اور کو موضوع یا پیغام تفویض کرنے کے لئے ، %{icon} تفویض والے بٹن کو تلاش کریں۔
|
||||
admin:
|
||||
web_hooks:
|
||||
assign_event:
|
||||
name: "واقعہ تفویض کریں"
|
||||
details: "جب کوئی صارف کسی موضوع کو تفویض یا غیر متعین کرتا ہے۔"
|
||||
assignment_description:
|
||||
topic: "%{topic_title}"
|
||||
search:
|
||||
advanced:
|
||||
in:
|
||||
|
|
|
@ -29,6 +29,8 @@ vi:
|
|||
user:
|
||||
messages:
|
||||
assigned: "Được chỉ định"
|
||||
assignment_description:
|
||||
topic: "%{topic_title}"
|
||||
notifications:
|
||||
assigned: "<span>%{username}</span> %{description}"
|
||||
titles:
|
||||
|
|
|
@ -5,6 +5,11 @@
|
|||
# https://translate.discourse.org/
|
||||
|
||||
zh_CN:
|
||||
admin_js:
|
||||
admin:
|
||||
site_settings:
|
||||
categories:
|
||||
discourse_assign: "Discourse Assign"
|
||||
js:
|
||||
filters:
|
||||
unassigned:
|
||||
|
@ -35,9 +40,8 @@ zh_CN:
|
|||
assigned_to: "已分配给"
|
||||
assigned_topic_to: "将话题分配给 <a href='%{path}'>%{username}</a>"
|
||||
assign_post_to: "将 #%{post_number} 分配给 %{username}"
|
||||
assign_post_to_multiple: "#%{post_number} 被分配给 %{username}"
|
||||
assigned_to_w_ellipsis: "已分配给…"
|
||||
assign_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
assign_group_notification: "<p><span>%{username}</span> %{description}</p>"
|
||||
unassign:
|
||||
title: "取消分配"
|
||||
title_w_ellipsis: "取消分配…"
|
||||
|
@ -99,13 +103,21 @@ zh_CN:
|
|||
no_assignments_body: >
|
||||
分配给您的话题和消息将在此处列出。您还将收到关于您的任务分配的定期提醒通知,您可以在<a href='%{preferencesUrl}'>用户偏好设置</a>中进行调整。 <br><br>要将话题或消息分配给自己或其他人,请查找底部的 %{icon} 分配按钮。
|
||||
dismiss_assigned_tooltip: "将所有未读分配通知标记为已读"
|
||||
assigned_to_group: "已被分配至%{group_name}"
|
||||
assigned_to_you: "已被分配给您"
|
||||
assigned_to_group:
|
||||
post: "帖子被分配给 %{group_name}"
|
||||
topic: "话题被分配给 %{group_name}"
|
||||
assigned_to_you:
|
||||
post: "帖子被分配给您"
|
||||
topic: "话题被分配给您"
|
||||
assignment_description:
|
||||
post: "%{topic_title} (#%{post_number})"
|
||||
topic: "%{topic_title}"
|
||||
admin:
|
||||
web_hooks:
|
||||
assign_event:
|
||||
name: "分配事件"
|
||||
details: "当用户分配或取消分配话题时。"
|
||||
group_name: "分配事件"
|
||||
assigned: "当用户分配主题时"
|
||||
unassigned: "当用户取消分配主题时"
|
||||
search:
|
||||
advanced:
|
||||
in:
|
||||
|
|
|
@ -50,6 +50,8 @@ zh_TW:
|
|||
user:
|
||||
messages:
|
||||
assigned: "已指派"
|
||||
assignment_description:
|
||||
topic: "%{topic_title}"
|
||||
search:
|
||||
advanced:
|
||||
assigned:
|
||||
|
|
|
@ -43,6 +43,7 @@ pl_PL:
|
|||
flag_assigned: "Przepraszamy, temat, w którym jest ta flaga, został przypisany do innego użytkownika."
|
||||
flag_unclaimed: "Musisz przejąć ten temat, zanim zaczniesz działać na fladze."
|
||||
topic_assigned_excerpt: "przypisano ci temat '%{title}'"
|
||||
topic_group_assigned_excerpt: "przypisano temat '%{title}' do @%{group}"
|
||||
reminders_frequency:
|
||||
never: "nigdy"
|
||||
daily: "dziennie"
|
||||
|
|
|
@ -7,7 +7,6 @@ DiscourseAssign::Engine.routes.draw do
|
|||
get "/suggestions" => "assign#suggestions"
|
||||
get "/assigned" => "assign#assigned"
|
||||
get "/members/:group_name" => "assign#group_members"
|
||||
get "/user-menu-assigns" => "assign#user_menu_assigns"
|
||||
end
|
||||
|
||||
Discourse::Application.routes.draw do
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
plugins:
|
||||
discourse_assign:
|
||||
assign_enabled:
|
||||
default: false
|
||||
client: true
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class EnsureNotificationsConsistency < ActiveRecord::Migration[7.0]
|
||||
def up
|
||||
DB.exec(<<~SQL)
|
||||
DELETE FROM notifications
|
||||
WHERE id IN (
|
||||
SELECT notifications.id FROM notifications
|
||||
LEFT OUTER JOIN assignments ON assignments.id = ((notifications.data::jsonb)->'assignment_id')::int
|
||||
WHERE (notification_type = 34 AND assignments.id IS NULL OR assignments.active = FALSE)
|
||||
)
|
||||
SQL
|
||||
|
||||
DB.exec(<<~SQL)
|
||||
WITH tmp AS (
|
||||
SELECT
|
||||
assignments.assigned_to_id AS user_id,
|
||||
assignments.created_at,
|
||||
assignments.updated_at,
|
||||
assignments.topic_id,
|
||||
(
|
||||
CASE WHEN assignments.target_type = 'Topic' THEN 1
|
||||
ELSE (SELECT posts.post_number FROM posts WHERE posts.id = assignments.target_id)
|
||||
END
|
||||
) AS post_number,
|
||||
json_strip_nulls(json_build_object(
|
||||
'message', 'discourse_assign.assign_notification',
|
||||
'display_username', (SELECT users.username FROM users WHERE users.id = assignments.assigned_by_user_id),
|
||||
'topic_title', topics.title,
|
||||
'assignment_id', assignments.id
|
||||
))::text AS data
|
||||
FROM assignments
|
||||
LEFT OUTER JOIN topics ON topics.deleted_at IS NULL AND topics.id = assignments.topic_id
|
||||
LEFT OUTER JOIN users ON users.id = assignments.assigned_to_id AND assignments.assigned_to_type = 'User'
|
||||
LEFT OUTER JOIN notifications ON ((data::jsonb)->'assignment_id')::int = assignments.id
|
||||
WHERE assignments.active = TRUE
|
||||
AND NOT (topics.id IS NULL OR users.id IS NULL)
|
||||
AND assignments.assigned_to_type = 'User'
|
||||
AND notifications.id IS NULL
|
||||
)
|
||||
INSERT INTO notifications (notification_type, high_priority, read, user_id, created_at, updated_at, topic_id, post_number, data)
|
||||
SELECT 34, TRUE, TRUE, tmp.* FROM tmp
|
||||
SQL
|
||||
|
||||
DB.exec(<<~SQL)
|
||||
WITH tmp AS (
|
||||
SELECT
|
||||
users.id AS user_id,
|
||||
assignments.created_at,
|
||||
assignments.updated_at,
|
||||
assignments.topic_id,
|
||||
(
|
||||
CASE WHEN assignments.target_type = 'Topic' THEN 1
|
||||
ELSE (SELECT posts.post_number FROM posts WHERE posts.id = assignments.target_id)
|
||||
END
|
||||
) AS post_number,
|
||||
json_strip_nulls(json_build_object(
|
||||
'message', 'discourse_assign.assign_group_notification',
|
||||
'display_username', (SELECT groups.name FROM groups WHERE groups.id = assignments.assigned_to_id),
|
||||
'topic_title', topics.title,
|
||||
'assignment_id', assignments.id
|
||||
))::text AS data
|
||||
FROM assignments
|
||||
LEFT OUTER JOIN topics ON topics.deleted_at IS NULL AND topics.id = assignments.topic_id
|
||||
LEFT OUTER JOIN groups ON groups.id = assignments.assigned_to_id AND assignments.assigned_to_type = 'Group'
|
||||
LEFT OUTER JOIN group_users ON groups.id = group_users.group_id
|
||||
LEFT OUTER JOIN users ON users.id = group_users.user_id
|
||||
LEFT OUTER JOIN notifications ON ((data::jsonb)->'assignment_id')::int = assignments.id AND notifications.user_id = users.id
|
||||
WHERE assignments.active = TRUE
|
||||
AND NOT (topics.id IS NULL OR groups.id IS NULL)
|
||||
AND assignments.assigned_to_type = 'Group'
|
||||
AND notifications.id IS NULL
|
||||
)
|
||||
INSERT INTO notifications (notification_type, high_priority, read, user_id, created_at, updated_at, topic_id, post_number, data)
|
||||
SELECT 34, TRUE, TRUE, tmp.* FROM tmp
|
||||
SQL
|
||||
end
|
||||
|
||||
def down
|
||||
end
|
||||
end
|
|
@ -195,7 +195,7 @@ class ::Assigner
|
|||
topic.posts.where(post_number: 1).first
|
||||
end
|
||||
|
||||
def forbidden_reasons(assign_to:, type:, note:, status:)
|
||||
def forbidden_reasons(assign_to:, type:, note:, status:, allow_self_reassign:)
|
||||
case
|
||||
when assign_to.is_a?(User) && !can_assignee_see_target?(assign_to)
|
||||
if topic.private_message?
|
||||
|
@ -211,7 +211,7 @@ class ::Assigner
|
|||
end
|
||||
when !can_be_assigned?(assign_to)
|
||||
assign_to.is_a?(User) ? :forbidden_assign_to : :forbidden_group_assign_to
|
||||
when already_assigned?(assign_to, type, note, status)
|
||||
when !allow_self_reassign && already_assigned?(assign_to, type, note, status)
|
||||
assign_to.is_a?(User) ? :already_assigned : :group_already_assigned
|
||||
when Assignment.where(topic: topic, active: true).count >= ASSIGNMENTS_PER_TOPIC_LIMIT &&
|
||||
!reassign?
|
||||
|
@ -223,14 +223,15 @@ class ::Assigner
|
|||
|
||||
def update_details(assign_to, note, status, skip_small_action_post: false)
|
||||
case
|
||||
when @target.assignment.note != note && @target.assignment.status != status && status.present?
|
||||
when note.present? && status.present? && @target.assignment.note != note &&
|
||||
@target.assignment.status != status
|
||||
small_action_text = <<~TEXT
|
||||
Status: #{@target.assignment.status} → #{status}
|
||||
|
||||
#{note}
|
||||
TEXT
|
||||
change_type = "details"
|
||||
when @target.assignment.note != note
|
||||
when note.present? && @target.assignment.note != note
|
||||
small_action_text = note
|
||||
change_type = "note"
|
||||
when @target.assignment.status != status
|
||||
|
@ -239,11 +240,8 @@ class ::Assigner
|
|||
end
|
||||
|
||||
@target.assignment.update!(note: note, status: status)
|
||||
|
||||
queue_notification(assign_to, skip_small_action_post, @target.assignment)
|
||||
|
||||
assignment = @target.assignment
|
||||
publish_assignment(assignment, assign_to, note, status)
|
||||
queue_notification(@target.assignment)
|
||||
publish_assignment(@target.assignment, assign_to, note, status)
|
||||
|
||||
# email is skipped, for now
|
||||
|
||||
|
@ -255,7 +253,13 @@ class ::Assigner
|
|||
{ success: true }
|
||||
end
|
||||
|
||||
def assign(assign_to, note: nil, skip_small_action_post: false, status: nil)
|
||||
def assign(
|
||||
assign_to,
|
||||
note: nil,
|
||||
skip_small_action_post: false,
|
||||
status: nil,
|
||||
allow_self_reassign: false
|
||||
)
|
||||
assigned_to_type = assign_to.is_a?(User) ? "User" : "Group"
|
||||
|
||||
if topic.private_message? && SiteSetting.invite_on_assign
|
||||
|
@ -263,7 +267,13 @@ class ::Assigner
|
|||
end
|
||||
|
||||
forbidden_reason =
|
||||
forbidden_reasons(assign_to: assign_to, type: assigned_to_type, note: note, status: status)
|
||||
forbidden_reasons(
|
||||
assign_to: assign_to,
|
||||
type: assigned_to_type,
|
||||
note: note,
|
||||
status: status,
|
||||
allow_self_reassign: allow_self_reassign,
|
||||
)
|
||||
return { success: false, reason: forbidden_reason } if forbidden_reason
|
||||
|
||||
if no_assignee_change?(assign_to) && details_change?(note, status)
|
||||
|
@ -274,33 +284,31 @@ class ::Assigner
|
|||
action_code[:user] = topic.assignment.present? ? "reassigned" : "assigned"
|
||||
action_code[:group] = topic.assignment.present? ? "reassigned_group" : "assigned_group"
|
||||
|
||||
skip_small_action_post = skip_small_action_post || no_assignee_change?(assign_to)
|
||||
skip_small_action_post =
|
||||
skip_small_action_post || (!allow_self_reassign && no_assignee_change?(assign_to))
|
||||
|
||||
if topic.assignment.present?
|
||||
if @target.assignment
|
||||
Jobs.enqueue(
|
||||
:unassign_notification,
|
||||
topic_id: topic.id,
|
||||
assigned_to_id: topic.assignment.assigned_to_id,
|
||||
assigned_to_type: topic.assignment.assigned_to_type,
|
||||
assigned_to_id: @target.assignment.assigned_to_id,
|
||||
assigned_to_type: @target.assignment.assigned_to_type,
|
||||
assignment_id: @target.assignment.id,
|
||||
)
|
||||
@target.assignment.destroy!
|
||||
end
|
||||
|
||||
@target.assignment&.destroy!
|
||||
|
||||
assignment =
|
||||
@target.create_assignment!(
|
||||
assigned_to_id: assign_to.id,
|
||||
assigned_to_type: assigned_to_type,
|
||||
assigned_by_user_id: @assigned_by.id,
|
||||
topic_id: topic.id,
|
||||
assigned_to: assign_to,
|
||||
assigned_by_user: @assigned_by,
|
||||
topic: topic,
|
||||
note: note,
|
||||
status: status,
|
||||
)
|
||||
|
||||
first_post.publish_change_to_clients!(:revised, reload_topic: true)
|
||||
|
||||
queue_notification(assign_to, skip_small_action_post, assignment)
|
||||
|
||||
queue_notification(assignment)
|
||||
publish_assignment(assignment, assign_to, note, status)
|
||||
|
||||
if assignment.assigned_to_user?
|
||||
|
@ -335,7 +343,7 @@ class ::Assigner
|
|||
end
|
||||
|
||||
# Create a webhook event
|
||||
if WebHook.active_web_hooks(:assign).exists?
|
||||
if WebHook.active_web_hooks(:assigned).exists?
|
||||
assigned_to_type = :assigned
|
||||
payload = {
|
||||
type: assigned_to_type,
|
||||
|
@ -370,24 +378,9 @@ class ::Assigner
|
|||
topic_id: topic.id,
|
||||
assigned_to_id: assignment.assigned_to.id,
|
||||
assigned_to_type: assignment.assigned_to_type,
|
||||
assignment_id: assignment.id,
|
||||
)
|
||||
|
||||
if assignment.assigned_to_user?
|
||||
if TopicUser.exists?(
|
||||
user_id: assignment.assigned_to_id,
|
||||
topic: topic,
|
||||
notification_level: TopicUser.notification_levels[:watching],
|
||||
notifications_reason_id: TopicUser.notification_reasons[:plugin_changed],
|
||||
)
|
||||
TopicUser.change(
|
||||
assignment.assigned_to_id,
|
||||
topic.id,
|
||||
notification_level: TopicUser.notification_levels[:tracking],
|
||||
notifications_reason_id: TopicUser.notification_reasons[:plugin_changed],
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
assigned_to = assignment.assigned_to
|
||||
|
||||
if SiteSetting.unassign_creates_tracking_post && !silent
|
||||
|
@ -413,7 +406,7 @@ class ::Assigner
|
|||
end
|
||||
|
||||
# Create a webhook event
|
||||
if WebHook.active_web_hooks(:assign).exists?
|
||||
if WebHook.active_web_hooks(:unassigned).exists?
|
||||
type = :unassigned
|
||||
payload = {
|
||||
type: type,
|
||||
|
@ -477,17 +470,8 @@ class ::Assigner
|
|||
@guardian ||= Guardian.new(@assigned_by)
|
||||
end
|
||||
|
||||
def queue_notification(assign_to, skip_small_action_post, assignment)
|
||||
Jobs.enqueue(
|
||||
:assign_notification,
|
||||
topic_id: topic.id,
|
||||
post_id: topic_target? ? first_post.id : @target.id,
|
||||
assigned_to_id: assign_to.id,
|
||||
assigned_to_type: assign_to.is_a?(User) ? "User" : "Group",
|
||||
assigned_by_id: @assigned_by.id,
|
||||
skip_small_action_post: skip_small_action_post,
|
||||
assignment_id: assignment.id,
|
||||
)
|
||||
def queue_notification(assignment)
|
||||
Jobs.enqueue(:assign_notification, assignment_id: assignment.id)
|
||||
end
|
||||
|
||||
def add_small_action_post(action_code, assign_to, text)
|
||||
|
@ -546,7 +530,7 @@ class ::Assigner
|
|||
""
|
||||
end
|
||||
return "unassigned#{suffix}" if assignment.assigned_to_user?
|
||||
return "unassigned_group#{suffix}" if assignment.assigned_to_group?
|
||||
"unassigned_group#{suffix}" if assignment.assigned_to_group?
|
||||
end
|
||||
|
||||
def already_assigned?(assign_to, type, note, status)
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module DiscourseAssign
|
||||
class CreateNotification
|
||||
class UserAssignment
|
||||
attr_reader :assignment
|
||||
|
||||
def initialize(assignment)
|
||||
@assignment = assignment
|
||||
end
|
||||
|
||||
def excerpt_key
|
||||
"discourse_assign.topic_assigned_excerpt"
|
||||
end
|
||||
|
||||
def notification_message
|
||||
"discourse_assign.assign_notification"
|
||||
end
|
||||
|
||||
def display_username
|
||||
assignment.assigned_by_user.username
|
||||
end
|
||||
end
|
||||
|
||||
class GroupAssignment < UserAssignment
|
||||
def excerpt_key
|
||||
"discourse_assign.topic_group_assigned_excerpt"
|
||||
end
|
||||
|
||||
def notification_message
|
||||
"discourse_assign.assign_group_notification"
|
||||
end
|
||||
|
||||
def display_username
|
||||
assignment.assigned_to.name
|
||||
end
|
||||
end
|
||||
|
||||
def self.call(...)
|
||||
new(...).call
|
||||
end
|
||||
|
||||
attr_reader :assignment, :user, :mark_as_read, :assignment_type
|
||||
alias mark_as_read? mark_as_read
|
||||
|
||||
delegate :topic,
|
||||
:post,
|
||||
:assigned_by_user,
|
||||
:assigned_to,
|
||||
:created_at,
|
||||
:updated_at,
|
||||
:assigned_to_user?,
|
||||
:id,
|
||||
to: :assignment,
|
||||
private: true
|
||||
delegate :excerpt_key,
|
||||
:notification_message,
|
||||
:display_username,
|
||||
to: :assignment_type,
|
||||
private: true
|
||||
|
||||
def initialize(assignment:, user:, mark_as_read:)
|
||||
@assignment = assignment
|
||||
@user = user
|
||||
@mark_as_read = mark_as_read
|
||||
@assignment_type =
|
||||
"#{self.class}::#{assignment.assigned_to.class}Assignment".constantize.new(assignment)
|
||||
end
|
||||
|
||||
def call
|
||||
Assigner.publish_topic_tracking_state(topic, user.id)
|
||||
unless mark_as_read?
|
||||
PostAlerter.new(post).create_notification_alert(
|
||||
user: user,
|
||||
post: post,
|
||||
username: assigned_by_user.username,
|
||||
notification_type: Notification.types[:assigned],
|
||||
excerpt:
|
||||
I18n.t(
|
||||
excerpt_key,
|
||||
title: topic.title,
|
||||
group: assigned_to.name,
|
||||
locale: user.effective_locale,
|
||||
),
|
||||
)
|
||||
end
|
||||
user.notifications.assigned.create!(
|
||||
created_at: created_at,
|
||||
updated_at: updated_at,
|
||||
topic: topic,
|
||||
post_number: post.post_number,
|
||||
high_priority: true,
|
||||
read: mark_as_read?,
|
||||
data: {
|
||||
message: notification_message,
|
||||
display_username: display_username,
|
||||
topic_title: topic.title,
|
||||
assignment_id: id,
|
||||
}.to_json,
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -2,12 +2,15 @@
|
|||
|
||||
module DiscourseAssign
|
||||
module GroupExtension
|
||||
def self.prepended(base)
|
||||
base.class_eval do
|
||||
scope :assignable,
|
||||
->(user) {
|
||||
where(
|
||||
"assignable_level in (:levels) OR
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
prepended do
|
||||
has_many :assignments, as: :assigned_to
|
||||
|
||||
scope :assignable,
|
||||
->(user) do
|
||||
where(
|
||||
"assignable_level in (:levels) OR
|
||||
(
|
||||
assignable_level = #{Group::ALIAS_LEVELS[:members_mods_and_admins]} AND id in (
|
||||
SELECT group_id FROM group_users WHERE user_id = :user_id)
|
||||
|
@ -15,11 +18,10 @@ module DiscourseAssign
|
|||
assignable_level = #{Group::ALIAS_LEVELS[:owners_mods_and_admins]} AND id in (
|
||||
SELECT group_id FROM group_users WHERE user_id = :user_id AND owner IS TRUE)
|
||||
)",
|
||||
levels: alias_levels(user),
|
||||
user_id: user&.id,
|
||||
)
|
||||
}
|
||||
end
|
||||
levels: alias_levels(user),
|
||||
user_id: user&.id,
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module DiscourseAssign
|
||||
module NotificationExtension
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
prepended do
|
||||
scope :assigned, -> { where(notification_type: Notification.types[:assigned]) }
|
||||
scope :for_assignment,
|
||||
->(assignment) do
|
||||
assigned.where("((data::jsonb)->'assignment_id')::int IN (?)", assignment)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -5,9 +5,7 @@ module DiscourseAssign
|
|||
def self.prepended(base)
|
||||
base.class_eval do
|
||||
def self.enqueue_assign_hooks(event, payload)
|
||||
if active_web_hooks("assign").exists?
|
||||
WebHook.enqueue_hooks(:assign, event, payload: payload)
|
||||
end
|
||||
WebHook.enqueue_hooks(:assign, event, payload: payload) if active_web_hooks(event).exists?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,130 +1,190 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RandomAssignUtils
|
||||
def self.raise_error(automation, message)
|
||||
raise("[discourse-automation id=#{automation.id}] #{message}.")
|
||||
attr_reader :context, :fields, :automation, :topic, :group
|
||||
|
||||
def self.automation_script!(...)
|
||||
new(...).automation_script!
|
||||
end
|
||||
|
||||
def self.log_info(automation, message)
|
||||
Rails.logger.info("[discourse-automation id=#{automation.id}] #{message}.")
|
||||
end
|
||||
|
||||
def self.automation_script!(context, fields, automation)
|
||||
raise_error(automation, "discourse-assign is not enabled") unless SiteSetting.assign_enabled?
|
||||
def initialize(context, fields, automation)
|
||||
@context = context
|
||||
@fields = fields
|
||||
@automation = automation
|
||||
|
||||
raise_error("discourse-assign is not enabled") unless SiteSetting.assign_enabled?
|
||||
unless topic_id = fields.dig("assigned_topic", "value")
|
||||
raise_error(automation, "`assigned_topic` not provided")
|
||||
raise_error("`assigned_topic` not provided")
|
||||
end
|
||||
|
||||
unless topic = Topic.find_by(id: topic_id)
|
||||
raise_error(automation, "Topic(#{topic_id}) not found")
|
||||
end
|
||||
|
||||
min_hours = fields.dig("minimum_time_between_assignments", "value").presence
|
||||
if min_hours &&
|
||||
TopicCustomField
|
||||
.where(name: "assigned_to_id", topic_id: topic_id)
|
||||
.where("created_at < ?", min_hours.to_i.hours.ago)
|
||||
.exists?
|
||||
log_info(automation, "Topic(#{topic_id}) has already been assigned recently")
|
||||
return
|
||||
unless @topic = Topic.find_by(id: topic_id)
|
||||
raise_error("Topic(#{topic_id}) not found")
|
||||
end
|
||||
|
||||
unless group_id = fields.dig("assignees_group", "value")
|
||||
raise_error(automation, "`assignees_group` not provided")
|
||||
raise_error("`assignees_group` not provided")
|
||||
end
|
||||
|
||||
unless group = Group.find_by(id: group_id)
|
||||
raise_error(automation, "Group(#{group_id}) not found")
|
||||
unless @group = Group.find_by(id: group_id)
|
||||
raise_error("Group(#{group_id}) not found")
|
||||
end
|
||||
|
||||
assignable_user_ids = User.assign_allowed.pluck(:id)
|
||||
users_on_holiday =
|
||||
Set.new(
|
||||
User.where(
|
||||
id: UserCustomField.where(name: "on_holiday", value: "t").select(:user_id),
|
||||
).pluck(:id),
|
||||
)
|
||||
|
||||
group_users = group.group_users.joins(:user)
|
||||
if skip_new_users_for_days = fields.dig("skip_new_users_for_days", "value").presence
|
||||
group_users = group_users.where("users.created_at < ?", skip_new_users_for_days.to_i.days.ago)
|
||||
end
|
||||
|
||||
group_users_ids =
|
||||
group_users
|
||||
.pluck("users.id")
|
||||
.filter { |user_id| assignable_user_ids.include?(user_id) }
|
||||
.reject { |user_id| users_on_holiday.include?(user_id) }
|
||||
|
||||
if group_users_ids.empty?
|
||||
RandomAssignUtils.no_one!(topic_id, group.name)
|
||||
return
|
||||
end
|
||||
|
||||
max_recently_assigned_days =
|
||||
(fields.dig("max_recently_assigned_days", "value").presence || 180).to_i.days.ago
|
||||
last_assignees_ids =
|
||||
RandomAssignUtils.recently_assigned_users_ids(topic_id, max_recently_assigned_days)
|
||||
users_ids = group_users_ids - last_assignees_ids
|
||||
if users_ids.blank?
|
||||
min_recently_assigned_days =
|
||||
(fields.dig("min_recently_assigned_days", "value").presence || 14).to_i.days.ago
|
||||
recently_assigned_users_ids =
|
||||
RandomAssignUtils.recently_assigned_users_ids(topic_id, min_recently_assigned_days)
|
||||
users_ids = group_users_ids - recently_assigned_users_ids
|
||||
end
|
||||
|
||||
if users_ids.blank?
|
||||
RandomAssignUtils.no_one!(topic_id, group.name)
|
||||
return
|
||||
end
|
||||
|
||||
if fields.dig("in_working_hours", "value")
|
||||
assign_to_user_id =
|
||||
users_ids.shuffle.find { |user_id| RandomAssignUtils.in_working_hours?(user_id) }
|
||||
end
|
||||
|
||||
assign_to_user_id ||= users_ids.sample
|
||||
if assign_to_user_id.blank?
|
||||
RandomAssignUtils.no_one!(topic_id, group.name)
|
||||
return
|
||||
end
|
||||
|
||||
assign_to = User.find(assign_to_user_id)
|
||||
result = nil
|
||||
if raw = fields.dig("post_template", "value").presence
|
||||
post =
|
||||
PostCreator.new(
|
||||
Discourse.system_user,
|
||||
raw: raw,
|
||||
skip_validations: true,
|
||||
topic_id: topic.id,
|
||||
).create!
|
||||
|
||||
result = Assigner.new(post, Discourse.system_user).assign(assign_to)
|
||||
|
||||
PostDestroyer.new(Discourse.system_user, post).destroy if !result[:success]
|
||||
else
|
||||
result = Assigner.new(topic, Discourse.system_user).assign(assign_to)
|
||||
end
|
||||
|
||||
RandomAssignUtils.no_one!(topic_id, group.name) if !result[:success]
|
||||
end
|
||||
|
||||
def self.recently_assigned_users_ids(topic_id, from)
|
||||
posts =
|
||||
Post
|
||||
.joins(:user)
|
||||
.where(topic_id: topic_id, action_code: %w[assigned reassigned assigned_to_post])
|
||||
.where("posts.created_at > ?", from)
|
||||
.order(created_at: :desc)
|
||||
def automation_script!
|
||||
return log_info("Topic(#{topic.id}) has already been assigned recently") if assigned_recently?
|
||||
return no_one! unless assigned_user
|
||||
assign_user!
|
||||
end
|
||||
|
||||
def recently_assigned_users_ids(from)
|
||||
usernames =
|
||||
Post.custom_fields_for_ids(posts, [:action_code_who]).map { |_, v| v["action_code_who"] }.uniq
|
||||
User.where(username: usernames).limit(100).pluck(:id)
|
||||
PostCustomField
|
||||
.joins(:post)
|
||||
.where(
|
||||
name: "action_code_who",
|
||||
posts: {
|
||||
topic: topic,
|
||||
action_code: %w[assigned reassigned assigned_to_post],
|
||||
},
|
||||
)
|
||||
.where("posts.created_at > ?", from)
|
||||
.order("posts.created_at DESC")
|
||||
.pluck(:value)
|
||||
.uniq
|
||||
User
|
||||
.where(username: usernames)
|
||||
.joins(
|
||||
"JOIN unnest('{#{usernames.join(",")}}'::text[]) WITH ORDINALITY t(username, ord) USING(username)",
|
||||
)
|
||||
.limit(100)
|
||||
.order("ord")
|
||||
.pluck(:id)
|
||||
end
|
||||
|
||||
def self.user_tzinfo(user_id)
|
||||
private
|
||||
|
||||
def assigned_user
|
||||
@assigned_user ||=
|
||||
begin
|
||||
group_users_ids = group_users.pluck(:id)
|
||||
return if group_users_ids.empty?
|
||||
|
||||
last_assignees_ids = recently_assigned_users_ids(max_recently_assigned_days)
|
||||
users_ids = group_users_ids - last_assignees_ids
|
||||
if users_ids.blank?
|
||||
recently_assigned_users_ids = recently_assigned_users_ids(min_recently_assigned_days)
|
||||
users_ids = group_users_ids - recently_assigned_users_ids
|
||||
end
|
||||
users_ids << last_assignees_ids.intersection(group_users_ids).last if users_ids.blank?
|
||||
if fields.dig("in_working_hours", "value")
|
||||
assign_to_user_id = users_ids.shuffle.detect { |user_id| in_working_hours?(user_id) }
|
||||
end
|
||||
assign_to_user_id ||= users_ids.sample
|
||||
|
||||
User.find(assign_to_user_id)
|
||||
end
|
||||
end
|
||||
|
||||
def assign_user!
|
||||
return create_post_template if post_template
|
||||
Assigner
|
||||
.new(topic, Discourse.system_user)
|
||||
.assign(assigned_user, allow_self_reassign: true)
|
||||
.then do |result|
|
||||
next if result[:success]
|
||||
no_one!
|
||||
end
|
||||
end
|
||||
|
||||
def create_post_template
|
||||
post =
|
||||
PostCreator.new(
|
||||
Discourse.system_user,
|
||||
raw: post_template,
|
||||
skip_validations: true,
|
||||
topic_id: topic.id,
|
||||
).create!
|
||||
Assigner
|
||||
.new(post, Discourse.system_user)
|
||||
.assign(assigned_user, allow_self_reassign: true)
|
||||
.then do |result|
|
||||
next if result[:success]
|
||||
PostDestroyer.new(Discourse.system_user, post).destroy
|
||||
no_one!
|
||||
end
|
||||
end
|
||||
|
||||
def group_users
|
||||
users =
|
||||
group
|
||||
.users
|
||||
.where(id: User.assign_allowed.select(:id))
|
||||
.where.not(
|
||||
id:
|
||||
User
|
||||
.joins(:_custom_fields)
|
||||
.where(user_custom_fields: { name: "on_holiday", value: "t" })
|
||||
.select(:id),
|
||||
)
|
||||
return users unless skip_new_users_for_days
|
||||
users.where("users.created_at < ?", skip_new_users_for_days)
|
||||
end
|
||||
|
||||
def raise_error(message)
|
||||
raise("[discourse-automation id=#{automation.id}] #{message}.")
|
||||
end
|
||||
|
||||
def log_info(message)
|
||||
Rails.logger.info("[discourse-automation id=#{automation.id}] #{message}.")
|
||||
end
|
||||
|
||||
def no_one!
|
||||
PostCreator.create!(
|
||||
Discourse.system_user,
|
||||
topic_id: topic.id,
|
||||
raw: I18n.t("discourse_automation.scriptables.random_assign.no_one", group: group.name),
|
||||
validate: false,
|
||||
)
|
||||
end
|
||||
|
||||
def assigned_recently?
|
||||
return unless min_hours
|
||||
TopicCustomField
|
||||
.where(name: "assigned_to_id", topic: topic)
|
||||
.where("created_at < ?", min_hours)
|
||||
.exists?
|
||||
end
|
||||
|
||||
def skip_new_users_for_days
|
||||
days = fields.dig("skip_new_users_for_days", "value").presence
|
||||
return unless days
|
||||
days.to_i.days.ago
|
||||
end
|
||||
|
||||
def max_recently_assigned_days
|
||||
@max_days ||= (fields.dig("max_recently_assigned_days", "value").presence || 180).to_i.days.ago
|
||||
end
|
||||
|
||||
def min_recently_assigned_days
|
||||
@min_days ||= (fields.dig("min_recently_assigned_days", "value").presence || 14).to_i.days.ago
|
||||
end
|
||||
|
||||
def post_template
|
||||
@post_template ||= fields.dig("post_template", "value").presence
|
||||
end
|
||||
|
||||
def min_hours
|
||||
hours = fields.dig("minimum_time_between_assignments", "value").presence
|
||||
return unless hours
|
||||
hours.to_i.hours.ago
|
||||
end
|
||||
|
||||
def in_working_hours?(user_id)
|
||||
tzinfo = user_tzinfo(user_id)
|
||||
tztime = tzinfo.now
|
||||
|
||||
!tztime.saturday? && !tztime.sunday? && tztime.hour > 7 && tztime.hour < 11
|
||||
end
|
||||
|
||||
def user_tzinfo(user_id)
|
||||
timezone = UserOption.where(user_id: user_id).pluck(:timezone).first || "UTC"
|
||||
|
||||
tzinfo = nil
|
||||
|
@ -140,20 +200,4 @@ class RandomAssignUtils
|
|||
|
||||
tzinfo
|
||||
end
|
||||
|
||||
def self.no_one!(topic_id, group)
|
||||
PostCreator.create!(
|
||||
Discourse.system_user,
|
||||
topic_id: topic_id,
|
||||
raw: I18n.t("discourse_automation.scriptables.random_assign.no_one", group: group),
|
||||
validate: false,
|
||||
)
|
||||
end
|
||||
|
||||
def self.in_working_hours?(user_id)
|
||||
tzinfo = RandomAssignUtils.user_tzinfo(user_id)
|
||||
tztime = tzinfo.now
|
||||
|
||||
!tztime.saturday? && !tztime.sunday? && tztime.hour > 7 && tztime.hour < 11
|
||||
end
|
||||
end
|
||||
|
|
12
package.json
12
package.json
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
"name": "discourse",
|
||||
"version": "1.0.1",
|
||||
"repository": "https://github.com/discourse/discourse-assign",
|
||||
"author": "Discourse",
|
||||
"license": "MIT",
|
||||
"name": "discourse-assign",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"eslint-config-discourse": "^3.4.0"
|
||||
"@discourse/lint-configs": "^1.3.5",
|
||||
"ember-template-lint": "^5.13.0",
|
||||
"eslint": "^8.56.0",
|
||||
"prettier": "^2.8.8"
|
||||
}
|
||||
}
|
||||
|
|
112
plugin.rb
112
plugin.rb
|
@ -1,7 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# name: discourse-assign
|
||||
# about: Assign users to topics
|
||||
# about: Provides the ability to assign topics and individual posts to a user or group.
|
||||
# meta_topic_id: 58044
|
||||
# version: 1.0.1
|
||||
# authors: Sam Saffron
|
||||
# url: https://github.com/discourse/discourse-assign
|
||||
|
@ -26,10 +27,12 @@ after_initialize do
|
|||
require_relative "app/jobs/regular/unassign_notification"
|
||||
require_relative "app/jobs/scheduled/enqueue_reminders"
|
||||
require_relative "lib/assigner"
|
||||
require_relative "lib/discourse_assign/create_notification"
|
||||
require_relative "lib/discourse_assign/discourse_calendar"
|
||||
require_relative "lib/discourse_assign/group_extension"
|
||||
require_relative "lib/discourse_assign/helpers"
|
||||
require_relative "lib/discourse_assign/list_controller_extension"
|
||||
require_relative "lib/discourse_assign/notification_extension"
|
||||
require_relative "lib/discourse_assign/post_extension"
|
||||
require_relative "lib/discourse_assign/topic_extension"
|
||||
require_relative "lib/discourse_assign/web_hook_extension"
|
||||
|
@ -38,11 +41,12 @@ after_initialize do
|
|||
require_relative "lib/topic_assigner"
|
||||
|
||||
reloadable_patch do |plugin|
|
||||
Group.class_eval { prepend DiscourseAssign::GroupExtension }
|
||||
ListController.class_eval { prepend DiscourseAssign::ListControllerExtension }
|
||||
Post.class_eval { prepend DiscourseAssign::PostExtension }
|
||||
Topic.class_eval { prepend DiscourseAssign::TopicExtension }
|
||||
WebHook.class_eval { prepend DiscourseAssign::WebHookExtension }
|
||||
Group.prepend(DiscourseAssign::GroupExtension)
|
||||
ListController.prepend(DiscourseAssign::ListControllerExtension)
|
||||
Post.prepend(DiscourseAssign::PostExtension)
|
||||
Topic.prepend(DiscourseAssign::TopicExtension)
|
||||
WebHook.prepend(DiscourseAssign::WebHookExtension)
|
||||
Notification.prepend(DiscourseAssign::NotificationExtension)
|
||||
end
|
||||
|
||||
register_group_param(:assignable_level)
|
||||
|
@ -52,7 +56,7 @@ after_initialize do
|
|||
|
||||
frequency_field = PendingAssignsReminder::REMINDERS_FREQUENCY
|
||||
register_editable_user_custom_field frequency_field
|
||||
User.register_custom_field_type frequency_field, :integer
|
||||
register_user_custom_field_type(frequency_field, :integer, max_length: 10)
|
||||
DiscoursePluginRegistry.serialized_current_user_fields << frequency_field
|
||||
add_to_serializer(:user, :reminders_frequency) { RemindAssignsFrequencySiteSettings.values }
|
||||
|
||||
|
@ -88,13 +92,10 @@ after_initialize do
|
|||
end
|
||||
|
||||
add_to_class(:user, :can_assign?) do
|
||||
@can_assign ||=
|
||||
begin
|
||||
return true if admin?
|
||||
allowed_groups = SiteSetting.assign_allowed_on_groups.split("|").compact
|
||||
allowed_groups.present? && groups.where(id: allowed_groups).exists? ? :true : :false
|
||||
end
|
||||
@can_assign == :true
|
||||
return @can_assign if defined?(@can_assign)
|
||||
|
||||
allowed_groups = SiteSetting.assign_allowed_on_groups.split("|").compact
|
||||
@can_assign = admin? || (allowed_groups.present? && groups.where(id: allowed_groups).exists?)
|
||||
end
|
||||
|
||||
add_to_serializer(:current_user, :never_auto_track_topics) do
|
||||
|
@ -167,7 +168,12 @@ after_initialize do
|
|||
|
||||
on(:unassign_topic) { |topic, unassigning_user| Assigner.new(topic, unassigning_user).unassign }
|
||||
|
||||
Site.preloaded_category_custom_fields << "enable_unassigned_filter"
|
||||
if respond_to?(:register_preloaded_category_custom_fields)
|
||||
register_preloaded_category_custom_fields("enable_unassigned_filter")
|
||||
else
|
||||
# TODO: Drop the if-statement and this if-branch in Discourse v3.2
|
||||
Site.preloaded_category_custom_fields << "enable_unassigned_filter"
|
||||
end
|
||||
|
||||
BookmarkQuery.on_preload do |bookmarks, bookmark_query|
|
||||
if SiteSetting.assign_enabled?
|
||||
|
@ -201,7 +207,8 @@ after_initialize do
|
|||
allowed_access = SiteSetting.assigns_public || can_assign
|
||||
|
||||
if allowed_access && topics.length > 0
|
||||
assignments = Assignment.strict_loading.where(topic: topics, active: true).includes(:target)
|
||||
assignments =
|
||||
Assignment.strict_loading.active.where(topic: topics).includes(:target, :assigned_to)
|
||||
assignments_map = assignments.group_by(&:topic_id)
|
||||
|
||||
user_ids =
|
||||
|
@ -503,7 +510,7 @@ after_initialize do
|
|||
:topic_view,
|
||||
:assigned_to_user,
|
||||
include_condition: -> do
|
||||
(SiteSetting.assigns_public || scope.can_assign?) && object.topic.assigned_to&.is_a?(User)
|
||||
(SiteSetting.assigns_public || scope.can_assign?) && object.topic.assigned_to.is_a?(User)
|
||||
end,
|
||||
) { DiscourseAssign::Helpers.build_assigned_to_user(object.topic.assigned_to, object.topic) }
|
||||
|
||||
|
@ -511,7 +518,7 @@ after_initialize do
|
|||
:topic_view,
|
||||
:assigned_to_group,
|
||||
include_condition: -> do
|
||||
(SiteSetting.assigns_public || scope.can_assign?) && object.topic.assigned_to&.is_a?(Group)
|
||||
(SiteSetting.assigns_public || scope.can_assign?) && object.topic.assigned_to.is_a?(Group)
|
||||
end,
|
||||
) { DiscourseAssign::Helpers.build_assigned_to_group(object.topic.assigned_to, object.topic) }
|
||||
|
||||
|
@ -551,7 +558,7 @@ after_initialize do
|
|||
:suggested_topic,
|
||||
:assigned_to_user,
|
||||
include_condition: -> do
|
||||
(SiteSetting.assigns_public || scope.can_assign?) && object.assigned_to&.is_a?(User)
|
||||
(SiteSetting.assigns_public || scope.can_assign?) && object.assigned_to.is_a?(User)
|
||||
end,
|
||||
) { DiscourseAssign::Helpers.build_assigned_to_user(object.assigned_to, object) }
|
||||
|
||||
|
@ -559,7 +566,7 @@ after_initialize do
|
|||
:suggested_topic,
|
||||
:assigned_to_group,
|
||||
include_condition: -> do
|
||||
(SiteSetting.assigns_public || scope.can_assign?) && object.assigned_to&.is_a?(Group)
|
||||
(SiteSetting.assigns_public || scope.can_assign?) && object.assigned_to.is_a?(Group)
|
||||
end,
|
||||
) { DiscourseAssign::Helpers.build_assigned_to_group(object.assigned_to, object) }
|
||||
|
||||
|
@ -584,7 +591,7 @@ after_initialize do
|
|||
:topic_list_item,
|
||||
:assigned_to_user,
|
||||
include_condition: -> do
|
||||
(SiteSetting.assigns_public || scope.can_assign?) && object.assigned_to&.is_a?(User)
|
||||
(SiteSetting.assigns_public || scope.can_assign?) && object.assigned_to.is_a?(User)
|
||||
end,
|
||||
) { BasicUserSerializer.new(object.assigned_to, scope: scope, root: false).as_json }
|
||||
|
||||
|
@ -592,7 +599,7 @@ after_initialize do
|
|||
:topic_list_item,
|
||||
:assigned_to_group,
|
||||
include_condition: -> do
|
||||
(SiteSetting.assigns_public || scope.can_assign?) && object.assigned_to&.is_a?(Group)
|
||||
(SiteSetting.assigns_public || scope.can_assign?) && object.assigned_to.is_a?(Group)
|
||||
end,
|
||||
) { AssignedGroupSerializer.new(object.assigned_to, scope: scope, root: false).as_json }
|
||||
|
||||
|
@ -610,7 +617,7 @@ after_initialize do
|
|||
:search_topic_list_item,
|
||||
:assigned_to_user,
|
||||
include_condition: -> do
|
||||
(SiteSetting.assigns_public || scope.can_assign?) && object.assigned_to&.is_a?(User)
|
||||
(SiteSetting.assigns_public || scope.can_assign?) && object.assigned_to.is_a?(User)
|
||||
end,
|
||||
) { DiscourseAssign::Helpers.build_assigned_to_user(object.assigned_to, object) }
|
||||
|
||||
|
@ -618,7 +625,7 @@ after_initialize do
|
|||
:search_topic_list_item,
|
||||
:assigned_to_group,
|
||||
include_condition: -> do
|
||||
(SiteSetting.assigns_public || scope.can_assign?) && object.assigned_to&.is_a?(Group)
|
||||
(SiteSetting.assigns_public || scope.can_assign?) && object.assigned_to.is_a?(Group)
|
||||
end,
|
||||
) { AssignedGroupSerializer.new(object.assigned_to, scope: scope, root: false).as_json }
|
||||
|
||||
|
@ -635,7 +642,11 @@ after_initialize do
|
|||
if @user.can_assign?
|
||||
assign_user = User.find_by_username(@operation[:username])
|
||||
topics.each do |topic|
|
||||
Assigner.new(topic, @user).assign(assign_user, note: @operation[:note])
|
||||
Assigner.new(topic, @user).assign(
|
||||
assign_user,
|
||||
status: @operation[:status],
|
||||
note: @operation[:note],
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -647,6 +658,7 @@ after_initialize do
|
|||
end
|
||||
|
||||
register_permitted_bulk_action_parameter :username
|
||||
register_permitted_bulk_action_parameter :status
|
||||
register_permitted_bulk_action_parameter :note
|
||||
|
||||
add_to_class(:user_bookmark_base_serializer, :assigned_to) do
|
||||
|
@ -663,7 +675,7 @@ after_initialize do
|
|||
:assigned_to_user,
|
||||
include_condition: -> do
|
||||
return false if !can_have_assignment?
|
||||
(SiteSetting.assigns_public || scope.can_assign?) && assigned_to&.is_a?(User)
|
||||
(SiteSetting.assigns_public || scope.can_assign?) && assigned_to.is_a?(User)
|
||||
end,
|
||||
) do
|
||||
return if !can_have_assignment?
|
||||
|
@ -675,7 +687,7 @@ after_initialize do
|
|||
:assigned_to_group,
|
||||
include_condition: -> do
|
||||
return false if !can_have_assignment?
|
||||
(SiteSetting.assigns_public || scope.can_assign?) && assigned_to&.is_a?(Group)
|
||||
(SiteSetting.assigns_public || scope.can_assign?) && assigned_to.is_a?(Group)
|
||||
end,
|
||||
) do
|
||||
return if !can_have_assignment?
|
||||
|
@ -694,7 +706,7 @@ after_initialize do
|
|||
:assigned_to_user,
|
||||
include_condition: -> do
|
||||
(SiteSetting.assigns_public || scope.can_assign?) &&
|
||||
object.assignment&.assigned_to&.is_a?(User) && object.assignment.active
|
||||
object.assignment&.assigned_to.is_a?(User) && object.assignment.active
|
||||
end,
|
||||
) { BasicUserSerializer.new(object.assignment.assigned_to, scope: scope, root: false).as_json }
|
||||
|
||||
|
@ -703,7 +715,7 @@ after_initialize do
|
|||
:assigned_to_group,
|
||||
include_condition: -> do
|
||||
(SiteSetting.assigns_public || scope.can_assign?) &&
|
||||
object.assignment&.assigned_to&.is_a?(Group) && object.assignment.active
|
||||
object.assignment&.assigned_to.is_a?(Group) && object.assignment.active
|
||||
end,
|
||||
) do
|
||||
AssignedGroupSerializer.new(object.assignment.assigned_to, scope: scope, root: false).as_json
|
||||
|
@ -733,13 +745,13 @@ after_initialize do
|
|||
add_to_serializer(
|
||||
:flagged_topic,
|
||||
:assigned_to_user,
|
||||
include_condition: -> { object.assigned_to && object.assigned_to&.is_a?(User) },
|
||||
include_condition: -> { object.assigned_to && object.assigned_to.is_a?(User) },
|
||||
) { DiscourseAssign::Helpers.build_assigned_to_user(object.assigned_to, object) }
|
||||
|
||||
add_to_serializer(
|
||||
:flagged_topic,
|
||||
:assigned_to_group,
|
||||
include_condition: -> { object.assigned_to && object.assigned_to&.is_a?(Group) },
|
||||
include_condition: -> { object.assigned_to && object.assigned_to.is_a?(Group) },
|
||||
) { DiscourseAssign::Helpers.build_assigned_to_group(object.assigned_to, object) }
|
||||
|
||||
# Reviewable
|
||||
|
@ -836,20 +848,12 @@ after_initialize do
|
|||
next if !info[:group]
|
||||
|
||||
Assignment
|
||||
.where(topic_id: topic.id, active: false)
|
||||
.inactive
|
||||
.where(topic: topic)
|
||||
.find_each do |assignment|
|
||||
next unless assignment.target
|
||||
assignment.update!(active: true)
|
||||
Jobs.enqueue(
|
||||
:assign_notification,
|
||||
topic_id: topic.id,
|
||||
post_id: assignment.target_type.is_a?(Topic) ? topic.first_post.id : assignment.target.id,
|
||||
assigned_to_id: assignment.assigned_to_id,
|
||||
assigned_to_type: assignment.assigned_to_type,
|
||||
assigned_by_id: assignment.assigned_by_user_id,
|
||||
skip_small_action_post: true,
|
||||
assignment_id: assignment.id,
|
||||
)
|
||||
Jobs.enqueue(:assign_notification, assignment_id: assignment.id)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -863,32 +867,30 @@ after_initialize do
|
|||
next if !info[:group]
|
||||
|
||||
Assignment
|
||||
.where(topic_id: topic.id, active: true)
|
||||
.active
|
||||
.where(topic: topic)
|
||||
.find_each do |assignment|
|
||||
assignment.update!(active: false)
|
||||
Jobs.enqueue(
|
||||
:unassign_notification,
|
||||
topic_id: topic.id,
|
||||
assigned_to_id: assignment.assigned_to.id,
|
||||
assigned_to_id: assignment.assigned_to_id,
|
||||
assigned_to_type: assignment.assigned_to_type,
|
||||
assignment_id: assignment.id,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
on(:user_removed_from_group) do |user, group|
|
||||
assign_allowed_groups = SiteSetting.assign_allowed_on_groups.split("|").map(&:to_i)
|
||||
|
||||
if assign_allowed_groups.include?(group.id)
|
||||
groups = GroupUser.where(user: user).pluck(:group_id)
|
||||
|
||||
if (groups & assign_allowed_groups).empty?
|
||||
topics = Topic.joins(:assignment).where("assignments.assigned_to_id = ?", user.id)
|
||||
|
||||
topics.each { |topic| Assigner.new(topic, Discourse.system_user).unassign }
|
||||
end
|
||||
on(:user_added_to_group) do |user, group, automatic:|
|
||||
group.assignments.active.find_each do |assignment|
|
||||
Jobs.enqueue(:assign_notification, assignment_id: assignment.id)
|
||||
end
|
||||
end
|
||||
|
||||
on(:user_removed_from_group) do |user, group|
|
||||
user.notifications.for_assignment(group.assignments.select(:id)).destroy_all
|
||||
end
|
||||
|
||||
on(:post_moved) do |post, original_topic_id|
|
||||
assignment =
|
||||
Assignment.where(topic_id: original_topic_id, target_type: "Post", target_id: post.id).first
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue