diff --git a/composer.json b/composer.json index 3716721..4be3ad0 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,9 @@ }, "require-dev": { "statonlab/tripal-test-suite": "1.5.*", - "phpunit/php-code-coverage": "^6.1" + "phpunit/php-code-coverage": "^6.1", + "squizlabs/php_codesniffer": "^2.9.1", + "drupal/coder": "^8.2" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index a86db15..068563e 100644 --- a/composer.lock +++ b/composer.lock @@ -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": "133a7b77ddffc432ea97c9bb53fa2a5d", + "content-hash": "641337ac7c0ef1f645d7b31fe0c3da5c", "packages": [], "packages-dev": [ { @@ -63,6 +63,37 @@ ], "time": "2019-03-17T17:37:11+00:00" }, + { + "name": "drupal/coder", + "version": "8.2.12", + "source": { + "type": "git", + "url": "https://git.drupalcode.org/project/coder.git", + "reference": "984c54a7b1e8f27ff1c32348df69712afd86b17f" + }, + "require": { + "ext-mbstring": "*", + "php": ">=5.4.0", + "squizlabs/php_codesniffer": ">=2.8.1 <3.0", + "symfony/yaml": ">=2.0.0" + }, + "require-dev": { + "phpunit/phpunit": ">=3.7 <6" + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0+" + ], + "description": "Coder is a library to review Drupal code.", + "homepage": "https://www.drupal.org/project/coder", + "keywords": [ + "code review", + "phpcs", + "standards" + ], + "time": "2017-03-18T10:28:49+00:00" + }, { "name": "fzaninotto/faker", "version": "v1.8.0", @@ -1705,6 +1736,84 @@ "homepage": "https://github.com/sebastianbergmann/version", "time": "2016-10-03T07:35:21+00:00" }, + { + "name": "squizlabs/php_codesniffer", + "version": "2.9.2", + "source": { + "type": "git", + "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", + "reference": "2acf168de78487db620ab4bc524135a13cfe6745" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/2acf168de78487db620ab4bc524135a13cfe6745", + "reference": "2acf168de78487db620ab4bc524135a13cfe6745", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.1.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "bin": [ + "scripts/phpcs", + "scripts/phpcbf" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "classmap": [ + "CodeSniffer.php", + "CodeSniffer/CLI.php", + "CodeSniffer/Exception.php", + "CodeSniffer/File.php", + "CodeSniffer/Fixer.php", + "CodeSniffer/Report.php", + "CodeSniffer/Reporting.php", + "CodeSniffer/Sniff.php", + "CodeSniffer/Tokens.php", + "CodeSniffer/Reports/", + "CodeSniffer/Tokenizers/", + "CodeSniffer/DocGenerators/", + "CodeSniffer/Standards/AbstractPatternSniff.php", + "CodeSniffer/Standards/AbstractScopeSniff.php", + "CodeSniffer/Standards/AbstractVariableSniff.php", + "CodeSniffer/Standards/IncorrectPatternException.php", + "CodeSniffer/Standards/Generic/Sniffs/", + "CodeSniffer/Standards/MySource/Sniffs/", + "CodeSniffer/Standards/PEAR/Sniffs/", + "CodeSniffer/Standards/PSR1/Sniffs/", + "CodeSniffer/Standards/PSR2/Sniffs/", + "CodeSniffer/Standards/Squiz/Sniffs/", + "CodeSniffer/Standards/Zend/Sniffs/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "lead" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "http://www.squizlabs.com/php-codesniffer", + "keywords": [ + "phpcs", + "standards" + ], + "time": "2018-11-07T22:31:41+00:00" + }, { "name": "statonlab/tripal-test-suite", "version": "1.5.2", @@ -2061,6 +2170,65 @@ ], "time": "2019-09-17T11:12:18+00:00" }, + { + "name": "symfony/yaml", + "version": "v4.3.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "324cf4b19c345465fad14f3602050519e09e361d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/324cf4b19c345465fad14f3602050519e09e361d", + "reference": "324cf4b19c345465fad14f3602050519e09e361d", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/console": "<3.4" + }, + "require-dev": { + "symfony/console": "~3.4|~4.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "https://symfony.com", + "time": "2019-10-30T12:58:49+00:00" + }, { "name": "theseer/tokenizer", "version": "1.1.3", diff --git a/docs/_static/img/imports_userdash.png b/docs/_static/img/imports_userdash.png new file mode 100644 index 0000000..13ea942 Binary files /dev/null and b/docs/_static/img/imports_userdash.png differ diff --git a/docs/contributing.rst b/docs/contributing.rst index d75009a..b361404 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -13,3 +13,23 @@ It provides a database seeder to make development a bit easier. Once you've inst .. warning:: **NEVER** run seeders on production sites. They will insert fictitious data into Chado. + +Coding Standards +------------------ + +This project uses code climate to ensure coding standards are met. We suggest you use php_codesniffer locally to check coding standards before submitting a Pull Request for a smoother experience. This can be done as follows: + +1. Run ``composer up`` within the Tripal HQ directory. This will install php_codesniffer locally. +2. Check coding standards by running ``./vendor/bin/phpcs --standard=vendor/drupal/coder/coder_sniffer/Drupal/ruleset.xml [file]`` where ``[file]`` contains your changes. This will output a report meant to help you improve your code. +3. php_codesniffer includes a tool for automatically fixing many warnings you may have encountered. To run it execute ``./vendor/bin/phpcbf --standard=vendor/drupal/coder/coder_sniffer/Drupal/ruleset.xml [file]`` on the same file. Make sure to review any changes it makes. +4. Manually fix any remaining errors and re-run step 2 to confirm. + +We truely appretiate your effort in keeping our project standards compliant! + +Automated Testing +------------------- + +This project uses TripalTestSuite and phpunit for automated testing. To run tests: + +1. Run ``composer up`` within the Tripal HQ directory. This will install phpunit locally. +2. Run ``.vendor/bin/phpunit`` to execute all tests. diff --git a/docs/introduction.rst b/docs/introduction.rst index 8148925..aff757e 100644 --- a/docs/introduction.rst +++ b/docs/introduction.rst @@ -9,12 +9,14 @@ What is Tripal HQ? Tripal HQ provides a user-contributed content control center and administrative toolbox for your Tripal site. This means that users are able to create whatever Chado content you'd like them, but withhold inserting it into the database until someone has approved it. +Tripal HQ Imports extends this functionality to data importers. In this way your users can submit any single-page Tripal importer and both the file and associated metadata will remain in holding for administrative review. Once approved, the importer will be run, inserting all data into the database. + HQ also provides a Chado-specific permission (currently only supporting organisms). If you have lots of organisms on your site, and want to allow users to be the admin of a specific organism or set of organisms, this feature is for you! Module Features ================ -* Users create data using your existing Bundle configuration- no extra forms! +* Users create data using your existing Bundle configuration or Importers- no extra forms! * User dashboard area for viewing pending submissions * Admin dashboard for viewing submissions * Chado-based permissions to create admins for certain projects or organisms diff --git a/docs/setup/install.rst b/docs/setup/install.rst index 143437d..529f67a 100644 --- a/docs/setup/install.rst +++ b/docs/setup/install.rst @@ -13,7 +13,7 @@ Clone the repo with github and enable the module with drush. For example: cd /var/www/html/sites/all/modules/custom/ git clone https://github.com/statonlab/tripal_hq.git - drush pm-enable tripal_hq tripal_hq_permissions + drush pm-enable tripal_hq tripal_hq_imports tripal_hq_permissions diff --git a/docs/setup/permissions.rst b/docs/setup/permissions.rst index 06ab23f..33ec9ba 100644 --- a/docs/setup/permissions.rst +++ b/docs/setup/permissions.rst @@ -14,6 +14,7 @@ On installation, HQ defines the following permissions: * **Administer CHADO-specific Tripal HQ content**: Lets you specify what Chado content a user can administer. * **Create Tripal HQ content requests**: Allows users to submit content requests and view their dashboard. * **Propose Tripal HQ Content** permission for each of your defined bundles. This will let you configure which bundles can be proposed by users. +* **Propose Tripal HQ Data File** permission for each available Tripal Importer. This will let you configure which importers can be proposed by users. To get started, you'll need to create a role for your content submitters, and give them the "Create Tripal Content Requests" permission, plus whatever specific bundle permissions you'd like them to see. diff --git a/docs/usage.rst b/docs/usage.rst index e2adedb..d2d4b48 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -5,6 +5,9 @@ Module Usage Users ======= +Content Pages +--------------- + User content submission can be reached at ``tripal_hq/bio_data``. @@ -14,6 +17,17 @@ Approved, pending, and rejected content is displayed in the table. Clicking the Click the "Submit Content" button to submit Tripal Content. The submission form is the **exact same form** as the admin content creation form. +Data Files +------------ + +Data files can be submitted by users at ``tripal_hq/bio_data/import-data``. + +.. image:: /_static/img/imports_userdash.png + +As you can see in the above screenshot when Tripal HQ Imports is enabled, it adds a second table to the user dashboard. This makes it easy to see the status of both your content and data file submissions. + +To submit a new data file with associated metadata, click "Import data file". You can edit a pending submission by clicking the "Edit" link beside that particular data file. Both the submission add and edit forms match the administrative data import forms exactly which ensures consistent metadata and validation. + Admins ======= diff --git a/tripal_hq_imports/includes/tripal_hq_imports.api.inc b/tripal_hq_imports/includes/tripal_hq_imports.api.inc index b4d0474..71e0eba 100644 --- a/tripal_hq_imports/includes/tripal_hq_imports.api.inc +++ b/tripal_hq_imports/includes/tripal_hq_imports.api.inc @@ -53,6 +53,7 @@ function tripal_hq_get_importers() { * * @param string $class * The TripalImporter class to include. + * * @return bool * TRUE if the field type class file was found, FALSE otherwise. */ @@ -80,6 +81,7 @@ function tripal_hq_load_include_importer_class($class) { * * @param int $submission_id * The id of the submission you would like returned. + * * @return object * An object describing the submission. */ @@ -97,6 +99,7 @@ function tripal_hq_imports_get_submission_by_id($submission_id) { * * @param object $submission * An object describing the submission to be rejected. + * * @return bool * True if the rejection was successful, FALSE otherwise. */ @@ -124,6 +127,7 @@ function tripal_hq_imports_reject_submission($submission) { * * @param object $submission * An object describing the submission to be approved. + * * @return bool * Returns FALSE if an error is encountered, otherwise TRUE. */ @@ -165,10 +169,12 @@ function tripal_hq_imports_approve_submission($submission) { * A form field defined using the Drupal Form API. * @param array $values * The full values list for the form. + * @param string $element_key + * THe key of the element to be checked. * @param string $op * One of 'edit' or 'view' where the second will be read-only. */ -function tripal_hq_editview_form_field(&$form_field, $values, $element_key, $op) { +function tripal_hq_editview_form_field(array &$form_field, array $values, $element_key, $op) { if (isset($form_field['#type']) and ($form_field['#type'] == 'fieldset')) { foreach (element_children($form_field) as $child_element) { diff --git a/tripal_hq_imports/includes/tripal_hq_imports_admin_dashboard.form.inc b/tripal_hq_imports/includes/tripal_hq_imports_admin_dashboard.form.inc index 3396fa5..9d9b56f 100644 --- a/tripal_hq_imports/includes/tripal_hq_imports_admin_dashboard.form.inc +++ b/tripal_hq_imports/includes/tripal_hq_imports_admin_dashboard.form.inc @@ -5,151 +5,152 @@ * Provides changes to the administrative dashboard for tripal HQ imports. */ - /** - * Alter the administration dashboard to add our submissions. - * - * @param array $form - * The default form to be altered. - * @param array $form_state - * The state of the form to be altered. - * @param string $status - * The status of the submissions to show (e.g. pending). - * @return array - * The altered form array. - */ - function tripal_hq_imports_tripal_hq_admin_dashboard_alter(&$form, &$form_state, $status) { - global $user; - $uid = $user->uid; - - $items_per_page = 10; - - // Add a caption to differentiate the two tables. - $form['table']['#caption'] = '
', - '#markup' => t('Please click a data type below to submit a new data file and associated metadata for admin approval.'), - '#suffix' => '
', - ]; - - $user_has_stuff = 0; - $importers = tripal_hq_get_importers(); - foreach ($importers as $importer_class) { - - // Pull some important info out of the class. - $importer_label = $importer_class::$name; - $importer_machine_name = $importer_class::$machine_name; - $importer_description = $importer_class::$description; - - // Publication importer is not supported. - if ($importer_class == 'PubBulkImporter' OR $importer_class == 'OBOImporter') { - continue; - } - - // Ensure the user has permission to propose that file type. - if (($user->uid === 0) OR (!user_access("propose $importer_label"))) { - continue; - } - $user_has_stuff = 1; - - - $link = l($importer_label, - "tripal_hq/bio_data/import-data/$importer_class"); - $page[$importer_machine_name] = [ - '#type' => 'item', - '#markup' => $link, - '#description' => $importer_description, - ]; - } - - if ($user_has_stuff === 0) { - $page['description'] = [ - '#type' => 'markup', - '#prefix' => '', - '#markup' => t('You do not have site permissions to submit data files for consideration.'), - '#suffix' => '
', - ]; - - } - - return $page; - } - - /** - * TripalImporter submission form. - * - * @param int $importer_class - * The class name for the importer to show the form for. - * @param int $sid - * Submission ID. - * - * @return array - * Renderable array. - */ -function tripal_hq_user_importer_form($form, &$form_state) { +/** + * Provides the page view for user importer submissions. + * + * @return array + * Array of markup describing bundles. + */ +function tripal_hq_import_list_importers_page() { + global $user; + $page = []; + + // We might also add a warning for *admin* users to use the actual tripal + // import forms. + $page['description'] = [ + '#type' => 'markup', + '#prefix' => '', + '#markup' => t('Please click a data type below to submit a new data file and associated metadata for admin approval.'), + '#suffix' => '
', + ]; + + $user_has_stuff = 0; + $importers = tripal_hq_get_importers(); + foreach ($importers as $importer_class) { + + // Pull some important info out of the class. + $importer_label = $importer_class::$name; + $importer_machine_name = $importer_class::$machine_name; + $importer_description = $importer_class::$description; + + // Publication importer is not supported. + if ($importer_class == 'PubBulkImporter' OR $importer_class == 'OBOImporter') { + continue; + } + + // Ensure the user has permission to propose that file type. + if (($user->uid === 0) or (!user_access("propose $importer_label"))) { + continue; + } + $user_has_stuff = 1; + + $link = l($importer_label, + "tripal_hq/bio_data/import-data/$importer_class"); + $page[$importer_machine_name] = [ + '#type' => 'item', + '#markup' => $link, + '#description' => $importer_description, + ]; + } + + if ($user_has_stuff === 0) { + $page['description'] = [ + '#type' => 'markup', + '#prefix' => '', + '#markup' => t('You do not have site permissions to submit data files for consideration.'), + '#suffix' => '
', + ]; + + } + + return $page; +} + +/** + * TripalImporter submission form. + * + * @param array $form + * The form array. + * @param array $form_state + * The state of the form. + * + * @return array + * Renderable array. + */ +function tripal_hq_user_importer_form(array $form, array &$form_state) { $importer_class = $form_state['build_info']['args'][0]; $success = tripal_hq_load_include_importer_class($importer_class); $importer_label = $importer_class::$name; @@ -138,11 +137,12 @@ function tripal_hq_user_importer_form($form, &$form_state) { } // Most importers don't actually fill in defaults since they expect to be - // sumbitted only once. As such, we should still fill in the defaults ourselves. + // sumbitted only once. As such, we should still fill in the + // defaults ourselves. if (isset($importer_form_state['values'])) { // Ensure people do not try to edit submissions which are no longer pending. - if (($op != 'edit') OR ($status != 'pending')) { + if (($op != 'edit') or ($status != 'pending')) { $op = 'view'; } @@ -179,9 +179,8 @@ function tripal_hq_user_importer_form_submit($form, $form_state) { if ($sid) { // @todo currently we cant use tripal_hq_send_emails() since it retrieves - // the submission with the assumption of db table. - //tripal_hq_send_emails($sid, 'submit'); - + // the submission with the assumption of db table. + // tripal_hq_send_emails($sid, 'submit'); if (isset($form_state['values']['submission_id'])) { drupal_set_message( 'Submission created successfully. We will review your submission and get diff --git a/tripal_hq_imports/tripal_hq_imports.install b/tripal_hq_imports/tripal_hq_imports.install index 874fe76..68a83bc 100644 --- a/tripal_hq_imports/tripal_hq_imports.install +++ b/tripal_hq_imports/tripal_hq_imports.install @@ -1,4 +1,5 @@ 'Store pending user requests to import files.', - 'fields' => [ - 'id' => [ - 'type' => 'serial', - 'unsigned' => TRUE, - 'not null' => TRUE, - ], - 'uid' => [ - 'description' => "Drupal user ID of submitter", - 'type' => 'int', - 'not null' => TRUE, - ], - 'nid' => [ - 'description' => "Comments node ID", - 'type' => 'int', - 'not null' => FALSE, - ], - 'job_id' => [ - 'description' => "The id of the tripal job which ran the importer once approved.", - 'type' => 'int', - 'not null' => FALSE, - ], - 'class' => [ - 'description' => "The importer class the file and metadata is associated with.", - 'type' => 'varchar', - 'length' => 255, - 'not null' => FALSE, - ], - 'data' => [ - 'description' => 'Serialized tripal importer data.', - 'type' => 'blob', - 'size' => 'big', - 'serialize' => TRUE, - ], - 'status' => [ - 'description' => 'One of pending, published, rejected, obsolete', - 'type' => 'varchar', - 'length' => '60', - 'not null' => TRUE, - ], - 'created_at' => [ - 'description' => 'Date submission created', - 'type' => 'int', - 'size' => 'big', - 'not null' => TRUE, - ], - 'updated_at' => [ - 'description' => 'Date submission updated', - 'type' => 'int', - 'size' => 'big', - 'not null' => FALSE, - ], - ], - 'primary key' => [ - 'id', - ], - ]; + $schema['tripal_hq_importer_submission'] = [ + 'description' => 'Store pending user requests to import files.', + 'fields' => [ + 'id' => [ + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + ], + 'uid' => [ + 'description' => "Drupal user ID of submitter", + 'type' => 'int', + 'not null' => TRUE, + ], + 'nid' => [ + 'description' => "Comments node ID", + 'type' => 'int', + 'not null' => FALSE, + ], + 'job_id' => [ + 'description' => "The id of the tripal job which ran the importer once approved.", + 'type' => 'int', + 'not null' => FALSE, + ], + 'class' => [ + 'description' => "The importer class the file and metadata is associated with.", + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + ], + 'data' => [ + 'description' => 'Serialized tripal importer data.', + 'type' => 'blob', + 'size' => 'big', + 'serialize' => TRUE, + ], + 'status' => [ + 'description' => 'One of pending, published, rejected, obsolete', + 'type' => 'varchar', + 'length' => '60', + 'not null' => TRUE, + ], + 'created_at' => [ + 'description' => 'Date submission created', + 'type' => 'int', + 'size' => 'big', + 'not null' => TRUE, + ], + 'updated_at' => [ + 'description' => 'Date submission updated', + 'type' => 'int', + 'size' => 'big', + 'not null' => FALSE, + ], + ], + 'primary key' => [ + 'id', + ], + ]; - return $schema; + return $schema; }