Commit 7a1494a24ac6f7761bb59c8776c0aa77a56a4e41

Authored by Leandro Santos
2 parents 804762ae 0de5286e

Merge branches 'rails3_AI3033-serpro_integration' and 'rails3_stable' into rails3_stable

Showing 29 changed files with 1137 additions and 2 deletions   Show diff stats
lib/acts_as_having_settings.rb
... ... @@ -44,7 +44,11 @@ module ActsAsHavingSettings
44 44 class_eval <<-CODE
45 45 def #{setting}
46 46 val = send(self.class.settings_field)[:#{setting}]
47   - val.nil? ? (#{default}.is_a?(String) ? gettext(#{default}) : #{default}) : val
  47 + if val.nil?
  48 + val = #{default}.is_a?(String) ? gettext(#{default}) : #{default}
  49 + send(self.class.settings_field)[:#{setting}] = val
  50 + end
  51 + val
48 52 end
49 53 def #{setting}=(value)
50 54 h = send(self.class.settings_field).clone
... ...
plugins/community_track/public/public 0 → 120000
... ... @@ -0,0 +1 @@
  1 +/root/noosfero/config/plugins/community_track/public
0 2 \ No newline at end of file
... ...
plugins/serpro_integration/Gemfile 0 → 100644
... ... @@ -0,0 +1,2 @@
  1 +gem 'gitlab', '~> 3.1.0'
  2 +gem 'jenkins_api_client', '~> 0.14.1'
... ...
plugins/serpro_integration/controllers/serpro_integration_plugin_admin_controller.rb 0 → 100644
... ... @@ -0,0 +1,15 @@
  1 +class SerproIntegrationPluginAdminController < AdminController
  2 +
  3 + def index
  4 + settings = params[:settings]
  5 + settings ||= {}
  6 +
  7 + @settings = Noosfero::Plugin::Settings.new(environment, SerproIntegrationPlugin, settings)
  8 + if request.post?
  9 + @settings.save!
  10 + session[:notice] = 'Settings succefully saved.'
  11 + redirect_to :action => 'index'
  12 + end
  13 + end
  14 +
  15 +end
... ...
plugins/serpro_integration/controllers/serpro_integration_plugin_myprofile_controller.rb 0 → 100644
... ... @@ -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
... ...
plugins/serpro_integration/features/sonar.feature 0 → 100644
... ... @@ -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
... ...
plugins/serpro_integration/install.rb 0 → 100644
... ... @@ -0,0 +1,2 @@
  1 +system "gem install --user-install gitlab"
  2 +system "gem install --user-install jenkins_api_client"
... ...
plugins/serpro_integration/lib/ext/community.rb 0 → 100644
... ... @@ -0,0 +1,68 @@
  1 +require_dependency 'community'
  2 +
  3 +class Community
  4 +
  5 + settings_items :allow_sonar_integration, :type => :boolean, :default => true
  6 + settings_items :allow_gitlab_integration, :type => :boolean, :default => true
  7 + settings_items :allow_jenkins_integration, :type => :boolean, :default => true
  8 +
  9 + settings_items :serpro_integration_plugin_gitlab, :type => Hash, :default => {}
  10 + settings_items :serpro_integration_plugin_jenkins, :type => Hash, :default => {}
  11 + settings_items :serpro_integration_plugin_sonar, :type => Hash, :default => {}
  12 +
  13 + attr_accessible :allow_unauthenticated_comments, :allow_gitlab_integration, :allow_sonar_integration, :allow_jenkins_integration, :serpro_integration_plugin_gitlab, :serpro_integration_plugin_jenkins, :serpro_integration_plugin_sonar
  14 +
  15 + before_update :create_integration_projects
  16 +
  17 + def create_integration_projects
  18 + return unless setting_changed?(:serpro_integration_plugin_gitlab)
  19 +
  20 + if allow_gitlab_integration
  21 + gitlab_integration = SerproIntegrationPlugin::GitlabIntegration.new(gitlab_host, gitlab_private_token)
  22 + gitlab_project = gitlab_integration.create_gitlab_project(self)
  23 + serpro_integration_plugin_gitlab[:project_id] = gitlab_project.id
  24 +
  25 + if allow_jenkins_integration
  26 + jenkins_integration = SerproIntegrationPlugin::JenkinsIntegration.new(jenkins_host, jenkins_private_token, jenkins_user)
  27 + jenkins_integration.create_jenkis_project(self, gitlab_project.path_with_namespace, gitlab_project.web_url, gitlab_project.http_url_to_repo)
  28 + end
  29 + end
  30 + end
  31 +
  32 + def serpro_integration_plugin_settings
  33 + @settings ||= Noosfero::Plugin::Settings.new(environment, SerproIntegrationPlugin)
  34 + end
  35 +
  36 + def gitlab_group
  37 + serpro_integration_plugin_gitlab[:group] || self.identifier
  38 + end
  39 +
  40 + def gitlab_project_name
  41 + serpro_integration_plugin_gitlab[:project_name] || self.identifier
  42 + end
  43 +
  44 + def gitlab_host
  45 + serpro_integration_plugin_settings.gitlab[:host]
  46 + end
  47 +
  48 + def gitlab_private_token
  49 + serpro_integration_plugin_settings.gitlab[:private_token]
  50 + end
  51 +
  52 + def jenkins_host
  53 + serpro_integration_plugin_settings.jenkins[:host]
  54 + end
  55 +
  56 + def jenkins_private_token
  57 + serpro_integration_plugin_settings.jenkins[:private_token]
  58 + end
  59 +
  60 + def jenkins_user
  61 + serpro_integration_plugin_settings.jenkins[:user]
  62 + end
  63 +
  64 + def jenkins_project_name
  65 + serpro_integration_plugin_jenkins[:project_name] || self.identifier
  66 + end
  67 +
  68 +end
... ...
plugins/serpro_integration/lib/ext/profile.rb 0 → 100644
... ... @@ -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
... ...
plugins/serpro_integration/lib/serpro_integration_plugin.rb 0 → 100644
... ... @@ -0,0 +1,39 @@
  1 +class SerproIntegrationPlugin < Noosfero::Plugin
  2 +
  3 + def self.plugin_name
  4 + "Serpro Integration Plugin"
  5 + end
  6 +
  7 + def self.plugin_description
  8 + _("Make integration with serpro servers.")
  9 + end
  10 +
  11 + #FIXME make this test
  12 + # User could not have this block
  13 + def self.extra_blocks
  14 + { SerproIntegrationPlugin::SonarWidgetBlock => {:type => [Community] },
  15 + SerproIntegrationPlugin::SmileBlock => {:type => [Community] },
  16 + }
  17 + end
  18 +
  19 + #FIXME make this test
  20 + def profile_editor_extras
  21 + lambda do
  22 + render :file => 'profile-editor-extras' if profile.kind_of?(Community)
  23 + end
  24 + end
  25 +
  26 + def profile_id
  27 + context.profile
  28 + end
  29 +
  30 + def stylesheet?
  31 + true
  32 + end
  33 +
  34 +# FIXME make this test
  35 + def js_files
  36 + ['smile_face.js']
  37 + end
  38 +
  39 +end
... ...
plugins/serpro_integration/lib/serpro_integration_plugin/gitlab_integration.rb 0 → 100644
... ... @@ -0,0 +1,62 @@
  1 +require 'gitlab'
  2 +
  3 +class SerproIntegrationPlugin::GitlabIntegration
  4 +
  5 + def initialize(host, private_token)
  6 + @client = Gitlab.client(:endpoint => host, :private_token => private_token)
  7 + end
  8 +
  9 + def create_group(group_name)
  10 + #FIXME find group by name
  11 + group = @client.groups.select {|group| group.name == group_name}.first
  12 + group ||= @client.create_group(group_name, group_name)
  13 + end
  14 +
  15 + def create_project(project_name, group)
  16 + path_with_namespace = "#{group.name}/#{project_name}"
  17 + #FIXME find project by namespace
  18 + project = @client.get("/projects/search/#{project_name}").select do |project|
  19 + project.path_with_namespace == path_with_namespace
  20 + end.first
  21 +
  22 + if project.nil?
  23 + project_options = {}
  24 + project_options[:namespace_id] = group.id
  25 + project_options[:issues_enabled ] = true
  26 + project_options[:wall_enabled] = true
  27 + project_options[:wiki_enabled] = true
  28 + project_options[:public] = true
  29 +
  30 + project = @client.create_project(project_name, project_options)
  31 + #Create Web Hook for Jenkins' integration
  32 + #Gitlab.add_project_hook(project.id, "#{self.jenkins[:url]}/gitlab/build_now")
  33 + end
  34 + project
  35 + end
  36 +
  37 + def create_user(email, group)
  38 + user = @client.users(:search => email).first
  39 + username = name = email[/[^@]+/]
  40 + user ||= @client.create_user(email, '123456', {:username => username, :name => name, :provider => 'ldap'})
  41 +
  42 + begin
  43 + @client.add_group_member(group.id, user.id, 40)
  44 + rescue Gitlab::Error::Conflict => e
  45 + #already member
  46 + end
  47 + user
  48 + end
  49 +
  50 + #http://rubydoc.info/gems/gitlab/frames
  51 + def create_gitlab_project(profile)
  52 + group = create_group(profile.gitlab_group)
  53 +
  54 + #create admins and add to group
  55 + profile.admins.each do |person|
  56 + create_user(person.user.email, group)
  57 + end
  58 +
  59 + project = create_project(profile.gitlab_project_name, group)
  60 + end
  61 +
  62 +end
... ...
plugins/serpro_integration/lib/serpro_integration_plugin/gitlab_issues_block.rb 0 → 100644
... ... @@ -0,0 +1,33 @@
  1 +require 'open-uri'
  2 +require 'json'
  3 +
  4 +class SerproIntegrationPlugin::GitlabIssuesBlock < Block
  5 +
  6 + def self.description
  7 + _('Gitlab Issues')
  8 + end
  9 +
  10 + def help
  11 + _('This block list gitlab issues')
  12 + end
  13 +
  14 + #FIXME make this test
  15 + def content(args={})
  16 + gitlab_integration = SerproIntegrationPlugin::GitlabIntegration.new(owner.gitlab_host, owner.gitlab_private_token)
  17 + issues = gitlab_integration.issues(owner)
  18 + block = self
  19 + proc do
  20 + render :file => 'blocks/gitlab_issues', :locals => {:issues => issues, :block => block}
  21 + end
  22 + #content_tag(:div,
  23 + # content_tag(:canvas, '', :id => smile_face_id, :width => '95%', :height => '95%' ) +
  24 + # "<script type='text/javascript'>drawFace('#{smile_face_id}', '#{self.smile_factor}')</script>",
  25 + # :class => 'smile'
  26 + #)
  27 + end
  28 +
  29 + def cacheable?
  30 + false
  31 + end
  32 +
  33 +end
... ...
plugins/serpro_integration/lib/serpro_integration_plugin/jenkins_integration.rb 0 → 100644
... ... @@ -0,0 +1,102 @@
  1 +# encoding: UTF-8
  2 +require 'jenkins_api_client'
  3 +
  4 +class SerproIntegrationPlugin::JenkinsIntegration
  5 +
  6 + def initialize(host, private_token, user)
  7 + @client = JenkinsApi::Client.new(:server_url => host, :password => private_token, :username => user)
  8 + end
  9 +
  10 + #FIXME make jenkins integration works
  11 + def create_jenkis_project(profile, repository_path, web_url, git_url)
  12 + #begin
  13 + project_name = repository_path.split('/').last
  14 + if @client.job.list(project_name).blank?
  15 + @client.job.create(profile.jenkins_project_name, xml_jenkins(repository_path, web_url, git_url))
  16 + end
  17 + #rescue JenkinsApi::Exceptions::ApiException
  18 + #end
  19 + end
  20 +
  21 + #FIXME
  22 + def xml_jenkins(repository_path, web_url, git_url)
  23 + "
  24 + <maven2-moduleset plugin='maven-plugin@1.509'>
  25 + <actions/>
  26 + <description>Projeto criado para o repositório #{repository_path} do Gitlab - #{web_url}</description>
  27 + <logRotator class='hudson.tasks.LogRotator'>
  28 + <daysToKeep>-1</daysToKeep>
  29 + <numToKeep>2</numToKeep>
  30 + <artifactDaysToKeep>-1</artifactDaysToKeep>
  31 + <artifactNumToKeep>-1</artifactNumToKeep>
  32 + </logRotator>
  33 + <keepDependencies>false</keepDependencies>
  34 + <properties/>
  35 + <scm class='hudson.plugins.git.GitSCM' plugin='git@2.2.1'>
  36 + <configVersion>2</configVersion>
  37 + <userRemoteConfigs>
  38 + <hudson.plugins.git.UserRemoteConfig>
  39 + <url>#{git_url}</url>
  40 + </hudson.plugins.git.UserRemoteConfig>
  41 + </userRemoteConfigs>
  42 + <branches>
  43 + <hudson.plugins.git.BranchSpec>
  44 + <name>*/master</name>
  45 + </hudson.plugins.git.BranchSpec>
  46 + </branches>
  47 + <doGenerateSubmoduleConfigurations>false</doGenerateSubmoduleConfigurations>
  48 + <submoduleCfg class='list'/>
  49 + <extensions/>
  50 + </scm>
  51 + <canRoam>true</canRoam>
  52 + <disabled>false</disabled>
  53 + <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
  54 + <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
  55 + <jdk>(Inherit From Job)</jdk>
  56 + <triggers class='vector'/>
  57 + <concurrentBuild>false</concurrentBuild>
  58 + <goals>clean package install deploy</goals>
  59 + <aggregatorStyleBuild>true</aggregatorStyleBuild>
  60 + <incrementalBuild>false</incrementalBuild>
  61 + <perModuleEmail>true</perModuleEmail>
  62 + <ignoreUpstremChanges>false</ignoreUpstremChanges>
  63 + <archivingDisabled>false</archivingDisabled>
  64 + <resolveDependencies>false</resolveDependencies>
  65 + <processPlugins>false</processPlugins>
  66 + <mavenValidationLevel>-1</mavenValidationLevel>
  67 + <runHeadless>false</runHeadless>
  68 + <disableTriggerDownstreamProjects>false</disableTriggerDownstreamProjects>
  69 + <settings class='jenkins.mvn.DefaultSettingsProvider'/>
  70 + <globalSettings class='jenkins.mvn.DefaultGlobalSettingsProvider'/>
  71 + <reporters>
  72 + <hudson.maven.reporters.MavenMailer>
  73 + <recipients/>
  74 + <dontNotifyEveryUnstableBuild>false</dontNotifyEveryUnstableBuild>
  75 + <sendToIndividuals>true</sendToIndividuals>
  76 + <perModuleEmail>true</perModuleEmail>
  77 + </hudson.maven.reporters.MavenMailer>
  78 + </reporters>
  79 + <publishers>
  80 + <hudson.plugins.sonar.SonarPublisher plugin='sonar@2.1'>
  81 + <jdk>(Inherit From Job)</jdk>
  82 + <branch/>
  83 + <language/>
  84 + <mavenOpts/>
  85 + <jobAdditionalProperties/>
  86 + <settings class='jenkins.mvn.DefaultSettingsProvider'/>
  87 + <globalSettings class='jenkins.mvn.DefaultGlobalSettingsProvider'/>
  88 + <usePrivateRepository>false</usePrivateRepository>
  89 + </hudson.plugins.sonar.SonarPublisher>
  90 + </publishers>
  91 + <buildWrappers/>
  92 + <prebuilders/>
  93 + <postbuilders/>
  94 + <runPostStepsIfResult>
  95 + <name>FAILURE</name>
  96 + <ordinal>2</ordinal>
  97 + <color>RED</color>
  98 + </runPostStepsIfResult>
  99 + </maven2-moduleset>
  100 + "
  101 + end
  102 +end
... ...
plugins/serpro_integration/lib/serpro_integration_plugin/smile_block.rb 0 → 100644
... ... @@ -0,0 +1,100 @@
  1 +require 'open-uri'
  2 +require 'json'
  3 +
  4 +class SerproIntegrationPlugin::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
... ...
plugins/serpro_integration/lib/serpro_integration_plugin/sonar_widget_block.rb 0 → 100644
... ... @@ -0,0 +1,81 @@
  1 +class SerproIntegrationPlugin::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
... ...
plugins/serpro_integration/public/smile_face.js 0 → 100644
... ... @@ -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 +}
... ...
plugins/serpro_integration/public/style.css 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +.serpro-integration-plugin_smile-block .smile {
  2 + text-align: center;
  3 +}
... ...
plugins/serpro_integration/smile.html 0 → 100644
... ... @@ -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>
... ...
plugins/serpro_integration/test/unit/sonar_plugin_test.rb 0 → 100644
... ... @@ -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
... ...
plugins/serpro_integration/views/blocks/_gitlab_issue.html.erb 0 → 100644
... ... @@ -0,0 +1,6 @@
  1 +<li>
  2 + <span><%= gitlab_issue.id %></span>
  3 + <span><%= gitlab_issue.title %></span>
  4 + <span><%= gitlab_url gitlab_issue %></span>
  5 + <%= gitlab_issue.inspect %>
  6 +</li>
... ...
plugins/serpro_integration/views/blocks/gitlab_issues.html.erb 0 → 100644
... ... @@ -0,0 +1,6 @@
  1 +
  2 +<%= block_title(block.title) %>
  3 +
  4 +<ul>
  5 + <%= render :partial => 'blocks/gitlab_issue', :collection => issues %>
  6 +</ul>
... ...
plugins/serpro_integration/views/profile-editor-extras.html.erb 0 → 100644
... ... @@ -0,0 +1,40 @@
  1 +<script>
  2 +jQuery( document ).ready(function( $ ) {
  3 + function toggle_checkbox_selectors(el) {
  4 + if( el.is(':checked')) {
  5 + $(el.data('selector')).show('fast');
  6 + } else {
  7 + $(el.data('selector')).hide('fast');
  8 + }
  9 + }
  10 + $(".toggle_checkbox").click(function() {
  11 + toggle_checkbox_selectors($(this));
  12 + });
  13 + $(".toggle_checkbox").each(function() {
  14 + toggle_checkbox_selectors($(this));
  15 + });
  16 +});
  17 +</script>
  18 +
  19 +<div id='serpro-integration'>
  20 + <h2><%= _('Serpro Integration') %></h2>
  21 +
  22 + <%= render :partial => 'gitlab' %>
  23 + <%= render :partial => 'jenkins' %>
  24 +
  25 + <div id="sonar">
  26 + <h3>
  27 + <%= labelled_check_box('', 'profile_data[allow_sonar_integration]', true, profile.allow_sonar_integration, {:class => "toggle_checkbox", 'data-selector' => 'ul.sonar'}) %>
  28 + <%= _('Sonar Integration') %>
  29 + </h3>
  30 + <ul class='sonar'>
  31 + <li>
  32 + <%= labelled_text_field(_('Server Host'), 'profile_data[serpro_integration_plugin_sonar][host]', profile.serpro_integration_plugin_sonar[:host]) %>
  33 + </li>
  34 + <li>
  35 + <%= labelled_text_field(_('Project: '), 'profile_data[serpro_integration_plugin_sonar][project]', profile.serpro_integration_plugin_sonar[:project]) %>
  36 + </li>
  37 + </ul>
  38 + </div>
  39 +
  40 +</div>
... ...
plugins/serpro_integration/views/profile_design/sonar_plugin/_sonar_widget_block.rhtml 0 → 100644
... ... @@ -0,0 +1,4 @@
  1 +
  2 +<%= labelled_form_field(_('Widget'), select(:block, :widget, SerproIntegrationPlugin::SonarWidgetBlock::AVAILABLE_WIDGETS.map {|id, name| [name, id]} )) %>
  3 +
  4 +
... ...
plugins/serpro_integration/views/profile_editor/_gitlab.html.erb 0 → 100644
... ... @@ -0,0 +1,20 @@
  1 +<div id="gitlab">
  2 +
  3 +<h3>
  4 + <%= labelled_check_box('', 'profile_data[allow_gitlab_integration]', true, profile.allow_gitlab_integration, {:class => "toggle_checkbox", 'data-selector' => "ul.gitlab"}) %>
  5 + <%= _('Gitlab Integration') %>
  6 +</h3>
  7 +<ul class="gitlab">
  8 + <li>
  9 + <span class="label"><%= _('Server Host:') %><span>
  10 + <span class="value"><%= profile.gitlab_host %><span>
  11 + <%= link_to _('Force'), {:controller => 'serpro_integration_plugin_myprofile', :action => 'create_gitlab'}, {:remote => true} %>
  12 + </li>
  13 + <li>
  14 + <%= labelled_text_field(_('Group Name:'), 'profile_data[serpro_integration_plugin_gitlab][group]', profile.gitlab_group) %>
  15 + </li>
  16 + <li>
  17 + <%= labelled_text_field(_('Project Name:'), 'profile_data[serpro_integration_plugin_gitlab][project_name]', profile.gitlab_project_name ) %>
  18 + </li>
  19 +</ul>
  20 +</div>
... ...
plugins/serpro_integration/views/profile_editor/_jenkins.html.erb 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +<div id="jenkins">
  2 +<h3>
  3 + <%= labelled_check_box('', 'profile_data[allow_jenkins_integration]', true, profile.allow_jenkins_integration, {:class => "toggle_checkbox", 'data-selector' => 'ul.jenkins'}) %>
  4 + <%= _('Jenkins Integration') %>
  5 +</h3>
  6 +<ul class='jenkins'>
  7 + <li>
  8 + <%= labelled_text_field(_('Project Name:'), 'profile_data[serpro_integration_plugin_jenkins][project_name]', profile.jenkins_project_name ) %>
  9 + </li>
  10 +</ul>
  11 +</div>
... ...
plugins/serpro_integration/views/serpro_integration_plugin_admin/index.html.erb 0 → 100644
... ... @@ -0,0 +1,27 @@
  1 +<h1><%= _('Serpro Integration Settings')%></h1>
  2 +
  3 +<%= form_for(:settings) do |f| %>
  4 +
  5 + <div class="gitlab_settings">
  6 + <h2><%= _('Gitlab Settings')%></h2>
  7 + <%= f.fields_for :gitlab, OpenStruct.new(@settings.gitlab) do |g| %>
  8 + <%= labelled_form_field _('Server Host'), g.text_field("host") %>
  9 + <%= labelled_form_field _('Private Token'), g.text_field(:private_token) %>
  10 + <% end %>
  11 + </div>
  12 +
  13 + <div class="jenkins_settings">
  14 + <h2><%= _('Jenkins Settings')%></h2>
  15 + <%= f.fields_for :jenkins, OpenStruct.new(@settings.jenkins) do |g| %>
  16 + <%= labelled_form_field _('Server Host'), g.text_field("host") %>
  17 + <%= labelled_form_field _('Server User'), g.text_field(:user) %>
  18 + <%= labelled_form_field _('Private Token'), g.text_field(:private_token) %>
  19 + <% end %>
  20 + </div>
  21 +
  22 + <% button_bar do %>
  23 + <%= submit_button(:save, _('Save'), :cancel => {:controller => 'plugins', :action => 'index'}) %>
  24 + <% end %>
  25 +
  26 +<% end %>
  27 +
... ...
plugins/serpro_integration/views/sonar_widget_block.rhtml 0 → 100644
... ... @@ -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>
... ...
test/fixtures/files/500.html
... ... @@ -1 +0,0 @@
1   -../../../public/500.html
2 0 \ No newline at end of file
test/fixtures/files/500.html 0 → 100644
... ... @@ -0,0 +1,151 @@
  1 +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  2 +<html xmlns="http://www.w3.org/1999/xhtml" >
  3 + <head>
  4 + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  5 + <meta http-equiv="refresh" content="60"/>
  6 + <title>Technical problems</title>
  7 + <link rel="stylesheet" type="text/css" href="/designs/themes/default/errors.css"/>
  8 + <link rel="shortcut icon" href='/designs/themes/default/favicon.ico' type="image/x-icon" />
  9 + <script type='text/javascript' src='/javascripts/prototype.js'></script>
  10 + <script type='text/javascript' src='/javascripts/errors.js'></script>
  11 +</head>
  12 +<body onload='display_error_message()'>
  13 + <div id='wrap'>
  14 + <div id='header'>
  15 + <div id='logo'>
  16 + &nbsp;
  17 + </div>
  18 + </div>
  19 +
  20 +
  21 + <div id='de' style='display: none' class='message'>
  22 + <h1>Kurzzeitiges Systemproblem</h1>
  23 + <p>
  24 + Unser technisches Team arbeitet gerade daran, bitte probieren Sie es nachher erneut. Wir entschuldigen uns für die Unannehmlichkeiten.
  25 + </p>
  26 + <ul>
  27 + <li><a href='javascript: history.back()'>Zurück</a></li>
  28 + <li><a href='/'>Gehe zur Homepage</a></li>
  29 + </ul>
  30 + </div>
  31 +
  32 +
  33 + <div id='en' style='display: none' class='message'>
  34 + <h1>Temporary system problem</h1>
  35 + <p>
  36 + Our technical team is working on it, please try again later. Sorry for the inconvenience.
  37 + </p>
  38 + <ul>
  39 + <li><a href='javascript: history.back()'>Go back</a></li>
  40 + <li><a href='/'>Go to the site home page</a></li>
  41 + </ul>
  42 + </div>
  43 +
  44 +
  45 + <div id='eo' style='display: none' class='message'>
  46 + <h1>Temporary system problem</h1>
  47 + <p>
  48 + Our technical team is working on it, please try again later. Sorry for the inconvenience.
  49 + </p>
  50 + <ul>
  51 + <li><a href='javascript: history.back()'>Go back</a></li>
  52 + <li><a href='/'>Go to the site home page</a></li>
  53 + </ul>
  54 + </div>
  55 +
  56 +
  57 + <div id='es' style='display: none' class='message'>
  58 + <h1>Problema temporal del sistema</h1>
  59 + <p>
  60 + Nuestro equipo técnico está trabajando en ello, por favor, inténtalo de nuevo más tarde. Disculpa las molestias.
  61 + </p>
  62 + <ul>
  63 + <li><a href='javascript: history.back()'>Regresar</a></li>
  64 + <li><a href='/'>Ir a la página de inicio del sitio</a></li>
  65 + </ul>
  66 + </div>
  67 +
  68 +
  69 + <div id='fr' style='display: none' class='message'>
  70 + <h1>Problème temporaire du système.</h1>
  71 + <p>
  72 + Notre équipe technique est en train d'y travailler. Merci de réessayer plus tard. Nous sommes désolés de la gêne occasionnée.
  73 + </p>
  74 + <ul>
  75 + <li><a href='javascript: history.back()'>Retour</a></li>
  76 + <li><a href='/'>Aller à la page d'accueil du site</a></li>
  77 + </ul>
  78 + </div>
  79 +
  80 +
  81 + <div id='hy' style='display: none' class='message'>
  82 + <h1>Temporary system problem</h1>
  83 + <p>
  84 + Our technical team is working on it, please try again later. Sorry for the inconvenience.
  85 + </p>
  86 + <ul>
  87 + <li><a href='javascript: history.back()'>Վերադառնալ</a></li>
  88 + <li><a href='/'>Go to the site home page</a></li>
  89 + </ul>
  90 + </div>
  91 +
  92 +
  93 + <div id='it' style='display: none' class='message'>
  94 + <h1>Temporary system problem</h1>
  95 + <p>
  96 + Our technical team is working on it, please try again later. Sorry for the inconvenience.
  97 + </p>
  98 + <ul>
  99 + <li><a href='javascript: history.back()'>Go back</a></li>
  100 + <li><a href='/'>Go to the site home page</a></li>
  101 + </ul>
  102 + </div>
  103 +
  104 +
  105 + <div id='pt' style='display: none' class='message'>
  106 + <h1>Problema temporário no sistema</h1>
  107 + <p>
  108 + Nossa equipe técnica está trabalhando nele, por favor tente mais tarde. Perdoe o incoveniente.
  109 + </p>
  110 + <ul>
  111 + <li><a href='javascript: history.back()'>Voltar</a></li>
  112 + <li><a href='/'>Ir para a página inicial do site.</a></li>
  113 + </ul>
  114 + </div>
  115 +
  116 +
  117 + <div id='ru' style='display: none' class='message'>
  118 + <h1>Временная ошибка системы</h1>
  119 + <p>
  120 + Техники уже работают над проблемой, пожалуйста, попробуйте позже.
  121 + </p>
  122 + <ul>
  123 + <li><a href='javascript: history.back()'>Назад</a></li>
  124 + <li><a href='/'>Перейти на домашнюю страницу сайта</a></li>
  125 + </ul>
  126 + </div>
  127 +
  128 + <div id='languages'>
  129 +
  130 + <a href="javascript: display_error_message('de')">Deutsch</a>
  131 +
  132 + <a href="javascript: display_error_message('en')">English</a>
  133 +
  134 + <a href="javascript: display_error_message('eo')">Esperanto</a>
  135 +
  136 + <a href="javascript: display_error_message('es')">Español</a>
  137 +
  138 + <a href="javascript: display_error_message('fr')">Français</a>
  139 +
  140 + <a href="javascript: display_error_message('hy')">հայերեն լեզու</a>
  141 +
  142 + <a href="javascript: display_error_message('it')">Italiano</a>
  143 +
  144 + <a href="javascript: display_error_message('pt')">Português</a>
  145 +
  146 + <a href="javascript: display_error_message('ru')">русский язык</a>
  147 +
  148 + </div>
  149 + </div>
  150 +</body>
  151 +</html>
... ...