From 2d267ba9fe1f711329a092e04ed900d21d4aee8b Mon Sep 17 00:00:00 2001 From: jtbx Date: Mon, 2 Oct 2023 12:08:18 +0200 Subject: [PATCH] Create CCCHH theme --- README.md | 40 +++++++- content/_index.md | 8 ++ content/blog/_index.md | 7 ++ hugo.toml | 4 + themes/ccchh/LICENSE | 20 ++++ themes/ccchh/archetypes/article.md | 8 ++ themes/ccchh/archetypes/default.md | 5 + themes/ccchh/archetypes/event.md | 9 ++ themes/ccchh/assets/images/logo.svg | 59 +++++++++++ themes/ccchh/assets/js/roomstate.js | 84 ++++++++++++++++ themes/ccchh/assets/js/util/timeDistance.js | 104 ++++++++++++++++++++ themes/ccchh/assets/sass/main.scss | 78 +++++++++++++++ themes/ccchh/hugo.toml | 1 + themes/ccchh/layouts/404.html | 8 ++ themes/ccchh/layouts/_default/baseof.html | 12 +++ themes/ccchh/layouts/_default/single.html | 13 +++ themes/ccchh/layouts/blog/list.html | 33 +++++++ themes/ccchh/layouts/index.html | 50 ++++++++++ themes/ccchh/layouts/partials/footer.html | 6 ++ themes/ccchh/layouts/partials/head.html | 22 +++++ themes/ccchh/layouts/partials/header.html | 6 ++ themes/ccchh/layouts/partials/menu.html | 32 ++++++ themes/ccchh/theme.toml | 21 ++++ 23 files changed, 629 insertions(+), 1 deletion(-) create mode 100644 content/_index.md create mode 100644 content/blog/_index.md create mode 100644 hugo.toml create mode 100644 themes/ccchh/LICENSE create mode 100644 themes/ccchh/archetypes/article.md create mode 100644 themes/ccchh/archetypes/default.md create mode 100644 themes/ccchh/archetypes/event.md create mode 100644 themes/ccchh/assets/images/logo.svg create mode 100644 themes/ccchh/assets/js/roomstate.js create mode 100644 themes/ccchh/assets/js/util/timeDistance.js create mode 100644 themes/ccchh/assets/sass/main.scss create mode 100644 themes/ccchh/hugo.toml create mode 100644 themes/ccchh/layouts/404.html create mode 100644 themes/ccchh/layouts/_default/baseof.html create mode 100644 themes/ccchh/layouts/_default/single.html create mode 100644 themes/ccchh/layouts/blog/list.html create mode 100644 themes/ccchh/layouts/index.html create mode 100644 themes/ccchh/layouts/partials/footer.html create mode 100644 themes/ccchh/layouts/partials/head.html create mode 100644 themes/ccchh/layouts/partials/header.html create mode 100644 themes/ccchh/layouts/partials/menu.html create mode 100644 themes/ccchh/theme.toml diff --git a/README.md b/README.md index 8f9cb16..c7859d5 100644 --- a/README.md +++ b/README.md @@ -22,4 +22,42 @@ All other information like details on groups, projects and recurring events shou ## Usage -TODO +### Build and Deploy + +Running the hugo command without and parameters will re-generate the site in the `public` directory. +To deploy the website, just copy the whole folder to a directory which is servered by the webserver of your preference. + +Please note, that the website should be re-deployed at least daily to update the "announcement" section on the front page even if there were no changed to the content. + +### Add Pages + +To run a local version, just install HUGO by following the [instructions](https://gohugo.io/installation/) for your operating system. +To build the website and run a development webserver, execute: +```shell +hugo server +``` + +To also build posts in the future or in draft state, run this instead: +```shell +hugo server -D +``` + +#### Add an Event Announcement + +There are two basic types of posts: Events and articles. +Events will be shown on the home page from their publishing date until they have happened. + +To create a new event blog post, run a command like this: +```shell +hugo new content --king event blog/your-event-title.md +``` + +#### Add a Blog Entry + +As mentioned before, you can also create blog posts for things which aren't events. +They will only be shown in the "blog" section and posted to the RSS feeds. + +To create a new general blog post, run a command like this: +```shell +hugo new content --king article blog/your-article-title.md +``` diff --git a/content/_index.md b/content/_index.md new file mode 100644 index 0000000..3fb14ce --- /dev/null +++ b/content/_index.md @@ -0,0 +1,8 @@ +--- +title: "Home" +date: 2022-11-20T09:03:20-08:00 +draft: false +--- +TODO: Hallo, wir sind der CCC Hamburg... + +TOOD: Was wir machen und wer wir sind diff --git a/content/blog/_index.md b/content/blog/_index.md new file mode 100644 index 0000000..4c429de --- /dev/null +++ b/content/blog/_index.md @@ -0,0 +1,7 @@ +--- +title: "Blog" +draft: false +menu: main +--- + +TODO: Provide filters by type (announcement, post) diff --git a/hugo.toml b/hugo.toml new file mode 100644 index 0000000..5f6c940 --- /dev/null +++ b/hugo.toml @@ -0,0 +1,4 @@ +baseURL = 'https://hamburg.ccc.de/' +languageCode = 'de-de' +title = 'CCC Hansestadt Hamburg e.V.' +theme = 'ccchh' diff --git a/themes/ccchh/LICENSE b/themes/ccchh/LICENSE new file mode 100644 index 0000000..17993f6 --- /dev/null +++ b/themes/ccchh/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2023 YOUR_NAME_HERE + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/themes/ccchh/archetypes/article.md b/themes/ccchh/archetypes/article.md new file mode 100644 index 0000000..8aa30b2 --- /dev/null +++ b/themes/ccchh/archetypes/article.md @@ -0,0 +1,8 @@ +--- +kind: article +title: '{{ replace .File.ContentBaseName `-` ` ` | title }}' +date: '{{ .Date }}' +draft: true +--- + +TODO: Write your blog post diff --git a/themes/ccchh/archetypes/default.md b/themes/ccchh/archetypes/default.md new file mode 100644 index 0000000..26f317f --- /dev/null +++ b/themes/ccchh/archetypes/default.md @@ -0,0 +1,5 @@ +--- +title: "{{ replace .Name "-" " " | title }}" +date: {{ .Date }} +draft: true +--- diff --git a/themes/ccchh/archetypes/event.md b/themes/ccchh/archetypes/event.md new file mode 100644 index 0000000..287f8ec --- /dev/null +++ b/themes/ccchh/archetypes/event.md @@ -0,0 +1,9 @@ +--- +kind: event +title: '{{ replace .File.ContentBaseName `-` ` ` | title }}' +date: '{{ .Date }}' # date of the event +publishDate: '{{ .Date }}' # when to publish +draft: true +--- + +TODO: Describe your event here diff --git a/themes/ccchh/assets/images/logo.svg b/themes/ccchh/assets/images/logo.svg new file mode 100644 index 0000000..7198c3d --- /dev/null +++ b/themes/ccchh/assets/images/logo.svg @@ -0,0 +1,59 @@ + + + + + + image/svg+xml + + + + + + + Chaos Computer Club Hamburg Logo + + diff --git a/themes/ccchh/assets/js/roomstate.js b/themes/ccchh/assets/js/roomstate.js new file mode 100644 index 0000000..a70e878 --- /dev/null +++ b/themes/ccchh/assets/js/roomstate.js @@ -0,0 +1,84 @@ +import timeDistance, { deLocale } from 'js/util/timeDistance' + +const spaceapiUrl = "https://www.hamburg.ccc.de/dooris/status.json" +const interval_ms = 60000 + +const classNameOpen = "open" +const classNameClosed = "closed" + + +function updateRoomState(openState, lastChangeTimestamp) { + console.debug("SpaceAPI: Setting room " + openState + " since " + lastChangeTimestamp) + + const roomstate = document.querySelector('#roomstate') + var statusItem = roomstate.querySelector(".state") + var durationItem = roomstate.querySelector(".duration") + + if (openState === null || lastChangeTimestamp === null) { + console.warn("SpaceAPI: Room state unknown"); + statusItem.textContent = "unbekannt" + roomstate.classList.remove(classNameOpen) + roomstate.classList.remove(classNameClosed) + durationItem.textContent = "(seit unbekannt)" + + } else { + if (openState) { + statusItem.textContent = "offen" + roomstate.classList.remove(classNameClosed) + roomstate.classList.add(classNameOpen) + + } else { + statusItem.textContent = "geschlossen" + roomstate.classList.remove(classNameOpen) + roomstate.classList.add(classNameClosed) + + } + + const changeDate = new Date(lastChangeTimestamp * 1000) + const changeText = timeDistance(deLocale).inWords(changeDate) + + durationItem.textContent = "(" + changeText + ")" + } +} + +function parseResponse(apiJson) { + console.debug('SpaceAPI got:', apiJson) + + if (typeof apiJson.api === "undefined") { + console.error("SpaceAPI JSON invalid: 'api' item not present") + return [null, null] + } + if (typeof apiJson.state === "undefined") { + console.error("SpaceAPI JSON invalid: 'state' item not present") + return [null, null] + } + if (typeof apiJson.state.open === "undefined") { + console.error("SpaceAPI JSON invalid: 'state.open' item not present") + return [null, null] + } + + console.debug('SpaceAPI state:', apiJson.state) + var openState = (apiJson.state.open == true) + var lastchange = null + + if (apiJson.state.lastchange !== "undefined") { + lastchange = apiJson.state.lastchange + } + + return [openState, lastchange] +} + +function update() { + fetch(spaceapiUrl) + .then(resp => resp.json()) + .then(function (data) { + var state = parseResponse(data) + updateRoomState(state[0], state[1]) + }) + .catch(err => { throw err }); +} + +window.onload = function () { + update() + window.setTimeout(update, interval_ms) +} diff --git a/themes/ccchh/assets/js/util/timeDistance.js b/themes/ccchh/assets/js/util/timeDistance.js new file mode 100644 index 0000000..ab578c3 --- /dev/null +++ b/themes/ccchh/assets/js/util/timeDistance.js @@ -0,0 +1,104 @@ +export default ((locale) => { + const self = {} + + if (locale === undefined) { + self.locales = { + pastPrefix: '', + pastSufix: 'ago', + + futurePrefix: 'in', + futureSufix: '', + + seconds: '%p less than a minutei %s', + minute: '%p about a minute %s', + minutes: '%p %d minutes %s', + hour: '%p about an hour %s', + hours: '%p about %d%r hours %s', + day: '%p a day %s', + days: '%p %d%r days %s', + month: '%p about a month %s', + months: '%p %d%r months %s', + year: '%p about a year %s', + years: '%p %d%r years %s' + } + } else { + self.locales = locale + } + + self.inWords = (timeDistance, start) => { + if (start === undefined) { + start = new Date() + } else { + start = start instanceof(Date) ? start : parseInt(start) + } + timeDistance = timeDistance instanceof(Date) ? timeDistance : parseInt(timeDistance) + const prefix = timeDistance < new Date() ? self.locales.pastPrefix : self.locales.futurePrefix + const sufix = timeDistance < new Date() ? self.locales.pastSufix : self.locales.futureSufix + + let seconds = Math.abs(Math.floor((start - timeDistance) / 1000)), + interval = 0, + rest = 0, + intervals = { + year: seconds / 31536000, + month: seconds / 2592000, + day: seconds / 86400, + hour: seconds / 3600, + minute: seconds / 60 + } + + let distance = self.locales.seconds + let key + + for (key in intervals) { + interval = Math.floor(intervals[key]) + rest = intervals[key] - interval + + if (interval >= 1) { + distance = self.locales[key + 's'] + break + } else if (interval === 1) { + distance = self.locales[key] + break + } + } + + if (rest > 0.5) { + distance = distance.replace(/%r/, "½") + } else if (rest > 0.25) { + distance = distance.replace(/%r/, "¼") + } else { + if (interval === 1) { + distance = self.locales[key] + } + distance = distance.replace(/\s*%r/, "") + } + + distance = distance.replace(/%d/i, interval) + distance = distance.replace(/%p/i, prefix) + distance = distance.replace(/%s/i, sufix) + + return distance.trim() + } + + return self +}) + +export const deLocale = { + pastPrefix: 'seit', + pastSufix: '', + + futurePrefix: 'in', + futureSufix: '', + + seconds: '%p %d Sekunden', + minute: '%p einer Minute', + minutes: '%p %d Minuten', + hour: '%p einer Stunde', + hours: '%p %d%r Stunden', + day: '%p einem Tag', + days: '%p %d%r Tagen', + month: '%p einem Monat', + months: '%p %d%r Monaten', + year: '%p einem Jahr', + years: '%p %d%r Jahren' +} diff --git a/themes/ccchh/assets/sass/main.scss b/themes/ccchh/assets/sass/main.scss new file mode 100644 index 0000000..3de3c01 --- /dev/null +++ b/themes/ccchh/assets/sass/main.scss @@ -0,0 +1,78 @@ +$roomstate_color_unknown: #dda218; +$roomstate_color_open: var(--ins-color); +$roomstate_color_closed: var(--del-color); + + +// Room State in Menu +#roomstate { + font-size: 0.9em; + line-height: 1.0em; + max-width: 8em; + padding: 5px; + + color: $roomstate_color_unknown; + + &.open { + color: $roomstate_color_open; + } + + &.closed { + color: $roomstate_color_closed; + } + + span.duration { + font-size: 0.7em; + } +} + +// CCCHH Icon in Menu +@media only screen and (prefers-color-scheme: light) { + .invert-on-light { + filter: invert(1); + } +} + +// Home page Announcements +.announcement { + border-radius: var(--border-radius); + background: var(--code-background-color); + padding: 10px 15px; + + margin-bottom: var(--typography-spacing-vertical); + + p:last-child { + margin-bottom: 0; + } +} + +@media only screen and (prefers-color-scheme: dark) { + .announcement { + background: var(--code-background-color); + } +} + +// Home Friends&Family Gallery +.flex-grid { + display: flex; + flex-wrap: wrap; + justify-content: center; + + div { + width: 250px; + + // 2*250px + 2*var(--spacing) + 1px + @media only screen and (max-width: 533px) { + width: 150px; + } + } +} + +.img-link { + a { + img { + display: block; + padding: var(--grid-spacing-horizontal); + margin: 0 auto; + } + } +} \ No newline at end of file diff --git a/themes/ccchh/hugo.toml b/themes/ccchh/hugo.toml new file mode 100644 index 0000000..938865a --- /dev/null +++ b/themes/ccchh/hugo.toml @@ -0,0 +1 @@ +# Theme config. diff --git a/themes/ccchh/layouts/404.html b/themes/ccchh/layouts/404.html new file mode 100644 index 0000000..c4413b4 --- /dev/null +++ b/themes/ccchh/layouts/404.html @@ -0,0 +1,8 @@ +{{ define "main" }} +
+
+

