<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  </head>
  <body text="#000000" bgcolor="#FFFFFF">
    Hello,<br>
    <br>
    Regarding Web Author, we'll retain the current behavior regarding
    git sub-modules for now.<br>
    <br>
    But for your use-case we think allowing users to switch the
    file-browser to the parent repository and choosing a file from there
    is a good compromise.<br>
    To make this work you will need to add custom code that transforms
    absolute URLs into relative URLs and vice versa while taking into
    account sub-modules.<br>
    <br>
    To do that there are 3 hooks where you should intervene using a
    custom Web Author plugin [1][2].<br>
    <br>
    1. Add a URI resolver [3] which will be used to resolve relative
    paths to absolute paths.<br>
    2. Add a Relative Reference Resolver which will be used to resolve
    absolute paths to relative paths on the server side.<br>
    3. Add a Relative Reference Resolver which will be used to resolve
    absolute paths to relative paths on the client side.<br>
    <br>
    I'm attaching an example for points 1 & 2:<br>
    <blockquote><font size="-1"><tt>package example.plugin<br>
          <br>
          import java.net.MalformedURLException;</tt><tt><br>
        </tt><tt>import java.net.URI;</tt><tt><br>
        </tt><tt>import java.net.URISyntaxException;</tt><tt><br>
        </tt><tt>import java.net.URL;</tt><tt><br>
        </tt><tt>import java.util.Optional;</tt><tt><br>
        </tt><tt>import java.util.function.Predicate;</tt><tt><br>
        </tt><tt>import java.util.regex.Matcher;</tt><tt><br>
        </tt><tt>import java.util.regex.Pattern;</tt><tt><br>
        </tt><tt><br>
        </tt><tt>import javax.xml.transform.Source;</tt><tt><br>
        </tt><tt>import javax.xml.transform.TransformerException;</tt><tt><br>
        </tt><tt>import javax.xml.transform.URIResolver;</tt><tt><br>
        </tt><tt><br>
        </tt><tt>import ro.sync.basic.util.URLUtil;</tt><tt><br>
        </tt><tt>import
          ro.sync.exml.plugin.workspace.WorkspaceAccessPluginExtension;</tt><tt><br>
        </tt><tt>import
          ro.sync.exml.workspace.api.standalone.StandalonePluginWorkspace;</tt><tt><br>
        </tt><tt><br>
        </tt><tt>public class MyWorkspaceAccessPluginExtension
          implements WorkspaceAccessPluginExtension {</tt><tt><br>
        </tt><tt><br>
        </tt><tt>  /**</tt><tt><br>
        </tt><tt>   * Predicate to check if a URL is inside a configured
          parent repository.<br>
             * You can hard-code this, save it in an option or determine
          it dynamically depending on your use-case.<br>
        </tt><tt> </tt><tt>   * */</tt><tt><br>
        </tt><tt>  private Predicate<String>
          urlIsInsideParentRepository =
          Pattern.compile(<a class="moz-txt-link-rfc2396E"
href="mailto:%5Egitgle://%28[%5E@]+@%29?https%3A%2F%2Fgitlab.example.com%2Fowner%2Fparent-project/master/">"^gitgle://([^@]+@)?https%3A%2F%2Fgitlab.example.com%2Fowner%2Fparent-project/master/"</a>).asPredicate();</tt><tt><br>
        </tt><tt><br>
        </tt><tt>  /**</tt><tt><br>
        </tt><tt>   * This regExp extract first part of a Git URL,
          without the path part.</tt><tt><br>
        </tt><tt>   * More details about Git URLs:
          <a class="moz-txt-link-freetext"
href="https://www.oxygenxml.com/doc/versions/21.1.1.0/ug-waCustom/topics/oxy-url.html#oxy-url__oxyurl_unique_scheme">https://www.oxygenxml.com/doc/versions/21.1.1.0/ug-waCustom/topics/oxy-url.html#oxy-url__oxyurl_unique_scheme</a></tt><tt><br>
        </tt><tt>   * </tt><tt><br>
        </tt><tt>   * In this example I'm looking at `gitgle` URLs which
          are URLs for GitLab on-premise.</tt><tt><br>
        </tt><tt>   */</tt><tt><br>
        </tt><tt>  private Pattern gitUrlWithoutPathPattern =
          Pattern.compile("^gitgle://([^@]+@)?([^:/]+)(:\\d+)?/[^/]+/");</tt><tt><br>
        </tt><tt>  </tt><tt><br>
        </tt><tt>  /**</tt><tt><br>
        </tt><tt>   * The URL of the submodule inside a parent
          repository.</tt><tt><br>
        </tt><tt>   */</tt><tt><br>
        </tt><tt>  private String submoduleUrlInsideParent =
"gitgle://https%3A%2F%2Fgitlab.example.com%2Fowner%2Fparent-project/master/submodule/";</tt><tt><br>
        </tt><tt>  </tt><tt><br>
        </tt><tt>  URIResolver uriResolver = new URIResolver() {</tt><tt><br>
        </tt><tt>    @Override</tt><tt><br>
        </tt><tt>    public Source resolve(String href, String base)
          throws TransformerException {</tt><tt><br>
        </tt><tt>      </tt><tt><br>
        </tt><tt>      try {</tt><tt><br>
        </tt><tt>        Optional<String> pathRelativeToParentRepo
          = getPathRelativeToSubmoduleInParentRepo(href, base);</tt><tt><br>
        </tt><tt>        </tt><tt><br>
        </tt><tt>        if (pathRelativeToParentRepo.isPresent()) {</tt><tt><br>
        </tt><tt>          URI uriInParentRepo = new
          URI(submoduleUrlInsideParent +
          pathRelativeToParentRepo.get());</tt><tt><br>
        </tt><tt>          </tt><tt><br>
        </tt><tt>          URL normalizedUrlInParentRepo =
          uriInParentRepo.normalize().toURL();</tt><tt><br>
        </tt><tt>          </tt><tt><br>
        </tt><tt>          /*</tt><tt><br>
        </tt><tt>           * Attach session information to the URL.</tt><tt><br>
        </tt><tt>           * This is needed to authenticate the
          requests that will retrieve the content of this URL.</tt><tt><br>
        </tt><tt>           * */</tt><tt><br>
        </tt><tt>          URL hrefWithUserInfo =
          URLUtil.attachUserInfo(normalizedUrlInParentRepo, new
          URL(base).getUserInfo(), null, false);</tt><tt><br>
        </tt><tt>          </tt><tt><br>
        </tt><tt>          return new Source() {</tt><tt><br>
        </tt><tt>            @Override</tt><tt><br>
        </tt><tt>            public void setSystemId(String systemId)
          {/**/}</tt><tt><br>
        </tt><tt>            </tt><tt><br>
        </tt><tt>            @Override</tt><tt><br>
        </tt><tt>            public String getSystemId() {</tt><tt><br>
        </tt><tt>              return hrefWithUserInfo.toExternalForm();</tt><tt><br>
        </tt><tt>            }</tt><tt><br>
        </tt><tt>          };</tt><tt><br>
        </tt><tt>        }</tt><tt><br>
        </tt><tt>      } catch (URISyntaxException |
          MalformedURLException e) {/**/}</tt><tt><br>
        </tt><tt>      </tt><tt><br>
        </tt><tt>      return null;</tt><tt><br>
        </tt><tt>    }</tt><tt><br>
        </tt><tt>    </tt><tt><br>
        </tt><tt>    /**</tt><tt><br>
        </tt><tt>     * Returns a href that is relative to the submodule
          from the parent URL if the given href represents a navigation
          outside of the submodule.</tt><tt><br>
        </tt><tt>     * @param href An href attribute, which may be
          relative or absolute.</tt><tt><br>
        </tt><tt>     * @param base The base URI against which the first
          argument will be made absolute if the absolute URI is
          required.</tt><tt><br>
        </tt><tt>     * @return A href that is relative to the parent
          URL (The repository that contains submodules).</tt><tt><br>
        </tt><tt>     * @throws URISyntaxException</tt><tt><br>
        </tt><tt>     */</tt><tt><br>
        </tt><tt>    private Optional<String>
          getPathRelativeToSubmoduleInParentRepo(String href, String
          base) throws URISyntaxException {</tt><tt><br>
        </tt><tt>      Optional<String> relativePathToParentRepo =
          Optional.empty();</tt><tt><br>
        </tt><tt>      </tt><tt><br>
        </tt><tt>      // If the URL is not inside a submodule there's
          no need to make any change to it.</tt><tt><br>
        </tt><tt>      if (base != null && href != null
          /*&& urlIsInsideSubmodule.test(base)*/) {</tt><tt><br>
        </tt><tt>        </tt><tt><br>
        </tt><tt>        // If the href is absolute we leave it as is.</tt><tt><br>
        </tt><tt>        if (!new URI(href).isAbsolute()) {</tt><tt><br>
        </tt><tt>          </tt><tt><br>
        </tt><tt>          String baseUrlWithoutFileName =
          base.substring(0, base.lastIndexOf('/') + 1);</tt><tt><br>
        </tt><tt>          </tt><tt><br>
        </tt><tt>          /*</tt><tt><br>
        </tt><tt>           *                                    
          ______________ ______ ____________</tt><tt><br>
        </tt><tt>           * A git URL looks like this:
          gitgle://REPOSITORY_URI/BRANCH/PATH/TO/FILE</tt><tt><br>
        </tt><tt>           * </tt><tt><br>
        </tt><tt>           * We want to append our relative href to the
          base git URL and normalize it.</tt><tt><br>
        </tt><tt>           * But we need to add an extra "../" to
          account for the /BRANCH/ part of the git URL.</tt><tt><br>
        </tt><tt>           * */</tt><tt><br>
        </tt><tt>          URI normalized = new
          URI(baseUrlWithoutFileName + "../" + href).normalize();</tt><tt><br>
        </tt><tt>          </tt><tt><br>
        </tt><tt>          String absoluteUrlInsideSubmodule =
          normalized.toString();</tt><tt><br>
        </tt><tt>          int upOneLevelIndex =
          absoluteUrlInsideSubmodule.indexOf("..");</tt><tt><br>
        </tt><tt>          </tt><tt><br>
        </tt><tt>          // After normalization any "../" left
          represents a navigation outside the current submodule.</tt><tt><br>
        </tt><tt>          if (upOneLevelIndex != -1) {</tt><tt><br>
        </tt><tt>            String hrefRelativeToParent =
          absoluteUrlInsideSubmodule.substring(upOneLevelIndex);</tt><tt><br>
        </tt><tt>            relativePathToParentRepo =
          Optional.of(hrefRelativeToParent);</tt><tt><br>
        </tt><tt>          }</tt><tt><br>
        </tt><tt>        }</tt><tt><br>
        </tt><tt>      }</tt><tt><br>
        </tt><tt>      </tt><tt><br>
        </tt><tt>      return relativePathToParentRepo;</tt><tt><br>
        </tt><tt>    }</tt><tt><br>
        </tt><tt>  };</tt><tt><br>
        </tt><tt>  </tt><tt><br>
        </tt><tt>  @Override</tt><tt><br>
        </tt><tt>  public void
          applicationStarted(StandalonePluginWorkspace
          pluginWorkspaceAccess) {</tt><tt><br>
        </tt><tt>    // Add a URI resolver which will be used to resolve
          relative paths to absolute paths.</tt><tt><br>
        </tt><tt>   
pluginWorkspaceAccess.getXMLUtilAccess().addPriorityURIResolver(uriResolver);</tt><tt><br>
        </tt><tt>    </tt><tt><br>
        </tt><tt>    // Add a Relative Reference Resolver which will be
          used to resolve absolute paths to relative paths.</tt><tt><br>
        </tt><tt>   
          pluginWorkspaceAccess.addRelativeReferencesResolver("gitgle",
          (baseUrl, childUrl) -> {</tt><tt><br>
        </tt><tt><br>
        </tt><tt>      try {</tt><tt><br>
        </tt><tt>        boolean baseIsInsideAParent =
          urlIsInsideParentRepository.test(baseUrl.toExternalForm());</tt><tt><br>
        </tt><tt>        boolean childIsInsideAParent =
          urlIsInsideParentRepository.test(childUrl.toExternalForm());</tt><tt><br>
        </tt><tt>        </tt><tt><br>
        </tt><tt>        if (!baseIsInsideAParent &&
          childIsInsideAParent) {</tt><tt><br>
        </tt><tt>          // Make the childUrl relative to the baseUrl
          while taking submodules into account.</tt><tt><br>
        </tt><tt>          </tt><tt><br>
        </tt><tt>          String baseUrlString =
          baseUrl.toExternalForm();</tt><tt><br>
        </tt><tt>          Matcher matcher =
          gitUrlWithoutPathPattern.matcher(baseUrlString);</tt><tt><br>
        </tt><tt>          </tt><tt><br>
        </tt><tt>          if (matcher.find()) {</tt><tt><br>
        </tt><tt>            String submoduleUrl =
          baseUrlString.substring(matcher.start(), matcher.end());</tt><tt><br>
        </tt><tt>            </tt><tt><br>
        </tt><tt>            URL submoduleURL =
          URLUtil.attachUserInfo(new URL(submoduleUrl),
          baseUrl.getUserInfo(), null, false);</tt><tt><br>
        </tt><tt>            </tt><tt><br>
        </tt><tt>            // To make this work for all submodules you
          could have a map from a submodule URL to its URL inside the
          parent. </tt><tt><br>
        </tt><tt>            URL submoduleInParentURL =
          URLUtil.attachUserInfo(new URL(submoduleUrlInsideParent),
          baseUrl.getUserInfo(), null, false);</tt><tt><br>
        </tt><tt>            </tt><tt><br>
        </tt><tt>            String relativePathToRootOfSubmodule =
          URLUtil.makeRelative(baseUrl, submoduleURL, true, true);</tt><tt><br>
        </tt><tt>            if
          (relativePathToRootOfSubmodule.equals(".")) {</tt><tt><br>
        </tt><tt>              relativePathToRootOfSubmodule = "";</tt><tt><br>
        </tt><tt>            }</tt><tt><br>
        </tt><tt>            </tt><tt><br>
        </tt><tt>            String relativePathToTargetInsideParent =
          URLUtil.makeRelative(submoduleInParentURL, childUrl, true,
          true);</tt><tt><br>
        </tt><tt>            </tt><tt><br>
        </tt><tt>            // Resolved relative URL</tt><tt><br>
        </tt><tt>            return relativePathToRootOfSubmodule +
          relativePathToTargetInsideParent;</tt><tt><br>
        </tt><tt>          }</tt><tt><br>
        </tt><tt>        }</tt><tt><br>
        </tt><tt>      </tt><tt><br>
        </tt><tt>      } catch (MalformedURLException e) { /**/ }</tt><tt><br>
        </tt><tt>      </tt><tt><br>
        </tt><tt>      return null;</tt><tt><br>
        </tt><tt>    });</tt><tt><br>
        </tt><tt>  }</tt><tt><br>
        </tt><tt><br>
        </tt><tt>  @Override</tt><tt><br>
        </tt><tt>  public boolean applicationClosing() {</tt><tt><br>
        </tt><tt>    return true;</tt><tt><br>
        </tt><tt>  }</tt><tt><br>
        </tt><tt>}</tt></font><br>
    </blockquote>
    The <tt>example.plugin.MyWorkspaceAccessPluginExtension</tt> class
    need to be referenced in the <tt>plugin.xml</tt> file of your
    plugin [4].<br>
    <br>
    For point 2, you need to add a relative references resolver [5] in
    javascript. This resolver will be used on the client's browser to
    resolve URLs.<br>
    <blockquote><font size="-1"><tt>(function () {</tt><br>
        <tt>  workspace.addRelativeReferencesResolver('gitgle', function
          (baseUrl, childUrl) {</tt><br>
        <br>
        <tt>    // Re-implement the java code which was used for
          `pluginWorkspaceAccess.addRelativeReferencesResolver`.</tt><br>
        <br>
        <tt>    return "resolvedUrl";</tt><br>
        <tt>  });</tt><br>
        <tt>})();</tt></font><br>
    </blockquote>
    <br>
    Let us know if you need anything else.<br>
    <br>
    Best,<br>
    Gabriel<br>
    <br>
    [1] <a
