الإضافات جيرا: بعض الأمثلة للاختراع الناجح للدراجة



نحن في Mail.ru Group نبذل الكثير من الجهد لتطوير المنتجات الأطلسية وخاصة جيرا. بفضل الجهود التي بذلناها ، شاهدت المكونات الإضافية MyGroovy و JsIncluder و My Calendar و My ToDo وغيرها . نحن نطور ونستخدم بنشاط كل هذه الإضافات داخل الشركة.

نتلقى الكثير من الطلبات من الإدارات ذات الصلة لتقديم ميزات جديدة. في بعض الأحيان ، يترجم هذا إلى مكونات إضافية جديدة ، ولكن في أغلب الأحيان نقوم بحل المهام باستخدام مكونات إضافية موجودة ، لأن معظم المهام اليومية يتم تغطيتها بسهولة.

لإجراء الرحلات في المكتب ، كان من الضروري توفير طلبات مع التحقق من الرحلات المتقاطعة. للمختبرين - لعمل آلية لمراقبة مراحل الاختبار مع الشخص المسؤول عن التنفيذ. أراد الدعم الفني الوصول تلقائيًا إلى قاعدة المعرفة.

سأخبرك اليوم كيف ، من خلال الجمع بين المكونات الإضافية ، تمكنت من حل هذه المشكلات.

طلب من "أدلة"


الأدوات:

  • التقويم الخاص بي
  • شبيبة includer

المشكلة


هناك الكثير من "الأدلة" في مكتب مجموعة Mail.ru الذين يقومون بالترتيب مع الضيوف ثم يقومون بتعيين المهام لـ AXO. يحدث في بعض الأحيان أن العديد من الرحلات يمكن أن تتم في نفس الوقت - ثم تذهب عدة مجموعات إلى المكتب في نفس الوقت ، أو يتم رفض دليل واحد ، ويذهب للتفاوض مع الضيوف.

قرار


  1. ظهور في مهمة "فتحات" (التاريخ والوقت من مجموعة من الخيارات المجانية) للاختيار عند إنشاء تطبيق لجولة لليوم - 3 فتحات. على سبيل المثال:

    • 9 صباحًا إلى 10 صباحًا
    • 17: 30-18: 30
    • 20:00 حتي 21:00

    إذا تم تحديد فتحة في مهمة أخرى ، فلا يمكنك تقديمها للاختيار في واحدة جديدة. تحتاج أيضًا إلى القدرة على إزالة الفتحات من الاختيار باليد (في الحالة ، على سبيل المثال ، عندما تكون الرحلات في المكتب مستحيلة من حيث المبدأ).
  2. مظهر التقويم ، يتكون من فتحات حرة ومشغولة ، والتي يمكن مشاركتها على الأدلة.

تطبيق


الخطوة 1 : إضافة الحقول المطلوبة إلى شاشة إنشاء الطلب.

للقيام بذلك ، قم بإنشاء حقل "التاريخ" من نوع التاريخ وحقل "وقت الجولة" لنوع Radiobutton لتحديد قيمة واحدة من 3 خيارات (9: 00-10: 00 ؛ 17: 30-18: 30 ؛ 20: 00-21: 00).

الخطوة 2 : إنشاء التقويم.

صنع تقويم جديد. نهدف من خلال JQL إلى مشروعنا مع الرحلات ،
أشر إلى بدء الحدث في حقل "التاريخ" الذي تم إنشاؤه مسبقًا ، وإضافة حقل "وقت الرحلة" الذي تم إنشاؤه مسبقًا على الشاشة.



حفظ التقويم. الآن يمكن الاطلاع على جولاتنا على التقويم.



الخطوة 3 : نحن نحد من إنشاء الرحلات ونضيف لافتة مع وصلة إلى التقويم.

