diff options
| -rw-r--r-- | public/js/util.js | 34 | ||||
| -rwxr-xr-x | views/event.handlebars | 110 | ||||
| -rwxr-xr-x | views/eventgroup.handlebars | 114 | ||||
| -rwxr-xr-x | views/layouts/main.handlebars | 1 | ||||
| -rw-r--r-- | views/partials/editeventgroupmodal.handlebars | 14 | ||||
| -rw-r--r-- | views/partials/editeventmodal.handlebars | 11 | 
6 files changed, 263 insertions, 21 deletions
diff --git a/public/js/util.js b/public/js/util.js new file mode 100644 index 0000000..e2e9938 --- /dev/null +++ b/public/js/util.js @@ -0,0 +1,34 @@ +const getStoredToken = function(eventID) { +    try { +        let editTokens = JSON.parse(localStorage.getItem('editTokens')); +        return editTokens[eventID]; +    } catch(e) { +        console.error(e); +        localStorage.setItem('editTokens', JSON.stringify({})); +        return false; +    } +} + +const addStoredToken = function(eventID, token) { +    try { +        let editTokens = JSON.parse(localStorage.getItem('editTokens')); +        editTokens[eventID] = token; +        localStorage.setItem('editTokens', JSON.stringify(editTokens)); +    } catch(e) { +        console.error(e); +        localStorage.setItem('editTokens', JSON.stringify({ [eventID]: token })); +        return false; +    } +}  + +const removeStoredToken = function(eventID) { +    try { +        let editTokens = JSON.parse(localStorage.getItem('editTokens')); +        delete editTokens[eventID]; +        localStorage.setItem('editTokens', JSON.stringify(editTokens)); +    } catch(e) { +        console.error(e); +        localStorage.setItem('editTokens', JSON.stringify({})); +        return false; +    } +} diff --git a/views/event.handlebars b/views/event.handlebars index 6a65f07..4fb1a46 100755 --- a/views/event.handlebars +++ b/views/event.handlebars @@ -5,16 +5,15 @@  {{/if}}  <div class="row">    <div class="col-lg"> -    <h3 id="eventName">{{eventData.name}}</h3> +      <h3 id="eventName" data-event-id="{{eventData.id}}">{{eventData.name}}</h3>    </div> -  {{#if editingEnabled}}    <div class="col-lg-3 ml-2 edit-buttons"> -    <div class="btn-group" role="group" aria-label="Event controls"> -      <button type="button" id="editEvent" class="btn btn-success" data-toggle="modal" data-target="#editModal" {{#if eventHasConcluded}}disabled{{/if}}><i class="fas fa-edit"></i> Edit</button> -      <button type="button" id="deleteEvent" class="btn btn-danger" data-toggle="modal" data-target="#deleteModal"><i class="fas fa-trash"></i> Delete</button> -    </div> +    {{#if editingEnabled}} +      <button type="button" id="editEvent" class="btn btn-success" {{#if eventHasConcluded}}disabled{{/if}} data-event-id="{{eventData.id}}" data-toggle="modal" data-target="#editModal"><i class="fas fa-edit"></i> Edit event</button> +    {{else}} +      <button type="button" id="editEvent" class="btn btn-success" {{#if eventHasConcluded}}disabled{{/if}} data-event-id="{{eventData.id}}" data-toggle="modal" data-target="#editTokenModal"><i class="fas fa-edit"></i> Edit event</button> +    {{/if}}    </div> -  {{/if}}  </div>  <div class="container my-4 pr-0">    <div class="row"> @@ -324,6 +323,36 @@  </div>  {{/if}} +<div class="modal fade" id="editTokenModal" tabindex="-1" role="dialog" aria-labelledby="editTokenModalLabel" aria-hidden="true"> +  <div class="modal-dialog" role="document"> +    <div class="modal-content"> +      <div class="modal-header"> +        <h5 class="modal-title" id="editTokenModalLabel">Enter editing password</h5> +        <button type="button" class="close" data-dismiss="modal" aria-label="Close"> +          <span aria-hidden="true">×</span> +        </button> +      </div> +      <form id="verifyTokenForm" action="/verifytoken/event/{{eventData.id}}" method="post"> +      <div class="modal-body"> +        <div class="form-group"> +          <p class="form-text small">Enter the editing password you received by email or were shown when the event was created.</p> +          <div class="form-group"> +            <input type="text" class="form-control" id="editToken" name="editToken" placeholder="Get it right!" data-validation="required"> +          </div> +          <div class="form-group"> +              <div class="alert alert-danger" style="display:none;"></div> +          </div> +        </div> +      </div> +      <div class="modal-footer"> +        <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> +        <button type="submit" class="btn btn-primary">Edit event</button> +      </div> +      </form> +    </div> +  </div> +</div> +  {{#if editingEnabled}}  {{#unless eventHasConcluded}}  {{> editeventmodal }} @@ -338,7 +367,7 @@            <span aria-hidden="true">×</span>          </button>        </div> -      <form id="editEventForm" action="/deleteevent/{{eventData.id}}/{{eventData.editToken}}" method="post"> +      <form id="deleteEventForm" action="/deleteevent/{{eventData.id}}" method="post">        <div class="modal-body">          <p>Are you sure you want to delete this event? This action cannot be undone.</p>        </div> @@ -403,6 +432,47 @@    })    $(document).ready(function() { +    // Save the editing token from the URL, if it is valid +    const eventID = $('#eventName').attr('data-event-id'); +    const urlParams = new URLSearchParams(window.location.search); +    if (urlParams.has('e')) { +      $.ajax({ +        type: "POST", +        url: `/verifytoken/event/${eventID}`, +        data: { editToken: urlParams.get('e') }, +        success: function(response, status, xhr) { +          if (xhr.status === 200) { +            addStoredToken(eventID, urlParams.get('e')); +          } +        }, +        error: function(response, status, xhr) { +          // The editing token is wrong - remove it +          removeStoredToken(eventID); +          window.location = window.location.pathname; +        } +      }); +    } else if (getStoredToken(eventID)) { +      const editToken = getStoredToken(eventID); +      $.ajax({ +        type: "POST", +        url: `/verifytoken/event/${eventID}`, +        data: { editToken }, +        success: function(response, status, xhr) { +          if (xhr.status === 200) { +            window.location.search = `?e=${editToken}`; +          } +        }, +        error: function(response, status, xhr) { +          // The editing token is wrong - remove it +          removeStoredToken(eventID); +        } +      }); +    } + +    if (urlParams.has('show_edit')) { +      $('#editModal').modal('show'); +    } +      // From https://davidwalsh.name/javascript-download      function downloadFile(data, fileName, type="text/plain") {        // Create an invisible A element @@ -502,6 +572,30 @@        const passphrase = window.niceware.generatePassphrase(6).join('-');        modal.find('#removeAttendancePassword').val(passphrase);      }); + +      $('#verifyTokenForm').on('submit', function(e) { +        e.preventDefault(); +        let form = $(this); +        $.ajax({ +            type: "POST", +            url: form.attr('action'), +            data: form.serialize(), +            success: function(response, status, xhr) { +                if (xhr.status === 200) { +                    // Save the token to localStorage for later +                    addStoredToken($('#eventName').attr('data-event-id'), new FormData(form[0]).get('editToken')); +                    window.location.search = `?e=${new FormData(form[0]).get('editToken')}&show_edit=true`; +                } +            }, +            error: function(response, status, xhr) { +               form.find('.alert').text('That editing password is incorrect. Try again.').show(); +            } +        }); +      }); + +    $('#deleteEvent').on('click', function() { +        $('#editModal').modal('hide'); +    })    });    </script> diff --git a/views/eventgroup.handlebars b/views/eventgroup.handlebars index 03a5b27..d7726bd 100755 --- a/views/eventgroup.handlebars +++ b/views/eventgroup.handlebars @@ -5,16 +5,15 @@  {{/if}}  <div class="row">    <div class="col-lg"> -    <h3 id="eventName">{{eventGroupData.name}}</h3> +    <h3 id="eventName" data-event-id="{{eventGroupData.id}}">{{eventGroupData.name}}</h3>    </div> -  {{#if editingEnabled}}    <div class="col-lg-2 ml-2 edit-buttons"> -    <div class="btn-group" role="group" aria-label="Event controls"> -      <button type="button" id="editEvent" class="btn btn-success" data-toggle="modal" data-target="#editModal" ><i class="fas fa-edit"></i></button> -      <button type="button" id="deleteEvent" class="btn btn-danger" data-toggle="modal" data-target="#deleteModal"><i class="fas fa-trash"></i></button> -    </div> +    {{#if editingEnabled}} +      <button type="button" id="editEvent" class="btn btn-success text-nowrap" data-event-id="{{eventGroupData.id}}" data-toggle="modal" data-target="#editModal"><i class="fas fa-edit"></i> Edit group</button> +    {{else}} +      <button type="button" id="editEvent" class="btn btn-success text-nowrap" data-event-id="{{eventGroupData.id}}" data-toggle="modal" data-target="#editTokenModal"><i class="fas fa-edit"></i> Edit group</button> +    {{/if}}    </div> -  {{/if}}  </div>  {{#if firstLoad}}  <div class="alert alert-success alert-dismissible fade show" role="alert"> @@ -70,7 +69,7 @@            <td><span class="code">{{eventGroupData.id}}</span></td>          </tr>          <tr> -          <td><strong>Event group secret editing code</strong></td> +          <td><strong>Event group editing password</strong></td>            <td><span class="code">{{eventGroupData.editToken}}</span></td>          </tr>        </table> @@ -116,7 +115,7 @@            <span aria-hidden="true">×</span>          </button>        </div> -      <form action="/deleteeventgroup/{{eventGroupData.id}}/{{eventGroupData.editToken}}" method="post"> +      <form id="deleteEventGroupForm" action="/deleteeventgroup/{{eventGroupData.id}}/{{eventGroupData.editToken}}" method="post">        <div class="modal-body">          <p>Are you sure you want to delete this event group? This action cannot be undone.</p>          <p>This will <strong>not</strong> delete the individual events contained in this group. They can be linked to another group later.</p> @@ -132,6 +131,37 @@  {{/if}} +<div class="modal fade" id="editTokenModal" tabindex="-1" role="dialog" aria-labelledby="editTokenModalLabel" aria-hidden="true"> +  <div class="modal-dialog" role="document"> +    <div class="modal-content"> +      <div class="modal-header"> +        <h5 class="modal-title" id="editTokenModalLabel">Enter editing password</h5> +        <button type="button" class="close" data-dismiss="modal" aria-label="Close"> +          <span aria-hidden="true">×</span> +        </button> +      </div> +      <form id="verifyTokenForm" action="/verifytoken/group/{{eventGroupData.id}}" method="post"> +      <div class="modal-body"> +        <div class="form-group"> +          <p class="form-text small">Enter the editing password you received by email or were shown when the event was created.</p> +          <div class="form-group"> +            <input type="text" class="form-control" id="editToken" name="editToken" placeholder="Get it right!" data-validation="required"> +          </div> +          <div class="form-group"> +              <div class="alert alert-danger" style="display:none;"></div> +          </div> +        </div> +      </div> +      <div class="modal-footer"> +        <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> +        <button type="submit" class="btn btn-primary">Edit group</button> +      </div> +      </form> +    </div> +  </div> +</div> + +  <script>    $.validate({      lang: 'en', @@ -141,6 +171,47 @@    });    $(document).ready(function() { +    // Save the editing token from the URL, if it is valid +    const eventID = $('#eventName').attr('data-event-id'); +    const urlParams = new URLSearchParams(window.location.search); +    if (urlParams.has('e')) { +      $.ajax({ +        type: "POST", +        url: `/verifytoken/group/${eventID}`, +        data: { editToken: urlParams.get('e') }, +        success: function(response, status, xhr) { +          if (xhr.status === 200) { +            addStoredToken(eventID, urlParams.get('e')); +          } +        }, +        error: function(response, status, xhr) { +          // The editing token is wrong - remove it +          removeStoredToken(eventID); +          window.location = window.location.pathname; +        } +      }); +    } else if (getStoredToken(eventID)) { +      const editToken = getStoredToken(eventID); +      $.ajax({ +        type: "POST", +        url: `/verifytoken/group/${eventID}`, +        data: { editToken }, +        success: function(response, status, xhr) { +          if (xhr.status === 200) { +            window.location.search = `?e=${editToken}`; +          } +        }, +        error: function(response, status, xhr) { +          // The editing token is wrong - remove it +          removeStoredToken(eventID); +        } +      }); +    } + +    if (urlParams.has('show_edit')) { +      $('#editModal').modal('show'); +    } +      $.uploadPreview({        input_field: "#eventGroupImageUpload",        preview_box: "#eventGroupImagePreview", @@ -157,6 +228,31 @@      $("#copyEventLink").click(function(){        $(this).html('<i class="fas fa-copy"></i> Copied!');        setTimeout(function(){ $("#copyEventLink").html('<i class="fas fa-copy"></i> Copy');}, 5000); +    }); + +      $('#verifyTokenForm').on('submit', function(e) { +        e.preventDefault(); +        let form = $(this); +        $.ajax({ +            type: "POST", +            url: form.attr('action'), +            data: form.serialize(), +            success: function(response, status, xhr) { +                if (xhr.status === 200) { +                    // Save the token to localStorage for later +                    addStoredToken($('#eventName').attr('data-event-id'), new FormData(form[0]).get('editToken')); +                    window.location.search = `?e=${new FormData(form[0]).get('editToken')}&show_edit=true`; +                } +            }, +            error: function(response, status, xhr) { +               form.find('.alert').text('That editing password is incorrect. Try again.').show(); +            } +        }); +      }); + +    $('#deleteEvent').on('click', function() { +        $('#editModal').modal('hide');      }) +    });  </script> diff --git a/views/layouts/main.handlebars b/views/layouts/main.handlebars index 029e761..7e44810 100755 --- a/views/layouts/main.handlebars +++ b/views/layouts/main.handlebars @@ -51,6 +51,7 @@      <script src="/js/i18n/datepicker.en.js"></script>      <script src="/js/select2.min.js"></script>      <script src="/js/moment-timezone.js"></script> +    <script src="/js/util.js"></script>    </head> diff --git a/views/partials/editeventgroupmodal.handlebars b/views/partials/editeventgroupmodal.handlebars index a6102fa..3b8f55a 100644 --- a/views/partials/editeventgroupmodal.handlebars +++ b/views/partials/editeventgroupmodal.handlebars @@ -1,5 +1,5 @@  <div class="modal fade" id="editModal" tabindex="-1" role="dialog" aria-labelledby="editModalLabel" aria-hidden="true"> -  <div class="modal-dialog" role="document"> +  <div class="modal-dialog modal-xl modal-dialog-scrollable" role="document">      <div class="modal-content">        <div class="modal-header">          <h5 class="modal-title" id="editModalLabel">Edit '{{eventGroupData.name}}'</h5> @@ -7,8 +7,8 @@            <span aria-hidden="true">×</span>          </button>        </div> -      <form id="editEventForm" action="/editeventgroup/{{eventGroupData.id}}/{{eventGroupData.editToken}}" method="post" enctype="multipart/form-data" autocomplete="off">        <div class="modal-body"> +      <form id="editEventForm" action="/editeventgroup/{{eventGroupData.id}}/{{eventGroupData.editToken}}" method="post" enctype="multipart/form-data" autocomplete="off">          <div class="form-group">            <label for="eventGroupName" >Name</label>            <input type="text" class="form-control" id="eventGroupName" name="eventGroupName" placeholder="Make it snappy." value="{{eventGroupData.name}}" data-validation="required length" data-validation-length="3-120"> @@ -34,12 +34,20 @@            </div>            <small class="form-text">Recommended dimensions (w x h): 920px by 300px.</small>          </div> +        <div class="form-group"> +          <div class="card border-danger mb-3"> +            <div class="card-header text-danger">Delete this event group</div> +            <div class="card-body text-danger"> +              <button type="button" id="deleteEvent" class="btn btn-danger" data-toggle="modal" data-target="#deleteModal"><i class="fas fa-trash"></i> Delete event group</button> +            </div> +          </div> +        </div>        </div>        <div class="modal-footer">          <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>          <button type="submit" class="btn btn-primary">Save changes</button> -      </div>        </form> +      </div>      </div>    </div>  </div> diff --git a/views/partials/editeventmodal.handlebars b/views/partials/editeventmodal.handlebars index a1ccd83..b4b0ea6 100644 --- a/views/partials/editeventmodal.handlebars +++ b/views/partials/editeventmodal.handlebars @@ -1,5 +1,5 @@  <div class="modal fade" id="editModal" tabindex="-1" role="dialog" aria-labelledby="editModalLabel" aria-hidden="true"> -  <div class="modal-dialog" role="document"> +  <div class="modal-dialog modal-xl modal-dialog-scrollable" role="document">      <div class="modal-content">        <div class="modal-header">          <h5 class="modal-title" id="editModalLabel">Edit '{{eventData.name}}'</h5> @@ -133,6 +133,15 @@                placeholder="Enter a number." data-validation="number" data-validation-optional="true"                value="{{eventData.maxAttendees}}">            </div> + +          <div class="form-group"> +            <div class="card border-danger mb-3"> +              <div class="card-header text-danger">Delete this event</div> +              <div class="card-body text-danger"> +                <button type="button" id="deleteEvent" class="btn btn-danger" data-toggle="modal" data-target="#deleteModal" data-event-id="{{eventData.id}}"><i class="fas fa-trash"></i> Delete</button> +              </div> +            </div> +          </div>        </div>        <div class="modal-footer">          <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>  | 
