annotot 0.2.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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.