diff --git a/controllers/serpro_integration_plugin_myprofile_controller.rb b/controllers/serpro_integration_plugin_myprofile_controller.rb
new file mode 100644
index 0000000..0566d08
--- /dev/null
+++ b/controllers/serpro_integration_plugin_myprofile_controller.rb
@@ -0,0 +1,13 @@
+class SerproIntegrationPluginMyprofileController < MyProfileController
+ append_view_path File.join(File.dirname(__FILE__) + '/../views')
+
+ def create_gitlab
+ profile.create_gitlab_project
+ render :update do |page|
+ page.replace_html 'gitlab', :partial => 'gitlab'
+# page.replace_html 'gitlab', 'teste'
+ end
+# raise 'teste my profile'
+ end
+
+end
diff --git a/features/sonar.feature b/features/sonar.feature
new file mode 100644
index 0000000..9a8ce8d
--- /dev/null
+++ b/features/sonar.feature
@@ -0,0 +1,15 @@
+Feature: require authentication to comment
+
+ Background:
+ Given plugin RequireAuthToCommentPlugin is enabled on environment
+ And the following users
+ | login |
+ | bozo |
+
+ Scenario: enabling unauthenticated comment per profile
+ Given I am logged in as "bozo"
+ When I edit my profile
+ And I check "Accept comments from unauthenticated users"
+ And I press "Save"
+ Then I edit my profile
+ And the "Accept comments from unauthenticated users" checkbox should be checked
diff --git a/install.rb b/install.rb
new file mode 100644
index 0000000..6944eaf
--- /dev/null
+++ b/install.rb
@@ -0,0 +1,2 @@
+system "gem install --user-install gitlab"
+system "gem install --user-install jenkins_api_client"
diff --git a/lib/ext/community.rb b/lib/ext/community.rb
new file mode 100644
index 0000000..208aed4
--- /dev/null
+++ b/lib/ext/community.rb
@@ -0,0 +1,220 @@
+require_dependency 'community'
+require 'gitlab'
+#require 'jenkins_api_client'
+
+class Community
+
+ settings_items :allow_sonar_integration, :type => :boolean, :default => true
+ settings_items :allow_gitlab_integration, :type => :boolean, :default => true
+ settings_items :allow_jenkins_integration, :type => :boolean, :default => true
+
+ #FIXME make test for default option
+ settings_items :serpro_integration_plugin, :type => Hash, :default => {}
+
+ ##########################################
+ # Gitlab stuff #
+ ##########################################
+
+ after_create :create_gitlab_project
+
+ def gitlab= params
+ self.serpro_integration_plugin[:gitlab] = params
+ end
+
+ def gitlab
+ self.serpro_integration_plugin[:gitlab] ||= {}
+ self.serpro_integration_plugin[:gitlab]
+ end
+
+ def create_gitlab_project
+ Gitlab.endpoint = self.gitlab_host
+ Gitlab.private_token = self.gitlab_private_token
+
+ user = nil
+
+ #Find user by email
+ begin
+ user = Gitlab.users(:search => email)
+ rescue Gitlab::Error::NotFound, Gitlab::Error::Parsing
+ user = nil
+ end
+
+ #User not found, create user
+ if user == nil || user.count == 0
+ user = self.admins.first
+ gitlab_user = Gitlab.create_user(user.email, '123456', {:username => user.identifier, :name => user.name, :provider => 'ldap'})
+ end
+
+ if gitlab_user.nil?
+ self.gitlab[:errors] = _('Gitlab user could not be created')
+ return nil
+ end
+
+ #Create project for user
+ begin
+ #FIXME Why this?
+ if gitlab_user.is_a?(Array)
+ gitlab_user = user[0]
+ end
+
+ project_options = {}
+ project_options[:user_id] = gitlab_user.id
+ project_options[:issues_enabled ] = true
+ project_options[:wall_enabled] = true
+ project_options[:wiki_enabled] = true
+ project_options[:public] = true
+ project = Gitlab.create_project(self.identifier, project_options)
+
+ #Create Web Hook for Jenkins' integration
+# Gitlab.add_project_hook(project.id, "#{self.jenkins[:url]}/gitlab/build_now")
+# createJenkinsJob(project.name, project.path_with_namespace, project.web_url, project.http_url_to_repo)
+
+ rescue Gitlab::Error::NotFound, Gitlab::Error::Parsing
+ #Project already exists
+ end
+
+ self.gitlab[:errors] = nil
+ end
+
+ # set an API endpoint
+ def gitlab_host
+ self.serpro_integration_plugin[:gitlab_host]
+ end
+
+ # set a user private token
+ def gitlab_private_token
+ self.serpro_integration_plugin[:gitlab_private_token]
+ end
+
+ ##########################################
+ # Sonar stuff #
+ ##########################################
+
+# after_create :create_sonar_project
+
+ def sonar= params
+ self.serpro_integration_plugin[:sonar] = params
+ end
+
+ def sonar
+ self.serpro_integration_plugin[:sonar] ||= {}
+ self.serpro_integration_plugin[:sonar]
+ end
+
+ ##########################################
+ # Jenkins stuff #
+ ##########################################
+
+# after_create :create_jenkis_project
+
+ def jenkins= params
+ self.serpro_integration_plugin[:jenkins] = params
+ end
+
+ def jenkins
+ self.serpro_integration_plugin[:jenkins] ||= {}
+ url = "#{self.serpro_integration_plugin[:jenkins][:host]}:"
+ url += "#{self.serpro_integration_plugin[:jenkins][:port]}/"
+ url += "#{self.serpro_integration_plugin[:jenkins][:context_name]}"
+ self.serpro_integration_plugin[:jenkins][:url] = url
+ self.serpro_integration_plugin[:jenkins]
+ end
+
+
+ #FIXME make jenkins integration works
+ def create_jenkis_project
+#(projectName, repositoryPath, webUrl, gitUrl)
+
+ @client = JenkinsApi::Client.new(:server_url => "#{$jenkins_url}/",
+ :password => $jenkins_private_token,
+ :username => $jenkins_user)
+
+ xmlJenkins = "
+
+
+ Projeto criado para o repositório #{repositoryPath} do Gitlab - #{webUrl}
+
+ -1
+ 2
+ -1
+ -1
+
+ false
+
+
+ 2
+
+
+ #{gitUrl}
+
+
+
+
+ */master
+
+
+ false
+
+
+
+ true
+ false
+ false
+ false
+ (Inherit From Job)
+
+ false
+ clean package install deploy
+ true
+ false
+ true
+ false
+ false
+ false
+ false
+ -1
+ false
+ false
+
+
+
+
+
+ false
+ true
+ true
+
+
+
+
+ (Inherit From Job)
+
+
+
+
+
+
+ false
+
+
+
+
+
+
+ FAILURE
+ 2
+ RED
+
+
+ "
+
+ begin
+ @client.job.create(projectName, xmlJenkins)
+ rescue JenkinsApi::Exceptions::ApiException
+
+ end
+
+ end
+
+
+end
diff --git a/lib/ext/profile.rb b/lib/ext/profile.rb
new file mode 100644
index 0000000..d6f029c
--- /dev/null
+++ b/lib/ext/profile.rb
@@ -0,0 +1,9 @@
+require_dependency 'profile'
+
+class Profile
+ settings_items :allow_sonar_integration, :type => :boolean, :default => true
+ settings_items :allow_gitlab_integration, :type => :boolean, :default => true
+
+ #FIXME make test for default option
+ settings_items :serpro_integration_plugin, :type => Hash, :default => {}
+end
diff --git a/lib/serpro_integration_plugin.rb b/lib/serpro_integration_plugin.rb
new file mode 100644
index 0000000..a49ba27
--- /dev/null
+++ b/lib/serpro_integration_plugin.rb
@@ -0,0 +1,64 @@
+class SerproIntegrationPlugin < Noosfero::Plugin
+
+#include ActionController::UrlWriter
+# include ActionView::Helpers::TagHelper
+# include ActionView::Helpers::FormTagHelper
+# include FormsHelper
+
+
+# include ActionView::Helpers
+# include FormsHelper
+
+ def self.plugin_name
+ "Serpro Integration Plugin"
+ end
+
+ def self.plugin_description
+ _("Make integration with serpro servers.")
+ end
+
+# def filter_comment(c)
+# c.reject! unless logged_in? || allowed_by_profile
+# end
+
+ #FIXME make this test
+ # User could not have this block
+ def self.extra_blocks
+ { SonarPlugin::SonarWidgetBlock => {:type => [Community] },
+ SonarPlugin::SmileBlock => {:type => [Community] }
+ }
+ end
+
+ #FIXME make this test
+ def profile_editor_extras
+ lambda do
+ render :file => 'profile-editor-extras'
+ end
+ end
+
+ def profile_id
+ context.profile
+ end
+
+ def stylesheet?
+ true
+ end
+
+# FIXME make this test
+ def js_files
+ ['smile_face.js']
+ end
+
+# def body_beginning
+# "" if allowed_by_profile
+# end
+#
+# protected
+#
+# delegate :logged_in?, :to => :context
+#
+# def allowed_by_profile
+# context.profile && context.profile.allow_unauthenticated_comments
+# end
+
+end
diff --git a/lib/serpro_integration_plugin/smile_block.rb b/lib/serpro_integration_plugin/smile_block.rb
new file mode 100644
index 0000000..c1cff00
--- /dev/null
+++ b/lib/serpro_integration_plugin/smile_block.rb
@@ -0,0 +1,100 @@
+require 'open-uri'
+require 'json'
+
+class SonarPlugin::SmileBlock < Block
+
+
+ METRIC_SUCCESS_DENSITY = 'test_success_density'
+ METRIC_LOC = 'ncloc'
+ METRIC_UNCOVERED_LINE = 'uncovered_lines'
+ METRIC_COVERAGE = 'coverage'
+
+ #FIXME make this test
+ settings_items :sonar_info, :type => Hash, :default => {}
+
+ def self.description
+ _('Sonar Smile')
+ end
+
+ def help
+ _('This block adds a smile face that make tecnical debits visible with funny way.')
+ end
+
+ #FIXME make this test
+ def sonar_host
+ self.owner.sonar_plugin['host']
+ end
+
+ #FIXME make this test
+ def sonar_project
+ self.owner.sonar_plugin['project'] #|| ''
+ end
+
+#FIXME make this test
+ def smile_factor
+ collect_sonar_information
+ factor = (self.sonar_info[METRIC_COVERAGE] * self.sonar_info[METRIC_SUCCESS_DENSITY]).to_f/1000
+ factor
+ end
+
+ #FIXME make this test
+ def content(args={})
+ smile_face_id = 'smileFace-' + self.id.to_s
+
+ content_tag(:div,
+ content_tag(:canvas, '', :id => smile_face_id, :width => '95%', :height => '95%' ) +
+ "",
+ :class => 'smile'
+ )
+ end
+
+ #FIXME make this test
+ def self.metrics
+ [
+ METRIC_SUCCESS_DENSITY,
+ METRIC_LOC,
+ METRIC_UNCOVERED_LINE,
+ METRIC_COVERAGE
+ ]
+ end
+
+ private
+
+ #FIXME make this test
+ def collect_sonar_information
+ return false unless cache_expired?
+ begin
+ data = open("#{self.sonar_host}/api/resources?resource=#{self.sonar_project}&metrics=ncloc,coverage,weighted_violations,uncovered_lines,test_success_density&format=json").read
+ self.sonar_info = parse_sonar_resource(JSON.parse(data).first)
+ rescue
+ self.sonar_info = {}
+ end
+ self.sonar_info[:updated_at] = Time.now
+ self.save
+ end
+
+ #FIXME make this test
+ def parse_sonar_resource(data)
+ parsed_data = {}
+ return {} if data['msr'].nil?
+ data['msr'].map do |metric|
+ self.class.metrics.map do |defined_metric|
+ parsed_data[defined_metric] = metric['val'] if metric['key'] == defined_metric
+ end
+ end
+ parsed_data
+ end
+
+ #FIXME make this test
+ def cache_expired?
+ return true if self.sonar_info[:updated_at].nil?
+ (Time.now - self.sonar_info[:updated_at]) > cache_timestamp
+ end
+
+ #FIXME make this test
+ # Cach time to load new data in seconds
+ def cache_timestamp
+ 60 * 60
+ end
+
+end
diff --git a/lib/serpro_integration_plugin/sonar_widget_block.rb b/lib/serpro_integration_plugin/sonar_widget_block.rb
new file mode 100644
index 0000000..3ba1e2b
--- /dev/null
+++ b/lib/serpro_integration_plugin/sonar_widget_block.rb
@@ -0,0 +1,81 @@
+class SerproIntegrationrPlugin::SonarWidgetBlock < Block
+
+ #FIXME make this test
+ AVAILABLE_WIDGETS = {
+ 'project_motion_chart' => 'Project Motion Chart',
+ 'timeline' => 'Timeline',
+ 'complexity' => 'Complexity'
+ }
+
+ #FIXME make this test. Make test for default widget
+ settings_items :widget, :type => String, :default => 'timeline'
+
+ def self.description
+ _('Sonar Widgets')
+ end
+
+ def help
+ _('This block adds sonar widgets on profile.')
+ end
+
+ #FIXME make this test
+ def sonar_host
+ self.owner.serpro_integration_plugin['host']
+ end
+
+ #FIXME make this test
+ def sonar_project
+ self.owner.serpro_integration_plugin['project']
+ end
+
+ #FIXME make this test
+ def widget_url
+ self.sonar_host + 'widget?id=' + self.widget + '&resource=' + self.sonar_project + '&metric1=complexity&metric2=ncloc'
+ end
+
+ #FIXME make this test
+ def is_widget_well_formed_url?
+ !self.widget_url.match(/http[s]?:\/\/[\w|.|\/]+\/widget\?id=[\w]+&resource=[\w|\W]+/).nil?
+ end
+
+ #FIXME make this test
+ def widget_width
+ case widget
+ when 'project_motion_chart'
+ '360px'
+ when 'timeline'
+ '100%'
+ when 'complexity'
+ '100%'
+ else
+ '300px'
+ end
+ end
+
+ #FIXME make this test
+ def widget_height
+ case widget
+ when 'project_motion_chart'
+ '450px'
+ when 'timeline'
+ '205px'
+ when 'complexity'
+ '170px'
+ else
+ '300px'
+ end
+ end
+
+ def content(args={})
+# render this url
+#http://sonar.serpro/widget?id=timeline&resource=br.gov.fazenda.coaf.siscoaf:siscoaf-parent&metric1=complexity&metric2=ncloc
+
+ block = self
+
+ lambda do
+ render :file => 'sonar_widget_block', :locals => { :block => block }
+ end
+
+ end
+
+end
diff --git a/public/smile_face.js b/public/smile_face.js
new file mode 100644
index 0000000..e74e71c
--- /dev/null
+++ b/public/smile_face.js
@@ -0,0 +1,129 @@
+function drawBaseFace(element_id) {
+ var canvas = document.getElementById(element_id);
+ var ctx = canvas.getContext("2d");
+
+ var x = canvas.width / 2;
+ var y = canvas.height / 2;
+ var radius = canvas.width/2 - 1;
+ var startAngle = 0;
+ var endAngle = 2 * Math.PI;
+
+ ctx.beginPath();
+ ctx.arc(x, y, radius, startAngle, endAngle);
+ ctx.stroke();
+ ctx.fillStyle = "yellow";
+ ctx.fill();
+}
+
+function drawSmile(element_id, factor){
+ var canvas = document.getElementById(element_id);
+ var ctx = canvas.getContext("2d");
+ var x = canvas.width / 2;
+ var y = canvas.height / 2
+ var radius = canvas.width/2 - canvas.width/4;
+
+ var startAngle = 0;
+ var endAngle = 0;
+ var delta = 0;
+
+ ctx.beginPath();
+ ctx.lineWidth = canvas.width/100 * 2;
+
+ if (factor >= 0 && factor < 5){
+ //Draw sadness mouth
+ delta = 0.5 - factor* 0.1
+ startAngle = (1.5 - delta) * Math.PI;
+ endAngle = (1.5 + delta) * Math.PI;
+ radius = radius - radius*delta;
+ y = y + (radius + radius*(1 - delta));
+ ctx.arc(x, y, radius, startAngle, endAngle);
+ } else if (factor == 5) {
+ //Draw normal mouth
+ ctx.moveTo(x-canvas.width/8,y+canvas.width/8);
+ ctx.lineTo(x-canvas.width/8+(canvas.width/4),y+canvas.width/8);
+ } else if (factor > 5 && factor <= 10) {
+ //Draw happiness mouth
+ delta = 1 - factor * 0.1
+ startAngle = delta * Math.PI;
+ endAngle = (1 - delta) * Math.PI;
+ radius = radius - radius*delta;
+ y = y - (radius - radius*(1-delta));
+ ctx.arc(x, y, radius, startAngle, endAngle);
+ }else{
+ //Draw scare mouth
+ radius = radius*0.4
+ y = y + radius;
+ ctx.ellipse(x,y,radius*0.4, radius,0,0,2 * Math.PI, false);
+ ctx.fill();
+ }
+
+
+ // line color
+ ctx.strokeStyle = 'black';
+ ctx.stroke();
+}
+
+function drawEyes(element_id){
+ var canvas = document.getElementById(element_id);
+ var ctx = canvas.getContext("2d");
+ var centerX = canvas.width/5;
+ var centerY = 0;
+ var radius = canvas.width * 0.05;
+
+ // save state
+ ctx.save();
+
+ // translate context so height is 1/3'rd from top of enclosing circle
+ ctx.translate(canvas.width / 2, canvas.height / 3);
+
+ // scale context horizontally by 50%
+ ctx.scale(.5, 1);
+
+ // draw circle which will be stretched into an oval
+ ctx.beginPath();
+ ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
+
+ // restore to original state
+ ctx.restore();
+
+ // apply styling
+ ctx.fillStyle = 'black';
+ ctx.fill();
+ ctx.lineWidth = 2;
+ ctx.strokeStyle = 'black';
+ ctx.stroke();
+
+
+ //left eye
+ centerX = centerX * -1;
+
+ // save state
+ ctx.save();
+
+ // translate context so height is 1/3'rd from top of enclosing circle
+ ctx.translate(canvas.width / 2, canvas.height / 3);
+
+ // scale context horizontally by 50%
+ ctx.scale(.5, 1);
+
+ // draw circle which will be stretched into an oval
+ ctx.beginPath();
+ ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
+
+ // restore to original state
+ ctx.restore();
+
+ // apply styling
+ ctx.fillStyle = 'black';
+ ctx.fill();
+ ctx.lineWidth = 2;
+ ctx.strokeStyle = 'black';
+ ctx.stroke();
+}
+
+
+function drawFace(element_id, factor){
+ drawBaseFace(element_id);
+ drawEyes(element_id);
+ drawSmile(element_id, factor);
+}
diff --git a/public/style.css b/public/style.css
new file mode 100644
index 0000000..f89b362
--- /dev/null
+++ b/public/style.css
@@ -0,0 +1,3 @@
+.serpro-integration-plugin_smile-block .smile {
+ text-align: center;
+}
diff --git a/smile.html b/smile.html
new file mode 100644
index 0000000..672b48f
--- /dev/null
+++ b/smile.html
@@ -0,0 +1,142 @@
+
+
+
+
+
+
+
+
diff --git a/test/unit/sonar_plugin_test.rb b/test/unit/sonar_plugin_test.rb
new file mode 100644
index 0000000..92b4e1d
--- /dev/null
+++ b/test/unit/sonar_plugin_test.rb
@@ -0,0 +1,41 @@
+require File.dirname(__FILE__) + '/../../../../test/test_helper'
+
+class RequireAuthToCommentPluginTest < ActiveSupport::TestCase
+
+ def setup
+ @plugin = RequireAuthToCommentPlugin.new
+ @comment = Comment.new
+ end
+
+ attr_reader :plugin, :comment
+
+ should 'reject comments for unauthenticated users' do
+ plugin.context = logged_in(false)
+ plugin.filter_comment(comment)
+ assert comment.rejected?
+ end
+
+ should 'allow comments from authenticated users' do
+ plugin.context = logged_in(true)
+ plugin.filter_comment(comment)
+ assert !comment.rejected?
+ end
+
+ should 'allow comments from unauthenticated users if allowed by profile' do
+ plugin.context = logged_in(false)
+ plugin.context.profile.allow_unauthenticated_comments = true
+
+ plugin.filter_comment(comment)
+ assert !comment.rejected?
+ end
+
+ protected
+
+ def logged_in(boolean)
+ controller = mock()
+ controller.stubs(:logged_in?).returns(boolean)
+ controller.stubs(:profile).returns(Profile.new)
+ controller
+ end
+
+end
diff --git a/views/profile-editor-extras.html.erb b/views/profile-editor-extras.html.erb
new file mode 100644
index 0000000..24f9a7f
--- /dev/null
+++ b/views/profile-editor-extras.html.erb
@@ -0,0 +1,49 @@
+
+
+
+ <%= render :partial => 'gitlab' %>
+
+
+
+ <%= labelled_check_box(_('Uses sonar integration'), 'profile_data[allow_sonar_integration]', true, profile.allow_sonar_integration) %>
+
+
+
+ <%= _('Sonar Integration') %>
+ -
+ <%= labelled_text_field(_('Server Host'), 'profile_data[sonar]host]', profile.sonar[:host]) %>
+
+ -
+ <%= labelled_text_field(_('Project: '), 'profile_data[sonar][project]', profile.sonar[:project]) %>
+
+
+
+
+
+ <%= labelled_check_box(_('Uses Jenkins integration'), 'profile_data[allow_jenkins_integration]', true, profile.allow_jenkins_integration) %>
+
+
+
+ <%= _('Jenkins Integration') %>
+ -
+ <%= labelled_text_field(_('Server Host'), 'profile_data[jenkins][host]', profile.jenkins[:host]) %>
+
+ -
+ <%= labelled_text_field(_('Server Port'), 'profile_data[jenkins][port]', profile.jenkins[:port]) %>
+
+ -
+ <%= labelled_text_field(_('Server Context Name'), 'profile_data[jenkins][context_name]', profile.jenkins[:context_name]) %>
+
+ -
+ <%= labelled_text_field(_('Server User'), 'profile_data[jenkins][user]', profile.jenkins[:user]) %>
+
+ -
+ <%= labelled_text_field(_('Server Private Token'), 'profile_data[jenkins][private_token]', profile.jenkins[:private_token]) %>
+
+ -
+ <%= labelled_text_field(_('Server Url'), 'profile_data[jenkins][url]', profile.jenkins[:url], {:disabled => true}) %>
+
+
+
+
+
diff --git a/views/profile_design/sonar_plugin/_sonar_widget_block.rhtml b/views/profile_design/sonar_plugin/_sonar_widget_block.rhtml
new file mode 100644
index 0000000..fe9138d
--- /dev/null
+++ b/views/profile_design/sonar_plugin/_sonar_widget_block.rhtml
@@ -0,0 +1,4 @@
+
+<%= labelled_form_field(_('Widget'), select(:block, :widget, SerproIntegrationPlugin::SonarWidgetBlock::AVAILABLE_WIDGETS.map {|id, name| [name, id]} )) %>
+
+
diff --git a/views/profile_editor/_gitlab.html.erb b/views/profile_editor/_gitlab.html.erb
new file mode 100644
index 0000000..86799ba
--- /dev/null
+++ b/views/profile_editor/_gitlab.html.erb
@@ -0,0 +1,22 @@
+
+ <%= labelled_check_box(_('Uses Gitlab integration'), 'profile_data[allow_gitlab_integration]', true, profile.allow_gitlab_integration) %>
+
+
+
+
+ <%= _('Gitlab Integration') %>
+ <%= link_to_remote _('Force'), :url => {:controller => 'serpro_integration_plugin_myprofile', :action => 'create_gitlab'} %>
+
+
+ -
+ <%= labelled_text_field(_('Server Host'), 'profile_data[gitlab][host]', profile.gitlab[:host]) %>
+
+ -
+ <%= labelled_text_field(_('User private token: '), 'profile_data[gitlab][private_token]', profile.gitlab[:private_token]) %>
+
+ -
+ <%= _('Erros: %s') % (profile.gitlab[:errors] || _('None')) %>
+
+
+
+
diff --git a/views/sonar_widget_block.rhtml b/views/sonar_widget_block.rhtml
new file mode 100644
index 0000000..ef97408
--- /dev/null
+++ b/views/sonar_widget_block.rhtml
@@ -0,0 +1,10 @@
+
--
libgit2 0.21.2