Commit 16268a09da714c7ff8493296a765350de0a53889

Authored by Leandro Santos
0 parents
Exists in master

merging with branch AI3033-serpro_integration of rails 2.3

controllers/serpro_integration_plugin_myprofile_controller.rb 0 → 100644
  1 +++ a/controllers/serpro_integration_plugin_myprofile_controller.rb
... ... @@ -0,0 +1,13 @@
  1 +class SerproIntegrationPluginMyprofileController < MyProfileController
  2 + append_view_path File.join(File.dirname(__FILE__) + '/../views')
  3 +
  4 + def create_gitlab
  5 + profile.create_gitlab_project
  6 + render :update do |page|
  7 + page.replace_html 'gitlab', :partial => 'gitlab'
  8 +# page.replace_html 'gitlab', 'teste'
  9 + end
  10 +# raise 'teste my profile'
  11 + end
  12 +
  13 +end
... ...
features/sonar.feature 0 → 100644
  1 +++ a/features/sonar.feature
... ... @@ -0,0 +1,15 @@
  1 +Feature: require authentication to comment
  2 +
  3 + Background:
  4 + Given plugin RequireAuthToCommentPlugin is enabled on environment
  5 + And the following users
  6 + | login |
  7 + | bozo |
  8 +
  9 + Scenario: enabling unauthenticated comment per profile
  10 + Given I am logged in as "bozo"
  11 + When I edit my profile
  12 + And I check "Accept comments from unauthenticated users"
  13 + And I press "Save"
  14 + Then I edit my profile
  15 + And the "Accept comments from unauthenticated users" checkbox should be checked
... ...
install.rb 0 → 100644
  1 +++ a/install.rb
... ... @@ -0,0 +1,2 @@
  1 +system "gem install --user-install gitlab"
  2 +system "gem install --user-install jenkins_api_client"
... ...
lib/ext/community.rb 0 → 100644
  1 +++ a/lib/ext/community.rb
