diff --git a/config/lang/de.json b/config/lang/de.json index 1a5ef39f412236844859fc6d0ce224c5575ace9a..2701d0da5376e11c67043aba759f5832904655e2 100644 --- a/config/lang/de.json +++ b/config/lang/de.json @@ -214,6 +214,9 @@ "accept": "Accept", "accept-hint": "Accept Phase [%s] und in die Queue von Phase [%s] stellen.", "all": "alle", + "api-secret": "Secret für API Zugriff", + "api-secret-hint": "Hinweise: Bei leeren Secret ist der Zugriff via API deaktiviert. Um den API Zugriff zu aktivieren, ist ein geshartes Secret zu setzen. Ein Neusetzen eines Secrets macht den bisherigen Key ungültig.", + "api-secret-generate": "Neues Secret erzeugen", "archive": "Archiv", "back": "zurück", "branch": "Branch/ Tag", @@ -238,7 +241,7 @@ "delete": "Löschen", "deploy": "Deploy", "deploy-configfile": "Konfiguration", - "deploy-configfile-hint": "Hier können Variablen in Bash-Syntax hinterlegt werden, die sich vom onbuild oder ondeploy Hook lesen lassen.", + "deploy-configfile-hint": "Hier können Variablen in Bash-Syntax hinterlegt werden. Bei einem Build werden diese in [root]/ci-custom-vars geschrieben und lassen sich vom onbuild oder ondeploy Hook lesen.", "deploy-hint": "Deploy der Queue von Phase [%s]", "deploy-impossible": "Deploy der Queue von Phase [%s] ist nicht möglich.", "deploy-settings": "Deployment-Einstellungen", diff --git a/config/lang/en.json b/config/lang/en.json index 969d944b89cc275167bb6fadbe8ab47123f1ed0a..ce3575c1c14d687f50108d2aeaa856c8c5b5a693 100644 --- a/config/lang/en.json +++ b/config/lang/en.json @@ -216,6 +216,9 @@ "accept": "Accept", "accept-hint": "Accept phase [%s] and put package to the queue of phase [%s].", "all": "all", + "api-secret": "Secret for API access", + "api-secret-hint": "To enable access via API it is required to set a shared secret. If you set a new key then the former key is invalid.", + "api-secret-generate": "Generate new secret", "archive": "Archive", "back": "back", "branch": "Branch/ tag", @@ -240,7 +243,7 @@ "delete": "Delete", "deploy": "Deploy", "deploy-configfile": "Configuration", - "deploy-configfile-hint": "Here you can place variables in Bash syntax that onbuild oder ondeploy hook can read.", + "deploy-configfile-hint": "Here you can place variables in Bash syntax. During the build it will be writen as [root]/ci-custom-vars and is readable in the onbuild oder ondeploy hook.", "deploy-hint": "Deploy queue of phase [%s]", "deploy-impossible": "Deploy queue of phase [%s] is not possible.", "deploy-settings": "Deployment settings", diff --git a/public_html/api/index.php b/public_html/api/index.php index aa9d1cc3661cf73f2ad4b5a2bbaa0886ecd21fe7..88ca9c3de1ecd428f21af603e70f8cd8d137cd4b 100644 --- a/public_html/api/index.php +++ b/public_html/api/index.php @@ -3,12 +3,12 @@ * IDEA TO REALIZE: * * /api/v1/projects/ - * /api/v1/project/[Name]/[token]/build[/[name-of-branch]] - * /api/v1/project/[Name]/[token]/accept/[phase] + * /api/v1/project/[Name]/build/[name-of-branch]] + * /api/v1/project/[Name]/accept/[phase] * */ - $bDebug=true; + $bDebug=false; ini_set('display_errors', 1); ini_set('display_startup_errors', 1); error_reporting(E_ALL); @@ -25,23 +25,39 @@ // ---------------------------------------------------------------------- // FUNCTIONS // ---------------------------------------------------------------------- + /** + * write debug text (if enabled) + * @global boolean $bDebug + * @param string $s message + * @param string $sLevel level; one of info| + * @return boolean + */ function _wd($s, $sLevel='info'){ global $bDebug; if ($bDebug){ - echo '<div class="debug ">DEBUG: '.$s.'</div>'; + echo '<div class="debug debug-'.$sLevel.'">DEBUG: '.$s.'</div>'; } return true; } + /** + * abort execution with error + * @param string $s message + * @param integer $iStatus http status code to send + */ function _quit($s, $iStatus=400){ $aStatus=array( 400=>'HTTP/1.0 400 Bad Request', - 404=>'HTTP/1.0 403 Access denied', + 403=>'HTTP/1.0 403 Access denied', + 404=>'HTTP/1.0 404 Not found', ); header($aStatus[$iStatus]); _done(array('status'=>$iStatus, 'info'=>$aStatus[$iStatus], 'message'=>$s)); } - + /** + * end with OK output + * @param type $Data + */ function _done($Data){ echo is_array($Data) ? json_encode($Data, 1, JSON_PRETTY_PRINT) @@ -71,14 +87,13 @@ _wd('<pre>$aUriSplit: '.print_r($aUriSplit, 1).'</pre>'); /* - /api/v1/projects/ci/my-token/build?auth=123 + /api/v1/projects/ci/build?auth=123 $aUriSplit: Array ( [0] => v1 [1] => projects [2] => ci - [3] => my-token - [4] => build + [3] => build ) */ $sApiVersion = isset($aUriSplit[0]) ? $aUriSplit[0] : false; @@ -107,26 +122,35 @@ _wd('<pre>'.print_r($aList,1).'</pre>'); _done($aList); break;; + case 'project': - + // path /api/v1/project $sPrjId = isset($aUriSplit[2]) ? $aUriSplit[2] : false; - $sPrjToken = isset($aUriSplit[3]) ? $aUriSplit[3] : false; - $sPrjAction = isset($aUriSplit[4]) ? $aUriSplit[4] : false; + $sPrjAction = isset($aUriSplit[3]) ? $aUriSplit[3] : false; _wd('$sPrjId = '.$sPrjId); - _wd('$sPrjToken = '.$sPrjToken); _wd('$sPrjAction = '.$sPrjAction); - $oProject=new project(); - if(!in_array($sPrjId, $oProject->getProjects())){ - _quit('ERROR: project with id ['.$sPrjId.'] does not exist.'); + // try to init the given project + try{ + ob_start(); + $oProject=new project($sPrjId); + $oProject->setProjectById($sPrjId); + ob_end_clean(); + + } catch (Exception $exc) { + _quit('ERROR: project with id ['.$sPrjId.'] does not exist.', 404); } - $oProject->setProjectById($sPrjId); - _wd('TODO: verify given token with that in the config.'); - + // get secret $aPrjCfg=$oProject->getConfig(); - _wd('<pre>'.print_r($aPrjCfg, 1).'</pre>'); + $sProjectSecret=isset($aPrjCfg['api']['secret']) ? $aPrjCfg['api']['secret'] : false; + if(!$sProjectSecret){ + _quit('Access denied. API access is disabled.'); + } + + $aReqHeaders=apache_request_headers(); + _wd('<pre>'.print_r($aReqHeaders, 1).'</pre>'); break;; default: diff --git a/public_html/deployment/classes/project.class.php b/public_html/deployment/classes/project.class.php index 976db48df1314270be79fc3a178d3d976bfa753b..687ba48fb328dedcb0f3e92994f84cfe396cffcf 100644 --- a/public_html/deployment/classes/project.class.php +++ b/public_html/deployment/classes/project.class.php @@ -195,8 +195,10 @@ class project extends base { * @return boolean */ private function _verifyConfig() { - if (!count($this->_aPrjConfig)) - die(t("class-project-error-no-config")); + if (!count($this->_aPrjConfig)){ + // die(t("class-project-error-no-config")); + throw new Exception(t("class-project-error-no-config")); + } if (!array_key_exists("packageDir", $this->_aConfig)) { die(t("class-project-error-no-packagedir")); @@ -3508,7 +3510,25 @@ class project extends base { 'cols' => 100, 'rows' => 10, 'placeholder' => 'export myvariable="hello world"', + ), + + 'input' . $i++ => array( + 'type' => 'text', + 'name' => 'api[secret]', + 'label' => t("api-secret"), + 'value' => $this->_aPrjConfig["api"]["secret"], + 'validate' => 'isastring', + 'size' => 100, + 'placeholder' => '', ), + 'input' . $i++ => array( + 'type' => 'markup', + 'value' => '<div class="col-sm-12">' + . '<p>' . t('api-secret-hint') . '<br>' + . '<a href="#" class="btn btn-default" onclick="$(\'#input'.($i-2).'\').val(generateSecret(64)); return false">'.t("api-secret-generate").'</a>' + . '</p></div>', + ), + // -------------------------------------------------- 'input' . $i++ => array( 'type' => 'markup', diff --git a/public_html/deployment/js/functions.js b/public_html/deployment/js/functions.js index c8c2af19bc169bbe1ee0b60ac532ad08b62d7378..8275ea01ccadec30f9249464158a58c0a716b5ce 100644 --- a/public_html/deployment/js/functions.js +++ b/public_html/deployment/js/functions.js @@ -1,305 +1,320 @@ - -/** - * initialize soft scrolling for links with css class "scroll-link" - * @see http://css-tricks.com/snippets/jquery/smooth-scrolling/ - * @returns {undefined} - */ -function initSoftscroll(){ - $(function() { - // $('a[href*=#]:not([href=#])').click(function() { - $('a.scroll-link').click(function() { - if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) { - var target = $(this.hash); - target = target.length ? target : $('[name=' + this.hash.slice(1) +']'); - if (target.length) { - $('html,body').animate({ - scrollTop: target.offset().top - 70 - }, 300); - return false; - } - } - }); - }); -} - -function showModalMessage(sMessage){ - $('#divmodalmessage').html(sMessage); - $('#divmodal').show(); -} -function showIdAsModalMessage(sId){ - var o=$('#'+sId); - var sHtml='<a href="#" onclick="return hideModalMessage()" class="btn btn-danger" style="float:right"> X </a>' - + o.html() - + '<hr><a href="#" onclick="return hideModalMessage()" class="btn btn-primary"><i class="fa fa-check"></i> OK </a>' - ; - $('#divmodalmessage').html(sHtml); - $('#divmodal').show(); - return false; -} - -function hideModalMessage(){ - $('#divmodal').hide(); - return false; -} - -// ---------------------------------------------------------------------- -// general init in each page -// ---------------------------------------------------------------------- - -$(document).ready(function() { - initSoftscroll(); - // $(".optionName").popover({trigger: "hover"}); - // $("#content").hide().fadeIn(300); -}); - - -// ---------------------------------------------------------------------- -// action log -// ---------------------------------------------------------------------- - -/** - * get filtered action log table - * @returns {undefined} - */ -function updateActionlog(){ - var sUrlBase="/webservice/?class=Actionlog&action=getLogs&type=json&args="; - var aArgs={}; - - var aFilteritems=["project", "where", "order", "limit"]; - var aTableitems=["id", "time", "loglevel", "ip", "user", "project", "action", "message"]; - - // --- create query url - - for (i=0; i<aFilteritems.length; i++){ - sValue=$('#select' + aFilteritems[i]).val(); - if(sValue){ - aArgs[aFilteritems[i]]=sValue; - } - } - - var sWhere=''; - for (j=0; j<aTableitems.length; j++){ - sValue=$('#selectWhere' + aTableitems[j]).val(); - if(sValue){ - if (sWhere){ - sWhere+' AND '; - } - sWhere+='`'+aTableitems[j]+'`'+sValue; - } - } - if (sWhere) { - aArgs["where"]=sWhere; - } - - // --- get data - - var sUrl=sUrlBase+'['+JSON.stringify(aArgs)+']'; - $.post( sUrl, function( aData ) { - var sHtml=''; - - // --- generate output - if (aData.length && aData[0]["id"]){ - for (i=0; i<aData.length; i++){ - sHtml+='<tr class="tractionlogs loglevel-'+aData[i]["loglevel"]+' '+aData[i]["project"]+'">'; - for (j=0; j<aTableitems.length; j++){ - sHtml+='<td>'+aData[i][aTableitems[j]]+'</td>'; - } - sHtml+='</tr>'; - } - } - drawTimeline(aData); - - if (!sHtml){ - sHtml=sMsgNolog; // variable is set in actionlog.class.php - } else { - sHead=''; - for (j=0; j<aTableitems.length; j++){ - sHead+='<th>'+aTableitems[j]+'</th>'; - } - sHead='<thead><tr>'+sHead+'</tr></thead>'; - sHtml='<table class="table table-condensed">'+sHead+'<tbody>'+sHtml+'</tbody></table>'; - } - $('#tableLogactions').html(sHtml); - filterLogTable(); - }); - -} - -/** - * render timeline with Visjs - * - * @param {array} aData - * @returns {undefined} - */ -function drawTimeline(aData){ - var sDataset=''; - - var container = document.getElementById('divTimeline'); - if(!container){ - return false; - } - container.innerHTML=''; // empty the div - - if (aData.length && aData[0]["id"]){ - for (i=0; i<aData.length; i++){ - // keys are - // var aTableitems=["id", "time", "loglevel", "ip", "user", "project", "action", "message"]; - sLabel=aData[i]["project"]+'<br>'+aData[i]["action"]; - sTitle=aData[i]["time"] + '<br>'+aData[i]["loglevel"]+'<br><br>Projekt: ' + aData[i]["project"] +'<br>User: ' + aData[i]["user"] + ' (' + aData[i]["ip"] +')<br>'+ aData[i]["message"] ; - sDataset+= (sDataset ? ', ': '' ) - + '{"id": '+i+', "content": "'+sLabel+'", "start": "'+aData[i]["time"].replace(/\ /, "T") +'", "title": "'+sTitle+'", "group": "'+aData[i]["project"]+'", "className": "loglevel-'+aData[i]["loglevel"]+'" }'; - } - aDataset=JSON.parse('['+sDataset+']'); - - var items = new vis.DataSet(aDataset); - - // Configuration for the Timeline - var options = { - // verticalScroll: false, - clickToUse: true - }; - - // Create a Timeline - var timeline = new vis.Timeline(container, items, options); - } -} - -/** -* filter table with action logs by filtertext (input field) -*/ -function filterLogTable(){ - var sSearch=$("#efilterlogs").val(); - var Regex = new RegExp(sSearch, "i"); - $(".tractionlogs").each(function() { - sVisible="none"; - if ( Regex.exec(this.innerHTML)) { - sVisible=""; - } - $(this).css("display", sVisible); - }); - return false; -} - -// ---------------------------------------------------------------------- -// tables -// ---------------------------------------------------------------------- -var localStoreTablefilter="tblvalue" + location.pathname; - - -// http://blog.mastykarz.nl/jquery-regex-filter/ -jQuery.extend( - jQuery.expr[':'], { - regex: function (a, i, m) { - var r = new RegExp(m[3], 'i'); - return r.test(jQuery(a).text()); - } -} -); - -/* - highlight v4 - Highlights arbitrary terms. - - <http://johannburkard.de/blog/programming/javascript/highlight-javascript-text-higlighting-jquery-plugin.html> - - MIT license. - - Johann Burkard - <http://johannburkard.de> - <mailto:jb@eaio.com> - */ - -jQuery.fn.highlight = function (pat) { - function innerHighlight(node, pat) { - var skip = 0; - if (node.nodeType == 3) { - var pos = node.data.toUpperCase().indexOf(pat); - if (pos >= 0) { - var spannode = document.createElement('span'); - spannode.className = 'highlight'; - var middlebit = node.splitText(pos); - var endbit = middlebit.splitText(pat.length); - var middleclone = middlebit.cloneNode(true); - spannode.appendChild(middleclone); - middlebit.parentNode.replaceChild(spannode, middlebit); - skip = 1; - } - } - else if (node.nodeType == 1 && node.childNodes && !/(script|style)/i.test(node.tagName)) { - for (var i = 0; i < node.childNodes.length; ++i) { - i += innerHighlight(node.childNodes[i], pat); - } - } - return skip; - } - return this.length && pat && pat.length ? this.each(function () { - innerHighlight(this, pat.toUpperCase()); - }) : this; -}; - -jQuery.fn.removeHighlight = function () { - return this.find("span.highlight").each(function () { - this.parentNode.firstChild.nodeName; - with (this.parentNode) { - replaceChild(this.firstChild, this); - normalize(); - } - }).end(); -}; - - -/** - * add a filter form to a table - * @returns {undefined} - */ -function addFilterToTable(){ - var sValue=localStorage.getItem(localStoreTablefilter) ? localStorage.getItem(localStoreTablefilter) : ''; - var sForm='<form class="pure-form">\n\ - <fieldset>\n\ - <label for="eFilter">\n\ - <i class="fa fa-filter"></i> Tabelle filtern\n\ - </label>\n\ - <input type="text" id="eFilter" size="40" name="q" placeholder="Suchbegriff..." value="'+sValue+'" onkeypress="filterTable(this);" onkeyup="filterTable(this);" onchange="filterTable(this);">\n\ - <button class="pure-button" onclick="$(\'#eFilter\').val(\'\'); filterTable(); return false;"><i class="fa fa-times"></i> </button>\n\ - <span id="filterinfo"></span>\n\ - </fieldset>\n\ - </form><div style="clear: both;"></div>'; - $(sForm).insertBefore($("table").first()); -} - - -/** - * callback ... filter the table - * use addFilterToTable() before. - * @returns {undefined} - */ -function filterTable() { - var filter = $('#eFilter').val(); - localStorage.setItem(localStoreTablefilter, filter); - $("table").removeHighlight(); - if (filter) { - $("tr:regex('" + filter + "')").show(); - $("tr:not(:regex('" + filter + "'))").hide(); - $("tr").first().show(); - - $("td").highlight(filter); - } else { - $("td").removeHighlight(); - $('tr').show(); - } - - var sInfo = ''; - var iVisible = -1; // 1 abziehen wg. tr der ersten Zeile - $("tr").each(function () { - if ($(this).css('display') != 'none') { - iVisible++; - } - }); - - sInfo = (iVisible == ($("tr").length - 1)) - ? "ges.: <strong>" + ($("tr").length - 1) + "</strong> Einträge" - : "<strong>" + iVisible + "</strong> von " + ($("tr").length - 1) + " Einträgen" - ; - $('#filterinfo').html(sInfo); - -} + +/** + * initialize soft scrolling for links with css class "scroll-link" + * @see http://css-tricks.com/snippets/jquery/smooth-scrolling/ + * @returns {undefined} + */ +function initSoftscroll(){ + $(function() { + // $('a[href*=#]:not([href=#])').click(function() { + $('a.scroll-link').click(function() { + if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) { + var target = $(this.hash); + target = target.length ? target : $('[name=' + this.hash.slice(1) +']'); + if (target.length) { + $('html,body').animate({ + scrollTop: target.offset().top - 70 + }, 300); + return false; + } + } + }); + }); +} + +function showModalMessage(sMessage){ + $('#divmodalmessage').html(sMessage); + $('#divmodal').show(); +} +function showIdAsModalMessage(sId){ + var o=$('#'+sId); + var sHtml='<a href="#" onclick="return hideModalMessage()" class="btn btn-danger" style="float:right"> X </a>' + + o.html() + + '<hr><a href="#" onclick="return hideModalMessage()" class="btn btn-primary"><i class="fa fa-check"></i> OK </a>' + ; + $('#divmodalmessage').html(sHtml); + $('#divmodal').show(); + return false; +} + +function hideModalMessage(){ + $('#divmodal').hide(); + return false; +} + +// ---------------------------------------------------------------------- +// general init in each page +// ---------------------------------------------------------------------- + +$(document).ready(function() { + initSoftscroll(); + // $(".optionName").popover({trigger: "hover"}); + // $("#content").hide().fadeIn(300); +}); + + +// ---------------------------------------------------------------------- +// action log +// ---------------------------------------------------------------------- + +/** + * get filtered action log table + * @returns {undefined} + */ +function updateActionlog(){ + var sUrlBase="/webservice/?class=Actionlog&action=getLogs&type=json&args="; + var aArgs={}; + + var aFilteritems=["project", "where", "order", "limit"]; + var aTableitems=["id", "time", "loglevel", "ip", "user", "project", "action", "message"]; + + // --- create query url + + for (i=0; i<aFilteritems.length; i++){ + sValue=$('#select' + aFilteritems[i]).val(); + if(sValue){ + aArgs[aFilteritems[i]]=sValue; + } + } + + var sWhere=''; + for (j=0; j<aTableitems.length; j++){ + sValue=$('#selectWhere' + aTableitems[j]).val(); + if(sValue){ + if (sWhere){ + sWhere+' AND '; + } + sWhere+='`'+aTableitems[j]+'`'+sValue; + } + } + if (sWhere) { + aArgs["where"]=sWhere; + } + + // --- get data + + var sUrl=sUrlBase+'['+JSON.stringify(aArgs)+']'; + $.post( sUrl, function( aData ) { + var sHtml=''; + + // --- generate output + if (aData.length && aData[0]["id"]){ + for (i=0; i<aData.length; i++){ + sHtml+='<tr class="tractionlogs loglevel-'+aData[i]["loglevel"]+' '+aData[i]["project"]+'">'; + for (j=0; j<aTableitems.length; j++){ + sHtml+='<td>'+aData[i][aTableitems[j]]+'</td>'; + } + sHtml+='</tr>'; + } + } + drawTimeline(aData); + + if (!sHtml){ + sHtml=sMsgNolog; // variable is set in actionlog.class.php + } else { + sHead=''; + for (j=0; j<aTableitems.length; j++){ + sHead+='<th>'+aTableitems[j]+'</th>'; + } + sHead='<thead><tr>'+sHead+'</tr></thead>'; + sHtml='<table class="table table-condensed">'+sHead+'<tbody>'+sHtml+'</tbody></table>'; + } + $('#tableLogactions').html(sHtml); + filterLogTable(); + }); + +} + +/** + * render timeline with Visjs + * + * @param {array} aData + * @returns {undefined} + */ +function drawTimeline(aData){ + var sDataset=''; + + var container = document.getElementById('divTimeline'); + if(!container){ + return false; + } + container.innerHTML=''; // empty the div + + if (aData.length && aData[0]["id"]){ + for (i=0; i<aData.length; i++){ + // keys are + // var aTableitems=["id", "time", "loglevel", "ip", "user", "project", "action", "message"]; + sLabel=aData[i]["project"]+'<br>'+aData[i]["action"]; + sTitle=aData[i]["time"] + '<br>'+aData[i]["loglevel"]+'<br><br>Projekt: ' + aData[i]["project"] +'<br>User: ' + aData[i]["user"] + ' (' + aData[i]["ip"] +')<br>'+ aData[i]["message"] ; + sDataset+= (sDataset ? ', ': '' ) + + '{"id": '+i+', "content": "'+sLabel+'", "start": "'+aData[i]["time"].replace(/\ /, "T") +'", "title": "'+sTitle+'", "group": "'+aData[i]["project"]+'", "className": "loglevel-'+aData[i]["loglevel"]+'" }'; + } + aDataset=JSON.parse('['+sDataset+']'); + + var items = new vis.DataSet(aDataset); + + // Configuration for the Timeline + var options = { + // verticalScroll: false, + clickToUse: true + }; + + // Create a Timeline + var timeline = new vis.Timeline(container, items, options); + } +} + +/** +* filter table with action logs by filtertext (input field) +*/ +function filterLogTable(){ + var sSearch=$("#efilterlogs").val(); + var Regex = new RegExp(sSearch, "i"); + $(".tractionlogs").each(function() { + sVisible="none"; + if ( Regex.exec(this.innerHTML)) { + sVisible=""; + } + $(this).css("display", sVisible); + }); + return false; +} + +// ---------------------------------------------------------------------- +// tables +// ---------------------------------------------------------------------- +var localStoreTablefilter="tblvalue" + location.pathname; + + +// http://blog.mastykarz.nl/jquery-regex-filter/ +jQuery.extend( + jQuery.expr[':'], { + regex: function (a, i, m) { + var r = new RegExp(m[3], 'i'); + return r.test(jQuery(a).text()); + } +} +); + +/* + highlight v4 + Highlights arbitrary terms. + + <http://johannburkard.de/blog/programming/javascript/highlight-javascript-text-higlighting-jquery-plugin.html> + + MIT license. + + Johann Burkard + <http://johannburkard.de> + <mailto:jb@eaio.com> + */ + +jQuery.fn.highlight = function (pat) { + function innerHighlight(node, pat) { + var skip = 0; + if (node.nodeType == 3) { + var pos = node.data.toUpperCase().indexOf(pat); + if (pos >= 0) { + var spannode = document.createElement('span'); + spannode.className = 'highlight'; + var middlebit = node.splitText(pos); + var endbit = middlebit.splitText(pat.length); + var middleclone = middlebit.cloneNode(true); + spannode.appendChild(middleclone); + middlebit.parentNode.replaceChild(spannode, middlebit); + skip = 1; + } + } + else if (node.nodeType == 1 && node.childNodes && !/(script|style)/i.test(node.tagName)) { + for (var i = 0; i < node.childNodes.length; ++i) { + i += innerHighlight(node.childNodes[i], pat); + } + } + return skip; + } + return this.length && pat && pat.length ? this.each(function () { + innerHighlight(this, pat.toUpperCase()); + }) : this; +}; + +jQuery.fn.removeHighlight = function () { + return this.find("span.highlight").each(function () { + this.parentNode.firstChild.nodeName; + with (this.parentNode) { + replaceChild(this.firstChild, this); + normalize(); + } + }).end(); +}; + + +/** + * add a filter form to a table + * @returns {undefined} + */ +function addFilterToTable(){ + var sValue=localStorage.getItem(localStoreTablefilter) ? localStorage.getItem(localStoreTablefilter) : ''; + var sForm='<form class="pure-form">\n\ + <fieldset>\n\ + <label for="eFilter">\n\ + <i class="fa fa-filter"></i> Tabelle filtern\n\ + </label>\n\ + <input type="text" id="eFilter" size="40" name="q" placeholder="Suchbegriff..." value="'+sValue+'" onkeypress="filterTable(this);" onkeyup="filterTable(this);" onchange="filterTable(this);">\n\ + <button class="pure-button" onclick="$(\'#eFilter\').val(\'\'); filterTable(); return false;"><i class="fa fa-times"></i> </button>\n\ + <span id="filterinfo"></span>\n\ + </fieldset>\n\ + </form><div style="clear: both;"></div>'; + $(sForm).insertBefore($("table").first()); +} + + +/** + * callback ... filter the table + * use addFilterToTable() before. + * @returns {undefined} + */ +function filterTable() { + var filter = $('#eFilter').val(); + localStorage.setItem(localStoreTablefilter, filter); + $("table").removeHighlight(); + if (filter) { + $("tr:regex('" + filter + "')").show(); + $("tr:not(:regex('" + filter + "'))").hide(); + $("tr").first().show(); + + $("td").highlight(filter); + } else { + $("td").removeHighlight(); + $('tr').show(); + } + + var sInfo = ''; + var iVisible = -1; // 1 abziehen wg. tr der ersten Zeile + $("tr").each(function () { + if ($(this).css('display') != 'none') { + iVisible++; + } + }); + + sInfo = (iVisible == ($("tr").length - 1)) + ? "ges.: <strong>" + ($("tr").length - 1) + "</strong> Einträge" + : "<strong>" + iVisible + "</strong> von " + ($("tr").length - 1) + " Einträgen" + ; + $('#filterinfo').html(sInfo); + +} + + +// ---------------------------------------------------------------------- +// page settings +// ---------------------------------------------------------------------- +function generateSecret(length){ + + var result = ''; + var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + var charactersLength = characters.length; + for ( var i = 0; i < length; i++ ) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + return result; +} diff --git a/shellscripts/api-client.sh b/shellscripts/api-client.sh new file mode 100644 index 0000000000000000000000000000000000000000..a4a6f433a16d57cef0c3bf33685239a90346c86e --- /dev/null +++ b/shellscripts/api-client.sh @@ -0,0 +1,53 @@ +#!/bin/bash +# ====================================================================== +# +# API CLIENT :: proof of concept +# +# This is a demo api client +# +# ---------------------------------------------------------------------- +# 2020-06-12 first lines <axel.hahn@iml.unibe.ch> +# ====================================================================== + +# ---------------------------------------------------------------------- +# CONFIG +# ---------------------------------------------------------------------- + + +myProject=ci-webgui +secret="cOiScVAElvcJKmJ1eGrKXZvv6ZROlSgZ9VpSVFK1uxZI8J5ITXuZZb8jIYobuoAB" + +myAction=build +apiHost="http://dev.ci.iml.unibe.ch:8002" +apiBaseUrl="/api/v1" +apiMethod=GET + + +# ---------------------------------------------------------------------- +# MAIN +# ---------------------------------------------------------------------- + +# --- build url +apiRequest="${apiBaseUrl}/project/${myProject}/${myAction}" + +# --- generate auth +data="`date`\n${apiMethod}\n${apiRequest}" +myHash=`echo -n "$data" | openssl sha1 -hmac "${secret}" | cut -f 2 -d" "` + + + +# https://stackoverflow.com/questions/356705/how-to-send-a-header-using-a-http-request-through-a-curl-call + +echo HASH: $myHash ... made from [$data] +echo REQEST: $apiRequest - $myHash + + +curl -i \ + -H "Accept: application/json" -H "Content-Type: application/json" \ + -H "Authorization: demo-bash-client:${myHash}" \ + -X $apiMethod \ + ${apiHost}${apiRequest} + +rc=$? +echo rc = $rc +