annotot 0.2.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: be4df7aa9f2057db9fca11f7d9d502aca19f0e5f
4
- data.tar.gz: f61ed7cc230a9baa013a802d28a0a303cafcc565
2
+ SHA256:
3
+ metadata.gz: f30142a82474f3723a5d0e041a7b26bce696b1aa975d6b4dc3da81b7cc89ad43
4
+ data.tar.gz: 1f3d9a4ebb62364cb8ca4a2eea465abc6e4a461b397a61949afe4380c0c2f200
5
5
  SHA512:
6
- metadata.gz: a8d2afb622d7478d7572a5d51c7da7812a33d2ce53218b33c975f9ca535a6e8d1e1144a87e35a64cf0ec5c7540675f5485506e95f3fcae54802952e18cfedc0b
7
- data.tar.gz: c394334b580a956cada38ca7848a51f14b6780eaa566e5d09b5b3b0c81be948121986bd0aff73c96c6c62081df5141e0e52bfb445768b0bf5a64f70dd1f9dde3
6
+ metadata.gz: bfbefc765005d29ab2ec2810941692a93a2d02111701c86648ba9e08efd4b21ee6f639ebf6ea209a185777e7b8020d013b1244c34dedbaff4a3b48ee5dd39421
7
+ data.tar.gz: 0a93107a49bf745413b4f515e6ceb072d62159f9c5d47919bf615b7b22eef27e4e1af984cf10ea647a0664491552f5f58dbcd666562a0bd3dccf784bf7442059
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  Need to persist annotations quick and easily? Annotot, the original mini annotation API is for you. Don't annotate, annotot instead.
5
5
 