... ... @@ -0,0 +1,220 @@
  1 +require_dependency 'community'
  2 +require 'gitlab'
  3 +#require 'jenkins_api_client'
  4 +
  5 +class Community
  6 +
  7 + settings_items :allow_sonar_integration, :type => :boolean, :default => true
  8 + settings_items :allow_gitlab_integration, :type => :boolean, :default => true
  9 + settings_items :allow_jenkins_integration, :type => :boolean, :default => true
  10 +
  11 + #FIXME make test for default option
  12 + settings_items :serpro_integration_plugin, :type => Hash, :default => {}
  13 +
  14 + ##########################################
  15 + # Gitlab stuff #
  16 + ##########################################
  17 +
  18 + after_create :create_gitlab_project
  19 +
  20 + def gitlab= params
  21 + self.serpro_integration_plugin[:gitlab] = params
  22 + end
  23 +
  24 + def gitlab
  25 + self.serpro_integration_plugin[:gitlab] ||= {}
  26 + self.serpro_integration_plugin[:gitlab]
  27 + end
  28 +
  29 + def create_gitlab_project
  30 + Gitlab.endpoint = self.gitlab_host
  31 + Gitlab.private_token = self.gitlab_private_token
  32 +
  33 + user = nil
  34 +
  35 + #Find user by email
  36 + begin
  37 + user = Gitlab.users(:search => email)
  38 + rescue Gitlab::Error::NotFound, Gitlab::Error::Parsing
  39 + user = nil
  40 + end
  41 +
  42 + #User not found, create user
  43 + if user == nil || user.count == 0
  44 + user = self.admins.first
  45 + gitlab_user = Gitlab.create_user(user.email, '123456', {:username => user.identifier, :name => user.name, :provider => 'ldap'})
  46 + end
  47 +
  48 + if gitlab_user.nil?
  49 + self.gitlab[:errors] = _('Gitlab user could not be created')
  50 + return nil
  51 + end
  52 +
  53 + #Create project for user
  54 + begin
  55 + #FIXME Why this?
  56 + if gitlab_user.is_a?(Array)
  57 + gitlab_user = user[0]
  58 + end
  59 +
  60 + project_options = {}
  61 + project_options[:user_id] = gitlab_user.id
  62 + project_options[:issues_enabled ] = true
  63 + project_options[:wall_enabled] = true
  64 + project_options[:wiki_enabled] = true
  65 + project_options[:public] = true
  66 + project = Gitlab.create_project(self.identifier, project_options)
  67 +
  68 + #Create Web Hook for Jenkins' integration
  69 +# Gitlab.add_project_hook(project.id, "#{self.jenkins[:url]}/gitlab/build_now")
  70 +# createJenkinsJob(project.name, project.path_with_namespace, project.web_url, project.http_url_to_repo)
  71 +
  72 + rescue Gitlab::Error::NotFound, Gitlab::Error::Parsing
  73 + #Project already exists
  74 + end
  75 +
  76 + self.gitlab[:errors] = nil
  77 + end
  78 +
  79 + # set an API endpoint
  80 + def gitlab_host
  81 + self.serpro_integration_plugin[:gitlab_host]
  82 + end
  83 +
  84 + # set a user private token
  85 + def gitlab_private_token
  86 + self.serpro_integration_plugin[:gitlab_private_token]
  87 + end
  88 +
  89 + ##########################################
  90 + # Sonar stuff #
  91 + ##########################################
  92 +
  93 +# after_create :create_sonar_project
  94 +
  95 + def sonar= params
  96 + self.serpro_integration_plugin[:sonar] = params
  97 + end
  98 +
  99 + def sonar
  100 + self.serpro_integration_plugin[:sonar] ||= {}
  101 + self.serpro_integration_plugin[:sonar]
  102 + end
  103 +
  104 + ##########################################
  105 + # Jenkins stuff #
  106 + ##########################################
  107 +
  108 +# after_create :create_jenkis_project
  109 +
  110 + def jenkins= params
  111 + self.serpro_integration_plugin[:jenkins] = params
  112 + end
  113 +
  114 + def jenkins
  115 + self.serpro_integration_plugin[:jenkins] ||= {}
  116 + url = "#{self.serpro_integration_plugin[:jenkins][:host]}:"
  117 + url += "#{self.serpro_integration_plugin[:jenkins][:port]}/"
  118 + url += "#{self.serpro_integration_plugin[:jenkins][:context_name]}"
  119 + self.serpro_integration_plugin[:jenkins][:url] = url
  120 + self.serpro_integration_plugin[:jenkins]
  121 + end
  122 +
  123 +
  124 + #FIXME make jenkins integration works
  125 + def create_jenkis_project
  126 +#(projectName, repositoryPath, webUrl, gitUrl)
  127 +
  128 + @client = JenkinsApi::Client.new(:server_url => "#{$jenkins_url}/",
  129 + :password => $jenkins_private_token,
  130 + :username => $jenkins_user)
  131 +
  132 + xmlJenkins = "
  133 + <maven2-moduleset plugin='maven-plugin@1.509'>
  134 + <actions/>
  135 + <description>Projeto criado para o repositório #{repositoryPath} do Gitlab - #{webUrl}</description>
  136 + <logRotator class='hudson.tasks.LogRotator'>
  137 + <daysToKeep>-1</daysToKeep>
  138 + <numToKeep>2</numToKeep>
  139 + <artifactDaysToKeep>-1</artifactDaysToKeep>
  140 + <artifactNumToKeep>-1</artifactNumToKeep>
  141 + </logRotator>
  142 + <keepDependencies>false</keepDependencies>
  143 + <properties/>
  144 + <scm class='hudson.plugins.git.GitSCM' plugin='git@2.2.1'>
  145 + <configVersion>2</configVersion>
  146 + <userRemoteConfigs>
  147 + <hudson.plugins.git.UserRemoteConfig>
  148 + <url>#{gitUrl}</url>
  149 + </hudson.plugins.git.UserRemoteConfig>
  150 + </userRemoteConfigs>
  151 + <branches>
  152 + <hudson.plugins.git.BranchSpec>
  153 + <name>*/master</name>
  154 + </hudson.plugins.git.BranchSpec>
  155 + </branches>
  156 + <doGenerateSubmoduleConfigurations>false</doGenerateSubmoduleConfigurations>
  157 + <submoduleCfg class='list'/>
  158 + <extensions/>
  159 + </scm>
  160 + <canRoam>true</canRoam>
  161 + <disabled>false</disabled>
  162 + <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
  163 + <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
  164 + <jdk>(Inherit From Job)</jdk>
  165 + <triggers class='vector'/>
  166 + <concurrentBuild>false</concurrentBuild>
  167 + <goals>clean package install deploy</goals>
  168 + <aggregatorStyleBuild>true</aggregatorStyleBuild>
  169 + <incrementalBuild>false</incrementalBuild>
  170 + <perModuleEmail>true</perModuleEmail>
  171 + <ignoreUpstremChanges>false</ignoreUpstremChanges>
  172 + <archivingDisabled>false</archivingDisabled>
  173 + <resolveDependencies>false</resolveDependencies>
  174 + <processPlugins>false</processPlugins>
  175 + <mavenValidationLevel>-1</mavenValidationLevel>
  176 + <runHeadless>false</runHeadless>
  177 + <disableTriggerDownstreamProjects>false</disableTriggerDownstreamProjects>
  178 + <settings class='jenkins.mvn.DefaultSettingsProvider'/>
  179 + <globalSettings class='jenkins.mvn.DefaultGlobalSettingsProvider'/>
  180 + <reporters>
  181 + <hudson.maven.reporters.MavenMailer>
  182 + <recipients/>
  183 + <dontNotifyEveryUnstableBuild>false</dontNotifyEveryUnstableBuild>
  184 + <sendToIndividuals>true</sendToIndividuals>
  185 + <perModuleEmail>true</perModuleEmail>
  186 + </hudson.maven.reporters.MavenMailer>
  187 + </reporters>
  188 + <publishers>
  189 + <hudson.plugins.sonar.SonarPublisher plugin='sonar@2.1'>
  190 + <jdk>(Inherit From Job)</jdk>
  191 + <branch/>
  192 + <language/>
  193 + <mavenOpts/>
  194 + <jobAdditionalProperties/>
  195 + <settings class='jenkins.mvn.DefaultSettingsProvider'/>
  196 + <globalSettings class='jenkins.mvn.DefaultGlobalSettingsProvider'/>
  197 + <usePrivateRepository>false</usePrivateRepository>
  198 + </hudson.plugins.sonar.SonarPublisher>
  199 + </publishers>
  200 + <buildWrappers/>
  201 + <prebuilders/>
  202 + <postbuilders/>
  203 + <runPostStepsIfResult>
  204 + <name>FAILURE</name>
  205 + <ordinal>2</ordinal>
  206 + <color>RED</color>
  207 + </runPostStepsIfResult>
  208 + </maven2-moduleset>
  209 + "
  210 +
  211 + begin
  212 + @client.job.create(projectName, xmlJenkins)
  213 + rescue JenkinsApi::Exceptions::ApiException
  214 +
  215 + end
  216 +
  217 + end
  218 +
  219 +
  220 +end
