Custom menu of options for InsertFragmentOperation

Post here questions and problems related to oXygen frameworks/document types.
mboudreau
Posts: 68
Joined: Sat Jan 07, 2017 1:23 am

Custom menu of options for InsertFragmentOperation

Post by mboudreau »

I'm creating a framework author action to add a subject heading to a document when it's missing. E.g.,

/article/front/article-meta/article-categories/subj-group/subject

At the moment, however, my action only adds this fragment with the placeholder "My Heading" as the text content of <subject>.

What I'd like is to present the user with a menu of choices based on the journal being edited.

For example, if /article/front/journal-meta/journal-id = "Journal A", then the possible subject headings would be "Research Article", "Brief Report", or "Letter". But if journal-id = "Journal B", then the possible headings would be "Research Article", "Editorial", and "Book Review".

Is there a way to do this?

And a follow-up: once I have this action configured, I'd like to copy it to a different framework. Is there an easy way to do that?
mboudreau
Posts: 68
Joined: Sat Jan 07, 2017 1:23 am

Re: Custom menu of options for InsertFragmentOperation

Post by mboudreau »

To follow up, I've figured out how to style the inserted element to display a menu of choices:

Code: Select all

article-categories subj-group subject:before {
  content:"Subject: " 
          oxy_combobox(
            edit, '#text',
            values, 'Research Article, Brief Report, Obituary',
            fontInherit, true);
}
article-categories subj-group subject {
    visibility:-oxy-collapse-text;
}
If I could make the list of values conditional on the value of /article/front/journal-meta/journal-id, that would be ideal.
sorin_carbunaru
Posts: 398
Joined: Mon May 09, 2016 9:37 am

Re: Custom menu of options for InsertFragmentOperation

Post by sorin_carbunaru »

Hello,

For computing the values from the "oxy_combobox" you could use "oxy_xpath". The XPath expression should look at the value of "journal-id" and return a corresponding string of values.