6
- h/t @efahy for the inspiration and name
6
+ h/t [@eefahy](https://github.com/eefahy) for the inspiration and name
7
7
 
8
8
  ![](annotot.png)
9
9
 
@@ -30,6 +30,76 @@ annotationEndpoint: {
30
30
 
31
31
  If you want to configure Annotot to receive annotations from external sources make sure that you enable CORs in the Rails application. This can be done using [rack-cors](https://github.com/cyu/rack-cors).
32
32
 
33
+ ## API
34
+
35
+ Annotot by default mounts itself at `/`. Though [this can be changed](http://guides.rubyonrails.org/engines.html#mounting-the-engine). All API endpoints are relative to its mount location.
36
+
37
+ | Path / Url | HTTP Verb | Path | Controller#Action |
38
+ | --- | --- | --- | --- |
39
+ | [annotations_path](#annotationspath) | GET | /annotations(.:format) | annotot/annotations#index {:format=>:json} |
40
+ | | POST | /annotations(.:format) | annotot/annotations#create {:format=>:json} |
41
+ | [lists_annotations_path](#listsannotationspath) | GET | /annotations/lists(.:format) | annotot/annotations#lists {:format=>:json} |
42
+ | [annotation_path](#annotationpath) | PATCH | /annotations/:id(.:format) | annotot/annotations#update {:format=>:json} |
43
+ | | PUT | /annotations/:id(.:format) | annotot/annotations#update {:format=>:json}
44
+ | | DELETE | /annotations/:id(.:format) | annotot/annotations#destroy {:format=>:json}
45
+
46
+ ---
47
+
48
+ ### annotations_path
49
+ `GET` - Return annotations for a given canvas
50
+
51
+ Parameters:
52
+
53
+ | Name | Required? | Description |
54
+ | --- | --- | --- |
55
+ | uri | yes | Canvas uri for which to return annotations
56
+
57
+ `POST` - Create a new annotation
58
+
59
+ Parameters:
60
+
61
+ | Name | Required? | Description |
62
+ | --- | --- | --- |
63
+ | annotation | yes | object containing creation parameters
64
+ | annotation.uuid | no | uuid for annotation
65
+ | annotation.data | no | annotation body data as string
66
+ | annotation.canvas | no | canvas to place the annotation on
67
+
68
+ ---
69
+
70
+ ### lists_annotations_path
71
+ `GET` - Return an AnnotationList of annotations for a given canvas
72
+
73
+ Parameters:
74
+
75
+ | Name | Required? | Description |
76
+ | --- | --- | --- |
77
+ | uri | yes | Canvas uri for which to return annotations
78
+
79
+ ---
80
+
81
+ ### annotation_path
82
+ `PATCH`, `PUT` - Update an annotation
83
+
84
+ Parameters:
85
+
86
+ | Name | Required? | Description |
87
+ | --- | --- | --- |
88
+ | id | yes | Canvas uri or Rails ActiveRecord id for annotation to update
89
+ | annotation | yes | object containing creation parameters
90
+ | annotation.uuid | no | uuid for annotation
91
+ | annotation.data | no | annotation body data as string
92
+ | annotation.canvas | no | canvas to place the annotation on
93
+
94
+ `DELETE` - Delete an annotation
95
+
96
+ Parameters:
97
+
98
+ | Name | Required? | Description |
99
+ | --- | --- | --- |
100
+ | id | yes | Canvas uri or Rails ActiveRecord id for annotation to delete
101
+
102
+
33
103
  ## Installation
34
104
  Add this line to your application's Gemfile:
35
105
 
@@ -31,7 +31,7 @@
31
31
  search: function(options, successCallback, errorCallback) {
32
32
  var _this = this;
33
33
  _this.annotationsList = [];
34
- options.format = 'json'
34
+ options.format = 'json';
35
35
 
36
36
  jQuery.ajax({
37
37
  url: _this.endpoint,
@@ -65,7 +65,7 @@
65
65
  deleteAnnotation: function(annotationID, successCallback, errorCallback) {
66
66
  var _this = this;
67
67
  jQuery.ajax({
68
- url: _this.endpoint + '/' + annotationID,
68
+ url: _this.endpoint + '/' + encodeURIComponent(annotationID),
69
69
  type: 'DELETE',
70
70
  dataType: 'json',
71
71
  headers: {
@@ -89,11 +89,11 @@
89
89
  update: function(oaAnnotation, successCallback, errorCallback) {
90
90
  var _this = this;
91
91
  var annotation = _this.getAnnotationInEndpoint(oaAnnotation);
92
-
93
- var annotationID = annotation.annotation['uuid'];
92
+
93
+ var annotationID = annotation.annotation.uuid;
94
94
 
95
95
  jQuery.ajax({
96
- url: _this.endpoint + '/' + annotationID,
96
+ url: _this.endpoint + '/' + encodeURIComponent(annotationID),
97
97
  type: 'PATCH',
98
98
  dataType: 'json',
99
99
  headers: {
@@ -158,13 +158,15 @@
158
158
  // Converts OA Annotation to endpoint format
159
159
  getAnnotationInEndpoint: function(oaAnnotation) {
160
160
  // Generate a new uuid if one is not present
161
- if (oaAnnotation['@id'] == null) {
161
+ // condition needs to be set to check for "falsy" values
162
+ // !oaAnnotation['@id'] should match null and undefined
163
+ if (!oaAnnotation['@id']) {
162
164
  oaAnnotation["@id"] = Mirador.genUUID();
163
165
  }
164
-
166
+
165
167
  canvas = oaAnnotation.on[0].full;
166
168
  var newAnno = jQuery.extend({}, oaAnnotation);
167
- delete newAnno.endpoint
169
+ delete newAnno.endpoint;
168
170
  return {
169
171
  annotation: {
170
172
  uuid: oaAnnotation["@id"],
@@ -2,18 +2,26 @@ require_dependency "annotot/application_controller"
2
2
 
3
3
  module Annotot
4
4
  class AnnotationsController < ApplicationController
5
- before_action :set_annotation, only: %i[update destroy]
5
+ before_action :set_annotation, only: %i[update destroy show]
6
6
 
7
7
  # GET /annotations
8
8
  def index
9
9
  @annotations = Annotation.where(canvas: annotation_search_params)
10
10
  end
11
11
 
12
+ # GET /annotations
13
+ def show; end
14
+
12
15
  # Get /annotations/lists
13
16
  def lists
14
17
  @annotations = Annotation.where(canvas: annotation_search_params)
15
18
  end
16
19
 
20
+ # Get /annotations/pages
21
+ def pages
22
+ @annotations = Annotation.where(canvas: annotation_search_params)
23
+ end
24
+
17
25
  # POST /annotations
18
26
  def create
19
27
  @annotation = Annotation.new(annotation_params)
@@ -50,7 +58,9 @@ module Annotot
50
58
  private
51
59
 
52
60
  def set_annotation
53
- @annotation = Annotation.find_by(id: params[:id]) || Annotation.find_by(uuid: params[:id])
61
+ @annotation = Annotot::Annotation.retrieve_by_id_or_uuid(
62
+ CGI.unescape(params[:id])
63
+ )
54
64
  raise ActiveRecord::RecordNotFound unless @annotation.present?
55
65
  end
56
66
 
@@ -59,7 +69,7 @@ module Annotot
59
69
  end
60
70
 
61
71
  def annotation_search_params
62
- params.require(:uri)
72
+ CGI.unescape(params.require(:uri))
63
73
  end
64
74
  end
65
75
  end
@@ -1,5 +1,5 @@
1
1
  module Annotot
2
2
  class ApplicationController < ActionController::Base
3
- protect_from_forgery
3
+ protect_from_forgery with: :exception, unless: -> { request.format.json? }
4
4
  end
5
5
  end
@@ -4,5 +4,17 @@ module Annotot
4
4
  validates :canvas, presence: true
5
5
 
6
6
  serialize :data, JSON
7
+
8
+ ##
9
+ # Used to differentiate between a numeric id and a uuid. Rails will trim a
10
+ # uuid with leading numbers eg '123a'.to_i => 123
11
+ # @param [String] identifier
12
+ def self.retrieve_by_id_or_uuid(identifier)
13
+ if identifier =~ /\A\d+\z/
14
+ find_by(id: identifier)
15
+ else
16
+ find_by(uuid: identifier)
17
+ end
18
+ end
7
19
  end
8
20
  end
@@ -0,0 +1,4 @@
1
+ json.set! '@context', 'http://iiif.io/api/presentation/3/context.json'
2
+ json.set! 'id', request.original_url
3
+ json.set! 'type', 'AnnotationPage'
4
+ json.items(@annotations.map { |a| JSON.parse(a.data) })
@@ -0,0 +1 @@
1
+ <%= JSON.parse(@annotation.data).to_json.html_safe %>
@@ -1,7 +1,8 @@
1
1
  Annotot::Engine.routes.draw do
2
- resources :annotations, except: %i[new edit show], defaults: { format: :json } do
2
+ resources :annotations, path: '/', except: %i[new edit], defaults: { format: :json } do
3
3
  collection do
4
4
  get 'lists'
5
+ get 'pages'
5
6
  end
6
7
  end
7
8
  end
@@ -1,3 +1,3 @@
1
1
  module Annotot
2
- VERSION = '0.2.1'
2
+ VERSION = '0.7.0'
3
3
  end
@@ -5,5 +5,10 @@ module Annotot
5
5
  def add_routes
6
6
  route "mount Annotot::Engine => '/'"
7
7
  end
8
+
9
+ def run_annotot_migrations
10
+ rake 'annotot:install:migrations'
11
+ rake 'db:migrate'
12
+ end
8
13
  end
9
14
  end
@@ -1,4 +1,59 @@
1
- # desc "Explaining what the task does"
2
- # task :annotot do
3
- # # Task goes here
4
- # end
1
+ require 'json'
2
+ require 'pathname'
3
+
4
+ namespace :annotot do
5
+ desc 'Import annotation list from path'
6
+ task :import_file, [:url_path] => :environment do |_t, args|
7
+ path = Pathname.new(args.url_path)
8
+ puts "Importing annotations from #{path}"
9
+ anno_list = JSON.parse(File.read(path))
10
+ puts "#{anno_list['resources'].length} resources found"
11
+ touch_count = 0
12
+ anno_list['resources'].map do |resource|
13
+ uuid = resource['@id']
14
+ selector = resource['on'].first['selector']
15
+ canvas = resource['on'].first['full']
16
+
17
+ ##
18
+ # Annotation appears to be Mirador 2.6.0 compatible
19
+ if resource['on'].first['@type'] == 'oa:SpecificResource' && selector['@type'] == 'oa:Choice'
20
+ if selector['default']['@type'] == 'oa:FragmentSelector' && selector['item']['@type'] == 'oa:SvgSelector'
21
+ touch_count += 1
22
+ end
23
+ end
24
+
25
+ ##
26
+ # Annotation needs to be updated to a single fragment selector
27
+ if resource['on'].first['@type'] == 'oa:SpecificResource' && selector['@type'] == 'oa:FragmentSelector'
28
+ new_on = "#{canvas}##{selector['value']}"
29
+ resource['on'] = new_on
30
+ touch_count += 1
31
+ end
32
+
33
+ ##
34
+ # Annotation only has an SvgSelector and in an non-compliant format
35
+ if selector['@type'] == 'oa:SvgSelector'
36
+ item = selector.deep_dup
37
+ # Fix missing quotes in SVG
38
+ item['value'] = item['value'].gsub('xmlns=http://www.w3.org/2000/svg', 'xmlns="http://www.w3.org/2000/svg"')
39
+ selector['@type'] = 'oa:Choice'
40
+ selector['default'] = {
41
+ '@type' => 'oa:FragmentSelector',
42
+ 'value' => 'xywh=0,0,0,0'
43
+ }
44
+ selector.delete('value')
45
+ selector['item'] = item
46
+ resource['on'].first['selector'] = selector.deep_dup
47
+ touch_count += 1
48
+ end
49
+
50
+ anno_json = resource.to_json
51
+ anno = Annotot::Annotation.find_or_create_by(uuid: uuid)
52
+ anno.update!(
53
+ canvas: canvas,
54
+ data: anno_json
55
+ )
56
+ end
57
+ puts "Updated #{touch_count} annotations"
58
+ end
59
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: annotot
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jack Reed
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-02-28 00:00:00.000000000 Z
11
+ date: 2020-07-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 5.1.4
19
+ version: '5.1'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 5.1.4
26
+ version: '5.1'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: engine_cart
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -117,6 +117,8 @@ files:
117
117
  - app/models/annotot/application_record.rb
118
118
  - app/views/annotot/annotations/index.json.erb
119
119
  - app/views/annotot/annotations/lists.json.jbuilder
120
+ - app/views/annotot/annotations/pages.json.jbuilder
121
+ - app/views/annotot/annotations/show.json.erb
120
122
  - app/views/layouts/annotot/application.html.erb
121
123
  - config/routes.rb
122
124
  - db/migrate/20180215183225_create_annotot_annotations.rb
@@ -144,8 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
144
146
  - !ruby/object:Gem::Version
145
147
  version: '0'
146
148
  requirements: []
147
- rubyforge_project:
148
- rubygems_version: 2.6.11
149
+ rubygems_version: 3.1.2
149
150
  signing_key:
150
151
  specification_version: 4
151
152
  summary: Annotot. Open annotations in Rails.