... ...
lib/ext/profile.rb 0 → 100644
  1 +++ a/lib/ext/profile.rb
... ... @@ -0,0 +1,9 @@
  1 +require_dependency 'profile'
  2 +
  3 +class Profile
  4 + settings_items :allow_sonar_integration, :type => :boolean, :default => true
  5 + settings_items :allow_gitlab_integration, :type => :boolean, :default => true
  6 +
  7 + #FIXME make test for default option
  8 + settings_items :serpro_integration_plugin, :type => Hash, :default => {}
  9 +end
... ...
lib/serpro_integration_plugin.rb 0 → 100644
  1 +++ a/lib/serpro_integration_plugin.rb
... ... @@ -0,0 +1,64 @@
  1 +class SerproIntegrationPlugin < Noosfero::Plugin
  2 +
  3 +#include ActionController::UrlWriter
  4 +# include ActionView::Helpers::TagHelper
  5 +# include ActionView::Helpers::FormTagHelper
  6 +# include FormsHelper
  7 +
  8 +
  9 +# include ActionView::Helpers
  10 +# include FormsHelper
  11 +
  12 + def self.plugin_name
  13 + "Serpro Integration Plugin"
  14 + end
  15 +
  16 + def self.plugin_description
  17 + _("Make integration with serpro servers.")
  18 + end
  19 +
  20 +# def filter_comment(c)
  21 +# c.reject! unless logged_in? || allowed_by_profile
  22 +# end
  23 +
  24 + #FIXME make this test
  25 + # User could not have this block
  26 + def self.extra_blocks
  27 + { SonarPlugin::SonarWidgetBlock => {:type => [Community] },
  28 + SonarPlugin::SmileBlock => {:type => [Community] }
  29 + }
  30 + end
  31 +
  32 + #FIXME make this test
  33 + def profile_editor_extras
  34 + lambda do
  35 + render :file => 'profile-editor-extras'
  36 + end
  37 + end
  38 +
  39 + def profile_id
  40 + context.profile
  41 + end
  42 +
  43 + def stylesheet?
  44 + true
  45 + end
  46 +
  47 +# FIXME make this test
  48 + def js_files
  49 + ['smile_face.js']
  50 + end
  51 +
  52 +# def body_beginning
  53 +# "<meta name='profile.allow_unauthenticated_comments'/>" if allowed_by_profile
  54 +# end
  55 +#
  56 +# protected
  57 +#
  58 +# delegate :logged_in?, :to => :context
  59 +#
  60 +# def allowed_by_profile
  61 +# context.profile && context.profile.allow_unauthenticated_comments
  62 +# end
  63 +
  64 +end
... ...
lib/serpro_integration_plugin/smile_block.rb 0 → 100644
  1 +++ a/lib/serpro_integration_plugin/smile_block.rb
