archimatetool / archi

Archi: ArchiMate Modelling Tool
https://www.archimatetool.com
MIT License
966 stars 269 forks source link

[Feature Request] Allow setting of initial view in HTML Report #365

Open steve-d-mottram opened 6 years ago

steve-d-mottram commented 6 years ago

Hi, I'm hoping to start publishing models to a wider community who are not Archi users, and the HTML Report is a great starting point for this. However I'm finding users are confused because the inital view when they open the report is blank, and they don't know where to navigate to.

I can of course create a "Start here" canvas view with explanatory text and links to the relevant views for different stakeholders, but I would like this view to be automatically loaded when the user opens the HTML report. I saw from a forum post that this can be done by some post-processing of the generated HTML, but I suspect this would be a very simple feature to implement at export time. Could we perhaps set a "default.html.view=true" property on the view we want to be displayed, which the report generator then picks up?

jbsarrodie commented 6 years ago

Hi,

However I'm finding users are confused because the inital view when they open the report is blank, and they don't know where to navigate to

In fact it should not be blank. This is the page generated using the model element itself, thus you should put at least some description there (the Purpose field in Archi when the model name is selected).

I can of course create a "Start here" canvas view [...] but I would like this view to be automatically loaded

I have the exact same need ;-)

I suspect this would be a very simple feature to implement at export time

Well... in fact no. The report is generated using a template engine. This engine (Stringtemplate) can only check if an attribute exists, but not its value, so this limits a lot the kind of features you are suggesting.

That being said, I'm working on an enhanced version of the HTML report that will also include a dump of the model in an embedded JS based database. Maybe this could help access some properties at run time.

steve-d-mottram commented 6 years ago

Hi, thanks for responding to this request. I was wrong about the initial page being "blank", but because documentation is unstyled text, the content tends to be not very noticeable - better than nothing, but a very different experience from a view with Archimate or Canvas elements and links.

I'll probably try a script for post-processing the HTML as a short-term fix, but your solution of an embedded database sounds very interesting. I wish I had the right skills to help out. Archi has reached a level of maturity where it is getting close to meeting my team's core needs - a fantastic tool.

peterprib commented 6 years ago

Suggest alasql if looking for JavaScript database. Has some great features which I think should greatly assist.

jbsarrodie commented 6 years ago

Suggest alasql if looking for JavaScript database. Has some great features which I think should greatly assist.

It already uses alasql. I discovered it some years ago and that's a really great solution. It can be used as a relational DB, but also as a graph DB and I should also explore this.

If you have any experiences or ideas, please share ;-)

peterprib commented 6 years ago

I am playing with it on node.js. having to fix to handle async. Have forked and partly working. Started on journey as I wanted function for data in on parameter to find out it was already there but undocumented. Seems deliberate but could have been by accident of coding.

Can see using it with visjs to achieve dynamic nodal diagrams very easily. Was playing with it to test generating XML from tableau server.

evlibra commented 3 years ago

I have the same need to select an initial view of HTML report. Can we summarise the progress on this topic since 2018? As I can see, alasql is now part of HTML reports as a separate tab, what use cases of it could be? What resources would you suggest on customizing or post-processing HTML reports? For example, except for the initial view, I would like to embed some comments integration.

vic51 commented 3 years ago

I have changed the model.js included with the HTML report to allow for linking directly to a view. It doesn't change the initial view, but you could simply link to whichever initial view you like. With this change, clicking a view changes the URL, and the URL refers to the view's ID. If this is something the devs would like to include with Archi I'd be happy to do a PR, or you're welcome to just take it.

I plan to set up CI so that when I push a commit of my Archi model it triggers a build that generates the HTML report using the CLI, replaces model.js, and publishes the updated drawing.

