use Modern::Perl;
+use MIME::Base64 qw( decode_base64 );
+use MIME::Types;
+
use Koha::Database;
use Koha::DateUtils qw( dt_from_string );
my $schema = $self->_result->result_source->schema;
$schema->txn_do(
sub {
- $self->documents->delete;
+ my $existing_documents = $self->documents;
+
+ # FIXME Here we are not deleting all the documents before recreating them, like we do for other related resources.
+ # As we do not want the content of the documents to transit over the network we need to use the document_id (and allow it in the API spec)
+ # to distinguish from each other
+ # Delete all the documents that are not part of the PUT request
+ my $modified_document_ids = [ map { $_->{document_id} || () } @$documents ];
+ $self->documents->search(
+ {
+ @$modified_document_ids
+ ? (
+ document_id => {
+ '-not_in' => $modified_document_ids
+ }
+ )
+ : ()
+ }
+ )->delete;
+
for my $document (@$documents) {
- if ( $document->{file_content} ) {
- $document->{file_type} = 'unknown'; # FIXME How to detect file type from base64?
- $document->{uploaded_on} //= dt_from_string;
+ if ( $document->{document_id} ) {
+ # The document already exists in DB
+ $existing_documents->find( $document->{document_id} )
+ ->set(
+ {
+ file_description => $document->{file_description},
+ physical_location => $document->{physical_location},
+ uri => $document->{uri},
+ notes => $document->{notes},
+ }
+ )->store;
+ }
+ else {
+ # Creating a whole new document
+ my $file_content = decode_base64( $document->{file_content} );
+ my $mt = MIME::Types->new();
+ $document->{file_type} = $mt->mimeTypeOf( $document->{file_name} );
+ $document->{uploaded_on} //= dt_from_string;
+ $document->{file_content} = $file_content;
+ $self->_result->add_to_erm_agreement_documents( $document);
}
- $self->_result->add_to_erm_agreement_documents($document);
}
}
);
=cut
+=head3 to_api_mapping
+
+=cut
+
+sub to_api_mapping {
+ return {
+ # Do not expose file_content to prevent the content to be fetch from the DB and sent over the network when embeded
+ file_content => undef,
+ };
+}
+
=head2 Internal methods
=head3 _type
route => $self->routes->under('/api/v1')->to('Auth#under'),
}
);
+
+ $self->plugin('RenderFile');
}
catch {
# Validation of the complete spec failed. Resort to validation one-by-one
--- /dev/null
+package Koha::REST::V1::ERM::Documents;
+
+# This file is part of Koha.
+#
+# Koha is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# Koha 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Koha; if not, see <http://www.gnu.org/licenses>.
+
+use Modern::Perl;
+
+use Mojo::Base 'Mojolicious::Controller';
+
+use Koha::ERM::Agreement::Documents;
+
+use Scalar::Util qw( blessed );
+use Try::Tiny qw( catch try );
+
+=head1 API
+
+=head2 Methods
+
+=head3 get
+
+Controller function that handles retrieving a single Koha::ERM::Agreement object
+
+=cut
+
+sub get {
+ my $c = shift->openapi->valid_input or return;
+
+ return try {
+ my $agreement_id = $c->validation->param('agreement_id');
+ my $document_id = $c->validation->param('document_id');
+
+ # Do not use $c->objects->find here, we need the file_content
+ my $document = Koha::ERM::Agreement::Documents->find($document_id);
+
+ if ( !$document || $document->agreement_id != $agreement_id) {
+ return $c->render(
+ status => 404,
+ openapi => { error => "Document not found" }
+ );
+ }
+
+ $c->render_file('data' => $document->file_content, 'filename' => $document->file_name);
+ }
+ catch {
+ $c->unhandled_exception($_);
+ };
+}
+
+1;
document_id:
type: integer
description: internally assigned identifier
- readOnly: true
agreement_id:
type: integer
description: Internal agreement identifier
type:
- string
- "null"
- description: Name of the file
+ readOnly: true
+ description: Type of the file
file_description:
type:
- string
type:
- string
- "null"
+ readOnly: true
format: date-time
description: Datetime of the upload
- file_type:
- type:
- - string
- - "null"
- description: Name of the file
physical_location:
type:
- string
--- /dev/null
+---
+"/erm/agreements/{agreement_id}/documents/{document_id}/file/content":
+ get:
+ x-mojo-to: ERM::Documents#get
+ operationId: downloadErmAgreementDocument
+ tags:
+ - document
+ summary: Download agreement document
+ produces:
+ - application/octet-stream
+ parameters:
+ - description: Case insensitive search on agreement agreement_id
+ in: path
+ name: agreement_id
+ required: true
+ type: integer
+ - description: Case insensitive search on agreement document_id
+ in: path
+ name: document_id
+ required: true
+ type: integer
+ responses:
+ 200:
+ description: Anagreement
+ schema:
+ type: file
+ 401:
+ description: Authentication required
+ schema:
+ $ref: "../swagger.yaml#/definitions/error"
+ 403:
+ description: Access forbidden
+ schema:
+ $ref: "../swagger.yaml#/definitions/error"
+ 404:
+ description: Ressource not found
+ schema:
+ $ref: "../swagger.yaml#/definitions/error"
+ 500:
+ description: |-
+ Internal server error. Possible `error_code` attribute values:
+ * `internal_server_error`
+ schema:
+ $ref: "../swagger.yaml#/definitions/error"
+ 503:
+ description: Under maintenance
+ schema:
+ $ref: "../swagger.yaml#/definitions/error"
+ x-koha-authorization:
+ permissions:
+ erm: 1
$ref: ./paths/erm_agreements.yaml#/~1erm~1agreements
"/erm/agreements/{agreement_id}":
$ref: "./paths/erm_agreements.yaml#/~1erm~1agreements~1{agreement_id}"
+ "/erm/agreements/{agreement_id}/documents/{document_id}/file/content":
+ $ref: "./paths/erm_documents.yaml#/~1erm~1agreements~1{agreement_id}~1documents~1{document_id}~1file~1content"
"/erm/eholdings/{provider}/titles":
$ref: ./paths/erm_eholdings_titles.yaml#/~1erm~1eholdings~1{provider}~1titles
"/erm/eholdings/{provider}/titles/{title_id}":
requires 'Mojo::JWT', '0.08';
requires 'Mojolicious', '8.12';
requires 'Mojolicious::Plugin::OpenAPI', '5.05';
+requires 'Mojolicious::Plugin::RenderFile', '0.12';
requires 'Net::CIDR', '0.17';
requires 'Net::Netmask', '1.9022';
requires 'Net::Stomp', '0.57';
agreement.agreement_relationships = agreement.agreement_relationships.map(({ related_agreement, ...keepAttrs }) => keepAttrs)
- agreement.documents = agreement.documents.map(({ document_id, ...keepAttrs }) => keepAttrs)
+ agreement.documents = agreement.documents.map(({ file_type, uploaded_on, ...keepAttrs }) => keepAttrs)
delete agreement.agreement_packages
</div>
</div>
</li>
+
+ <li v-if="agreement.documents.length">
+ <label>{{ $t("Documents") }}</label>
+ <div id="agreement_documents">
+ <ul>
+ <li
+ v-for="document in agreement.documents"
+ v-bind:key="document.document_id"
+ >
+ <div v-if="document.file_name">
+ <span v-if="document.file_description"
+ >{{ document.file_description }} -
+ </span>
+ <a
+ download
+ :href="`/api/v1/erm/agreements/${agreement.agreement_id}/documents/${document.document_id}/file/content`"
+ >
+ {{ document.file_name }}
+ <i class="fa fa-download"></i>
+ </a>
+ ({{ document.file_type }}) Uploaded on:
+ {{ format_date(document.uploaded_on) }}
+ </div>
+ <div v-if="document.physical_location">
+ {{ $t("Physical location") }}:
+ {{ document.physical_location }}
+ </div>
+ <div v-if="document.uri">
+ {{ $t("URI") }}: {{ document.uri }}
+ </div>
+ <div v-if="document.notes">
+ {{ $t("Notes") }}: {{ document.notes }}
+ </div>
+ </li>
+ </ul>
+ </div>
+ </li>
</ol>
</fieldset>
<fieldset class="action">
font-size: 11px;
}
#agreement_relationships,
-#agreement_packages {
+#agreement_packages,
+#agreement_documents {
padding-left: 10rem;
}
-</style>
\ No newline at end of file
+#agreement_documents ul {
+ padding-left: 0;
+}
+</style>