... ... @@ -0,0 +1,100 @@
  1 +require 'open-uri'
  2 +require 'json'
  3 +
  4 +class SonarPlugin::SmileBlock < Block
  5 +
  6 +
  7 + METRIC_SUCCESS_DENSITY = 'test_success_density'
  8 + METRIC_LOC = 'ncloc'
  9 + METRIC_UNCOVERED_LINE = 'uncovered_lines'
  10 + METRIC_COVERAGE = 'coverage'
  11 +
  12 + #FIXME make this test
  13 + settings_items :sonar_info, :type => Hash, :default => {}
  14 +
  15 + def self.description
  16 + _('Sonar Smile')
  17 + end
  18 +
  19 + def help
  20 + _('This block adds a smile face that make tecnical debits visible with funny way.')
  21 + end
  22 +
  23 + #FIXME make this test
  24 + def sonar_host
  25 + self.owner.sonar_plugin['host']
  26 + end
  27 +
  28 + #FIXME make this test
  29 + def sonar_project
  30 + self.owner.sonar_plugin['project'] #|| ''
  31 + end
  32 +
  33 +#FIXME make this test
  34 + def smile_factor
  35 + collect_sonar_information
  36 + factor = (self.sonar_info[METRIC_COVERAGE] * self.sonar_info[METRIC_SUCCESS_DENSITY]).to_f/1000
  37 + factor
  38 + end
  39 +
  40 + #FIXME make this test
  41 + def content(args={})
  42 + smile_face_id = 'smileFace-' + self.id.to_s
  43 +
  44 + content_tag(:div,
  45 + content_tag(:canvas, '', :id => smile_face_id, :width => '95%', :height => '95%' ) +
  46 + "<script type='text/javascript'>drawFace('#{smile_face_id}', '#{self.smile_factor}')</script>",
  47 + :class => 'smile'
  48 + )
  49 + end
  50 +
  51 + #FIXME make this test
  52 + def self.metrics
  53 + [
  54 + METRIC_SUCCESS_DENSITY,
  55 + METRIC_LOC,
  56 + METRIC_UNCOVERED_LINE,
  57 + METRIC_COVERAGE
  58 + ]
  59 + end
  60 +
  61 + private
  62 +
  63 + #FIXME make this test
  64 + def collect_sonar_information
  65 + return false unless cache_expired?
  66 + begin
  67 + data = open("#{self.sonar_host}/api/resources?resource=#{self.sonar_project}&metrics=ncloc,coverage,weighted_violations,uncovered_lines,test_success_density&format=json").read
  68 + self.sonar_info = parse_sonar_resource(JSON.parse(data).first)
  69 + rescue
  70 + self.sonar_info = {}
  71 + end
  72 + self.sonar_info[:updated_at] = Time.now
  73 + self.save
  74 + end
  75 +
  76 + #FIXME make this test
  77 + def parse_sonar_resource(data)
  78 + parsed_data = {}
  79 + return {} if data['msr'].nil?
  80 + data['msr'].map do |metric|
  81 + self.class.metrics.map do |defined_metric|
  82 + parsed_data[defined_metric] = metric['val'] if metric['key'] == defined_metric
  83 + end
  84 + end
  85 + parsed_data
  86 + end
  87 +
  88 + #FIXME make this test
  89 + def cache_expired?
  90 + return true if self.sonar_info[:updated_at].nil?
  91 + (Time.now - self.sonar_info[:updated_at]) > cache_timestamp
  92 + end
  93 +
  94 + #FIXME make this test
  95 + # Cach time to load new data in seconds
  96 + def cache_timestamp
  97 + 60 * 60
  98 + end
  99 +
  100 +end
... ...
lib/serpro_integration_plugin/sonar_widget_block.rb 0 → 100644
  1 +++ a/lib/serpro_integration_plugin/sonar_widget_block.rb