404 Page not Found

+ Back Home +
+
+{{ end }} diff --git a/themes/ccchh/layouts/_default/baseof.html b/themes/ccchh/layouts/_default/baseof.html new file mode 100644 index 0000000..9125f99 --- /dev/null +++ b/themes/ccchh/layouts/_default/baseof.html @@ -0,0 +1,12 @@ + + + + {{- partial "head.html" . }} + + + + {{- partial "header.html" . }} + {{- block "main" . }}{{- end }} + {{- partial "footer.html" . }} + + diff --git a/themes/ccchh/layouts/_default/single.html b/themes/ccchh/layouts/_default/single.html new file mode 100644 index 0000000..b3d580c --- /dev/null +++ b/themes/ccchh/layouts/_default/single.html @@ -0,0 +1,13 @@ +{{ define "main" }} +
+
+

{{ .Title }}

+ {{ with .Params.subtitle }} + {{ . }} + {{ end }} +
+
+ {{ .Content }} +
+
+{{ end }} diff --git a/themes/ccchh/layouts/blog/list.html b/themes/ccchh/layouts/blog/list.html new file mode 100644 index 0000000..311f660 --- /dev/null +++ b/themes/ccchh/layouts/blog/list.html @@ -0,0 +1,33 @@ +{{ define "main" }} +
+
+

