diff --git a/.gitignore b/.gitignore
index 7f07453b..f1a48207 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,9 @@
# Created by .ignore support plugin (hsz.mobi)
### Gancio dev configuration
+*.sqlite
+releases
+wp-plugin/wpgancio
config/development.json
gancio_config.json
config.json
diff --git a/CHANGELOG b/CHANGELOG
index 67ad4a66..017f7c48 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,100 @@
All notable changes to this project will be documented in this file.
-### Unreleased
+### 1.2.2 - 7 dic '21
+ - shiny new gancio-event\[s\] webcomponents => [docs](https://gancio.org/usage/embed)
+ - new backend plugin system
+ - improve media focal point selection
+ - improve non-js experience (load img, use native lazy loading)
+ - improve user_confirm / recover code flow
+ - fix task manager exception
+ - fix db initialization when a custom setup is used, #131
+ - remove vue-clipboard2 dependency due to [this](https://github.com/euvl/v-clipboard/issues/18) bug and using a [native with fallback mixin instead](./assets/clipboard.js)
+ - fix a regression to support old CPU, #130
+ - makes dialog use fullscreen on mobile
+ - fix Delete AP Actor Action from fediverse when remote Actor is gone
+ - add `max` param to /events API
+
+### 1.2.1 - 11 nov '21
+ - fix `Note` remove from fediverse
+ - AP Actor is now `Application`, was `Person`
+ - better handling event AP representations
+
+ this release is a step forward to improve AP compatibility with other platforms, thanks @tcit
+
+### 1.2.0 - 9 nov '21
+ - do not overwrite event slug when title is modified to preserve links
+ - add public cache to events images
+ - fix baseurl in initial setup configuration
+ - fix user removal
+ - load settings during startup and not for each request
+ - refactoring user custom locale
+ - published AP event's type is not `Note` anymore but `Event`
+
+### 1.1.1 - 29 ott '21
+ - fix issue adding event with dueHour resulting in `bad request`
+ - fix restart during setup
+ - do not use @nuxt/vuetify module, manually preload vuetify via plugin
+ - remove deprecated nuxt-express-module and use serverMiddleware directly
+
+### 1.1.0 - 26 ott '21
+
+ - a whole new setup via web! fix #126
+ - new SMTP configuration dialog, fix #115
+ - re-order general settings in admin panel
+ - new dark/light theme setting
+ - move quite all configuration into db
+ - fix some email recipients
+ - fix hidden events when not ended
+ - update translations
+ - improve install documentation
+ - add systemd gancio.service
+ - allow italic and span tags inside editor
+ - remove moment-timezone, consola, config, inquirer dependencies
+ - update deps
+
+### 1.0.6 (alpha)
+ - fix Dockerfile yarn cache issue on update, #123
+ - fix overflow on event title @homepage
+ - better import dialog on mobile
+ - re-add attachment to AP
+ - fix max event export
+ - update deps
+
+### 1.0.5 (alpha)
+ - fix/improve debian install docs
+ - fix ics export, use new ics package
+ - use slug url everywhere (rss feed, embedded list)
+ - use i18n in event confirmation email
+ - remove lot of deps warning and remove some unused dependencies
+ - fix show_recurrent in embedded list
+ - remove old to-ico dep, use png favicon instead
+
+### 1.0.4 (alpha)
+ - shows a generic img for events without it
+
+### 1.0.3 (alpha)
+ - 12 hour clock selection, #119
+ - improve media management
+ - add alt-text to featured image, fix #106
+ - add focalPoint support, fix #116
+ - improve a11y
+ - improve node v16 compatibility
+ - fix #122 ? (downgrade prettier)
+
+### 1.0.2 (alpha)
+ - improve oauth flow UI
+ - [WordPress plugin](https://wordpress.org/plugins/wpgancio/)
+ - fix h-event import
+ - improve error logging (add stack trace to exception)
+ - choose start date for recurreing events (#120)
+ - fix user delete from admin
+
+### 1.0.1 (alpha)
+
+ - fix AP resource removal
+ - improve AP resource UI
+ - fix Docker setup
+ - update deps
### 1.0 (alpha)
This release is a complete rewrite of frontend UI and many internals, main changes are:
diff --git a/RELEASE.md b/RELEASE.md
new file mode 100644
index 00000000..2736e4cb
--- /dev/null
+++ b/RELEASE.md
@@ -0,0 +1,11 @@
+- change version in package.json
+- add changes to CHANGELOG / changelog.md
+- yarn build
+- yarn pack
+- yarn publish
+- yarn doc
+- git add .
+- git ci -m 'v...'
+- git tag ...
+- git push --tags
+-
diff --git a/app/router.scrollBehavior.js b/app/router.scrollBehavior.js
deleted file mode 100644
index a054e10b..00000000
--- a/app/router.scrollBehavior.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export default function (to, from, savedPosition) {
- return { x: 0, y: 0 }
-}
diff --git a/assets/clipboard.js b/assets/clipboard.js
new file mode 100644
index 00000000..6b4f6f54
--- /dev/null
+++ b/assets/clipboard.js
@@ -0,0 +1,18 @@
+export default {
+ methods: {
+ clipboard (str, msg = 'common.copied') {
+ try {
+ navigator.clipboard.writeText(str)
+ } catch (e) {
+ const el = document.createElement('textarea')
+ el.addEventListener('focusin', e => e.stopPropagation())
+ el.value = str
+ document.body.appendChild(el)
+ el.select()
+ document.execCommand('copy')
+ document.body.removeChild(el)
+ }
+ this.$root.$message(msg)
+ }
+ }
+}
diff --git a/assets/style.less b/assets/style.less
index 4dc453ee..8d65d218 100644
--- a/assets/style.less
+++ b/assets/style.less
@@ -37,6 +37,9 @@ li {
.v-dialog {
width: 600px;
max-width: 800px;
+ &.v-dialog--fullscreen {
+ max-width: 100%;
+ }
}
.theme--dark.v-list {
@@ -62,14 +65,14 @@ li {
overflow: hidden;
.title {
- transition: all .5s;
- display: block;
- max-height: 3em;
- color: white;
+ display: -webkit-box;
overflow: hidden;
margin: 0.5rem 1rem 0.5rem 1rem;
+ text-overflow: ellipsis;
+ -webkit-line-clamp: 3;
+ -webkit-box-orient: vertical;
font-size: 1.1em !important;
- line-height: 1em !important;
+ line-height: 1.2em !important;
}
.body {
@@ -80,9 +83,9 @@ li {
width: 100%;
max-height: 250px;
min-height: 160px;
- background-color: #222;
object-fit: cover;
object-position: top;
+ aspect-ratio: 1.7778;
}
.place {
@@ -99,10 +102,6 @@ li {
}
}
-.v-list {
- background-color: #333 !important;
-}
-
.vc-past {
opacity: 0.4;
}
@@ -116,4 +115,14 @@ li {
white-space: nowrap;
overflow: hidden;
display: block;
+}
+
+
+.cursorPointer {
+ cursor: pointer;
+}
+
+pre {
+ white-space: break-spaces;
+ font-size: 13px;
}
\ No newline at end of file
diff --git a/components/Calendar.vue b/components/Calendar.vue
index 4e49ebfa..327241ac 100644
--- a/components/Calendar.vue
+++ b/components/Calendar.vue
@@ -8,6 +8,7 @@
:locale='$i18n.locale'
:attributes='attributes'
transition='fade'
+ aria-label='Calendar'
is-expanded
is-inline
@dayclick='click')
diff --git a/components/Confirm.vue b/components/Confirm.vue
index 57c5b279..d7b354b3 100644
--- a/components/Confirm.vue
+++ b/components/Confirm.vue
@@ -1,6 +1,7 @@
v-dialog(v-model='show'
+ :fullscreen='$vuetify.breakpoint.xsOnly'
:color='options.color'
:title='title'
:max-width='options.width'
@@ -11,8 +12,8 @@
v-card-text(v-show='!!message') {{ message }}
v-card-actions
v-spacer
- v-btn(color='error' @click='cancel') {{$t('common.cancel')}}
- v-btn(color='primary' @click='agree') {{$t('common.ok')}}
+ v-btn(text color='error' @click='cancel') {{$t('common.cancel')}}
+ v-btn(text color='primary' @click='agree') {{$t('common.ok')}}
-
diff --git a/pages/add/_edit.vue b/pages/add/_edit.vue
index 64551851..f004340c 100644
--- a/pages/add/_edit.vue
+++ b/pages/add/_edit.vue
@@ -6,7 +6,7 @@
v-spacer
v-btn(link text color='primary' @click='openImportDialog=true')
mdi-file-import {{$t('common.import')}}
- v-dialog(v-model='openImportDialog')
+ v-dialog(v-model='openImportDialog' :fullscreen='$vuetify.breakpoint.xsOnly')
ImportDialog(@close='openImportDialog=false' @imported='eventImported')
v-card-text.px-0.px-xs-2
@@ -33,8 +33,7 @@
WhereInput(ref='where' v-model='event.place')
//- When
- DateInput(v-model='date')
-
+ DateInput(v-model='date' :event='event')
//- Description
v-col.px-0(cols='12')
Editor.px-3.ma-0(
@@ -45,14 +44,7 @@
//- MEDIA / FLYER / POSTER
v-col(cols=12 md=6)
- v-file-input(
- :label="$t('common.media')"
- :hint="$t('event.media_description')"
- prepend-icon="mdi-camera"
- v-model='event.image'
- persistent-hint
- accept='image/*')
- v-img.col-12.col-sm-2.ml-3(v-if='mediaPreview' :src='mediaPreview')
+ MediaInput(v-model='event.media[0]' :event='event' @remove='event.media=[]')
//- tags
v-col(cols=12 md=6)
@@ -66,7 +58,7 @@
v-card-actions
v-spacer
v-btn(@click='done' :loading='loading' :disabled='!valid || loading'
- color='primary') {{edit?$t('common.edit'):$t('common.send')}}
+ color='primary') {{edit?$t('common.save'):$t('common.send')}}
-`;
+ init(this, {
+ target: this.shadowRoot,
+ props: attribute_to_object(this.attributes),
+ customElement: true
+ }, instance$1, create_fragment$1, safe_not_equal, {
+ baseurl: 0,
+ title: 1,
+ maxlength: 3,
+ tags: 4,
+ places: 5
+ }, null);
+ if (options) {
+ if (options.target) {
+ insert(options.target, this, options.anchor);
+ }
+ if (options.props) {
+ this.$set(options.props);
+ flush();
+ }
+ }
+ }
+ static get observedAttributes() {
+ return ["baseurl", "title", "maxlength", "tags", "places"];
+ }
+ get baseurl() {
+ return this.$$.ctx[0];
+ }
+ set baseurl(baseurl) {
+ this.$$set({ baseurl });
+ flush();
+ }
+ get title() {
+ return this.$$.ctx[1];
+ }
+ set title(title) {
+ this.$$set({ title });
+ flush();
+ }
+ get maxlength() {
+ return this.$$.ctx[3];
+ }
+ set maxlength(maxlength) {
+ this.$$set({ maxlength });
+ flush();
+ }
+ get tags() {
+ return this.$$.ctx[4];
+ }
+ set tags(tags) {
+ this.$$set({ tags });
+ flush();
+ }
+ get places() {
+ return this.$$.ctx[5];
+ }
+ set places(places) {
+ this.$$set({ places });
+ flush();
+ }
+}
+customElements.define("gancio-events", GancioEvents);
+function create_if_block(ctx) {
+ let a;
+ let t0;
+ let div2;
+ let strong;
+ let t1_value = ctx[1].title + "";
+ let t1;
+ let t2;
+ let div0;
+ let t3_value = when(ctx[1]) + "";
+ let t3;
+ let t4;
+ let div1;
+ let t5;
+ let t6_value = ctx[1].place.name + "";
+ let t6;
+ let a_href_value;
+ let if_block = ctx[1].media.length && create_if_block_1(ctx);
+ return {
+ c() {
+ a = element("a");
+ if (if_block)
+ if_block.c();
+ t0 = space();
+ div2 = element("div");
+ strong = element("strong");
+ t1 = text(t1_value);
+ t2 = space();
+ div0 = element("div");
+ t3 = text(t3_value);
+ t4 = space();
+ div1 = element("div");
+ t5 = text("@");
+ t6 = text(t6_value);
+ attr(div1, "class", "place");
+ attr(div2, "class", "container");
+ attr(a, "href", a_href_value = "" + (ctx[0] + "/event/" + (ctx[1].slug || ctx[1].id)));
+ attr(a, "class", "card");
+ attr(a, "target", "_blank");
+ },
+ m(target, anchor) {
+ insert(target, a, anchor);
+ if (if_block)
+ if_block.m(a, null);
+ append(a, t0);
+ append(a, div2);
+ append(div2, strong);
+ append(strong, t1);
+ append(div2, t2);
+ append(div2, div0);
+ append(div0, t3);
+ append(div2, t4);
+ append(div2, div1);
+ append(div1, t5);
+ append(div1, t6);
+ },
+ p(ctx2, dirty) {
+ if (ctx2[1].media.length) {
+ if (if_block) {
+ if_block.p(ctx2, dirty);
+ } else {
+ if_block = create_if_block_1(ctx2);
+ if_block.c();
+ if_block.m(a, t0);
+ }
+ } else if (if_block) {
+ if_block.d(1);
+ if_block = null;
+ }
+ if (dirty & 2 && t1_value !== (t1_value = ctx2[1].title + ""))
+ set_data(t1, t1_value);
+ if (dirty & 2 && t3_value !== (t3_value = when(ctx2[1]) + ""))
+ set_data(t3, t3_value);
+ if (dirty & 2 && t6_value !== (t6_value = ctx2[1].place.name + ""))
+ set_data(t6, t6_value);
+ if (dirty & 3 && a_href_value !== (a_href_value = "" + (ctx2[0] + "/event/" + (ctx2[1].slug || ctx2[1].id)))) {
+ attr(a, "href", a_href_value);
+ }
+ },
+ d(detaching) {
+ if (detaching)
+ detach(a);
+ if (if_block)
+ if_block.d();
+ }
+ };
+}
+function create_if_block_1(ctx) {
+ let img;
+ let img_src_value;
+ let img_alt_value;
+ let img_style_value;
+ return {
+ c() {
+ img = element("img");
+ if (!src_url_equal(img.src, img_src_value = ctx[2](ctx[1])))
+ attr(img, "src", img_src_value);
+ attr(img, "alt", img_alt_value = ctx[1].media[0].name);
+ attr(img, "style", img_style_value = "object-position: " + position(ctx[1]) + "; aspect-ratio=1.7778;");
+ },
+ m(target, anchor) {
+ insert(target, img, anchor);
+ },
+ p(ctx2, dirty) {
+ if (dirty & 2 && !src_url_equal(img.src, img_src_value = ctx2[2](ctx2[1]))) {
+ attr(img, "src", img_src_value);
+ }
+ if (dirty & 2 && img_alt_value !== (img_alt_value = ctx2[1].media[0].name)) {
+ attr(img, "alt", img_alt_value);
+ }
+ if (dirty & 2 && img_style_value !== (img_style_value = "object-position: " + position(ctx2[1]) + "; aspect-ratio=1.7778;")) {
+ attr(img, "style", img_style_value);
+ }
+ },
+ d(detaching) {
+ if (detaching)
+ detach(img);
+ }
+ };
+}
+function create_fragment(ctx) {
+ let if_block_anchor;
+ let if_block = ctx[1] && create_if_block(ctx);
+ return {
+ c() {
+ if (if_block)
+ if_block.c();
+ if_block_anchor = empty();
+ this.c = noop;
+ },
+ m(target, anchor) {
+ if (if_block)
+ if_block.m(target, anchor);
+ insert(target, if_block_anchor, anchor);
+ },
+ p(ctx2, [dirty]) {
+ if (ctx2[1]) {
+ if (if_block) {
+ if_block.p(ctx2, dirty);
+ } else {
+ if_block = create_if_block(ctx2);
+ if_block.c();
+ if_block.m(if_block_anchor.parentNode, if_block_anchor);
+ }
+ } else if (if_block) {
+ if_block.d(1);
+ if_block = null;
+ }
+ },
+ i: noop,
+ o: noop,
+ d(detaching) {
+ if (if_block)
+ if_block.d(detaching);
+ if (detaching)
+ detach(if_block_anchor);
+ }
+ };
+}
+function when(event) {
+ return new Date(event.start_datetime * 1e3).toLocaleDateString(void 0, {
+ weekday: "long",
+ month: "short",
+ day: "numeric",
+ hour: "2-digit",
+ minute: "2-digit"
+ });
+}
+function position(event) {
+ if (event.media[0].focalpoint) {
+ const focalpoint = event.media[0].focalpoint;
+ return `${(focalpoint[0] + 1) * 50}% ${(focalpoint[1] + 1) * 50}%`;
+ }
+ return "center center";
+}
+function instance($$self, $$props, $$invalidate) {
+ let { baseurl = "https://demo.gancio.org" } = $$props;
+ let { id } = $$props;
+ let mounted = false;
+ let event;
+ function update2(id2, baseurl2) {
+ if (mounted) {
+ fetch(`${baseurl2}/api/event/${id2}`).then((res) => res.json()).then((e) => $$invalidate(1, event = e));
+ }
+ }
+ onMount(() => {
+ mounted = true;
+ update2(id, baseurl);
+ });
+ function thumbnail(event2) {
+ return `${baseurl}/media/thumb/${event2.media[0].url}`;
+ }
+ $$self.$$set = ($$props2) => {
+ if ("baseurl" in $$props2)
+ $$invalidate(0, baseurl = $$props2.baseurl);
+ if ("id" in $$props2)
+ $$invalidate(3, id = $$props2.id);
+ };
+ $$self.$$.update = () => {
+ if ($$self.$$.dirty & 9) {
+ update2(id, baseurl);
+ }
+ };
+ return [baseurl, event, thumbnail, id];
+}
+class GancioEvent extends SvelteElement {
+ constructor(options) {
+ super();
+ this.shadowRoot.innerHTML = ``;
+ init(this, {
+ target: this.shadowRoot,
+ props: attribute_to_object(this.attributes),
+ customElement: true
+ }, instance, create_fragment, safe_not_equal, { baseurl: 0, id: 3 }, null);
+ if (options) {
+ if (options.target) {
+ insert(options.target, this, options.anchor);
+ }
+ if (options.props) {
+ this.$set(options.props);
+ flush();
+ }
+ }
+ }
+ static get observedAttributes() {
+ return ["baseurl", "id"];
+ }
+ get baseurl() {
+ return this.$$.ctx[0];
+ }
+ set baseurl(baseurl) {
+ this.$$set({ baseurl });
+ flush();
+ }
+ get id() {
+ return this.$$.ctx[3];
+ }
+ set id(id) {
+ this.$$set({ id });
+ flush();
+ }
+}
+customElements.define("gancio-event", GancioEvent);
diff --git a/static/noimg.svg b/static/noimg.svg
new file mode 100644
index 00000000..44b83d6c
--- /dev/null
+++ b/static/noimg.svg
@@ -0,0 +1,274 @@
+
+
+
diff --git a/store/index.js b/store/index.js
index 851348fb..8437284b 100644
--- a/store/index.js
+++ b/store/index.js
@@ -15,7 +15,8 @@ export const state = () => ({
enable_resources: false,
hide_boosts: true,
enable_trusted_instances: true,
- trusted_instances: []
+ trusted_instances: [],
+ footerLinks: []
},
announcements: []
})
@@ -35,6 +36,9 @@ export const mutations = {
setLocale (state, locale) {
state.locale = locale
},
+ setUserlocale (state, messages) {
+ state.user_locale = messages
+ },
setFilters (state, filters) {
state.filters.tags = [...filters.tags]
state.filters.places = [...filters.places]
@@ -50,8 +54,10 @@ export const actions = {
// we use it to get configuration from db, set locale, etc...
nuxtServerInit ({ commit }, { req }) {
commit('setSettings', req.settings)
- commit('setAnnouncements', req.announcements)
- commit('update', req.meta)
+ if (!req.firstrun) {
+ commit('setAnnouncements', req.announcements)
+ commit('update', req.meta)
+ }
},
async updateAnnouncements ({ commit }) {
const announcements = await this.$axios.$get('/announcements')
diff --git a/views/feed/rss.pug b/views/feed/rss.pug
index 472d6686..b78d8a23 100644
--- a/views/feed/rss.pug
+++ b/views/feed/rss.pug
@@ -8,15 +8,15 @@ rss(version='2.0' xmlns:atom="http://www.w3.org/2005/Atom")
each event in events
item
title [#{moment.unix(event.start_datetime).format("YY-MM-DD")}] #{event.title} @#{event.place.name}
- link #{settings.baseurl}/event/#{event.id}
+ link #{settings.baseurl}/event/#{event.slug || event.id}
description
| #{event.title}
| #{event.place.name} - #{event.place.address}
| (#{moment.unix(event.start_datetime).format("dddd, D MMMM HH:mm")})
- if (event.image_path)
- |
+ if (event.media && event.media.length)
+ |
|
!{event.description}
| ]]>
pubDate= new Date(event.updatedAt).toUTCString()
- guid(isPermaLink='false') #{settings.baseurl}/event/#{event.id}
+ guid(isPermaLink='false') #{settings.baseurl}/event/#{event.slug || event.id}
diff --git a/vuetify.options.js b/vuetify.options.js
deleted file mode 100644
index 474ac8bf..00000000
--- a/vuetify.options.js
+++ /dev/null
@@ -1,15 +0,0 @@
-// vuetify.options.js
-export default {
- theme: {
- dark: true,
- // theme: { disable: true },
- themes: {
- dark: {
- primary: '#FF6E40'
- },
- light: {
- primary: '#FF4500'
- }
- }
- }
-}
diff --git a/webcomponents/.gitignore b/webcomponents/.gitignore
new file mode 100644
index 00000000..126fe84d
--- /dev/null
+++ b/webcomponents/.gitignore
@@ -0,0 +1,4 @@
+/node_modules/
+/dist/
+/.vscode/
+.DS_Store
diff --git a/webcomponents/README.md b/webcomponents/README.md
new file mode 100644
index 00000000..8e35d33d
--- /dev/null
+++ b/webcomponents/README.md
@@ -0,0 +1,48 @@
+# Svelte + Vite
+
+This template should help get you started developing with Svelte in Vite.
+
+## Recommended IDE Setup
+
+[VSCode](https://code.visualstudio.com/) + [Svelte](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode).
+
+## Need an official Svelte framework?
+
+Check out [SvelteKit](https://github.com/sveltejs/kit#readme), which is also powered by Vite. Deploy anywhere with its serverless-first approach and adapt to various platforms, with out of the box support for TypeScript, SCSS, and Less, and easily-added support for mdsvex, GraphQL, PostCSS, Tailwind CSS, and more.
+
+## Technical considerations
+
+**Why use this over SvelteKit?**
+
+- It brings its own routing solution which might not be preferable for some users.
+- It is first and foremost a framework that just happens to use Vite under the hood, not a Vite app.
+ `vite dev` and `vite build` wouldn't work in a SvelteKit environment, for example.
+
+This template contains as little as possible to get started with Vite + Svelte, while taking into account the developer experience with regards to HMR and intellisense. It demonstrates capabilities on par with the other `create-vite` templates and is a good starting point for beginners dipping their toes into a Vite + Svelte project.
+
+Should you later need the extended capabilities and extensibility provided by SvelteKit, the template has been structured similarly to SvelteKit so that it is easy to migrate.
+
+**Why `global.d.ts` instead of `compilerOptions.types` inside `jsconfig.json` or `tsconfig.json`?**
+
+Setting `compilerOptions.types` shuts out all other types not explicitly listed in the configuration. Using triple-slash references keeps the default TypeScript setting of accepting type information from the entire workspace, while also adding `svelte` and `vite/client` type information.
+
+**Why include `.vscode/extensions.json`?**
+
+Other templates indirectly recommend extensions via the README, but this file allows VS Code to prompt the user to install the recommended extension upon opening the project.
+
+**Why enable `checkJs` in the JS template?**
+
+It is likely that most cases of changing variable types in runtime are likely to be accidental, rather than deliberate. This provides advanced typechecking out of the box. Should you like to take advantage of the dynamically-typed nature of JavaScript, it is trivial to change the configuration.
+
+**Why is HMR not preserving my local component state?**
+
+HMR state preservation comes with a number of gotchas! It has been disabled by default in both `svelte-hmr` and `@sveltejs/vite-plugin-svelte` due to its often surprising behavior. You can read the details [here](https://github.com/rixo/svelte-hmr#svelte-hmr).
+
+If you have state that's important to retain within a component, consider creating an external store which would not be replaced by HMR.
+
+```js
+// store.js
+// An extremely simple external store
+import { writable } from 'svelte/store'
+export default writable(0)
+```
diff --git a/webcomponents/index.html b/webcomponents/index.html
new file mode 100644
index 00000000..95a69b70
--- /dev/null
+++ b/webcomponents/index.html
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+ Gancio Events Custom Element Demo
+
+
+
+
+
+
+
diff --git a/webcomponents/jsconfig.json b/webcomponents/jsconfig.json
new file mode 100644
index 00000000..42585941
--- /dev/null
+++ b/webcomponents/jsconfig.json
@@ -0,0 +1,34 @@
+{
+ "compilerOptions": {
+ "moduleResolution": "node",
+ "target": "esnext",
+ "module": "esnext",
+ /**
+ * svelte-preprocess cannot figure out whether you have
+ * a value or a type, so tell TypeScript to enforce using
+ * `import type` instead of `import` for Types.
+ */
+ "importsNotUsedAsValues": "error",
+ "isolatedModules": true,
+ "resolveJsonModule": true,
+ /**
+ * To have warnings / errors of the Svelte compiler at the
+ * correct position, enable source maps by default.
+ */
+ "sourceMap": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true,
+ "baseUrl": ".",
+ /**
+ * Typecheck JS in `.svelte` and `.js` files by default.
+ * Disable this if you'd like to use dynamic types.
+ */
+ "checkJs": true
+ },
+ /**
+ * Use global.d.ts instead of compilerOptions.types
+ * to avoid limiting type declarations.
+ */
+ "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"]
+}
diff --git a/webcomponents/package.json b/webcomponents/package.json
new file mode 100644
index 00000000..1488ed0b
--- /dev/null
+++ b/webcomponents/package.json
@@ -0,0 +1,16 @@
+{
+ "name": "gancio-events",
+ "version": "0.0.1",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "build:lib": "vite build -c=vite.lib.config.js",
+ "serve": "vite preview"
+ },
+ "devDependencies": {
+ "@sveltejs/vite-plugin-svelte": "^1.0.0-next.11",
+ "svelte": "^3.37.0",
+ "vite": "^2.6.4"
+ }
+}
diff --git a/webcomponents/src/GancioEvent.svelte b/webcomponents/src/GancioEvent.svelte
new file mode 100644
index 00000000..ab87e9c0
--- /dev/null
+++ b/webcomponents/src/GancioEvent.svelte
@@ -0,0 +1,104 @@
+
+
+{#if event}
+
+ {#if event.media.length}
+
+ {/if}
+
";
return;
}
$data = json_decode(wp_remote_retrieve_body($response));
- update_post_meta($post_id, 'gancio_id', $data->id);
+ update_post_meta($post_id, 'wpgancio_gancio_id', intval($data->id));
}
diff --git a/wp-plugin/readme.txt b/wp-plugin/readme.txt
index a705fa08..c1315b70 100644
--- a/wp-plugin/readme.txt
+++ b/wp-plugin/readme.txt
@@ -1,10 +1,10 @@
=== WPGancio ===
Contributors: lesion
Donate link: https://gancio.org
-Tags: events, gancio
+Tags: events, gancio, fediverse, AP, activity pub
Requires at least: 4.7
-Tested up to: 5.4
-Stable tag: 4.3
+Tested up to: 5.7.2
+Stable tag: 1.0
Requires PHP: 7.0
License: AGPLv3 or later
License URI: https://www.gnu.org/licenses/agpl-3.0.html
@@ -13,36 +13,12 @@ Connect a gancio instance to a Wordpress user so that published events are autom
== Description ==
+This plugin connects a [Gancio](https://gancio.org) instance to a Wordpress website to automatically push events published on Wordpress.
-
-== Frequently Asked Questions ==
-
-= A question that someone might have =
-
-An answer to that question.
-
-= What about foo bar? =
-
-Answer to foo bar dilemma.
-
-== Screenshots ==
-
-1. This screen shot description corresponds to screenshot-1.(png|jpg|jpeg|gif). Note that the screenshot is stored in the /assets directory.
-2. This is the second screen shot
+It requires an event manager plugin, only [Event Organiser[(https://wp-event-organiser.com/) is supported but adding another plugin it's an easy task.
+
== Changelog ==
= 1.0 =
-* A change since the previous version.
-* Another change.
-
-= 0.5 =
-* List versions from most recent at top to oldest at bottom.
-
-== Upgrade Notice ==
-
-= 1.0 =
-Upgrade notices describe the reason a user should upgrade. No more than 300 characters.
-
-= 0.5 =
-This version fixes a security related bug. Upgrade immediately.
+* First release
diff --git a/wp-plugin/settings.php b/wp-plugin/settings.php
index 55e57712..d0900540 100644
--- a/wp-plugin/settings.php
+++ b/wp-plugin/settings.php
@@ -22,7 +22,7 @@ function wpgancio_update_options ($old_value, $instance_url) {
$redirect_uri = get_site_url(null, '/wp-admin/options-general.php?page=wpgancio' );
$query = join('&', array(
'response_type=code',
- 'redirect_uri=' . esc_html($redirect_uri),
+ 'redirect_uri=' . esc_url($redirect_uri),
'scope=event:write',
'client_id=' . get_option('wpgancio_client_id'),
));
@@ -53,8 +53,8 @@ function wpgancio_instance_url_validate ($instance_url) {
$response->get_error_message());
} else {
$data = json_decode( wp_remote_retrieve_body($response), true);
- update_option('wpgancio_client_secret', $data['client_secret']);
- update_option('wpgancio_client_id', $data['client_id']);
+ update_option('wpgancio_client_secret', sanitize_key($data['client_secret']));
+ update_option('wpgancio_client_id', sanitize_key($data['client_id']));
return $instance_url;
}
}
@@ -88,7 +88,7 @@ function wpgancio_instance_url_cb( $args ) {
name="wpgancio_instance_url">