Service za ether API, izboljsave nastavitev, validacija, manjsi popravek

obrazca za dodajanje
Jurij Podgoršek 2023-11-12 22:55:24 +01:00
parent 11b1569273
commit b8e394b5a0
18 changed files with 292 additions and 173 deletions

View File

@ -16,6 +16,7 @@
],
"require": {
"composer/installers": "^2.0",
"drupal/config_ignore": "^3.1",
"drupal/content_as_config": "^1.0",
"drupal/core-composer-scaffold": "^10.0",
"drupal/core-project-message": "^10.0",

64
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "71ddb6ffcc6a7fe35213ac457365e193",
"content-hash": "6830860dbd11d7a08dcff163b7d54d20",
"packages": [
{
"name": "asm89/stack-cors",
@ -1094,6 +1094,66 @@
],
"time": "2022-02-28T11:07:21+00:00"
},
{
"name": "drupal/config_ignore",
"version": "3.1.0",
"source": {
"type": "git",
"url": "https://git.drupalcode.org/project/config_ignore.git",
"reference": "8.x-3.1"
},
"dist": {
"type": "zip",
"url": "https://ftp.drupal.org/files/projects/config_ignore-8.x-3.1.zip",
"reference": "8.x-3.1",
"shasum": "5e60f312e02735060d5e5b1724330bb243face4c"
},
"require": {
"drupal/core": "^8.8 || ^9 || ^10"
},
"require-dev": {
"drupal/config_filter": "^1.8||^2.2",
"drush/drush": "^10 || ^11 || ^12"
},
"type": "drupal-module",
"extra": {
"drupal": {
"version": "8.x-3.1",
"datestamp": "1699302168",
"security-coverage": {
"status": "covered",
"message": "Covered by Drupal's security advisory policy"
}
}
},
"notification-url": "https://packages.drupal.org/8/downloads",
"license": [
"GPL-2.0+"
],
"authors": [
{
"name": "Tommy Lynge Jørgensen",
"homepage": "https://www.drupal.org/u/tlyngej",
"email": "tlyngej@gmail.com",
"role": "Maintainer"
},
{
"name": "Fabian Bircher",
"homepage": "https://www.drupal.org/u/bircher",
"role": "Maintainer"
},
{
"name": "tlyngej",
"homepage": "https://www.drupal.org/user/413139"
}
],
"description": "Ignore certain configuration during import and export.",
"homepage": "http://drupal.org/project/config_ignore",
"support": {
"source": "https://git.drupalcode.org/project/config_ignore",
"issues": "http://drupal.org/project/config_ignore"
}
},
{
"name": "drupal/content_as_config",
"version": "1.0.10",
@ -6254,5 +6314,5 @@
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
"plugin-api-version": "2.3.0"
"plugin-api-version": "2.6.0"
}

View File

@ -0,0 +1,5 @@
_core:
default_config_hash: IgOVnECx6lbVt6JVFnadoEEugneDf3UblPZnOzov43Q
mode: simple
ignored_config_entities:
- etherpad_api.settings

View File

@ -6,7 +6,6 @@ dependencies:
- field.field.node.concept.body
- field.field.node.concept.field_media
- field.field.node.concept.field_related_concept
- field.field.node.concept.field_tags
- node.type.concept
module:
- media_library
@ -50,16 +49,6 @@ content:
size: 60
placeholder: ''
third_party_settings: { }
field_tags:
type: entity_reference_autocomplete
weight: 124
region: content
settings:
match_operator: CONTAINS
match_limit: 10
size: 60
placeholder: ''
third_party_settings: { }
langcode:
type: language_select
weight: 2

View File

@ -6,7 +6,6 @@ dependencies:
- field.field.node.concept.body
- field.field.node.concept.field_media
- field.field.node.concept.field_related_concept
- field.field.node.concept.field_tags
- node.type.concept
module:
- text
@ -40,14 +39,6 @@ content:
third_party_settings: { }
weight: 103
region: content
field_tags:
type: entity_reference_label
label: above
settings:
link: true
third_party_settings: { }
weight: 104
region: content
links:
settings: { }
third_party_settings: { }

View File

@ -7,7 +7,6 @@ dependencies:
- field.field.node.concept.body
- field.field.node.concept.field_media
- field.field.node.concept.field_related_concept
- field.field.node.concept.field_tags
- node.type.concept
module:
- text
@ -33,5 +32,4 @@ content:
hidden:
field_media: true
field_related_concept: true
field_tags: true
langcode: true

View File

@ -9,6 +9,7 @@ module:
ckeditor5: 0
comment: 0
config: 0
config_ignore: 0
config_translation: 0
contextual: 0
datetime: 0

View File

@ -1,29 +0,0 @@
uuid: f5fade64-73dc-420b-8c1f-ab4fe3355a52
langcode: en
status: true
dependencies:
config:
- field.storage.node.field_tags
- node.type.concept
- taxonomy.vocabulary.tags
id: node.concept.field_tags
field_name: field_tags
entity_type: node
bundle: concept
label: Tags
description: ''
required: false
translatable: true
default_value: { }
default_value_callback: ''
settings:
handler: 'default:taxonomy_term'
handler_settings:
target_bundles:
tags: tags
sort:
field: name
direction: asc
auto_create: true
auto_create_bundle: ''
field_type: entity_reference

View File

@ -1,3 +1,3 @@
_core:
default_config_hash: 2OMXCScXUOLSYID9-phjO4q36nnnaMWNUlDxEqZzG1U
use_admin_theme: true
use_admin_theme: false

View File

@ -1,8 +1,21 @@
<script setup="setup">
const prikazi = ref(false)
const revisionId = ref(null)
const dodajPojem = () => {
revisionId.value = crypto.randomUUID()
prikazi.value = true
console.log('NOV POJEM!', revisionId.value)
}
</script>
<template>
<details>
<summary class="gumb">
<div>
<div class="gumb" @click="dodajPojem()">
Dodaj pojem
</summary>
<PojemForm />
</details>
</div>
<PojemForm v-if="prikazi" :revisionId="revisionId" />
</div>
</template>

View File

@ -5,20 +5,21 @@ import { ref } from 'vue'
const { etherpadUrl, etherpadPrefix } = useRuntimeConfig().public
const props = defineProps({
revisionId: String
revisionId: String,
onLoad: Function
})
const embed = ref(null)
onMounted(() => {
// Ce ni revisionId propertyja, se random generira.
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" />
<iframe ref="embed" class="etherpad-textarea" @load="props.onLoad" />
</template>

View File

@ -1,8 +1,12 @@
<script setup="setup">
const { etherpadUrl, etherpadPrefix } = useRuntimeConfig().public
const store = usePojmiStore()
const props = defineProps({
naslov: String
naslov: String,
revisionId: String
})
//const pojem = computed(() => store.pojmi[props.naslov])
@ -21,6 +25,14 @@ const oddajPredlog = data => {
store.ustvariPojem(data)
}
const etherNalozen = ev => {
// @TODO DRY
const padUrl = `${etherpadUrl}p/${etherpadPrefix}${props.revisionId}?showChat=false&showLineNumbers=false&toc=false`
// Izprazni pad!
console.log('etherpad nalozen!')
window.location.hash = props.revisionId
}
</script>
<template>
@ -31,7 +43,7 @@ const oddajPredlog = data => {
<input name="naslov" type="text" v-model="naslov">
<label for="tekst">Besedilo</label>
<EtherpadTextarea />
<EtherpadTextarea :onLoad="etherNalozen" :revisionId="props.revisionId" />
<label for="email">E-poštni naslov</label>
<input name="email" type="email" v-model="email">

View File

@ -1,95 +1,36 @@
<?php
use GuzzleHttp\Exception\ClientException;
/**
* @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 = [];
// Preveri ali api deluje
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,
];
$client = Drupal::service('etherpad_api.client');
try {
$client->checkToken();
} catch (ClientException $exception) {
$value = $exception->getCode();
$msg = $exception->getMessage();
$requirements['etherpad_api_status'] = [
'title' => t('Etherpad API status'),
'value' => t('Etherpad API not accessible (@code: @msg)', [
'@code' => $exception->getCode(),
'@msg' => $exception->getMessage()
]),
'severity' => REQUIREMENT_ERROR,
];
}
}
return $requirements;

View File

@ -3,3 +3,10 @@ services:
class: Drupal\etherpad_api\PathProcessor\EtherpadAPIPathProcessor
tags:
- { name: path_processor_inbound, priority: 1000 }
etherpad_api.settings:
class: Drupal\Core\Config\ImmutableConfig
factory: config.factory:get
arguments: ['etherpad_api.settings']
etherpad_api.client:
class: Drupal\etherpad_api\Client
arguments: ['@http_client', '@etherpad_api.settings']

View File

@ -0,0 +1,66 @@
<?php
namespace Drupal\etherpad_api;
use Drupal\Core\Config\ImmutableConfig;
use GuzzleHttp\ClientInterface;
/**
* Service description.
*/
class Client {
/**
* The HTTP client.
*
* @var \GuzzleHttp\ClientInterface
*/
protected $httpClient;
/**
* The config.
*
* @var \Drupal\Core\Config\ImmutableConfig
*/
protected $config;
protected $apiKey = null;
protected $baseUrl = null;
// (Minimal) Etherpad version
const API_VERSION = '1.2';
/**
* Constructs a Client object.
*
* @param \GuzzleHttp\ClientInterface $httpClient
* The HTTP client.
* @param \Drupal\Core\Config\ImmutableConfig $config
* The config.
*/
public function __construct(ClientInterface $httpClient, ImmutableConfig $config) {
$this->httpClient = $httpClient;
$this->config = $config;
$this->baseUrl = rtrim($config->get('url'), '/');
$this->apiKey = $config->get('key');
}
public function checkToken() {
return $this->request('get', '/checkToken');
}
/**
* Method description.
*/
public function request($method, $url) {
$uri = "{$this->baseUrl}/" . self::API_VERSION . $url;
if (str_contains($uri, '?')) {
$uri .= "&apikey={$this->apiKey}";
} else {
$uri .= "?apikey={$this->apiKey}";
}
return $this->httpClient->request($method, $uri);
}
}

View File

@ -0,0 +1,42 @@
<?php
namespace Drupal\etherpad_api\Controller;
use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use GuzzleHttp\Exception\ClientException;
use Drupal\etherpad_api\Client;
/**
* Returns responses for Etherpad API routes.
*/
class EtherpadApiController extends ControllerBase {
protected $client;
public function __construct(Client $client) {
$this->client = $client;
}
public static function create(ContainerInterface $container) {
return new static($container->get('etherpad_api.client'));
}
/**
* Builds the response.
*/
public function build($components, Request $request) {
$uri = str_replace(':', '/', $components);
if ($params = $request->getQueryString()) {
$uri .= "?$params";
}
try {
return $this->client->request($request->getMethod(), $uri);
} catch (ClientException $exception) {
return new Response($exception->getMessage(), $exception->getCode());
}
}
}

View File

@ -3,47 +3,38 @@
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;
use GuzzleHttp\Exception\ClientException;
use Drupal\etherpad_api\Client;
/**
* Returns responses for Etherpad API routes.
*/
class EtherpadApiController extends ControllerBase {
protected $httpClient;
protected $config;
protected $client;
public function __construct(ClientInterface $http_client, ConfigFactoryInterface $config_factory) {
$this->httpClient = $http_client;
$this->configFactory = $config_factory;
public function __construct(Client $client) {
$this->client = $client;
}
public static function create(ContainerInterface $container) {
return new static($container->get('http_client'), $container->get('config.factory'));
return new static($container->get('etherpad_api.client'));
}
/**
* 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 = str_replace(':', '/', $components);
if ($params = $request->getQueryString()) {
$uri .= "?$params";
}
$uri = $baseUrl . str_replace(':', '/', $components) . "?$params";
$method = $request->getMethod();
try {
return $this->httpClient->request($method, $uri);
return $this->client->request($request->getMethod(), $uri);
} catch (ClientException $exception) {
return new Response($exception->getMessage(), $exception->getCode());
}

View File

@ -2,13 +2,29 @@
namespace Drupal\etherpad_api\Form;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception\ClientException;
use Drupal\etherpad_api\Client;
/**
* Configure Etherpad API settings for this site.
*/
class SettingsForm extends ConfigFormBase {
protected $httpClient;
protected $settings;
public function __construct(ClientInterface $httpClient) {
$this->httpClient = $httpClient;
$this->settings = $this->config('etherpad_api.settings');
}
public static function create(ContainerInterface $container) {
return new static($container->get('http_client'));
}
/**
* {@inheritdoc}
@ -31,12 +47,13 @@ class SettingsForm extends ConfigFormBase {
$form['url'] = [
'#type' => 'textfield',
'#title' => $this->t('Etherpad API URL'),
'#default_value' => $this->config('etherpad_api.settings')->get('url'),
'#required' => true,
'#default_value' => $this->settings->get('url'),
];
$form['key'] = [
'#type' => 'textfield',
'#type' => 'password',
'#title' => $this->t('Etherpad API key (found in instance root folder)'),
'#default_value' => $this->config('etherpad_api.settings')->get('key'),
'#default_value' => $this->settings->get('key'),
];
return parent::buildForm($form, $form_state);
}
@ -45,11 +62,22 @@ class SettingsForm extends ConfigFormBase {
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
if (!$form_state->getValue('url')) {
$form_state->setErrorByName('url', $this->t('The value is required.'));
// Check API accessibility
$apiKey = $form_state->getValue('key');
if (!$apiKey) {
$apiKey = $this->settings->get('key');
}
if (!$form_state->getValue('key')) {
$form_state->setErrorByName('key', $this->t('The value is required.'));
$baseUrl = $form_state->getValue('url');
$url = rtrim($baseUrl, '/') . "/1.2/checkToken?apikey=$apiKey";
try {
$this->httpClient->request('get', $url);
} catch (ClientException $e) {
$form_state->setErrorByName('url', $this->t('Etherpad API not accessible (@code: @msg). The URL or the API key is wrong', [
'@value' => $e->getCode(),
'@msg' => $e->getMessage()
]));
}
parent::validateForm($form, $form_state);
}
@ -58,10 +86,12 @@ class SettingsForm extends ConfigFormBase {
* {@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();
$this->settings->set('url', $form_state->getValue('url'));
if ($form_state->getValue('key')) {
$this->settings->set('key', $form_state->getValue('key'));
}
$this->settings->save();
parent::submitForm($form, $form_state);
}