From 9dd24535c542c033bfe97fe2d6c4236c3d282c53 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Thu, 1 Aug 2024 13:42:44 +0100 Subject: [PATCH] [Waste] Add calendar help page. --- perllib/FixMyStreet/App/Controller/Waste.pm | 8 ++- perllib/FixMyStreet/Roles/Cobrand/Echo.pm | 2 +- t/app/controller/waste.t | 3 +- t/app/controller/waste_bexley.t | 4 +- templates/web/base/waste/bin_days.html | 2 +- .../web/base/waste/bin_days_sidebar.html | 2 +- templates/web/base/waste/header.html | 1 + .../borsetshire/waste/bin_days_sidebar.html | 2 +- .../about/waste-calendar.html | 62 +++++++++++++++++++ .../web/peterborough/waste/_calendar.html | 2 +- web/vendor/clipboard-copy-element.js | 8 +++ 11 files changed, 87 insertions(+), 9 deletions(-) create mode 100644 templates/web/fixmystreet-uk-councils/about/waste-calendar.html create mode 100644 web/vendor/clipboard-copy-element.js diff --git a/perllib/FixMyStreet/App/Controller/Waste.pm b/perllib/FixMyStreet/App/Controller/Waste.pm index 845e926bf4d..231288cebca 100644 --- a/perllib/FixMyStreet/App/Controller/Waste.pm +++ b/perllib/FixMyStreet/App/Controller/Waste.pm @@ -742,7 +742,13 @@ sub bin_day_deny : Private { $c->detach('/page_error_403_access_denied', [ $msg ]); } -sub calendar : Chained('property') : PathPart('calendar.ics') : Args(0) { +sub calendar : Chained('property') : Args(0) { + my ($self, $c) = @_; + + $c->forward('/about/page', ['waste-calendar']); +} + +sub calendar_ics : Chained('property') : PathPart('calendar.ics') : Args(0) { my ($self, $c) = @_; $c->res->header(Content_Type => 'text/calendar'); require Data::ICal::RFC7986; diff --git a/perllib/FixMyStreet/Roles/Cobrand/Echo.pm b/perllib/FixMyStreet/Roles/Cobrand/Echo.pm index 4ff0d706dbd..ee78556bbbd 100644 --- a/perllib/FixMyStreet/Roles/Cobrand/Echo.pm +++ b/perllib/FixMyStreet/Roles/Cobrand/Echo.pm @@ -90,7 +90,7 @@ sub bin_addresses_for_postcode { sub _allow_async_echo_lookup { my $self = shift; my $action = $self->{c}->action; - return 0 if $action eq 'waste/pay_retry' || $action eq 'waste/direct_debit_error' || $action eq 'waste/calendar'; + return 0 if $action eq 'waste/pay_retry' || $action eq 'waste/direct_debit_error' || $action eq 'waste/calendar_ics'; return 1; } diff --git a/t/app/controller/waste.t b/t/app/controller/waste.t index c55360fa771..4c51c5dbe07 100644 --- a/t/app/controller/waste.t +++ b/t/app/controller/waste.t @@ -333,7 +333,8 @@ FixMyStreet::override_config { is $mech->uri->path, '/waste/12345'; }; subtest 'Checking calendar' => sub { - $mech->follow_link_ok({ text => 'Add to your calendar (.ics file)' }); + $mech->follow_link_ok({ text => 'Add to your calendar' }); + $mech->follow_link_ok({ text_regex => qr/this link/ }); $mech->content_contains('BEGIN:VCALENDAR'); my @events = split /BEGIN:VEVENT/, $mech->encoded_content; shift @events; # Header diff --git a/t/app/controller/waste_bexley.t b/t/app/controller/waste_bexley.t index e236c16e301..7360d97b966 100644 --- a/t/app/controller/waste_bexley.t +++ b/t/app/controller/waste_bexley.t @@ -456,8 +456,8 @@ FixMyStreet::override_config { } subtest 'Checking calendar' => sub { - $mech->follow_link_ok( - { text => 'Add to your calendar (.ics file)' } ); + $mech->follow_link_ok({ text => 'Add to your calendar' }); + $mech->follow_link_ok({ text_regex => qr/this link/ }); $mech->content_contains('BEGIN:VCALENDAR'); my @events = split /BEGIN:VEVENT/, $mech->encoded_content; shift @events; # Header diff --git a/templates/web/base/waste/bin_days.html b/templates/web/base/waste/bin_days.html index 93d820853ef..37d62bb8cbd 100644 --- a/templates/web/base/waste/bin_days.html +++ b/templates/web/base/waste/bin_days.html @@ -20,7 +20,7 @@

Your collections

Your collections

[% IF service_data.size %] [% TRY %][% PROCESS waste/_calendar.html %][% CATCH file %] - Add to your calendar (.ics file) + Add to your calendar [% END %] [% END %] diff --git a/templates/web/base/waste/bin_days_sidebar.html b/templates/web/base/waste/bin_days_sidebar.html index 4f1ebf27b3a..1ea4221ebf6 100644 --- a/templates/web/base/waste/bin_days_sidebar.html +++ b/templates/web/base/waste/bin_days_sidebar.html @@ -2,7 +2,7 @@

Download your collection schedule

diff --git a/templates/web/fixmystreet-uk-councils/about/waste-calendar.html b/templates/web/fixmystreet-uk-councils/about/waste-calendar.html new file mode 100644 index 00000000000..15a0aa9cb4c --- /dev/null +++ b/templates/web/fixmystreet-uk-councils/about/waste-calendar.html @@ -0,0 +1,62 @@ +[% +PROCESS 'waste/header.html', title = 'Add your waste collection days to your online calendar'; +SET ics_link = c.uri_for_action('waste/calendar_ics', [ property.id ]); +%] + +
+ + + + + +

Add your waste collection days to your online calendar

+ +

Subscribe using your smartphone or computer, and your online calendar will always show your next collection days.

+ +

Step 1: copy the link to the calendar

+ +
+

Click the Copy button below to copy the calendar link to your clipboard: +

+ Loading... + Copy + +

+ +

Or press and hold this link until a menu appears, then tap “Copy Link” or “Copy Link Address”.

+

Or right-click / Ctrl-click this link, then click “Copy Link” or “Copy Link Address”.

+ + + +

Step 2: subscribe to the calendar

+

Follow these instructions and paste in the link you just copied when it asks you for the calendar’s address or URL.

+ + +
+ +[% INCLUDE footer.html %] diff --git a/templates/web/peterborough/waste/_calendar.html b/templates/web/peterborough/waste/_calendar.html index 2c4b192a6c9..6a6d1763005 100644 --- a/templates/web/peterborough/waste/_calendar.html +++ b/templates/web/peterborough/waste/_calendar.html @@ -1 +1 @@ -Add to your calendar (.ics file) +Add to your calendar diff --git a/web/vendor/clipboard-copy-element.js b/web/vendor/clipboard-copy-element.js new file mode 100644 index 00000000000..1b46e3e68f8 --- /dev/null +++ b/web/vendor/clipboard-copy-element.js @@ -0,0 +1,8 @@ +/** + * Bundled by jsDelivr using Rollup v2.79.1 and Terser v5.19.2. + * Original file: /npm/@github/clipboard-copy-element@1.3.0/dist/index.js + * + * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files + */ +function e(e){if("clipboard"in navigator)return navigator.clipboard.writeText(e.textContent||"");const t=getSelection();if(null==t)return Promise.reject(new Error);t.removeAllRanges();const n=document.createRange();return n.selectNodeContents(e),t.addRange(n),document.execCommand("copy"),t.removeAllRanges(),Promise.resolve()}function t(t){if("clipboard"in navigator)return navigator.clipboard.writeText(t);const n=document.body;if(!n)return Promise.reject(new Error);const o=function(e){const t=document.createElement("pre");return t.style.width="1px",t.style.height="1px",t.style.position="fixed",t.style.top="5px",t.textContent=e,t}(t);return n.appendChild(o),e(o),n.removeChild(o),Promise.resolve()}async function n(n){const o=n.getAttribute("for"),r=n.getAttribute("value");function i(){n.dispatchEvent(new CustomEvent("clipboard-copy",{bubbles:!0}))}var a;if("true"!==n.getAttribute("aria-disabled"))if(r)await t(r),i();else if(o){const r="getRootNode"in Element.prototype?n.getRootNode():n.ownerDocument;if(!(r instanceof Document||"ShadowRoot"in window&&r instanceof ShadowRoot))return;const s=r.getElementById(o);s&&(await(a=s,a instanceof HTMLInputElement||a instanceof HTMLTextAreaElement?t(a.value):a instanceof HTMLAnchorElement&&a.hasAttribute("href")?t(a.href):e(a)),i())}}function o(e){const t=e.currentTarget;t instanceof HTMLElement&&n(t)}function r(e){if(" "===e.key||"Enter"===e.key){const t=e.currentTarget;t instanceof HTMLElement&&(e.preventDefault(),n(t))}}function i(e){e.currentTarget.addEventListener("keydown",r)}function a(e){e.currentTarget.removeEventListener("keydown",r)}class s extends HTMLElement{static define(e="clipboard-copy",t=customElements){return t.define(e,this),this}constructor(){super(),this.addEventListener("click",o),this.addEventListener("focus",i),this.addEventListener("blur",a)}connectedCallback(){this.hasAttribute("tabindex")||this.setAttribute("tabindex","0"),this.hasAttribute("role")||this.setAttribute("role","button")}get value(){return this.getAttribute("value")||""}set value(e){this.setAttribute("value",e)}}const c="undefined"!=typeof globalThis?globalThis:window;try{c.ClipboardCopyElement=s.define()}catch(e){if(!(c.DOMException&&e instanceof DOMException&&"NotSupportedError"===e.name||e instanceof ReferenceError))throw e}export{s as ClipboardCopyElement,s as default}; +//# sourceMappingURL=/sm/d07c5d884b8a4edb8adc6ad670c97cd087b3bad123400edf84c6aab5193dff65.map \ No newline at end of file