... ... @@ -0,0 +1,81 @@
  1 +class SerproIntegrationrPlugin::SonarWidgetBlock < Block
  2 +
  3 + #FIXME make this test
  4 + AVAILABLE_WIDGETS = {
  5 + 'project_motion_chart' => 'Project Motion Chart',
  6 + 'timeline' => 'Timeline',
  7 + 'complexity' => 'Complexity'
  8 + }
  9 +
  10 + #FIXME make this test. Make test for default widget
  11 + settings_items :widget, :type => String, :default => 'timeline'
  12 +
  13 + def self.description
  14 + _('Sonar Widgets')
  15 + end
  16 +
  17 + def help
  18 + _('This block adds sonar widgets on profile.')
  19 + end
  20 +
  21 + #FIXME make this test
  22 + def sonar_host
  23 + self.owner.serpro_integration_plugin['host']
  24 + end
  25 +
  26 + #FIXME make this test
  27 + def sonar_project
  28 + self.owner.serpro_integration_plugin['project']
  29 + end
  30 +
  31 + #FIXME make this test
  32 + def widget_url
  33 + self.sonar_host + 'widget?id=' + self.widget + '&resource=' + self.sonar_project + '&metric1=complexity&metric2=ncloc'
  34 + end
  35 +
  36 + #FIXME make this test
  37 + def is_widget_well_formed_url?
  38 + !self.widget_url.match(/http[s]?:\/\/[\w|.|\/]+\/widget\?id=[\w]+&resource=[\w|\W]+/).nil?
  39 + end
  40 +
  41 + #FIXME make this test
  42 + def widget_width
  43 + case widget
  44 + when 'project_motion_chart'
  45 + '360px'
  46 + when 'timeline'
  47 + '100%'
  48 + when 'complexity'
  49 + '100%'
  50 + else
  51 + '300px'
  52 + end
  53 + end
  54 +
  55 + #FIXME make this test
  56 + def widget_height
  57 + case widget
  58 + when 'project_motion_chart'
  59 + '450px'
  60 + when 'timeline'
  61 + '205px'
  62 + when 'complexity'
  63 + '170px'
  64 + else
  65 + '300px'
  66 + end
  67 + end
  68 +
  69 + def content(args={})
  70 +# render this url
  71 +#http://sonar.serpro/widget?id=timeline&resource=br.gov.fazenda.coaf.siscoaf:siscoaf-parent&metric1=complexity&metric2=ncloc
  72 +
  73 + block = self
  74 +
  75 + lambda do
  76 + render :file => 'sonar_widget_block', :locals => { :block => block }
  77 + end
  78 +
  79 + end
  80 +
  81 +end
... ...
public/smile_face.js 0 → 100644
  1 +++ a/public/smile_face.js
... ... @@ -0,0 +1,129 @@
  1 +function drawBaseFace(element_id) {
  2 + var canvas = document.getElementById(element_id);
  3 + var ctx = canvas.getContext("2d");
  4 +
  5 + var x = canvas.width / 2;
  6 + var y = canvas.height / 2;
  7 + var radius = canvas.width/2 - 1;
  8 + var startAngle = 0;
  9 + var endAngle = 2 * Math.PI;
  10 +
  11 + ctx.beginPath();
  12 + ctx.arc(x, y, radius, startAngle, endAngle);
  13 + ctx.stroke();
  14 + ctx.fillStyle = "yellow";
  15 + ctx.fill();
  16 +}
  17 +
  18 +function drawSmile(element_id, factor){
  19 + var canvas = document.getElementById(element_id);
  20 + var ctx = canvas.getContext("2d");
  21 + var x = canvas.width / 2;
  22 + var y = canvas.height / 2
  23 + var radius = canvas.width/2 - canvas.width/4;
  24 +
  25 + var startAngle = 0;
  26 + var endAngle = 0;
  27 + var delta = 0;
  28 +
  29 + ctx.beginPath();
  30 + ctx.lineWidth = canvas.width/100 * 2;
  31 +
  32 + if (factor >= 0 && factor < 5){
  33 + //Draw sadness mouth
  34 + delta = 0.5 - factor* 0.1
  35 + startAngle = (1.5 - delta) * Math.PI;
  36 + endAngle = (1.5 + delta) * Math.PI;
  37 + radius = radius - radius*delta;
  38 + y = y + (radius + radius*(1 - delta));
  39 + ctx.arc(x, y, radius, startAngle, endAngle);
  40 + } else if (factor == 5) {
  41 + //Draw normal mouth
  42 + ctx.moveTo(x-canvas.width/8,y+canvas.width/8);
  43 + ctx.lineTo(x-canvas.width/8+(canvas.width/4),y+canvas.width/8);
  44 + } else if (factor > 5 && factor <= 10) {
  45 + //Draw happiness mouth
  46 + delta = 1 - factor * 0.1
  47 + startAngle = delta * Math.PI;
  48 + endAngle = (1 - delta) * Math.PI;
  49 + radius = radius - radius*delta;
  50 + y = y - (radius - radius*(1-delta));
  51 + ctx.arc(x, y, radius, startAngle, endAngle);
  52 + }else{
  53 + //Draw scare mouth
  54 + radius = radius*0.4
  55 + y = y + radius;
  56 + ctx.ellipse(x,y,radius*0.4, radius,0,0,2 * Math.PI, false);
  57 + ctx.fill();
  58 + }
  59 +
  60 +
  61 + // line color
  62 + ctx.strokeStyle = 'black';
  63 + ctx.stroke();
  64 +}
  65 +
  66 +function drawEyes(element_id){
  67 + var canvas = document.getElementById(element_id);
  68 + var ctx = canvas.getContext("2d");
  69 + var centerX = canvas.width/5;
  70 + var centerY = 0;
  71 + var radius = canvas.width * 0.05;
  72 +
  73 + // save state
  74 + ctx.save();
  75 +
  76 + // translate context so height is 1/3'rd from top of enclosing circle
  77 + ctx.translate(canvas.width / 2, canvas.height / 3);
  78 +
  79 + // scale context horizontally by 50%
  80 + ctx.scale(.5, 1);
  81 +
  82 + // draw circle which will be stretched into an oval
  83 + ctx.beginPath();
  84 + ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
  85 +
  86 + // restore to original state
  87 + ctx.restore();
  88 +
  89 + // apply styling
  90 + ctx.fillStyle = 'black';
  91 + ctx.fill();
  92 + ctx.lineWidth = 2;
  93 + ctx.strokeStyle = 'black';
  94 + ctx.stroke();
  95 +
  96 +
  97 + //left eye
  98 + centerX = centerX * -1;
  99 +
  100 + // save state
  101 + ctx.save();
  102 +
  103 + // translate context so height is 1/3'rd from top of enclosing circle
  104 + ctx.translate(canvas.width / 2, canvas.height / 3);
  105 +
  106 + // scale context horizontally by 50%
  107 + ctx.scale(.5, 1);
  108 +
  109 + // draw circle which will be stretched into an oval
  110 + ctx.beginPath();
  111 + ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
  112 +
  113 + // restore to original state
  114 + ctx.restore();
  115 +
  116 + // apply styling
  117 + ctx.fillStyle = 'black';
  118 + ctx.fill();
  119 + ctx.lineWidth = 2;
  120 + ctx.strokeStyle = 'black';
  121 + ctx.stroke();
  122 +}
  123 +
  124 +
  125 +function drawFace(element_id, factor){
  126 + drawBaseFace(element_id);
  127 + drawEyes(element_id);
  128 + drawSmile(element_id, factor);
  129 +}
