diff --git a/include/ajax.forms.php b/include/ajax.forms.php index 2142e8e4faa947b5060539e79a8ef9d2b535025a..1ec760b077e1c934ee13cffa568147eb22897344 100644 --- a/include/ajax.forms.php +++ b/include/ajax.forms.php @@ -36,5 +36,19 @@ class DynamicFormsAjaxAPI extends AjaxController { else $field->save(); } + + function deleteAnswer($entry_id, $field_id) { + global $thisstaff; + + if (!$thisstaff) + Http::response(403, 'Login required'); + + $ent = DynamicFormEntryAnswer::lookup(array( + 'entry_id'=>$entry_id, 'field_id'=>$field_id)); + if (!$ent) + Http::response(404, 'Answer not found'); + + $ent->delete(); + } } ?> diff --git a/include/ajax.tickets.php b/include/ajax.tickets.php index 248ee66d2210156f25ce120e18e603af91484c3e..6869fe7076d668b578d097490dc464553b40b8a3 100644 --- a/include/ajax.tickets.php +++ b/include/ajax.tickets.php @@ -611,5 +611,51 @@ class TicketsAjaxAPI extends AjaxController { } + function manageForms($ticket_id) { + include(STAFFINC_DIR . 'templates/form-manage.tmpl.php'); + } + + function updateForms($ticket_id) { + global $thisstaff; + + if (!$thisstaff) + Http::response(403, "Login required"); + elseif (!($ticket = Ticket::lookup($ticket_id))) + Http::response(404, "No such ticket"); + elseif (!$ticket->checkStaffAccess($thisstaff)) + Http::response(403, "Access Denied"); + elseif (!isset($_POST['forms'])) + Http::response(422, "Send updated forms list"); + + // Add new forms + $forms = DynamicFormEntry::forTicket($ticket_id); + foreach ($_POST['forms'] as $sort => $id) { + $found = false; + foreach ($forms as $e) { + if ($e->get('form_id') == $id) { + $e->set('sort', $sort); + $e->save(); + $found = true; + break; + } + } + // New form added + if (!$found && ($new = DynamicForm::lookup($id))) { + $f = $new->instanciate(); + $f->set('sort', $sort); + $f->setTicketId($ticket_id); + $f->save(); + } + } + + // Deleted forms + foreach ($forms as $idx => $e) { + if (!in_array($e->get('form_id'), $_POST['forms'])) + $e->delete(); + } + + Http::response(201, 'Successfully managed'); + } + } ?> diff --git a/include/class.dynamic_forms.php b/include/class.dynamic_forms.php index c58a22f4644e3d768caf172579fe5b2c2310ff4a..c1b790ccb46410923fcd11364539bd0ed057ad4a 100644 --- a/include/class.dynamic_forms.php +++ b/include/class.dynamic_forms.php @@ -457,6 +457,10 @@ class DynamicFormEntry extends VerySimpleModel { var $_errors = false; var $_clean = false; + function getId() { + return $this->get('id'); + } + function getAnswers() { if (!isset($this->_values)) { $this->_values = DynamicFormEntryAnswer::objects() @@ -579,8 +583,8 @@ class DynamicFormEntry extends VerySimpleModel { ->filter(array('object_id'=>$org_id, 'object_type'=>'O')); } - function render($staff=true, $title=false) { - return $this->getForm()->render($staff, $title); + function render($staff=true, $title=false, $options=array()) { + return $this->getForm()->render($staff, $title, $options); } /** @@ -592,11 +596,15 @@ class DynamicFormEntry extends VerySimpleModel { * entry. */ function addMissingFields() { + // Track deletions + foreach ($this->getAnswers() as $answer) + $answer->deleted = true; + foreach ($this->getForm()->getDynamicFields() as $field) { $found = false; foreach ($this->getAnswers() as $answer) { if ($answer->get('field_id') == $field->get('id')) { - $found = true; break; + $answer->deleted = false; $found = true; break; } } if (!$found && ($field = $field->getImpl($field)) @@ -605,6 +613,7 @@ class DynamicFormEntry extends VerySimpleModel { array('field_id'=>$field->get('id'), 'entry_id'=>$this->id)); $a->field = $field; $a->entry = $this; + $a->deleted = false; // Add to list of answers $this->_values[] = $a; $this->_fields[] = $field; @@ -710,6 +719,7 @@ class DynamicFormEntryAnswer extends VerySimpleModel { var $field; var $form; var $entry; + var $deleted = false; var $_value; function getEntry() { @@ -742,6 +752,10 @@ class DynamicFormEntryAnswer extends VerySimpleModel { return $this->get('value_id'); } + function isDeleted() { + return $this->deleted; + } + function toString() { return $this->getField()->toString($this->getValue()); } diff --git a/include/class.forms.php b/include/class.forms.php index 42f4651a1debd6fd0234244ef6f9f1f6aec10ea7..4eeed67e73c36e51ae944873698a701eb34b4a1e 100644 --- a/include/class.forms.php +++ b/include/class.forms.php @@ -99,11 +99,11 @@ class Form { return $this->_errors; } - function render($staff=true, $title=false, $instructions=false) { + function render($staff=true, $title=false, $options=array()) { if ($title) $this->title = $title; - if ($instructions) - $this->instructions = $instructions; + if (isset($options['instructions'])) + $this->instructions = $options['instructions']; $form = $this; if ($staff) include(STAFFINC_DIR . 'templates/dynamic-form.tmpl.php'); diff --git a/include/class.orm.php b/include/class.orm.php index cdf25599501b82e474893d2ccca18823804d38f1..ef04e3d3e9bb7bb2412c074e9c73e2c57d3d9157 100644 --- a/include/class.orm.php +++ b/include/class.orm.php @@ -319,6 +319,14 @@ class QuerySet implements IteratorAggregate, ArrayAccess { return $this->count() > 0; } + function delete() { + $class = $this->compiler; + $compiler = new $class(); + $ex = $compiler->compileDelete($this); + $ex->execute(); + return $ex->affected_rows(); + } + // IteratorAggregate interface function getIterator() { $class = $this->iterator; @@ -863,7 +871,28 @@ class MySqlCompiler extends SqlCompiler { function compileInsert() { } - function compileDelete() { + function compileDelete($queryset) { + $model = $queryset->model; + $table = $model::$meta['table']; + $where_pos = array(); + $where_neg = array(); + $joins = array(); + foreach ($queryset->constraints as $where) { + $where_pos[] = $this->compileWhere($where, $model); + } + foreach ($queryset->exclusions as $where) { + $where_neg[] = $this->compileWhere($where, $model); + } + + $where = ''; + if ($where_pos || $where_neg) { + $where = ' WHERE '.implode(' AND ', $where_pos) + .implode(' AND NOT ', $where_neg); + } + $joins = $this->getJoins(); + $sql = 'DELETE '.$this->quote($table).'.* FROM ' + .$this->quote($table).$joins.$where; + return new MysqlExecutor($sql, $this->params); } // Returns meta data about the table used to build queries @@ -885,14 +914,18 @@ class MysqlExecutor { } function _prepare() { + $this->execute(); + $this->_setup_output(); + $this->stmt->store_result(); + } + + function execute() { if (!($this->stmt = db_prepare($this->sql))) throw new Exception('Unable to prepare query: '.db_error() .' '.$this->sql); if (count($this->params)) $this->_bind($this->params); - $this->stmt->execute(); - $this->_setup_output(); - $this->stmt->store_result(); + return $this->stmt->execute(); } function _bind($params) { @@ -997,6 +1030,14 @@ class MysqlExecutor { $this->stmt = null; } + function affected_rows() { + return $this->stmt->affected_rows; + } + + function insert_id() { + return $this->stmt->insert_id; + } + function __toString() { return $this->sql; } diff --git a/include/staff/dynamic-form.inc.php b/include/staff/dynamic-form.inc.php index a565e1be5bf5e0b8e51523afaaf8be4d181460b7..86adb1c1315d69c587afaf3ec16e164431fc69be 100644 --- a/include/staff/dynamic-form.inc.php +++ b/include/staff/dynamic-form.inc.php @@ -16,7 +16,7 @@ if($form && $_REQUEST['a']!='add') { $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); ?> -<form action="?id=<?php echo urlencode($_REQUEST['id']); ?>" method="post" id="save"> +<form id="manage-form" action="?id=<?php echo urlencode($_REQUEST['id']); ?>" method="post"> <?php csrf_token(); ?> <input type="hidden" name="do" value="<?php echo $action; ?>"> <input type="hidden" name="id" value="<?php echo $info['id']; ?>"> @@ -167,7 +167,9 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); if ($ferrors['name']) echo '<br/>'; echo $ferrors['name']; ?></font> </td> - <td><input type="checkbox" name="delete-<?php echo $id; ?>" + <td><input class="delete-box" type="checkbox" name="delete-<?php echo $id; ?>" + data-field-label="<?php echo $f->get('label'); ?>" + data-field-id="<?php echo $id; ?>" <?php echo $deletable; ?>/> <input type="hidden" name="sort-<?php echo $id; ?>" value="<?php echo $f->get('sort'); ?>"/> @@ -228,8 +230,63 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <input type="reset" name="reset" value="Reset"> <input type="button" name="cancel" value="Cancel" onclick='window.location.href="?"'> </p> + +<div style="display:none;" class="draggable dialog" id="delete-confirm"> + <h3><i class="icon-trash"></i> Remove Existing Data?</h3> + <a class="close" href=""><i class="icon-remove-circle"></i></a> + <hr/> + <p> + <strong>You are about to delete <span id="deleted-count"></span> fields.</strong> + Would you also like to remove data currently entered for this field? + <em>If you opt not to remove the data now, you will have the option + to delete the the data when editing it</em> + </p><p style="color:red"> + Deleted data CANNOT be recovered. + </p> + <hr> + <div id="deleted-fields"></div> + <hr style="margin-top:1em"/> + <p class="full-width"> + <span class="buttons" style="float:left"> + <input type="button" value="No, Cancel" class="close"> + </span> + <span class="buttons" style="float:right"> + <input type="submit" value="Continue" class="confirm"> + </span> + </p> + <div class="clear"></div> +</div> </form> <div style="display:none;" class="dialog draggable" id="field-config"> <div class="body"></div> </div> + +<script type="text/javascript"> +$('#manage-form').on('submit.inline', function() { + var formObj = this, deleted = $('input.delete-box:checked', this); + if (deleted.length) { + $('#overlay').show(); + $('#deleted-fields').empty(); + deleted.each(function(i, e) { + $('#deleted-fields').append($('<p></p>') + .append($('<input/>').attr({type:'checkbox',name:'delete-data-' + + $(e).data('fieldId')}) + ).append($('<strong>').html( + 'Remove all data entered for <u>' + $(e).data('fieldLabel') + '</u>' + )) + ); + }); + $('#delete-confirm').show().delegate('input.confirm', 'click.confirm', function() { + $('.dialog#delete-confirm').hide(); + $(formObj).unbind('submit.inline'); + $(window).unbind('beforeunload'); + $('#loading').show(); + }) + return false; + } + // TODO: Popup the 'please wait' dialog + $(window).unbind('beforeunload'); + $('#loading').show(); +}); +</script> diff --git a/include/staff/templates/dynamic-form.tmpl.php b/include/staff/templates/dynamic-form.tmpl.php index 09d97ab650ab3d79df393153cda76fb999eeb980..e6afae15a37d7b1ebb80eafa8fb1e341e6c98672 100644 --- a/include/staff/templates/dynamic-form.tmpl.php +++ b/include/staff/templates/dynamic-form.tmpl.php @@ -1,8 +1,35 @@ -<tr><td style="width:150px"></td><td></td></tr> +<?php +// If the form was removed using the trashcan option, and there was some +// other validation error, don't render the deleted form the second time +if (isset($options['entry']) && $options['mode'] == 'edit' + && $_POST + && ($_POST['forms'] && !in_array($options['entry']->getId(), $_POST['forms'])) +) + return; +?> +<tbody> +<?php +// Keep up with the entry id in a hidden field to decide what to add and +// delete when the parent form is submitted +if (isset($options['entry']) && $options['mode'] == 'edit') { ?> + <input type="hidden" name="forms[]" value="<?php + echo $options['entry']->getId(); ?>" /> +<?php } ?> <?php if ($form->getTitle()) { ?> <tr><th colspan="2"> <em><strong><?php echo Format::htmlchars($form->getTitle()); ?></strong>: - <?php echo Format::htmlchars($form->getInstructions()); ?></em> + <?php echo Format::htmlchars($form->getInstructions()); ?> +<?php if ($options['mode'] == 'edit') { ?> + <div class="pull-right"> + <?php if ($options['entry'] + && $options['entry']->getForm()->get('type') == 'G') { ?> + <a href="#" title="Delete Entry" onclick="javascript: + $(this).closest('tbody').remove(); + return false;"><i class="icon-trash"></i></a> + <?php } ?> + <i class="icon-sort" title="Drag to Sort"></i> + </div> +<?php } ?></em> </th></tr> <?php } @@ -13,15 +40,33 @@ <?php } else { ?> - <td class="multi-line <?php if ($field->get('required')) echo 'required'; ?>" style="min-width:120px;"> + <td class="multi-line <?php if ($field->get('required')) echo 'required'; + ?>" style="min-width:120px;" <?php if ($options['width']) + echo "width=\"{$options['width']}\""; ?>> <?php echo Format::htmlchars($field->get('label')); ?>:</td> - <td><?php + <td><div style="position:relative"><?php } $field->render(); ?> <?php if ($field->get('required')) { ?> <font class="error">*</font> <?php } + if (($a = $field->getAnswer()) && $a->isDeleted()) { + ?><a class="action-button danger overlay" title="Delete this data" + href="#delete-answer" + onclick="javascript:if (confirm('You sure?')) + $.ajax({ + url: 'ajax.php/form/answer/' + +$(this).data('entryId') + '/' + $(this).data('fieldId'), + type: 'delete', + success: $.proxy(function() { + $(this).closest('tr').fadeOut(); + }, this) + });" + data-field-id="<?php echo $field->getAnswer()->get('field_id'); + ?>" data-entry-id="<?php echo $field->getAnswer()->get('entry_id'); + ?>"> <i class="icon-trash"></i> </a></div><?php + } if ($field->get('hint') && !$field->isBlockLevel()) { ?> <br /><em style="color:gray;display:inline-block"><?php echo Format::htmlchars($field->get('hint')); ?></em> @@ -31,8 +76,7 @@ <br /> <font class="error"><?php echo Format::htmlchars($e); ?></font> <?php } ?> - </td> + </div></td> </tr> - <?php - } -?> + <?php } ?> +</tbody> diff --git a/include/staff/templates/form-manage.tmpl.php b/include/staff/templates/form-manage.tmpl.php new file mode 100644 index 0000000000000000000000000000000000000000..5e59011df9c12931f013395dbaa11920e0a1214f --- /dev/null +++ b/include/staff/templates/form-manage.tmpl.php @@ -0,0 +1,77 @@ +<h3><i class="icon-paste"></i> Manage Forms</i></h3> +<b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> +<hr/> +Sort the forms on this ticket by click and dragging on them. Use the box +below the forms list to add new forms to the ticket. +<br/> +<br/> +<form method="post" action="#tickets/<?php echo $ticket_id; ?>/forms/manage"> +<div id="ticket-entries"> +<?php +$current_list = array(); +foreach (DynamicFormEntry::forTicket($ticket_id) as $e) { ?> +<div class="sortable-row-item" data-id="<?php echo $e->get('id'); ?>"> + <input type="hidden" name="forms[]" value="<?php echo $e->get('form_id'); ?>" /> + <i class="icon-reorder"></i> <?php echo $e->getForm()->getTitle(); + $current_list[] = $e->get('form_id'); + if ($e->getForm()->get('type') == 'G') { ?> + <div class="delete"><a href="#"><i class="icon-trash"></i></a></div> + <?php } ?> +</div> +<?php } ?> +</div> +<hr/> +<i class="icon-plus"></i> +<select name="new-form" onchange="javascript: + var $sel = $(this).find('option:selected'); + $('#ticket-entries').append($('<div></div>').addClass('sortable-row-item') + .text(' '+$sel.text()) + .data('id', $sel.val()) + .prepend($('<i>').addClass('icon-reorder')) + .append($('<input/>').attr({name:'forms[]', type:'hidden'}).val($sel.val())) + .append($('<div></div>').addClass('delete') + .append($('<a href=\'#\'>').append($('<i>').addClass('icon-trash'))) + ) + ); + $sel.prop('disabled',true);"> +<option selected="selected" disabled="disabled">Add a new form to this ticket</option> +<?php foreach (DynamicForm::objects()->filter(array( + 'type'=>'G')) as $f +) { + if (in_array($f->get('id'), $current_list)) + continue; + ?><option value="<?php echo $f->get('id'); ?>"><?php + echo $f->getTitle(); ?></option><?php +} ?> +</select> +<div id="delete-warning" style="display:none"> +<hr> + <div id="msg_warning"> + Clicking <strong>Save Changes</strong> will permanently delete data + associated with the deleted forms + </div> +</div> + <hr> + <p class="full-width"> + <span class="buttons" style="float:left"> + <input type="reset" value="Reset"> + <input type="button" name="cancel" class="<?php echo $user ? 'cancel' : 'close' ?>" value="Cancel"> + </span> + <span class="buttons" style="float:right"> + <input type="submit" value="Save Changes"> + </span> + </p> + +<script type="text/javascript"> +$(function() { + $('#ticket-entries').sortable({containment:'parent',tolerance:'pointer'}); + $('#ticket-entries .delete a').live('click', function() { + var $div = $(this).closest('.sortable-row-item'); + $('select[name=new-form]').find('option[data-id='+$div.data('id')+']') + .prop('disabled',false); + $div.remove(); + $('#delete-warning').show(); + return false; + }) +}); +</script> diff --git a/include/staff/ticket-edit.inc.php b/include/staff/ticket-edit.inc.php index e60f275c2633c237a51f91eeab1b048bf0abf980..299835b3734d270cbd9e62a73d7fc73bf872c329 100644 --- a/include/staff/ticket-edit.inc.php +++ b/include/staff/ticket-edit.inc.php @@ -128,14 +128,16 @@ if ($_POST) <em>Time is based on your time zone (GMT <?php echo $thisstaff->getTZoffset(); ?>)</em> </td> </tr> - </tbody> - <tbody id="dynamic-form"> + </tbody> +</table> +<table class="form_table dynamic-forms" width="940" border="0" cellspacing="0" cellpadding="2"> <?php if ($forms) foreach ($forms as $form) { - $form->render(true); + $form->render(true, false, array('mode'=>'edit','width'=>160,'entry'=>$form)); } ?> - </tbody> - <tbody> +</table> +<table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2"> + <tbody> <tr> <th colspan="2"> <em><strong>Internal Note</strong>: Reason for editing the ticket (required) <font class="error"> <?php echo $errors['note'];?></font></em> @@ -159,3 +161,18 @@ if ($_POST) <div style="display:none;" class="dialog draggable" id="user-lookup"> <div class="body"></div> </div> +<script type="text/javascript"> +$('table.dynamic-forms').sortable({ + items: 'tbody', + handle: 'th', + helper: function(e, ui) { + ui.children().each(function() { + $(this).children().each(function() { + $(this).width($(this).width()); + }); + }); + ui=ui.clone().css({'background-color':'white', 'opacity':0.8}); + return ui; + } +}); +</script> diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php index fc636f515dee5388adaa6ee20d554137238f3b0a..d2c8ebe9a72cc4c88c2ef56ab230bf9236b0814b 100644 --- a/include/staff/ticket-view.inc.php +++ b/include/staff/ticket-view.inc.php @@ -107,9 +107,14 @@ if($ticket->isOverdue()) <li><a class="confirm-action" id="ticket-answered" href="#answered"><i class="icon-circle-arrow-right"></i> Mark as Answered</a></li> <?php } - } + } ?> + <li><a href="#ajax.php/tickets/<?php echo $ticket->getId(); + ?>/forms/manage" onclick="javascript: + $.dialog($(this).attr('href').substr(1), 201); + return false" + ><i class="icon-paste"></i> Manage Forms</a></li> - if($thisstaff->canBanEmails()) { +<?php if($thisstaff->canBanEmails()) { if(!$emailBanned) {?> <li><a class="confirm-action" id="ticket-banemail" href="#banemail"><i class="icon-ban-circle"></i> Ban Email (<?php echo $ticket->getEmail(); ?>)</a></li> diff --git a/scp/ajax.php b/scp/ajax.php index b13822d17c0ebefd04d02a89d4af76c79d8c9980..5d12a8b43ebc215fce392f197b203bf842cfd1ec 100644 --- a/scp/ajax.php +++ b/scp/ajax.php @@ -53,7 +53,8 @@ $dispatcher = patterns('', url('^/form/', patterns('ajax.forms.php:DynamicFormsAjaxAPI', url_get('^help-topic/(?P<id>\d+)$', 'getFormsForHelpTopic'), url_get('^field-config/(?P<id>\d+)$', 'getFieldConfiguration'), - url_post('^field-config/(?P<id>\d+)$', 'saveFieldConfiguration') + url_post('^field-config/(?P<id>\d+)$', 'saveFieldConfiguration'), + url_delete('^answer/(?P<entry>\d+)/(?P<field>\d+)$', 'deleteAnswer') )), url('^/report/overview/', patterns('ajax.reports.php:OverviewReportAjaxAPI', # Send @@ -117,7 +118,9 @@ $dispatcher = patterns('', url_get('^(?P<tid>\d+)/add-collaborator/auth:(?P<bk>\w+):(?P<id>.+)$', 'addRemoteCollaborator'), url('^(?P<tid>\d+)/add-collaborator$', 'addCollaborator'), url_get('^lookup', 'lookup'), - url_get('^search', 'search') + url_get('^search', 'search'), + url_get('^(?P<tid>\d+)/forms/manage$', 'manageForms'), + url_post('^(?P<tid>\d+)/forms/manage$', 'updateForms') )), url('^/collaborators/', patterns('ajax.tickets.php:TicketsAjaxAPI', url_get('^(?P<cid>\d+)/view$', 'viewCollaborator'), diff --git a/scp/css/scp.css b/scp/css/scp.css index 50bea7ea4bc26d2cc5634889b0ff35e6c9ec3d34..55b706178234fb2d8a20134cbb55f35553176d2c 100644 --- a/scp/css/scp.css +++ b/scp/css/scp.css @@ -1559,3 +1559,73 @@ div.selected-signature { div.selected-signature .inner { opacity: 0.5; } + +.action-button.danger:hover { + opacity: 1.0; + border-color: rgba(95,75,0,0.8) !important; + background: #fc9f41; /* Old browsers */ + color: rgba(255,255,255,0.8) !important; + /* IE9 SVG, needs conditional override of 'filter' to 'none' */ + background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZjOWY0MSIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNjZTgxMTQiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+); + background: -moz-linear-gradient(top, #fc9f41 0%, #ce8114 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#fc9f41), color-stop(100%,#ce8114)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #fc9f41 0%,#ce8114 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #fc9f41 0%,#ce8114 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(top, #fc9f41 0%,#ce8114 100%); /* IE10+ */ + background: linear-gradient(to bottom, #fc9f41 0%,#ce8114 100%); /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#fc9f41', endColorstr='#ce8114',GradientType=0 ); /* IE6-8 */ +} +.action-button.danger { + color: #999 !important; + background: transparent; + border: 1px solid rgba(0,0,0,0.5); + opacity: 0.6; +} + +.action-button.overlay { + float: none; + position: absolute; + top: 4px; + right: 5px; +} + +.sortable-row-item { + border: 1px solid rgba(0, 0, 0, 0.7); + padding: 9px; + cursor: move; + position: relative; +} +.sortable-row-item:hover { + background: rgba(0, 0, 0, 0.1); +} +.sortable-row-item:active { + background: rgba(0, 0, 0, 0.3); +} +.sortable-row-item:first-child { + border-top-right-radius: 5px; + border-top-left-radius: 5px; +} +.sortable-row-item:last-child { + border-bottom-right-radius: 5px; + border-bottom-left-radius: 5px; +} +.sortable-row-item + .sortable-row-item { + margin-top: -1px; +} + +.sortable-row-item .delete { + border-left: 1px solid rgba(0, 0, 0, 0.7); + border-top-right-radius: inherit; + border-bottom-right-radius: inherit; + position: absolute; + top: 0px; + right: 0; + padding: 9px; + padding-left: 12px; + font-size: 105%; +} + +.sortable-row-item .delete:hover { + background: #fc9f41; /* Old browsers */ + color: rgba(255,255,255,0.8) !important; +} diff --git a/scp/forms.php b/scp/forms.php index 077b3e0ff409c5b84b0fa6babd4751ad775efad1..43e66242aa85f55ea2baad290a9fa29369990055 100644 --- a/scp/forms.php +++ b/scp/forms.php @@ -23,6 +23,11 @@ if($_POST) { foreach ($form->getDynamicFields() as $field) { $id = $field->get('id'); if ($_POST["delete-$id"] == 'on' && $field->isDeletable()) { + if ($_POST["delete-data-$id"]) { + DynamicFormEntryAnswer::objects() + ->filter(array('field_id'=>$id)) + ->delete(); + } $field->delete(); // Don't bother updating the field continue; diff --git a/scp/tickets.php b/scp/tickets.php index f679f5035a2355ef80482f4a8084f09975c25427..2a8c9e6ee4938a4a19ea57812c0a5559499f4255 100644 --- a/scp/tickets.php +++ b/scp/tickets.php @@ -198,16 +198,30 @@ if($_POST && !$errors): case 'edit': case 'update': $forms=DynamicFormEntry::forTicket($ticket->getId()); - foreach ($forms as $form) - if (!$form->isValid()) + foreach ($forms as $form) { + // Don't validate deleted forms + if (!in_array($form->getId(), $_POST['forms'])) + continue; + elseif (!$form->isValid()) $errors = array_merge($errors, $form->errors()); + } if(!$ticket || !$thisstaff->canEditTickets()) $errors['err']='Permission Denied. You are not allowed to edit tickets'; elseif($ticket->update($_POST,$errors)) { $msg='Ticket updated successfully'; $_REQUEST['a'] = null; //Clear edit action - going back to view. //Check to make sure the staff STILL has access post-update (e.g dept change). - foreach ($forms as $f) $f->save(); + foreach ($forms as $f) { + // Drop deleted forms + $idx = array_search($f->getId(), $_POST['forms']); + if ($idx === false) { + $f->delete(); + } + else { + $f->set('sort', $idx); + $f->save(); + } + } if(!$ticket->checkStaffAccess($thisstaff)) $ticket=null; } elseif(!$errors['err']) {