Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Minor bug fixes #1941

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions b2share/modules/files/ext.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from __future__ import absolute_import, print_function

from .cli import files as files_cmd

from .views import object_view_object_resources

class B2ShareFiles(object):
"""B2Share Files extension."""
Expand All @@ -40,7 +40,11 @@ def init_app(self, app):
"""Flask application initialization."""
self.init_config(app)
app.cli.add_command(files_cmd)
app.extensions['b2share-files'] = self
app.extensions['b2share-files-rest'] = self
@app.before_first_request
def replace_files_rest_object_view():
app.view_functions['invenio_files_rest.object_api'] = object_view_object_resources


def init_config(self, app):
"""Initialize configuration."""
Expand Down
121 changes: 87 additions & 34 deletions b2share/modules/files/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,42 +21,95 @@

from __future__ import absolute_import

from flask import Blueprint, abort, request, jsonify

from invenio_rest import ContentNegotiatedMethodView
# from b2handle.handleclient import EUDATHandleClient
from invenio_files_rest.views import (
ObjectResource,
invalid_subresource_validator,
need_permissions
)
from invenio_files_rest.serializer import json_serializer
from invenio_files_rest.models import ObjectVersion
from webargs import fields

from invenio_files_rest.tasks import remove_file_data
from invenio_db import db

blueprint = Blueprint(
'handle',
__name__,
)


# class B2HandleRetriever(ContentNegotiatedMethodView):
# """Class for handling the b2handle record retrieval."""
# view_name = 'b2handle_retriever'
#
# def __init__(self, **kwargs):
#
# default_media_type = 'application/json'
#
# super(B2HandleRetriever, self).__init__(
# serializers={
# default_media_type: lambda response: jsonify(response)
# },
# default_method_media_type={
# 'GET': default_media_type,
# },
# default_media_type=default_media_type,
# **kwargs
# )
#
# def get(self, prefix, file_pid, **kwargs):
# eudat_handle_client = EUDATHandleClient()
# return eudat_handle_client.retrieve_handle_record(prefix + "/" + file_pid)
#
#
# blueprint.add_url_rule('/handle/<prefix>/<file_pid>',
# view_func=B2HandleRetriever.as_view(
# B2HandleRetriever.view_name))
class B2ShareObjectResource(ObjectResource):
"""Object item resource for B2SHARE.

Adds capability to enable user to download of a file with a JWT token,
without explicitly giving this user permission to file.
Enables to sharing of restricted files or files from draft records
to anonymous (i.e. not logged in) users.
"""

view_name = 'b2share_files_rest_object_view'

get_args = {
'version_id': fields.UUID(
location='query',
load_from='versionId',
missing=None,
),
'upload_id': fields.UUID(
location='query',
load_from='uploadId',
missing=None,
),
'uploads': fields.Raw(
location='query',
validate=invalid_subresource_validator,
),
'encoded_jwt': fields.Str(
location='query',
load_from='jwt',
missing=None,
),
}

def __init__(self, *args, **kwargs):
"""Constructor."""
super(B2ShareObjectResource, self).__init__(*args, **kwargs)


@need_permissions(
lambda self, bucket, obj, *args: obj,
'object-delete',
hidden=False, # Because get_object permission check has already run
)
def delete_object(self, bucket, obj, version_id):
"""Delete an existing object.

:param bucket: The bucket (instance or id) to get the object from.
:param obj: A :class:`invenio_files_rest.models.ObjectVersion`
instance.
:param version_id: The version ID.
:returns: A Flask response.
"""
if version_id is None:
# Create a delete marker.
with db.session.begin_nested():
ObjectVersion.delete(bucket, obj.key)
db.session.commit()
else:
# Permanently delete specific object version.
# check_permission(
# current_permission_factory(bucket, 'object-delete-version'),
# hidden=False,
#)
obj.remove()
db.session.commit()
if obj.file_id:
remove_file_data.delay(str(obj.file_id))

return self.make_response('', 204)


