Atlassian合流:可在python中扩展

在Alfastrakhovanie中,我们积极使用“ Wiki”,其引擎是Atlassian Confluence。 第一次认真尝试(试图在其中创建内容)时,我缺乏“动态性”-我希望能够以编程方式形成页面的一部分,并与其他系统进行交互等。


有一段时间,他把头撞在不同的墙壁上,但是后来他看到“房子里没有一堵墙壁”。 我想分享我的经验-如何在Confluence中添加发言人。 我希望这对使用它的人有用。 而且,像往常一样,对每个人都有好奇心。


动态维基


最初,Confluence提出了一种扩展其功能的通用方法-插件。 他可能很好,只有一个缺点-较高的入门门槛(您需要学习很多知识)。


简要回顾一下(按空间标准)后,有两种更简单的方法来扩展其功能:标准宏“ HTML”和“ HTML Include”,在本文中,我将更详细地介绍后者。


这些方法有一个操作原理-HTML代码嵌入在Confluence页面中,它可以是静态的(对于非标准页面格式,这也可能是有趣的),可以是动态的(包括服务器组件)。


示例:对帐请求


让我们通过一个简单的示例(文档批准列表)来看看扩展Confluence功能的建议方法。


我们想要做的是:在页面上添加批准列表,以便每个人都可以看到谁应就该文档达成一致,是否达成一致以及何时达成一致。


为方便起见,我们将列表项设为按钮,并在其中输入协调器的名称。 如果协调员查看了该页面,则该按钮将处于活动状态,而在所有其他情况下,该按钮将不处于活动状态。


批准后,按钮“将不再是按钮”-而不是批准后的按钮,我们在页面上以文本形式(批准者名称和批准日期)绘制批准事实。 在最草稿的版本(无设计)中,可能看起来像这样:


图片


互动方案


图片


评论-运作方式:


  • Confluence中的页面包含多个宏“ html包含”-实际上是带有参数的HTTP GET请求(我们将在下面更详细地考虑)
  • 呈现页面时,这些请求(python脚本)在应用程序服务器上执行,结果(生成的HTML)绘制在页面上
  • 例如,在脚本的工作过程中,如果页面尚未被签名者“签名”,则脚本将使用页面签名的历史记录与数据库联系-HTML将包含一个按钮(如上所述)
  • 当您单击按钮时,将提交-另一个python脚本被调用(用于处理对帐事实的脚本)
  • 该脚本将有关登录事实的信息保存到数据库中,并将用户重定向到原始页面(渲染时将考虑签名事实-单击“签名”按钮)

言语有点复杂,让我们尝试澄清代码。


所以我们需要创建两个脚本


  • 用于形成“签名”按钮的脚本(我将其称为“按钮”脚本)
  • 一个用于计算签名事实的脚本(对按钮单击的反应-我将其称为“处理程序”)

让我们从按钮开始创建它们。


脚本按钮


从原理上讲,按钮脚本是如何工作的:


date = getSignDate() if date: #       . else: if user==signee: #    else: #  inactive  

该脚本应该是有效的HTML文档,其主体以最小的形式是一个表单


  • 表单的动作包含脚本的URL
  • 活动按钮已提交
  • 隐藏的表单字段包含处理程序的其他参数(在我们的示例中,页面名称和签名者登录名)

