- rename from jitsi_calendar to atocal, to reflect more the usage. This plugin allows to create events
- add initial documentation
This commit is contained in:
1 parent
8c5d19375e
commit
b100d9c986
6 files changed
+118
-38
No files matched your search
@@ -0,0 +1,80 @@
|
|||||||
|
# AToCal — Add To Calendar
|
||||||
|
|
||||||
|
A Nextcloud app that lets remote applications create events in the
|
||||||
|
authenticated user's Nextcloud Calendar via a simple link.
|
||||||
|
|
||||||
|
## How it works
|
||||||
|
|
||||||
|
```
|
||||||
|
remote app Nextcloud (atocal)
|
||||||
|
| |
|
||||||
|
| GET /apps/atocal/confirm |
|
||||||
|
| summary, start, end, ... |
|
||||||
|
|----------------------------->|
|
||||||
|
| | confirm page
|
||||||
|
|<-----------------------------|
|
||||||
|
| |
|
||||||
|
| POST /apps/atocal/create | (CSRF token from form)
|
||||||
|
|----------------------------->|
|
||||||
|
| | event in "Personal" calendar
|
||||||
|
|<-----------------------------| redirect to Calendar app
|
||||||
|
```
|
||||||
|
|
||||||
|
The remote app links the user to the confirm page. The user reviews
|
||||||
|
the event and submits the form. The event lands in the user's
|
||||||
|
"Personal" calendar and the browser opens the Calendar app.
|
||||||
|
|
||||||
|
## Routes
|
||||||
|
|
||||||
|
| Method | Path | Purpose |
|
||||||
|
|--------|------|---------|
|
||||||
|
| GET | `/apps/atocal/confirm` | Show confirmation page |
|
||||||
|
| POST | `/apps/atocal/create` | Create the event |
|
||||||
|
|
||||||
|
## Link parameters (GET `/confirm` and form POST `/create`)
|
||||||
|
|
||||||
|
| Parameter | Required | Rules |
|
||||||
|
|-----------|----------|-------|
|
||||||
|
| `summary` | yes | max 255 chars |
|
||||||
|
| `start` | yes | datetime-local, e.g. `2026-10-07T14:15` |
|
||||||
|
| `end` | yes | after `start`, same format |
|
||||||
|
| `description` | no | max 10000 chars |
|
||||||
|
| `location` | no | HTTPS URL |
|
||||||
|
| `url` | no | HTTPS URL; merged into description |
|
||||||
|
| `organizer` | no | email address |
|
||||||
|
|
||||||
|
Dates carry no timezone and are interpreted in the user's Nextcloud
|
||||||
|
timezone (server default as fallback).
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```
|
||||||
|
https://cloud.example.com/apps/atocal/confirm?summary=Kickoff&start=2026-10-12T10:00&end=2026-10-12T11:00&location=https%3A%2F%2Fmeet.example.com%2Fkickoff
|
||||||
|
```
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- Nextcloud 34 or 35
|
||||||
|
- Calendar app enabled
|
||||||
|
- A writable calendar named "Personal"
|
||||||
|
(event creation fails with a friendly error otherwise)
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
Copy this directory to the Nextcloud server as
|
||||||
|
`custom_apps/atocal`, then:
|
||||||
|
|
||||||
|
```
|
||||||
|
occ app:enable atocal
|
||||||
|
```
|
||||||
|
|
||||||
|
## Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
appinfo/info.xml app metadata
|
||||||
|
appinfo/routes.php routes
|
||||||
|
lib/AppInfo/Application.php container setup
|
||||||
|
lib/Controller/CalendarController.php confirm/create logic
|
||||||
|
templates/confirm.php confirmation page
|
||||||
|
templates/style.css page styles
|
||||||
|
```
|
||||||
+5
-5
@@ -1,15 +1,15 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<info xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd">
|
<info xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd">
|
||||||
<id>jitsi_calendar</id>
|
<id>atocal</id>
|
||||||
<name>Jitsi Calendar</name>
|
<name>Add To Calendar</name>
|
||||||
<summary>Create Jitsi meetings in the user's Nextcloud Calendar</summary>
|
<summary>Create events in the user's Nextcloud Calendar</summary>
|
||||||
<description>
|
<description>
|
||||||
Allows the Jitsi meeting launcher to create calendar events in the authenticated user's Nextcloud Calendar.
|
Allows remote applications to create calendar events in the authenticated user's Nextcloud Calendar.
|
||||||
</description>
|
</description>
|
||||||
<version>1.0.0</version>
|
<version>1.0.0</version>
|
||||||
<licence>AGPL</licence>
|
<licence>AGPL</licence>
|
||||||
<author>Stefan Tollkühn s.tollkuehn@bnvb-ingenieure.de</author>
|
<author>Stefan Tollkühn s.tollkuehn@bnvb-ingenieure.de</author>
|
||||||
<namespace>JitsiCalendar</namespace>
|
<namespace>AToCal</namespace>
|
||||||
<category>integration</category>
|
<category>integration</category>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<nextcloud min-version="34" max-version="35"/>
|
<nextcloud min-version="34" max-version="35"/>
|
||||||
|
|||||||
@@ -2,15 +2,15 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace OCA\JitsiCalendar\AppInfo;
|
namespace OCA\AToCal\AppInfo;
|
||||||
|
|
||||||
use OCA\JitsiCalendar\Controller\CalendarController;
|
use OCA\AToCal\Controller\CalendarController;
|
||||||
use OCP\AppFramework\App;
|
use OCP\AppFramework\App;
|
||||||
use OCP\AppFramework\IAppContainer;
|
use OCP\AppFramework\IAppContainer;
|
||||||
|
|
||||||
class Application extends App
|
class Application extends App
|
||||||
{
|
{
|
||||||
public const APP_ID = 'jitsi_calendar';
|
public const APP_ID = 'atocal';
|
||||||
|
|
||||||
public function __construct(array $urlParams = [])
|
public function __construct(array $urlParams = [])
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace OCA\JitsiCalendar\Controller;
|
namespace OCA\AToCal\Controller;
|
||||||
|
|
||||||
use OCA\JitsiCalendar\AppInfo\Application;
|
use OCA\AToCal\AppInfo\Application;
|
||||||
use OCP\AppFramework\Controller;
|
use OCP\AppFramework\Controller;
|
||||||
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
|
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
|
||||||
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
|
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
|
||||||
|
|||||||
+13
-13
@@ -12,34 +12,34 @@ $h = static function (string $value): string {
|
|||||||
?>
|
?>
|
||||||
|
|
||||||
<div id="app-content">
|
<div id="app-content">
|
||||||
<div class="jitsi-calendar-container">
|
<div class="atocal-container">
|
||||||
|
|
||||||
<div class="jitsi-calendar-card">
|
<div class="atocal-card">
|
||||||
|
|
||||||
<h1>
|
<h1>
|
||||||
Add Jitsi Meeting to Calendar
|
Add To Calendar
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p class="jitsi-calendar-intro">
|
<p class="atocal-intro">
|
||||||
The following meeting will be added to your
|
The following meeting will be added to your
|
||||||
Nextcloud Calendar.
|
Nextcloud Calendar.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<?php if ($_['error'] !== ''): ?>
|
<?php if ($_['error'] !== ''): ?>
|
||||||
<div class="jitsi-calendar-error">
|
<div class="atocal-error">
|
||||||
<?= $h($_['error']) ?>
|
<?= $h($_['error']) ?>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<div class="jitsi-calendar-details">
|
<div class="atocal-details">
|
||||||
|
|
||||||
<div class="jitsi-calendar-row">
|
<div class="atocal-row">
|
||||||
<strong>Title</strong>
|
<strong>Title</strong>
|
||||||
<span><?= $h($_['summary']) ?></span>
|
<span><?= $h($_['summary']) ?></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php if ($_['description'] !== ''): ?>
|
<?php if ($_['description'] !== ''): ?>
|
||||||
<div class="jitsi-calendar-row">
|
<div class="atocal-row">
|
||||||
<strong>Description</strong>
|
<strong>Description</strong>
|
||||||
<span class="description">
|
<span class="description">
|
||||||
<?= nl2br($h($_['description'])) ?>
|
<?= nl2br($h($_['description'])) ?>
|
||||||
@@ -47,25 +47,25 @@ $h = static function (string $value): string {
|
|||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<div class="jitsi-calendar-row">
|
<div class="atocal-row">
|
||||||
<strong>Start</strong>
|
<strong>Start</strong>
|
||||||
<span><?= $h(str_replace('T', ' ', $_['start'])) ?></span>
|
<span><?= $h(str_replace('T', ' ', $_['start'])) ?></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="jitsi-calendar-row">
|
<div class="atocal-row">
|
||||||
<strong>End</strong>
|
<strong>End</strong>
|
||||||
<span><?= $h(str_replace('T', ' ', $_['end'])) ?></span>
|
<span><?= $h(str_replace('T', ' ', $_['end'])) ?></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php if ($_['organizer'] !== ''): ?>
|
<?php if ($_['organizer'] !== ''): ?>
|
||||||
<div class="jitsi-calendar-row">
|
<div class="atocal-row">
|
||||||
<strong>Organizer</strong>
|
<strong>Organizer</strong>
|
||||||
<span><?= $h($_['organizer']) ?></span>
|
<span><?= $h($_['organizer']) ?></span>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php if ($_['location'] !== ''): ?>
|
<?php if ($_['location'] !== ''): ?>
|
||||||
<div class="jitsi-calendar-row">
|
<div class="atocal-row">
|
||||||
<strong>Location</strong>
|
<strong>Location</strong>
|
||||||
<span>
|
<span>
|
||||||
<a
|
<a
|
||||||
@@ -133,7 +133,7 @@ $h = static function (string $value): string {
|
|||||||
value="<?= $h($_['url']) ?>"
|
value="<?= $h($_['url']) ?>"
|
||||||
>
|
>
|
||||||
|
|
||||||
<div class="jitsi-calendar-actions">
|
<div class="atocal-actions">
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
|
|||||||
+15
-15
@@ -1,10 +1,10 @@
|
|||||||
.jitsi-calendar-container {
|
.atocal-container {
|
||||||
max-width: 760px;
|
max-width: 760px;
|
||||||
margin: 60px auto;
|
margin: 60px auto;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.jitsi-calendar-card {
|
.atocal-card {
|
||||||
background: var(--color-main-background);
|
background: var(--color-main-background);
|
||||||
border: 1px solid var(--color-border);
|
border: 1px solid var(--color-border);
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
@@ -12,17 +12,17 @@
|
|||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
.jitsi-calendar-card h1 {
|
.atocal-card h1 {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.jitsi-calendar-intro {
|
.atocal-intro {
|
||||||
color: var(--color-text-maxcontrast);
|
color: var(--color-text-maxcontrast);
|
||||||
margin-bottom: 28px;
|
margin-bottom: 28px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.jitsi-calendar-error {
|
.atocal-error {
|
||||||
background: var(--color-error);
|
background: var(--color-error);
|
||||||
color: var(--color-error-text);
|
color: var(--color-error-text);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
@@ -30,13 +30,13 @@
|
|||||||
margin-bottom: 28px;
|
margin-bottom: 28px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.jitsi-calendar-details {
|
.atocal-details {
|
||||||
border-top: 1px solid var(--color-border);
|
border-top: 1px solid var(--color-border);
|
||||||
border-bottom: 1px solid var(--color-border);
|
border-bottom: 1px solid var(--color-border);
|
||||||
margin-bottom: 28px;
|
margin-bottom: 28px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.jitsi-calendar-row {
|
.atocal-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 150px 1fr;
|
grid-template-columns: 150px 1fr;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
@@ -44,26 +44,26 @@
|
|||||||
border-bottom: 1px solid var(--color-border);
|
border-bottom: 1px solid var(--color-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.jitsi-calendar-row:last-child {
|
.atocal-row:last-child {
|
||||||
border-bottom: 0;
|
border-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.jitsi-calendar-row span {
|
.atocal-row span {
|
||||||
overflow-wrap: anywhere;
|
overflow-wrap: anywhere;
|
||||||
}
|
}
|
||||||
|
|
||||||
.jitsi-calendar-row .description {
|
.atocal-row .description {
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
.jitsi-calendar-actions {
|
.atocal-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.jitsi-calendar-actions button,
|
.atocal-actions button,
|
||||||
.jitsi-calendar-actions a {
|
.atocal-actions a {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 10px 18px;
|
padding: 10px 18px;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
@@ -71,13 +71,13 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.jitsi-calendar-actions button.primary {
|
.atocal-actions button.primary {
|
||||||
background: var(--color-primary-element);
|
background: var(--color-primary-element);
|
||||||
color: var(--color-primary-element-text);
|
color: var(--color-primary-element-text);
|
||||||
border: 0;
|
border: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.jitsi-calendar-actions a.secondary {
|
.atocal-actions a.secondary {
|
||||||
border: 1px solid var(--color-border);
|
border: 1px solid var(--color-border);
|
||||||
color: var(--color-main-text);
|
color: var(--color-main-text);
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user