This is the code I added to the bottom of the document ready function:

    let $viewLinks = $("a[href][target='view']");
    $viewLinks.on('click', function (event) {
        const id = event.currentTarget.href.split("/").pop().slice(0, -5);
        window.location.hash = '#' + id;
        event.stopPropagation();
        return false;
    });
    function openViewFromHash(e) {
        const isInitialLoad = e === undefined;
        const targetId = window.location.hash.substr(1);
        const matchingLinks = $viewLinks.filter(function (index, element) {
            const id = element.href.split("/").pop().slice(0, -5);
            return id === targetId;
        });
        const link = matchingLinks[0];
        if (link) {
            const $link = $(link);
            $("iframe[name='view']").attr('src', $link.attr('href'));
            if (isInitialLoad) {
                let spans = [];
                let $parentListItem = $link.parent().parent().parent();
                while ($parentListItem[0].tagName === 'LI') {
                    spans.push($parentListItem.children().first());
                    $parentListItem = $parentListItem.parent().parent();
                }
                while (spans.length) {
                    spans.pop().click();
                }
            }
        }
    }
    openViewFromHash(); //Read initial hash on page load
    $(window).on('hashchange', openViewFromHash);

Disclaimer: I have not given this code extensive testing - it is hot off the press.

Phillipus commented 3 years ago

@vic51 Thanks for that, it might be useful, although needs @jbsarrodie to look at it.

jbsarrodie commented 3 years ago

Thank you for this code. That's an interresting approach which limits impact on other parts of the report. I'll test it this week and will commit it in a new branch.

Phillipus commented 3 years ago

It's a lot of JS to get the URL to display in the bar. What's stopping the URL being updated in the first place? Or some other way to get the link - with a right-click, copy link menu?

jbsarrodie commented 3 years ago

It's a lot of JS to get the URL to display in the bar. What's stopping the URL being updated in the first place?

The way the report works (through iframes) there is no other way of doing it. Thisis how I would have implemented it with current contraints. That's also very similar to some attempts I made on other versions of the report.

Or some other way to get the link - with a right-click, copy link menu?

This does not address the same need because a direct link to a view of diagram doesn't show the navigation menu.

In short that the right way to do it. I simply have to test it a bit more before including it.

Phillipus commented 3 years ago

In short that the right way to do it.

OK, just trying to throw out alternate thinking before going with it. i.e. is the use case to display the URL or a convenient way to get it?

jbsarrodie commented 3 years ago

is the use case to display the URL or a convenient way to get it?

The use case it to enable deeplink: ie. a mean for users to bookmark or share a link to the report with a chosen view already opened. This makes it possible (for example) to add links to specific views inside other architecture documents or wikis.

So the easiest way for users to bookmark or share the page is to simply update the browser's URL. In fact the proposed code acts as a very light JS router. I've seen much worse and big implementations for this kind of stuff, so that's a good start.

That was listed on my ideas for a new version of the report

vic51 commented 3 years ago

Thanks, I'm glad to hear it may be useful. For me the worst part was invoking the click event to re-expand the tree view on the first page load. If I was willing to edit the rest of the file I would probably have made that something I could have invoked directly. If I was able to edit the generated HTML I might have facilitated something cleaner so that I could just get the list of nodes that need to be expanded with a simple selector. Those things could make this code simpler and shorter, but I wanted to keep it contained so that I could easily re-incorporate it if the base model.js changed.

Let me know if there's anything I can do to help from my end.

evlibra commented 3 years ago

the worst part was invoking the click event to re-expand the tree view on the first page load.

Looks similar to what I was stucking with - if the tree is shown expanded by default - the way to sort it could be re-expand, which I've not figured out how to do yet here: https://github.com/archimatetool/archi/issues/696#issuecomment-761889767 @vic51 what do you think?

By, the way, probably your approach could help in my 2nd challenge - to change url even on model elements clicks (to not only see its description but be able to comment on it too).

evlibra commented 3 years ago

I have not given this code extensive testing - it is hot off the press.

That works well so far - I'm testing it also. Thanks for such a valuable update @vic51 !

jbsarrodie commented 3 years ago

Disclaimer: I have not given this code extensive testing - it is hot off the press.

@vic51 I've just tested and found an issue: it works when navigating using the model tree, but not when following view-links (view references) inside the view itself. This is because the onClick event is managed only for top level links and not those from the view iframe. A workaround could be to rely on onLoad event on the iframe.