... ...
public/style.css 0 → 100644
  1 +++ a/public/style.css
... ... @@ -0,0 +1,3 @@
  1 +.serpro-integration-plugin_smile-block .smile {
  2 + text-align: center;
  3 +}
... ...
smile.html 0 → 100644
  1 +++ a/smile.html
... ... @@ -0,0 +1,142 @@
  1 +<html>
  2 + <head> </head>
  3 + <body>
  4 + <canvas id="smileFace" width="60" height="60"></canvas>
  5 +<script>
  6 +
  7 +function drawBaseFace() {
  8 + var canvas = document.getElementById("smileFace");
  9 + var ctx = canvas.getContext("2d");
  10 +
  11 + var x = canvas.width / 2;
  12 + var y = canvas.height / 2;
  13 + var radius = canvas.width/2 - 1;
  14 + var startAngle = 0;
  15 + var endAngle = 2 * Math.PI;
  16 +
  17 + ctx.beginPath();
  18 + ctx.arc(x, y, radius, startAngle, endAngle);
  19 + ctx.stroke();
  20 + ctx.fillStyle = "yellow";
  21 + ctx.fill();
  22 +}
  23 +
  24 +function drawSmile(factor){
  25 + var canvas = document.getElementById("smileFace");
  26 + var ctx = canvas.getContext("2d");
  27 + var x = canvas.width / 2;
  28 + var y = canvas.height / 2
  29 + var radius = canvas.width/2 - canvas.width/4;
  30 +
  31 + var startAngle = 0;
  32 + var endAngle = 0;
  33 + var delta = 0;
  34 +
  35 + ctx.beginPath();
  36 + ctx.lineWidth = canvas.width/100 * 2;
  37 +
  38 + if (factor >= 0 && factor < 5){
  39 + //Draw sadness mouth
  40 + delta = 0.5 - factor* 0.1
  41 + startAngle = (1.5 - delta) * Math.PI;
  42 + endAngle = (1.5 + delta) * Math.PI;
  43 + radius = radius - radius*delta;
  44 + y = y + (radius + radius*(1 - delta));
  45 + ctx.arc(x, y, radius, startAngle, endAngle);
  46 + } else if (factor == 5) {
  47 + //Draw normal mouth
  48 + ctx.moveTo(x-canvas.width/8,y+canvas.width/8);
  49 + ctx.lineTo(x-canvas.width/8+(canvas.width/4),y+canvas.width/8);
  50 + } else if (factor > 5 && factor <= 10) {
  51 + //Draw happiness mouth
  52 + delta = 1 - factor * 0.1
  53 + startAngle = delta * Math.PI;
  54 + endAngle = (1 - delta) * Math.PI;
  55 + radius = radius - radius*delta;
  56 + y = y - (radius - radius*(1-delta));
  57 + ctx.arc(x, y, radius, startAngle, endAngle);
  58 + }else{
  59 + //Draw scare mouth
  60 + radius = radius*0.4
  61 + y = y + radius;
  62 + ctx.ellipse(x,y,radius*0.4, radius,0,0,2 * Math.PI, false);
  63 + ctx.fill();
  64 + }
  65 +
  66 +
  67 + // line color
  68 + ctx.strokeStyle = 'black';
  69 + ctx.stroke();
  70 +}
  71 +
  72 +function drawEyes(){
  73 + var canvas = document.getElementById("smileFace");
  74 + var ctx = canvas.getContext("2d");
  75 + var centerX = canvas.width/5;
  76 + var centerY = 0;
  77 + var radius = canvas.width * 0.05;
  78 +
  79 + // save state
  80 + ctx.save();
  81 +
  82 + // translate context so height is 1/3'rd from top of enclosing circle
  83 + ctx.translate(canvas.width / 2, canvas.height / 3);
  84 +
  85 + // scale context horizontally by 50%
  86 + ctx.scale(.5, 1);
  87 +
  88 + // draw circle which will be stretched into an oval
  89 + ctx.beginPath();
  90 + ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
  91 +
  92 + // restore to original state
  93 + ctx.restore();
  94 +
  95 + // apply styling
  96 + ctx.fillStyle = 'black';
  97 + ctx.fill();
  98 + ctx.lineWidth = 2;
  99 + ctx.strokeStyle = 'black';
  100 + ctx.stroke();
  101 +
  102 +
  103 + //left eye
  104 + centerX = centerX * -1;
  105 +
  106 + // save state
  107 + ctx.save();
  108 +
  109 + // translate context so height is 1/3'rd from top of enclosing circle
  110 + ctx.translate(canvas.width / 2, canvas.height / 3);
  111 +
  112 + // scale context horizontally by 50%
  113 + ctx.scale(.5, 1);
  114 +
  115 + // draw circle which will be stretched into an oval
  116 + ctx.beginPath();
  117 + ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
  118 +
  119 + // restore to original state
  120 + ctx.restore();
  121 +
  122 + // apply styling
  123 + ctx.fillStyle = 'black';
  124 + ctx.fill();
  125 + ctx.lineWidth = 2;
  126 + ctx.strokeStyle = 'black';
  127 + ctx.stroke();
  128 +}
  129 +
  130 +
  131 +function drawFace(factor){
  132 + drawBaseFace();
  133 + drawEyes();
  134 + drawSmile(factor);
  135 +}
  136 +
  137 +drawFace(9);
  138 +
  139 +</script>
  140 +
  141 + </body>
  142 +</html>
