Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
192 changes: 98 additions & 94 deletions Classes/Aspects/WorkspaceAspect.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<?php

namespace Networkteam\ContentComments\Aspects;

use Neos\Flow\Annotations as Flow;
Expand All @@ -13,98 +14,101 @@
* @Flow\Scope("singleton")
* @Flow\Aspect
*/
class WorkspaceAspect {

/**
* @Flow\Around("method(Neos\ContentRepository\Domain\Model\Workspace->replaceNodeData())")
* @param \Neos\Flow\Aop\JoinPointInterface $joinPoint The current join point
* @return string The result of the target method if it has not been intercepted
*/
public function replaceNodeData(JoinPointInterface $joinPoint) {
/** @var Node $node */
$node = $joinPoint->getMethodArgument('sourceNode');
if ($node->isRemoved()) {
// If the node is supposed to be removed, we do not need to do anything as the node will be gone anyways afterwards
return $joinPoint->getAdviceChain()->proceed($joinPoint);
}

/** @var NodeData $targetNodeData */
$targetNodeData = $joinPoint->getMethodArgument('targetNodeData');

$commentsForToBePublishedNode = $this->extractComments($node);
$commentsInTargetWorkspace = $this->extractComments($targetNodeData);

// Call original Method
$result = $joinPoint->getAdviceChain()->proceed($joinPoint);

if (count($commentsForToBePublishedNode) == 0 && count($commentsInTargetWorkspace) == 0) {
return $result;
}

// After publishing the node, we update the published node with the merged comments. We cannot do this
// before publishing, as otherwise the NodeData which is underneath the to-be-published Node will be "dirty"
// and marked as "removed" at the same time, leading to a CR crash. This also is a CR bug which only occurs in
// very rare occasions.
$mergedComments = $this->mergeComments($commentsInTargetWorkspace, $commentsForToBePublishedNode);
$this->writeComments($node, $mergedComments);

return $result;
}

/**
* Extract comments and deserialize them
*
* @param NodeInterface|NodeData $nodeOrNodeData
* @return array
*/
protected function extractComments($nodeOrNodeData) {
if ($nodeOrNodeData->hasProperty('comments')) {
$comments = $nodeOrNodeData->getProperty('comments');
if (is_string($comments) && strlen($comments) > 0) {
return json_decode($comments, TRUE);
}
}
return array();
}

/**
* Merge the $second comments array onto the $first comments array; the $second one wins. Returns the merged result.
*
* @param array $first
* @param array $second
* @return array
*/
protected function mergeComments($first, $second) {
$result = [];
foreach ($first as $value) {
$result[$value['date'] . $value['user'] ?? ''] = $value;
}

foreach ($second as $value) {
$result[$value['date'] . $value['user'] ?? ''] = $value;
}

foreach ($result as $key => $value) {
if (key_exists('deleted', $value)) {
unset($result[$key]);
}
}

ksort($result);
return array_values($result);
}

/**
* Write back the merged comments onto the node
*
* @param NodeInterface $node
* @param array $mergedComments
*/
protected function writeComments(NodeInterface $node, $mergedComments) {
// We directly write to the NodeData instead of Node here; as the Node is not fully correct after publishing - the.
// node's context is still the same as before publishing.
// (This is a bug in TYPO3CR which only manifests when trying to read the node after publishing in the same request)
// If we would write to $node directly then we would create a copy in the user's workspace; which is not what we want effectively :)
$node->getNodeData()->setProperty('comments', json_encode($mergedComments));
}
class WorkspaceAspect
{

/**
* @Flow\Around("method(Neos\ContentRepository\Domain\Model\Workspace->replaceNodeData())")
* @param \Neos\Flow\Aop\JoinPointInterface $joinPoint The current join point
* @return string The result of the target method if it has not been intercepted
*/
public function replaceNodeData(JoinPointInterface $joinPoint)
{
/** @var \Neos\ContentRepository\Core\Projection\ContentGraph\Node $node */
$node = $joinPoint->getMethodArgument('sourceNode');

/** @var NodeData $targetNodeData */
$targetNodeData = $joinPoint->getMethodArgument('targetNodeData');

$commentsForToBePublishedNode = $this->extractComments($node);
$commentsInTargetWorkspace = $this->extractComments($targetNodeData);

// Call original Method
$result = $joinPoint->getAdviceChain()->proceed($joinPoint);

if (count($commentsForToBePublishedNode) == 0 && count($commentsInTargetWorkspace) == 0) {
return $result;
}

// After publishing the node, we update the published node with the merged comments. We cannot do this
// before publishing, as otherwise the NodeData which is underneath the to-be-published Node will be "dirty"
// and marked as "removed" at the same time, leading to a CR crash. This also is a CR bug which only occurs in
// very rare occasions.
$mergedComments = $this->mergeComments($commentsInTargetWorkspace, $commentsForToBePublishedNode);
$this->writeComments($node, $mergedComments);

return $result;
}

/**
* Extract comments and deserialize them
*
* @param \Neos\ContentRepository\Core\Projection\ContentGraph\Node|NodeData $nodeOrNodeData
* @return array
*/
protected function extractComments($nodeOrNodeData)
{
if ($nodeOrNodeData->hasProperty('comments')) {
$comments = $nodeOrNodeData->getProperty('comments');
if (is_string($comments) && strlen($comments) > 0) {
return json_decode($comments, TRUE);
}
}
return array();
}

/**
* Merge the $second comments array onto the $first comments array; the $second one wins. Returns the merged result.
*
* @param array $first
* @param array $second
* @return array
*/
protected function mergeComments($first, $second)
{
$result = [];
foreach ($first as $value) {
$result[$value['date'] . $value['user'] ?? ''] = $value;
}

foreach ($second as $value) {
$result[$value['date'] . $value['user'] ?? ''] = $value;
}

foreach ($result as $key => $value) {
if (key_exists('deleted', $value)) {
unset($result[$key]);
}
}

ksort($result);
return array_values($result);
}

/**
* Write back the merged comments onto the node
*
* @param \Neos\ContentRepository\Core\Projection\ContentGraph\Node $node
* @param array $mergedComments
*/
protected function writeComments(\Neos\ContentRepository\Core\Projection\ContentGraph\Node $node, $mergedComments)
{
// TODO 9.0 migration: !! Node::getNodeData() - the new CR is not based around the concept of NodeData anymore. You need to rewrite your code here.

// We directly write to the NodeData instead of Node here; as the Node is not fully correct after publishing - the.
// node's context is still the same as before publishing.
// (This is a bug in TYPO3CR which only manifests when trying to read the node after publishing in the same request)
// If we would write to $node directly then we would create a copy in the user's workspace; which is not what we want effectively :)
$node->getNodeData()->setProperty('comments', json_encode($mergedComments));
}
}
16 changes: 8 additions & 8 deletions Classes/CommentingCurrentUserDataSource.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ class CommentingCurrentUserDataSource extends AbstractDataSource {
protected $userService;

/**
* Get data
*
* @param NodeInterface $node The node that is currently edited (optional)
* @param array $arguments Additional arguments (key / value)
* @return mixed JSON serializable data
* @api
*/
public function getData(NodeInterface $node = NULL, array $arguments = []) {
* Get data
*
* @param \Neos\ContentRepository\Core\Projection\ContentGraph\Node $node The node that is currently edited (optional)
* @param array $arguments Additional arguments (key / value)
* @return mixed JSON serializable data
* @api
*/
public function getData(\Neos\ContentRepository\Core\Projection\ContentGraph\Node $node = NULL, array $arguments = []) {
return array('name' => $this->userService->getBackendUser()->getName()->getFullName());
}
}
12 changes: 6 additions & 6 deletions Configuration/NodeTypes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@
inspector:
tabs:
comments:
label: 'Comments'
label: Comments
position: 50
icon: 'icon-comments'
icon: icon-comments
groups:
comments:
label: 'Comments'
tab: 'comments'
label: Comments
tab: comments
position: 1000
properties:
comments:
type: 'string'
type: string
ui:
inspector:
group: 'comments'
group: comments
editor: 'Neos.Neos.UI.NetworkteamContentComments:CommentEditor'
5 changes: 2 additions & 3 deletions Configuration/Settings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ Neos:
resources:
javascript:
'Neos.Neos.UI.NetworkteamContentComments:CommentEditor':
resource: resource://Networkteam.ContentComments/Public/JavaScript/Plugin.js
resource: 'resource://Networkteam.ContentComments/Public/JavaScript/Plugin.js'
fusion:
autoInclude:
Networkteam.ContentComments: true

Networkteam:
ContentComments:
ShowInFrontend: false
ShowInFrontend: false
18 changes: 10 additions & 8 deletions Resources/Private/Fusion/CommentRenderer.fusion
Original file line number Diff line number Diff line change
@@ -1,45 +1,47 @@
// TODO 9.0 migration: Line 74: You very likely need to rewrite "VARIABLE.label" to "Neos.Node.label(VARIABLE)". We did not auto-apply this migration because we cannot be sure whether the variable is a Node.
// I think this is not relevant, so we can keep everything like it is. JP.
# Add prototype to render comment in frontend mit bootstrap 3 based styles
prototype(Networkteam.ContentComments:CommentRenderer) < prototype(Neos.Fusion:Value) {
value = ${value}

input = Neos.Fusion:Tag {
@if.isLiveWorkspace = ${node.context.workspace.name == 'live'}
@if.isLiveWorkspace = ${node.workspaceName == 'live'}
tagName = 'input'
attributes {
type='checkbox'
id = ${'c' + node.identifier}
id = ${'c' + node.aggregateId}
class="content-comments__state"
}
}
label = Neos.Fusion:Tag {
tagName = 'label'
attributes {
class = 'content-comments__trigger'
for = ${'c' + node.identifier}
for = ${'c' + node.aggregateId}
}
content = 'i'
}
comments = Neos.Fusion:Tag {
@if.isLiveWorkspace = ${node.context.workspace.name == 'live'}
@if.isLiveWorkspace = ${node.workspaceName == 'live'}
tagName = 'div'
attributes {
class = 'content-comments__comments-container'
}
content = Neos.Fusion:Collection {
collection = ${Json.parse(q(node).property('comments'))}
content = Neos.Fusion:Loop {
items = ${Json.parse(q(node).property('comments'))}
itemRenderer = Neos.Fusion:Tag {
tagName = 'article'
attributes {
class="content-comments__comment-item"
style = ${'border-left: 3px solid ' + item.userColor}
}
content = Neos.Fusion:Array {
content = Neos.Fusion:Join {
commentHeader = Neos.Fusion:Tag {
tagName = 'header'
attributes {
class = "content-comments__comment-item-header"
}
content = Neos.Fusion:Array {
content = Neos.Fusion:Join {
date = Neos.Fusion:Tag {
tagName = 'span'
attributes {
Expand Down
5 changes: 3 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
"source": "https://github.com/networkteam/ContentComments"
},
"require": {
"neos/neos": "~4.0 || ~5.0 || ~7.0 || ~8.0",
"neos/neos": "~9.0",
"neos/contentgraph-doctrinedbaladapter": "*",
"neos/neos-ui": "*"
},
"autoload": {
Expand Down Expand Up @@ -128,4 +129,4 @@
"Neos.Flow-20201207104500"
]
}
}
}