href="https://www.oxygenxml.com/doc/versions/21.1.1.0/ug-waCustom/topics/customizing_plugins.html">https://www.oxygenxml.com/doc/versions/21.1.1.0/ug-waCustom/topics/customizing_plugins.html</a><br>
    [2] <a
href="https://www.oxygenxml.com/doc/versions/21.1.1.0/ug-waCustom/topics/webapp-plugin-prototyping.html?hl=Go%20to%20the%20following%20repository%20and%20follow%20the%20instructions">https://www.oxygenxml.com/doc/versions/21.1.1.0/ug-waCustom/topics/webapp-plugin-prototyping.html</a><br>
    [3] <a
href="https://www.oxygenxml.com/doc/versions/21.1.1.0/ug-waCustom/topics/oxy-url.html">https://www.oxygenxml.com/doc/versions/21.1.1.0/ug-waCustom/topics/oxy-url.html</a><br>
    [4] <a
href="https://www.oxygenxml.com/doc/versions/21.1/ug-editor/topics/workspace-access-plugin.html">https://www.oxygenxml.com/doc/versions/21.1/ug-editor/topics/workspace-access-plugin.html</a><br>
    [5] <a
href="https://www.oxygenxml.com/maven/com/oxygenxml/oxygen-webapp/21.1.1.0/jsdoc/sync.api.Workspace.html#addRelativeReferencesResolver">https://www.oxygenxml.com/maven/com/oxygenxml/oxygen-webapp/21.1.1.0/jsdoc/sync.api.Workspace.html#addRelativeReferencesResolver</a><br>
    <br>
    <pre class="moz-signature" cols="72">Gabriel Titerlea
