scrivito_rich_snippet_widget 0.1.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.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +129 -0
  3. data/Rakefile +35 -0
  4. data/app/assets/images/widgets/scrivito_rich_snippet_widget.png +0 -0
  5. data/app/assets/javascripts/scrivito_rich_snippet_widget.js +41 -0
  6. data/app/models/rich_snippet/creative_work.rb +88 -0
  7. data/app/models/rich_snippet/event.rb +70 -0
  8. data/app/models/rich_snippet/job_posting.rb +47 -0
  9. data/app/models/rich_snippet/offer.rb +41 -0
  10. data/app/models/rich_snippet/organization.rb +33 -0
  11. data/app/models/rich_snippet/person.rb +70 -0
  12. data/app/models/rich_snippet/postal_address.rb +33 -0
  13. data/app/models/rich_snippet/product.rb +40 -0
  14. data/app/models/rich_snippet/recipe.rb +69 -0
  15. data/app/models/rich_snippet/thing.rb +43 -0
  16. data/app/models/rich_snippet_widget.rb +20 -0
  17. data/app/views/rich_snippet/creative_work/_details.html.erb +136 -0
  18. data/app/views/rich_snippet/creative_work/details.html.erb +1 -0
  19. data/app/views/rich_snippet/event/_details.html.erb +39 -0
  20. data/app/views/rich_snippet/event/details.html.erb +1 -0
  21. data/app/views/rich_snippet/job_posting/_details.html.erb +59 -0
  22. data/app/views/rich_snippet/job_posting/details.html.erb +1 -0
  23. data/app/views/rich_snippet/offer/_details.html.erb +35 -0
  24. data/app/views/rich_snippet/offer/details.html.erb +1 -0
  25. data/app/views/rich_snippet/organization/_details.html.erb +39 -0
  26. data/app/views/rich_snippet/organization/details.html.erb +1 -0
  27. data/app/views/rich_snippet/person/_details.html.erb +79 -0
  28. data/app/views/rich_snippet/person/details.html.erb +1 -0
  29. data/app/views/rich_snippet/postal_address/_details.html.erb +39 -0
  30. data/app/views/rich_snippet/postal_address/details.html.erb +1 -0
  31. data/app/views/rich_snippet/product/_details.html.erb +55 -0
  32. data/app/views/rich_snippet/product/details.html.erb +1 -0
  33. data/app/views/rich_snippet/recipe/_details.html.erb +91 -0
  34. data/app/views/rich_snippet/recipe/details.html.erb +1 -0
  35. data/app/views/rich_snippet/thing/_details.html.erb +17 -0
  36. data/app/views/rich_snippet_widget/details.html.erb +11 -0
  37. data/app/views/rich_snippet_widget/show.html.erb +21 -0
  38. data/app/views/rich_snippet_widget/thumbnail.html.erb +3 -0
  39. data/config/routes.rb +2 -0
  40. data/lib/scrivito_rich_snippet_widget.rb +5 -0
  41. data/lib/scrivito_rich_snippet_widget/engine.rb +5 -0
  42. data/lib/scrivito_rich_snippet_widget/version.rb +3 -0
  43. data/lib/tasks/scrivito_rich_snippet_widget_tasks.rake +4 -0
  44. metadata +100 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f51102a6c203e57414807e885c092fb2bc404b9e