Another option would be compute the value when the Author action is invoked. Inside the inserted fragment you could use an ${ask()} editor variable (see https://www.oxygenxml.com/doc/versions/ ... ables.html). When invoking the action, the $ask would show a dialog with a combo box or some radio buttons with the allowed values from which the user would choose. Again XPath should be used to compute the list of allowed values (see "${xpath_eval(expression)}" at the link mentioned earlier).

Yet another option would be to create an Author action based on JSOperation, not InsertFragmentOperation (see https://www.oxygenxml.com/InstData/Edit ... ation.html).

Best wishes,
Sorin Carbunaru
oXygen XML
mboudreau
Posts: 68
Joined: Sat Jan 07, 2017 1:23 am

Re: Custom menu of options for InsertFragmentOperation

Post by mboudreau »

I'm trying to implement the middle option, using ${ask()} and ${xpath_eval()}.

I can get this work as the fragment to be inserted:

Code: Select all

<article-categories>
<subj-group>
<subject>${ask('Choose one', 
               combobox,
               ('Article':'Article';
                'Book Review':'Book Review';
                'Editorial':'Editorial'))}</subject>
</subj-group>
</article-categories>
However, going the next step and using a fairly simple "if...then...else" XPath expression is giving me trouble. I have tried this:

Code: Select all

${ask('Choose one', 
      combobox,
      (${xpath_eval(if matches(/article/front/journal-meta/journal-id[@journal-id-type="publisher-id"]/text(), 'Journal A', 'i') 
                   then "'Subject 1':'Subject 1';'Subject 2':'Subject 2';'Subject 3':'Subject 3'" 
                   else "'Subject 4':'Subject 4';'Subject 5':'Subject 5';'Subject 6':'Subject 6'"
      )})
     )
  }
But Oxygen keeps telling me it can't expand the ${ask()} variable, but the rest of the message isn't helpful.

However, even if I figure out the syntax for this, I think my actual use case is too complicated for this technique. I need to accommodate about 40 different journals, each with a list of 3-5 or more unique subject headings, so the XPath expression will become too large and unwieldy.

If there were a way to read an array in an XML configuration file, that would be ideal.
mboudreau
Posts: 68
Joined: Sat Jan 07, 2017 1:23 am

Re: Custom menu of options for InsertFragmentOperation

Post by mboudreau »

I finally worked out the syntax for the ${ask()} with an embedded ${xpath_eval()}:

Code: Select all

${ask('Choose one', 
combobox,
(${xpath_eval(if (matches(/article/front/journal-meta/journal-id[@journal-id-type="publisher-id"]/text(), 'Journal A', 'i')) then "'A':'A';'B':'B';'C':'C'" else "'D':'D';'E':'E';'F':'F'")})
)}
But this remains too cumbersome if the choice of menu items is more complex than the binary choice offered by "if...then...else". If you're taking new feature suggestions, please consider a way for editor variables like this to collect values from an XML configuration file, similar to the file used by the ${i18n()} function.

In the meantime, I also found that when I click the "Cancel" button in the combobox produced by the ${ask()} variable, Oxygen produces an error message. I used the dialog box to report this.
mboudreau
Posts: 68
Joined: Sat Jan 07, 2017 1:23 am

Re: Custom menu of options for InsertFragmentOperation

Post by mboudreau »

One more follow-up.

As I noted previously, this works:

Code: Select all

${ask('Choose one', 
combobox,
(${xpath_eval(if (matches(/article/front/journal-meta/journal-id[@journal-id-type="publisher-id"]/text(), 'Journal A', 'i')) then "'A':'A';'B':'B';'C':'C'" else "'D':'D';'E':'E';'F':'F'")})
)}
And when I worked previously with a simpler example, an additional ${xpath_eval()} variable in the combobox label worked:

Code: Select all

${ask('Select subject heading for ${xpath_eval(/article/front/journal-meta/journal-id[@journal-id-type="publisher-id"]/text())}', 
combobox,
('Article':'Article';
 'Book Review':'Book Review';
  'Editorial':'Editorial')
)}
However, when I add the longer label to the first example:

Code: Select all

${ask('Select subject heading for ${xpath_eval(/article/front/journal-meta/journal-id[@journal-id-type="publisher-id"]/text())}', 
combobox,
(${xpath_eval(if (matches(/article/front/journal-meta/journal-id[@journal-id-type="publisher-id"]/text(), 'Journal A', 'i')) then "'A':'A';'B':'B';'C':'C'" else "'D':'D';'E':'E';'F':'F'")})
)}
the combobox appears with no label at all.
sorin_carbunaru
Posts: 398
Joined: Mon May 09, 2016 9:37 am

Re: Custom menu of options for InsertFragmentOperation

Post by sorin_carbunaru »

Hello Michael,

1. For the values you could use the "doc" function in XPath to read the 'value':'label' pairs from a document.

2. A colleague of mine is already investigating the issue regarding the error shown when cancelling the $ask. We will contact you when we release a kit having a fix for that problem. For reference purposes, EXM-44688 is the issue ID.

3. Regarding your last post, it seems there is a problem with creating a "static_text dynamically_computed_text"' message for $ask when also using $xpath_eval for the values. I created another bug for this (EXM-44698). As a workaround, you could build your message using an $xpath_eval which uses the "concat()" function; something like $xpath_eval( concat( 'TEXT', $xpath_eval(...) ) ).

Best wishes,
Sorin Carbunaru
oXygen XML
mboudreau
Posts: 68
Joined: Sat Jan 07, 2017 1:23 am

Re: Custom menu of options for InsertFragmentOperation

Post by mboudreau »

Hi Sorin,

I'm trying your suggestion of using the "doc" function. I started with a very simple version just to see if Oxygen could locate the file:

Code: Select all

${ask('Select subject heading', 
combobox,
(${xpath_eval( if ( doc-available(${framework}/resources/subject_headings.txt) ) then "'A':'A';'B':'B';'C':'C'" else "'X':'X';'Y':'Y';'Z':'Z'")})
)}
However, this produces the error message
An error occurred while expanding the 'ask' editor variable, at line 1, column 44. Please check the syntax of the editor variable: '${ask("Select subject heading', combobox, ())}'.
This seems to indicate a problem with the ${xpath_eval()} expression, but after stripping it down to the very simple form above, I can't tell what the problem is.
sorin_carbunaru
Posts: 398
Joined: Mon May 09, 2016 9:37 am