<a class="moz-txt-link-freetext" href="https://www.oxygenxml.com">https://www.oxygenxml.com</a></pre>
    <div class="moz-cite-prefix">On 03-Dec-19 18:48, Jirka Kosek wrote:<br>
    </div>
    <blockquote type="cite"
      cite="mid:5ab44311-8860-0b48-c0e3-e2923c763df1@kosek.cz">
      <pre wrap="">On 3.12.2019 12:40, oXygen XML Editor Support (Gabriel Titerlea) wrote:
</pre>
      <blockquote type="cite">
        <pre wrap="">We're trying to justify a change to the current behavior regarding Git
sub-modules in Web Author. Please describe your use-case in more detail.
</pre>
      </blockquote>
      <pre wrap="">It's project where requirements changed over the time ;-)

Originally separate textbooks have been edited -- each textbook in a
separate git repository. Each textbook has been standalone. For each
textbook different group of users has access rights.

Now requirements changed and some books need to reuse content
(chapters/section, images, ...) from other books. Merging all individual
repositories into one large repository (for example using git subtree
feature) where there will be separate folder for each book is not viable
as this way we would loose ability to give different access rights for
different textbooks. Git can set access rights only on whole repository
not on individual folders inside it.

So using submodules we can keep textbooks that don't require inclusion
of assets from other textbooks as they were in a separate repositories.
Then textbooks that need to reuse content can be edited from "grandrepo"
that would include all textbooks as submodules.

Well, yes perhaps this is far beyond what git has been designed for. Git
was fine for original requirements. Knowing current requirements at the
start of project would mean that expensive CCMS should be used instead
of git. :-(

Thanks and have a nice day,

                                        Jirka


</pre>
    </blockquote>
    <br>
  </body>
</html>