脚本的大概视图(为简洁起见,我丢弃了所有不必要的内容-参见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")) 

脚本“处理程序”


处理程序的任务非常简单-修复按下“同意”按钮的事实。 然后重定向回请求来自的页面-呈现页面时,已经考虑到协调的事实(请参见上面的描述)。


一个示例(缩写)脚本“ handler”(完整版本-请参见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")) 

汇合页


Confluence中的页面包含三个带有不同参数的宏HTML包含(请参见下面的代码),最简单的版本如下所示:


图片


Wiki标记格式的页面代码-在这里您可以看到参数


 <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&amp;actual=korolevmv&amp;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&amp;actual=korolevmv&amp;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&amp;actual=korolevmv&amp;id=wikitools_sign"/> </ac:parameter> </ac:structured-macro> </li> </ul> 

关于设置的一点


为了使所描述的方案起作用,有必要考虑以下因素


HTML包含宏的可用性


有时,管理员“隐藏”它-这样做有很多其他麻烦(可能性的另一面)。


此宏是免费的,并且是Confluence的一部分-如果您没有宏,请与管理员联系,让他们看看...


融合白名单


Confluence不会执行托管在未知来源上的脚本,托管脚本的主机应位于所谓的“白名单”中(请与管理员联系)。 这仅适用于“按钮”脚本-处理程序的脚本已由按钮调用,Confluence限制不适用于该按钮。


脚本执行


脚本(按钮和处理程序)必须是可执行的-将URL复制到浏览器,它应该可以工作并生成HTML,如果没有发生,请对其进行调试。


脚本错误


如果任何脚本因错误而崩溃-您将看不到任何好消息,请在将其发布到Confluence之前进行正确的调试。


脚本选项


询问者可能会注意脚本参数-一方面,它们是多余的(例如,放置批准按钮的页面名称-是否已知,为什么要填写?)。 另一方面,它们是不安全的(在我们的示例中,“攻击者”可以将批准者的名称更改为自己的名称,也可以完全删除对帐按钮)。


冗余可以被用户宏“克服”(我很久以来一直想知道-它们在实际中如何有用?我将在另一篇文章中简短地写一下)。 页面历史记录确保了Confluence的安全性-记录了所有“动作”,您可以更改任何内容,在Confluence中,还有一个很棒的“审核线索”,可让您详细了解谁更改了内容和时间。


页面互动


在我们的实践中,有些情况下我们不得不解析页面代码以收集数据(例如,这就是我们管理项目组合的方式)。 这是可能的,甚至不是太困难。 从进化上,我们得出的结论是,如果仅页面上的某些信息仅用于解释该信息的代码,则此信息更易于立即在代码本身中表达(例如,以JSON的形式):以页面编辑模式进行编辑非常简单,它是由程序绘制的,但在解析和节省的可靠性方面可节省大量资金。


更多例子


为什么我们还要简短地使用这些宏(作为想法-突然证明有用)


页面设计:如果要以非标准方式设计页面-使用CSS标记,HTML包含在HTML块中


假期日历 :我们使用一些简单的JS日历,将JSON中的数据填充到其中,然后直接在页面代码中进行编辑,我们得到了一个漂亮的图板,其中列出了假期(年,月,周)的详细信息。


打印Scrum板的任务卡 :我们在Wiki页面(HTML块)中的表单中设置参数(Jira中的任务号和其他属性),服务器端以适合发送给打印机的形式形成卡片。


项目组合管理 :我们有这样的想法。 在Wiki上对地图进行了评分(分数地图是一个特殊标记的页面),这些地图收集了一个大块计划,该计划以甘特图的形式绘制精美。


词汇表 :可以从通用词典中可视化术语(对页面各个单词的解释)并以“弹出窗口”的形式发布词典条目的能力。 字典本身会自动收集在页面底部。


图表 :如果您需要绘制某种简单的图表,则可以通过将HTML块嵌入到任何标准包(Google图表,HighCharts等)中来完成此操作,这非常简单


表格 :在Confluence中任何表格的顶部,您都可以“挂起”数据表-我们获得了带有“分页”功能的经过过滤的表格,非常方便。


该列表可以持续足够长的时间,在操作过程中,注意到一个重大的不便之处:服务器代码中的错误被捕获得很差-500个错误的可视化效果很差,并且实际上不包含任何信息。 否则,请进行实验-足够安全。


总结一下


上面描述的增加Confluence的动态性的简单方法使您能够解决许多不同的问题。 要使用它,不需要特殊的工具和技能。 使用足够安全。 我推荐。


在下一篇文章中,我将讨论在Confluence中使用自定义宏的经验-在我看来,借助它们的帮助确实可以改善这种体验。


编程中的一切皆有可能-仅是时间和动力问题。

Source: https://habr.com/ru/post/zh-CN460929/


All Articles