Re: Custom menu of options for InsertFragmentOperation

Post by sorin_carbunaru »

Hello Michael,

The "doc-available()" function must have a string argument, apparently, so you should surround the expression with quotes:

Code: Select all

doc-available('${framework}/resources/subject_headings.txt')
Sorin C.
mboudreau
Posts: 68
Joined: Sat Jan 07, 2017 1:23 am

Re: Custom menu of options for InsertFragmentOperation

Post by mboudreau »

Thanks, Sorin!

This is what finally works:

Code: Select all

${ask('Choose or type a subject heading:', 
editable_combobox,
(${xpath_eval( if ( doc-available(concat('${framework}/resources/subjects_', /article/front/journal-meta/journal-id[@journal-id-type="publisher-id"], '.xml')) )
then string(doc(concat('${framework}/resources/subjects_', /article/front/journal-meta/journal-id[@journal-id-type="publisher-id"], '.xml')))
else "'My Heading':'My Heading'")})
)}
I changed from combobox to editable_combobox to accommodate a scenario where the file of subjects is not up to date or is missing.

This solution requires that I create a separate file of subject headings for each journal, which is a bit cluttered but workable. Each file currently looks like this:

Code: Select all

<?xml version="1.0"?>
<subject-headings>
    'Journal A Article':'Journal A Article';
    'Journal A Editorial':'Journal A Editorial';
    'Journal A Review':'Journal A Review'
</subject-headings>
What would be even better is if I could address a particular node in a file, as I have done in an XSLT transform, e.g.,

Code: Select all

<xsl:when test="doc-available(concat($ms-directory, '/graphic_dimensions.xml'))">
    <xsl:value-of select="doc(concat($ms-directory, '/graphic_dimensions.xml'))/graphics-log/file[@name = $filename][1]/@height" />
If that were possible, I could use a single file that looks like this:

Code: Select all

<?xml version="1.0"?>
<subject-headings>
    <journal id="JournalA">
        'Journal A Article':'Journal A Article';
        'Journal A Editorial':'Journal A Editorial';
        'Journal A Review':'Journal A Review'
    </journal>
    <journal id="JournalB">
        'Journal B Article':'Journal B Article';
        'Journal B Editorial':'Journal B Editorial';
        'Journal B Review':'Journal B Review'
    </journal>
    <journal id="JournalC">
        'Journal C Article':'Journal C Article';
        'Journal B Editorial':'Journal C Editorial';
        'Journal C Review':'Journal C Review'
    </journal>
    etc.
</subject-headings>
sorin_carbunaru
Posts: 398
Joined: Mon May 09, 2016 9:37 am

Re: Custom menu of options for InsertFragmentOperation

Post by sorin_carbunaru »

Hi Michael,

You can already address a particular node in a file by using $xpath_eval() with a parameter such as:

Code: Select all

doc(concat($ms-directory, '/graphic_dimensions.xml'))/graphics-log/file[@name = $filename][1]/@height
Sorin C.
mboudreau
Posts: 68
Joined: Sat Jan 07, 2017 1:23 am

Re: Custom menu of options for InsertFragmentOperation

Post by mboudreau »

Hm. Stumped again. With this file:

Code: Select all

<?xml version="1.0"?>
<subject-headings>
    <journal id="AJS">
        'AJS Article':'AJS Article';
        'AJS Editorial':'AJS Editorial';
        'AJS Review':'AJS Review'
    </journal>
    <journal id="BBL">
        'BBL Article':'BBL Article';
        'BBL Editorial':'BBL Editorial';
        'BBL Review':'BBL Review'
    </journal>
    <journal id="CA">
        'CA Article':'CA Article';
        'CA Editorial':'CA Editorial';
        'CA Review':'CA Review'
    </journal>
