Commit c770f8a428b74680b54f3c71d7b440e35307b901
Exists in
staging
and in
4 other branches
Merge branch 'virtuoso_integration' of gitlab.com:participa/noosfero into virtuoso_integration
Showing
15 changed files
with
122 additions
and
17 deletions
Show diff stats
Gemfile
@@ -13,7 +13,7 @@ gem 'ruby-feedparser', '~> 0.7' | @@ -13,7 +13,7 @@ gem 'ruby-feedparser', '~> 0.7' | ||
13 | gem 'daemons', '~> 1.1.5' | 13 | gem 'daemons', '~> 1.1.5' |
14 | gem 'thin', '~> 1.3.1' | 14 | gem 'thin', '~> 1.3.1' |
15 | gem 'hpricot', '~> 0.8.6' | 15 | gem 'hpricot', '~> 0.8.6' |
16 | -gem 'nokogiri', '~> 1.5.5' | 16 | +gem 'nokogiri', '~> 1.6.0' |
17 | gem 'rake', :require => false | 17 | gem 'rake', :require => false |
18 | gem 'rest-client', '~> 1.6.7' | 18 | gem 'rest-client', '~> 1.6.7' |
19 | gem 'exception_notification', '~> 4.0.1' | 19 | gem 'exception_notification', '~> 4.0.1' |
plugins/virtuoso/Gemfile
plugins/virtuoso/db/migrate/20141113131439_create_custom_queries.rb
@@ -6,6 +6,7 @@ class CreateCustomQueries < ActiveRecord::Migration | @@ -6,6 +6,7 @@ class CreateCustomQueries < ActiveRecord::Migration | ||
6 | t.string :name | 6 | t.string :name |
7 | t.text :query | 7 | t.text :query |
8 | t.text :template | 8 | t.text :template |
9 | + t.text :stylesheet | ||
9 | t.boolean :enabled, :default => true | 10 | t.boolean :enabled, :default => true |
10 | t.timestamps | 11 | t.timestamps |
11 | end | 12 | end |
@@ -0,0 +1,25 @@ | @@ -0,0 +1,25 @@ | ||
1 | +Feature: edit template | ||
2 | + As a noosfero user | ||
3 | + I want to create and edit templates | ||
4 | + | ||
5 | + Background: | ||
6 | + Given I am on the homepage | ||
7 | + And plugin Virtuoso is enabled on environment | ||
8 | + And the following users | ||
9 | + | login | name | | ||
10 | + | joaosilva | Joao Silva | | ||
11 | + And I am logged in as "joaosilva" | ||
12 | + And the following custom queries | ||
13 | + | name | query | template | | ||
14 | + | Default | query-1 | template-1 | | ||
15 | + | ||
16 | + @selenium | ||
17 | + Scenario: | ||
18 | + Given I am on joaosilva's control panel | ||
19 | + And I follow "Manage Content" | ||
20 | + And I should see "New content" | ||
21 | + And I follow "New content" | ||
22 | + And I should see "Triples template" within ".article-types" | ||
23 | + When I follow "Triples template" within ".article-types" | ||
24 | + And I follow "Copy" within ".custom-query" | ||
25 | + Then the "SPARQL Query" field should contain "query-1" |
plugins/virtuoso/features/step_definitions/virtuoso_plugin_steps.rb
0 → 100644
plugins/virtuoso/lib/ext/literal.rb
plugins/virtuoso/lib/virtuoso_plugin/custom_query.rb
@@ -2,7 +2,7 @@ class VirtuosoPlugin::CustomQuery < Noosfero::Plugin::ActiveRecord | @@ -2,7 +2,7 @@ class VirtuosoPlugin::CustomQuery < Noosfero::Plugin::ActiveRecord | ||
2 | 2 | ||
3 | belongs_to :environment | 3 | belongs_to :environment |
4 | 4 | ||
5 | - attr_accessible :enabled, :environment, :query, :template, :name | 5 | + attr_accessible :enabled, :environment, :query, :template, :name, :stylesheet |
6 | 6 | ||
7 | validates_presence_of :name | 7 | validates_presence_of :name |
8 | 8 |
plugins/virtuoso/lib/virtuoso_plugin/triples_management.rb
@@ -18,15 +18,21 @@ class VirtuosoPlugin::TriplesManagement | @@ -18,15 +18,21 @@ class VirtuosoPlugin::TriplesManagement | ||
18 | def update_triple(graph_uri, from_triple, to_triple) | 18 | def update_triple(graph_uri, from_triple, to_triple) |
19 | from_subject = from_triple[:subject] | 19 | from_subject = from_triple[:subject] |
20 | from_predicate = from_triple[:predicate] | 20 | from_predicate = from_triple[:predicate] |
21 | - from_object = from_triple[:object] | 21 | + from_object = format_triple_term(from_triple[:object]) |
22 | 22 | ||
23 | to_subject = to_triple[:subject] | 23 | to_subject = to_triple[:subject] |
24 | to_predicate = to_triple[:predicate] | 24 | to_predicate = to_triple[:predicate] |
25 | - to_object = to_triple[:object] | 25 | + to_object = format_triple_term(to_triple[:object]) |
26 | 26 | ||
27 | - query = "WITH <#{graph_uri}> DELETE { <#{from_subject}> <#{from_predicate}> '#{from_object}' } INSERT { <#{to_subject}> <#{to_predicate}> '#{to_object}' }" | 27 | + query = "WITH <#{graph_uri}> DELETE { <#{from_subject}> <#{from_predicate}> #{from_object} } INSERT { <#{to_subject}> <#{to_predicate}> #{to_object} }" |
28 | 28 | ||
29 | plugin.virtuoso_client.query(query) | 29 | plugin.virtuoso_client.query(query) |
30 | end | 30 | end |
31 | 31 | ||
32 | + protected | ||
33 | + | ||
34 | + def format_triple_term(term) | ||
35 | + term =~ /^(http|https):\/\// ? "<#{term}>" : "'#{term}'" | ||
36 | + end | ||
37 | + | ||
32 | end | 38 | end |
plugins/virtuoso/lib/virtuoso_plugin/triples_template.rb
@@ -8,10 +8,21 @@ class VirtuosoPlugin::TriplesTemplate < Article | @@ -8,10 +8,21 @@ class VirtuosoPlugin::TriplesTemplate < Article | ||
8 | _('Triples template') | 8 | _('Triples template') |
9 | end | 9 | end |
10 | 10 | ||
11 | + def self.initial_template | ||
12 | + ' | ||
13 | + {% for row in results %} | ||
14 | + <div> | ||
15 | + {{row}} | ||
16 | + </div> | ||
17 | + {% endfor %} | ||
18 | + ' | ||
19 | + end | ||
20 | + | ||
11 | settings_items :query, :type => :string | 21 | settings_items :query, :type => :string |
12 | - settings_items :template, :type => :string | 22 | + settings_items :template, :type => :string, :default => initial_template |
23 | + settings_items :stylesheet, :type => :string | ||
13 | 24 | ||
14 | - attr_accessible :query, :template | 25 | + attr_accessible :query, :template, :stylesheet |
15 | 26 | ||
16 | def to_html(options = {}) | 27 | def to_html(options = {}) |
17 | article = self | 28 | article = self |
@@ -27,12 +38,21 @@ class VirtuosoPlugin::TriplesTemplate < Article | @@ -27,12 +38,21 @@ class VirtuosoPlugin::TriplesTemplate < Article | ||
27 | def template_content | 38 | def template_content |
28 | begin | 39 | begin |
29 | results = plugin.virtuoso_client.query(query) | 40 | results = plugin.virtuoso_client.query(query) |
30 | - liquid_template = Liquid::Template.parse("{% for row in results %}#{template}{% endfor %}") | ||
31 | - liquid_template.render('results' => results) | 41 | + liquid_template = Liquid::Template.parse(template) |
42 | + page = liquid_template.render('results' => results) | ||
43 | + transform_html(page) | ||
32 | rescue => ex | 44 | rescue => ex |
33 | logger.info ex.to_s | 45 | logger.info ex.to_s |
34 | "Failed to process the template" | 46 | "Failed to process the template" |
35 | end | 47 | end |
36 | end | 48 | end |
37 | 49 | ||
50 | + protected | ||
51 | + | ||
52 | + def transform_html(html) | ||
53 | + document = Roadie::Document.new(html) | ||
54 | + document.add_css(stylesheet) if stylesheet.present? | ||
55 | + document.transform | ||
56 | + end | ||
57 | + | ||
38 | end | 58 | end |
plugins/virtuoso/public/custom_query.js
@@ -2,6 +2,7 @@ jQuery(document).ready(function($) { | @@ -2,6 +2,7 @@ jQuery(document).ready(function($) { | ||
2 | $('#copy_custom_query').on('click', function() { | 2 | $('#copy_custom_query').on('click', function() { |
3 | $.getJSON("/plugin/virtuoso/public/custom_query", {id: $('#select_custom_query').val()}, function(data) { | 3 | $.getJSON("/plugin/virtuoso/public/custom_query", {id: $('#select_custom_query').val()}, function(data) { |
4 | $('#article_query').val(data.custom_query.query); | 4 | $('#article_query').val(data.custom_query.query); |
5 | + $('#article_stylesheet').val(data.custom_query.stylesheet); | ||
5 | tinymce.get('article_template').setContent(data.custom_query.template); | 6 | tinymce.get('article_template').setContent(data.custom_query.template); |
6 | }); | 7 | }); |
7 | }); | 8 | }); |
plugins/virtuoso/test/functional/virtuoso_plugin_custom_queries_controller_test.rb
@@ -2,7 +2,7 @@ require 'test_helper' | @@ -2,7 +2,7 @@ require 'test_helper' | ||
2 | 2 | ||
3 | class VirtuosoPluginCustomQueriesControllerTest < ActionController::TestCase | 3 | class VirtuosoPluginCustomQueriesControllerTest < ActionController::TestCase |
4 | setup do | 4 | setup do |
5 | - @custom_query = VirtuosoPlugin::CustomQuery.create!(:name => 'name', :query => 'query', :template => 'template', :environment => Environment.default) | 5 | + @custom_query = VirtuosoPlugin::CustomQuery.create!(:name => 'name', :query => 'query', :template => 'template', :stylesheet => 'stylesheet', :environment => Environment.default) |
6 | login_as(create_admin_user(Environment.default)) | 6 | login_as(create_admin_user(Environment.default)) |
7 | end | 7 | end |
8 | 8 | ||
@@ -19,7 +19,7 @@ class VirtuosoPluginCustomQueriesControllerTest < ActionController::TestCase | @@ -19,7 +19,7 @@ class VirtuosoPluginCustomQueriesControllerTest < ActionController::TestCase | ||
19 | 19 | ||
20 | should "create custom_query" do | 20 | should "create custom_query" do |
21 | assert_difference('VirtuosoPlugin::CustomQuery.count') do | 21 | assert_difference('VirtuosoPlugin::CustomQuery.count') do |
22 | - post :create, custom_query: { name: @custom_query.name, enabled: @custom_query.enabled, query: @custom_query.query, template: @custom_query.template } | 22 | + post :create, custom_query: { name: @custom_query.name, enabled: @custom_query.enabled, query: @custom_query.query, template: @custom_query.template, stylesheet: @custom_query.stylesheet } |
23 | end | 23 | end |
24 | 24 | ||
25 | assert_redirected_to :action => :index | 25 | assert_redirected_to :action => :index |
@@ -31,7 +31,7 @@ class VirtuosoPluginCustomQueriesControllerTest < ActionController::TestCase | @@ -31,7 +31,7 @@ class VirtuosoPluginCustomQueriesControllerTest < ActionController::TestCase | ||
31 | end | 31 | end |
32 | 32 | ||
33 | should "update custom_query" do | 33 | should "update custom_query" do |
34 | - put :update, id: @custom_query, custom_query: { name: @custom_query.name, enabled: @custom_query.enabled, query: @custom_query.query, template: @custom_query.template } | 34 | + put :update, id: @custom_query, custom_query: { name: @custom_query.name, enabled: @custom_query.enabled, query: @custom_query.query, template: @custom_query.template, stylesheet: @custom_query.stylesheet } |
35 | assert_redirected_to :action => :index | 35 | assert_redirected_to :action => :index |
36 | end | 36 | end |
37 | 37 |
plugins/virtuoso/test/unit/triples_template_test.rb
@@ -12,18 +12,39 @@ class TriplesTemplateTest < ActiveSupport::TestCase | @@ -12,18 +12,39 @@ class TriplesTemplateTest < ActiveSupport::TestCase | ||
12 | article.stubs(:plugin).returns(mock) | 12 | article.stubs(:plugin).returns(mock) |
13 | article.plugin.expects(:virtuoso_client).at_least_once.returns(mock) | 13 | article.plugin.expects(:virtuoso_client).at_least_once.returns(mock) |
14 | article.plugin.virtuoso_client.expects(:query).returns([{'var' => 'Hello '}, {'var' => 'World'}]) | 14 | article.plugin.virtuoso_client.expects(:query).returns([{'var' => 'Hello '}, {'var' => 'World'}]) |
15 | - article.template = "{{row.var}}" | 15 | + article.template = "{% for row in results %}{{row.var}}{% endfor %}" |
16 | 16 | ||
17 | - assert_equal 'Hello World', article.template_content | 17 | + assert_match /Hello World/, article.template_content |
18 | end | 18 | end |
19 | 19 | ||
20 | should 'display error message when failed to execute the query' do | 20 | should 'display error message when failed to execute the query' do |
21 | article.stubs(:plugin).returns(mock) | 21 | article.stubs(:plugin).returns(mock) |
22 | article.plugin.expects(:virtuoso_client).at_least_once.returns(mock) | 22 | article.plugin.expects(:virtuoso_client).at_least_once.returns(mock) |
23 | article.plugin.virtuoso_client.expects(:query).raises(RuntimeError.new) | 23 | article.plugin.virtuoso_client.expects(:query).raises(RuntimeError.new) |
24 | - article.template = "{{row.var}}" | 24 | + article.template = "{% for row in results %}{{row.var}}{% endfor %}" |
25 | 25 | ||
26 | assert_equal "Failed to process the template", article.template_content | 26 | assert_equal "Failed to process the template", article.template_content |
27 | end | 27 | end |
28 | 28 | ||
29 | + should 'transform css into inline stylesheet' do | ||
30 | + article.stubs(:plugin).returns(mock) | ||
31 | + article.plugin.expects(:virtuoso_client).at_least_once.returns(mock) | ||
32 | + article.plugin.virtuoso_client.expects(:query).returns([{'var' => 'Hello '}, {'var' => 'World'}]) | ||
33 | + article.template = "{% for row in results %}<p>{{row.var}}</p>{% endfor %}" | ||
34 | + article.stylesheet = "p {color: red}" | ||
35 | + | ||
36 | + content = article.template_content | ||
37 | + assert_match /<p style="color:red">Hello <\/p>/, content | ||
38 | + assert_match /<p style="color:red">World<\/p>/, content | ||
39 | + end | ||
40 | + | ||
41 | + should 'do not allow js injection' do | ||
42 | + article.stubs(:plugin).returns(mock) | ||
43 | + article.plugin.expects(:virtuoso_client).at_least_once.returns(mock) | ||
44 | + article.plugin.virtuoso_client.expects(:query).returns([{'var' => RDF::Literal.new('<script>alert("hello");</script>')}]) | ||
45 | + article.template = "{% for row in results %}{{row.var}}{% endfor %}" | ||
46 | + | ||
47 | + assert_no_match /<script>/, article.template_content | ||
48 | + end | ||
49 | + | ||
29 | end | 50 | end |
plugins/virtuoso/views/cms/virtuoso_plugin/_tinymce_template_editor.html.erb
0 → 100644
@@ -0,0 +1,11 @@ | @@ -0,0 +1,11 @@ | ||
1 | +<% extend TinymceHelper %> | ||
2 | + | ||
3 | +<%= tinymce_js %> | ||
4 | + | ||
5 | +<script> | ||
6 | + function tinymce_macros_setup(editor) {} | ||
7 | + | ||
8 | + jQuery(document).ready(function($) { | ||
9 | + <%= tinymce_init_js(:valid_children => "+body[style]", :convert_urls => false) %> | ||
10 | + }); | ||
11 | +</script> |
plugins/virtuoso/views/cms/virtuoso_plugin/_triples_template.html.erb
1 | <div class="virtuoso-triples-template"> | 1 | <div class="virtuoso-triples-template"> |
2 | <%= required_fields_message %> | 2 | <%= required_fields_message %> |
3 | 3 | ||
4 | - <%= render :file => 'shared/tiny_mce' %> | 4 | + <%= render :partial => 'cms/virtuoso_plugin/tinymce_template_editor' %> |
5 | 5 | ||
6 | <%= required labelled_form_field(_('Title'), text_field(:article, 'name', :size => '64', :maxlength => 150)) %> | 6 | <%= required labelled_form_field(_('Title'), text_field(:article, 'name', :size => '64', :maxlength => 150)) %> |
7 | 7 | ||
@@ -20,6 +20,13 @@ | @@ -20,6 +20,13 @@ | ||
20 | </span> | 20 | </span> |
21 | </div> | 21 | </div> |
22 | 22 | ||
23 | + <div class="stylesheet"> | ||
24 | + <span class="label"><%= _('Stylesheet') %></span> | ||
25 | + <span> | ||
26 | + <%= text_area(:article, :stylesheet, :style => 'width: 98%; height: 100px;') %> | ||
27 | + </span> | ||
28 | + </div> | ||
29 | + | ||
23 | <%= render :partial => 'shared/lead_and_body', :locals => {:tiny_mce => true} %> | 30 | <%= render :partial => 'shared/lead_and_body', :locals => {:tiny_mce => true} %> |
24 | <%= render :partial => 'general_fields' %> | 31 | <%= render :partial => 'general_fields' %> |
25 | </div> | 32 | </div> |
plugins/virtuoso/views/virtuoso_plugin_custom_queries/_form.html.erb
@@ -24,6 +24,10 @@ | @@ -24,6 +24,10 @@ | ||
24 | <%= f.text_area :template %> | 24 | <%= f.text_area :template %> |
25 | </div> | 25 | </div> |
26 | <div class="field"> | 26 | <div class="field"> |
27 | + <%= f.label :stylesheet %><br /> | ||
28 | + <%= f.text_area :stylesheet %> | ||
29 | + </div> | ||
30 | + <div class="field"> | ||
27 | <%= f.label :enabled %><br /> | 31 | <%= f.label :enabled %><br /> |
28 | <%= f.check_box :enabled %> | 32 | <%= f.check_box :enabled %> |
29 | </div> | 33 | </div> |