浏览代码

HPCC-12294 Add option to use password on ZAP files

The existing code for creating ZAP report are revised
to support an option of using password when creating
the ZAP files. Linux ZIP command is called to compress
the ZAP files with a password option.

Signed-off-by: wangkx <kevin.wang@lexisnexis.com>
wangkx 10 年之前
父节点
当前提交
ce892fe345

+ 9 - 1
esp/eclwatch/ws_XSLT/WUZAPInfoForm.xslt

@@ -66,7 +66,8 @@
                                 var desc = document.getElementById("ProblemDescription").value;
                                 var history = document.getElementById("WhatChanged").value;
                                 var timing = document.getElementById("WhereSlow").value;
-                                opener.createZAPInfo(wuid, espIP, thorIP, buildVersion, desc, history, timing);
+                                var password = document.getElementById("Password").value;
+                                opener.createZAPInfo(wuid, espIP, thorIP, buildVersion, desc, history, timing, password);
                             }
                             window.close();
                         }
@@ -81,6 +82,7 @@
                         document.getElementById("ProblemDescription").value = "";
                         document.getElementById("WhatChanged").value = "";
                         document.getElementById("WhereSlow").value = "";
+                        document.getElementById("Password").value = "";
                     }
                     ]]></xsl:text>
                 </script>
@@ -178,6 +180,12 @@
                             <tr>
                                 <td></td>
                                 <td>
+                                    Password to open ZAP(optional):<input type="password" id="Password" name="Password"/>
+                                </td>
+                            </tr>
+                            <tr>
+                                <td></td>
+                                <td>
                                     <input type="submit" value="Create Report" name="Report" onclick="onReport()"/>
                                 </td>
                             </tr>

+ 12 - 2
esp/eclwatch/ws_XSLT/wuid.xslt

@@ -750,18 +750,28 @@
                         mywindow.focus();
                         return false;
                     }
-                    function createZAPInfo(wuid, espIP, thorIP, ESPBuildVersion, problemDesciption, history, timingInfo)
+                    function createZAPInfo(wuid, espIP, thorIP, ESPBuildVersion, problemDesciption, history, timingInfo, password)
                     {
                         document.getElementById("ESPIPAddress").value=espIP;
                         if (thorIP != '')
-                            document.getElementById("ESPIPAddress").value=thorIP;
+                            document.getElementById("ThorIPAddress").value=thorIP;
                         document.getElementById("BuildVersion").value=ESPBuildVersion;
                         if (problemDesciption != '')
                             document.getElementById("ProblemDescription").value=problemDesciption;
+                        else
+                            document.getElementById("ProblemDescription").value = "";
                         if (history != '')
                             document.getElementById("WhatChanged").value=history;
+                        else
+                            document.getElementById("WhatChanged").value = "";
                         if (timingInfo != '')
                             document.getElementById("WhereSlow").value=timingInfo;
+                        else
+                            document.getElementById("WhereSlow").value = "";
+                        if (password != '')
+                            document.getElementById("Password").value=password;
+                        else
+                            document.getElementById("Password").value = "";
 
                         document.forms['protect'].action = "/WsWorkunits/WUCreateZAPInfo";
                         document.forms['protect'].encType="application/x-www-form-urlencoded";

+ 1 - 0
esp/eclwatch/ws_XSLT/wuidcommon.xslt

@@ -831,6 +831,7 @@
               <input type="hidden" id="ProblemDescription" name="ProblemDescription" value=""/>
               <input type="hidden" id="WhatChanged" name="WhatChanged" value=""/>
               <input type="hidden" id="WhereSlow" name="WhereSlow" value=""/>
+              <input type="hidden" id="Password" name="Password" value=""/>
               <input type="button" name="Type" value="Save" class="sbutton" onclick="updateWorkunit('{$wuid}');">
                         <xsl:if test="number(AccessFlag) &lt; 7">
                           <xsl:attribute name="disabled">disabled</xsl:attribute>

+ 1 - 0
esp/scm/ws_workunits.ecm

@@ -1539,6 +1539,7 @@ ESPrequest [nil_remove] WUCreateZAPInfoRequest
     string ProblemDescription;
     string WhatChanged;
     string WhereSlow;
+    [min_ver("1.53")] string Password;
 };
 
 ESPresponse [exceptions_inline] WUCreateZAPInfoResponse

