Compare commits

...

13 Commits

Author SHA1 Message Date
lesion
8eb43ac7b5 update telegram plugin submodule 2023-07-01 07:38:36 +02:00
lesion
b7e6adda06 CHANGELOG, prepare 1.6.14 2023-06-30 23:58:16 +02:00
lesion
378c3da58f Merge remote-tracking branch 'weblate/master' 2023-06-30 23:56:40 +02:00
lesion
5deca0ad4f minor 2023-06-30 23:50:01 +02:00
lesion
5e802c4577 minor 2023-06-30 23:49:27 +02:00
lesion
d3207daf2b improve example plugin 2023-06-30 23:49:16 +02:00
lesion
64ed506baf minor 2023-06-30 23:48:33 +02:00
lesion
76e51f0f46 documenting plugins dev 2023-06-30 23:48:17 +02:00
lesion
1bfe4b3995 CLI documentation and new cli users 2023-06-30 23:44:35 +02:00
Artur Mancha
aa5a29123c Translated using Weblate (Portuguese)
Currently translated at 100.0% (13 of 13 strings)

Translation: Gancio/Email
Translate-URL: https://hosted.weblate.org/projects/gancio/email/pt/
2023-06-30 22:46:36 +02:00
Artur Mancha
84f0dcca94 Translated using Weblate (Portuguese)
Currently translated at 100.0% (13 of 13 strings)

Translation: Gancio/Email
Translate-URL: https://hosted.weblate.org/projects/gancio/email/pt/
2023-06-21 01:05:49 +02:00
Artur Mancha
b78cb59a51 Translated using Weblate (Portuguese)
Currently translated at 93.9% (310 of 330 strings)

Translation: Gancio/Web
Translate-URL: https://hosted.weblate.org/projects/gancio/web/pt/
2023-06-21 01:05:48 +02:00
Nathan
023eebdc50 Translated using Weblate (French)
Currently translated at 94.5% (312 of 330 strings)

Translation: Gancio/Web
Translate-URL: https://hosted.weblate.org/projects/gancio/web/fr/
2023-06-16 16:49:34 +02:00
16 changed files with 377 additions and 130 deletions

2
.gitmodules vendored
View File

@@ -1,3 +1,3 @@
[submodule "gancio_plugins/gancio-plugin-telegram-bridge"]
path = gancio_plugins/gancio-plugin-telegram-bridge
url = https://framagit.org/bcn.convocala/gancio-plugin-telegram-bridge.git
url = https://framagit.org/bcn.convocala/gancio-plugin-telegram-bridge

View File

