Announcing ZoterZero for author-only cites
Due to personal need, I've created a crude patch to insert author-only cites, for example to pair with 'suppress author' cites. This is far from an ideal solution, but it is useful until Zotero can natively handle this necessary function.
Background information: https://forums.zotero.org/discussion/5282/multiple-in-text-citation-patterns
(If you like this and want to see a better version implemented in Zotero itself, I would encourage you to voice your support of the proposal there, or to help implement it if you know how.)
1. Purpose
Zotero only allows two output formats for cites:
A) Full, e.g.
B) Suppress-author, e.g.
A third variant is needed to allow integration with sentences:
C) Integrated, e.g.
Or, more simply, we can build (C) from (B) plus author-only:
D) Author-only, e.g
Combining (D) + (B), technically two cites, we would get:
2. Method and limitations
This crude patch is surface-level in MS Word and modifies the text of cites themselves. It does not interact directly with Zotero.
I have written this for my own purposes, and it functions based on:
A) Word 2011 for Mac.
It may work with other versions or may require adjustments but the outline should apply if you want to adapt it.
B) "Unified style sheet for linguistics journals"
The format of cites is
C) Manual use: No interaction with Zotero
You must apply it manually to update cites, but I made doing so as effortless as possible (see below), because I plan on using this a lot.
D) Supplied as-is
The code works for me, and I happily release it into public domain for reuse and modification. However, it comes with no warranty or guarantee that it will work. I've done what I can to try to make it harmless and just a cosmetic patch on top of Zotero's default functions, but keep an eye out for any bugs. Feedback is welcome although I can't promise I'll be able to fix it.
3. Usage
1. Citing in Zotero
If you want an author-only citation insert pages as
In the extremely unlikely event that for some reason you actually need to cite page zero of a text, I suppose the workaround would be to add that manually as a suffix to the cite, e.g.,
2. Converting to author-only
Run the ZoterZero macro and watch as, e.g.,
The macro will initially try to convert any cites in the current selection (or within close range of the cursor). If no zero-cites are found to be converted, then it will continue for the whole document. This gives you two options:
A) Convert only the current cite, e.g., after inserting or editing it via Zotero. Your cursor should be in or next to the cite field. Or select a range of text anywhere in your document to update, and any cites within that range will be converted.
B) Convert ALL cites in the document, which for very long documents might take some time. You can activate this mode by running the macro twice, the first time converting the selected cite(s), and the next (because those are then already converted) for the whole document. Or this will happen if your cursor/selection is somewhere without any relevant cites.
In short, I recommend running the macro when you insert/edit a citation with Zotero, and also periodically (especially before save/export) on the whole document.
3. Zotero will reset this!
If you edit the cite, or refresh Zotero for the document, all author-only cites will revert back to their full 'page 0' forms. Simply run the ZoterZero macro again to fix this.
Unfortunately you must do this every time because I have found no way to integrate these macros into Zotero's macros. On this limitation, see: https://forums.zotero.org/discussion/73150/trigger-word-macro-on-creating-modifying-cite-field
(Code in next post.)
Background information: https://forums.zotero.org/discussion/5282/multiple-in-text-citation-patterns
(If you like this and want to see a better version implemented in Zotero itself, I would encourage you to voice your support of the proposal there, or to help implement it if you know how.)
1. Purpose
Zotero only allows two output formats for cites:
A) Full, e.g.
(Author YYYY)
B) Suppress-author, e.g.
(YYYY)
A third variant is needed to allow integration with sentences:
C) Integrated, e.g.
Author (YYYY)
Or, more simply, we can build (C) from (B) plus author-only:
D) Author-only, e.g
Author
Combining (D) + (B), technically two cites, we would get:
Author (YYYY)
(=C)2. Method and limitations
This crude patch is surface-level in MS Word and modifies the text of cites themselves. It does not interact directly with Zotero.
I have written this for my own purposes, and it functions based on:
A) Word 2011 for Mac.
It may work with other versions or may require adjustments but the outline should apply if you want to adapt it.
B) "Unified style sheet for linguistics journals"
The format of cites is
(Author YYYY:pp)
. Because this text is edited on the surface-level, the code does NOT support other, even similar, variants, like APA's (Author, YYYY:pp)
(with a comma). It could, however be easily adapted to work with it, if you want to modify it for your own uses. (This is entirely based on the VBA macro code for Word, not controlled from Zotero's CSL styles.)C) Manual use: No interaction with Zotero
You must apply it manually to update cites, but I made doing so as effortless as possible (see below), because I plan on using this a lot.
D) Supplied as-is
The code works for me, and I happily release it into public domain for reuse and modification. However, it comes with no warranty or guarantee that it will work. I've done what I can to try to make it harmless and just a cosmetic patch on top of Zotero's default functions, but keep an eye out for any bugs. Feedback is welcome although I can't promise I'll be able to fix it.
3. Usage
1. Citing in Zotero
If you want an author-only citation insert pages as
0
. This is a crude trick (and you can see why I picked the name 'ZoterZero'), but it's the only way we have of controlling the output given no other options in Zotero's add/edit citation menu.In the extremely unlikely event that for some reason you actually need to cite page zero of a text, I suppose the workaround would be to add that manually as a suffix to the cite, e.g.,
:0
2. Converting to author-only
Run the ZoterZero macro and watch as, e.g.,
(Author YYYY:0)
becomes Author
.The macro will initially try to convert any cites in the current selection (or within close range of the cursor). If no zero-cites are found to be converted, then it will continue for the whole document. This gives you two options:
A) Convert only the current cite, e.g., after inserting or editing it via Zotero. Your cursor should be in or next to the cite field. Or select a range of text anywhere in your document to update, and any cites within that range will be converted.
B) Convert ALL cites in the document, which for very long documents might take some time. You can activate this mode by running the macro twice, the first time converting the selected cite(s), and the next (because those are then already converted) for the whole document. Or this will happen if your cursor/selection is somewhere without any relevant cites.
In short, I recommend running the macro when you insert/edit a citation with Zotero, and also periodically (especially before save/export) on the whole document.
3. Zotero will reset this!
If you edit the cite, or refresh Zotero for the document, all author-only cites will revert back to their full 'page 0' forms. Simply run the ZoterZero macro again to fix this.
Unfortunately you must do this every time because I have found no way to integrate these macros into Zotero's macros. On this limitation, see: https://forums.zotero.org/discussion/73150/trigger-word-macro-on-creating-modifying-cite-field
(Code in next post.)
Function ZoterZeroFieldFix(F) ' Fix a field with ZoterZero
fieldText = F.Code.Text ' get the current text of the field
If InStr(fieldText, " ADDIN ZOTERO_ITEM CSL_CITATION") = 1 Then ' make sure this is a Zotero field to modify
myString = F.Code.Text ' get the code from the field
myArray = Split(myString, " ", 5) ' split that code into parts up to 5
Dim Json As Object ' create a Json object to work with
Set Json = JsonConverter.ParseJson(myArray(4)) ' get the fourth part, which is the Json data
If Json("citationItems").Count = 1 Then ' only works for one citation item
If Json("citationItems")(1)("locator") = "0" Then ' if the page range is set to "0" process for ZoterZero:
myResult = F.Result.Text ' get the displayed text from the field
If InStr(myResult, "(") = 1 Then ' if it begins with open parentheses...
myResult = Split(myResult, "(", 2) ' split at parentheses, only 2 parts
myResult = myResult(1) ' get the second part (without open parentheses)
myResult = StrReverse(myResult) ' reverse the string so we can:
myResult = Split(myResult, " ", 2) ' split at spaces, up to 2 parts
myResult = myResult(1) ' get the second part (without the actually-last [=year] section)
' the following two lines make Zotero think this was the original output, so no warnings!
myResult = StrReverse(myResult) ' reverse back to normal order
Json("properties")("plainCitation") = myResult ' set the Json citation data to new label
Json("properties")("formattedCitation") = myResult ' again, other instance
F.Result.Text = myResult ' replace the displayed text with the next text
myJson = JsonConverter.ConvertToJson(Json) ' collapse Json back to text
F.Code.Text = " " & myArray(1) & " " & myArray(2) & " " & myArray(3) & " " & myJson & " " ' reconstruct field code
ZoterZeroFieldFix = 1 ' updated, return success
End If
End If
End If
End If
End Function
Sub ZoterZero()
'
' ZoterZero main function
'' if selection or text near cursor contains fields, check and fix them
'' else check and fix all fields in document
changeSuccess = 0 ' no fields fixed yet
Selection.Expand Unit:=wdSentence ' expand the selection to at least sentence-level
If Selection.Fields.Count > 0 Then ' if fields are selected...
checkField = Selection.Fields.Count ' get the total number of fields
While checkField > 0 ' check each field
changeSuccess = ZoterZeroFieldFix(Selection.Fields(checkField)) ' check and fix this field
checkField = checkField - 1 ' check the previous field next
Wend
End If
If changeSuccess = 0 Then ' no fields have been updated yet, let's update all fields in document
' based on http://www.vbaexpress.com/kb/getarticle.php?kb_id=1100
Dim rngStory As Word.Range ' vars for below
Dim lngValidate As Long ' vars for below
Dim oShp As Shape ' vars for below
lngValidate = ActiveDocument.Sections(1).Headers(1).Range.StoryType ' starting point
For Each rngStory In ActiveDocument.StoryRanges 'Iterate through all linked stories
Do
On Error Resume Next
checkField = rngStory.Fields.Count ' get the total number of fields in this section
While checkField > 0 ' check each field
changeSuccess = ZoterZeroFieldFix(rngStory.Fields(checkField)) ' check and fix this field
checkField = checkField - 1 ' check the previous field next
Wend
Select Case rngStory.StoryType
Case 6, 7, 8, 9, 10, 11
If rngStory.ShapeRange.Count > 0 Then
For Each oShp In rngStory.ShapeRange
If oShp.TextFrame.HasText Then
checkField = Shp.TextFrame.TextRange.Fields.Count ' get the total number of fields in this section
While checkField > 0 ' check each field
changeSuccess = ZoterZeroFieldFix(Shp.TextFrame.TextRange.Fields(checkField)) ' check and fix this field
checkField = checkField - 1 ' check the previous field next
Wend
End If
Next
End If
Case Else 'Do Nothing
End Select
On Error GoTo 0
'Get next linked story (if any)
Set rngStory = rngStory.NextStoryRange ' get ready for next section
Loop Until rngStory Is Nothing ' keep going through until all sections are done
Next
End If
Selection.Collapse ' reset cursor to beginning of section which isn't quite right but close enough
End Sub
How does it work?
This code searches through fields in Word for any that are Zotero cites, and if the page (stored in "locator") is "0" then it replaces the text with a name-only variant (using surface-level string editing). It does not modify multi-item cites, because it isn't clear what format would be used. (If you have multiple items from the same author, you could add an author-only cite from just one of those items!)
This code circumvents the Zotero warning about modifying cites, by editing the metadata within those cites to match the author-only text, but does not stop Zotero from updating them, and they will be replaced again with Zotero's default format (including "page 0"). See also comments in the code above for details.
Installation: you can use this macro in Word as you would any other macro. Personally I have saved it in ZoterZero.dot and saved this in my Word Startup folder, next to where the Zotero.dot file is installed by Zotero's Word plugin. I have also added a menu item to the Zotero menu that runs the
ZoterZero
macro when clicked, and you could assign a keyboard command too.Also required: you must also install the VBA-Json parser available here:
https://github.com/VBA-tools/VBA-JSON (follow instructions there, also on the 'VBA-dictionary' it requires)
Comments, feedback, suggestions, and corrections are all welcome.
I literally taught myself the basics of VBA (Word's macro scripting language) over the last few days to try to find a solution to my personal need for this function, and there are certainly many ways in which the code could be improved. I'm just happy I finally got something to work.
(If anyone knows how to create an easy-to-install package for Word, please let me know. I can't offer better installation instructions at this time.)
https://pastebin.com/VYY0LuH8
I added some features for my own use. The same original functions still work. But three things have been added/changed:
1. I found a bug when no date was included for an entry (causing a parse error if no space was found in the author, or splitting the author's name if there was a space). Now it just, in effect, removes parentheses (and the ":0") if no year is included. This makes it more robust in case you might add or remove a year from an entry at some point.
2. I added a shortcut option for page ":00" to generate a single
Author (YYYY)
cite automatically rather than needing to combine two. This is limited only to that basic functionality: it doesn't work when specifying page numbers (you're using "00" as the command instead!) or when citing multiple items (like above), nor does it allow any modification of the author's name (like possessive 's) or splitting the author and year. For any variants like that just use the original approach with two separate cites, one forAuthor
(via page ":0") and another suppressed-author(YYYY)
. But since this format comes up often, it's just a shortcut to not slow down Word so much with multiple cites every time.3. Remove parentheses: if you insert as a prefix "(" or as a suffix ")" then the resulting doubled parentheses at the edges of any cite will be recognized as a command to remove parentheses. So for example,
((Author YYYY))
will automatically be converted toAuthor YYYY
. This is meant to be used only in the rare cases where you must avoid parentheses-within-parentheses or similar. This also allows you to avoid having overly complex prefixes and suffixes. Personally I found the need especially important when I wanted to insert other Word fields within the same parentheses: the citation in particular that motivated me to add this was roughly:(See also Smith 2018, and discussion in Section 3)
where that3
was a Word cross-reference to another section in the document that cannot be inserted as a suffix to the cite of course. Note that this option does combine with those above, if needed. You can also have only one side (start or end) of the cite "open" without parentheses. Use only when needed, but this should now cover all imaginable usage cases.I also cleaned up the code overall a bit.
--
Known limitations:
1. Multi-word "dates" like "In Progress". The code just isn't setup to deal with this, so hopefully it comes up rarely. But in the rare event of
(Author In Progress)
you would get the erroneous parseAuthor In (Progress)
. The code could be expanded to deal with this, but hopefully it's rare enough to not be a problem. Note that you could also get around this by citing some other entry for the same author as the author-only cite, then suppress-author for this one, if you're already citing multiple works by the same author. Awkward, and this is a real bug, but hopefully not a common problem. You can of course edit the citation manually as a last resort.2. Capitalization: the only variation not covered above that I have run into in normal usage is capitalization of the author's name. With certain lowercase prefixes like "von" (Dutch or German names), the lowercase form is correct within parentheses and when used in the middle of a sentence. But it should be capitalized at the beginning of a sentence ("Von"), and there is no way to do this automatically at the moment. For now, this will have to be manually modified for such usage. I thought about maybe adding "!" as a prefix-command in author-only cites to automatically capitalize but decided against adding more complexity at the moment. UPDATE: Feature added with an update posted below.
3. Now perhaps even more than before this is specifically designed for my customized version of the Unified Linguistics stylesheet. I should also note that a recent change to the official Zotero version added a space between year and page, which is NOT found in my version, and therefore will not be compatible with this code. It could be rewritten to expect an extra space, but that isn't currently the case. If you have questions, let me know and I'll try to point you in the right direction, but given how much this relies on the specific output form of cites, it will need to be customized for any individual style.
The good news is the developers have opened a ticket to add some or all of these features to Zotero itself, so this Word-macro hack may be able to be retired soon. But it's helping me for now, and maybe someone else!
As above, there could be some errors/limitations but I think this will work well. It does require the same dependencies as above (the JSON macro, etc.).
https://pastebin.com/8pKgihjR
Usage:
To remove opening parentheses, add "(" as the prefix.
To remove closing parentheses, add ")" as the suffix.
(No other functions.)
This works for multiple-item cites, by adding the prefix to the first item, and/or suffix to the last item.
On the one hand, changing CSL to allow a hack for this seems like the wrong solution when adding official commands would be the best solution (already in progress as linked in the other discussion about multiple citation patterns). But on the other hand, having if-statements based on the specific values of terms in CSL comes up as a request fairly often on the forums, such as checking whether a day/month is set in a date and if so displaying it at the end of the cite as the date of a talk, etc. (but not if it would only be the year, duplicating the info at the beginning of the cite).
CSL does allow checking if a variable is set (to any value at all), but I can't think of any way to work that into the limited insert/edit citation popup to control how cites are displayed. Actually, one very crude way of doing this would be to always manually cite pages as suffixes, and then only insert a "page" (with any 'page number'=locator) in order to show an author-only cite. I don't think anything else in that window would be available to check if it is set, in CSL. Almost...
--
Edit: actually... would this work!??
In the insert/edit cite window, you can click "page" and swap it out for another locator like chapter or various other things. If we picked one we're unlikely to ever use (like "opus"!), could CSL recognize that and then, because that weird type of locator is set, use that as a flag to display the cite differently and get an author-only cite? It's a bizarre workaround, but interesting conceptually.
Comments on compatibility are welcome here.
https://pastebin.com/tSbsHg6w
I've added an additional option to force the capitalization of the first letter of a name when it might begin a sentence, such as in the case of "von">"Von" or "de">"De".
(Inspired by: https://forums.zotero.org/discussion/79177/csl-text-case-of-particles )
To use, just add
^
as a prefix, and the first letter of the name will be capitalized. (If you're using the remove-parentheses option with the prefix(
, then the combined prefix is(^
.)First, let's define the differences between the style I'm using and APA (see also note above). My style has no space after the colon, so it looks like:
(Author YYYY:pp)
, while APA (and also now the Unified Linguistics style hosted in the Zotero repository) has a space, like this:(Author YYYY: pp)
.In principle, that should be easy to adopt the code, but because I used the space character as a splitting point, we'll need to adjust that a bit. The simplest option seems to be simply repeating the split, so it first splits off the pages (i.e. "0"), then the year, leaving only the author.
Note that I really know very little about Word macros, and I simply spent hours of trial and error to get this to work in the current version. Each line is commented so you can try to edit it yourself. Here's a suggestion on what I think would work here, but it's untested.
Find the following section:
myResult = Split(myResult, splitChar, 2) ' split at splitChar, up to 2 parts
myResult = myResult(1) ' get the second part (without the actually-last [=year] section)
Replace that with:
myResult = Split(myResult, splitChar, 2) ' split at splitChar, up to 2 parts
myResult = myResult(1) ' get the second part (without the actually-last [=pages] section)
myResult = Split(myResult, splitChar, 2) ' split at splitChar, up to 2 parts
myResult = myResult(1) ' get the second part (without the actually-last [=year] section)
(That just does the same operation twice, splitting off the end of the string that has the pages then the year, while in the original version that was a single piece.)
If that works, I think something similar can be done for the "00" option to have a simple
Author (YYYY)
cite, in the next section of the code. I think this might work, but again, untested:Find:
myResult = Split(myResult, splitChar, 2) ' split at splitChar, up to 2 parts
myResult = myResult(0) & "(" & splitChar & myResult(1) ' recombine with parentheses inserted
myResult = Split(myResult, ":", 2) ' split without pages, up to 2 parts
myResult = ")" & myResult(1) ' recombine without pages
Replace:
myResult = Split(myResult, splitChar, 3) ' split at splitChar, up to 3 parts
myResult = ")" & myResult(1) & "(" & splitChar & myResult(2) ' recombine with parentheses inserted around the year
myResult = Split(myResult, ":", 2) ' split around the colon, up to 2 parts
myResult = myResult(0) & myResult(1) ' recombine without colon
(I think that will work for the most common use cases, but I haven't fully thought through all possible formats with two spaces and whether there might be something that could go wrong with unusually formatted entries.)
[Note that while this is untested, I don't think it will severely break the code even if something isn't right. It should give you a result to check, and if the formatting isn't quite right you can adjust it as needed.]
> In the insert/edit cite window, you can click "page" and swap it out for another locator like chapter or various other things. If we picked one we're unlikely to ever use (like "opus"!)
It works. This is the citation part of my modified CSL for APA 7th: https://hastebin.com/share/afohojuroy.xml
Pick "opus" through the Zotero search bar and enter any value in it, the updated citation switches to the non-parenthesized version
Edit: unable to switch between different
layout
settings thoughDo you have a repository for your vba code? I have adapted it for it to work with APA citations and I wish to publish it on github.
I can publish it as such and refer this thread as the origin of the code too, but if the original code is on github it's easier to compare the differences, track changes or even collaborate.
It's great that you adapted this for APA! Thank you! Please go ahead and share it.
Yes, at this time the best option would be to link back to this thread. I don't have it on github or elsewhere.
But please do post here again with a link to github where it is available for everyone with APA formatting.
ZoterZero for APA citations
https://github.com/loneguardian/zotero-zoterzero
I have no plans to further develop this code - since it is more of a hack than anything, but feel free to PR for small fixes/improvements.