scrivito_rich_snippet_widget 0.1.0

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