assigned users notes
This commit is contained in:
@@ -272,7 +272,10 @@ class Note extends Entity
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setData(stdClass $data): self
|
||||
/**
|
||||
* @param stdClass|array<string, mixed> $data
|
||||
*/
|
||||
public function setData(stdClass|array $data): self
|
||||
{
|
||||
$this->set('data', $data);
|
||||
|
||||
|
||||
@@ -565,6 +565,14 @@
|
||||
"assignThisSelf": "{user} self-assigned this {entityType}",
|
||||
"assignSelf": "{user} self-assigned {entityType} {entity}",
|
||||
|
||||
"assignMultiAdd": "{user} assigned {entity} to {assignee}",
|
||||
"assignMultiRemove": "{user} unassigned {entity} from {removedAssignee}",
|
||||
"assignMultiAddRemove": "{user} assigned {entity} to {assignee} and unassigned from {removedAssignee}",
|
||||
|
||||
"assignMultiAddThis": "{user} assigned this {entityType} to {assignee}",
|
||||
"assignMultiRemoveThis": "{user} unassigned this {entityType} from {removedAssignee}",
|
||||
"assignMultiAddRemoveThis": "{user} assigned this {entityType} to {assignee} and unassigned from {removedAssignee}",
|
||||
|
||||
"postThis": "{user} posted",
|
||||
"attachThis": "{user} attached",
|
||||
"statusThis": "{user} updated {field}",
|
||||
|
||||
@@ -62,6 +62,8 @@ class HookProcessor
|
||||
/** @var ?array<string, ?string> */
|
||||
private $statusFields = null;
|
||||
|
||||
private const FIELD_ASSIGNED_USERS = 'assignedUsers';
|
||||
|
||||
public function __construct(
|
||||
private Metadata $metadata,
|
||||
private EntityManager $entityManager,
|
||||
@@ -425,6 +427,11 @@ class HookProcessor
|
||||
|
||||
if ($entity->isAttributeChanged('assignedUserId')) {
|
||||
$this->afterSaveStreamNotNewAssignedUserIdChanged($entity, $options);
|
||||
} else if (
|
||||
$entity->hasLinkMultipleField(self::FIELD_ASSIGNED_USERS) &&
|
||||
$entity->isAttributeChanged(self::FIELD_ASSIGNED_USERS . 'Ids')
|
||||
) {
|
||||
$this->afterSaveStreamNotNewAssignedUsersIdsChanged($entity, $options);
|
||||
}
|
||||
|
||||
$this->service->handleAudited($entity, $options);
|
||||
@@ -483,6 +490,27 @@ class HookProcessor
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $options
|
||||
*/
|
||||
private function afterSaveStreamNotNewAssignedUsersIdsChanged(CoreEntity $entity, array $options): void
|
||||
{
|
||||
$userIds = $entity->getLinkMultipleIdList(self::FIELD_ASSIGNED_USERS);
|
||||
|
||||
/** @var string[] $prevUserIds */
|
||||
$prevUserIds = $entity->getFetched(self::FIELD_ASSIGNED_USERS . 'Ids') ?? [];
|
||||
|
||||
foreach (array_diff($userIds, $prevUserIds) as $userId) {
|
||||
$this->service->followEntity($entity, $userId);
|
||||
}
|
||||
|
||||
$this->service->noteAssign($entity, $options);
|
||||
|
||||
if (in_array($this->user->getId(), $userIds)) {
|
||||
$entity->set('isFollowed', true);
|
||||
}
|
||||
}
|
||||
|
||||
private function afterSaveStreamNotNew2(CoreEntity $entity): void
|
||||
{
|
||||
// Not recommended to use.
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
namespace Espo\Tools\Stream;
|
||||
|
||||
use Espo\Core\Field\LinkMultiple;
|
||||
use Espo\Core\Field\LinkParent;
|
||||
use Espo\Core\ORM\Repository\Option\SaveOption;
|
||||
use Espo\Core\ORM\Type\FieldType;
|
||||
@@ -87,6 +88,8 @@ class Service
|
||||
'Closed Lost',
|
||||
];
|
||||
|
||||
private const FIELD_ASSIGNED_USERS = 'assignedUsers';
|
||||
|
||||
/**
|
||||
* @var array<
|
||||
* string,
|
||||
@@ -581,6 +584,20 @@ class Service
|
||||
|
||||
$data['assignedUserId'] = $entity->get('assignedUserId');
|
||||
$data['assignedUserName'] = $entity->get('assignedUserName');
|
||||
} else if (
|
||||
$entity instanceof CoreEntity &&
|
||||
$entity->hasLinkMultipleField(self::FIELD_ASSIGNED_USERS) &&
|
||||
$entity->getLinkMultipleIdList(self::FIELD_ASSIGNED_USERS) !== []
|
||||
) {
|
||||
/** @var LinkMultiple $users */
|
||||
$users = $entity->getValueObject(self::FIELD_ASSIGNED_USERS);
|
||||
|
||||
$data['assignedUsers'] = array_map(function ($it) {
|
||||
return [
|
||||
'id' => $it->getId(),
|
||||
'name' => $it->getName(),
|
||||
];
|
||||
}, $users->getList());
|
||||
}
|
||||
|
||||
$field = $this->getStatusField($entityType);
|
||||
@@ -754,19 +771,7 @@ class Service
|
||||
$note->setParent(LinkParent::createFromEntity($entity));
|
||||
|
||||
$this->setSuperParent($entity, $note, true);
|
||||
|
||||
if ($entity->get('assignedUserId')) {
|
||||
$this->loadAssignedUserName($entity);
|
||||
|
||||
$note->set('data', [
|
||||
'assignedUserId' => $entity->get('assignedUserId'),
|
||||
'assignedUserName' => $entity->get('assignedUserName'),
|
||||
]);
|
||||
} else {
|
||||
$note->set('data', [
|
||||
'assignedUserId' => null
|
||||
]);
|
||||
}
|
||||
$this->setAssignData($entity, $note);
|
||||
|
||||
$noteOptions = [];
|
||||
|
||||
@@ -1185,4 +1190,58 @@ class Service
|
||||
/** @var Note */
|
||||
return $this->entityManager->getNewEntity(Note::ENTITY_TYPE);
|
||||
}
|
||||
|
||||
private function setAssignData(Entity $entity, Note $note): void
|
||||
{
|
||||
if (
|
||||
$entity instanceof CoreEntity &&
|
||||
$entity->hasLinkMultipleField(self::FIELD_ASSIGNED_USERS)
|
||||
) {
|
||||
$data = [];
|
||||
|
||||
$newIds = $entity->getLinkMultipleIdList(self::FIELD_ASSIGNED_USERS);
|
||||
/** @var array<string, ?string> $newNames */
|
||||
$newNames = get_object_vars($entity->get(self::FIELD_ASSIGNED_USERS . 'Names') ?? (object) []);
|
||||
|
||||
/** @var string[] $prevIds */
|
||||
$prevIds = $entity->getFetched(self::FIELD_ASSIGNED_USERS . 'Ids') ?? [];
|
||||
/** @var array<string, ?string> $prevNames */
|
||||
$prevNames = get_object_vars($entity->getFetched(self::FIELD_ASSIGNED_USERS . 'Names') ?? (object) []);
|
||||
|
||||
$addedIds = array_values(array_diff($newIds, $prevIds));
|
||||
$removedIds = array_values(array_diff($prevIds, $newIds));
|
||||
$names = array_merge($prevNames, $newNames);
|
||||
|
||||
$data['addedAssignedUsers'] = array_map(function ($id) use ($names) {
|
||||
return [
|
||||
'id' => $id,
|
||||
'name' => $names[$id] ?? null,
|
||||
];
|
||||
}, $addedIds);
|
||||
|
||||
$data['removedAssignedUsers'] = array_map(function ($id) use ($names) {
|
||||
return [
|
||||
'id' => $id,
|
||||
'name' => $names[$id] ?? null,
|
||||
];
|
||||
}, $removedIds);
|
||||
|
||||
$note->setData($data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($entity->get('assignedUserId')) {
|
||||
$this->loadAssignedUserName($entity);
|
||||
|
||||
$note->setData([
|
||||
'assignedUserId' => $entity->get('assignedUserId'),
|
||||
'assignedUserName' => $entity->get('assignedUserName'),
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$note->setData(['assignedUserId' => null]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
|
||||
import NoteStreamView from 'views/stream/note';
|
||||
|
||||
/** @module views/stream/notes/assign */
|
||||
|
||||
class AssignNoteStreamView extends NoteStreamView {
|
||||
|
||||
template = 'stream/notes/assign'
|
||||
@@ -48,12 +50,34 @@ class AssignNoteStreamView extends NoteStreamView {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* assignedUserId?: string,
|
||||
* assignedUserName?: string,
|
||||
* addedAssignedUsers?: {id: string, name: string|null}[],
|
||||
* removedAssignedUsers?: {id: string, name: string|null}[],
|
||||
* }} module:views/stream/notes/assign~data
|
||||
*/
|
||||
|
||||
setup() {
|
||||
const data = this.model.get('data');
|
||||
this.setupData();
|
||||
|
||||
this.createMessage();
|
||||
}
|
||||
|
||||
setupData() {
|
||||
/** @type {module:views/stream/notes/assign~data} */
|
||||
const data = this.model.get('data') || {};
|
||||
|
||||
this.assignedUserId = data.assignedUserId || null;
|
||||
this.assignedUserName = data.assignedUserName || data.assignedUserId || null;
|
||||
|
||||
if (data.addedAssignedUsers) {
|
||||
this.setupDataMulti(data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.messageData['assignee'] =
|
||||
$('<span>')
|
||||
.addClass('nowrap name-avatar')
|
||||
@@ -78,17 +102,78 @@ class AssignNoteStreamView extends NoteStreamView {
|
||||
} else {
|
||||
this.messageName += 'Void';
|
||||
}
|
||||
} else {
|
||||
if (this.assignedUserId) {
|
||||
if (this.assignedUserId === this.model.get('createdById')) {
|
||||
this.messageName += 'Self';
|
||||
}
|
||||
} else {
|
||||
this.messageName += 'Void';
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.createMessage();
|
||||
if (this.assignedUserId) {
|
||||
if (this.assignedUserId === this.model.get('createdById')) {
|
||||
this.messageName += 'Self';
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.messageName += 'Void';
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {module:views/stream/notes/assign~data} data
|
||||
*/
|
||||
setupDataMulti(data) {
|
||||
this.messageName = 'assignMultiAdd';
|
||||
|
||||
const added = data.addedAssignedUsers;
|
||||
const removed = data.removedAssignedUsers;
|
||||
|
||||
if (!added || !removed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (added.length && removed.length) {
|
||||
this.messageName = 'assignMultiAddRemove';
|
||||
} else if (removed.length) {
|
||||
this.messageName = 'assignMultiRemove';
|
||||
}
|
||||
|
||||
if (added.length) {
|
||||
this.messageData['assignee'] = this.createUsersElement(added);
|
||||
}
|
||||
|
||||
if (removed.length) {
|
||||
this.messageData['removedAssignee'] = this.createUsersElement(removed);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {{id: string, name: ?string}[]} users
|
||||
* @return {HTMLElement}
|
||||
*/
|
||||
createUsersElement(users) {
|
||||
const wrapper = document.createElement('span');
|
||||
|
||||
users.forEach((it, i) => {
|
||||
const a = document.createElement('a');
|
||||
a.href = `#User/view/${it.id}`;
|
||||
a.text = it.name || it.id;
|
||||
a.dataset.id = it.id;
|
||||
a.dataset.scope = 'User';
|
||||
|
||||
const span = document.createElement('span');
|
||||
span.className = 'nowrap name-avatar';
|
||||
span.innerHTML = this.getHelper().getAvatarHtml(it.id, 'small', 16, 'avatar-link');
|
||||
span.appendChild(a);
|
||||
|
||||
wrapper.appendChild(span);
|
||||
|
||||
if (i < users.length - 1) {
|
||||
wrapper.appendChild(document.createTextNode(', '));
|
||||
}
|
||||
});
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
|
||||
import NoteStreamView from 'views/stream/note';
|
||||
|
||||
/** @module views/stream/notes/create */
|
||||
|
||||
class CreateNoteStreamView extends NoteStreamView {
|
||||
|
||||
template = 'stream/notes/create'
|
||||
@@ -52,17 +54,56 @@ class CreateNoteStreamView extends NoteStreamView {
|
||||
this.createMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* assignedUserId?: string,
|
||||
* assignedUserName?: string,
|
||||
* assignedUsers?: {id: string, name: string|null}[],
|
||||
* statusField?: string,
|
||||
* statusValue?: string|null,
|
||||
* statusStyle?: string|null,
|
||||
* }} module:views/stream/notes/create~data
|
||||
*/
|
||||
|
||||
setupData() {
|
||||
const data = /** @type {Object.<string, *>} */this.model.get('data');
|
||||
/** @type {module:views/stream/notes/create~data} */
|
||||
const data = this.model.get('data') || {};
|
||||
|
||||
this.setupUsersData();
|
||||
|
||||
if (data.statusField) {
|
||||
const statusField = this.statusField = data.statusField;
|
||||
const statusValue = data.statusValue;
|
||||
|
||||
this.statusStyle = data.statusStyle || 'default';
|
||||
this.statusText = this.getLanguage()
|
||||
.translateOption(statusValue, statusField, this.model.attributes.parentType);
|
||||
}
|
||||
}
|
||||
|
||||
setupUsersData() {
|
||||
/** @type {module:views/stream/notes/create~data} */
|
||||
const data = this.model.get('data') || {};
|
||||
|
||||
this.assignedUserId = data.assignedUserId || null;
|
||||
this.assignedUserName = data.assignedUserName || data.assignedUserId || null;
|
||||
|
||||
if (data.assignedUsers) {
|
||||
if (data.assignedUsers.length === 1) {
|
||||
this.assignedUserId = data.assignedUsers[0].id;
|
||||
this.assignedUserName = data.assignedUsers[0].name;
|
||||
} else if (data.assignedUsers.length > 1) {
|
||||
this.setupUsersDataMulti();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.messageData['assignee'] =
|
||||
$('<span>')
|
||||
.addClass('nowrap name-avatar')
|
||||
.append(
|
||||
this.getHelper().getAvatarHtml(data.assignedUserId, 'small', 16, 'avatar-link'),
|
||||
this.getHelper().getAvatarHtml(this.assignedUserId, 'small', 16, 'avatar-link'),
|
||||
$('<a>')
|
||||
.attr('href', `#User/view/${this.assignedUserId}`)
|
||||
.text(this.assignedUserName)
|
||||
@@ -84,27 +125,56 @@ class CreateNoteStreamView extends NoteStreamView {
|
||||
if (this.isThis) {
|
||||
this.messageName += 'This';
|
||||
|
||||
if (this.assignedUserId === this.model.get('createdById')) {
|
||||
if (this.assignedUserId === this.model.attributes.createdById) {
|
||||
this.messageName += 'Self';
|
||||
}
|
||||
} else {
|
||||
if (this.assignedUserId === this.model.get('createdById')) {
|
||||
if (this.assignedUserId === this.model.attributes.createdById) {
|
||||
this.messageName += 'Self';
|
||||
}
|
||||
else if (isYou) {
|
||||
} else if (isYou) {
|
||||
this.messageName += 'You';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (data.statusField) {
|
||||
const statusField = this.statusField = data.statusField;
|
||||
const statusValue = data.statusValue;
|
||||
setupUsersDataMulti() {
|
||||
this.messageName = 'createAssigned';
|
||||
|
||||
this.statusStyle = data.statusStyle || 'default';
|
||||
this.statusText = this.getLanguage()
|
||||
.translateOption(statusValue, statusField, this.model.get('parentType'));
|
||||
}
|
||||
/** @type {module:views/stream/notes/create~data} */
|
||||
const data = this.model.get('data') || {};
|
||||
|
||||
this.messageData['assignee'] = this.createUsersElement(data.assignedUsers);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {{id: string, name: ?string}[]} users
|
||||
* @return {HTMLElement}
|
||||
*/
|
||||
createUsersElement(users) {
|
||||
const wrapper = document.createElement('span');
|
||||
|
||||
users.forEach((it, i) => {
|
||||
const a = document.createElement('a');
|
||||
a.href = `#User/view/${it.id}`;
|
||||
a.text = it.name || it.id;
|
||||
a.dataset.id = it.id;
|
||||
a.dataset.scope = 'User';
|
||||
|
||||
const span = document.createElement('span');
|
||||
span.className = 'nowrap name-avatar';
|
||||
span.innerHTML = this.getHelper().getAvatarHtml(it.id, 'small', 16, 'avatar-link');
|
||||
span.appendChild(a);
|
||||
|
||||
wrapper.appendChild(span);
|
||||
|
||||
if (i < users.length - 1) {
|
||||
wrapper.appendChild(document.createTextNode(', '));
|
||||
}
|
||||
});
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user