diff --git a/docs/changes/1.x/1.3.0.md b/docs/changes/1.x/1.3.0.md
index a843c80bb1..a52e16e339 100644
--- a/docs/changes/1.x/1.3.0.md
+++ b/docs/changes/1.x/1.3.0.md
@@ -12,6 +12,7 @@
- RTF Writer : Support for Table Border Style fixing [#345](https://github.com/PHPOffice/PHPWord/issues/345) by [@Progi1984](https://github.com/Progi1984) in [#2656](https://github.com/PHPOffice/PHPWord/pull/2656)
- Word2007 Reader: Support the page break () by [@stanolacko](https://github.com/stanolacko) in [#2662](https://github.com/PHPOffice/PHPWord/pull/2662)
- MsDoc Reader: Support for UTF-8 characters by [@Progi1984] fixing [#881](https://github.com/PHPOffice/PHPWord/issues/881), [#1454](https://github.com/PHPOffice/PHPWord/issues/1454), [#1817](https://github.com/PHPOffice/PHPWord/issues/1817), [#1927](https://github.com/PHPOffice/PHPWord/issues/1927), [#2383](https://github.com/PHPOffice/PHPWord/issues/2383), [#2565](https://github.com/PHPOffice/PHPWord/issues/2565) in [#2664](https://github.com/PHPOffice/PHPWord/pull/2664)
+- Word2007 Writer: Added support for multiples comment for the same text by [@rodrigoq](https://github.com/rodrigoq) fixing [#2109](https://github.com/PHPOffice/PHPWord/issues/2109) in [#2665](https://github.com/PHPOffice/PHPWord/pull/2665)
### Bug fixes
diff --git a/samples/Sample_37_Comments.php b/samples/Sample_37_Comments.php
index 79647478e8..8254f35c6d 100644
--- a/samples/Sample_37_Comments.php
+++ b/samples/Sample_37_Comments.php
@@ -42,7 +42,7 @@
$imageComment->addText('Hey, Mars does look ');
$imageComment->addText('red', ['color' => 'FF0000']);
$phpWord->addComment($commentOnImage);
-$image = $section->addImage('resources/_mars.jpg');
+$image = $section->addImage(__DIR__ . '/resources/_mars.jpg');
$image->setCommentRangeStart($commentOnImage);
$section->addTextBreak(2);
@@ -56,6 +56,21 @@
$comment1->setEndElement($anotherText);
$phpWord->addComment($comment1);
+// We can also do things the other way round, link the comment to the element
+$lastText = $section->addText('with a last text and two comments');
+
+$comment1 = new \PhpOffice\PhpWord\Element\Comment('Authors name', new \DateTime(), 'my_initials');
+$comment1->addText('Comment 1', ['bold' => true]);
+$comment1->setStartElement($lastText);
+$comment1->setEndElement($lastText);
+$phpWord->addComment($comment1);
+
+$comment2 = new \PhpOffice\PhpWord\Element\Comment('Authors name', new \DateTime(), 'my_initials');
+$comment2->addText('Comment 2', ['bold' => true]);
+$comment2->setStartElement($lastText);
+$comment2->setEndElement($lastText);
+$phpWord->addComment($comment2);
+
// Save file
echo write($phpWord, basename(__FILE__, '.php'), $writers);
if (!CLI) {
diff --git a/src/PhpWord/Collection/AbstractCollection.php b/src/PhpWord/Collection/AbstractCollection.php
index 78b5b891b8..646489d886 100644
--- a/src/PhpWord/Collection/AbstractCollection.php
+++ b/src/PhpWord/Collection/AbstractCollection.php
@@ -21,22 +21,23 @@
* Collection abstract class.
*
* @since 0.10.0
+ * @template T
*/
abstract class AbstractCollection
{
/**
* Items.
*
- * @var \PhpOffice\PhpWord\Element\AbstractContainer[]
+ * @var T[]
*/
private $items = [];
/**
* Get items.
*
- * @return \PhpOffice\PhpWord\Element\AbstractContainer[]
+ * @return T[]
*/
- public function getItems()
+ public function getItems(): array
{
return $this->items;
}
@@ -44,11 +45,9 @@ public function getItems()
/**
* Get item by index.
*
- * @param int $index
- *
- * @return ?\PhpOffice\PhpWord\Element\AbstractContainer
+ * @return ?T
*/
- public function getItem($index)
+ public function getItem(int $index)
{
if (array_key_exists($index, $this->items)) {
return $this->items[$index];
@@ -60,10 +59,9 @@ public function getItem($index)
/**
* Set item.
*
- * @param int $index
- * @param ?\PhpOffice\PhpWord\Element\AbstractContainer $item
+ * @param ?T $item
*/
- public function setItem($index, $item): void
+ public function setItem(int $index, $item): void
{
if (array_key_exists($index, $this->items)) {
$this->items[$index] = $item;
@@ -73,11 +71,9 @@ public function setItem($index, $item): void
/**
* Add new item.
*
- * @param \PhpOffice\PhpWord\Element\AbstractContainer $item
- *
- * @return int
+ * @param T $item
*/
- public function addItem($item)
+ public function addItem($item): int
{
$index = $this->countItems();
$this->items[$index] = $item;
@@ -87,10 +83,8 @@ public function addItem($item)
/**
* Get item count.
- *
- * @return int
*/
- public function countItems()
+ public function countItems(): int
{
return count($this->items);
}
diff --git a/src/PhpWord/Collection/Bookmarks.php b/src/PhpWord/Collection/Bookmarks.php
index e7d9b4a384..71544c9469 100644
--- a/src/PhpWord/Collection/Bookmarks.php
+++ b/src/PhpWord/Collection/Bookmarks.php
@@ -17,10 +17,13 @@
namespace PhpOffice\PhpWord\Collection;
+use PhpOffice\PhpWord\Element\Bookmark;
+
/**
* Bookmarks collection.
*
* @since 0.12.0
+ * @extends AbstractCollection
*/
class Bookmarks extends AbstractCollection
{
diff --git a/src/PhpWord/Collection/Charts.php b/src/PhpWord/Collection/Charts.php
index bb63a13962..7c2dfbab94 100644
--- a/src/PhpWord/Collection/Charts.php
+++ b/src/PhpWord/Collection/Charts.php
@@ -17,10 +17,13 @@
namespace PhpOffice\PhpWord\Collection;
+use PhpOffice\PhpWord\Element\Chart;
+
/**
* Charts collection.
*
* @since 0.12.0
+ * @extends AbstractCollection
*/
class Charts extends AbstractCollection
{
diff --git a/src/PhpWord/Collection/Comments.php b/src/PhpWord/Collection/Comments.php
index 8c6b577d7e..5fa4020a5a 100644
--- a/src/PhpWord/Collection/Comments.php
+++ b/src/PhpWord/Collection/Comments.php
@@ -17,10 +17,13 @@
namespace PhpOffice\PhpWord\Collection;
+use PhpOffice\PhpWord\Element\Comment;
+
/**
* Comments collection.
*
* @since 0.12.0
+ * @extends AbstractCollection
*/
class Comments extends AbstractCollection
{
diff --git a/src/PhpWord/Collection/Endnotes.php b/src/PhpWord/Collection/Endnotes.php
index 362b25881d..09903b1bf6 100644
--- a/src/PhpWord/Collection/Endnotes.php
+++ b/src/PhpWord/Collection/Endnotes.php
@@ -17,10 +17,13 @@
namespace PhpOffice\PhpWord\Collection;
+use PhpOffice\PhpWord\Element\Endnote;
+
/**
* Endnotes collection.
*
* @since 0.10.0
+ * @extends AbstractCollection
*/
class Endnotes extends AbstractCollection
{
diff --git a/src/PhpWord/Collection/Footnotes.php b/src/PhpWord/Collection/Footnotes.php
index 76eae3eab3..0387fce3c7 100644
--- a/src/PhpWord/Collection/Footnotes.php
+++ b/src/PhpWord/Collection/Footnotes.php
@@ -17,10 +17,13 @@
namespace PhpOffice\PhpWord\Collection;
+use PhpOffice\PhpWord\Element\Footnote;
+
/**
* Footnotes collection.
*
* @since 0.10.0
+ * @extends AbstractCollection
*/
class Footnotes extends AbstractCollection
{
diff --git a/src/PhpWord/Collection/Titles.php b/src/PhpWord/Collection/Titles.php
index 7b795771e8..543aabda1d 100644
--- a/src/PhpWord/Collection/Titles.php
+++ b/src/PhpWord/Collection/Titles.php
@@ -17,10 +17,13 @@
namespace PhpOffice\PhpWord\Collection;
+use PhpOffice\PhpWord\Element\Title;
+
/**
* Titles collection.
*
* @since 0.10.0
+ * @extends AbstractCollection
*/
class Titles extends AbstractCollection
{
diff --git a/src/PhpWord/Element/AbstractElement.php b/src/PhpWord/Element/AbstractElement.php
index 9f9c2e82aa..385e4d3140 100644
--- a/src/PhpWord/Element/AbstractElement.php
+++ b/src/PhpWord/Element/AbstractElement.php
@@ -19,8 +19,10 @@
use DateTime;
use InvalidArgumentException;
+use PhpOffice\PhpWord\Collection\Comments;
use PhpOffice\PhpWord\Media;
use PhpOffice\PhpWord\PhpWord;
+use PhpOffice\PhpWord\Style;
/**
* Element abstract class.
@@ -32,7 +34,7 @@ abstract class AbstractElement
/**
* PhpWord object.
*
- * @var ?\PhpOffice\PhpWord\PhpWord
+ * @var ?PhpWord
*/
protected $phpWord;
@@ -131,25 +133,25 @@ abstract class AbstractElement
protected $collectionRelation = false;
/**
- * The start position for the linked comment.
+ * The start position for the linked comments.
*
- * @var Comment
+ * @var Comments
*/
- protected $commentRangeStart;
+ protected $commentsRangeStart;
/**
- * The end position for the linked comment.
+ * The end position for the linked comments.
*
- * @var Comment
+ * @var Comments
*/
- protected $commentRangeEnd;
+ protected $commentsRangeEnd;
/**
* Get PhpWord.
*
- * @return ?\PhpOffice\PhpWord\PhpWord
+ * @return ?PhpWord
*/
- public function getPhpWord()
+ public function getPhpWord(): ?PhpWord
{
return $this->phpWord;
}
@@ -287,14 +289,28 @@ public function getNestedLevel()
return $this->nestedLevel;
}
+ /**
+ * Get comments start.
+ *
+ * @return Comments
+ */
+ public function getCommentsRangeStart(): ?Comments
+ {
+ return $this->commentsRangeStart;
+ }
+
/**
* Get comment start.
*
* @return Comment
*/
- public function getCommentRangeStart()
+ public function getCommentRangeStart(): ?Comment
{
- return $this->commentRangeStart;
+ if ($this->commentsRangeStart != null) {
+ return $this->commentsRangeStart->getItem($this->commentsRangeStart->countItems());
+ }
+
+ return null;
}
/**
@@ -305,8 +321,30 @@ public function setCommentRangeStart(Comment $value): void
if ($this instanceof Comment) {
throw new InvalidArgumentException('Cannot set a Comment on a Comment');
}
- $this->commentRangeStart = $value;
- $this->commentRangeStart->setStartElement($this);
+ if ($this->commentsRangeStart == null) {
+ $this->commentsRangeStart = new Comments();
+ }
+ // Set ID early to avoid duplicates.
+ if ($value->getElementId() == null) {
+ $value->setElementId();
+ }
+ foreach ($this->commentsRangeStart->getItems() as $comment) {
+ if ($value->getElementId() == $comment->getElementId()) {
+ return;
+ }
+ }
+ $idxItem = $this->commentsRangeStart->addItem($value);
+ $this->commentsRangeStart->getItem($idxItem)->setStartElement($this);
+ }
+
+ /**
+ * Get comments end.
+ *
+ * @return Comments
+ */
+ public function getCommentsRangeEnd(): ?Comments
+ {
+ return $this->commentsRangeEnd;
}
/**
@@ -314,9 +352,13 @@ public function setCommentRangeStart(Comment $value): void
*
* @return Comment
*/
- public function getCommentRangeEnd()
+ public function getCommentRangeEnd(): ?Comment
{
- return $this->commentRangeEnd;
+ if ($this->commentsRangeEnd != null) {
+ return $this->commentsRangeEnd->getItem($this->commentsRangeEnd->countItems());
+ }
+
+ return null;
}
/**
@@ -327,8 +369,20 @@ public function setCommentRangeEnd(Comment $value): void
if ($this instanceof Comment) {
throw new InvalidArgumentException('Cannot set a Comment on a Comment');
}
- $this->commentRangeEnd = $value;
- $this->commentRangeEnd->setEndElement($this);
+ if ($this->commentsRangeEnd == null) {
+ $this->commentsRangeEnd = new Comments();
+ }
+ // Set ID early to avoid duplicates.
+ if ($value->getElementId() == null) {
+ $value->setElementId();
+ }
+ foreach ($this->commentsRangeEnd->getItems() as $comment) {
+ if ($value->getElementId() == $comment->getElementId()) {
+ return;
+ }
+ }
+ $idxItem = $this->commentsRangeEnd->addItem($value);
+ $this->commentsRangeEnd->getItem($idxItem)->setEndElement($this);
}
/**
@@ -428,7 +482,7 @@ public function isInSection()
* Set new style value.
*
* @param mixed $styleObject Style object
- * @param null|array|\PhpOffice\PhpWord\Style|string $styleValue Style value
+ * @param null|array|string|Style $styleValue Style value
* @param bool $returnObject Always return object
*
* @return mixed
diff --git a/src/PhpWord/Element/Comment.php b/src/PhpWord/Element/Comment.php
index 7e7c5241fa..9173c49148 100644
--- a/src/PhpWord/Element/Comment.php
+++ b/src/PhpWord/Element/Comment.php
@@ -83,9 +83,7 @@ public function getInitials()
public function setStartElement(AbstractElement $value): void
{
$this->startElement = $value;
- if ($value->getCommentRangeStart() == null) {
- $value->setCommentRangeStart($this);
- }
+ $value->setCommentRangeStart($this);
}
/**
@@ -104,9 +102,7 @@ public function getStartElement()
public function setEndElement(AbstractElement $value): void
{
$this->endElement = $value;
- if ($value->getCommentRangeEnd() == null) {
- $value->setCommentRangeEnd($this);
- }
+ $value->setCommentRangeEnd($this);
}
/**
diff --git a/src/PhpWord/PhpWord.php b/src/PhpWord/PhpWord.php
index a7aa95ce45..cf6f16ae02 100644
--- a/src/PhpWord/PhpWord.php
+++ b/src/PhpWord/PhpWord.php
@@ -134,7 +134,6 @@ public function __call($function, $args)
if (in_array($function, $addCollection)) {
$key = ucfirst(str_replace('add', '', $function) . 's');
- /** @var \PhpOffice\PhpWord\Collection\AbstractCollection $collectionObject */
$collectionObject = $this->collections[$key];
return $collectionObject->addItem($args[0] ?? null);
diff --git a/src/PhpWord/Writer/Word2007.php b/src/PhpWord/Writer/Word2007.php
index ab4fd1e3eb..e7801c04c2 100644
--- a/src/PhpWord/Writer/Word2007.php
+++ b/src/PhpWord/Writer/Word2007.php
@@ -227,7 +227,6 @@ private function addNotes(ZipArchive $zip, &$rId, $noteType = 'footnote'): void
$collection = $phpWord->$method();
// Add footnotes media files, relations, and contents
- /** @var \PhpOffice\PhpWord\Collection\AbstractCollection $collection Type hint */
if ($collection->countItems() > 0) {
$media = Media::getElements($noteType);
$this->addFilesToPackage($zip, $media);
@@ -260,7 +259,6 @@ private function addComments(ZipArchive $zip, &$rId): void
$partName = 'comments';
// Add comment relations and contents
- /** @var \PhpOffice\PhpWord\Collection\AbstractCollection $collection Type hint */
if ($collection->countItems() > 0) {
$this->relationships[] = ['target' => "{$partName}.xml", 'type' => $partName, 'rID' => ++$rId];
diff --git a/src/PhpWord/Writer/Word2007/Element/AbstractElement.php b/src/PhpWord/Writer/Word2007/Element/AbstractElement.php
index abd1324aad..b677556d62 100644
--- a/src/PhpWord/Writer/Word2007/Element/AbstractElement.php
+++ b/src/PhpWord/Writer/Word2007/Element/AbstractElement.php
@@ -126,14 +126,10 @@ protected function endElementP(): void
*/
protected function writeCommentRangeStart(): void
{
- if ($this->element->getCommentRangeStart() != null) {
- $comment = $this->element->getCommentRangeStart();
- //only set the ID if it is not yet set, otherwise it will overwrite it
- if ($comment->getElementId() == null) {
- $comment->setElementId();
+ if ($this->element->getCommentsRangeStart() != null) {
+ foreach ($this->element->getCommentsRangeStart()->getItems() as $comment) {
+ $this->xmlWriter->writeElementBlock('w:commentRangeStart', ['w:id' => $comment->getElementId()]);
}
-
- $this->xmlWriter->writeElementBlock('w:commentRangeStart', ['w:id' => $comment->getElementId()]);
}
}
@@ -142,28 +138,23 @@ protected function writeCommentRangeStart(): void
*/
protected function writeCommentRangeEnd(): void
{
- if ($this->element->getCommentRangeEnd() != null) {
- $comment = $this->element->getCommentRangeEnd();
- //only set the ID if it is not yet set, otherwise it will overwrite it, this should normally not happen
- if ($comment->getElementId() == null) {
- $comment->setElementId(); // @codeCoverageIgnore
- } // @codeCoverageIgnore
-
- $this->xmlWriter->writeElementBlock('w:commentRangeEnd', ['w:id' => $comment->getElementId()]);
- $this->xmlWriter->startElement('w:r');
- $this->xmlWriter->writeElementBlock('w:commentReference', ['w:id' => $comment->getElementId()]);
- $this->xmlWriter->endElement();
- } elseif ($this->element->getCommentRangeStart() != null && $this->element->getCommentRangeStart()->getEndElement() == null) {
- $comment = $this->element->getCommentRangeStart();
- //only set the ID if it is not yet set, otherwise it will overwrite it, this should normally not happen
- if ($comment->getElementId() == null) {
- $comment->setElementId(); // @codeCoverageIgnore
- } // @codeCoverageIgnore
-
- $this->xmlWriter->writeElementBlock('w:commentRangeEnd', ['w:id' => $comment->getElementId()]);
- $this->xmlWriter->startElement('w:r');
- $this->xmlWriter->writeElementBlock('w:commentReference', ['w:id' => $comment->getElementId()]);
- $this->xmlWriter->endElement();
+ if ($this->element->getCommentsRangeEnd() != null) {
+ foreach ($this->element->getCommentsRangeEnd()->getItems() as $comment) {
+ $this->xmlWriter->writeElementBlock('w:commentRangeEnd', ['w:id' => $comment->getElementId()]);
+ $this->xmlWriter->startElement('w:r');
+ $this->xmlWriter->writeElementBlock('w:commentReference', ['w:id' => $comment->getElementId()]);
+ $this->xmlWriter->endElement();
+ }
+ }
+ if ($this->element->getCommentsRangeStart() != null) {
+ foreach ($this->element->getCommentsRangeStart()->getItems() as $comment) {
+ if ($comment->getEndElement() == null) {
+ $this->xmlWriter->writeElementBlock('w:commentRangeEnd', ['w:id' => $comment->getElementId()]);
+ $this->xmlWriter->startElement('w:r');
+ $this->xmlWriter->writeElementBlock('w:commentReference', ['w:id' => $comment->getElementId()]);
+ $this->xmlWriter->endElement();
+ }
+ }
}
}
diff --git a/tests/PhpWordTests/Element/CommentTest.php b/tests/PhpWordTests/Element/CommentTest.php
index fe346307c4..b4d9cc6a5d 100644
--- a/tests/PhpWordTests/Element/CommentTest.php
+++ b/tests/PhpWordTests/Element/CommentTest.php
@@ -20,6 +20,7 @@
use DateTime;
use InvalidArgumentException;
use PhpOffice\PhpWord\Element\Comment;
+use PhpOffice\PhpWord\Element\Section;
use PhpOffice\PhpWord\Element\Text;
/**
@@ -51,6 +52,39 @@ public function testConstructDefault(): void
self::assertEquals($oText, $oComment->getEndElement());
}
+ /**
+ * Two comments on same text.
+ */
+ public function testTwoCommentsOnSameText(): void
+ {
+ $section = new Section(0);
+ $text = $section->addText('Text');
+
+ $comment1 = new Comment('Author1', new DateTime(), 'A1');
+ $comment1->addText('Comment1');
+
+ $comment2 = new Comment('Author2', new DateTime(), 'A2');
+ $comment2->addText('Comment2');
+
+ $comment1->setStartElement($text);
+ $comment2->setStartElement($text);
+
+ $text->setCommentRangeStart($comment1);
+ $text->setCommentRangeEnd($comment1);
+
+ $text->setCommentRangeStart($comment2);
+ $text->setCommentRangeEnd($comment2);
+
+ self::assertEquals(2, $text->getCommentsRangeStart()->countItems());
+ self::assertEquals(2, $text->getCommentsRangeEnd()->countItems());
+
+ self::assertEquals($text->getCommentsRangeStart()->getItem(0)->getElementId(), $comment1->getElementId());
+ self::assertEquals($text->getCommentsRangeEnd()->getItem(0)->getElementId(), $comment1->getElementId());
+
+ self::assertEquals($text->getCommentsRangeStart()->getItem(1)->getElementId(), $comment2->getElementId());
+ self::assertEquals($text->getCommentsRangeEnd()->getItem(1)->getElementId(), $comment2->getElementId());
+ }
+
/**
* Add text.
*/