WIP etherpad integracija

pull/26/head
Jurij Podgoršek 2023-09-30 20:14:02 +02:00
parent affae91fd9
commit 1ef66a3d75
17 changed files with 336 additions and 11 deletions

View File

@ -18,6 +18,7 @@ module:
dblog: 0 dblog: 0
dynamic_page_cache: 0 dynamic_page_cache: 0
editor: 0 editor: 0
etherpad_api: 0
field: 0 field: 0
field_ui: 0 field_ui: 0
file: 0 file: 0

View File

@ -1,3 +1,5 @@
BASE_URL="https://yufu-manifest.ddev.site" BASE_URL="https://yufu-manifest.ddev.site"
JSONAPI_PATH="/jsonapi" JSONAPI_PATH="/jsonapi"
FILE_PATH="/sites/default/files" FILE_PATH="/sites/default/files"
ETHERPAD_URL="https://pisi.kompot.si/"
ETHERPAD_PREFIX="yufu-"

View File

@ -0,0 +1,24 @@
<script setup="setup">
import { ref } from 'vue'
const { etherpadUrl, etherpadPrefix } = useRuntimeConfig().public
const props = defineProps({
revisionId: String
})
const embed = ref(null)
onMounted(() => {
const uuid = props.revisionId ? props.revisionId : crypto.randomUUID()
const padUrl = `${etherpadUrl}p/${etherpadPrefix}${uuid}?showChat=false&showLineNumbers=false&toc=false`
embed.value.src = padUrl
window.location.hash = uuid
})
</script>
<template>
<iframe ref="embed" class="etherpad-textarea" />
</template>

View File

