From 16268a09da714c7ff8493296a765350de0a53889 Mon Sep 17 00:00:00 2001 From: Leandro Nunes dos Santos Date: Mon, 2 Jun 2014 19:21:31 -0300 Subject: [PATCH] merging with branch AI3033-serpro_integration of rails 2.3 --- controllers/serpro_integration_plugin_myprofile_controller.rb | 13 +++++++++++++ features/sonar.feature | 15 +++++++++++++++ install.rb | 2 ++ lib/ext/community.rb | 220 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/ext/profile.rb | 9 +++++++++ lib/serpro_integration_plugin.rb | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/serpro_integration_plugin/smile_block.rb | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/serpro_integration_plugin/sonar_widget_block.rb | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ public/smile_face.js | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ public/style.css | 3 +++ smile.html | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ test/unit/sonar_plugin_test.rb | 41 +++++++++++++++++++++++++++++++++++++++++ views/profile-editor-extras.html.erb | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ views/profile_design/sonar_plugin/_sonar_widget_block.rhtml | 4 ++++ views/profile_editor/_gitlab.html.erb | 22 ++++++++++++++++++++++ views/sonar_widget_block.rhtml | 10 ++++++++++ 16 files changed, 904 insertions(+), 0 deletions(-) create mode 100644 controllers/serpro_integration_plugin_myprofile_controller.rb create mode 100644 features/sonar.feature create mode 100644 install.rb create mode 100644 lib/ext/community.rb create mode 100644 lib/ext/profile.rb create mode 100644 lib/serpro_integration_plugin.rb create mode 100644 lib/serpro_integration_plugin/smile_block.rb create mode 100644 lib/serpro_integration_plugin/sonar_widget_block.rb create mode 100644 public/smile_face.js create mode 100644 public/style.css create mode 100644 smile.html create mode 100644 test/unit/sonar_plugin_test.rb create mode 100644 views/profile-editor-extras.html.erb create mode 100644 views/profile_design/sonar_plugin/_sonar_widget_block.rhtml create mode 100644 views/profile_editor/_gitlab.html.erb create mode 100644 views/sonar_widget_block.rhtml 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) %> +
+ + + + +
+ <%= labelled_check_box(_('Uses Jenkins integration'), 'profile_data[allow_jenkins_integration]', true, profile.allow_jenkins_integration) %> +
+ + + +
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) %> +
+ + + 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 @@ +
+ <% if block.is_widget_well_formed_url? %> + + <% else %> +
+ + <%= _('Something wrong happened. Please see your sonar configuration.') %> +
+ <% end %> +
-- libgit2 0.21.2