#247 [calendar] ics

This commit is contained in:
Avris 2021-10-26 23:09:02 +02:00
parent a47d7fcfe4
commit be09e8a3fd
6 changed files with 141 additions and 0 deletions

View File

@ -21,6 +21,15 @@
</a>
</p>
</div>
<div v-if="!day">
<p class="mb-0">
iCalendar:
</p>
<a :href="`/api/calendar/queer-calendar-${year.year}.ics`" class="btn btn-outline-primary m-1">
<Icon v="calendar-plus"/>
ICS
</a>
</div>
<div>
<p class="mb-0">
<T>calendar.image.header</T>:

View File

@ -25,6 +25,7 @@
"express-session": "^1.17.1",
"grant": "^5.4.5",
"html-loader": "^1.3.2",
"ics": "^2.31.0",
"js-base64": "^3.5.2",
"js-md5": "^0.7.3",
"jsonwebtoken": "^8.5.1",

View File

@ -99,6 +99,7 @@ app.use(require('./routes/names').default);
app.use(require('./routes/images').default);
app.use(require('./routes/blog').default);
app.use(require('./routes/calendar').default);
app.use(function (err, req, res, next) {
console.error(err.stack);

33
server/routes/calendar.js Normal file
View File

@ -0,0 +1,33 @@
import { Router } from 'express';
import {handleErrorAsync} from "../../src/helpers";
import { createEvents } from 'ics';
import { calendar } from '../../src/calendar/calendar';
import {loadSuml} from "../loader";
import { clearLinkedText } from '../../src/helpers';
// TODO caching? // import { caches } from "../../src/cache";
const translations = loadSuml('translations');
const router = Router();
router.get('/calendar/queer-calendar-:year.ics', handleErrorAsync(async (req, res) => {
const year = calendar.getYear(req.params.year);
if (!year) {
return res.status(404).json({error: 'Not found'});
}
const events = year.events
.map(e => e.toIcs(req.params.year, translations, clearLinkedText))
.filter(e => e !== null);
createEvents(events, (error, value) => {
if (error) {
console.error(error);
return res.status(500).json({error: 'Unexpected server error'});
}
res.header('Content-type', 'text/calendar');
res.send(value);
})
}));
export default router;

View File

@ -16,6 +16,10 @@ class Day {
return Day.fromDate(new Date);
}
toDate() {
return new Date(this.year, this.month - 1, this.day);
}
equals(other) {
return other && this.year === other.year && this.month === other.month && this.day === other.day;
}
@ -28,6 +32,18 @@ class Day {
toInt() {
return parseInt(`${this.year}${this.month.toString().padStart(2, '0')}${this.day.toString().padStart(2, '0')}`);
}
next() {
let d = this.toDate();
d.setDate(d.getDate() + 1);
return Day.fromDate(d);
}
prev() {
let d = this.toDate();
d.setDate(d.getDate() - 1);
return Day.fromDate(d);
}
}
module.exports.Day = Day;
@ -60,6 +76,7 @@ module.exports.Event = class {
}
getDays(year) {
year = parseInt(year);
if (this.daysMemoise[year] === undefined) {
this.daysMemoise[year] = [...this.generator(iterateMonth(year, this.month))];
}
@ -86,6 +103,28 @@ module.exports.Event = class {
isFirstDay(day) {
return this.getDays(day.year)[0].equals(day);
}
toIcs(year, translations, clearLinkedText) {
const days = this.getDays(year);
const first = days[0];
const last = days[days.length - 1].next();
let [name, param] = this.name.split('$');
if (translations.calendar.events[name] !== undefined) {
name = translations.calendar.events[name];
}
if (param) {
name = name.replace(/%param%/g, param);
}
name = clearLinkedText(name);
return {
title: name,
start: [first.year, first.month, first.day],
end: [last.year, last.month, last.day],
calName: translations.calendar.headerLong,
}
}
}
module.exports.day = function (dayOfMonth) {

View File

@ -824,6 +824,13 @@
dependencies:
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.15.4":
version "7.15.4"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a"
integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==
dependencies:
regenerator-runtime "^0.13.4"
"@babel/template@^7.12.13":
version "7.12.13"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.13.tgz#530265be8a2589dbb37523844c5bcb55947fb327"
@ -1310,6 +1317,11 @@
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad"
integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==
"@types/lodash@^4.14.175":
version "4.14.176"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.176.tgz#641150fc1cda36fbfa329de603bbb175d7ee20c0"
integrity sha512-xZmuPTa3rlZoIbtDUyJKZQimJV3bxCmzMIO2c9Pz9afyDro6kr7R79GwcB6mRhuoPmV2p1Vb66WOJH7F886WKQ==
"@types/node@*":
version "14.14.31"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.31.tgz#72286bd33d137aa0d152d47ec7c1762563d34055"
@ -5112,6 +5124,14 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4:
dependencies:
safer-buffer ">= 2.1.2 < 3"
ics@^2.31.0:
version "2.31.0"
resolved "https://registry.yarnpkg.com/ics/-/ics-2.31.0.tgz#4c26b755d017ee64c963cfbaa5ea9766e604ceec"
integrity sha512-3pW62uD097nl6LfFXIt92eBZtbwDESXsaRcgZPn3NO01zpUUM+L2G6fjf6qXhiyFcGIrJjsGuNB/y3AV58CvFg==
dependencies:
nanoid "^3.1.23"
yup "^0.32.9"
icss-utils@^4.0.0, icss-utils@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467"
@ -5905,6 +5925,11 @@ locate-path@^6.0.0:
dependencies:
p-locate "^5.0.0"
lodash-es@^4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee"
integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==
lodash._reinterpolate@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"
@ -6494,11 +6519,21 @@ nan@^2.12.1, nan@^2.14.0:
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19"
integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==
nanoclone@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/nanoclone/-/nanoclone-0.2.1.tgz#dd4090f8f1a110d26bb32c49ed2f5b9235209ed4"
integrity sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==
nanoid@^3.1.20:
version "3.1.20"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788"
integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==
nanoid@^3.1.23:
version "3.1.30"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.30.tgz#63f93cc548d2a113dc5dfbc63bfa09e2b9b64362"
integrity sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ==
nanomatch@^1.2.9:
version "1.2.13"
resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
@ -8111,6 +8146,11 @@ proper-lockfile@^4.1.2:
retry "^0.12.0"
signal-exit "^3.0.2"
property-expr@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.4.tgz#37b925478e58965031bb612ec5b3260f8241e910"
integrity sha512-sFPkHQjVKheDNnPvotjQmm3KD3uk1fWKUN7CrpdbwmUx3CrG3QiM8QpTSimvig5vTXmTvjz7+TDvXOI9+4rkcg==
protocols@^1.1.0, protocols@^1.4.0:
version "1.4.8"
resolved "https://registry.yarnpkg.com/protocols/-/protocols-1.4.8.tgz#48eea2d8f58d9644a4a32caae5d5db290a075ce8"
@ -9702,6 +9742,11 @@ toidentifier@1.0.0:
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
toposort@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330"
integrity sha1-riF2gXXRVZ1IvvNUILL0li8JwzA=
totalist@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df"
@ -10571,6 +10616,19 @@ yocto-queue@^0.1.0:
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
yup@^0.32.9:
version "0.32.11"
resolved "https://registry.yarnpkg.com/yup/-/yup-0.32.11.tgz#d67fb83eefa4698607982e63f7ca4c5ed3cf18c5"
integrity sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==
dependencies:
"@babel/runtime" "^7.15.4"
"@types/lodash" "^4.14.175"
lodash "^4.17.21"
lodash-es "^4.17.21"
nanoclone "^0.2.1"
property-expr "^2.0.4"
toposort "^2.0.2"
zh_cn_zh_tw@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/zh_cn_zh_tw/-/zh_cn_zh_tw-1.0.7.tgz#dc6fa59f4964cc2b65e6b4b525771f286890447d"