{{ .Title }}

+ {{ with .Params.subtitle }} + {{ . }} + {{ end }} +
+
+ {{ .Content }} +
+ + + + {{ range .Pages.GroupByParam "kind" }} +

{{ .Key }}

+ + {{ end }} + +
+{{ end }} diff --git a/themes/ccchh/layouts/index.html b/themes/ccchh/layouts/index.html new file mode 100644 index 0000000..77379ce --- /dev/null +++ b/themes/ccchh/layouts/index.html @@ -0,0 +1,50 @@ +{{ define "main" }} +
+ +
+ + {{ .Content }} +
+ + {{- $events := where (.Site.GetPage "blog").Pages ".Params.kind" "event" }} + {{- $upcoming := where $events ".Params.date" "ge" now }} + {{ if $upcoming}} +
+

Neuigkeiten

+ {{- range $upcoming.ByDate }} +
+

{{ .Date.Format "2006-01-02" }}: {{ .Title }} + {{ .Summary }} + {{- if .Truncated }} + Read More… + {{- end }} +

+
+ {{- end }} +
+ {{- end }} + + {{- $home_sections := .Site.GetPage "/home" }} + {{- range sort ($home_sections.Resources.Match "*.md") ".Name" }} +
+

{{ .Title }}

+ {{ .Content -}} + + {{- with .Params.Resources }} +
+ {{- range . }} + {{- $img := $home_sections.Resources.GetMatch (printf "%s" .src) }} + + {{- end }} +
+ {{- end }} +
+ {{ end }} + +
+{{ end }} diff --git a/themes/ccchh/layouts/partials/footer.html b/themes/ccchh/layouts/partials/footer.html new file mode 100644 index 0000000..3e65991 --- /dev/null +++ b/themes/ccchh/layouts/partials/footer.html @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/themes/ccchh/layouts/partials/head.html b/themes/ccchh/layouts/partials/head.html new file mode 100644 index 0000000..4a59e2a --- /dev/null +++ b/themes/ccchh/layouts/partials/head.html @@ -0,0 +1,22 @@ +{{- $options := dict "transpiler" "libsass" "targetPath" "css/style.css" -}} + + + + {{- with resources.GetRemote "https://cdn.jsdelivr.net/npm/@picocss/pico@1/css/pico.min.css" }} + + {{ end }} + {{ with resources.Get "sass/main.scss" | toCSS $options | minify | fingerprint -}} + + {{- end }} + + + {{ with resources.Get "js/roomstate.js" | js.Build | minify | fingerprint -}} + + {{- end }} + + {{ range .AlternativeOutputFormats -}} + {{ printf `` .Rel .MediaType.Type .Permalink $.Site.Title | safeHTML }} + {{ end -}} + + {{ .Title }} + \ No newline at end of file diff --git a/themes/ccchh/layouts/partials/header.html b/themes/ccchh/layouts/partials/header.html new file mode 100644 index 0000000..2711c51 --- /dev/null +++ b/themes/ccchh/layouts/partials/header.html @@ -0,0 +1,6 @@ + +
+ +
diff --git a/themes/ccchh/layouts/partials/menu.html b/themes/ccchh/layouts/partials/menu.html new file mode 100644 index 0000000..69b32f9 --- /dev/null +++ b/themes/ccchh/layouts/partials/menu.html @@ -0,0 +1,32 @@ +{{- $page := .page -}} +{{- $menuID := .menuID -}} +{{- $menuContent := index site.Menus .menuID -}} + + + {{- if compare.Eq $menuID "main" -}} + + {{- end }} \ No newline at end of file diff --git a/themes/ccchh/theme.toml b/themes/ccchh/theme.toml new file mode 100644 index 0000000..9b74ef4 --- /dev/null +++ b/themes/ccchh/theme.toml @@ -0,0 +1,21 @@ +# theme.toml template for a Hugo theme +# See https://github.com/gohugoio/hugoThemes#themetoml for an example + +name = "CCCHH" +license = "MIT" +licenselink = "https://github.com/yourname/yourtheme/blob/master/LICENSE" +description = "" +# homepage = "http://example.com/" +tags = [] +features = [] +min_version = "0.114.1" + +[author] + name = "" + homepage = "" + +# If porting an existing theme +[original] + name = "" + homepage = "" + repo = ""