+ 191 - 157
esp/services/ws_workunits/ws_workunitsService.cpp

@@ -3880,8 +3880,34 @@ bool CWsWorkunitsEx::onWUDeployWorkunit(IEspContext &context, IEspWUDeployWorkun
     }
     return true;
 }
-#ifdef _USE_ZLIB
-void CWsWorkunitsEx::addProcessLogfile(IZZIPor* zipper, Owned<IConstWorkUnit> &cwu, WsWuInfo &winfo, const char * process, PointerArray &mbArr)
+
+
+void CWsWorkunitsEx::createZAPFile(const char* fileName, size32_t len, const void* data)
+{
+    if (!fileName || !*fileName)
+        throw MakeStringException(ECLWATCH_CANNOT_COMPRESS_DATA,"File name not specified.");
+    Owned<IFile> wuInfoIFile = createIFile(fileName);
+    Owned<IFileIO> wuInfoIO = wuInfoIFile->open(IFOcreate);
+    if (wuInfoIO)
+        wuInfoIO->write(0, len, data);
+}
+
+void CWsWorkunitsEx::cleanZAPFolder(IFile* zipDir, bool removeFolder)
+{
+    if (!zipDir)
+        throw MakeStringException(ECLWATCH_CANNOT_COMPRESS_DATA,"Invalid file interface for the zip folder.");
+    Owned<IDirectoryIterator> iter = zipDir->directoryFiles(NULL,false,false);
+    ForEach(*iter)
+    {
+        OwnedIFile thisFile = createIFile(iter->query().queryFilename());
+        if (thisFile->isFile() == foundYes)
+            thisFile->remove();
+    }
+    if (removeFolder)
+        zipDir->remove();
+}
+
+void CWsWorkunitsEx::addProcessLogfile(Owned<IConstWorkUnit>& cwu, WsWuInfo& winfo, const char* process, const char* path)
 {
     Owned<IPropertyTreeIterator> procs = cwu->getProcesses(process, NULL);
     ForEach (*procs)
@@ -3893,189 +3919,198 @@ void CWsWorkunitsEx::addProcessLogfile(IZZIPor* zipper, Owned<IConstWorkUnit> &c
             continue;
         StringBuffer pid;
         pid.appendf("%d",proc.getPropInt("@pid"));
-        MemoryBuffer * pMB = NULL;
+        MemoryBuffer mb;
         try
         {
-            pMB = new MemoryBuffer;
-            if (0 == stricmp(process, "EclAgent"))
-                winfo.getWorkunitEclAgentLog(logSpec.str(), pid.str(), *pMB);
-            else if (0 == stricmp(process, "Thor"))
-                winfo.getWorkunitThorLog(logSpec.str(), *pMB);
-            else
-            {
-                delete pMB;
-                return;
-            }
-            mbArr.append(pMB);
+            if (strieq(process, "EclAgent"))
+                winfo.getWorkunitEclAgentLog(logSpec.str(), pid.str(), mb);
+            else if (strieq(process, "Thor"))
+                winfo.getWorkunitThorLog(logSpec.str(), mb);
         }
-
         catch(IException *e)
         {
             StringBuffer s;
             e->errorMessage(s);
-            pMB->append(s.str());
+            DBGLOG("Error accessing Process Log file %s: %s", logSpec.str(), s.str());
+            mb.append(s.str());
             e->Release();
-            mbArr.append(pMB);
         }
 
-        if (pMB && pMB->length())
+        if (!mb.length())
+            continue;
+
+        const char * logName = logSpec.str();
+        for (const char * p=logSpec; *p; p++)
         {
-            const char * logName = logSpec.str();
-            for (const char * p=logSpec; *p; p++)
-            {
-                if (*p == '\\' || *p == '/')
-                    logName = p+1;
-            }
-            zipper->addContentToZIP(pMB->length(), pMB->bufferBase(), (char*)logName, true);
+            if (*p == '\\' || *p == '/')
+                logName = p+1;
         }
+        VStringBuffer fileName("%s%c%s", path, PATHSEPCHAR, logName);
+        createZAPFile(fileName.str(), mb.length(), mb.bufferBase());
+    }
+}
+
+void CWsWorkunitsEx::createZAPWUInfoFile(IEspWUCreateZAPInfoRequest &req, Owned<IConstWorkUnit>& cwu, const char* pathNameStr)
+{
+    SCMStringBuffer temp;
+    StringBuffer sb;
+    sb.append("Workunit:     ").append(cwu->getWuid(temp)).append("\r\n");
+    sb.append("User:         ").append(cwu->getUser(temp).str()).append("\r\n");
+    sb.append("Build Version:").append(req.getBuildVersion()).append("\r\n");
+    sb.append("Cluster:      ").append(cwu->getClusterName(temp).str()).append("\r\n");
+    if (req.getESPIPAddress())
+        sb.append("ESP:          ").append(req.getESPIPAddress()).append("\r\n");
+    if (req.getThorIPAddress())
+        sb.append("Thor:         ").append(req.getThorIPAddress()).append("\r\n");
+    //Exceptions/Warnings/Info
+    Owned<IConstWUExceptionIterator> exceptions = &cwu->getExceptions();
+    StringBuffer info, warn, err, alert;
+    ForEach(*exceptions)
+    {
+        switch (exceptions->query().getSeverity())
+        {
+        case ExceptionSeverityInformation:
+            info.append("\t").append(exceptions->query().getExceptionMessage(temp)).append("\r\n\r\n");
+            break;
+        case ExceptionSeverityWarning:
+            warn.append("\t").append(exceptions->query().getExceptionMessage(temp)).append("\r\n\r\n");
+            break;
+        case ExceptionSeverityError:
+            err.append("\t").append(exceptions->query().getExceptionMessage(temp)).append("\r\n\r\n");
+            break;
+        case ExceptionSeverityAlert:
+            alert.append("\t").append(exceptions->query().getExceptionMessage(temp)).append("\r\n\r\n");
+            break;
+        }
+    }
+    if (err.length())
+        sb.append("Exceptions:   ").append("\r\n").append(err);
+    if (warn.length())
+        sb.append("Warnings:     ").append("\r\n").append(warn);
+    if (info.length())
+        sb.append("Information:  ").append("\r\n").append(info);
+    if (alert.length())
+        sb.append("Alert:        ").append("\r\n").append(alert);
+
+    //User provided Information
+    sb.append("Problem:      ").append(req.getProblemDescription()).append("\r\n\r\n");
+    sb.append("What Changed: ").append(req.getWhatChanged()).append("\r\n\r\n");
+    sb.append("Timing:       ").append(req.getWhereSlow()).append("\r\n\r\n");
+
+    VStringBuffer fileName("%s.txt", pathNameStr);
+    createZAPFile(fileName.str(), sb.length(), sb.str());
+}
+
+void CWsWorkunitsEx::createZAPWUXMLFile(WsWuInfo &winfo, const char* pathNameStr)
+{
+    MemoryBuffer mb;
+    winfo.getWorkunitXml(NULL, mb);
+    VStringBuffer fileName("%s.xml", pathNameStr);
+    createZAPFile(fileName.str(), mb.length(), mb.bufferBase());
+}
+
+void CWsWorkunitsEx::createZAPECLQueryArchiveFiles(Owned<IConstWorkUnit>& cwu, const char* pathNameStr)
+{
+    Owned<IConstWUQuery> query = cwu->getQuery();
+    if(!query)
+        return;
+
+    //Add archive if present
+    Owned<IConstWUAssociatedFileIterator> iter = &query->getAssociatedFiles();
+    ForEach(*iter)
+    {
+        IConstWUAssociatedFile & cur = iter->query();
+        SCMStringBuffer ssb;
+        cur.getDescription(ssb);
+        if (!strieq(ssb.str(), "archive"))
+            continue;
+
+        cur.getName(ssb);
+        if (!ssb.length())
+            continue;
+
+        StringBuffer fileName, archiveContents;
+        try
+        {
+            archiveContents.loadFile(ssb.str());
+        }
+        catch (IException *e)
+        {
+            StringBuffer s;
+            e->errorMessage(s);
+            DBGLOG("Error accessing archive file %s: %s", ssb.str(), s.str());
+            archiveContents.insert(0, "Error accessing archive file ").appendf("%s: %s\r\n\r\n", ssb.str(), s.str());
+            e->Release();
+        }
+        fileName.setf("%s.archive", pathNameStr);
+        createZAPFile(fileName.str(), archiveContents.length(), archiveContents.str());
+        break;
+    }
+
+    //Add Query
+    SCMStringBuffer temp;
+    query->getQueryText(temp);
+    if (temp.length())
+    {
+        VStringBuffer fileName("%s.ecl", pathNameStr);
+        createZAPFile(fileName.str(), temp.length(), temp.str());
     }
 }
