From 712a8c228b317a97f504f8cdf70528f08e2cfdc7 Mon Sep 17 00:00:00 2001 From: Calvin Montgomery Date: Tue, 22 Aug 2017 22:09:48 -0700 Subject: [PATCH] Refactor most pug templates to share a common layout template --- templates/account-channels.pug | 183 ++++++++++----------- templates/account-edit.pug | 165 +++++++++---------- templates/account-passwordrecover.pug | 41 ++--- templates/account-passwordreset.pug | 58 +++---- templates/account-profile.pug | 130 +++++++-------- templates/contact.pug | 73 ++++----- templates/csrferror.pug | 44 ++--- templates/google_drive_userscript.pug | 102 +++++------- templates/httperror.pug | 37 ++--- templates/index.pug | 68 +++----- templates/layout.pug | 22 +++ templates/login.pug | 93 +++++------ templates/logout.pug | 32 ++-- templates/register.pug | 225 ++++++++++++-------------- 14 files changed, 552 insertions(+), 721 deletions(-) create mode 100644 templates/layout.pug diff --git a/templates/account-channels.pug b/templates/account-channels.pug index b1d6aa7b..7b2a1dc8 100644 --- a/templates/account-channels.pug +++ b/templates/account-channels.pug @@ -1,103 +1,88 @@ -doctype html -html(lang="en") - head - include head - +head() - body - #wrap - nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation") - include nav - +navheader() - #nav-collapsible.collapse.navbar-collapse - ul.nav.navbar-nav - +navdefaultlinks() - +navloginlogout() - section#mainpage - .container - if !loggedIn - .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3 - .alert.alert-danger.messagebox.center - strong Authorization Required - p You must be logged in to view this page. - else - .col-lg-6.col-md-6 - h3 My Channels - if deleteChannelError - .alert.alert-danger.center.messagebox - strong Channel Deletion Failed - p= deleteChannelError - if channels.length == 0 - .center - strong You haven't registered any channels - else - table.table.table-bordered - thead - tr - th Channel - tbody - for c in channels - tr - th - form.form-inline.pull-right(action="/account/channels", method="post", onsubmit="return confirm('Are you sure you want to delete " +c.name+ "? This cannot be undone');") - input(type="hidden", name="_csrf", value=csrfToken) - input(type="hidden", name="action", value="delete_channel") - input(type="hidden", name="name", value=c.name) - button.btn.btn-xs.btn-danger(type="submit") Delete - span.glyphicon.glyphicon-trash - a(href=`/${channelPath}/${c.name}`, style="margin-left: 5px")= c.name - .col-lg-6.col-md-6 - h3 Register a new channel - if newChannelError - .alert.alert-danger.messagebox.center - strong Channel Registration Failed - p= newChannelError - form(action="/account/channels", method="post") - input(type="hidden", name="_csrf", value=csrfToken) - input(type="hidden", name="action", value="new_channel") - .form-group - label.control-label(for="channelname") Channel URL - .input-group - span.input-group-addon #{baseUrl}/#{channelPath}/ - input#channelname.form-control(type="text", name="name", maxlength="30", onkeyup="checkChannel()") - p#validate_channel.text-danger.pull-right - button#register.btn.btn-primary.btn-block(type="submit") Register +extends layout.pug - include footer - +footer() - script( type='text/javascript'). - function checkChannel(){ - function nameIsInvalid(id){ - if(/\s/.test(id)){ - return 'Channel URL may not contain spaces'; - } - if(id === ''){ - return 'Channel URL must not be empty'; - } - if(!/^[\w-]{1,30}$/.test(id)){ - return 'Channel URL may only consist of a-z, A-Z, 0-9, - and _'; - } - return false; +block content + if !loggedIn + .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3 + .alert.alert-danger.messagebox.center + strong Authorization Required + p You must be logged in to view this page. + else + .col-lg-6.col-md-6 + h3 My Channels + if deleteChannelError + .alert.alert-danger.center.messagebox + strong Channel Deletion Failed + p= deleteChannelError + if channels.length == 0 + .center + strong You haven't registered any channels + else + table.table.table-bordered + thead + tr + th Channel + tbody + for c in channels + tr + th + form.form-inline.pull-right(action="/account/channels", method="post", onsubmit="return confirm('Are you sure you want to delete " +c.name+ "? This cannot be undone');") + input(type="hidden", name="_csrf", value=csrfToken) + input(type="hidden", name="action", value="delete_channel") + input(type="hidden", name="name", value=c.name) + button.btn.btn-xs.btn-danger(type="submit") Delete + span.glyphicon.glyphicon-trash + a(href=`/${channelPath}/${c.name}`, style="margin-left: 5px")= c.name + .col-lg-6.col-md-6 + h3 Register a new channel + if newChannelError + .alert.alert-danger.messagebox.center + strong Channel Registration Failed + p= newChannelError + form(action="/account/channels", method="post") + input(type="hidden", name="_csrf", value=csrfToken) + input(type="hidden", name="action", value="new_channel") + .form-group + label.control-label(for="channelname") Channel URL + .input-group + span.input-group-addon #{baseUrl}/#{channelPath}/ + input#channelname.form-control(type="text", name="name", maxlength="30", onkeyup="checkChannel()") + p#validate_channel.text-danger.pull-right + button#register.btn.btn-primary.btn-block(type="submit") Register + +append footer + script(type='text/javascript'). + function checkChannel(){ + function nameIsInvalid(id){ + if(/\s/.test(id)){ + return 'Channel URL may not contain spaces'; } - - var box = $("#channelname"); - var value = box.val(); - var lastkey = Date.now(); - box.data("lastkey", lastkey); - - setTimeout(function () { - if (box.data("lastkey") !== lastkey || box.val() !== value) { - return; - } - if(nameIsInvalid(value)){ - $('#validate_channel').text(nameIsInvalid(value)) - .parent().addClass('has-error').removeClass('has-success'); - $('#register').addClass('disabled'); - } else { - $('#validate_channel').text('') - .parent().addClass('has-success').removeClass('has-error'); - $('#register').removeClass('disabled'); - } - }, 200); - + if(id === ''){ + return 'Channel URL must not be empty'; + } + if(!/^[\w-]{1,30}$/.test(id)){ + return 'Channel URL may only consist of a-z, A-Z, 0-9, - and _'; + } + return false; } + var box = $("#channelname"); + var value = box.val(); + var lastkey = Date.now(); + box.data("lastkey", lastkey); + + setTimeout(function () { + if (box.data("lastkey") !== lastkey || box.val() !== value) { + return; + } + if(nameIsInvalid(value)){ + $('#validate_channel').text(nameIsInvalid(value)) + .parent().addClass('has-error').removeClass('has-success'); + $('#register').addClass('disabled'); + } else { + $('#validate_channel').text('') + .parent().addClass('has-success').removeClass('has-error'); + $('#register').removeClass('disabled'); + } + }, 200); + + } diff --git a/templates/account-edit.pug b/templates/account-edit.pug index bc8881b1..2882cfbd 100644 --- a/templates/account-edit.pug +++ b/templates/account-edit.pug @@ -1,96 +1,83 @@ -doctype html -html(lang="en") - head - include head - +head() - body - #wrap - nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation") - include nav - +navheader() - #nav-collapsible.collapse.navbar-collapse - ul.nav.navbar-nav - +navdefaultlinks() - +navloginlogout() - section#mainpage - .container - if !loggedIn - .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3 - .alert.alert-danger.messagebox.center - strong Authorization Required - p You must be logged in to view this page. - else - .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3 - if successMessage - .alert.alert-success.center - p= successMessage - else if errorMessage - .alert.alert-danger.center - p= errorMessage - h3 Change Password - form(action="/account/edit", method="post", onsubmit="return validatePasswordChange()") - input(type="hidden", name="_csrf", value=csrfToken) - input(type="hidden", name="action", value="change_password") - .form-group - label.control-label(for="username") Username - input#username.form-control(type="text", name="name", value=loginName, disabled=true) - .form-group - label.control-label(for="oldpassword") Current Password - input#oldpassword.form-control(type="password", name="oldpassword") - .form-group - label.control-label(for="newpassword") New Password - input#newpassword.form-control(type="password", name="newpassword") - .form-group - label.control-label(for="newpassword_confirm") Confirm New Password - input#newpassword_confirm.form-control(type="password", name="newpassword_confirm") - button#changepassbtn.btn.btn-danger.btn-block(type="submit") Change Password - hr - h3 Change Email - form(action="/account/edit", method="post", onsubmit="return submitEmail()") - input(type="hidden", name="_csrf", value=csrfToken) - input(type="hidden", name="action", value="change_email") - .form-group - label.control-label(for="username2") Username - input#username2.form-control(type="text", name="name", value=loginName, disabled=true) - .form-group - label.control-label(for="password2") Password - input#password2.form-control(type="password", name="password") - .form-group - label.control-label(for="email") New Email - input#email.form-control(type="email", name="email") - button#changeemailbtn.btn.btn-danger.btn-block(type="submit") Change Email - include footer - +footer() - script(type="text/javascript"). - function validatePasswordChange() { - var pw = $("#newpassword").val(); - var pwc = $("#newpassword_confirm").val(); - $("#passwordempty").remove(); - $("#passwordmismatch").remove(); +extends layout.pug - if (pw === '') { +block content + if !loggedIn + .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3 + .alert.alert-danger.messagebox.center + strong Authorization Required + p You must be logged in to view this page. + else + .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3 + if successMessage + .alert.alert-success.center + p= successMessage + else if errorMessage + .alert.alert-danger.center + p= errorMessage + h3 Change Password + form(action="/account/edit", method="post", onsubmit="return validatePasswordChange()") + input(type="hidden", name="_csrf", value=csrfToken) + input(type="hidden", name="action", value="change_password") + .form-group + label.control-label(for="username") Username + input#username.form-control(type="text", name="name", value=loginName, disabled=true) + .form-group + label.control-label(for="oldpassword") Current Password + input#oldpassword.form-control(type="password", name="oldpassword") + .form-group + label.control-label(for="newpassword") New Password + input#newpassword.form-control(type="password", name="newpassword") + .form-group + label.control-label(for="newpassword_confirm") Confirm New Password + input#newpassword_confirm.form-control(type="password", name="newpassword_confirm") + button#changepassbtn.btn.btn-danger.btn-block(type="submit") Change Password + hr + h3 Change Email + form(action="/account/edit", method="post", onsubmit="return submitEmail()") + input(type="hidden", name="_csrf", value=csrfToken) + input(type="hidden", name="action", value="change_email") + .form-group + label.control-label(for="username2") Username + input#username2.form-control(type="text", name="name", value=loginName, disabled=true) + .form-group + label.control-label(for="password2") Password + input#password2.form-control(type="password", name="password") + .form-group + label.control-label(for="email") New Email + input#email.form-control(type="email", name="email") + button#changeemailbtn.btn.btn-danger.btn-block(type="submit") Change Email + +append footer + script(type="text/javascript"). + function validatePasswordChange() { + var pw = $("#newpassword").val(); + var pwc = $("#newpassword_confirm").val(); + $("#passwordempty").remove(); + $("#passwordmismatch").remove(); + + if (pw === '') { + $("#newpassword").parent().addClass("has-error"); + $("