Here's a working code:

    // Add show/hide function on modeltree
    $('.tree li.parent_li > span').on('click', function (e) {
        var children = $(this).parent('li.parent_li').find(' > ul > li');
        if (children.is(":visible")) {
            children.hide('fast');
            $(this).find(' > i').addClass('glyphicon-triangle-right').removeClass('glyphicon-triangle-bottom');
        } else {
            // START SORT
            $(this).parent('li.parent_li').find(' > ul').each(function(index){
                $(this).children('li.tree-folder').sort(strcmp).appendTo($(this));
                $(this).children('li.tree-element').sort(strcmp).appendTo($(this));
            });
            // END SORT
            children.show('fast');
            $(this).find(' > i').addClass('glyphicon-triangle-bottom').removeClass('glyphicon-triangle-right');
        }
        e.stopPropagation();
    });

    // Enable deeplinks
    let $viewLinks = $("a[href][target='view']");

    function openViewFromHash(e) {
        const isInitialLoad = e === undefined;
        const targetId = window.location.hash.substr(1);
        const matchingLinks = $viewLinks.filter(function (index, element) {
            const id = element.href.split("/").pop().slice(0, -5);
            return id === targetId;
        });
        const link = matchingLinks[0];
        if (link) {
            const $link = $(link);
            $("iframe[name='view']").attr('src', $link.attr('href'));
            if (isInitialLoad) {
                let spans = [];
                let $parentListItem = $link.parent().parent().parent();
                while ($parentListItem[0].tagName === 'LI') {
                    spans.push($parentListItem.children().first());
                    $parentListItem = $parentListItem.parent().parent();
                }
                while (spans.length) {
                    spans.pop().click();
                }
            }
        }
    }
    openViewFromHash(); //Read initial hash on page load
    $(window).on('hashchange', openViewFromHash);

    // Must be done last because otherwise the first load of the iframe would trigger a has change
    $("iframe[name='view']").on('load', function (event) {
        const id = event.currentTarget.contentWindow.location.href.split("/").pop().slice(0, -5);
        window.location.hash = '#' + id;
        event.stopPropagation();
        return false;
    });

This has two issues though:

jbsarrodie commented 3 years ago

Seing your other comment now...

For me the worst part was invoking the click event to re-expand the tree view on the first page load. If I was willing to edit the rest of the file I would probably have made that something I could have invoked directly.

That code (originally mine) is a piece of crap, but it works ;-) Don't hesitate to refactor it, for example to define the function called inside the onClick (// Add show/hide function on modeltree) outside so we can call it directly.

If I was able to edit the generated HTML I might have facilitated something cleaner so that I could just get the list of nodes that need to be expanded with a simple selector.

I'm interrested for two reasons:

Those things could make this code simpler and shorter, but I wanted to keep it contained so that I could easily re-incorporate it if the base model.js changed. Let me know if there's anything I can do to help from my end.

Feel free to fork the code and test your ideas ;-)

evlibra commented 3 years ago

Here's a working code

@jbsarrodie, for me it did not work, as you can check here (view-reference on Hips): https://evlibra.github.io/Archi_Body/#c14cadeb-c7f3-462e-aa9d-1a3e66872563 Also, when the browser window is refreshed, the view folder in a tree is collapsed even if it was uncollapsed before.

[Update]: Sorry, actually it works fine - there was some lag in the update, it just has the issue of collapsing view tree when refreshing the window (but in few cases, it was working fine also, I guess it is due to the SSO issue you've mentioned)

vic51 commented 3 years ago

Thanks @jbsarrodie I'll play with your changes and comments and see how it goes.

Feel free to fork the code and test your ideas ;-)

I'd like to! I actually gave it a shot, but I'm running IntelliJ IDEA on MacOS, and it looked like it was going to take more expertise than I have to get a build environment working. I'm primarily a PHP dev, although I have done a little Java in the past.

I could try again from a Windows PC and Eclipse and see how it goes. I'd love to play with your next gen HTML report.

@evlibra I don't know about the isse you were having. I didn't really take any time to understand the why or how of the sorting, I just saw that it was there and figured I'd better try to make sure it still runs.

I had a quick look at your example above, and for some reason the tree view is starting opened and then my click closes it. I could add a condition to only issue the click if it is already closed, but I'm curious why it starts open for you. I'm heading into a meeting so I'll have to look into it later.

evlibra commented 3 years ago

@vic51 I've intentionally made the view starting open - to better overview/comment on the content (with hypotes.is).

// Setup modeltree
    $('.tree li:has(ul)').addClass('parent_li').find(' > ul > li').show();

