In Alfastrakhovanie we actively use the "Wiki", the engine of which is Atlassian Confluence. The first time I seriously came across it (in an attempt to create content in it), I lacked "dynamism" in it - I wanted to be able to programmatically form parts of pages, interact with other systems, etc.
For some time he banged his head against different walls, but then he saw that "there was not one wall in the house." I want to share my experience - how can I add speakers to Confluence. I hope this will be useful to those who use it. And, as usual, to everyone inquisitive.
Initially, Confluence suggests one universal way to expand its functionality - plugins. Probably, he is good, there is only one drawback - a high entry threshold (you need to learn a lot).
After a brief (by space standards) reflection, there were two more fairly simple ways to expand its functionality: standard macro "HTML" and "HTML Include", in this article I will focus on the latter in more detail.
There is one principle of operation for these methods - HTML code is embedded in the Confluence page, it can be static (which can also be interesting, for example, for non-standard page formatting), it can be dynamic (including server components).
Let's look at the proposed way to expand the capabilities of Confluence using a simple example - a document approval list.
What we want to do: add a list of approval to the page so that everyone can see who should coordinate the document, whether it was agreed, and if so, when.
For convenience, we will make the list item a button and write in it the name of the coordinator. The button will be active if the page is viewed by a coordinator, and not active in all other cases.
After approval, the button "will cease to be a button" - instead of a button after approval, we will draw on the page the fact of approval in the form of a text (the name of the approver and the date of approval). In the most draft version (without design) it may look something like this:
Comments - how it works:
A little complicated in words, let's try to clarify the code.
So we need to create two scripts
Let's create them, start with the button.
Schematically, how the button script works:
date = getSignDate() if date: # . else: if user==signee: # else: # inactive
The script should be a valid HTML document, the body of which in the most minimal form is a form
An approximate view of the script (for brevity, I threw away everything unnecessary - see the full working code on github )
form = cgi.FieldStorage() signee = form["signee"].value # ( ) actual = form["actual"].value # id = form["id"].value # , resHtml = """ <!DOCTYPE HTML> <html> <head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /></head> <body> <form name="input" action="http://172.16.108.216/misc/sign_proc.py" method="get"> """ server, token = wikiLogin() # Confluence userName = getUserName(token, signee) # res = getSignDate(id, signee) # if res: # resHtml += "<p> ({0}, {1})</p>".format(userName, res) # else: # if signee.lower() == actual.lower(): # - resHtml += '<input type="hidden" name="id" value="{0}">'.format(id) resHtml += '<input type="hidden" name="signee" value="{0}">'.format(signee) resHtml += " <input type=\"submit\" value=\"{0}\">".format(userName) else: # - resHtml += " <input disabled type=\"submit\" value=\"{0}\">".format(userName) resHtml += "</form></body></html>" # HTML sys.stdout.buffer.write(b'Content-Type: text/html;charset=utf-8\n\n') sys.stdout.buffer.write(resHtml.encode("utf-8"))
The task of the handler is very simple - to fix the fact of pressing the "agree" button. Then redirect back to the page where the request came from - when rendering the page, the fact of coordination will already be taken into account (see the description above).
An example (abbreviated) script "handler" (the full version - see github)
form = cgi.FieldStorage() signee = form["signee"].value # id = form["id"].value # , addSignDate(id, signee) # resHtml = """ <html> <head> <meta http-equiv="refresh" content="5;url=http://wiki.alfastrah.ru/display/DIT/{0}"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title> </title> </head> <body> <p> <b>{0}</b> <b>{1}</b> . <br> ...</p> </body> </html> """.format(id, signee) sys.stdout.buffer.write(b'Content-Type: text/html;charset=utf-8\n\n') sys.stdout.buffer.write(resHtml.encode("utf-8"))
The page in Confluence contains three macro HTML include with different parameters (see the code below) and in the simplest version looks like this:
Page code in wiki markup format - here you can see the parameters
<h1> </h1> <p> , </p> <h1> </h1> <ul> <li> <ac:structured-macro ac:macro-id="..." ac:name="html-include" ac:schema-version="1"> <ac:parameter ac:name="url"> <ri:url ri:value="http://z14-0510-wiksap.vesta.ru/misc/sign_btn.py?signee=boss&actual=korolevmv&id=wikitools_sign"/> </ac:parameter> </ac:structured-macro> </li> <li> <ac:structured-macro ac:macro-id="..." ac:name="html-include" ac:schema-version="1"> <ac:parameter ac:name="url"> <ri:url ri:value="http://z14-0510-wiksap.vesta.ru/misc/sign_btn.py?signee=korolevmv&actual=korolevmv&id=wikitools_sign"/> </ac:parameter> </ac:structured-macro> </li> <li> <ac:structured-macro ac:macro-id="..." ac:name="html-include" ac:schema-version="1"> <ac:parameter ac:name="url"> <ri:url ri:value="http://z14-0510-wiksap.vesta.ru/misc/sign_btn.py?signee=maxvar&actual=korolevmv&id=wikitools_sign"/> </ac:parameter> </ac:structured-macro> </li> </ul>
In order for the described scheme to work, it is necessary to take into account the following
Sometimes administrators “hide” it - there are a lot of additional troubles for it (the other side of the possibilities).
This macro is free and is part of Confluence - if you don’t have one, contact the administrators, let them look ...
Confluence will not execute scripts hosted on unknown sources, the host on which the script is hosted should be in the so-called "white lists" (contact the admins). This applies only to the "button" script - the script of the handler is called by the button, the Confluence restrictions do not apply to it.
Scripts (buttons and a handler) must be executable - copy the URL to the browser, it should work out and generate HTML, if this did not happen, debug it.
If any of the scripts crashes with an error - you will not see anything good, debug properly before publishing to Confluence.
An inquiring mind could pay attention to script parameters - on the one hand, they are redundant (for example, the name of the page on which the approval button is placed - is it known, why fill it in?). On the other hand, they are unsafe (the “intruder” in our example can change the name of the approver to his own or delete the reconciliation button at all).
Redundancy can be "overcome" by user macros (I have been wondering for a long time - how can they be practically useful? I will write briefly in a separate article). Security in Confluence is ensured by the history of the page - all “moves” are recorded, you can change anything, in Confluence there remains a wonderful “audit trail” that allows you to understand in detail who changed what and when.
In our practice, there have been cases when for collecting data we had to parse the page code (for example, this is how we managed portfolio management). It is possible and not even too difficult. Evolutionarily, we have come to the conclusion that if some information on a page is needed only for code that interprets this information, then this information is easier to immediately formulate in the code itself (in the form of JSON, for example): editing it in the page editing mode is quite simple , it is drawn by the program, but the savings on parsing and increased reliability are significant.
Why else did we use these macros (as ideas - suddenly something proves useful), very briefly
Page design: if you want to design the page in a non-standard way - markup using CSS, which is contained in the HTML block
Vacation calendar : we use some simple JS calendar, fill it with data from JSON, which we edit directly in the page code, we get a beautiful plate with vacations broken down (year, month, week).
Printing task cards for a scrum board : we set the parameters (task numbers in Jira and additional attributes) in the form right on the wiki page (in the HTML block), the server side generates cards in a form suitable for sending to a printer.
Project portfolio management : we had such an idea. Maps were scored directly to the wiki (a scor map is a specially marked page), a large-block plan was compiled from these scor maps, which was beautifully drawn in the form of a gantt chart.
Glossary : the ability to visualize terms - explanations for individual words of a page - from a common dictionary and issue dictionary entries in the form of "pop-ups". The dictionary itself is automatically collected at the bottom of the page.
Charts : if you need to draw some kind of simple chart, then it’s very simple to do this by embedding the HTML block with any of the standard packages (Google Charts, HighCharts, etc)
Tables : on top of any table in Confluence you can "hang" Datatable - we get a filtered table with "pagination", very nice and convenient.
The list can be continued long enough; during operation, one significant inconvenience was noticed: errors in the server code are poorly caught - 500 errors are poorly visualized and practically do not contain information. Otherwise, experiment - it's safe enough.
The simple way described above to increase the dynamism of Confluence can solve many different problems. To use it does not require special tools and skills. Use is safe enough. Recommend.
In the next article I will talk about the experience of using custom macros in Confluence - which, in my opinion, can really be improved with their help.
Everything is possible in programming - a matter of time and motivation only.
Source: https://habr.com/ru/post/460929/
All Articles