").addClass("text-danger") + .attr("id", "passwordempty") + .text("Password must not be empty") + .insertAfter($("#newpassword")); + return false; + } else { + if (pw !== pwc) { + $("#newpassword_confirm").parent().addClass("has-error"); $("#newpassword").parent().addClass("has-error"); $("

").addClass("text-danger") - .attr("id", "passwordempty") - .text("Password must not be empty") - .insertAfter($("#newpassword")); + .attr("id", "passwordmismatch") + .text("Passwords do not match") + .insertAfter($("#newpassword_confirm")); return false; } else { - if (pw !== pwc) { - $("#newpassword_confirm").parent().addClass("has-error"); - $("#newpassword").parent().addClass("has-error"); - $("

").addClass("text-danger") - .attr("id", "passwordmismatch") - .text("Passwords do not match") - .insertAfter($("#newpassword_confirm")); - return false; - } else { - $("#username").attr("disabled", false); - return true; - } + $("#username").attr("disabled", false); + return true; } } - function submitEmail() { - $("#username2").attr("disabled", false); - return true; - } + } + function submitEmail() { + $("#username2").attr("disabled", false); + return true; + } diff --git a/templates/account-passwordrecover.pug b/templates/account-passwordrecover.pug index aff5cec9..09e3b119 100644 --- a/templates/account-passwordrecover.pug +++ b/templates/account-passwordrecover.pug @@ -1,28 +1,13 @@ -doctype html -html(lang="en") - head - include head - +head() - body - #wrap - nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation") - include nav - +navheader() - #nav-collapsible.collapse.navbar-collapse - ul.nav.navbar-nav - +navdefaultlinks() - +navloginlogout() - section#mainpage - .container - .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3 - h3 Recover Password - if recovered - .alert.alert-success.center.messagebox - strong Your password has been changed - p Your account has been assigned the temporary password #{recoverPw}. You may now use this password to log in and choose a new password by visiting the change password/email page. - else - .alert.alert-danger.center.messagebox - strong Password recovery failed - p= recoverErr - include footer - +footer() +extends layout.pug + +block content + .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3 + h3 Recover Password + if recovered + .alert.alert-success.center.messagebox + strong Your password has been changed + p Your account has been assigned the temporary password #{recoverPw}. You may now use this password to log in and choose a new password by visiting the change password/email page. + else + .alert.alert-danger.center.messagebox + strong Password recovery failed + p= recoverErr diff --git a/templates/account-passwordreset.pug b/templates/account-passwordreset.pug index ee536c2e..4b597c6f 100644 --- a/templates/account-passwordreset.pug +++ b/templates/account-passwordreset.pug @@ -1,38 +1,22 @@ -doctype html -html(lang="en") - head - include head - +head() - body - #wrap - nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation") - include nav - +navheader() - #nav-collapsible.collapse.navbar-collapse - ul.nav.navbar-nav - +navdefaultlinks() - +navloginlogout() - section#mainpage - .container - .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3 - h3 Reset Password - if reset - .alert.alert-success.center.messagebox - strong Password reset request sent - p Please check #{resetEmail} for your recovery link. - else if resetErr - .alert.alert-danger.center.messagebox - strong Error - p= resetErr - form(action="/account/passwordreset", method="post", role="form") - input(type="hidden", name="_csrf", value=csrfToken) - .form-group - label.control-label(for="username") Username - input#username.form-control(type="text", name="name") - .form-group - label.control-label(for="email") Email address - input#email.form-control(type="email", name="email") - button.btn.btn-primary.btn-block(type="submit") Send reset request +extends layout.pug - include footer - +footer() +block content + .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3 + h3 Reset Password + if reset + .alert.alert-success.center.messagebox + strong Password reset request sent + p Please check #{resetEmail} for your recovery link. + else if resetErr + .alert.alert-danger.center.messagebox + strong Error + p= resetErr + form(action="/account/passwordreset", method="post", role="form") + input(type="hidden", name="_csrf", value=csrfToken) + .form-group + label.control-label(for="username") Username + input#username.form-control(type="text", name="name") + .form-group + label.control-label(for="email") Email address + input#email.form-control(type="email", name="email") + button.btn.btn-primary.btn-block(type="submit") Send reset request diff --git a/templates/account-profile.pug b/templates/account-profile.pug index 3ecb9b05..f768a83e 100644 --- a/templates/account-profile.pug +++ b/templates/account-profile.pug @@ -1,76 +1,62 @@ -doctype html -html(lang="en") - head - include head - +head() - body - #wrap - nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation") - include nav - +navheader() - #nav-collapsible.collapse.navbar-collapse - ul.nav.navbar-nav - +navdefaultlinks() - +navloginlogout() - section#mainpage - .container - if !loggedIn - .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3 - .alert.alert-danger.messagebox.center - strong Authorization Required - p You must be logged in to view this page. - else - .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3 - h3 Profile - if profileError - .alert.alert-danger.center.messagebox - strong Profile Error - p= profileError - .profile-box.linewrap(style="position: inherit; z-index: auto;") - img.profile-image(src=profileImage) - strong= loginName - p= profileText - h3 Edit Profile - form(action="/account/profile", method="post", role="form") - input(type="hidden", name="_csrf", value=csrfToken) - .form-group - label.control-label(for="profileimage") Image - input#profileimage.form-control(type="text", name="image", maxlength="255") - .form-group - label.control-label(for="profiletext") Text - textarea#profiletext.form-control(cols="10", name="text", maxlength="255")= profileText - button.btn.btn-primary.btn-block(type="submit") Save +extends layout.pug - include footer - +footer() - script(type="text/javascript"). - var $profileImage = $("#profileimage"); - $profileImage.val("#{profileImage}"); - var hasError = false; - function validateImage() { - var value = $profileImage.val().trim(); - $profileImage.val(value); - if (!/^$|^https:/.test(value)) { - hasError = true; - $profileImage.parent().addClass("has-error"); - var $error = $("#profileimage-error"); - if ($error.length === 0) { - $error = $("