@ -31,7 +31,7 @@ const oddajPredlog = data => {
<input name="naslov" type="text" v-model="naslov"> <input name="naslov" type="text" v-model="naslov">
<label for="tekst">Besedilo</label> <label for="tekst">Besedilo</label>
<textarea name="tekst" v-model="tekst" /> <EtherpadTextarea />
<label for="email">E-poštni naslov</label> <label for="email">E-poštni naslov</label>
<input name="email" type="email" v-model="email"> <input name="email" type="email" v-model="email">

View File

@ -1,8 +1,7 @@
import jsonapi from 'jsonapi-serializer' import jsonapi from 'jsonapi-serializer'
export const useApi = () => { export const useApi = () => {
const baseUrl = useRuntimeConfig().public.baseUrl const { baseUrl, jsonapiPath } = useRuntimeConfig().public
const jsonapiPath = useRuntimeConfig().public.jsonapiPath
const apiBaseUrl = `${baseUrl}${jsonapiPath}` const apiBaseUrl = `${baseUrl}${jsonapiPath}`
const headers = { const headers = {

View File

@ -2,7 +2,7 @@ export default defineNuxtConfig({
// Global page headers: https://go.nuxtjs.dev/config-head // Global page headers: https://go.nuxtjs.dev/config-head
app: { app: {
head: { head: {
title: '...Hm!', title: 'Jugofuturistični manifest',
meta: [ meta: [
{ charset: 'utf-8' }, { charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' }, { name: 'viewport', content: 'width=device-width, initial-scale=1' },
@ -35,6 +35,8 @@ export default defineNuxtConfig({
public: { public: {
baseUrl: process.env.BASE_URL, baseUrl: process.env.BASE_URL,
jsonapiPath: process.env.JSONAPI_PATH, jsonapiPath: process.env.JSONAPI_PATH,
etherpadUrl: process.env.ETHERPAD_URL,
etherpadPrefix: process.env.ETHERPAD_PREFIX
} }
} }
}) })

24
nuxt/package-lock.json generated
View File

@ -6,10 +6,8 @@
"": { "": {
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
"axios": "^1.4.0",
"druxt": "^0.21.0", "druxt": "^0.21.0",
"jsonapi-serializer": "^3.6.7", "jsonapi-serializer": "^3.6.7",
"lodash": "^4.17.21",
"nuxt-proxy": "^0.4.1", "nuxt-proxy": "^0.4.1",
"pinia": "^2.0.32" "pinia": "^2.0.32"
}, },
@ -2900,7 +2898,8 @@
"node_modules/asynckit": { "node_modules/asynckit": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"peer": true
}, },
"node_modules/autoprefixer": { "node_modules/autoprefixer": {
"version": "10.4.13", "version": "10.4.13",
@ -2951,6 +2950,7 @@
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz",
"integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==",
"peer": true,
"dependencies": { "dependencies": {
"follow-redirects": "^1.15.0", "follow-redirects": "^1.15.0",
"form-data": "^4.0.0", "form-data": "^4.0.0",
@ -3243,9 +3243,9 @@
} }
}, },
"node_modules/caniuse-lite": { "node_modules/caniuse-lite": {
"version": "1.0.30001457", "version": "1.0.30001541",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001457.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001541.tgz",
"integrity": "sha512-SDIV6bgE1aVbK6XyxdURbUE89zY7+k1BBBaOwYwkNCglXlel/E7mELiHC64HQ+W0xSKlqWhV9Wh7iHxUjMs4fA==", "integrity": "sha512-bLOsqxDgTqUBkzxbNlSBt8annkDpQB9NdzdTbO2ooJ+eC/IQcvDspDc058g84ejCelF7vHUx57KIOjEecOHXaw==",
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@ -3254,6 +3254,10 @@
{ {
"type": "tidelift", "type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/caniuse-lite" "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
} }
] ]
}, },
@ -3576,6 +3580,7 @@
"version": "1.0.8", "version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"peer": true,
"dependencies": { "dependencies": {
"delayed-stream": "~1.0.0" "delayed-stream": "~1.0.0"
}, },
@ -4003,6 +4008,7 @@
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"peer": true,
"engines": { "engines": {
"node": ">=0.4.0" "node": ">=0.4.0"
} }
@ -5872,6 +5878,7 @@
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"peer": true,
"dependencies": { "dependencies": {
"asynckit": "^0.4.0", "asynckit": "^0.4.0",
"combined-stream": "^1.0.8", "combined-stream": "^1.0.8",
@ -7695,6 +7702,7 @@
"version": "1.52.0", "version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"peer": true,
"engines": { "engines": {
"node": ">= 0.6" "node": ">= 0.6"
} }
@ -7703,6 +7711,7 @@
"version": "2.1.35", "version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"peer": true,
"dependencies": { "dependencies": {
"mime-db": "1.52.0" "mime-db": "1.52.0"
}, },
@ -9302,7 +9311,8 @@
"node_modules/proxy-from-env": { "node_modules/proxy-from-env": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"peer": true
}, },
"node_modules/prr": { "node_modules/prr": {
"version": "1.0.1", "version": "1.0.1",

View File

@ -43,3 +43,7 @@ details > summary {
border: 2px solid black; border: 2px solid black;
cursor: pointer; cursor: pointer;
} }
.etherpad-textarea {
height: 25rem;
}

View File

@ -0,0 +1,11 @@
# Schema for the configuration files of the Etherpad API module.
etherpad_api.settings:
type: config_object
label: 'Etherpad API settings'
mapping:
url:
type: string
label: 'API URL of etherpad instance'
key:
type: string
label: 'API key of etherpad instance'

View File

@ -0,0 +1,6 @@
name: Etherpad API
type: module
description: API endpoint for working with an etherpad instance
package: Yufu
configure: etherpad_api.settings
core_version_requirement: ^9 || ^10

View File

@ -0,0 +1,96 @@
<?php
/**
* @file
* Install, update and uninstall functions for the Etherpad API module.
*/
/**
* Implements hook_install().
*/
function etherpad_api_install() {
\Drupal::messenger()->addStatus(__FUNCTION__);
}
/**
* Implements hook_uninstall().
*/
function etherpad_api_uninstall() {
\Drupal::messenger()->addStatus(__FUNCTION__);
}
/**
* Implements hook_schema().
*/
function etherpad_api_schema() {
$schema['etherpad_api_example'] = [
'description' => 'Table description.',
'fields' => [
'id' => [
'type' => 'serial',
'not null' => TRUE,
'description' => 'Primary Key: Unique record ID.',
],
'uid' => [
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'description' => 'The {users}.uid of the user who created the record.',
],
'status' => [
'description' => 'Boolean indicating whether this record is active.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'size' => 'tiny',
],
'type' => [
'type' => 'varchar_ascii',
'length' => 64,
'not null' => TRUE,
'default' => '',
'description' => 'Type of the record.',
],
'created' => [
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'description' => 'Timestamp when the record was created.',
],
'data' => [
'type' => 'blob',
'not null' => TRUE,
'size' => 'big',
'description' => 'The arbitrary data for the item.',
],
],
'primary key' => ['id'],
'indexes' => [
'type' => ['type'],
'uid' => ['uid'],
'status' => ['status'],
],
];
return $schema;
}
/**
* Implements hook_requirements().
*/
function etherpad_api_requirements($phase) {
$requirements = [];
if ($phase == 'runtime') {
$value = mt_rand(0, 100);
$requirements['etherpad_api_status'] = [
'title' => t('Etherpad API status'),
'value' => t('Etherpad API value: @value', ['@value' => $value]),
'severity' => $value > 50 ? REQUIREMENT_INFO : REQUIREMENT_WARNING,
];
}
return $requirements;
}

View File

@ -0,0 +1,6 @@
etherpad_api.settings_form:
title: 'Etherpad API'
description: 'Configure Etherpad API.'
parent: system.admin_config_system
route_name: etherpad_api.settings_form
weight: 10

View File

@ -0,0 +1,15 @@
etherpad_api.example:
path: '/etherpad-api/{components}'
defaults:
_title: 'Etherpad API'
_controller: '\Drupal\etherpad_api\Controller\EtherpadApiController::build'
requirements:
_permission: 'access content'
etherpad_api.settings_form:
path: '/admin/config/system/etherpad-api'
defaults:
_title: 'Etherpad API settings'
_form: 'Drupal\etherpad_api\Form\SettingsForm'
requirements:
_permission: 'administer etherpad_api configuration'

View File

@ -0,0 +1,5 @@
services:
path_processor.etherpad_api:
class: Drupal\etherpad_api\PathProcessor\EtherpadAPIPathProcessor
tags:
- { name: path_processor_inbound, priority: 1000 }

View File

@ -0,0 +1,51 @@
<?php
namespace Drupal\etherpad_api\Controller;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Config\ConfigFactoryInterface;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception\ClientException;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/**
* Returns responses for Etherpad API routes.
*/
class EtherpadApiController extends ControllerBase {
protected $httpClient;
protected $config;
public function __construct(ClientInterface $http_client, ConfigFactoryInterface $config_factory) {
$this->httpClient = $http_client;
$this->configFactory = $config_factory;
}
public static function create(ContainerInterface $container) {
return new static($container->get('http_client'), $container->get('config.factory'));
}
/**
* Builds the response.
*/
public function build($components, Request $request) {
$baseUrl = $this->config('etherpad_api.settings')->get('url');
$apiKey = $this->config('etherpad_api.settings')->get('key');
$params = $request->getQueryString();
if ($params) {
$params .= "&apikey=$apiKey";
} else {
$params = "apikey=$apiKey";
}
$uri = $baseUrl . str_replace(':', '/', $components) . "?$params";
$method = $request->getMethod();
try {
return $this->httpClient->request($method, $uri);
} catch (ClientException $exception) {
return new Response($exception->getMessage(), $exception->getCode());
}
}
}

View File

@ -0,0 +1,68 @@
<?php
namespace Drupal\etherpad_api\Form;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Configure Etherpad API settings for this site.
*/
class SettingsForm extends ConfigFormBase {
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'etherpad_api_settings';
}
/**
* {@inheritdoc}
*/
protected function getEditableConfigNames() {
return ['etherpad_api.settings'];
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form['url'] = [
'#type' => 'textfield',
'#title' => $this->t('Etherpad API URL'),
'#default_value' => $this->config('etherpad_api.settings')->get('url'),
];
$form['key'] = [
'#type' => 'textfield',
'#title' => $this->t('Etherpad API key (found in instance root folder)'),
'#default_value' => $this->config('etherpad_api.settings')->get('key'),
];
return parent::buildForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
if (!$form_state->getValue('url')) {
$form_state->setErrorByName('url', $this->t('The value is required.'));
}
if (!$form_state->getValue('key')) {
$form_state->setErrorByName('key', $this->t('The value is required.'));
}
parent::validateForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$this->config('etherpad_api.settings')
->set('url', $form_state->getValue('url'))
->set('key', $form_state->getValue('key'))
->save();
parent::submitForm($form, $form_state);
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace Drupal\etherpad_api\PathProcessor;
use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
use Drupal\Core\Render\BubbleableMetadata;
use Symfony\Component\HttpFoundation\Request;
/**
* Path processor to replace 'node' with 'content' in URLs.
*/
class EtherpadAPIPathProcessor implements InboundPathProcessorInterface {
const PREFIX = '/etherpad-api/';
/**
* {@inheritdoc}
*/
public function processInbound($path, Request $request) {
if (strpos($path, self::PREFIX) === 0) {
$subpath = substr($path, strlen(self::PREFIX));
return '/etherpad-api/' . str_replace('/', ':', $subpath);
}
return $path;
}
}