mirror of
https://framagit.org/les/gancio.git
synced 2025-01-31 08:32:23 +01:00
use id in places url (ics and rss feeds, place page) with a reasonable backward compatible fallback
This commit is contained in:
parent
0899e71684
commit
8f9bc7e9de
12 changed files with 107 additions and 17 deletions
|
@ -20,7 +20,7 @@
|
|||
|
||||
<div class='p-location' itemprop="location" itemscope itemtype="https://schema.org/Place">
|
||||
<nuxt-link class='place d-block pl-0' text
|
||||
:to='`/place/${encodeURIComponent(event.place.name)}`'>
|
||||
:to='`/place/${event.place.id}/${encodeURIComponent(event.place.name)}`'>
|
||||
<v-icon v-text='mdiMapMarker'></v-icon>
|
||||
<span itemprop='name'>{{ event.place.name }}</span>
|
||||
</nuxt-link>
|
||||
|
|
|
@ -7,7 +7,7 @@ v-card
|
|||
v-row.my-4.d-flex.flex-column.align-center.text-center
|
||||
.text-h6
|
||||
v-icon(v-text='mdiMapMarker' )
|
||||
nuxt-link.ml-2.text-decoration-none(v-text="place.name" :to='`/place/${place.name}`')
|
||||
nuxt-link.ml-2.text-decoration-none(v-text="place.name" :to='`/place/${place.id}/${encodeURIComponent(place.name)}`')
|
||||
.mx-2(v-text="`${place.address}`")
|
||||
v-card-actions.py-4
|
||||
HowToArriveNav.pl-1(:place='place')
|
||||
|
|
|
@ -84,7 +84,7 @@ v-container
|
|||
template(v-slot:item.actions='{ item }')
|
||||
v-btn(@click='editPlace(item)' color='primary' icon)
|
||||
v-icon(v-text='mdiPencil')
|
||||
nuxt-link(:to='`/place/${item.name}`')
|
||||
nuxt-link(:to='`/place/${item.id}/${encodeURIComponent(item.name)}`')
|
||||
v-icon(v-text='mdiEye')
|
||||
|
||||
</template>
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
.p-location.h-adr(itemprop="location" itemscope itemtype="https://schema.org/Place")
|
||||
v-icon(v-text='mdiMapMarker' small)
|
||||
nuxt-link.vcard.ml-2.p-name.text-decoration-none.text-uppercase(:to='`/place/${encodeURIComponent(event?.place?.name)}`')
|
||||
nuxt-link.vcard.ml-2.p-name.text-decoration-none.text-uppercase(:to='`/place/${event?.place?.id}/${encodeURIComponent(event?.place?.name)}`')
|
||||
span(itemprop='name') {{event?.place?.name}}
|
||||
.font-weight-light.p-street-address(v-if='event?.place?.name !=="online"' itemprop='address') {{event?.place?.address}}
|
||||
|
||||
|
|
65
pages/place/_id/_place.vue
Normal file
65
pages/place/_id/_place.vue
Normal file
|
@ -0,0 +1,65 @@
|
|||
<template>
|
||||
<v-container id='home' class='px-2 px-sm-6 pt-0'>
|
||||
<h1 class='d-block text-h4 font-weight-black text-center text-uppercase mt-10 mx-auto w-100 text-underline'>
|
||||
<u>{{ place.name }}</u>
|
||||
</h1>
|
||||
<span v-if='place.name!=="online"' class="d-block text-subtitle text-center w-100">{{ place.address }}</span>
|
||||
|
||||
<!-- Map -->
|
||||
<div v-if='settings.allow_geolocation && place.latitude && place.longitude' >
|
||||
<div class="mt-4 mx-auto px-4" >
|
||||
<Map :place='place' :height='mapHeight' />
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<HowToArriveNav :place='place' class="justify-center" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Events -->
|
||||
<div id="events" class='mt-14'>
|
||||
<v-lazy class='event v-card' :value='idx<9' v-for='(event, idx) in events' :key='event.id' :min-height='hide_thumbs ? 105 : undefined' :options="{ threshold: .5, rootMargin: '500px' }" :class="{ 'theme--dark': is_dark }">
|
||||
<Event :event='event' :lazy='idx > 9' />
|
||||
</v-lazy>
|
||||
</div>
|
||||
</v-container>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
import { mapState, mapGetters } from 'vuex'
|
||||
import Event from '@/components/Event'
|
||||
import HowToArriveNav from '@/components/HowToArriveNav.vue'
|
||||
|
||||
export default {
|
||||
name: 'Place',
|
||||
components: {
|
||||
Event,
|
||||
HowToArriveNav,
|
||||
[process.client && 'Map']: () => import('@/components/Map.vue')
|
||||
},
|
||||
data() {
|
||||
return { mapHeight: "14rem" }
|
||||
},
|
||||
head() {
|
||||
const title = `${this.settings.title} - ${this.place.name}`
|
||||
return {
|
||||
title,
|
||||
link: [
|
||||
{ rel: 'alternate', type: 'application/rss+xml', title, href: this.settings.baseurl + `/feed/rss/place/${this.place.id}` },
|
||||
{ rel: 'alternate', type: 'text/calendar', title, href: this.settings.baseurl + `/feed/ics/place/${this.place.id}` }
|
||||
],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['settings']),
|
||||
...mapGetters(['hide_thumbs', 'is_dark']),
|
||||
},
|
||||
async asyncData({ $axios, params, error }) {
|
||||
try {
|
||||
const { events, place } = await $axios.$get(`/place/${params.id}`)
|
||||
return { place, events }
|
||||
} catch (e) {
|
||||
error({ statusCode: 404, message: 'Place not found!' })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -44,8 +44,8 @@ export default {
|
|||
return {
|
||||
title,
|
||||
link: [
|
||||
{ rel: 'alternate', type: 'application/rss+xml', title, href: this.settings.baseurl + `/feed/rss/place/${this.place.name}` },
|
||||
{ rel: 'alternate', type: 'text/calendar', title, href: this.settings.baseurl + `/feed/ics/place/${this.place.name}` }
|
||||
{ rel: 'alternate', type: 'application/rss+xml', title, href: this.settings.baseurl + `/feed/rss/place/${this.place.id}` },
|
||||
{ rel: 'alternate', type: 'text/calendar', title, href: this.settings.baseurl + `/feed/ics/place/${this.place.id}` }
|
||||
],
|
||||
}
|
||||
},
|
||||
|
|
|
@ -9,22 +9,22 @@ const { Op, where, col, fn, cast } = require('sequelize')
|
|||
module.exports = {
|
||||
|
||||
async getEvents (req, res) {
|
||||
const placeName = req.params.placeName
|
||||
const place = await Place.findOne({ where: { name: placeName }})
|
||||
const placeNameOrId = req.params.placeNameOrId
|
||||
const place = await Place.findOne({ where: { [Op.or]: { id: placeNameOrId, name: placeNameOrId }}})
|
||||
if (!place) {
|
||||
log.warn(`Place ${placeName} not found`)
|
||||
log.warn(`Place ${placeNameOrId} not found`)
|
||||
return res.sendStatus(404)
|
||||
}
|
||||
|
||||
const format = req.params.format || 'json'
|
||||
log.debug(`Events for place: ${placeName}`)
|
||||
log.debug(`Events for place: ${place.name}`)
|
||||
const events = await eventController._select({ places: String(place.id), show_recurrent: true, show_multidate: true })
|
||||
|
||||
switch (format) {
|
||||
case 'rss':
|
||||
return exportController.feed(req, res, events,
|
||||
`${res.locals.settings.title} - Place @${place.name}`,
|
||||
`${res.locals.settings.baseurl}/feed/rss/place/${place.name}`)
|
||||
`${res.locals.settings.baseurl}/feed/rss/place/${place.id}`)
|
||||
case 'ics':
|
||||
return exportController.ics(req, res, events)
|
||||
default:
|
||||
|
|
|
@ -190,7 +190,7 @@ module.exports = () => {
|
|||
|
||||
// - PLACES
|
||||
api.get('/places', isAdmin, placeController.getAll)
|
||||
api.get('/place/:placeName', cors, placeController.getEvents)
|
||||
api.get('/place/:placeNameOrId', cors, placeController.getEvents)
|
||||
api.get('/place', cors, placeController.search)
|
||||
api.put('/place', isAdmin, placeController.updatePlace)
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ module.exports = (sequelize, DataTypes) => {
|
|||
const Place = sequelize.define('place', {
|
||||
name: {
|
||||
type: DataTypes.STRING,
|
||||
unique: true,
|
||||
index: true,
|
||||
allowNull: false
|
||||
},
|
||||
|
@ -18,9 +17,10 @@ module.exports = (sequelize, DataTypes) => {
|
|||
})
|
||||
|
||||
/**
|
||||
* @description WIP -> https://codeberg.org/fediverse/fep/src/commit/4a75a1bc50bc6d19fc1e6112f02c52621bc178fe/fep/8a8e/fep-8a8e.md#location
|
||||
* @todo support PostalAddress type
|
||||
* @returns ActivityStream location representation
|
||||
* @link https://www.w3.org/TR/activitystreams-vocabulary/#places
|
||||
* @todo support PostalAddress type
|
||||
* @link WIP -> https://codeberg.org/fediverse/fep/src/commit/4a75a1bc50bc6d19fc1e6112f02c52621bc178fe/fep/8a8e/fep-8a8e.md#location
|
||||
*/
|
||||
Place.prototype.toAP = function () {
|
||||
return {
|
||||
|
|
|
@ -288,7 +288,9 @@ const Helpers = {
|
|||
return false
|
||||
})
|
||||
|
||||
await event.setPlace(place)
|
||||
if (place) {
|
||||
await event.setPlace(place)
|
||||
}
|
||||
|
||||
// create/assign tags
|
||||
let tags = []
|
||||
|
|
23
server/migrations/20250109092712-removeNameUniqueness.js
Normal file
23
server/migrations/20250109092712-removeNameUniqueness.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
'use strict';
|
||||
|
||||
/** @type {import('sequelize-cli').Migration} */
|
||||
module.exports = {
|
||||
async up (queryInterface, Sequelize) {
|
||||
// needed as in sequelize there is no support for alter table and sequelize simulate this by creating a backup table and dropping the old one:
|
||||
// this will cause a foreign key error
|
||||
const dialect = queryInterface.sequelize.getDialect()
|
||||
if (dialect === 'sqlite') {
|
||||
await queryInterface.sequelize.query('PRAGMA foreign_keys = OFF')
|
||||
}
|
||||
|
||||
await queryInterface.changeColumn('places', 'name', { type: Sequelize.STRING, index: true, allowNull: false, unique: false })
|
||||
},
|
||||
|
||||
async down (queryInterface, Sequelize) {
|
||||
const dialect = queryInterface.sequelize.getDialect()
|
||||
if (dialect === 'sqlite') {
|
||||
await queryInterface.sequelize.query('PRAGMA foreign_keys = OFF')
|
||||
}
|
||||
await queryInterface.changeColumn('places', 'name', { type: Sequelize.STRING, index: true, allowNull: false, unique: true })
|
||||
}
|
||||
};
|
|
@ -42,7 +42,7 @@ async function main () {
|
|||
// rss / ics feed
|
||||
app.use(helpers.feedRedirect)
|
||||
app.get('/feed/:format/tag/:tag', cors(), tagController.getEvents)
|
||||
app.get('/feed/:format/place/:placeName', cors(), placeController.getEvents)
|
||||
app.get('/feed/:format/place/:placeNameOrId', cors(), placeController.getEvents)
|
||||
app.get('/feed/:format/collection/:name', cors(), collectionController.getEvents)
|
||||
app.get('/feed/:format', cors(), exportController.export)
|
||||
|
||||
|
|
Loading…
Reference in a new issue