That caused the issue of unsorted open tree by default, since sorting is done only on click:

// Add show/hide function on modeltree
    $('.tree li.parent_li > span').on('click', function (e) {
        var children = $(this).parent('li.parent_li').find(' > ul > li');
        if (children.is(":visible")) {
            children.hide('fast');
            $(this).find(' > i').addClass('glyphicon-triangle-right').removeClass('glyphicon-triangle-bottom');
        } else {
            // START SORT
            $(this).parent('li.parent_li').find(' > ul').each(function(index){
                $(this).children('li.tree-folder').sort(strcmp).appendTo($(this));
                $(this).children('li.tree-element').sort(strcmp).appendTo($(this));
            });
            // END SORT
            children.show('fast');
            $(this).find(' > i').addClass('glyphicon-triangle-bottom').removeClass('glyphicon-triangle-right');
        }
        e.stopPropagation();
    });
jbsarrodie commented 3 years ago

I'd like to! I actually gave it a shot, but I'm running IntelliJ IDEA on MacOS, and it looked like it was going to take more expertise than I have to get a build environment working. I'm primarily a PHP dev, although I have done a little Java in the past.

The good thing with Archi HTML report is that it is based on a templating system interpreted at runtime, meaning that you don't have to compile anything (no need to know Java).

Maybe a simple good start is the following:

The templating system used is StringTemplate.

vic51 commented 3 years ago

@evlibra ok, so try it without making the view start open and my code should open the tree to the node that the page is opened on. That code only runs on the first page load so it shouldn't cause problems as a user navigates the tree view. If you still want everything expanded my code could be modified to run the click on all spans. I took care to queue the required clicks in a stack so that I could expand the tree from the root down to the leaves. If instead you built the queue recursively from the root then you could process it in order, or even avoid the stack and just invoke the click event directly in the order of recursion.

Thanks for the tips @jbsarrodie! Looks straight forward enough. Should I be spending time on this if there's a next gen version on the horizon or should I work against that? I'll take a look at the problems you reported.

jbsarrodie commented 3 years ago

Should I be spending time on this if there's a next gen version on the horizon or should I work against that? I'll take a look at the problems you reported.

The current version of the report will still be used for some time. I don't expect my new version to take over before next year, so you should work on the current version you already know (unless you decide to do fundamental changes).

I'll pick code here and there when needed to add it to my new version ;-)

evlibra commented 3 years ago

If you still want everything expanded

yes, @vic51 , I want the option to have everything expanded by default. Would appreciate your advice on how to run the click on all spans. Was unable to figure it our from here without debugging:

                while ($parentListItem[0].tagName === 'LI') {
                    spans.push($parentListItem.children().first());
                    $parentListItem = $parentListItem.parent().parent();
                }
                while (spans.length) {
                    spans.pop().click();
                }
vic51 commented 3 years ago

@jbsarrodie I've done as you suggested with a git repo just for the html template files. Here's a commit which has the original work I did plus a fix for the linked views within the frame. What I got to work with local was to push a message from the frame to the main page, and then have the main page update the hash. Here's the commit: https://github.com/vic51/archi-html-template/commit/fb214875ad9edd7d236daa66c0b85f01556cd3c6

I like your idea of using pushState instead of the hash. I'll do that in another commit. I still need to get to the point of having this published behind a sso but might run into the same problem. I'm planning to use S3 and OpenID/Okta as described here: https://aws.amazon.com/blogs/networking-and-content-delivery/securing-cloudfront-distributions-using-openid-connect-and-aws-secrets-manager/

@evlibra I think the expanded tree view is a different issue from the link to a view thing I'm trying to achieve here. Maybe discussion could continue in your issue's thread for that. It would be generally independent from this unless it required this code to check for a node being already open before sending a click event. My approach would be to replace the code you quoted with code that did a recursive search for spans and then clicked them as they were found, since the discovery order should match a sane clicking order.

eduardobbs commented 3 years ago

Hi guys

I haven't checked your implementation, but I would just like to share what we've done, in case it helps. It opens our main canvas as the initial diagram, but it opens a specific diagram depending on the URL, e.g.: https://archi.intranet/?link=15ebcf04-0eea-444e-9e54-83933641db3a/views/id-130cf7bba0d641c8bff1110af492e0d1.html The URL above will automatically open the specific view desired, and the code will expand all related tree nodes and perform a scroll-down animation.

