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.
- checksums.yaml +7 -0
- data/README.md +129 -0
- data/Rakefile +35 -0
- data/app/assets/images/widgets/scrivito_rich_snippet_widget.png +0 -0
- data/app/assets/javascripts/scrivito_rich_snippet_widget.js +41 -0
- data/app/models/rich_snippet/creative_work.rb +88 -0
- data/app/models/rich_snippet/event.rb +70 -0
- data/app/models/rich_snippet/job_posting.rb +47 -0
- data/app/models/rich_snippet/offer.rb +41 -0
- data/app/models/rich_snippet/organization.rb +33 -0
- data/app/models/rich_snippet/person.rb +70 -0
- data/app/models/rich_snippet/postal_address.rb +33 -0
- data/app/models/rich_snippet/product.rb +40 -0
- data/app/models/rich_snippet/recipe.rb +69 -0
- data/app/models/rich_snippet/thing.rb +43 -0
- data/app/models/rich_snippet_widget.rb +20 -0
- data/app/views/rich_snippet/creative_work/_details.html.erb +136 -0
- data/app/views/rich_snippet/creative_work/details.html.erb +1 -0
- data/app/views/rich_snippet/event/_details.html.erb +39 -0
- data/app/views/rich_snippet/event/details.html.erb +1 -0
- data/app/views/rich_snippet/job_posting/_details.html.erb +59 -0
- data/app/views/rich_snippet/job_posting/details.html.erb +1 -0
- data/app/views/rich_snippet/offer/_details.html.erb +35 -0
- data/app/views/rich_snippet/offer/details.html.erb +1 -0
- data/app/views/rich_snippet/organization/_details.html.erb +39 -0
- data/app/views/rich_snippet/organization/details.html.erb +1 -0
- data/app/views/rich_snippet/person/_details.html.erb +79 -0
- data/app/views/rich_snippet/person/details.html.erb +1 -0
- data/app/views/rich_snippet/postal_address/_details.html.erb +39 -0
- data/app/views/rich_snippet/postal_address/details.html.erb +1 -0
- data/app/views/rich_snippet/product/_details.html.erb +55 -0
- data/app/views/rich_snippet/product/details.html.erb +1 -0
- data/app/views/rich_snippet/recipe/_details.html.erb +91 -0
- data/app/views/rich_snippet/recipe/details.html.erb +1 -0
- data/app/views/rich_snippet/thing/_details.html.erb +17 -0
- data/app/views/rich_snippet_widget/details.html.erb +11 -0
- data/app/views/rich_snippet_widget/show.html.erb +21 -0
- data/app/views/rich_snippet_widget/thumbnail.html.erb +3 -0
- data/config/routes.rb +2 -0
- data/lib/scrivito_rich_snippet_widget.rb +5 -0
- data/lib/scrivito_rich_snippet_widget/engine.rb +5 -0
- data/lib/scrivito_rich_snippet_widget/version.rb +3 -0
- data/lib/tasks/scrivito_rich_snippet_widget_tasks.rake +4 -0
- 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
|
Binary file
|
@@ -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
|