</subject-headings>
I'm trying this:

Code: Select all

${ask('Choose or type a subject heading:', 
editable_combobox,
(${xpath_eval( if ( doc-available(${framework}/resources/subject_headings.xml) )
then string(doc(${framework}/resources/subject_headings.xml)/subject-headings/journal[@id="AJS"]/text())
else "'My Heading':'My Heading'")})
)}
Once again I'm getting the error message that indicates the problem is in the 3rd parameter of ${ask()}, but I can't figure out what's wrong.
sorin_carbunaru
Posts: 398
Joined: Mon May 09, 2016 9:37 am

Re: Custom menu of options for InsertFragmentOperation

Post by sorin_carbunaru »

Hello,

The arguments of the "doc-available()" and "doc()" functions should be surrounded with quotes. Also, please make sure you use spaces and newlines when formatting the editor variables, not tabs. It seems they are not accepted, but I will add an issue for this.

The following variable is working fine for me:

Code: Select all

${ask(
    'Choose or type a subject heading:',
    editable_combobox,
    (${xpath_eval(if(doc-available('${framework}/resources/subject_headings.xml')) 
    then string(doc('${framework}/resources/subject_headings.xml')/subject-headings/journal[@id="AJS"]/text()) 
    else "'My Heading':'My Heading'")})
)}
mboudreau
Posts: 68
Joined: Sat Jan 07, 2017 1:23 am

Re: Custom menu of options for InsertFragmentOperation

Post by mboudreau »

Ah, those quotes! I've gotten tripped up on that before.

Since I want to choose the <journal> element from subject_headings.xml based on the content of /article/front/journal-meta/journal-id[@journal-id-type="publisher-id"] in the file being edited, I expanded the example to this:

Code: Select all

<article-categories>
<subj-group>
<subject>${ask(
    'Choose or type a subject heading:',
    editable_combobox,
    (${xpath_eval(if(doc-available('${framework}/resources/subject_headings.xml')) 
    then string(doc('${framework}/resources/subject_headings.xml')/subject-headings/journal[@id="${xpath_eval(/article/front/journal-meta/journal-id[@journal-id-type='publisher-id']/text())}"]/text()) 
    else "'My Heading':'My Heading'")})
)}</subject>
</subj-group>
</article-categories>
Happily, this works. I was surprised because I didn't expect to be able to nest ${xpath_eval} variables. Is there a limit to the level of nesting that is possible?

Also, it appears that both space characters and newlines are acceptable to separate the arguments of the XPath functions, correct? (I didn't intend to use tabs, but it's possible my text processor inserted them while trying to be helpful.)

The above example will work as long as my file subject_headings.xml includes a <journal> element for all the possible values of /article/front/journal-meta/journal-id[@journal-id-type="publisher-id"] in the document being edited.
sorin_carbunaru
Posts: 398
Joined: Mon May 09, 2016 9:37 am

Re: Custom menu of options for InsertFragmentOperation

Post by sorin_carbunaru »

Hi Michael,

1. We didn't set any limit to the nesting level of editor variables, but the more nested they are, the more difficult they are to read and edit. For this reason, I wouldn't recommend nesting too many editor variables.

2. Yes, spaces and newlines should be accepted as separators.

Sorin C.
sorin_carbunaru
Posts: 398
Joined: Mon May 09, 2016 9:37 am

Re: Custom menu of options for InsertFragmentOperation

Post by sorin_carbunaru »

Hello,

In regards to
In the meantime, I also found that when I click the "Cancel" button in the combobox produced by the ${ask()} variable, Oxygen produces an error message. I used the dialog box to report this.
I just wanted to let you know that the error has been fixed in the recently released oXygen 22.

Best wishes,
Sorin Carbunaru
oXygen XML
mboudreau
Posts: 68
Joined: Sat Jan 07, 2017 1:23 am

Re: Custom menu of options for InsertFragmentOperation

Post by mboudreau »

Thanks!
Post Reply