... ...
test/unit/sonar_plugin_test.rb 0 → 100644
  1 +++ a/test/unit/sonar_plugin_test.rb
... ... @@ -0,0 +1,41 @@
  1 +require File.dirname(__FILE__) + '/../../../../test/test_helper'
  2 +
  3 +class RequireAuthToCommentPluginTest < ActiveSupport::TestCase
  4 +
  5 + def setup
  6 + @plugin = RequireAuthToCommentPlugin.new
  7 + @comment = Comment.new
  8 + end
  9 +
  10 + attr_reader :plugin, :comment
  11 +
  12 + should 'reject comments for unauthenticated users' do
  13 + plugin.context = logged_in(false)
  14 + plugin.filter_comment(comment)
  15 + assert comment.rejected?
  16 + end
  17 +
  18 + should 'allow comments from authenticated users' do
  19 + plugin.context = logged_in(true)
  20 + plugin.filter_comment(comment)
  21 + assert !comment.rejected?
  22 + end
  23 +
  24 + should 'allow comments from unauthenticated users if allowed by profile' do
  25 + plugin.context = logged_in(false)
  26 + plugin.context.profile.allow_unauthenticated_comments = true
  27 +
  28 + plugin.filter_comment(comment)
  29 + assert !comment.rejected?
  30 + end
  31 +
  32 + protected
  33 +
  34 + def logged_in(boolean)
  35 + controller = mock()
  36 + controller.stubs(:logged_in?).returns(boolean)
  37 + controller.stubs(:profile).returns(Profile.new)
  38 + controller
  39 + end
  40 +
  41 +end
... ...
views/profile-editor-extras.html.erb 0 → 100644
  1 +++ a/views/profile-editor-extras.html.erb
