Commit 16268a09da714c7ff8493296a765350de0a53889
0 parents
Exists in
master
merging with branch AI3033-serpro_integration of rails 2.3
Showing
16 changed files
with
904 additions
and
0 deletions
Show diff stats
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 | ... | ... |
| 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 | ... | ... |
| 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 | ... | ... |
| 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 | ... | ... |
| 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 | ... | ... |
| 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 | ... | ... |
| 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 | ... | ... |
| 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 | +} | ... | ... |
| 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> | ... | ... |
| 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 | ... | ... |
| 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_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 | + | ... | ... |
| 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> | ... | ... |