Skip to content

Commit

Permalink
IIIF #5 - Adding ability to download resources; Adding preview and th…
Browse files Browse the repository at this point in the history
…umbnail URL helper methods
  • Loading branch information
dleadbetter committed Jul 13, 2022
1 parent 6ad1d20 commit e618a8c
Show file tree
Hide file tree
Showing 16 changed files with 134 additions and 42 deletions.
11 changes: 9 additions & 2 deletions app/models/concerns/attachable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,21 @@ def generate_url_method(name)
attachment = self.send(name)
return nil unless attachment.attached?

"#{ENV['IIIF_HOST']}/iiif/3/#{attachment.key}/full/500,/0/default.jpg"
"#{ENV['IIIF_HOST']}/iiif/3/#{attachment.key}/full/max/0/default.jpg"
end

define_method("#{name}_download_url") do
attachment = self.send(name)
return nil unless attachment.attached?

attachment.service_url({ disposition: 'attachment' })
attachment.url(disposition: 'attachment')
end

define_method("#{name}_preview_url") do
attachment = self.send(name)
return nil unless attachment.attached?

"#{ENV['IIIF_HOST']}/iiif/3/#{attachment.key}/full/500,/0/default.jpg"
end

define_method("#{name}_thumbnail_url") do
Expand Down
46 changes: 27 additions & 19 deletions app/models/resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,34 +9,42 @@ class Resource < ApplicationRecord
allow_params :project_id, :name, :metadata, :content

# Callbacks
after_create_commit :convert_to_tif
after_create_commit :convert

# ActiveStorage
has_one_attached :content
has_one_attached :content_converted

def content_url
return super unless content.image? && content_converted.attached?

"#{ENV['IIIF_HOST']}/iiif/3/#{content_converted.key}/full/max/0/default.jpg"
end

def content_preview_url
return super unless content.image? && content_converted.attached?

"#{ENV['IIIF_HOST']}/iiif/3/#{content_converted.key}/full/500,/0/default.jpg"
end

def content_thumbnail_url
return super unless content.image? && content_converted.attached?

"#{ENV['IIIF_HOST']}/iiif/3/#{content_converted.key}/square/250,250/0/default.jpg"
end

private

def convert_to_tif
def convert
return unless content.attached? && content.image?

content.open do |file|
output_file = "#{File.basename(file.path, '.*')}.tif"
output_path = File.join(File.dirname(file.path), output_file)

convert = MiniMagick::Tool::Convert.new
convert << file.path
convert << '-define'
convert << 'tiff:tile-geometry=1024x1024'
convert << '-depth'
convert << '8'
convert << '-compress'
convert << 'jpeg'
convert << "ptif:#{output_path}"
convert.call