-#endif
 
 bool CWsWorkunitsEx::onWUCreateZAPInfo(IEspContext &context, IEspWUCreateZAPInfoRequest &req, IEspWUCreateZAPInfoResponse &resp)
 {
     try
     {
-#ifndef _USE_ZLIB
-        throw MakeStringException(ECLWATCH_CANNOT_COMPRESS_DATA,"The data cannot be compressed.");
-#else
         Owned<IWorkUnitFactory> factory = getWorkUnitFactory(context.querySecManager(), context.queryUser());
         Owned<IConstWorkUnit> cwu = factory->openWorkUnit(req.getWuid(), false);
         if(!cwu.get())
             throw MakeStringException(ECLWATCH_CANNOT_OPEN_WORKUNIT, "Cannot open workunit %s.", req.getWuid());
 
-        //Create output report file
-        StringBuffer zipFile;
-        StringBuffer userName;
+        StringBuffer userName, nameStr, fileName;
+        StringBuffer zipFileName, zipFileNameWithPath, zipCommand, folderToZIP;
         if (context.queryUser())
             userName.append(context.queryUser()->getName());
-        zipFile.append("ZAPReport_").append(req.getWuid()).append('_').append(userName.str()).append(".zip");
-        SCMStringBuffer temp;
-        StringBuffer sb;
-        sb.append("Workunit:     ").append(cwu->getWuid(temp)).append("\r\n");
-        sb.append("User:         ").append(cwu->getUser(temp).str()).append("\r\n");
-        sb.append("Build Version:").append(req.getBuildVersion()).append("\r\n");
-        sb.append("Cluster:      ").append(cwu->getClusterName(temp).str()).append("\r\n");
-        if (req.getESPIPAddress())
-            sb.append("ESP:          ").append(req.getESPIPAddress()).append("\r\n");
-        if (req.getThorIPAddress())
-            sb.append("Thor:         ").append(req.getThorIPAddress()).append("\r\n");
-        //Exceptions/Warnings/Info
-        Owned<IConstWUExceptionIterator> exceptions = &cwu->getExceptions();
-        StringBuffer info, warn, err, alert;
-        ForEach(*exceptions)
-        {
-            switch (exceptions->query().getSeverity())
-            {
-            case ExceptionSeverityInformation:
-                info.append("\t").append(exceptions->query().getExceptionMessage(temp)).append("\r\n\r\n");
-                break;
-            case ExceptionSeverityWarning:
-                warn.append("\t").append(exceptions->query().getExceptionMessage(temp)).append("\r\n\r\n");
-                break;
-            case ExceptionSeverityError:
-                err.append("\t").append(exceptions->query().getExceptionMessage(temp)).append("\r\n\r\n");
-                break;
-            case ExceptionSeverityAlert:
-                alert.append("\t").append(exceptions->query().getExceptionMessage(temp)).append("\r\n\r\n");
-                break;
-            }
-        }
-        if (err.length())
-            sb.append("Exceptions:   ").append("\r\n").append(err);
-        if (warn.length())
-            sb.append("Warnings:     ").append("\r\n").append(warn);
-        if (info.length())
-            sb.append("Information:  ").append("\r\n").append(info);
-        if (alert.length())
-            sb.append("Alert:        ").append("\r\n").append(alert);
-
-        //User provided Information
-
-        sb.append("Problem:      ").append(req.getProblemDescription()).append("\r\n\r\n");
-        sb.append("What Changed: ").append(req.getWhatChanged()).append("\r\n\r\n");
-        sb.append("Timing:       ").append(req.getWhereSlow()).append("\r\n\r\n");
-
-        //Zip all files together
-        {
-            IZZIPor* zipper = createZZIPor();
-#ifdef _DEBUG
-            zipper->setTraceLevel(100);
-#endif
-            StringBuffer fs;
-            //add report file to ZIP
-            fs.append("ZAPReport_").append(req.getWuid()).append('_').append(userName.str()).append(".txt");
-            zipper->addContentToZIP(sb.length(), (void*)sb.str(), (char*)fs.str(), false);
-
-            //add ECL query/archive to zip
-            Owned<IConstWUQuery> query = cwu->getQuery();
-            StringBuffer eclContents;//String buffers containing file contents must persist until ziptofile is called !
-            StringBuffer archiveContents;//String buffers containing file contents must persist until ziptofile is called !
-            if(query)
-            {
-                //Add archive if present
-                Owned<IConstWUAssociatedFileIterator> iter = &query->getAssociatedFiles();
-                ForEach(*iter)
-                {
-                    IConstWUAssociatedFile & cur = iter->query();
-                    SCMStringBuffer ssb;
-                    cur.getDescription(ssb);
-                    if (0 == stricmp(ssb.str(), "archive"))
-                    {
-                        cur.getName(ssb);
-                        if (ssb.length())
-                        {
-                            fs.clear().append("ZAPReport_").append(req.getWuid()).append('_').append(userName.str()).append(".archive");
-                            try
-                            {
-                                archiveContents.loadFile(ssb.str());
-                                zipper->addContentToZIP(archiveContents.length(), (void*)archiveContents.str(), (char*)fs.str(), true);
-                            }
-                            catch (IException *E)
-                            {
-                                DBGLOG("Error accessing archive file %s", ssb.str());
-                                E->Release();
-                            }
-                            break;
-                        }
-                    }
-                }
-
-                //Add Query
-                query->getQueryText(temp);
-                if (temp.length())
-                {
-                    fs.clear().append("ZAPReport_").append(req.getWuid()).append('_').append(userName.str()).append(".ecl");
-                    eclContents.append(temp.str());
-                    zipper->addContentToZIP(eclContents.length(), (void*)eclContents.str(), (char*)fs.str(), true);
-                }
-            }
-
-            //Add logfiles to ZIP
-            WsWuInfo winfo(context, cwu);
-            PointerArray eclAgentLogs;//array of dynamically allocated MemoryBuffers
-            PointerArray thorLogs;
-
-            addProcessLogfile(zipper, cwu, winfo, "EclAgent", eclAgentLogs);
-            addProcessLogfile(zipper, cwu, winfo, "Thor", thorLogs);
+        nameStr.append("ZAPReport_").append(req.getWuid()).append('_').append(userName.str());
+
+        //create a folder for WU ZAP files
+        const char* zipFolder = "tempzipfiles"PATHSEPSTR;
+        folderToZIP.append(zipFolder).append(nameStr.str());
+        Owned<IFile> zipDir = createIFile(folderToZIP.str());
+        if (!zipDir->exists())
+            zipDir->createDirectory();
+        else
+            cleanZAPFolder(zipDir, false);
+
+        //create WU ZAP files
+        VStringBuffer pathNameStr("%s/%s", folderToZIP.str(), nameStr.str());
+        createZAPWUInfoFile(req, cwu, pathNameStr.str());
+        createZAPECLQueryArchiveFiles(cwu, pathNameStr.str());
+
+        WsWuInfo winfo(context, cwu);
+        createZAPWUXMLFile(winfo, pathNameStr.str());
+        addProcessLogfile(cwu, winfo, "EclAgent", folderToZIP.str());
+        addProcessLogfile(cwu, winfo, "Thor", folderToZIP.str());
+
+        //Write out to ZIP file
+        zipFileName.append(nameStr.str()).append(".zip");
+        zipFileNameWithPath.append(zipFolder).append(zipFileName.str());
+        pathNameStr.set(folderToZIP.str()).append("/*");
+
+        const char* password = req.getPassword();
+        if (password && *password)
+            zipCommand.appendf("zip -j --password %s %s %s", password, zipFileNameWithPath.str(), pathNameStr.str());
+        else
+            zipCommand.appendf("zip -j %s %s", zipFileNameWithPath.str(), pathNameStr.str());
+        int zipRet = system(zipCommand.str());
 
-            //Add Workunit XML file
-            MemoryBuffer wuXmlMB;
-            winfo.getWorkunitXml(NULL, wuXmlMB);
-            fs.clear().append("ZAPReport_").append(req.getWuid()).append('_').append(userName.str()).append(".xml");
-            zipper->addContentToZIP(wuXmlMB.length(), (void*)wuXmlMB.toByteArray(), (char*)fs.str(), true);
+        //Remove the temporary files and the folder
+        cleanZAPFolder(zipDir, true);
 
-            //Write out ZIP file
-            zipper->zipToFile(zipFile.str());
-
-            for (aindex_t x=0; x<eclAgentLogs.length(); x++)
-                delete (MemoryBuffer*)eclAgentLogs.item(x);
-            for (aindex_t x=0; x<thorLogs.length(); x++)
-                delete (MemoryBuffer*)thorLogs.item(x);
-        }
+        if (zipRet != 0)
+            throw MakeStringException(ECLWATCH_CANNOT_COMPRESS_DATA,"Failed to execute system command 'zip'. Please make sure that zip utility is installed.");
 
         //Download ZIP file to user
-        Owned<IFile> f = createIFile(zipFile.str());
+        Owned<IFile> f = createIFile(zipFileNameWithPath.str());
         Owned<IFileIO> io = f->open(IFOread);
         MemoryBuffer mb;
         void * data = mb.reserve((unsigned)io->size());
@@ -4084,11 +4119,10 @@ bool CWsWorkunitsEx::onWUCreateZAPInfo(IEspContext &context, IEspWUCreateZAPInfo
         resp.setThefile(mb);
         resp.setThefile_mimetype(HTTP_TYPE_OCTET_STREAM);
         StringBuffer headerStr("attachment;filename=");
-        headerStr.append(zipFile.str());
+        headerStr.append(zipFileName.str());
         context.addCustomerHeader("Content-disposition", headerStr.str());
         io->close();
         f->remove();
-#endif
     }
     catch(IException* e)
     {

+ 7 - 3
esp/services/ws_workunits/ws_workunitsService.hpp

@@ -256,9 +256,13 @@ public:
     bool onWUCheckFeatures(IEspContext &context, IEspWUCheckFeaturesRequest &req, IEspWUCheckFeaturesResponse &resp);
 
 private:
-#ifdef _USE_ZLIB
-    void addProcessLogfile(IZZIPor* zipper, Owned<IConstWorkUnit> &cwu, WsWuInfo &winfo, const char * process, PointerArray &mbArr);
-#endif
+    void addProcessLogfile(Owned<IConstWorkUnit> &cwu, WsWuInfo &winfo, const char * process, const char* path);
+    void createZAPWUInfoFile(IEspWUCreateZAPInfoRequest &req, Owned<IConstWorkUnit>& cwu, const char* pathNameStr);
+    void createZAPWUXMLFile(WsWuInfo &winfo, const char* pathNameStr);
+    void createZAPECLQueryArchiveFiles(Owned<IConstWorkUnit>& cwu, const char* pathNameStr);
+    void createZAPFile(const char* fileName, size32_t len, const void* data);
+    void cleanZAPFolder(IFile* zipDir, bool removeFolder);
+
     unsigned awusCacheMinutes;
     StringBuffer queryDirectory;
     StringAttr daliServers;

+ 1 - 0
esp/src/eclwatch/nls/hpcc.js

@@ -308,6 +308,7 @@ define({root:
     Parts: "Parts",
     Password: "Password",
     PasswordExpiration: "Password Expiration",
+    PasswordOpenZAP: "Password to open ZAP(optional)",
     PasswordsDoNotMatch: "Passwords do not match.",
     PasswordExpired: "Your password has expired.  Please change now.",
     PasswordExpirePrefix: "Your password will expire in ",

+ 2 - 1
esp/src/eclwatch/templates/WUDetailsWidget.html

@@ -118,7 +118,7 @@
         </div>
     </div>
     <div id="${id}ZapDialog" data-dojo-type="dijit.Dialog" title="${i18n.ZippedAnalysisPackage}">
-        <form id="${id}ZapForm" action="/WsWorkunits/WUCreateZAPInfo" style="width:400px" method="post" encType="application/x-www-form-urlencoded">
+        <form id="${id}ZapForm" action="/WsWorkunits/WUCreateZAPInfo" style="width:460px" method="post" encType="application/x-www-form-urlencoded">
             <div data-dojo-props="cols:2" data-dojo-type="hpcc.TableContainer">
                 <input id="${id}ZapWUID" title="${i18n.WUID}:" name="Wuid" colspan="2" data-dojo-props="trim: true, readonly: true," data-dojo-type="dijit.form.TextBox" />
                 <input id="${id}BuildVersion" title="${i18n.ESPBuildVersion}:" name="BuildVersion" colspan="2" data-dojo-props="trim: true, readonly: true," data-dojo-type="dijit.form.TextBox" />
@@ -127,6 +127,7 @@
                 <input id="${id}ZapDescription" title="${i18n.Description}:" name="ProblemDescription" cols="22" colspan="2" data-dojo-type="dijit.form.SimpleTextarea"/>
                 <input id="${id}WarnHistory" title="${i18n.History}:" name="WhatChanged" cols="22" colspan="2" data-dojo-type="dijit.form.SimpleTextarea"/>
                 <input id="${id}WarnTimings" title="${i18n.Timings}:" name="WhereSlow" cols="22" colspan="2" data-dojo-type="dijit.form.SimpleTextarea"/>
+                <input id="${id}Password" title="${i18n.PasswordOpenZAP}:" name="Password" cols="22" colspan="2" type="password" data-dojo-props="trim: true" data-dojo-type="dijit.form.ValidationTextBox" />
             </div>
             <button id="${id}onZapSubmit" data-dojo-attach-event="onClick:_onCancelDialog" class="bottomFormButtons" type="submit" data-dojo-type="dijit.form.Button">${i18n.Apply}</button>
             <button class="bottomFormButtons" data-dojo-attach-event="onClick:_onCancelDialog" data-dojo-type="dijit.form.Button">${i18n.Cancel}</button>