$(document).ready(() => setLandingPage());

function setLandingPage() {
    let link = new URLSearchParams(window.location.search).get('link');

    if (link === null) {
        // Opens our dashboard canvas as the initial diagram / IFrame
        $('ul.tree>li.parent_li:eq(1)>span').click();
        $('ul.tree>li.parent_li:eq(1)>ul>li.tree-element a')[1].click();
    }
    else {
        // Opens the view link passed in the querystring, e.g.: 
        //    https://archi.intranet/?link=15ebcf04-0eea-444e-9e54-83933641db3a/views/id-130cf7bba0d641c8bff1110af492e0d1.html

        let anchorSelection = $("a[href='" + link + "']");
        anchorSelection[0].click();

        let targetListItem = anchorSelection.parent().parent().parent();
        targetListItem.find("span")[0].click();

        while (true) {
            targetListItem = targetListItem.parent().parent();

            if (targetListItem[0].tagName == "LI")
                targetListItem.find("span")[0].click();
            else
                break;
        }

        window.onload = () => {
            setTimeout(() => {
                $('.root-panel-body').animate({
                    scrollTop: anchorSelection.offset().top + anchorSelection.scrollTop() - $('.root-panel-body').height()
                });
            }, 100);
        };
    }
}

On the top right you can see a link, which facilitates sharing the view that's on the iframe currently. image

The code related to that portion is in this other thread: link

jbsarrodie commented 3 years ago

Hi,

I recently had to finalize an implementation of the deeplink feature. Thanks to your help, I was able to update the report so that deeplink works in all use-cases I know (especially the one involving authentication through a third party SAML IDP server).

This implementation relies on a query string and pushstate and not a hash because hash are seen as "client" side and not transmitted to authentication servers. And thanks to @vic51 changing view from the view frame works using messages.

@Phillipus I'll add this (and some other changes) in a new html-report branch. This should be merged into the next version of Archi.

@eduardobbs I'll look at your other features and might add them too.

Phillipus commented 3 years ago

As with #726 any changes to the HTML report should be tested on the "Preview HTML Report" feature (the IE 11 internal browser on Windows). If it doesn't work the other option is to remove the "Preview HTML Report" feature from Archi. BTW - using another internal browser renderer (Chromium) is not supported properly on Eclipse.

jbsarrodie commented 3 years ago

As with #726 any changes to the HTML report should be tested on the "Preview HTML Report" feature (the IE 11 internal browser on Windows). If it doesn't work the other option is to remove the "Preview HTML Report" feature from Archi. BTW - using another internal browser renderer (Chromium) is not supported properly on Eclipse.

Already tested and it "works". In fact, because the internal browser doesn't show a URL bar, I don't know if it actually "works", but it doesn't brake anything.

eduardobbs commented 3 years ago

@jbsarrodie I was just thinking, in my situation I am IFraming the Archi HTML into another site (our Google Site intranet), therefore the visitor won't see the URL bar and hence won't be able to share the diagram link using the routing mechanism the way I suppose you are developing. The "link" link that you can see on the top-right-hand side of the last image I posted on this thread enables that, although I have not implemented it in the best way at the moment. A more elegant implementation than mine could be a "share" link with a popup whereby the user has the option to copy it to his clipboard. My implementation allows the same thing but by right-clicking the "link" link instead (not intuitive).

sonear commented 1 month ago

I had the same need and solved it using simple unix file tricks.

when complete your elements directory should look similar to this (your view GUID will differ):

 # pwd
<somedir>/<model guid>/elements
# ls -l model*
-rw-r--r--      1 scotty  staff  15494401 Sep  3 11:28 model-old.html
lrwxr-xr-x      1 scotty  staff        50 Aug 26 14:54 model.html -> ../views/b4f0323e-064d-479a-9688-1fd741291c02.html

Now reload the index and your home view will be automatically loaded first.

I also like to preserve the QUERY link that is part of the model-orig.html file... so I use perl to insert a link to into the index.html to model-orig.html named (QUERY DB) next to my model name.

This seems to keep my editing to a minimum prior to publishing. hope this helps someone.

Scotty