") - .attr({ id: "profileimage-error" }) - .addClass("text-danger") - .html("Profile image must be a URL beginning with https://") - .insertAfter($profileImage); - } - } else { - hasError = false; - $profileImage.parent().removeClass("has-error"); - $("#profileimage-error").remove(); +block content + if !loggedIn + .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3 + .alert.alert-danger.messagebox.center + strong Authorization Required + p You must be logged in to view this page. + else + .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3 + h3 Profile + if profileError + .alert.alert-danger.center.messagebox + strong Profile Error + p= profileError + .profile-box.linewrap(style="position: inherit; z-index: auto;") + img.profile-image(src=profileImage) + strong= loginName + p= profileText + h3 Edit Profile + form(action="/account/profile", method="post", role="form") + input(type="hidden", name="_csrf", value=csrfToken) + .form-group + label.control-label(for="profileimage") Image + input#profileimage.form-control(type="text", name="image", maxlength="255") + .form-group + label.control-label(for="profiletext") Text + textarea#profiletext.form-control(cols="10", name="text", maxlength="255")= profileText + button.btn.btn-primary.btn-block(type="submit") Save + +append footer + script(type="text/javascript"). + var $profileImage = $("#profileimage"); + $profileImage.val("#{profileImage}"); + var hasError = false; + function validateImage() { + var value = $profileImage.val().trim(); + $profileImage.val(value); + if (!/^$|^https:/.test(value)) { + hasError = true; + $profileImage.parent().addClass("has-error"); + var $error = $("#profileimage-error"); + if ($error.length === 0) { + $error = $("