... ... @@ -0,0 +1,49 @@
  1 +
  2 +<div id='serpro-integration'>
  3 + <div id='gitlab' class='gitlab'>
  4 + <%= render :partial => 'gitlab' %>
  5 + </div>
  6 +
  7 + <div>
  8 + <%= labelled_check_box(_('Uses sonar integration'), 'profile_data[allow_sonar_integration]', true, profile.allow_sonar_integration) %>
  9 + </div>
  10 +
  11 + <ul class='sonar'>
  12 + <h2><%= _('Sonar Integration') %></h2>
  13 + <li>
  14 + <%= labelled_text_field(_('Server Host'), 'profile_data[sonar]host]', profile.sonar[:host]) %>
  15 + </li>
  16 + <li>
  17 + <%= labelled_text_field(_('Project: '), 'profile_data[sonar][project]', profile.sonar[:project]) %>
  18 + </li>
  19 + </ul>
  20 +
  21 +
  22 + <div>
  23 + <%= labelled_check_box(_('Uses Jenkins integration'), 'profile_data[allow_jenkins_integration]', true, profile.allow_jenkins_integration) %>
  24 + </div>
  25 +
  26 + <ul class='jenkins'>
  27 + <h2><%= _('Jenkins Integration') %></h2>
  28 + <li>
  29 + <%= labelled_text_field(_('Server Host'), 'profile_data[jenkins][host]', profile.jenkins[:host]) %>
  30 + </li>
  31 + <li>
  32 + <%= labelled_text_field(_('Server Port'), 'profile_data[jenkins][port]', profile.jenkins[:port]) %>
  33 + </li>
  34 + <li>
  35 + <%= labelled_text_field(_('Server Context Name'), 'profile_data[jenkins][context_name]', profile.jenkins[:context_name]) %>
  36 + </li>
  37 + <li>
  38 + <%= labelled_text_field(_('Server User'), 'profile_data[jenkins][user]', profile.jenkins[:user]) %>
  39 + </li>
  40 + <li>
  41 + <%= labelled_text_field(_('Server Private Token'), 'profile_data[jenkins][private_token]', profile.jenkins[:private_token]) %>
  42 + </li>
  43 + <li>
  44 + <%= labelled_text_field(_('Server Url'), 'profile_data[jenkins][url]', profile.jenkins[:url], {:disabled => true}) %>
  45 + </li>
  46 +
  47 + </ul>
  48 +
  49 +</div>
... ...
views/profile_design/sonar_plugin/_sonar_widget_block.rhtml 0 → 100644
  1 +++ a/views/profile_design/sonar_plugin/_sonar_widget_block.rhtml
... ... @@ -0,0 +1,4 @@
  1 +
  2 +<%= labelled_form_field(_('Widget'), select(:block, :widget, SerproIntegrationPlugin::SonarWidgetBlock::AVAILABLE_WIDGETS.map {|id, name| [name, id]} )) %>
  3 +
  4 +
... ...
views/profile_editor/_gitlab.html.erb 0 → 100644
  1 +++ a/views/profile_editor/_gitlab.html.erb
... ... @@ -0,0 +1,22 @@
  1 +<div class='option'>
  2 + <%= labelled_check_box(_('Uses Gitlab integration'), 'profile_data[allow_gitlab_integration]', true, profile.allow_gitlab_integration) %>
  3 +</div>
  4 +
  5 +<ul>
  6 + <h2>
  7 + <%= _('Gitlab Integration') %>
  8 + <%= link_to_remote _('Force'), :url => {:controller => 'serpro_integration_plugin_myprofile', :action => 'create_gitlab'} %>
  9 +
  10 + </h2>
  11 + <li>
  12 + <%= labelled_text_field(_('Server Host'), 'profile_data[gitlab][host]', profile.gitlab[:host]) %>
  13 + </li>
  14 + <li>
  15 + <%= labelled_text_field(_('User private token: '), 'profile_data[gitlab][private_token]', profile.gitlab[:private_token]) %>
  16 + </li>
  17 + <li>
  18 + <%= _('Erros: %s') % (profile.gitlab[:errors] || _('None')) %>
  19 + </li>
  20 +
  21 +</ul>
  22 +
... ...
views/sonar_widget_block.rhtml 0 → 100644
  1 +++ a/views/sonar_widget_block.rhtml
... ... @@ -0,0 +1,10 @@
  1 +<div class='widget'>
  2 + <% if block.is_widget_well_formed_url? %>
  3 + <iframe width='<%=block.widget_width%>' height='<%=block.widget_height%>' src='<%=block.widget_url%>' frameborder="0" allowfullscreen></iframe>
  4 + <% else %>
  5 + <div class='wrong'>
  6 + <iframe width='<%=block.widget_width%>' height='<%=block.widget_height%>' src='<%=block.widget_url%>' frameborder="0" allowfullscreen></iframe>
  7 + <%= _('Something wrong happened. Please see your sonar configuration.') %>
  8 + </div>
  9 + <% end %>
  10 +</div>
... ...