لتحقيق ذلك ، تحتاج إلى JS ، والذي سيتتبع التغيير في حقل التاريخ. عند تحديد التاريخ ، يجب استبداله في وظيفة jql والحصول على جميع الطلبات لهذا التاريخ ، ثم سنكتشف الوقت الذي يستغرقه وإخفاء هذه الخيارات على الشاشة لتجعل من المستحيل اختيار الوقت المستغرق.


عندما لا تكون هناك طلبات


عندما يكون هناك طلبان في الساعة 9 صباحًا وفي الساعة 20 مساءً

(function($){ /* :  — customfield_19620   — customfield_52500   « »: 9:00-10:00 — 47611 17:30-18:30 — 47612 20:00-21:00 — 47613 */ /*       .       . */ $("input[name=customfield_19620]").on("click change", function(e) { var idOptions = []; var url = "/rest/api/latest/search"; /*  «»  ,    . */ if (!$("#customfield_19620").val()) { $('input:radio[name=customfield_52500]').closest('.group').hide(); } /*              jql ,        . */ else { var temp = $("#customfield_19620").val(); var arrDate = temp.split('.'); var result = "" + arrDate[2].trim() + "-" + arrDate[1].trim() + "-" + arrDate[0].trim(); $('input:radio[name=customfield_52500][value="-1"]').parent().remove(); $('input:radio[name=customfield_52500]').closest('.group').show(); $('input:radio[name=customfield_52500][value="47611"]').parent().show(); $('input:radio[name=customfield_52500][value="47612"]').parent().show(); $('input:radio[name=customfield_52500][value="47613"]').parent().show(); /*    jql. */ var params = { jql: "issuetype = Events and cf[52500] is not EMPTY and cf[19620] = 20" + result, fields: "customfield_52500" }; /*    JSON           . */ $.getJSON(url, params, function (data) { var issues = data.issues for (var i = 0; i < issues.length; i++) { idOptions.push(issues[i].fields.customfield_52500.id) } for (var k = 0; k < idOptions.length; k++) { $('input:radio[name=customfield_52500][value=' + idOptions[k] + ']').parent().hide(); } }); } }); /*      . */ $('div.field-group:has(#customfield_19620)').last().before(` <div id="bannerWithInfo" class="aui-message info"> <p class="title">     </p> <p>   </p> <p>      </p> <p>         </p> <p><a href='https://jira.ru/secure/MailRuCalendar.jspa#calendars=492' target="_blank"> </a></p> </div> `); })(AJS.$); 

طلب من اختبار


الأداة:

  • بلدي رائع

المشكلة


في الطلب ، تحتاج إلى تكوين عرض مراحل الاختبار مع الإشارة إلى الموظف المسؤول عن المهمة. يجب أن نرى أن المرحلة لم تكتمل بعد ، أو أن المرحلة اكتملت (ومن قام بها).

قرار


قم بتكوين حقل نوع الحقل النصي لعرض مراحل الاختبار والربط بسير العمل ، سجل في الانتقال المسؤول عن مرحلة المؤلف.

تطبيق


  1. قم بإنشاء حقل "تقدم" من نوع الحقل النصي.
  2. قم بإنشاء حقول من النوع UserPicker تتوافق مع مراحل الاختبار.

    على سبيل المثال ، حدد الخطوات التالية وقم بإنشاء حقول UserPicker بنفس الأسماء:

    • المعلومات الأساسية التي تم جمعها
    • مترجمة
    • السجلات التي تم جمعها
    • مستنسخة
    • وجدت المسؤول

  3. لقد أنشأنا سير العمل بحيث يملأ الأشخاص المسؤولون التحولات.

    على سبيل المثال ، يكتب الانتقال "المترجمة" currentUser إلى حقل UserPicker "المترجمة".
  4. تخصيص العرض باستخدام حقل نصي.

