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