4
+ data.tar.gz: f693bec6f0c865338ea4bc65ec1a3b463e4dec3e
5
+ SHA512:
6
+ metadata.gz: f78542fe3ae6372ae6f7ac9808f3d0b9c786d52ca9be3b5a5e77e69bd911130ca0bd1fd8b7f77f87a1c7dac27f0dcd3c2b59cc17c85e7f513c7b0ecb47c157f1
7
+ data.tar.gz: 6933e82d5906f753a829e4b0a9c34831d18426a4154d16a5f005ae5290f879a04c77a4373459ce0b5aa903a7c6eb3157ab8bc1535e0a0880ece4886188893fc7
data/README.md ADDED
@@ -0,0 +1,129 @@
1
+ # ScrivitoRichSnippetWidget
2
+ Add structured data to you page. Definitions are from https://schema.org
3
+
4
+ ## Installation
5
+ Add this line to your application's Gemfile:
6
+
7
+ ```ruby
8
+ gem 'scrivito_rich_snippet_widget'
9
+ ```
10
+
11
+ And then execute:
12
+ ```bash
13
+ $ bundle
14
+ ```
15
+
16
+ To make the content browser filter available add this to your content browser filter js:
17
+
18
+ Add this line to your JavaScript manifest **before** you load your content browser filter:
19
+
20
+ ```js
21
+ //= require scrivito_rich_snippet_widget
22
+ ```
23
+
24
+ ```js
25
+ scrivito.content_browser.filters(filter) {
26
+ if(filter.rich_snippet_filter) {
27
+ rich_snippet_filter(filter.rich_snippet_filter)
28
+ } else if (your filters) {
29
+ // ... add your filters here
30
+ } else {
31
+ '_obj_class': {
32
+ options: {
33
+ // ... add your type defs here
34
+ rich_snippets: {
35
+ title: 'Rich Snippets',
36
+ options: {
37
+ rich_snippet_filter()
38
+ }
39
+ }
40
+ }
41
+ }
42
+ }
43
+ }
44
+ ```
45
+
46
+ ## Add your own types
47
+
48
+ Create a modle at `/model/rich_snippet/new_type.rb` and add your attribute in this scheme:
49
+
50
+ ```ruby
51
+ module RichSnippets
52
+ class NewType < RichSnippet::Thing
53
+ attribute my_attribute, :string
54
+ attribute person, :refernce
55
+ attribute children, :referencelist
56
+ # ... more attributes
57
+
58
+ def to_json
59
+ {
60
+ myAttribute: my_attribute,
61
+ person: person ? person.to_json : nil, # render the json for another rich snippet
62
+ children: array_json(children), # render an array of other rich snippets
63
+ # ... definition for more attributes
64
+ }
65
+ end
66
+
67
+ # optional method to add warnings rendered by the widget
68
+ def warnings
69
+ warns = []
70
+ warns << "this attribute should be set!" if my_attribute.blank?
71
+ return warns
72
+ end
73
+ end
74
+ end
75
+ ```
76
+
77
+ The create the details view. You need to files:
78
+
79
+ ```ruby
80
+ # add rich_snippet/new_type/details.html.erb
81
+ <%= render 'rich_snippet/new_type/details', obj: @obj %>
82
+ ```
83
+
84
+ ```ruby
85
+ # add rich_snippet/new_type/_details.html.erb
86
+ <%= render 'rich_snippet/thing/details', obj: obj, url_is_mandatory: false %>
87
+
88
+ <%= scrivito_details_for "Event attributes" do %>
89
+ <%= scrivito_details_for 'My attribute' do %>
90
+ <%= scrivito_tag :div, obj, :my_attribute %>
91
+ <% end %>
92
+
93
+ <%= scrivito_details_for 'Person do %>
94
+ <%= scrivito_tag :div, obj, :person, data: {scrivito_editors_filter_context: {rich_snippet_filter: ['Person', 'Organizazion']}} %>
95
+ <% end %>
96
+
97
+ <%= scrivito_details_for 'Children' do %>
98
+ <%= scrivito_tag :div, obj, :children, data: {scrivito_editors_filter_context: {rich_snippet_filter: ['Person']}} %>
99
+ <% end %>
100
+
101
+ ... More attributes ...
102
+ <% end %>
103
+ ```
104
+
105
+ The filter definition like `data_scrivito_editors_filter_context={rich_snippet_filter: ['Person']}` can be used to show only the defined filters e.g. Person in this example.
106
+
107
+ In your contentbrowser filters, add the new types by using:
108
+
109
+ ```js
110
+ scrivito.content_browser.filters(filter) {
111
+ if(filter.rich_snippet_filter) {
112
+ rich_snippet_filter(filter.rich_snippet_filter, ['NewType','AnotherType'])
113
+ } else if (your filters) {
114
+ // ... add your filters here
115
+ } else {
116
+ '_obj_class': {
117
+ options: {
118
+ // ... add your type defs here
119
+ rich_snippets: {
120
+ title: 'Rich Snippets',
121
+ options: {
122
+ rich_snippet_filter(undefined, ['NewType','AnotherType'])
123
+ }
124
+ }
125
+ }
126
+ }
127
+ }
128
+ }
129
+ ```
data/Rakefile ADDED
@@ -0,0 +1,35 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'ScrivitoRichSnippetWidget'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+
18
+
19
+ load 'rails/tasks/statistics.rake'
20
+
21
+
22
+
23
+ require 'bundler/gem_tasks'
24
+
25
+ require 'rake/testtask'
26
+
27
+ Rake::TestTask.new(:test) do |t|
28
+ t.libs << 'lib'
29
+ t.libs << 'test'
30
+ t.pattern = 'test/**/*_test.rb'
31
+ t.verbose = false
32
+ end
33
+
34
+
35
+ task default: :test
@@ -0,0 +1,41 @@
1
+ Array.prototype.unique = function() {
2
+ var a = this.concat();
3
+ for(var i=0; i<a.length; ++i) {
4
+ for(var j=i+1; j<a.length; ++j) {
5
+ if(a[i] === a[j])
6
+ a.splice(j--, 1);
7
+ }
8
+ }
9
+ return a;
10
+ };
11
+
12
+ function rich_snippet_filter(filter, app_types=[]) {
13
+ var types = ['Event', 'Offer', 'PostalAddress', 'Person', 'Organization', 'Product', 'Recipe', 'CreativeWork', 'JobPosting'].concat(app_types).unique();
14
+
15
+ if(filter == undefined) {
16
+ return rich_snippet_all_options(types);
17
+ }
18
+ else {
19
+ return {
20
+ '_obj_class': {
21
+ 'field': '_obj_class',
22
+ 'options': rich_snippet_all_options(filter == 'all' ? types : filter.concat(app_types).unique())
23
+ }
24
+ }
25
+ }
26
+ }
27
+
28
+ function rich_snippet_all_options(types) {
29
+ var h = {}
30
+ types.forEach(function(e) {
31
+ h[e] = {
32
+ 'value': "RichSnippet::" + e,
33
+ 'enable_create': true,
34
+ 'title': e,
35
+ 'preset': {
36
+ '_obj_class': "RichSnippet::" + e
37
+ }
38
+ }
39
+ });
40
+ return h;
41
+ }
@@ -0,0 +1,88 @@
1
+ module RichSnippet
2
+ class CreativeWork < Thing
3
+ attribute :work_type, :enum, values: ['Book','Game','Movie','Photograph','Painting','Serie','WebPage','MusicComposition']
4
+ attribute :about, :string
5
+ attribute :author, :reference
6
+ attribute :contributor, :reference
7
+ attribute :copyright_holder, :reference
8
+ attribute :copyright_year, :integer
9
+ attribute :date_created, :date
10
+ attribute :date_published, :date
11
+ attribute :genre, :string
12
+ attribute :in_language, :string
13
+ attribute :is_accessible_for_free, :enum, values: ['Yes', 'No'], default: 'No'
14
+ attribute :is_based_on, :reference
15
+ attribute :is_family_friendly, :enum, values: ['Yes', 'No'], default: 'Yes'
16
+ attribute :license, :string
17
+ attribute :offers, :referencelist
18
+ attribute :position, :integer
19
+ attribute :publisher, :reference
20
+ attribute :thumbnail_url, :string
21
+ attribute :typical_age_range, :string
22
+
23
+ #Book
24
+ attribute :illustrator, :reference
25
+ attribute :isbn, :string
26
+ attribute :number_of_page, :integer
27
+
28
+ #Movie
29
+ attribute :actors, :referencelist
30
+ attribute :director, :reference
31
+ attribute :duration, :integer
32
+
33
+ #WebPage
34
+ attribute :breadcrumb, :string
35
+
36
+ #MusicComposition
37
+ attribute :composer, :reference
38
+ attribute :lyricist, :reference
39
+ attribute :lyrics, :string
40
+
41
+ def to_json(render_childs = false)
42
+ json = {
43
+ "@context": "http://schema.org",
44
+ "@type": "CreativeWork",
45
+ name: name,
46
+ description: description,
47
+ image: image ? image.binary_url : nil,
48
+ url: url,
49
+ about: about,
50
+ author: author ? author.to_json : nil,
51
+ contributor: contributor ? contributor.to_json : nil,
52
+ copyrightHolder: copyright_holder ? copyright_holder.to_json : nil,
53
+ copyrightYear: copyright_year,
54
+ dateCreated: date_created,
55
+ datePublished: date_published,
56
+ genre: genre,
57
+ inLanguage: in_language,
58
+ isAccessibleForFree: is_accessible_for_free == 'Yes',
59
+ isBasedOn: is_based_on ? is_based_on.to_json: nil,
60
+ isFamilyFriendly: is_family_friendly == 'Yes',
61
+ license: license,
62
+ offers: array_json(offers),
63
+ position: position,
64
+ publisher: publisher ? publisher.to_json : nil,
65
+ thumbnailUrl: thumbnail_url,
66
+ typicalAgeRange: typical_age_range,
67
+ }
68
+
69
+ if work_type == 'Book'
70
+ json[:illustrator] = illustrator ? illustrator.to_json : nil
71
+ json[:isbn] = isbn
72
+ json[:numberOfPage] = number_of_page
73
+ elsif work_type == 'Movie'
74
+ json[:actors] = array_json(actors)
75
+ json[:director] = director ? director.to_json : nil
76
+ json[:duration] = duration
77
+ elsif work_type == 'WebPage'
78
+ json[:breadcrumb] = Obj.repson_to?('page_breadcrumb') ? obj.page_breadcrumb : breadcrumb
79
+ elsif work_type == 'MusicComposition'
80
+ json[:composer] = composer ? composer.to_json : nil
81
+ json[:lyricist] = lyricist ? lyricist.to_json : nil
82
+ json[:lyrics] = lyrics
83
+ end
84
+
85
+ return json.delete_if { |k, v| !v.present? }
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,70 @@
1
+ module RichSnippet
2
+ class Event < Thing
3
+ attribute :start_date, :date
4
+ attribute :end_date, :date
5
+ attribute :organizer, :reference
6
+ attribute :in_language, :string
7
+ attribute :offers, :referencelist, only: Offer
8
+ attribute :location, :reference
9
+ attribute :performers, :referencelist
10
+ attribute :sub_events, :referencelist
11
+ attribute :super_event, :reference
12
+ attribute :event_status, :enum, values: ['auto','cancelled','rescheduled'], default: 'auto'
13
+
14
+ def event_status_type
15
+ return 'moron' if end_date < start_date
16
+ if (event_status == 'auto') || (event_status == '')
17
+ if Time.now < start_date
18
+ 'not started'
19
+ elsif Time.now > end_date
20
+ 'completed'
21
+ else
22
+ 'in process'
23
+ end
24
+ else
25
+ event_status
26
+ end
27
+ end
28
+
29
+ def warnings
30
+ warns = []
31
+ warns << 'End date is before start date!' if end_date < start_date
32
+ warns << 'Start date is mandatory' if start_date.blank?
33
+ warns << 'End date is recommendet' if end_date.blank?
34
+ warns << 'Location is mandatory' if location.nil?
35
+ warns << 'Event status is recommendet' if event_status.blank?
36
+ warns << 'Offers are recommendet' if offers.empty?
37
+ return warns
38
+ end
39
+
40
+ def to_json(render_childs = false)
41
+ json = {
42
+ "@context": "http://schema.org",
43
+ "@type": "Event",
44
+ name: name,
45
+ description: description,
46
+ image: image ? image.binary_url : nil,
47
+ startDate: start_date,
48
+ endDate: end_date,
49
+ organizer: organizer ? organizer.to_json : nil,
50
+ inLanguage: in_language,
51
+ offers: array_json(offers),
52
+ location: location ? location.to_json : nil,
53
+ performers: array_json(performers),
54
+ url: url,
55
+ EventStatus: {
56
+ "@context": "http://schema.org",
57
+ "@type": "EventStatusType",
58
+ description: event_status_type
59
+ }
60
+ }
61
+
62
+ if render_childs
63
+ json[:subEvents] = array_json(sub_events)
64
+ json[:superEvent] = super_event ? super_event.to_json : nil
65
+ end
66
+
67
+ return json.delete_if { |k, v| !v.present? }
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,47 @@
1
+ module RichSnippet
2
+ class JobPosting < Thing
3
+ attribute :base_salary, :integer
4
+ attribute :date_posted, :date
5
+ attribute :education_requirements, :string
6
+ attribute :employment_type, :enum, values: %w(full-time part-time contract temporary seasonal internship)
7
+ attribute :experience_requirements, :string
8
+ attribute :hiring_organization, :reference
9
+ attribute :job_benefits, :string
10
+ attribute :job_location, :reference
11
+ attribute :qualifications, :string
12
+ attribute :salary_currency, :string
13
+ attribute :skills, :string
14
+ attribute :job_title, :string
15
+ attribute :valid_through, :date
16
+ attribute :work_hours, :string
17
+
18
+ def to_json(render_childs = false)
19
+ {
20
+ "@context": "http://schema.org",
21
+ "@type": "JobPosting",
22
+ name: name,
23
+ description: description,
24
+ image: image ? image.binary_url : nil,
25
+ url: url,
26
+ baseSalary: base_salary,
27
+ datePosted: date_posted,
28
+ educationRequirements: education_requirements,
29
+ employmentType: employment_type,
30
+ experienceRequirements: experience_requirements,
31
+ hiringOrganization: hiring_organization ? hiring_organization.to_json : nil,
32
+ jobBenefits: job_benefits,
33
+ jobLocation: {
34
+ "@context": "http://schema.org",
35
+ "@type": "Place",
36
+ address: job_location ? job_location.to_json : nil,
37
+ },
38
+ qualifications: qualifications,
39
+ salaryCurrency: salary_currency,
40
+ skills: skills,
41
+ title: job_title,
42
+ validThrough: valid_through,
43
+ workHours: work_hours
44
+ }.delete_if { |k, v| !v.present? }
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,41 @@
1
+ module RichSnippet
2
+ class Offer < Thing
3
+ attribute :availability_starts, :date
4
+ attribute :availability_ends, :date
5
+ attribute :availability, :string
6
+ attribute :price, :string
7
+ attribute :price_currency, :string
8
+ attribute :item_condition, :enum, values: ['Damaged','New','Refurbished','Used']
9
+ attribute :inventory_level, :integer
10
+ attribute :seller, :reference
11
+
12
+ def to_json(render_childs = false)
13
+ {
14
+ "@context": "http://schema.org",
15
+ "@type": "Offer",
16
+ name: name,
17
+ image: image ? image.binary_url : '',
18
+ description: description,
19
+ url: url,
20
+ availability: {
21
+ '@context': "http://schema.org",
22
+ '@type': "ItemAvailability",
23
+ description: availability
24
+ },
25
+ inventoryLevel: inventory_level,
26
+ availabilityStarts: availability_starts,
27
+ availabilityEnds: availability_ends,
28
+ price: price,
29
+ priceCurrency: price_currency,
30
+ itemCondition: "https://schema.org/#{item_condition}Condition",
31
+ seller: seller ? seller.to_json : nil
32
+ }.delete_if { |k, v| !v.present? }
33
+ end
34
+
35
+ def warnings
36
+ warns = []
37
+ warns << "Price in the form xx.yy" if price.contains(',')
38
+ return warns
39
+ end
40
+ end
41
+ end