content.attach(
io: File.open(output_path),
filename: output_file,
filepath = Images::Convert.to_tiff(file)
filename = Images::Convert.filename(content.filename.to_s, 'tif')

content_converted.attach(
io: File.open(filepath),
filename: filename,
content_type: 'image/tiff'
)
end
Expand Down
4 changes: 2 additions & 2 deletions app/serializers/projects_serializer.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
class ProjectsSerializer < BaseSerializer
index_attributes :id, :uid, :name, :description, :avatar_url, :avatar_thumbnail_url, :organization_id, organization: OrganizationsSerializer
show_attributes :id, :uid, :name, :description, :api_key, :avatar_url, :organization_id, organization: OrganizationsSerializer
index_attributes :id, :uid, :name, :description, :avatar_thumbnail_url, :organization_id, organization: OrganizationsSerializer
show_attributes :id, :uid, :name, :description, :api_key, :avatar_url, :avatar_preview_url, :organization_id, organization: OrganizationsSerializer
end
4 changes: 2 additions & 2 deletions app/serializers/resources_serializer.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
class ResourcesSerializer < BaseSerializer
index_attributes :id, :name, :metadata, :exif, :project_id, :content_url, :content_thumbnail_url
show_attributes :id, :name, :metadata, :exif, :project_id, :content_url
index_attributes :id, :name, :metadata, :exif, :project_id, :content_thumbnail_url
show_attributes :id, :name, :metadata, :exif, :project_id, :content_url, :content_preview_url, :content_download_url
end
4 changes: 2 additions & 2 deletions app/serializers/users_serializer.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
class UsersSerializer < BaseSerializer
index_attributes :id, :name, :email, :admin, :avatar_url, :avatar_thumbnail_url
show_attributes :id, :name, :email, :admin, :avatar_url, user_organizations: [:id, :organization_id, organization: OrganizationsSerializer]
index_attributes :id, :name, :email, :admin, :avatar_thumbnail_url
show_attributes :id, :name, :email, :admin, :avatar_url, :avatar_preview_url, user_organizations: [:id, :organization_id, organization: OrganizationsSerializer]
end
25 changes: 25 additions & 0 deletions app/services/images/convert.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
module Images
class Convert
def self.filename(path, extension)
"#{File.basename(path, '.*')}.#{extension}"
end

def self.to_tiff(file)
output_file = "#{File.basename(file.path, '.*')}.tif"
output_path = File.join(File.dirname(file.path), output_file)

convert = MiniMagick::Tool::Convert.new
convert << file.path
convert << '-define'
convert << 'tiff:tile-geometry=1024x1024'
convert << '-depth'
convert << '8'
convert << '-compress'
convert << 'jpeg'
convert << "ptif:#{output_path}"
convert.call

output_path
end
end
end
6 changes: 6 additions & 0 deletions client/src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"Common": {
"buttons": {
"cancel": "Cancel",
"download": "Download",
"remove": "Remove",
"save": "Save",
"upload": "Upload"
Expand Down Expand Up @@ -92,6 +93,11 @@
"resources": "Resources"
}
},
"Resource": {
"labels": {
"content": "Content"
}
},
"Routes": {
"organizationDetails": "Organization Edit",
"organizations": "Organizations",
Expand Down
9 changes: 7 additions & 2 deletions client/src/pages/Project.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const ProjectForm = withTranslation()((props) => {
label={props.t('Common.labels.avatar')}
>
<LazyImage
preview={props.item.avatar_url}
preview={props.item.avatar_preview_url}
src={props.item.avatar_url}
size='medium'
>
Expand All @@ -50,7 +50,12 @@ const ProjectForm = withTranslation()((props) => {
icon='cloud upload'
onSelection={(files) => {
const file = _.first(files);
props.onSetState({ avatar: file, avatar_url: URL.createObjectURL(file) });
const url = URL.createObjectURL(file);
props.onSetState({
avatar: file,
avatar_url: url,
avatar_preview_url: url
});
}}
/>
)}
Expand Down
7 changes: 6 additions & 1 deletion client/src/pages/Projects.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ const Projects: ComponentType<any> = () => {
onDelete={(project) => ProjectsService.delete(project)}
renderDescription={(project) => project.description}
renderHeader={(project) => project.name}
renderImage={(project) => <LazyImage dimmable={false} src={project.avatar_thumbnail_url} />}
renderImage={(project) => (
<LazyImage
dimmable={false}
src={project.avatar_thumbnail_url}
/>
)}
renderMeta={(project) => project.organization && project.organization.name}
/>
);
Expand Down
29 changes: 23 additions & 6 deletions client/src/pages/Resource.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// @flow

import { FileInputButton, LazyImage } from '@performant-software/semantic-components';
import { DownloadButton, FileInputButton, LazyImage } from '@performant-software/semantic-components';
import React, { type ComponentType, useEffect } from 'react';
import { withTranslation } from 'react-i18next';
import { useParams } from 'react-router-dom';
import { Button, Form } from 'semantic-ui-react';
import { Link, useParams } from 'react-router-dom';
import { Button, Form, Icon } from 'semantic-ui-react';
import _ from 'underscore';
import ResourcesService from '../services/Resources';
import withEditPage from '../hooks/EditPage';
Expand All @@ -29,21 +29,38 @@ const ProjectForm = withTranslation()((props) => {
label={props.t('Resource.labels.content')}
>
<LazyImage
preview={props.item.content_url}
preview={props.item.content_preview_url}
src={props.item.content_url}
size='medium'
>
{ !props.item.content_url && (
<FileInputButton
color='green'
color='orange'
content={props.t('Common.buttons.upload')}
icon='cloud upload'
onSelection={(files) => {
const file = _.first(files);
props.onSetState({ content: file, content_url: URL.createObjectURL(file) });
const url = URL.createObjectURL(file);
props.onSetState({
content: file,
content_url: url,
content_preview_url: url
});
}}
/>
)}
{ props.item.content_download_url && (
<a
className='ui button green'
download={props.item.name}
href={props.item.content_download_url}
>
<Icon
name='cloud download'
/>
{ props.t('Common.buttons.download') }
</a>
)}
{ props.item.content_url && (
<Button
color='red'
Expand Down
12 changes: 9 additions & 3 deletions client/src/pages/Resources.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ const Resources: ComponentType<any> = () => {
<ItemList
actions={[{
name: 'edit',
label: null,
onClick: (item) => navigate(item.id.toString())
}, {
name: 'delete'
name: 'delete',
label: null
}]}
addButton={{
location: 'top',
Expand All @@ -61,9 +63,13 @@ const Resources: ComponentType<any> = () => {
onDelete={(resource) => ResourcesService.delete(resource)}
perPageOptions={[10, 25, 50, 100]}
renderHeader={(resource) => resource.name}
renderImage={(resource) => <LazyImage dimmable={false} src={resource.content_thumbnail_url} />}
renderImage={(resource) => (
<LazyImage
dimmable={false}
src={resource.content_thumbnail_url}
/>
)}
renderMeta={() => ''}
renderDescription={() => <div>Test</div>}
saved={location.state && location.state.saved}
sort={[{
key: 'name',
Expand Down
9 changes: 7 additions & 2 deletions client/src/pages/User.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const UserForm = withTranslation()((props: EditContainerProps & Translateable) =
label={props.t('Common.labels.avatar')}
>
<LazyImage
preview={props.item.avatar_url}
preview={props.item.avatar_preview_url}
src={props.item.avatar_url}
size='medium'
>
Expand All @@ -51,7 +51,12 @@ const UserForm = withTranslation()((props: EditContainerProps & Translateable) =
icon='cloud upload'
onSelection={(files) => {
const file = _.first(files);
props.onSetState({ avatar: file, avatar_url: URL.createObjectURL(file) });
const url = URL.createObjectURL(file);
props.onSetState({
avatar: file,
avatar_url: url,
avatar_preview_url: url
});
}}
/>
)}
Expand Down
7 changes: 6 additions & 1 deletion client/src/pages/Users.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ const Users: ComponentType<any> = withTranslation()(() => {
onLoad={(params) => UsersService.fetchAll(params)}
onDelete={(user) => UsersService.delete(user)}
renderHeader={(user) => user.name}
renderImage={(user) => <LazyImage dimmable={false} src={user.avatar_thumbnail_url} />}
renderImage={(user) => (
<LazyImage
dimmable={false}
src={user.avatar_thumbnail_url}
/>
)}
renderMeta={(user) => user.email}
/>
);
Expand Down
1 change: 1 addition & 0 deletions client/src/types/Project.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ export type Project = {
organization: Organization,
avatar_url: string,
avatar_download_url: string,
avatar_preview_url: string,
avatar_thumbnail_url: string
};
1 change: 1 addition & 0 deletions client/src/types/Resource.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export type Resource = {
name: string,
content_url: string,
content_download_url: string,
content_preview_url: string,
content_thumbnail_url: string,
exif: any,
metadata: any,
Expand Down
1 change: 1 addition & 0 deletions client/src/types/User.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export type User = {
password_confirmation: string,
avatar_url: string,
avatar_download_url: string,
avatar_preview_url: string,
avatar_thumbnail_url: string,
user_organizations: Array<UserOrganization>
};

0 comments on commit e618a8c

Please sign in to comment.