From 27e3b8b469fb3672e0d372da7f35837e60c7097f Mon Sep 17 00:00:00 2001 From: Pavel Pavlov Date: Wed, 27 Nov 2013 18:09:14 +0400 Subject: [PATCH 1/4] UI Improvement: Modal confirmation dialog --- PHPCI/Controller/BuildController.php | 6 + PHPCI/View/BuildsTable.phtml | 2 +- PHPCI/View/Home/index.phtml | 8 +- public/assets/js/phpci.js | 194 +++++++++++++++++++++++++++ 4 files changed, 206 insertions(+), 4 deletions(-) diff --git a/PHPCI/Controller/BuildController.php b/PHPCI/Controller/BuildController.php index 8c183774..f4a1d9c8 100644 --- a/PHPCI/Controller/BuildController.php +++ b/PHPCI/Controller/BuildController.php @@ -129,6 +129,12 @@ class BuildController extends \PHPCI\Controller } $build = $this->buildStore->getById($buildId); + + if (!$build) { + $this->response->setResponseCode(404); + return '404 - Not Found'; + } + $this->buildStore->delete($build); header('Location: '.PHPCI_URL.'project/view/' . $build->getProjectId()); diff --git a/PHPCI/View/BuildsTable.phtml b/PHPCI/View/BuildsTable.phtml index e38d8580..c2330d29 100644 --- a/PHPCI/View/BuildsTable.phtml +++ b/PHPCI/View/BuildsTable.phtml @@ -73,7 +73,7 @@ switch($build->getStatus()) diff --git a/PHPCI/View/Home/index.phtml b/PHPCI/View/Home/index.phtml index 68a0f26f..bfb90694 100644 --- a/PHPCI/View/Home/index.phtml +++ b/PHPCI/View/Home/index.phtml @@ -58,8 +58,10 @@ \ No newline at end of file diff --git a/public/assets/js/phpci.js b/public/assets/js/phpci.js index d662825b..8f338b53 100644 --- a/public/assets/js/phpci.js +++ b/public/assets/js/phpci.js @@ -1,3 +1,32 @@ +/** + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind + * for the details of code below + */ +if (!Function.prototype.bind) { + Function.prototype.bind = function (oThis) { + if (typeof this !== "function") { + // closest thing possible to the ECMAScript 5 internal IsCallable function + throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); + } + + var aArgs = Array.prototype.slice.call(arguments, 1), + fToBind = this, + fNOP = function () { + }, + fBound = function () { + return fToBind.apply(this instanceof fNOP && oThis + ? this + : oThis, + aArgs.concat(Array.prototype.slice.call(arguments))); + }; + + fNOP.prototype = this.prototype; + fBound.prototype = new fNOP(); + + return fBound; + }; +} + /** * Used for delete buttons in the system, just to prevent accidental clicks. */ @@ -13,6 +42,171 @@ function confirmDelete(url) } } +/** + * Used for delete build buttons in the system, just to prevent accidental clicks. + */ +function confirmDeleteBuild(url) { + + var dialog = new PHPCIConfirmDialog({ + message: 'This build will be permanently deleted. Are you sure?', + confirmBtnCaption: 'Delete', + /* + confirm-btn click handler + */ + confirmed: function (e) { + var dialog = this; + e.preventDefault(); + + /* + Call delete URL + */ + $.ajax({ + url: url, + 'success': function () { + if (refreshBuildsTable) { + dialog.$dialog.on('hidden.bs.modal', function () { + refreshBuildsTable(); + }); + } + + dialog.showStatusMessage('Successfully deleted!', 1000); + }, + 'error': function (data) { + dialog.showStatusMessage('Deletion failed! Server says "' + data.statusText + '"'); + } + }); + } + }); + + dialog.show(); +} + +/** + * PHPCIConfirmDialog constructor options object + * @type {{message: string, title: string, confirmBtnCaption: string, cancelBtnCaption: string, confirmed: Function}} + */ +var PHPCIConfirmDialogOptions = { + message: 'The action will be performed and cannot be undone. Are you sure?', + title: 'Confirmation Dialog', + confirmBtnCaption: 'Ok', + cancelBtnCaption: 'Cancel', + confirmed: function (e) { + this.close(); + } +}; + +var PHPCIConfirmDialog = Class.extend({ + /** + * @param {PHPCIConfirmDialogOptions} options + */ + init: function (options) { + + options = options ? $.extend(PHPCIConfirmDialogOptions, options) : PHPCIConfirmDialogOptions; + + if (!$('#confirm-dialog').length) { + /* + Add the dialog html to a page on first use. No need to have it there before first use. + */ + $('body').append( + '' + ); + } + + /* + Define dialog controls + */ + this.$dialog = $('#confirm-dialog'); + this.$cancelBtn = this.$dialog.find('div.modal-footer button.btn-default'); + this.$confirmBtn = this.$dialog.find('div.modal-footer button.btn-primary'); + this.$title = this.$dialog.find('h4.modal-title'); + this.$body = this.$dialog.find('div.modal-body'); + + /* + Initialize its values + */ + this.$title.html(options.title ? options.title : PHPCIConfirmDialogOptions.title); + this.$body.html(options.message ? options.message : PHPCIConfirmDialogOptions.message); + this.$confirmBtn.html( + options.confirmBtnCaption ? options.confirmBtnCaption : PHPCIConfirmDialogOptions.confirmBtnCaption + ); + + this.$cancelBtn.html( + options.cancelBtnCaption ? options.cancelBtnCaption : PHPCIConfirmDialogOptions.cancelBtnCaption + ); + + /* + Events + */ + this.confirmBtnClick = options.confirmed; + + /* + Re-bind on click handler + */ + this.$confirmBtn.unbind('click'); + this.$confirmBtn.click(this.onConfirm.bind(this)); + + /* + Restore state if was changed previously + */ + this.$cancelBtn.show(); + this.$confirmBtn.show(); + }, + + /** + * Show dialog + */ + show: function () { + this.$dialog.modal('show'); + }, + + /** + * Hide dialog + */ + close: function () { + this.$dialog.modal('hide'); + }, + + onConfirm: function (e) { + $(this).attr('disabled', 'disabled'); + this.confirmBtnClick(e); + }, + + showStatusMessage: function (message, closeTimeout) { + this.$confirmBtn.hide(); + this.$cancelBtn.html('Close'); + + /* + Status message + */ + this.$body.html(message); + + if (closeTimeout) { + window.setTimeout(function () { + /* + Hide the dialog + */ + this.close(); + }.bind(this), closeTimeout); + } + } +}); + /** * Used to initialise the project form: */ From f7e396d04cb3b627a7252a785e004706e7e3caf6 Mon Sep 17 00:00:00 2001 From: Pavel Pavlov Date: Wed, 27 Nov 2013 18:14:40 +0400 Subject: [PATCH 2/4] Fixed merging bug --- PHPCI/View/Home/index.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PHPCI/View/Home/index.phtml b/PHPCI/View/Home/index.phtml index bfb90694..5e48573c 100644 --- a/PHPCI/View/Home/index.phtml +++ b/PHPCI/View/Home/index.phtml @@ -63,5 +63,5 @@ $('#latest-builds').load('home/latest'); }; -}, 10000); + setInterval(refreshBuildsTable, 10000); \ No newline at end of file From 6c0aed7f28edd905d12ac28926112930e0707580 Mon Sep 17 00:00:00 2001 From: Pavel Pavlov Date: Wed, 27 Nov 2013 21:04:24 +0400 Subject: [PATCH 3/4] UI Improvement: Modal confirmation dialog for deletion actions --- PHPCI/View/BuildsTable.phtml | 2 +- PHPCI/View/Project/view.phtml | 4 +++- PHPCI/View/User/index.phtml | 2 +- public/assets/js/phpci.js | 37 +++++++++++++---------------------- 4 files changed, 19 insertions(+), 26 deletions(-) diff --git a/PHPCI/View/BuildsTable.phtml b/PHPCI/View/BuildsTable.phtml index c2330d29..09cdd09b 100644 --- a/PHPCI/View/BuildsTable.phtml +++ b/PHPCI/View/BuildsTable.phtml @@ -73,7 +73,7 @@ switch($build->getStatus()) diff --git a/PHPCI/View/Project/view.phtml b/PHPCI/View/Project/view.phtml index 762e476a..2fd6cf6d 100644 --- a/PHPCI/View/Project/view.phtml +++ b/PHPCI/View/Project/view.phtml @@ -92,7 +92,9 @@ $(function() { $('#delete-project').on('click', function (e) { e.preventDefault(); - confirmDelete("project/delete/getId(); ?>"); + confirmDelete( + "project/delete/getId(); ?>", "Project" + ).onClose = function () {window.location = '/'}; }); }) diff --git a/PHPCI/View/User/index.phtml b/PHPCI/View/User/index.phtml index bcdcb7f1..86d6c61d 100644 --- a/PHPCI/View/User/index.phtml +++ b/PHPCI/View/User/index.phtml @@ -50,7 +50,7 @@ diff --git a/public/assets/js/phpci.js b/public/assets/js/phpci.js index 8f338b53..179597c3 100644 --- a/public/assets/js/phpci.js +++ b/public/assets/js/phpci.js @@ -30,25 +30,10 @@ if (!Function.prototype.bind) { /** * Used for delete buttons in the system, just to prevent accidental clicks. */ -function confirmDelete(url) -{ - if(confirm('Are you sure you want to delete this?')) - { - window.location.href = url; - } - else - { - return false; - } -} - -/** - * Used for delete build buttons in the system, just to prevent accidental clicks. - */ -function confirmDeleteBuild(url) { +function confirmDelete(url, subject, reloadAfter) { var dialog = new PHPCIConfirmDialog({ - message: 'This build will be permanently deleted. Are you sure?', + message: subject + ' will be permanently deleted. Are you sure?', confirmBtnCaption: 'Delete', /* confirm-btn click handler @@ -62,11 +47,11 @@ function confirmDeleteBuild(url) { */ $.ajax({ url: url, - 'success': function () { - if (refreshBuildsTable) { - dialog.$dialog.on('hidden.bs.modal', function () { - refreshBuildsTable(); - }); + 'success': function (data) { + if (reloadAfter) { + dialog.onClose = function () { + window.location.reload(); + }; } dialog.showStatusMessage('Successfully deleted!', 1000); @@ -79,6 +64,7 @@ function confirmDeleteBuild(url) { }); dialog.show(); + return dialog; } /** @@ -156,11 +142,14 @@ var PHPCIConfirmDialog = Class.extend({ this.confirmBtnClick = options.confirmed; /* - Re-bind on click handler + Re-bind handlers */ this.$confirmBtn.unbind('click'); this.$confirmBtn.click(this.onConfirm.bind(this)); + this.$confirmBtn.unbind('hidden.bs.modal'); + this.$dialog.on('hidden.bs.modal', function () {this.onClose()}.bind(this)); + /* Restore state if was changed previously */ @@ -187,6 +176,8 @@ var PHPCIConfirmDialog = Class.extend({ this.confirmBtnClick(e); }, + onClose: function () {}, + showStatusMessage: function (message, closeTimeout) { this.$confirmBtn.hide(); this.$cancelBtn.html('Close'); From 148d30f74e2ab0ab0eefc3ff0adeb06dec56417f Mon Sep 17 00:00:00 2001 From: Pavel Pavlov Date: Sat, 7 Dec 2013 19:35:44 +0400 Subject: [PATCH 4/4] Don't redirect or reload a page when confirmation dialog wasn't confirmed --- PHPCI/View/Project/view.phtml | 2 +- public/assets/js/phpci.js | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/PHPCI/View/Project/view.phtml b/PHPCI/View/Project/view.phtml index 2fd6cf6d..036f9837 100644 --- a/PHPCI/View/Project/view.phtml +++ b/PHPCI/View/Project/view.phtml @@ -94,7 +94,7 @@ e.preventDefault(); confirmDelete( "project/delete/getId(); ?>", "Project" - ).onClose = function () {window.location = '/'}; + ).onCloseConfirmed = function () {window.location = '/'}; }); }) diff --git a/public/assets/js/phpci.js b/public/assets/js/phpci.js index 179597c3..7cf07458 100644 --- a/public/assets/js/phpci.js +++ b/public/assets/js/phpci.js @@ -82,6 +82,12 @@ var PHPCIConfirmDialogOptions = { }; var PHPCIConfirmDialog = Class.extend({ + /** + * @private + * @var {bool} Determines whether the dialog has been confirmed + */ + confirmed: false, + /** * @param {PHPCIConfirmDialogOptions} options */ @@ -148,13 +154,25 @@ var PHPCIConfirmDialog = Class.extend({ this.$confirmBtn.click(this.onConfirm.bind(this)); this.$confirmBtn.unbind('hidden.bs.modal'); + + /* + Bind the close event of the dialog to the set of onClose* methods + */ this.$dialog.on('hidden.bs.modal', function () {this.onClose()}.bind(this)); + this.$dialog.on('hidden.bs.modal', function () { + if (this.confirmed) { + this.onCloseConfirmed(); + } else { + this.onCloseCanceled(); + } + }.bind(this)); /* Restore state if was changed previously */ this.$cancelBtn.show(); this.$confirmBtn.show(); + this.confirmed = false; }, /** @@ -172,10 +190,24 @@ var PHPCIConfirmDialog = Class.extend({ }, onConfirm: function (e) { + this.confirmed = true; $(this).attr('disabled', 'disabled'); this.confirmBtnClick(e); }, + /** + * Called only when confirmed dialog was closed + */ + onCloseConfirmed: function () {}, + + /** + * Called only when canceled dialog was closed + */ + onCloseCanceled: function () {}, + + /** + * Called always when the dialog was closed + */ onClose: function () {}, showStatusMessage: function (message, closeTimeout) {