ملء كتلة رائع:

 import com.atlassian.jira.component.ComponentAccessor import com.atlassian.jira.config.properties.APKeys baseUrl = ComponentAccessor.getApplicationProperties().getString(APKeys.JIRA_BASEURL) colorApprove = "#D2F0C2" colorNotApprove = "#FDACAC" return getHTMLApproval() def getHTMLApproval(){ def approval = getApproval() def html = "<table class='aui'>" approval.each{k,v-> html += """<tr> <td ${v?"bgcolor='${colorApprove}'":"bgcolor='${colorNotApprove}'"}>${k}</td> <td ${v?"bgcolor='${colorApprove}'":"bgcolor='${colorNotApprove}'"}>${v?displayUser(v):""}</td> </tr>""" } html += "</table>" return html } def displayUser(user){ "<a href=${baseUrl}/secure/ViewProfile.jspa?name=${user.name}>${user.displayName}</a>" } def getApproval(){ def approval = [:] as LinkedHashMap if (issue.getIssueTypeId() == '10001'){ //  -  approval.put("  ", getCfValue(54407)) approval.put(" ", getCfValue(54409)) approval.put("", getCfValue(54410)) approval.put(" ", getCfValue(54411)) approval.put("", getCfValue(54408)) } return approval } def getCfValue(id){ ComponentAccessor.customFieldManager.getCustomFieldObject(id).getValue(issue) } 

في كتلة السرعة ، اطبع قيمة $. نحصل على النتيجة التالية:



طلب الدعم


الأدوات:

  • شبيبة includer
  • بلدي رائع

المشكلة


الدعم الفني لديه قاعدة المعرفة الخاصة به على التقاء. تحتاج إلى القدرة على عرض مقالات قاعدة المعارف ذات الصلة في استعلام جيرا. نحتاج أيضًا إلى آلية للحفاظ على تحديث قاعدة البيانات - إذا لم تكن المقالة مفيدة ، فستحتاج إلى طلب كاتب تقني في Jira لكتابة المقالة الحالية. عند إغلاق الطلب ، يجب أن تبقى المقالات المتعلقة بالطلب فقط. يمكن أن تكون الروابط مرئية فقط للدعم الفني.

قرار


عند اختيار نوع معين من الوصول في Jira (حقل النوع المتتالي) ، يجب أن يعرض الاستعلام مقالات مع Confluence التي تتوافق معها في حقل منفصل مع علامة wiki.

إذا تم استخدامها بنجاح ، يتم تحديد المقالة على أنها ذات صلة باستخدام علامة الاختيار.

عند حل مشكلة ، إذا لم يتم توضيحها في المقالة المرفقة ، فيجب إنشاء مهمة في جيرا بنوع "الوثائق" المرتبطة بالطلب الحالي.

تطبيق


الخطوة 1 : الإعداد

  1. إنشاء حقل نص (متعدد الأسطر) مع علامات ويكي - الروابط.
  2. قم بإنشاء حقل من النوع حدد قائمة (تتالي) - "نوع الاتصال".

    على سبيل المثال ، نستخدم القيم التالية:

    • ACCOUNT
    • HARDWARE
  3. سنقوم بإعداد تصنيفات للمقالات التي تربط المقالات حول التقاء مع الاستعلامات في جيرا:

    • تغيير عضوية المجموعة الإعلانية - officeit_jira_ad_group_addresses_ad
    • الاشتراك / إلغاء الاشتراك في رسالة إخبارية - officeit_jira_subscription_subscription_of_subscription
    • منح حق الوصول إلى المجلد - officeit_jira_sharing_access_to_folder
    • إعادة تعيين كلمة المرور من المجال KM - officeit_jira_reset_password_of_domain_uz
    • إعادة تعيين كلمة مرور البريد - officeit_jira_reset_password_mail_post
    • إصدار المعدات المؤقتة - officeit_jira_ إصدار المعدات المؤقتة
    • إصدار معدات جديدة - officeit_jira_new__new_technique
    • استبدال القرص الصلب وتثبيت النظام من نقطة الصفر - officeit_jira_replace_hard_drive_and_install_system_s_ صفر
    • استبدال القرص الصلب بنقل المعلومات - officeit_jira_replacing_hard_drive_with_data transfer_information
    • استبدال المعدات التالفة / القديمة - officeit_jira_replacing_ faulty_ المعدات القديمة

    بعد ذلك ، تحتاج إلى إنشاء مقالات حول التقاء ، ووضع ملصقات لها.
  4. تحضير سير العمل

    سيتم ملء نوع الاستئناف عند الإنشاء.

    تتم إضافة الروابط إلى شاشة منفصلة وتوضع عند النقل في نهايته (في المثال ، يسمى الانتقال "التحقق من الارتباطات الفعلية") ، نتذكر معرف النقل (ضروري في المستقبل لتهيئة js).

الخطوة 2 : وظيفة MyGroovy اللاحقة (إضافة مقالات إلى الطلب)

 /* :   — customfield_40001 Links — customfield_50001 */ /*  ,      . */ def usr = "bot" def pas = "qwerty" def url = "https://confluence.ru" def browse = "/pages/viewpage.action?pageId=" /*   */ def updateCustomFieldValue(issue, Long customFieldId, newValue) { def customField = ComponentAccessor.customFieldManager.getCustomFieldObject(customFieldId) customField.updateValue(null, issue, new ModifiedValue(customField.getValue(issue), newValue), new DefaultIssueChangeHolder()) return issue } def getCustomFieldObject(Long fieldId) { ComponentAccessor.customFieldManager.getCustomFieldObject(fieldId) } def parseText(text) { def jsonSlurper = new JsonSlurper() return jsonSlurper.parseText(text) } def getCustomFieldValue(issue, Long fieldId) { issue.getCustomFieldValue(ComponentAccessor.customFieldManager.getCustomFieldObject(fieldId)) } /*  ,      . */ def getLabelFromMap(String main, String sub){ def mapLabels = [ "ACCOUNT": [ "    AD" :["officeit_jira_____ad"], "/  " :["officeit_jira____"], "   " :["officeit_jira____"], "    " :["officeit_jira_____"], "   " :["officeit_jira____"] ], "HARDWARE": [ "  " :["officeit_jira___"], "  " :["officeit_jira___"], "       ":["officeit_jira________"], "     ":["officeit_jira______"], " / ":["officeit_jira____"] ] ] def labels = mapLabels[main][sub] def result = "" if(!labels){ return "" } for (def i=0;i<labels.size;i++){ if(i<labels.size-1){ result += "\"" +labels[i]+ "\"," }else{ result += "\"" +labels[i]+ "\"" } } result = URLEncoder.encode(result, "utf-8") return result } /*    —  . */ def wikiLinkFieldId = 50001L def requestTypeFieldValue = getCustomFieldValue(issue, 40001) if(!requestTypeFieldValue){ return "required field is empty" } def mainType = requestTypeFieldValue.getAt(null).toString() def subType = requestTypeFieldValue.getAt('1').toString() /*     ,       : [TEST    1 (    AD)|https://confluence.ru/pages/viewpage.action?pageId=500001]. */ String labels = getLabelFromMap(mainType,subType) if(labels==""){ return "no avalible position on LabelMap" } def api = "/rest/api/content/search?cql=label%20in(${labels})" def URL = (url+api) def wikiString = "" def resp = "curl -u ${usr}:${pas} -X GET ${URL}".execute().text def result = parseText(resp) def ids = result.results.id def title = result.results.title for (def i=0;i<ids.size;i++){ wikiString += "[${title[i]}|${url+browse+ids[i]}]\n" } updateCustomFieldValue(issue,wikiLinkFieldId,wikiString) return "Done" 



الخطوة 3 : JS النصي

 /* :  — Check actual Links id  — 10 Links — customfield_50001 */ (function($){ /*   ,    ,                . */ var buttonNewArticle = '  '; var buttonDeleteUnchecked = ' '; var buttonNewArticleTitle = '      '; var buttonDeleteUncheckedTitle = '    .'; var avalibleTransitions = [10]; var currentTransition = parseInt(AJS.$('.hidden input[name^="action"]').val()); if(avalibleTransitions.indexOf(currentTransition)==-1){ console.log('Error: transition ' + currentTransition + ' is not avalible'); return; } var customFieldId = 50001; var labelTxt = '  '; var idname = 'cblist'; var checkboxCounter = 'cbsq'; var text = '<div class="field-group"><label for="'+idname+'">' + labelTxt +'</label><div id="'+idname+'"></div></div>' AJS.$('.field-group label[for^="customfield_'+customFieldId+'"]').parent().hide(); AJS.$('.field-group label[for^="comment"]').parent().hide(); $('.jira-dialog-content div.form-body').prepend(text); /*    : */ /* renameButtonNeedNewArticle  renameButtonDeleteUnchecked —   « »            addCheckbox —     . */ function arrayToString(arrays) { return arrays.join('\n'); } function renameButtonNeedNewArticle() { $('#issue-workflow-transition-submit').val(buttonNewArticle); $('#issue-workflow-transition-submit').attr("title",buttonNewArticleTitle); } function renameButtonDeleteUnchecked() { $('#issue-workflow-transition-submit').val(buttonDeleteUnchecked); $('#issue-workflow-transition-submit').attr("title",buttonDeleteUncheckedTitle); } function addCheckbox(array) { var value = array.join('|'); var name = array[0].replace('[',''); var link = array[1].replace(']',''); var container = $('#'+idname); var inputs = container.find('input'); var id = inputs.length+1; $('<input />', { type: 'checkbox', id: checkboxCounter+id, value: value }).appendTo(container); $('<label />', { for: checkboxCounter+id, text: ' ' }).appendTo(container); $('<a />', { href: link, text: name,target: "_blank" }).appendTo(container); $('<br>').appendTo(container); } /*       ,   : */ renameButtonNeedNewArticle(); $(document).ready(function() { var val = AJS.$('#customfield_'+customFieldId+'').val(); AJS.$('#customfield_'+customFieldId+'').val(''); if(val==""){return;} var i = val.split('\n'); i.forEach(function( index ) { if(index == ""){return;} var link = index.split('|'); addCheckbox(link); }); }); /*          Links. */ $('#'+idname+' input[type="checkbox"]').change(function() { var prevalue = []; AJS.$('#'+idname+' input:checkbox:checked').each(function(){ prevalue.push(this.value); }); AJS.$('#customfield_'+customFieldId+'').val(arrayToString(prevalue)); if(prevalue.length<1){ renameButtonNeedNewArticle(); }else{ renameButtonDeleteUnchecked(); } }); })(AJS.$); 

هذا هو ما يشبه انتقالنا قبل معالجة JS.



هذا هو الانتقال بعد المعالجة.



وهكذا ، إذا تم اختيار مقال أو أكثر.



بعد اكتمال النقل ، سيتم الكتابة فوق حقل الارتباطات بالقيمة الجديدة.

الخطوة 4 : وظيفة MyGroovy اللاحقة (إنشاء طلب لمقال جديد)

في التحقق من انتقال الارتباطات الفعلي ، نكتب نصًا ينشئ طلبًا من نوع "الوثائق" إذا لم تكن هناك قيم في حقل الارتباطات.

في الختام


هذه الحلول لم تكن لتظهر بدون المشاركة النشطة للزملاء - في المقام الأول أولئك الذين يستخدمون بنشاط الأدوات الجاهزة أو الذين يواجهون مهام تحتاج إلى أتمتة في عملهم. غالبًا ما يتبين أن المهمة المهمة هي بالفعل نصف الحل: فأنت بحاجة فقط إلى اختيار الأداة التي تلبي احتياجاتك بأكبر قدر من الكفاءة وبسهولة وسهولة (للمستخدم النهائي). الآن ، ربما ، لديك أسئلة واقتراحات يمكن أن تجعل المكونات الإضافية المقدمة أفضل - اكتب في التعليقات.

Source: https://habr.com/ru/post/ar476446/


All Articles