@@ -1,5 +1,17 @@
All notable changes to this project will be documented in this file.
### 1.6.14 - 30 June '23
- improve CLI accounts operations ([documentation](https://gancio.org/usage/cli))
- allow plugins to expose API ([documentation](http://gancio.org/dev/plugins))
- allow plugins to access DB ([documentation](http://gancio.org/dev/plugins))
- show map on the places page, #276 #30
- add node v18 support, #278
- fix media update, #285
- fix nodejs v14 compatibility in export
- fix invalid event microdata, #277
- fix recurrent event, #280
- update deps and locales
### 1.6.13 - 16 may '23
- fix feed, ics, json exports
@@ -134,7 +146,7 @@ All notable changes to this project will be documented in this file.
- restore removed icons
### 1.5.5 - 21 set '22
- fix #185 - wrong res.download api usage
- fix #185 - wrong res.download api usage
- fix some dialog background on light theme
- update sequelize and remove live patch
- improve events filtering on selected day
@@ -202,7 +214,7 @@ All notable changes to this project will be documented in this file.
- fix plain description meta
- fix recurrent events always shown #150
- remove `less` and `less-loader` dependency
### 1.4.3 - 10 mar '22
- fix [#140](https://framagit.org/les/gancio/-/issues/140) - Invalid date
- fix [#141](https://framagit.org/les/gancio/-/issues/141) - Cannot change logo
@@ -285,7 +297,7 @@ All notable changes to this project will be documented in this file.
- 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
@@ -417,7 +429,7 @@ This release is a complete rewrite of frontend UI and many internals, main chang
- use html2text for event description in og: meta
- update deps
- fix a moment.js typo from/to issue
- fix #73
- fix #73
### 0.21.0
- a new recurring events logic (a la taskwarrior):
@@ -537,7 +549,7 @@ This release is a complete rewrite of frontend UI and many internals, main chang
### 0.14.18
- [improve] better quality for images
- [fix] password recovery email
- [feat] new action field for notification
- [feat] new action field for notification
- [feat] add DEBUG env variable in docker-compose.yml
- [style] fixed width in confirmation events table
- [fix] #38 timezone issue in rss export and using tor...

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -8,6 +8,18 @@ nav_order: 10
All notable changes to this project will be documented in this file.
### 1.6.14 - 30 June '23
- improve CLI accounts operations ([documentation](https://gancio.org/usage/cli))
- allow plugins to expose API ([documentation](http://gancio.org/dev/plugins))
- allow plugins to access DB ([documentation](http://gancio.org/dev/plugins))
- show map on the places page, [#276]((https://framagit.org/les/gancio/-/issues/276)) #30
- add node v18 support, [#278]((https://framagit.org/les/gancio/-/issues/278))
- fix media update, [#285]((https://framagit.org/les/gancio/-/issues/285))
- fix nodejs v14 compatibility in export
- fix invalid event microdata, [#277]((https://framagit.org/les/gancio/-/issues/277))
- fix recurrent event, [#280]((https://framagit.org/les/gancio/-/issues/280))
- update deps and locales
### 1.6.13 - 16 may '23
- fix feed, ics, json exports

View File

@@ -16,16 +16,110 @@ Since **v.1.2.2** you can write your own plugin that react to event related acti
>
> [**<u>Please share your plugins or your needs</u>**](/contacts)
Plugins should be inside `./plugins` directory, this is an example:
## Example
Here is a complete example of plugins feature: [https://framagit.org/les/gancio/-/blob/master/plugins/gancioPluginExample.js](https://framagit.org/les/gancio/-/blob/master/plugins/gancioPluginExample.js) .
## Basic plugin syntax
A plugin is essentially an `index.js` file inside its own path in `./plugins`, e.g. `./plugins/my-example-plugin/index.js`
```javascript
module.exports = {
}
```
Plugins should be inside `./plugins` directory but you can specify another location using [`plugins_path`](https://gancio.org/install/config#plugins-path) configuration.
## Plugin details
A plugins **MUST** expose a `configuration` key where to specify its details:
```js
const plugin = {
gancio: null,
load (gancio) {
console.error('Plugin GancioPluginExample loaded!')
plugin.gancio = gancio
},
module.exports = {
configuration: {
name: 'Example',
author: 'lesion',
url: 'https://framagit.org/les/gancio/plugins/gancioPluginExample.js',
description: 'Example plugin',
settings: {
my_plugin_string_setting: {
type: 'TEXT',
description: 'My plugin string setting',
required: true,
hint: 'My plugin setting support <strong>html too</strong>'
},
enable_this_feature_in_my_plugin: {
type: 'CHECK',
description: 'My plugin best feature',
required: true,
hint: 'This feature is super dupe, enable it!'
},
min_post: {
type: 'NUMBER',
description: 'it supports number too'
},
my_default_language: {
description: 'My default language',
type: 'LIST',
items: ['it', 'en', 'fr']
}
}
}
}
```
[![/assets/plugins/settings.png](/assets/plugins/settings.png)](/assets/plugins/settings.png){: data-fancybox="group" data-caption="Home"}
## Load a plugin
When a plugin is enabled by an administrator, Gancio will call the `load` method if specified:
```js
load ({ settings: gancio_settings, db, helpers, log}, settings) {
// access to your plugin local settings
console.info('Your local settings are in ', settings)
console.info(`For example, you can access to your default language setting by using ${settings.my_default_language}`)
// access to gancio settings
console.info(`Gancio settings are in ${gancio_settings}, e.g. ${gancio.settings.baseurl}`)
// log something
log.warn('This is a log entry from my example plugin')
// use the DB (since 1.6.14)
console.info(db.models.findAll())
console.info(db.query('CREATE TABLE IF NOT EXISTS myPluginTable'))
}
```
## Expose an API <span class='label label-yellow'>since 1.6.4</span>
Plugins could have public HTTP endpoints by exposing an express Router in routeAPI object.
```js
const express = require('express')
const routeAPI = express.Router()
routeAPI.get('/test', (req, res) => {
res.json('WOW!')
})
```
This endpoint will be exposed at <your_instance>/api/plugin/<your_plugin_name>/test
## Access to DB <span class='label label-yellow'>since 1.6.4</span>
TODO
## Helpers <span class='label label-red'>DOCUMENTATION NEEDED</span>
- randomString
- sanitizeHTML
- queryParamToBool
## React to events
```js
onEventCreate (event) {
const eventLink = `${plugin.gancio.settings.baseurl}/event/${event.slug}`
if (!event.is_visible) {
@@ -42,10 +136,6 @@ const plugin = {
onEventDelete (event) {
console.error(`Event "${event.title}" deleted`)
}
}
module.exports = plugin
```

View File

@@ -10,7 +10,7 @@ permalink: /
A shared agenda for local communities.
{: .fs-6 }
Last release **[1.6.13 - 16 May 2023](/changelog#1613---16-may-23)**
Last release **[1.6.14 - 30 June 2023](/changelog#1614---30-june-23)**
[Install]({% link install/install.md %}){: .btn .btn-primary .fs-5 .mb-4 .mb-md-0 .mr-2 }
[Demo](https://demo.gancio.org){: .btn .btn-green .fs-5 .mb-4 .mb-md-0 .mr-2 }

60
docs/usage/cli.md Normal file
View File

@@ -0,0 +1,60 @@
---
layout: default
title: CLI
permalink: /usage/cli
nav_order: 2
parent: Usage
has_toc: true
---
# CLI - Command Line Interface
{: .no_toc }
1. TOC
{:toc}
## Using CLI
Gancio is distributed with an embedded CLI.
To use the CLI you need to specify the `config.json` configuration file via `--config <your_config.json>` flag; by default the CLI will look for one in the current directory, so if your current directory is /opt/gancio (having followed the [installation instructions](/install/debian)) there is no need to specify it.
### Using CLI with Docker installation
To use the CLI in a docker installation you can execute a shell inside the container with:
`docker exec --workdir /home/node/data -it gancio sh` and following the normal CLI usage or running commands with:
`docker exec --workdir /home/node/data gancio gancio <your command>`
(the first "gancio" is the container name)
## Users <span class='label label-yellow'>since 1.6.14</span>
All users related sub-commands starts with `gancio users`.
Note that most of this actions could be done from administration panel (Admin > Users).
### List all users
To list all users use
`gancio users list`
### Create a new user
`gancio users create <username|email>`
To create an user with administrator privileges use the `--admin` flag, e.g. `gancio users create admin@example.com --admin`
### Remove a user
`gancio users remove <username|email>`
### Reset password
`gancio users reset-password <username|email>`
### Change administrator privileges
To add administrator privileges to an user:
`gancio users set-admin <username|email>`
To remove administrator privileges from an user:
`gancio users unset-admin <username|email>`

View File

@@ -10,13 +10,12 @@ has_toc: true
# Plugins
{: .no_toc }
This page is a guide to install plugins, if you want to develop one instead look [here](/dev/plugins)
This page is a guide to install plugins, if you want to develop one instead look [here](/dev/plugins).
__Please note that some plugins are officially supported and distributed along with the release__
1. TOC
{:toc}
## Install
To install a plugin you have to:
@@ -49,7 +48,7 @@ __with docker__
docker-compose restart
```
# List of plugins
# List of embedded plugins
## __Telegram__
@@ -57,6 +56,5 @@ This plugin republishes events to Telegram channels or groups.
The goal is to spread the info of our networks to the capitalist cyberspace, and pull otherwise isolated people to our radical and free part of the internet.
- **Website**: [https://framagit.org/bcn.convocala/gancio-plugin-telegram-bridge](https://framagit.org/bcn.convocala/gancio-plugin-telegram-bridge)
- **Download**: [gancio-plugin-telegram-bridge-v0.2.0.zip](https://framagit.org/les/gancio-plugin-telegram-bridge/-/archive/v0.2.0/gancio-plugin-telegram-bridge-v0.2.0.zip)
- **Release**: v0.2.0 / 10 Dec '22
- **Release**: https://framagit.org/bcn.convocala/gancio-plugin-telegram-bridge/-/commit/af0eed7b42242ba484d9828157f1be0355bba69b

View File

@@ -8,22 +8,22 @@
"subject": "Você pode agora começar a publicar eventos"
},
"event_confirm": {
"content": "Você pode confirmar este evento <a href='{{url}}'>nesta página</a>"
"content": "Pode confirmar este evento <a href='{{url}}'>nesta página</a>"
},
"test": {
"subject": "Sua configuração SMTP está funcionando",
"content": "Este é um e-mail de teste, se estiver lendo isto, sua configuração está funcionando."
"subject": "A sua configuração SMTP funciona",
"content": "Este é um e-mail de teste, se estiver a ler isto, a sua configuração funciona."
},
"user_confirm": {
"subject": "Você pode agora começar a publicar eventos",
"content": "Olá, sua conta em <a href='{{config.baseurl}}'>{{config.title}}</a> foi criada. <a href='{{config.baseurl}}/user_confirm/{{user.recover_code}}'>Confirme e escolha uma senha.</a>."
"content": "Olá, a sua conta em <a href='{{config.baseurl}}'>{{config.title}}</a> foi criada. <a href='{{config.baseurl}}/user_confirm/{{user.recover_code}}'>Confirme e escolha uma palavra-passe.</a>."
},
"admin_register": {
"subject": "Novo registro",
"content": "{{user.email}} pediu o registro em {{config.title}}: <br/><pre>{{user.description}}</pre><br/> Confirme <a href='{{config.baseurl}}/admin'>aqui</a>."
"subject": "Novo registo",
"content": "{{user.email}} pediu o registo em {{config.title}}: <br/><pre>{{user.description}}</pre><br/> Confirme <a href='{{config.baseurl}}/admin'>aqui</a>."
},
"recover": {
"subject": "Recuperação de senha",
"content": "Olá, você solicitou a recuperação de sua senha em {{config.title}}. <a href='{{config.baseurl}}/recover/{{user.recover_code}}'>Clique aqui</a> para confirmar."
"subject": "Recuperação da palavra-passe",
"content": "Olá, solicitou a recuperação da sua palavra-passe em {{config.title}}. <a href='{{config.baseurl}}/recover/{{user.recover_code}}'>Clique aqui</a> para confirmar."
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "gancio",
"version": "1.6.13",
"version": "1.6.14",
"description": "A shared agenda for local communities",
"author": "lesion",
"scripts": {

View File

@@ -1,9 +1,62 @@
const express = require('express')
const myPluginRouter = express.Router()
// this will answer at http://localhost:13120/api/plugin/Example/test
myPluginRouter.get('/test', (req, res) => {
return res.json('OK!')
})
const plugin = {
routeAPI: myPluginRouter,
configuration: {
name: 'Example',
author: 'lesion',
url: 'https://framagit.org/les/gancio',
description: 'Example plugin',
settings: {
my_plugin_string_setting: {
type: 'TEXT',
description: 'My plugin string setting',
required: true,
hint: 'My plugin setting support <strong>html too</strong>'
},
enable_this_feature_in_my_plugin: {
type: 'CHECK',
description: 'My plugin best feature',
required: true,
hint: 'This feature is super dupe, enable it!'
},
min_post: {
type: 'NUMBER',
description: 'it supports number too'
},
my_default_language: {
description: 'My default language',
type: 'LIST',
items: ['it', 'en', 'fr']
}
}
},
gancio: null,
load (gancio) {
settings: null,
load (gancio, settings) {
console.error('Plugin GancioPluginExample loaded!')
console.error('Your settings are in ', settings)
console.error(`For example, you can access to your default language setting by using ${settings.my_default_language}`)
plugin.gancio = gancio
plugin.settings = settings
// gancio.db.models.event.findAll({ where: })
// gancio.db.query('CREATE TABLE IF NOT EXISTS ()... ')
},
unload () {
console.error('Unload this plugin!')
},
onTest () {
console.error('called on "TEST" button pressed in admin interface')
},
onEventCreate (event) {

View File

@@ -86,7 +86,7 @@ const pluginController = {
}
if (plugin.unload && typeof plugin.unload === 'function') {
plugin.unload({ settings: settingsController.settings }, settings)
plugin.unload()
}
},

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env node
const pkg = require('../package.json')
const path = require('path')
const accountsCLI = require('./cli/accounts')
const usersCLI = require('./cli/users')
process.env.cwd = process.env.GANCIO_DATA || path.resolve('./')
@@ -29,7 +29,7 @@ require('yargs')
return absolute_config_path
}})
.command(['start', 'run', '$0'], 'Start gancio', {}, start)
.command(['accounts'], 'Manage accounts', accountsCLI)
.command(['users'], 'Manage users', usersCLI)
.help('h')
.alias('h', 'help')
.epilog('Made with ❤ by underscore hacklab - https://gancio.org')

View File

@@ -1,92 +0,0 @@
let db
function _initializeDB () {
const config = require('../config')
config.log_level = 'error'
db = require('../api/models/index')
return db.initialize()
}
async function modify (args) {
await _initializeDB()
const helpers = require('../helpers')
const { User } = require('../api/models/models')
const user = await User.findOne({ where: { email: args.account } })
console.log()
if (!user) {
console.error(`User ${args.account} not found`)
return
}
if (args['reset-password']) {
const password = helpers.randomString()
user.password = password
await user.save()
console.log(`New password for user ${user.email} is '${password}'`)
}
if (args.admin) {
user.is_admin = true
await user.save()
}
await db.close()
}
async function create (args) {
await _initializeDB()
const { User } = require('../api/models/models')
const user = await User.create({
email: args.email,
is_active: true,
is_admin: args.admin || false
}).catch(e => console.error(String(e)))
console.error(JSON.stringify(user, null, 2))
await db.close()
}
async function remove (args) {
await _initializeDB()
const { User } = require('../api/models/models')
const user = await User.findOne({
where: { email: args.email }
})
if (user) {
await user.destroy()
}
await db.close()
}
async function list () {
await _initializeDB()
const { User } = require('../api/models/models')
const users = await User.findAll()
console.log()
users.forEach(u => console.log(`${u.id}\tadmin: ${u.is_admin}\tenabled: ${u.is_active}\temail: ${u.email}`))
console.log()
await db.close()
}
const accountsCLI = yargs => yargs
.command('list', 'List all accounts', list)
.command('modify', 'Modify', {
account: {
describe: 'Account to modify',
type: 'string',
demandOption: true
},
'reset-password': {
describe: 'Resets the password of the given account ',
type: 'boolean'
},
admin: { describe: 'Define this account as administrator', type: 'boolean' }
}, modify)
.command('create <email|username> [admin]', 'Create an account', {
admin: { describe: 'Define this account as administrator', type: 'boolean' }
}, create)
.command('remove <email|username>', 'Remove an account', {}, remove)
.positional('email', { describe: 'account email or username', type: 'string', demandOption: true })
.recommendCommands()
.demandCommand(1, '')
.argv
module.exports = accountsCLI

114
server/cli/users.js Normal file
View File

@@ -0,0 +1,114 @@
let db
function _initializeDB () {
const config = require('../config')
config.log_level = 'error'
db = require('../api/models/index')
return db.initialize()
}
async function setAdmin (args) {
await _initializeDB()
const { User } = require('../api/models/models')
const user = await User.findOne({ where: { email: args.email } })
console.log()
if (!user) {
console.error(`User ${args.email} not found`)
return
}
user.is_admin = true
await user.save()
console.log(`User ${user.email} is now an administrator!`)
await db.close()
}
async function unsetAdmin (args) {
await _initializeDB()
const helpers = require('../helpers')
const { User } = require('../api/models/models')
const user = await User.findOne({ where: { email: args.email } })
console.log()
if (!user) {
console.error(`User ${args.email} not found`)
return
}
user.is_admin = false
await user.save()
console.log(`User ${user.email} is not an administrator anymore!`)
await db.close()
}
async function resetPassword (args) {
await _initializeDB()
const helpers = require('../helpers')
const { User } = require('../api/models/models')
const user = await User.findOne({ where: { email: args.email } })
console.log()
if (!user) {
console.error(`User ${args.email} not found`)
return
}
const password = helpers.randomString()
user.password = password
await user.save()
console.log(`New password for user ${user.email} is '${password}'`)
await db.close()
}
async function create (args) {
await _initializeDB()
const { User } = require('../api/models/models')
const user = await User.create({
email: args.email,
is_active: true,
is_admin: args.admin || false
}).catch(e => console.error(String(e)))
console.error(JSON.stringify(user, null, 2))
await db.close()
}
async function remove (args) {
await _initializeDB()
const { User } = require('../api/models/models')
const user = await User.findOne({
where: { email: args.email }
})
if (user) {
await user.destroy()
console.error(`User "${args.email}" succesfully removed`)
} else {
console.error(`User "${args.email}" not found!`)
}
await db.close()
}
async function list () {
await _initializeDB()
const { User } = require('../api/models/models')
const users = await User.findAll()
console.log()
users.forEach(u => console.log(`${u.id}\tadmin: ${u.is_admin}\tenabled: ${u.is_active}\temail: ${u.email}`))
console.log()
await db.close()
}
const usersCLI = yargs => yargs
.command('list', 'List all users', list)
.command('reset-password <email|username>', 'Resets the password of the given user', {
}, resetPassword)
.command('set-admin <email|username>', 'Set administrator privileges to the given user', {}, setAdmin)
.command('unset-admin <email|username>', 'Remove administrator privileges to the given user', {}, unsetAdmin)
.command('create <email|username> [admin]', 'Create an user', {
admin: { describe: 'Define this user as administrator', type: 'boolean' }
}, create)
.command('remove <email|username>', 'Remove an user', {}, remove)
.positional('email', { describe: 'user email or username', type: 'string', demandOption: true })
.recommendCommands()
.demandCommand(1, '')
.argv
module.exports = usersCLI