diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a2a720c..424b7c79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## v2.3.0 - tbd + +### Deprecated + +- Question property `mandatory` is deprecated and replaced by `isRequired`. The old property will be removed in API version 2. + [\#882](https://github.com/nextcloud/forms/pull/882) ([chartman123](https://github.com/Chartman123)) + ## v2.2.4 - 2021-03-30 [Full Changelog](https://github.com/nextcloud/forms/compare/v2.2.3...v2.2.4) diff --git a/appinfo/routes.php b/appinfo/routes.php index dd8bbfb0..a322db90 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -26,40 +26,192 @@ return [ 'routes' => [ // Internal views - ['name' => 'page#views', 'url' => '/{hash}/{view}', 'verb' => 'GET'], + [ + 'name' => 'page#views', + 'url' => '/{hash}/{view}', + 'verb' => 'GET' + ], // Share-Link & public submit - ['name' => 'page#goto_form', 'url' => '/{hash}', 'verb' => 'GET'], + [ + 'name' => 'page#goto_form', + 'url' => '/{hash}', + 'verb' => 'GET' + ], // App Root - ['name' => 'page#index', 'url' => '/', 'verb' => 'GET'], + [ + 'name' => 'page#index', + 'url' => '/', + 'verb' => 'GET' + ], ], 'ocs' => [ + // Forms - ['name' => 'api#getForms', 'url' => '/api/v1/forms', 'verb' => 'GET'], - ['name' => 'api#newForm', 'url' => '/api/v1/form', 'verb' => 'POST'], - ['name' => 'api#getForm', 'url' => '/api/v1/form/{id}', 'verb' => 'GET'], - ['name' => 'api#cloneForm', 'url' => '/api/v1/form/clone/{id}', 'verb' => 'POST'], - ['name' => 'api#updateForm', 'url' => '/api/v1/form/update', 'verb' => 'POST'], - ['name' => 'api#deleteForm', 'url' => '/api/v1/form/{id}', 'verb' => 'DELETE'], - ['name' => 'api#getSharedForms', 'url' => '/api/v1/shared_forms', 'verb' => 'GET'], + [ + 'name' => 'api#getForms', + 'url' => '/api/{apiVersion}/forms', + 'verb' => 'GET', + 'requirements' => [ + 'apiVersion' => 'v1(\.1)?' + ] + ], + [ + 'name' => 'api#newForm', + 'url' => '/api/{apiVersion}/form', + 'verb' => 'POST', + 'requirements' => [ + 'apiVersion' => 'v1(\.1)?' + ] + ], + [ + 'name' => 'api#getForm', + 'url' => '/api/{apiVersion}/form/{id}', + 'verb' => 'GET', + 'requirements' => [ + 'apiVersion' => 'v1(\.1)?' + ] + ], + [ + 'name' => 'api#cloneForm', + 'url' => '/api/{apiVersion}/form/clone/{id}', + 'verb' => 'POST', + 'requirements' => [ + 'apiVersion' => 'v1(\.1)?' + ] + ], + [ + 'name' => 'api#updateForm', + 'url' => '/api/{apiVersion}/form/update', + 'verb' => 'POST', + 'requirements' => [ + 'apiVersion' => 'v1(\.1)?' + ] + ], + [ + 'name' => 'api#deleteForm', + 'url' => '/api/{apiVersion}/form/{id}', + 'verb' => 'DELETE', + 'requirements' => [ + 'apiVersion' => 'v1(\.1)?' + ] + ], + [ + 'name' => 'api#getSharedForms', + 'url' => '/api/{apiVersion}/shared_forms', + 'verb' => 'GET', + 'requirements' => [ + 'apiVersion' => 'v1(\.1)?' + ] + ], // Questions - ['name' => 'api#newQuestion', 'url' => '/api/v1/question', 'verb' => 'POST'], - ['name' => 'api#updateQuestion', 'url' => '/api/v1/question/update', 'verb' => 'POST'], - ['name' => 'api#reorderQuestions', 'url' => '/api/v1/question/reorder', 'verb' => 'POST'], - ['name' => 'api#deleteQuestion', 'url' => '/api/v1/question/{id}', 'verb' => 'DELETE'], + [ + 'name' => 'api#newQuestion', + 'url' => '/api/{apiVersion}/question', + 'verb' => 'POST', + 'requirements' => [ + 'apiVersion' => 'v1(\.1)?' + ] + ], + [ + 'name' => 'api#updateQuestion', + 'url' => '/api/{apiVersion}/question/update', + 'verb' => 'POST', + 'requirements' => [ + 'apiVersion' => 'v1(\.1)?' + ] + ], + [ + 'name' => 'api#reorderQuestions', + 'url' => '/api/{apiVersion}/question/reorder', + 'verb' => 'POST', + 'requirements' => [ + 'apiVersion' => 'v1(\.1)?' + ] + ], + [ + 'name' => 'api#deleteQuestion', + 'url' => '/api/{apiVersion}/question/{id}', + 'verb' => 'DELETE', + 'requirements' => [ + 'apiVersion' => 'v1(\.1)?' + ] + ], - // Answers - ['name' => 'api#newOption', 'url' => '/api/v1/option', 'verb' => 'POST'], - ['name' => 'api#updateOption', 'url' => '/api/v1/option/update', 'verb' => 'POST'], - ['name' => 'api#deleteOption', 'url' => '/api/v1/option/{id}', 'verb' => 'DELETE'], + // Options + [ + 'name' => 'api#newOption', + 'url' => '/api/{apiVersion}/option', + 'verb' => 'POST', + 'requirements' => [ + 'apiVersion' => 'v1(\.1)?' + ] + ], + [ + 'name' => 'api#updateOption', + 'url' => '/api/{apiVersion}/option/update', + 'verb' => 'POST', + 'requirements' => [ + 'apiVersion' => 'v1(\.1)?' + ] + ], + [ + 'name' => 'api#deleteOption', + 'url' => '/api/{apiVersion}/option/{id}', + 'verb' => 'DELETE', + 'requirements' => [ + 'apiVersion' => 'v1(\.1)?' + ] + ], // Submissions - ['name' => 'api#getSubmissions', 'url' => '/api/v1/submissions/{hash}', 'verb' => 'GET'], - ['name' => 'api#exportSubmissions', 'url' => '/api/v1/submissions/export/{hash}', 'verb' => 'GET'], - ['name' => 'api#exportSubmissionsToCloud', 'url' => '/api/v1/submissions/export', 'verb' => 'POST'], - ['name' => 'api#deleteAllSubmissions', 'url' => '/api/v1/submissions/{formId}', 'verb' => 'DELETE'], - - ['name' => 'api#insertSubmission', 'url' => '/api/v1/submission/insert', 'verb' => 'POST'], - ['name' => 'api#deleteSubmission', 'url' => '/api/v1/submission/{id}', 'verb' => 'DELETE'], + [ + 'name' => 'api#getSubmissions', + 'url' => '/api/{apiVersion}/submissions/{hash}', + 'verb' => 'GET', + 'requirements' => [ + 'apiVersion' => 'v1(\.1)?' + ] + ], + [ + 'name' => 'api#exportSubmissions', + 'url' => '/api/{apiVersion}/submissions/export/{hash}', + 'verb' => 'GET', + 'requirements' => [ + 'apiVersion' => 'v1(\.1)?' + ] + ], + [ + 'name' => 'api#exportSubmissionsToCloud', + 'url' => '/api/{apiVersion}/submissions/export', + 'verb' => 'POST', + 'requirements' => [ + 'apiVersion' => 'v1(\.1)?' + ] + ], + [ + 'name' => 'api#deleteAllSubmissions', + 'url' => '/api/{apiVersion}/submissions/{formId}', + 'verb' => 'DELETE', + 'requirements' => [ + 'apiVersion' => 'v1(\.1)?' + ] + ], + [ + 'name' => 'api#insertSubmission', + 'url' => '/api/{apiVersion}/submission/insert', + 'verb' => 'POST', + 'requirements' => [ + 'apiVersion' => 'v1(\.1)?' + ] + ], + [ + 'name' => 'api#deleteSubmission', + 'url' => '/api/{apiVersion}/submission/{id}', + 'verb' => 'DELETE', + 'requirements' => [ + 'apiVersion' => 'v1(\.1)?' + ] + ], ] ]; diff --git a/docs/API.md b/docs/API.md index 41f4ea33..9d6799e1 100644 --- a/docs/API.md +++ b/docs/API.md @@ -20,10 +20,13 @@ This file contains the API-Documentation. For more information on the returned D } ``` +## Deprecation warning ⚠️ +Starting with API version 1.1, the `mandatory` property of questions is deprecated and replaced by `isRequired`. It will be removed in API version 2. + ## Form Endpoints ### List owned Forms Returns condensed objects of all Forms beeing owned by the authenticated user. -- Endpoint: `/api/v1/forms` +- Endpoint: `/api/v1.1/forms` - Method: `GET` - Parameters: None - Response: Array of condensed Form Objects, sorted as newest first. @@ -48,7 +51,7 @@ Returns condensed objects of all Forms beeing owned by the authenticated user. ### List shared Forms Returns condensed objects of all Forms, that are shared to the authenticated user via instance ([access-type](DataStructure.md#share-types) `registered` or `selected`) and have not expired yet. -- Endpoint: `/api/v1/shared_forms` +- Endpoint: `/api/v1.1/shared_forms` - Method: `GET` - Parameters: None - Response: Array of condensed Form Objects, sorted as newest first, similar to [List owned Forms](#list-owned-forms). @@ -57,7 +60,7 @@ See above, 'List owned forms' ``` ### Create a new Form -- Endpoint: `/api/v1/form` +- Endpoint: `/api/v1.1/form` - Method: `POST` - Parameters: None - Response: The new form object. @@ -82,7 +85,7 @@ See above, 'List owned forms' ### Request full data of a form Returns the full-depth object of the requested form (without submissions). -- Endpoint: `/api/v1/form/{id}` +- Endpoint: `/api/v1.1/form/{id}` - Url-Parameter: | Parameter | Type | Description | |-----------|---------|-------------| @@ -111,7 +114,8 @@ Returns the full-depth object of the requested form (without submissions). "formId": 3, "order": 1, "type": "dropdown", - "mandatory": false, + "mandatory": false, // deprecated, will be removed in API v2 + "isRequired": false, "text": "Question 1", "options": [ { @@ -131,7 +135,8 @@ Returns the full-depth object of the requested form (without submissions). "formId": 3, "order": 2, "type": "short", - "mandatory": true, + "mandatory": true, // deprecated, will be removed in API v2 + "isRequired": true, "text": "Question 2", "options": [] } @@ -141,7 +146,7 @@ Returns the full-depth object of the requested form (without submissions). ### Clone a form Creates a clone of a form (without submissions). -- Endpoint: `/api/v1/form/clone/{id}` +- Endpoint: `/api/v1.1/form/clone/{id}` - Url-Parameter: | Parameter | Type | Description | |-----------|---------|-------------| @@ -154,7 +159,7 @@ See section 'Request full data of a form'. ### Update form properties Update a single or multiple properties of a form-object. Concerns **only** the Form-Object, properties of Questions, Options and Submissions, as well as their creation or deletion, are handled separately. -- Endpoint: `/api/v1/form/update` +- Endpoint: `/api/v1.1/form/update` - Method: `POST` - Parameters: | Parameter | Type | Description | @@ -168,7 +173,7 @@ Update a single or multiple properties of a form-object. Concerns **only** the F ``` ### Delete a form -- Endpoint: `/api/v1/form/{id}` +- Endpoint: `/api/v1.1/form/{id}` - Url-Parameter: | Parameter | Type | Description | |-----------|---------|-------------| @@ -183,7 +188,7 @@ Update a single or multiple properties of a form-object. Concerns **only** the F Contains only manipulative question-endpoints. To retrieve questions, request the full form data. ### Create a new question -- Endpoint: `/api/v1/question` +- Endpoint: `/api/v1.1/question` - Method: `POST` - Parameters: | Parameter | Type | Optional | Description | @@ -198,7 +203,8 @@ Contains only manipulative question-endpoints. To retrieve questions, request th "formId": 3, "order": 3, "type": "short", - "mandatory": false, + "mandatory": false, // deprecated, will be removed in API v2 + "isRequired": false, "text": "", "options": [] } @@ -206,7 +212,7 @@ Contains only manipulative question-endpoints. To retrieve questions, request th ### Update question properties Update a single or multiple properties of a question-object. -- Endpoint: `/api/v1/question/update` +- Endpoint: `/api/v1.1/question/update` - Method: `POST` - Parameters: | Parameter | Type | Description | @@ -221,7 +227,7 @@ Update a single or multiple properties of a question-object. ### Reorder questions Reorders all Questions of a single form -- Endpoint: `/api/v1/question/reorder` +- Endpoint: `/api/v1.1/question/reorder` - Method: `POST` - Parameters: | Parameter | Type | Description | @@ -245,7 +251,7 @@ Reorders all Questions of a single form ``` ### Delete a question -- Endpoint: `/api/v1/question/{id}` +- Endpoint: `/api/v1.1/question/{id}` - Url-Parameter: | Parameter | Type | Description | |-----------|---------|-------------| @@ -260,7 +266,7 @@ Reorders all Questions of a single form Contains only manipulative question-endpoints. To retrieve options, request the full form data. ### Create a new Option -- Endpoint: `/api/v1/option` +- Endpoint: `/api/v1.1/option` - Method: `POST` - Parameters: | Parameter | Type | Description | @@ -277,7 +283,7 @@ Contains only manipulative question-endpoints. To retrieve options, request the ``` ### Update option properties -- Endpoint: `/api/v1/option/update` +- Endpoint: `/api/v1.1/option/update` - Method: `POST` - Parameters: | Parameter | Type | Description | @@ -291,7 +297,7 @@ Contains only manipulative question-endpoints. To retrieve options, request the ``` ### Delete an option -- Endpoint: `/api/v1/option/{id}` +- Endpoint: `/api/v1.1/option/{id}` - Url-Parameter: | Parameter | Type | Description | |-----------|---------|-------------| @@ -306,7 +312,7 @@ Contains only manipulative question-endpoints. To retrieve options, request the ## Submission Endpoints ### Get Form Submissions Get all Submissions to a Form -- Endpoint: `/api/v1/submissions/{hash}` +- Endpoint: `/api/v1.1/submissions/{hash}` - Url-Parameter: | Parameter | Type | Description | |-----------|---------|-------------| @@ -365,7 +371,8 @@ Get all Submissions to a Form "formId": 3, "order": 1, "type": "dropdown", - "mandatory": false, + "mandatory": false, // deprecated, will be removed in API v2 + "isRequired": false, "text": "Question 1", "options": [ { @@ -390,7 +397,8 @@ Get all Submissions to a Form "formId": 3, "order": 2, "type": "short", - "mandatory": true, + "mandatory": true, // deprecated will be removed in API v2 + "isRequired": true, "text": "Question 2", "options": [] } @@ -400,7 +408,7 @@ Get all Submissions to a Form ### Get Submissions as csv (Download) Returns all submissions to the form in form of a csv-file. -- Endpoint: `/api/v1/submissions/export/{hash}` +- Endpoint: `/api/v1.1/submissions/export/{hash}` - Url-Parameter: | Parameter | Type | Description | |-----------|---------|-------------| @@ -415,7 +423,7 @@ Returns all submissions to the form in form of a csv-file. ### Export Submissions to Cloud (Files-App) Creates a csv file and stores it to the cloud, resp. Files-App. -- Endpoint: `/api/v1/submissions/export` +- Endpoint: `/api/v1.1/submissions/export` - Method: `POST` - Parameters: | Parameter | Type | Description | @@ -429,7 +437,7 @@ Creates a csv file and stores it to the cloud, resp. Files-App. ### Delete Submissions Delete all Submissions to a form -- Endpoint: `/api/v1/submissions/{formId}` +- Endpoint: `/api/v1.1/submissions/{formId}` - Url-Parameter: | Parameter | Type | Description | |-----------|---------|-------------| @@ -442,7 +450,7 @@ Delete all Submissions to a form ### Insert a Submission Store Submission to Database -- Endpoint: `/api/v1/submission/insert` +- Endpoint: `/api/v1.1/submission/insert` - Method: `POST` - Parameters: | Parameter | Type | Description | @@ -462,7 +470,7 @@ Store Submission to Database - Response: **Status-Code OK**. ### Delete a single Submission -- Endpoint: `/api/v1/submission/{id}` +- Endpoint: `/api/v1.1/submission/{id}` - Url-Parameter: | Parameter | Type | Description | |-----------|---------|-------------| diff --git a/docs/DataStructure.md b/docs/DataStructure.md index 3a015b15..fab5f0e5 100644 --- a/docs/DataStructure.md +++ b/docs/DataStructure.md @@ -1,7 +1,7 @@ # Forms Data Structure -**State: Forms v2.2.0 - 22.01.2021** +**State: Forms v2.3.0 - 09.04.2021** -This document describes the Obect-Structure, that is used within the Forms App and on Forms API. It does partially **not** equal the actual database structure behind. +This document describes the Object-Structure, that is used within the Forms App and on Forms API v1.1. It does partially **not** equal the actual database structure behind. ## Data Structures ### Form @@ -46,7 +46,8 @@ This document describes the Obect-Structure, that is used within the Forms App a | formId | Integer | | The id of the form, the question belongs to | | order | Integer | unique within form; *not* `0` | The order of the question within that form. Value `0` indicates deleted questions within database (typ. not visible outside) | | type | [Question-Type](#question-types) | | Type of the question | -| mandatory | Boolean | | If the question is mandatory to fill the form | +| _mandatory_ | _Boolean_ | | _deprecated: will be removed in API v2, replaced by `isRequired`_ | +| isRequired | Boolean | | If the question is required to fill the form | | text | String | max. 2048 ch. | The question-text | | options | Array of [Options](#option) | | Array of options belonging to the question. Only relevant for question-type with predefined options. | ``` @@ -55,7 +56,8 @@ This document describes the Obect-Structure, that is used within the Forms App a "formId": 3, "order": 1, "type": "dropdown", - "mandatory": false, + "mandatory": false, // deprecated, will be removed in API v2 + "isRequired": false, "text": "Question 1", "options": [] } diff --git a/lib/Controller/ApiController.php b/lib/Controller/ApiController.php index da127227..0b7c393a 100644 --- a/lib/Controller/ApiController.php +++ b/lib/Controller/ApiController.php @@ -215,6 +215,12 @@ class ApiController extends OCSController { throw new OCSForbiddenException(); } + // TODO remove after removal of API v1.1 + // Backward compatibility for mandatory + foreach ($form['questions'] as $index => $question) { + $form['questions'][$index]['mandatory'] = $question['isRequired']; + } + return new DataResponse($form); } @@ -483,13 +489,17 @@ class ApiController extends OCSController { $question->setOrder($questionOrder); $question->setType($type); $question->setText($text); - $question->setMandatory(false); + $question->setIsRequired(false); $question = $this->questionMapper->insert($question); $response = $question->read(); $response['options'] = []; + // TODO remove after removal of API v1.1 + // Backward compatibility for mandatory + $response['mandatory'] = $response['isRequired']; + return new DataResponse($response); } @@ -634,6 +644,14 @@ class ApiController extends OCSController { throw new OCSForbiddenException('Please use reorderQuestions() to change order'); } + // TODO remove after removal of API v1.1 + // Rename deprecated mandatory key to isRequired + if (key_exists('mandatory', $keyValuePairs)) { + $this->logger->info('Key \'mandatory\' is deprecated, please use \'isRequired\'.'); + $keyValuePairs['isRequired'] = $keyValuePairs['mandatory']; + unset($keyValuePairs['mandatory']); + } + // Create QuestionEntity with given Params & Id. $question = Question::fromParams($keyValuePairs); $question->setId($id); @@ -903,6 +921,12 @@ class ApiController extends OCSController { // Load currently active questions $questions = $this->formsService->getQuestions($form->getId()); + // TODO remove after removal of API v1.1 + // Backward compatibility for mandatory + foreach ($questions as $index => $question) { + $questions[$index]['mandatory'] = $question['isRequired']; + } + $response = [ 'submissions' => $submissions, 'questions' => $questions, diff --git a/lib/Db/Question.php b/lib/Db/Question.php index 2cdd4502..53742cbc 100644 --- a/lib/Db/Question.php +++ b/lib/Db/Question.php @@ -47,7 +47,7 @@ class Question extends Entity { protected $formId; protected $order; protected $type; - protected $mandatory; + protected $isRequired; protected $text; public const TYPES = [ @@ -64,7 +64,7 @@ class Question extends Entity { $this->addType('formId', 'integer'); $this->addType('order', 'integer'); $this->addType('type', 'string'); - $this->addType('mandatory', 'bool'); + $this->addType('isRequired', 'bool'); $this->addType('text', 'string'); } @@ -74,7 +74,7 @@ class Question extends Entity { 'formId' => $this->getFormId(), 'order' => $this->getOrder(), 'type' => htmlspecialchars_decode($this->getType()), - 'mandatory' => $this->getMandatory(), + 'isRequired' => $this->getIsRequired(), 'text' => htmlspecialchars_decode($this->getText()), ]; } diff --git a/lib/Migration/Version020300Date20210406114130.php b/lib/Migration/Version020300Date20210406114130.php new file mode 100644 index 00000000..cada58d4 --- /dev/null +++ b/lib/Migration/Version020300Date20210406114130.php @@ -0,0 +1,78 @@ + + * + * @author Christian Hartmann + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ +namespace OCA\Forms\Migration; + +use Closure; +use OCP\DB\ISchemaWrapper; +use OCP\IDBConnection; +use OCP\Migration\IOutput; +use OCP\Migration\SimpleMigrationStep; + +class Version020300Date20210406114130 extends SimpleMigrationStep { + /** @var IDBConnection */ + protected $connection; + + /** + * @param IDBConnection $connection + */ + public function __construct(IDBConnection $connection) { + $this->connection = $connection; + } + + private const TYPE_BOOLEAN = 'boolean'; + + /** + * @param IOutput $output + * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` + * @param array $options + * @return null|ISchemaWrapper + */ + public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { + /** @var ISchemaWrapper $schema */ + $schema = $schemaClosure(); + + $table = $schema->getTable('forms_v2_questions'); + + if (!$table->hasColumn('is_required')) { + $table->addColumn('is_required', self::TYPE_BOOLEAN, [ + 'notnull' => false, + 'default' => 0, + ]); + + return $schema; + } + + return null; + } + + public function postSchemaChange(IOutput $output, \Closure $schemaClosure, array $options) { + $qb_update = $this->connection->getQueryBuilder(); + + $qb_update->update('forms_v2_questions') + ->set('is_required', 'mandatory'); + $qb_update->execute(); + } +} diff --git a/lib/Migration/Version020300Date20210406133704.php b/lib/Migration/Version020300Date20210406133704.php new file mode 100644 index 00000000..91ee0787 --- /dev/null +++ b/lib/Migration/Version020300Date20210406133704.php @@ -0,0 +1,55 @@ + + * + * @author Christian Hartmann + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ +namespace OCA\Forms\Migration; + +use Closure; +use OCP\DB\ISchemaWrapper; +use OCP\Migration\IOutput; +use OCP\Migration\SimpleMigrationStep; + +class Version020300Date20210406133704 extends SimpleMigrationStep { + + /** + * @param IOutput $output + * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` + * @param array $options + * @return null|ISchemaWrapper + */ + public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { + /** @var ISchemaWrapper $schema */ + $schema = $schemaClosure(); + + $table = $schema->getTable('forms_v2_questions'); + + if ($table->hasColumn('mandatory') && $table->hasColumn('is_required')) { + $table->dropColumn('mandatory'); + + return $schema; + } + + return null; + } +} diff --git a/src/Forms.vue b/src/Forms.vue index e52a355f..ef3a9581 100644 --- a/src/Forms.vue +++ b/src/Forms.vue @@ -202,7 +202,7 @@ export default { // Load Owned forms try { - const response = await axios.get(generateOcsUrl('apps/forms/api/v1', 2) + 'forms') + const response = await axios.get(generateOcsUrl('apps/forms/api/v1.1', 2) + 'forms') this.forms = OcsResponse2Data(response) } catch (error) { showError(t('forms', 'An error occurred while loading the forms list')) @@ -211,7 +211,7 @@ export default { // Load shared forms try { - const response = await axios.get(generateOcsUrl('apps/forms/api/v1', 2) + 'shared_forms') + const response = await axios.get(generateOcsUrl('apps/forms/api/v1.1', 2) + 'shared_forms') this.sharedForms = OcsResponse2Data(response) } catch (error) { showError(t('forms', 'An error occurred while loading the forms list')) @@ -227,7 +227,7 @@ export default { async onNewForm() { try { // Request a new empty form - const response = await axios.post(generateOcsUrl('apps/forms/api/v1', 2) + 'form') + const response = await axios.post(generateOcsUrl('apps/forms/api/v1.1', 2) + 'form') const newForm = OcsResponse2Data(response) this.forms.unshift(newForm) this.$router.push({ name: 'edit', params: { hash: newForm.hash } }) @@ -245,7 +245,7 @@ export default { */ async onCloneForm(id) { try { - const response = await axios.post(generateOcsUrl('apps/forms/api/v1', 2) + `form/clone/${id}`) + const response = await axios.post(generateOcsUrl('apps/forms/api/v1.1', 2) + `form/clone/${id}`) const newForm = OcsResponse2Data(response) this.forms.unshift(newForm) this.$router.push({ name: 'edit', params: { hash: newForm.hash } }) diff --git a/src/components/AppNavigationForm.vue b/src/components/AppNavigationForm.vue index 361d4c47..7db86d9b 100644 --- a/src/components/AppNavigationForm.vue +++ b/src/components/AppNavigationForm.vue @@ -183,7 +183,7 @@ export default { // All good, let's delete this.loading = true try { - await axios.delete(generateOcsUrl('apps/forms/api/v1', 2) + `form/${this.form.id}`) + await axios.delete(generateOcsUrl('apps/forms/api/v1.1', 2) + `form/${this.form.id}`) this.$emit('delete', this.form.id) } catch (error) { showError(t('forms', 'Error while deleting {title}', { title: this.formTitle })) diff --git a/src/components/Questions/AnswerInput.vue b/src/components/Questions/AnswerInput.vue index 0ca91f32..ef1e9961 100644 --- a/src/components/Questions/AnswerInput.vue +++ b/src/components/Questions/AnswerInput.vue @@ -146,7 +146,7 @@ export default { */ async createAnswer(answer) { try { - const response = await axios.post(generateOcsUrl('apps/forms/api/v1', 2) + 'option', { + const response = await axios.post(generateOcsUrl('apps/forms/api/v1.1', 2) + 'option', { questionId: answer.questionId, text: answer.text, }) @@ -174,7 +174,7 @@ export default { */ async updateAnswer(answer) { try { - await axios.post(generateOcsUrl('apps/forms/api/v1', 2) + 'option/update', { + await axios.post(generateOcsUrl('apps/forms/api/v1.1', 2) + 'option/update', { id: this.answer.id, keyValuePairs: { text: answer.text, diff --git a/src/components/Questions/Question.vue b/src/components/Questions/Question.vue index 9f2ac015..104ec798 100644 --- a/src/components/Questions/Question.vue +++ b/src/components/Questions/Question.vue @@ -51,8 +51,8 @@ class="question__header-warning icon-error-color" tabindex="0" /> - + {{ t('forms', 'Required') }} @@ -99,7 +99,7 @@ export default { type: String, required: true, }, - mandatory: { + isRequired: { type: Boolean, required: true, }, @@ -131,11 +131,11 @@ export default { computed: { /** - * Extend text with asterisk if question is mandatory + * Extend text with asterisk if question is required * @returns {Boolean} */ computedText() { - if (this.mandatory) { + if (this.isRequired) { return this.text + ' *' } return this.text @@ -155,8 +155,8 @@ export default { this.$emit('update:text', target.value) }, - onMandatoryChange(mandatory) { - this.$emit('update:mandatory', mandatory) + onRequiredChange(isRequired) { + this.$emit('update:isRequired', isRequired) }, /** diff --git a/src/components/Questions/QuestionDate.vue b/src/components/Questions/QuestionDate.vue index 2932ff71..524df81a 100644 --- a/src/components/Questions/QuestionDate.vue +++ b/src/components/Questions/QuestionDate.vue @@ -24,14 +24,14 @@