From 577e5d0fb34734fa41eaac22ba6ca0407db16f35 Mon Sep 17 00:00:00 2001 From: Andrei Khalipau Date: Wed, 1 Feb 2023 15:29:09 -0500 Subject: [PATCH 1/4] #145 Cite-proc ignores et-al attributes if they set on bibliography/citation level. --- src/Style/InheritableNameAttributesTrait.php | 114 ++++++++++++------- 1 file changed, 71 insertions(+), 43 deletions(-) diff --git a/src/Style/InheritableNameAttributesTrait.php b/src/Style/InheritableNameAttributesTrait.php index d581d1a..4695030 100644 --- a/src/Style/InheritableNameAttributesTrait.php +++ b/src/Style/InheritableNameAttributesTrait.php @@ -232,18 +232,15 @@ public function initInheritableNameAttributes(SimpleXMLElement $node) { $context = CiteProc::getContext(); $parentStyleElement = null; + $root = $context->getRoot(); if ($this instanceof Name || $this instanceof Names) { if ($context->getMode() === "bibliography") { - if ($this->isDescendantOfMacro()) { - $parentStyleElement = $context->getRoot(); - } else { - $parentStyleElement = $context->getBibliography(); - } + $parentStyleElement = $context->getBibliography(); } else { $parentStyleElement = $context->getCitation(); } } elseif ($this instanceof StyleElement) { - $parentStyleElement = $context->getRoot(); + $parentStyleElement = $root; } foreach (self::$attributes as $nameAttribute) { @@ -251,86 +248,111 @@ public function initInheritableNameAttributes(SimpleXMLElement $node) switch ($nameAttribute) { case 'and': if (!empty($attribute)) { - $this->and = (string) $attribute; - } elseif (!empty($parentStyleElement)) { //inherit from parent style - $this->and = $parentStyleElement->getAnd(); + $this->setAnd((string) $attribute); + } elseif (!empty($parentStyleElement) && !empty($parentStyleElement->getAnd())) { + $this->setAnd($parentStyleElement->getAnd()); + } elseif (!empty($root) && !empty($root->getAnd())) { + $this->setAnd($root->getAnd()); } break; case 'delimiter-precedes-et-al': if (!empty($attribute)) { - $this->delimiterPrecedesEtAl = (string) $attribute; - } elseif (!empty($parentStyleElement)) { //inherit from parent style - $this->delimiterPrecedesEtAl = $parentStyleElement->getDelimiterPrecedesEtAl(); + $this->setDelimiterPrecedesEtAl((string) $attribute); + } elseif (!empty($parentStyleElement) && !empty($parentStyleElement->getDelimiterPrecedesEtAl())) { + $this->setDelimiterPrecedesEtAl($parentStyleElement->getDelimiterPrecedesEtAl()); + } elseif (!empty($root)) { + $this->setDelimiterPrecedesEtAl($root->getDelimiterPrecedesEtAl()); } break; case 'delimiter-precedes-last': if (!empty($attribute)) { - $this->delimiterPrecedesLast = (string) $attribute; - } elseif (!empty($parentStyleElement)) { //inherit from parent style - $this->delimiterPrecedesLast = $parentStyleElement->getDelimiterPrecedesLast(); + $this->setDelimiterPrecedesLast((string) $attribute); + } elseif (!empty($parentStyleElement) && !empty($parentStyleElement->getDelimiterPrecedesLast())) { + $this->setDelimiterPrecedesLast($parentStyleElement->getDelimiterPrecedesLast()); + } elseif (!empty($root)) { + $this->setDelimiterPrecedesLast($root->getDelimiterPrecedesLast()); } break; case 'et-al-min': if (!empty($attribute)) { - $this->etAlMin = intval((string) $attribute); - } elseif (!empty($parentStyleElement)) { - $this->etAlMin = $parentStyleElement->getEtAlMin(); + $this->setEtAlMin(intval((string) $attribute)); + } elseif (!empty($parentStyleElement) && !empty($parentStyleElement->getEtAlMin())) { + $this->setEtAlMin($parentStyleElement->getEtAlMin()); + } elseif (!empty($root)) { + $this->setEtAlMin($root->getEtAlMin()); } break; case 'et-al-use-first': if (!empty($attribute)) { - $this->etAlUseFirst = intval((string) $attribute); - } elseif (!empty($parentStyleElement)) { - $this->etAlUseFirst = $parentStyleElement->getEtAlUseFirst(); + $this->setEtAlUseFirst(intval((string) $attribute)); + } elseif (!empty($parentStyleElement) && !empty($parentStyleElement->getEtAlUseFirst())) { + $this->setEtAlUseFirst($parentStyleElement->getEtAlUseFirst()); + } elseif (!empty($root)) { + $this->setEtAlUseFirst($root->getEtAlUseFirst()); } break; case 'et-al-subsequent-min': if (!empty($attribute)) { - $this->etAlSubsequentMin = intval((string) $attribute); - } elseif (!empty($parentStyleElement)) { - $this->etAlSubsequentMin = $parentStyleElement->getEtAlSubsequentMin(); + $this->setEtAlSubsequentMin(intval((string) $attribute)); + } elseif (!empty($parentStyleElement) && !empty($parentStyleElement->getEtAlSubsequentMin())) { + $this->setEtAlSubsequentMin($parentStyleElement->getEtAlSubsequentMin()); + } elseif (!empty($root)) { + $this->setEtAlSubsequentMin($root->getEtAlSubsequentMin()); } break; case 'et-al-subsequent-use-first': if (!empty($attribute)) { $this->etAlSubsequentUseFirst = intval((string) $attribute); - } elseif (!empty($parentStyleElement)) { - $this->etAlSubsequentUseFirst = $parentStyleElement->getEtAlSubsequentUseFirst(); + $this->setEtAlSubsequentUseFirst(intval((string) $attribute)); + } elseif (!empty($parentStyleElement) && !empty($parentStyleElement->getEtAlSubsequentUseFirst())) { + $this->setEtAlSubsequentUseFirst($parentStyleElement->getEtAlSubsequentUseFirst()); + } elseif (!empty($root)) { + $this->setEtAlSubsequentUseFirst($root->getEtAlSubsequentUseFirst()); } break; case 'et-al-use-last': if (!empty($attribute)) { - $this->etAlUseLast = ((string) $attribute) === "true"; - } elseif (!empty($parentStyleElement)) { - $this->etAlUseLast = $parentStyleElement->getEtAlUseLast(); + $this->setEtAlUseLast(((string) $attribute) === "true"); + } elseif (!empty($parentStyleElement) && !empty($parentStyleElement->getEtAlUseLast())) { + $this->setEtAlUseLast($parentStyleElement->getEtAlUseLast()); + } elseif (!empty($root)) { + $this->setEtAlUseLast($root->getEtAlUseLast()); } break; case 'initialize': if (!empty($attribute)) { - $this->initialize = ((string) $attribute) === "true"; - } elseif (!empty($parentStyleElement)) { - $this->initialize = $parentStyleElement->getInitialize(); + $this->setInitialize(((string) $attribute) === "true"); + } elseif (!empty($parentStyleElement) && !empty($parentStyleElement->getInitialize())) { + $this->setInitialize($parentStyleElement->getInitialize()); + } elseif (!empty($root)) { + $this->setInitialize($root->getInitialize()); } break; case 'initialize-with': if (!empty($attribute)) { - $this->initializeWith = (string) $attribute; - } elseif (!empty($parentStyleElement)) { - $this->initializeWith = $parentStyleElement->getInitializeWith(); + $this->setInitializeWith((string) $attribute); + } elseif (!empty($parentStyleElement) && !empty($parentStyleElement->getInitializeWith())) { + $this->setInitializeWith($parentStyleElement->getInitializeWith()); + } elseif (!empty($root)) { + $this->setInitializeWith($root->getInitializeWith()); } break; case 'name-as-sort-order': if (!empty($attribute)) { - $this->nameAsSortOrder = (string) $attribute; - } elseif (!empty($parentStyleElement)) { - $this->nameAsSortOrder = $parentStyleElement->getNameAsSortOrder(); + $this->setNameAsSortOrder((string) $attribute); + } elseif (!empty($parentStyleElement) && !empty($parentStyleElement->getNameAsSortOrder())) { + $this->setNameAsSortOrder($parentStyleElement->getNameAsSortOrder()); + } elseif (!empty($root)) { + $this->setNameAsSortOrder($root->getNameAsSortOrder()); } break; case 'sort-separator': if (!empty($attribute)) { - $this->sortSeparator = (string) $attribute; - } elseif (!empty($parentStyleElement)) { - $this->sortSeparator = $parentStyleElement->getSortSeparator(); + $this->setSortSeparator((string) $attribute); + } elseif (!empty($parentStyleElement) && !empty($parentStyleElement->getSortSeparator())) { + $this->setSortSeparator($parentStyleElement->getSortSeparator()); + } elseif (!empty($root)) { + $this->setSortSeparator($root->getSortSeparator()); } break; case 'name-form': @@ -346,8 +368,10 @@ public function initInheritableNameAttributes(SimpleXMLElement $node) if ($this instanceof Name) { if (!empty($attribute)) { $this->setForm((string) $attribute); - } elseif (!empty($parentStyleElement)) { + } elseif (!empty($parentStyleElement) && !empty($parentStyleElement->getNameForm())) { $this->setForm($parentStyleElement->getNameForm()); + } elseif (!empty($root)) { + $this->setForm($root->getNameForm()); } } break; @@ -363,8 +387,10 @@ public function initInheritableNameAttributes(SimpleXMLElement $node) on cs:name. Similarly, names-delimiter corresponds to the delimiter attribute on cs:names. */ if (!empty($attribute)) { $this->nameDelimiter = $this->delimiter = (string) $attribute; - } elseif (!empty($parentStyleElement)) { + } elseif (!empty($parentStyleElement) && !empty($parentStyleElement->getNameDelimiter())) { $this->nameDelimiter = $this->delimiter = $parentStyleElement->getNameDelimiter(); + } elseif (!empty($root)) { + $this->nameDelimiter = $this->delimiter = $root->getNameDelimiter(); } } break; @@ -374,6 +400,8 @@ public function initInheritableNameAttributes(SimpleXMLElement $node) $this->setDelimiter((string) $attribute); } elseif (!empty($parentStyleElement)) { $this->setDelimiter($parentStyleElement->getNameDelimiter()); + } elseif (!empty($root)) { + $this->setDelimiter($root->getNameDelimiter()); } } } From 103b8cda49ee291d533c8609f2c7d59064f7ed43 Mon Sep 17 00:00:00 2001 From: Andrei Khalipau Date: Tue, 7 Feb 2023 16:26:23 -0500 Subject: [PATCH 2/4] #145: Fixed an edge case for et-al-use-last divider. --- src/Rendering/Name/Name.php | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/Rendering/Name/Name.php b/src/Rendering/Name/Name.php index da20c1f..a62ebb8 100644 --- a/src/Rendering/Name/Name.php +++ b/src/Rendering/Name/Name.php @@ -147,7 +147,7 @@ public function render($data, $var, $citationNumber = null) possible when the original name list has at least two more names than the truncated name list (for this the value of et-al-use-first/et-al-subsequent-min must be at least 2 less than the value of et-al-min/et-al-subsequent-use-first). */ - if ($this->etAlUseLast) { + if ($this->etAlUseLast && $this->isEtAl($name, $resultNames)) { $this->and = "…"; // set "and" $this->etAl = null; //reset $etAl; } @@ -245,14 +245,12 @@ private function cloneNamePOSC($name) } /** - * @param $data - * @param $text - * @param $resultNames - * @return string + * @param $data + * @param $resultNames + * @return bool */ - protected function appendEtAl($data, $text, $resultNames) + protected function isEtAl($data, $resultNames): bool { - //append et al abbreviation if (count($data) > 1 && !empty($resultNames) && !empty($this->etAl) @@ -260,6 +258,22 @@ protected function appendEtAl($data, $text, $resultNames) && !empty($this->etAlUseFirst) && count($data) != count($resultNames) ) { + return true; + } + + return false; + } + + /** + * @param $data + * @param $text + * @param $resultNames + * @return string + */ + protected function appendEtAl($data, $text, $resultNames) + { + //append et al abbreviation + if ($this->isEtAl($data, $resultNames)) { /* By default, when a name list is truncated to a single name, the name and the “et-al” (or “and others”) term are separated by a space (e.g. “Doe et al.”). When a name list is truncated to two or more names, the name delimiter is used (e.g. “Doe, Smith, et al.”). This behavior can be changed with the From 3910bb7cd685114313dd6cd3b154bccaf52f318a Mon Sep 17 00:00:00 2001 From: Andrei Khalipau Date: Tue, 7 Feb 2023 16:41:40 -0500 Subject: [PATCH 3/4] #145: Added a test. --- .../humans/bugfix-github-145.txt | 1301 +++++++++++++++++ tests/src/BugfixTest.php | 5 + 2 files changed, 1306 insertions(+) create mode 100644 tests/fixtures/basic-tests/processor-tests/humans/bugfix-github-145.txt diff --git a/tests/fixtures/basic-tests/processor-tests/humans/bugfix-github-145.txt b/tests/fixtures/basic-tests/processor-tests/humans/bugfix-github-145.txt new file mode 100644 index 0000000..5a9cc31 --- /dev/null +++ b/tests/fixtures/basic-tests/processor-tests/humans/bugfix-github-145.txt @@ -0,0 +1,1301 @@ +>>===== MODE =====>> +bibliography +<<===== MODE =====<< + +>>===== RESULT =====>> +
+
First, James, Jay Second, John Third, Jack Fourth, Jacob Fifth, Joseph Sixth, Julian Seventh, et al. Test.
+
+<<===== RESULT =====<< + +>>===== CSL =====>> + + +<<===== CSL =====<< + +>>===== INPUT =====>> +[ + { + "author": [ + { + "family": "First", + "given": "James" + }, + { + "family": "Second", + "given": "Jay" + }, + { + "family": "Third", + "given": "John" + }, + { + "family": "Fourth", + "given": "Jack" + }, + { + "family": "Fifth", + "given": "Jacob" + }, + { + "family": "Sixth", + "given": "Joseph" + }, + { + "family": "Seventh", + "given": "Julian" + }, + { + "family": "Eighth", + "given": "Jayden" + }, + { + "family": "Ninth", + "given": "Jackson" + }, + { + "family": "Tenth", + "given": "Jasper" + }, + { + "family": "Eleventh", + "given": "Jonathan" + }, + { + "family": "Twelfth", + "given": "Jeremiah" + } + ], + "id": "ITEM-1", + "type": "book", + "title": "Test" + } +] +<<===== INPUT =====<< diff --git a/tests/src/BugfixTest.php b/tests/src/BugfixTest.php index add064d..37935f4 100644 --- a/tests/src/BugfixTest.php +++ b/tests/src/BugfixTest.php @@ -168,4 +168,9 @@ public function testBugfixGithub143() { $this->runTestSuite('bugfix-github-143'); } + + public function testBugfixGithub145() + { + $this->runTestSuite('bugfix-github-145'); + } } From 01bf1c22df90a557fd8e8e78fb66752389c683ee Mon Sep 17 00:00:00 2001 From: Andrei Khalipau Date: Wed, 8 Feb 2023 15:28:45 -0500 Subject: [PATCH 4/4] #145: Simplified isEtAl method. --- src/Rendering/Name/Name.php | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Rendering/Name/Name.php b/src/Rendering/Name/Name.php index a62ebb8..3ecce50 100644 --- a/src/Rendering/Name/Name.php +++ b/src/Rendering/Name/Name.php @@ -251,17 +251,12 @@ private function cloneNamePOSC($name) */ protected function isEtAl($data, $resultNames): bool { - if (count($data) > 1 + return count($data) > 1 && !empty($resultNames) && !empty($this->etAl) && !empty($this->etAlMin) && !empty($this->etAlUseFirst) - && count($data) != count($resultNames) - ) { - return true; - } - - return false; + && count($data) != count($resultNames); } /**