object_view_object_resources = B2ShareObjectResource.as_view(
'b2share_object_api',
serializers={
'application/json': json_serializer,
}
)
4 changes: 2 additions & 2 deletions webui/src/components/editfiles.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ export const EditFiles = React.createClass({
},

removeRecordFile: function(f) {
serverCache.deleteFile(this.props.record, f.key);
serverCache.deleteFile(this.props.record, f.key, f.version_id);
},

transferFileCallback(file, status, param) {
Expand Down Expand Up @@ -821,7 +821,7 @@ export const PersistentIdentifier = React.createClass({

let prefix = "";
let pid = this.props.pid;

for (const p of this.KNOWN_PREFIXES) {
if (pid.indexOf(p) === 0) {
prefix = p;
Expand Down
84 changes: 46 additions & 38 deletions webui/src/components/record.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ const B2NoteWidget = React.createClass({
const record_url = (record.links.self || "").replace('/api/records/', '/records/');

if (this.props.file) {

var file = this.props.file.toJS ? this.props.file.toJS() : this.props.file;
var pid = file.ePIC_PID;
var object_url = (file.url.indexOf('/api') == 0) ? (window.location.origin + file.url) : file.url;
Expand Down Expand Up @@ -128,7 +128,8 @@ const Record = React.createClass({
showB2NoteWindow: false,
record_notes: [],
files_notes: [],
b2noteUrl: this.props.b2noteUrl
b2noteUrl: this.props.b2noteUrl,
responseok: null
}

return state;
Expand Down Expand Up @@ -172,16 +173,21 @@ const Record = React.createClass({

componentDidMount() {
this.catchMatomoEvent(new Event("recordview"));

// this is set async in parent
if (this.state.b2noteUrl == "") {
return;
const doi = this.props.record.get("metadata").get('DOI');
if (doi && this.state.responseok === null) {
this.fetchCitations(doi);
}
window.addEventListener('message', this.catchB2NoteEvent);

this.updateNotes();
},

componentDidUpdate(prevProps) {
const doi = this.props.record.get("metadata").get('DOI');
if (doi !== prevProps.record.get("metadata").get('DOI') && this.state.responseok === null) {
this.fetchCitations(doi);
}
},


componentWillUnmount() {
window.removeEventListener('message', this.catchB2NoteEvent);
},
Expand Down Expand Up @@ -325,32 +331,32 @@ const Record = React.createClass({
files = parseThousands(recordData.get("files").size);
files_size = getTotalFileSize(recordData.get("files"));
}

} catch (err) {
files = "-";
files_size = "-";
console.error(err)
}
}
return (
<div>
<div className="statistic-row">
<p className="pid">
<span>Views</span><br /><span>{parseThousands(recordData.get("views"))}</span>
<span>Views</span><br /><span>{parseThousands(recordData.get("views"))}</span>
</p>
<p className="pid">
<span>File Downloads</span><br /><span>{parseThousands(recordData.get("file-views"))}</span>
</p>
</div>
<div className="statistic-details">
<p className="stat" title={openAccess ? "" : "Files are in embargo"}>
<span>Files</span><span>{files}</span>
<span>Files</span><span>{files}</span>
</p>
<p className="stat" title={openAccess ? "" : "Files are in embargo"}>
<span>Total Size</span><span>{files_size}</span>
<span>Total Size</span><span>{files_size}</span>
</p>
</div>
</div>

)
}

Expand Down Expand Up @@ -440,7 +446,7 @@ const Record = React.createClass({
<PersistentIdentifier pid={pid} />
</p>
}

</div>
<div className='col-sm-6 col-md-6 col-lg-4 statistic-wrapper'>
<div className="community-statistics">
Expand All @@ -458,7 +464,7 @@ const Record = React.createClass({
fixedFields: [
'community', 'titles', 'descriptions', 'creators', 'keywords', 'disciplines', 'publication_state'
],

renderShareButtons(doi){
const record = this.props.record.toJS ? this.props.record.toJS() : this.props.record;
const title = record.metadata.titles[0].title || "";
Expand All @@ -473,7 +479,7 @@ const Record = React.createClass({
style={{cursor: "pointer"}}
>
<svg viewBox="0 0 64 64" width="32" height="32"><circle cx="32" cy="32" r="32" fill="#000000"></circle><path d="M 41.116 18.375 h 4.962 l -10.8405 12.39 l 12.753 16.86 H 38.005 l -7.821 -10.2255 L 21.235 47.625 H 16.27 l 11.595 -13.2525 L 15.631 18.375 H 25.87 l 7.0695 9.3465 z m -1.7415 26.28 h 2.7495 L 24.376 21.189 H 21.4255 z" fill="white"></path></svg>
</TwitterShareButton>
</TwitterShareButton>
</span>
<span>
<FacebookShareButton
Expand All @@ -487,32 +493,34 @@ const Record = React.createClass({
</span>
</div>
)
},
},

renderCitations(doi) {

fetchCitations(doi) {
try {
const headers= {"Accept":"text/x-bibliography; style=apa"};
const headers = { "Accept": "text/x-bibliography; style=apa" };
let url = doi
if (url.includes("https") == false) { url = doi.replace('http', 'https') }
// Fixes Origin: null problems with HTTP 302 from doi.org
url = url.replace("doi.org", "data.crosscite.org")
fetch(url, {headers})
.then(response => {
if(response.ok){
this.setState({responsestatus: response.status, responseok: true})
return response.text()
}
}).then(text=>this.setState({data: text.replace(/<\/?i>/g, "")})
).catch((error) => {
console.log(error + " from "+ url)
this.setState({responsestatus: 404, responseok: false})
})
fetch(url, { headers })
.then(response => {
if (response.ok) {
this.setState({ responsestatus: response.status, responseok: true })
return response.text()
}

}).then(text => this.setState({ data: text.replace(/<\/?i>/g, "") })
).catch((error) => {
console.log(error + " from " + url)
this.setState({ responsestatus: 404, responseok: false })
})
} catch (error) {
console.log(error)
this.setState({responsestatus: 0, responseok: false})
this.setState({ responsestatus: 0, responseok: false })
}
},

renderCitations(doi) {

if(this.state.responsestatus == null){
//This if is for the cationbox not to rendering anything before it has fetched something from the DOI.
Expand Down Expand Up @@ -543,7 +551,7 @@ const Record = React.createClass({
<div className="row">
<div className="col-sm-9" > {this.state.data} </div>
<b className="col-sm-9">
Copy BibTeX
Copy BibTeX
<span style={this.props.style}>
<span><a className="btn btn-xs btn-default" onClick={onButtonClick.bind(this)} title="Copy BibTeX"><i className="fa fa-clipboard"/></a></span>
</span>
Expand Down Expand Up @@ -574,9 +582,9 @@ const Record = React.createClass({

)
}





},
Expand Down
12 changes: 8 additions & 4 deletions webui/src/data/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -243,13 +243,17 @@ class FilePoster {
return xhr;
}

delete(successFn) {
delete(successFn, version) {
if (this.xhr && this.xhr.abort) {
this.xhr.abort();
}
const completeFn = () => {this.xhr = null};
var extension = ""
if (version){
extension = "?versionId="+version
}
ajaxDelete({
url: this.url,
url: this.url + extension,
successFn: (data) => { completeFn(); successFn(data); },
completeFn: completeFn,
});
Expand Down Expand Up @@ -829,10 +833,10 @@ class ServerCache {
return this.posters.files.get(draft.get('id')).get(fileObject.name).put(fileObject, progFn);
}

deleteFile(draft, fileKey) {
deleteFile(draft, fileKey, version) {
return this.posters.files.get(draft.get('id')).get(fileKey).delete(() => {
this.getDraftFiles(draft.get('id'), true); // force fetch files
});
}, version);
}

updateDraft(id, metadata, successFn) {
Expand Down