") + .attr({ id: "profileimage-error" }) + .addClass("text-danger") + .html("Profile image must be a URL beginning with https://") + .insertAfter($profileImage); } + } else { + hasError = false; + $profileImage.parent().removeClass("has-error"); + $("#profileimage-error").remove(); } + } - $("form").submit(function (event) { - validateImage(); - if (hasError) { - event.preventDefault(); - } - }); + $("form").submit(function (event) { + validateImage(); + if (hasError) { + event.preventDefault(); + } + }); diff --git a/templates/contact.pug b/templates/contact.pug index a69556bb..67f2cd37 100644 --- a/templates/contact.pug +++ b/templates/contact.pug @@ -1,47 +1,34 @@ +extends layout.pug + mixin email(e, k) button.btn.btn-xs.btn-default(onclick="showEmail(this, '"+e+"', '"+k+"')") Show Email -doctype html -html(lang="en") - head - include head - +head() - body - #wrap - nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation") - include nav - +navheader() - #nav-collapsible.collapse.navbar-collapse - ul.nav.navbar-nav - +navdefaultlinks() - +navloginlogout() - section#mainpage - .container - .col-md-8.col-md-offset-2 - h1 Contact - h3 Email - if contacts.length == 0 - p No contacts listed. - else - each contact in contacts - strong= contact.name - p.text-muted= contact.title - +email(contact.email, contact.emkey) - br - hr - include footer - +footer() - script(type="text/javascript"). - function showEmail(btn, email, key) { - email = unescape(email); - key = unescape(key); - var dest = new Array(email.length); - for (var i = 0; i < email.length; i++) { - dest[i] = String.fromCharCode(email.charCodeAt(i) ^ key.charCodeAt(i % key.length)); - } - email = dest.join(""); - $("").attr("href", "mailto:" + email) - .text(email) - .insertBefore(btn); - $(btn).remove(); +block content + .col-md-8.col-md-offset-2 + h1 Contact + h3 Email + if contacts.length == 0 + p No contacts listed. + else + each contact in contacts + strong= contact.name + p.text-muted= contact.title + +email(contact.email, contact.emkey) + br + hr + +append footer + script(type="text/javascript"). + function showEmail(btn, email, key) { + email = unescape(email); + key = unescape(key); + var dest = new Array(email.length); + for (var i = 0; i < email.length; i++) { + dest[i] = String.fromCharCode(email.charCodeAt(i) ^ key.charCodeAt(i % key.length)); } + email = dest.join(""); + $("").attr("href", "mailto:" + email) + .text(email) + .insertBefore(btn); + $(btn).remove(); + } diff --git a/templates/csrferror.pug b/templates/csrferror.pug index 37da5485..0193ad25 100644 --- a/templates/csrferror.pug +++ b/templates/csrferror.pug @@ -1,31 +1,15 @@ -doctype html -html(lang="en") - head - include head - +head() - body - #wrap - nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation") - include nav - +navheader() - #nav-collapsible.collapse.navbar-collapse - ul.nav.navbar-nav - +navdefaultlinks() - +navloginlogout() +extends layout.pug - section#mainpage.container - .col-md-12 - .alert.alert-danger - h1 Invalid Session - p Your browser attempted to submit form data to #{path} with an invalid authentication token. This may be because: - ul - li Your session has expired - li Your request was missing the authentication token - li A malicious user has attempted to tamper with your session - li Your browser does not support cookies, or they are not enabled - | If the problem persists, please contact an administrator. - if referer - a(href=referer) Return to previous page - - include footer - +footer() +block content + .col-md-12 + .alert.alert-danger + h1 Invalid Session + p Your browser attempted to submit form data to #{path} with an invalid authentication token. This may be because: + ul + li Your session has expired + li Your request was missing the authentication token + li A malicious user has attempted to tamper with your session + li Your browser does not support cookies, or they are not enabled + | If the problem persists, please contact an administrator. + if referer + a(href=referer) Return to previous page diff --git a/templates/google_drive_userscript.pug b/templates/google_drive_userscript.pug index c0a1a45b..e7e15ea4 100644 --- a/templates/google_drive_userscript.pug +++ b/templates/google_drive_userscript.pug @@ -1,60 +1,44 @@ -doctype html -html(lang="en") - head - include head - +head() - body - #wrap - nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation") - include nav - +navheader() - #nav-collapsible.collapse.navbar-collapse - ul.nav.navbar-nav - +navdefaultlinks() - +navloginlogout() - section#mainpage - .container - .col-md-8.col-md-offset-2 - h1 Google Drive Userscript - h2 Why? - p. - Since Google Drive support was launched in early 2014, it has broken - at least 4-5 times, requiring increasing effort to get it working again - and disrupting many channels. This is because there is no official API - for it like there is for YouTube videos, which means support for it - relies on undocumented tricks. In August 2016, the decision was made - to phase out the native support for Google Drive and instead require - users to install a userscript, which allows to bypass certain browser - restrictions and make the code easier, simpler, and less prone to failure - (it could still break due to future Google Drive changes, but is less - likely to be difficult to fix). - h2 How It Works - p. - The userscript is a short script that you can install using a browser - extension such as Greasemonkey or Tampermonkey that runs on the page - and provides additional functionality needed to play Google Drive - videos. - h2 Installation - ul - li - strong Chrome - | —Install Tampermonkey. - li - strong Firefox - | —Install Tampermonkey - | or Greasemonkey. - li - strong Other Browsers - | —Install the appropriate userscript plugin for your browser. - | Tampermonkey supports many browsers besides Chrome. - p. - Once you have installed the userscript manager addon for your browser, - you can - install the userscript. If this link 404s, it means the administrator - of this server hasn't generated it yet. - p. - You can find a guide with screenshots of the installation process - on GitHub. +extends layout.pug - include footer - +footer() +block content + .col-md-8.col-md-offset-2 + h1 Google Drive Userscript + h2 Why? + p. + Since Google Drive support was launched in early 2014, it has broken + at least 4-5 times, requiring increasing effort to get it working again + and disrupting many channels. This is because there is no official API + for it like there is for YouTube videos, which means support for it + relies on undocumented tricks. In August 2016, the decision was made + to phase out the native support for Google Drive and instead require + users to install a userscript, which allows to bypass certain browser + restrictions and make the code easier, simpler, and less prone to failure + (it could still break due to future Google Drive changes, but is less + likely to be difficult to fix). + h2 How It Works + p. + The userscript is a short script that you can install using a browser + extension such as Greasemonkey or Tampermonkey that runs on the page + and provides additional functionality needed to play Google Drive + videos. + h2 Installation + ul + li + strong Chrome + | —Install Tampermonkey. + li + strong Firefox + | —Install Tampermonkey + | or Greasemonkey. + li + strong Other Browsers + | —Install the appropriate userscript plugin for your browser. + | Tampermonkey supports many browsers besides Chrome. + p. + Once you have installed the userscript manager addon for your browser, + you can + install the userscript. If this link 404s, it means the administrator + of this server hasn't generated it yet. + p. + You can find a guide with screenshots of the installation process + on GitHub. diff --git a/templates/httperror.pug b/templates/httperror.pug index 1e922f4d..5ce51b88 100644 --- a/templates/httperror.pug +++ b/templates/httperror.pug @@ -1,3 +1,5 @@ +extends layout.pug + mixin notfound() h1 Not Found p The page you were looking for doesn't seem to exist. Please check that you typed the URL correctly. @@ -9,30 +11,13 @@ mixin forbidden() mixin genericerror() h1 Oops p Your request could not be processed. Status code: #{status}, message: #{message} -doctype html -html(lang="en") - head - include head - +head() - body - #wrap - nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation") - include nav - +navheader() - #nav-collapsible.collapse.navbar-collapse - ul.nav.navbar-nav - +navdefaultlinks() - +navloginlogout() - section#mainpage.container - .col-md-12 - .alert.alert-danger - if status == 404 - +notfound() - else if status == 403 - +forbidden() - else - +genericerror() - - include footer - +footer() +block content + .col-md-12 + .alert.alert-danger + if status == 404 + +notfound() + else if status == 403 + +forbidden() + else + +genericerror() diff --git a/templates/index.pug b/templates/index.pug index 794ee4e4..8c9a7833 100644 --- a/templates/index.pug +++ b/templates/index.pug @@ -1,42 +1,26 @@ -doctype html -html(lang="en") - head - include head - +head() - body - #wrap - nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation") - include nav - +navheader() - #nav-collapsible.collapse.navbar-collapse - ul.nav.navbar-nav - +navdefaultlinks() - +navsuperadmin(false) - +navloginlogout() - section#mainpage - .container - .col-lg-9.col-md-9 - h3 Public Channels - table.table.table-bordered.table-striped - thead - th Channel - th # Connected - th Now Playing - tbody - each chan in channels - tr - td: a(href=`/${channelPath}/${chan.name}`) #{chan.pagetitle} (#{chan.name}) - td= chan.usercount - td= chan.mediatitle - .col-lg-3.col-md-3 - h3 Enter Channel - input#channelname.form-control(type="text", placeholder="Channel Name") - p.text-muted New channels can be registered from the My Channels page. - include footer - +footer() - script(type="text/javascript"). - $("#channelname").keydown(function (ev) { - if (ev.keyCode === 13) { - location.href = "/#{channelPath}/" + $("#channelname").val(); - } - }); +extends layout.pug + +block content + .col-lg-9.col-md-9 + h3 Public Channels + table.table.table-bordered.table-striped + thead + th Channel + th # Connected + th Now Playing + tbody + each chan in channels + tr + td: a(href=`/${channelPath}/${chan.name}`) #{chan.pagetitle} (#{chan.name}) + td= chan.usercount + td= chan.mediatitle + .col-lg-3.col-md-3 + h3 Enter Channel + input#channelname.form-control(type="text", placeholder="Channel Name") + p.text-muted New channels can be registered from the My Channels page. + script(type="text/javascript"). + $("#channelname").keydown(function (ev) { + if (ev.keyCode === 13) { + location.href = "/#{channelPath}/" + $("#channelname").val(); + } + }); diff --git a/templates/layout.pug b/templates/layout.pug new file mode 100644 index 00000000..aa1b2192 --- /dev/null +++ b/templates/layout.pug @@ -0,0 +1,22 @@ +doctype html +html(lang="en") + head + block head + include head + +head() + body + #wrap + nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation") + include nav + +navheader() + #nav-collapsible.collapse.navbar-collapse + ul.nav.navbar-nav + +navdefaultlinks() + +navsuperadmin(false) + +navloginlogout() + section#mainpage + .container + block content + block footer + include footer + +footer() diff --git a/templates/login.pug b/templates/login.pug index f5125437..37181ad7 100644 --- a/templates/login.pug +++ b/templates/login.pug @@ -1,54 +1,39 @@ -doctype html -html(lang="en") - head - include head - +head() - body - #wrap - nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation") - include nav - +navheader() - #nav-collapsible.collapse.navbar-collapse - ul.nav.navbar-nav - +navdefaultlinks() - if loggedIn - +navlogoutform() - section#mainpage.container - if wasAlreadyLoggedIn - .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3 - .alert.alert-info.messagebox.center - h3(style="margin: 5px auto") Logged in as #{loginName} - else if !loggedIn - .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3 - if loginError - .alert.alert-danger.messagebox.center - strong Login Failed - p= loginError - h2 Login - form(role="form", action="/login", method="post") - input(type="hidden", name="_csrf", value=csrfToken) - if redirect - input(type="hidden", name="dest", value=redirect) - .form-group - label(for="username") Username - input#username.form-control(type="text", name="name") - .form-group - label(for="password") Password - input#password.form-control(type="password", name="password") - a(href="/account/passwordreset") Forgot password? - .form-group - .checkbox - label - input(type="checkbox", name="remember") - | Remember me - button.btn.btn-success.btn-block(type="submit") Login - else - .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3 - .alert.alert-success.messagebox.center - strong Login Successful - p Logged in as #{loginName} - if redirect - br - a(href=redirect) Return to previous page - include footer - +footer() +extends layout.pug + +block content + if wasAlreadyLoggedIn + .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3 + .alert.alert-info.messagebox.center + h3(style="margin: 5px auto") Logged in as #{loginName} + else if !loggedIn + .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3 + if loginError + .alert.alert-danger.messagebox.center + strong Login Failed + p= loginError + h2 Login + form(role="form", action="/login", method="post") + input(type="hidden", name="_csrf", value=csrfToken) + if redirect + input(type="hidden", name="dest", value=redirect) + .form-group + label(for="username") Username + input#username.form-control(type="text", name="name") + .form-group + label(for="password") Password + input#password.form-control(type="password", name="password") + a(href="/account/passwordreset") Forgot password? + .form-group + .checkbox + label + input(type="checkbox", name="remember") + | Remember me + button.btn.btn-success.btn-block(type="submit") Login + else + .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3 + .alert.alert-success.messagebox.center + strong Login Successful + p Logged in as #{loginName} + if redirect + br + a(href=redirect) Return to previous page diff --git a/templates/logout.pug b/templates/logout.pug index fbddb942..a564e2c3 100644 --- a/templates/logout.pug +++ b/templates/logout.pug @@ -1,23 +1,9 @@ -doctype html -html(lang="en") - head - include head - +head() - body - #wrap - nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation") - include nav - +navheader() - #nav-collapsible.collapse.navbar-collapse - ul.nav.navbar-nav - +navdefaultlinks() - +navloginform("/") - section#mainpage.container - .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3 - .alert.alert-info.center.messagebox - strong Logged out - p - if redirect - a(href=redirect) Return to previous page - include footer - +footer() +extends layout.pug + +block content + .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3 + .alert.alert-info.center.messagebox + strong Logged out + p + if redirect + a(href=redirect) Return to previous page diff --git a/templates/register.pug b/templates/register.pug index 1061dbc7..be0d81d3 100644 --- a/templates/register.pug +++ b/templates/register.pug @@ -1,127 +1,114 @@ -doctype html -html(lang="en") - head - include head - +head() - body - #wrap - nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation") - include nav - +navheader() - #nav-collapsible.collapse.navbar-collapse - ul.nav.navbar-nav - +navdefaultlinks() - if loggedIn - +navlogoutform() - section#mainpage.container - if loggedIn - .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3 - .alert.alert-danger.messagebox.center - strong Already logged in - p. - You are already logged in. If you intend to register a new account, please Logout first. - // TODO Link to My Account page - else if !registered - .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3 - if registerError - .alert.alert-danger.messagebox.center - strong Registration Failed - p= registerError - h2 Register - form(role="form", action="/register", method="post", onsubmit="return verify()") - input(type="hidden", name="_csrf", value=csrfToken) - .form-group - label.control-label(for="username") Username - input#username.form-control(type="text", name="name", onkeyup="checkUsername()", maxlength="20") - p#validate_username.text-danger.pull-right - .form-group - label.control-label(for="password") Password - input#password.form-control(type="password", name="password", onkeyup="checkPasswords()") - p#validate_password.text-danger.pull-right - .form-group - label.control-label(for="password_confirm") Confirm Password - input#password_confirm.form-control(type="password", onkeyup="checkPasswords()") - p#validate_confirm.text-danger.pull-right - .form-group - label.control-label(for="email") Email (optional) - input#email.form-control(type="email", name="email") - p#validate_email.text-danger.pull-right - p - | Providing an email address is optional and will allow you to recover your account via email if you forget your password. - strong   If you do not provide an email address, you will not be able to recover a lost account! - button#registerbtn.btn.btn-success.btn-block(type="submit") Register - else - .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3 - .alert.alert-success.messagebox.center - strong Registration Successful - p Thanks for registering, #{registerName}! Now you can Login to use your account. - include footer - +footer() - script(type="text/javascript"). - function verify() { - var valid = checkUsername(); - valid = checkPasswords() && valid; - valid = checkEmail() && valid; - return valid; - } - function checkUsername() { - function stateError(text){ - target.parent() - .addClass("has-error") - .removeClass("has-success"); - $("#validate_username").text(text); - } - var target = $("#username"); - var name = target.val(); - if (name === "") { - stateError('Username must not be empty') - return false; - } else if (!(/^[-\w\u00c0-\u00ff]{1,20}$/).test(name)) { - stateError("Username must consist of 1-20 characters" + - " a-Z, A-Z, 0-9, -, or _."); - return false; - } else { - target.parent() - .removeClass("has-error") - .addClass("has-success"); - $("#validate_username").text(''); - } - } - function checkPasswords() { - function stateError(text, target, validator){ - target.parent() - .addClass("has-error") - .removeClass("has-success"); - $(`#${validator}`).text(text); - } - var target = $("#password"); - var target2 = $("#password_confirm"); - var pw = target.val(); - var pwc = target2.val(); +extends layout.pug - $("#validate_password").text(''); - $("#validate_confirm").text(''); - if (pw === "") { - stateError('Password must not be empty', target, 'validate_password') +block content + if loggedIn + .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3 + .alert.alert-danger.messagebox.center + strong Already logged in + p. + You are already logged in. If you intend to register a new account, please Logout first. + // TODO Link to My Account page + else if !registered + .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3 + if registerError + .alert.alert-danger.messagebox.center + strong Registration Failed + p= registerError + h2 Register + form(role="form", action="/register", method="post", onsubmit="return verify()") + input(type="hidden", name="_csrf", value=csrfToken) + .form-group + label.control-label(for="username") Username + input#register-username.form-control(type="text", name="name", onkeyup="checkUsername()", maxlength="20") + p#validate_username.text-danger.pull-right + .form-group + label.control-label(for="password") Password + input#register-password.form-control(type="password", name="password", onkeyup="checkPasswords()") + p#validate_password.text-danger.pull-right + .form-group + label.control-label(for="password_confirm") Confirm Password + input#register-password-confirm.form-control(type="password", onkeyup="checkPasswords()") + p#validate_confirm.text-danger.pull-right + .form-group + label.control-label(for="email") Email (optional) + input#register-email.form-control(type="email", name="email") + p#validate_email.text-danger.pull-right + p + | Providing an email address is optional and will allow you to recover your account via email if you forget your password. + strong   If you do not provide an email address, you will not be able to recover a lost account! + button#registerbtn.btn.btn-success.btn-block(type="submit") Register + else + .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3 + .alert.alert-success.messagebox.center + strong Registration Successful + p Thanks for registering, #{registerName}! Now you can Login to use your account. + +append footer + script(type="text/javascript"). + function verify() { + var valid = checkUsername(); + valid = checkPasswords() && valid; + valid = checkEmail() && valid; + return valid; + } + function checkUsername() { + function stateError(text){ + target.parent() + .addClass("has-error") + .removeClass("has-success"); + $("#validate_username").text(text); + } + var target = $("#register-username"); + var name = target.val(); + if (name === "") { + stateError('Username must not be empty') + return false; + } else if (!(/^[-\w\u00c0-\u00ff]{1,20}$/).test(name)) { + stateError("Username must consist of 1-20 characters" + + " a-Z, A-Z, 0-9, -, or _."); + return false; + } else { + target.parent() + .removeClass("has-error") + .addClass("has-success"); + $("#validate_username").text(''); + } + } + function checkPasswords() { + function stateError(text, target, validator){ + target.parent() + .addClass("has-error") + .removeClass("has-success"); + $(`#${validator}`).text(text); + } + var target = $("#register-password"); + var target2 = $("#register-password-confirm"); + var pw = target.val(); + var pwc = target2.val(); + + $("#validate_password").text(''); + $("#validate_confirm").text(''); + if (pw === "") { + stateError('Password must not be empty', target, 'validate_password') + return false; + } else { + target.parent() + .removeClass("has-error") + .addClass("has-success"); + if (pw !== pwc) { + stateError('Passwords do not match', target2, 'validate_confirm') return false; } else { - target.parent() + target2.parent() .removeClass("has-error") .addClass("has-success"); - if (pw !== pwc) { - stateError('Passwords do not match', target2, 'validate_confirm') - return false; - } else { - target2.parent() - .removeClass("has-error") - .addClass("has-success"); - } } } - function checkEmail() { - var email = $("#email").val(); - if (email.trim() === "") { - return confirm("Are you sure you want to register without setting a recovery email address? If you lose the password, or if your account is compromised, you WILL NOT be able to recover it."); - } - return true; + } + function checkEmail() { + var email = $("#register-email").val(); + if (email.trim() === "") { + return confirm("Are you sure you want to register without setting a recovery email address? If you lose the password, or if your account is compromised, you WILL NOT be able to recover it."); } + return true; + }