mirror of
				https://github.com/gitbucket/gitbucket.git
				synced 2025-10-31 10:36:05 +01:00 
			
		
		
		
	Compare commits
	
		
			1 Commits
		
	
	
		
			4.34.0
			...
			disable-gi
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | f16f395539 | 
							
								
								
									
										10
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							| @@ -14,16 +14,6 @@ jobs: | |||||||
|         java: [8, 11]     |         java: [8, 11]     | ||||||
|     steps: |     steps: | ||||||
|     - uses: actions/checkout@v2 |     - uses: actions/checkout@v2 | ||||||
|     - name: Cache |  | ||||||
|       uses: actions/cache@v2 |  | ||||||
|       env: |  | ||||||
|         cache-name: cache-sbt-libs |  | ||||||
|       with: |  | ||||||
|         path: | |  | ||||||
|           ~/.ivy2/cache |  | ||||||
|           ~/.sbt |  | ||||||
|           ~/.cache/coursier/v1 |  | ||||||
|         key: build-${{ env.cache-name }}-${{ hashFiles('build.sbt') }} |  | ||||||
|     - name: Set up JDK |     - name: Set up JDK | ||||||
|       uses: actions/setup-java@v1 |       uses: actions/setup-java@v1 | ||||||
|       with: |       with: | ||||||
|   | |||||||
							
								
								
									
										16
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -1,22 +1,6 @@ | |||||||
| # Changelog | # Changelog | ||||||
| All changes to the project will be documented in this file. | All changes to the project will be documented in this file. | ||||||
|  |  | ||||||
| ### 4.34.0 - 26 Jul 2020 |  | ||||||
|  |  | ||||||
| - Enhancement admin settings UI |  | ||||||
|    - File upload settings |  | ||||||
|    - Restrict repository operations |  | ||||||
|    - User-defined CSS |  | ||||||
|    - Limit the repository list in the sidebar |  | ||||||
| - Improve MariaDB support |  | ||||||
| - Improve activity logging |  | ||||||
| - CLI option to persist session on disk in the standalone mode |  | ||||||
| - Web API updates |  | ||||||
|   - Add [list commits API](https://developer.github.com/v3/repos/commits/#list-commits) |  | ||||||
| - Bundled plugins updates |  | ||||||
|   - [gitbucket-gist-plugin](https://github.com/gitbucket/gitbucket-gist-plugin) 4.18.0 -> 4.19.0 |  | ||||||
|   - [gitbucket-notifications-plugin](https://github.com/gitbucket/gitbucket-notifications-plugin) 1.8.0 -> 1.9.0 |  | ||||||
|  |  | ||||||
| ### 4.33.0 - 31 Dec 2019 | ### 4.33.0 - 31 Dec 2019 | ||||||
|  |  | ||||||
| - All CLI options are configurable by environment variables | - All CLI options are configurable by environment variables | ||||||
|   | |||||||
							
								
								
									
										19
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								README.md
									
									
									
									
									
								
							| @@ -57,20 +57,11 @@ Support | |||||||
|  |  | ||||||
| What's New in 4.33.x | What's New in 4.33.x | ||||||
| ------------- | ------------- | ||||||
| ### 4.34.0 - 26 Jul 2020 | ### 4.33.0 - 31 Dec 2019 | ||||||
|  |  | ||||||
| - Enhancement admin settings UI | - All CLI options are configurable by environment variables | ||||||
|    - File upload settings | - Folding pull request files | ||||||
|    - Restrict repository operations | - WebHook security options | ||||||
|    - User-defined CSS | - Add assignee and assignees properties to some Web APIs' response | ||||||
|    - Limit the repository list in the sidebar |  | ||||||
| - Improve MariaDB support |  | ||||||
| - Improve activity logging |  | ||||||
| - CLI option to persist session on disk in the standalone mode |  | ||||||
| - Web API updates |  | ||||||
|   - Add [list commits API](https://developer.github.com/v3/repos/commits/#list-commits) |  | ||||||
| - Bundled plugins updates |  | ||||||
|   - [gitbucket-gist-plugin](https://github.com/gitbucket/gitbucket-gist-plugin) 4.18.0 -> 4.19.0 |  | ||||||
|   - [gitbucket-notifications-plugin](https://github.com/gitbucket/gitbucket-notifications-plugin) 1.8.0 -> 1.9.0 |  | ||||||
|  |  | ||||||
| See the [change log](CHANGELOG.md) for all of the updates. | See the [change log](CHANGELOG.md) for all of the updates. | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ import com.typesafe.sbt.pgp.PgpKeys._ | |||||||
|  |  | ||||||
| val Organization = "io.github.gitbucket" | val Organization = "io.github.gitbucket" | ||||||
| val Name = "gitbucket" | val Name = "gitbucket" | ||||||
| val GitBucketVersion = "4.34.0" | val GitBucketVersion = "4.33.0" | ||||||
| val ScalatraVersion = "2.7.0-RC1" | val ScalatraVersion = "2.7.0-RC1" | ||||||
| val JettyVersion = "9.4.30.v20200611" | val JettyVersion = "9.4.30.v20200611" | ||||||
| val JgitVersion = "5.8.0.202006091008-r" | val JgitVersion = "5.8.0.202006091008-r" | ||||||
| @@ -54,9 +54,11 @@ libraryDependencies ++= Seq( | |||||||
|   "ch.qos.logback"                  % "logback-classic"              % "1.2.3", |   "ch.qos.logback"                  % "logback-classic"              % "1.2.3", | ||||||
|   "com.zaxxer"                      % "HikariCP"                     % "3.4.5", |   "com.zaxxer"                      % "HikariCP"                     % "3.4.5", | ||||||
|   "com.typesafe"                    % "config"                       % "1.4.0", |   "com.typesafe"                    % "config"                       % "1.4.0", | ||||||
|  |   "com.typesafe.akka"               %% "akka-actor"                  % "2.5.27", | ||||||
|   "fr.brouillard.oss.security.xhub" % "xhub4j-core"                  % "1.1.0", |   "fr.brouillard.oss.security.xhub" % "xhub4j-core"                  % "1.1.0", | ||||||
|   "com.github.bkromhout"            % "java-diff-utils"              % "2.1.1", |   "com.github.bkromhout"            % "java-diff-utils"              % "2.1.1", | ||||||
|   "org.cache2k"                     % "cache2k-all"                  % "1.2.4.Final", |   "org.cache2k"                     % "cache2k-all"                  % "1.2.4.Final", | ||||||
|  |   "com.enragedginger"               %% "akka-quartz-scheduler"       % "1.8.1-akka-2.5.x" exclude ("com.mchange", "c3p0") exclude ("com.zaxxer", "HikariCP-java6"), | ||||||
|   "net.coobird"                     % "thumbnailator"                % "0.4.11", |   "net.coobird"                     % "thumbnailator"                % "0.4.11", | ||||||
|   "com.github.zafarkhaja"           % "java-semver"                  % "0.9.0", |   "com.github.zafarkhaja"           % "java-semver"                  % "0.9.0", | ||||||
|   "com.nimbusds"                    % "oauth2-oidc-sdk"              % "5.64.4", |   "com.nimbusds"                    % "oauth2-oidc-sdk"              % "5.64.4", | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| notifications:1.9.0 | notifications:1.8.0 | ||||||
| gist:4.19.0 | gist:4.18.0 | ||||||
| emoji:4.6.0 | emoji:4.6.0 | ||||||
| pages:1.8.0 | pages:1.8.0 | ||||||
|   | |||||||
| @@ -1,4 +0,0 @@ | |||||||
| <?xml version="1.0" encoding="UTF-8"?> |  | ||||||
| <changeSet> |  | ||||||
|   <dropTable tableName="ACTIVITY" /> |  | ||||||
| </changeSet> |  | ||||||
| @@ -1,22 +1,7 @@ | |||||||
| package gitbucket.core | package gitbucket.core | ||||||
|  |  | ||||||
| import java.io.FileOutputStream | import io.github.gitbucket.solidbase.migration.{SqlMigration, LiquibaseMigration} | ||||||
| import java.nio.charset.StandardCharsets | import io.github.gitbucket.solidbase.model.{Version, Module} | ||||||
| import java.sql.Connection |  | ||||||
| import java.util |  | ||||||
| import java.util.UUID |  | ||||||
|  |  | ||||||
| import gitbucket.core.model.Activity |  | ||||||
| import gitbucket.core.util.Directory.ActivityLog |  | ||||||
| import gitbucket.core.util.JDBCUtil |  | ||||||
| import io.github.gitbucket.solidbase.Solidbase |  | ||||||
| import io.github.gitbucket.solidbase.migration.{LiquibaseMigration, Migration, SqlMigration} |  | ||||||
| import io.github.gitbucket.solidbase.model.{Module, Version} |  | ||||||
| import org.json4s.NoTypeHints |  | ||||||
| import org.json4s.jackson.Serialization |  | ||||||
| import org.json4s.jackson.Serialization.write |  | ||||||
|  |  | ||||||
| import scala.util.Using |  | ||||||
|  |  | ||||||
| object GitBucketCoreModule | object GitBucketCoreModule | ||||||
|     extends Module( |     extends Module( | ||||||
| @@ -80,38 +65,5 @@ object GitBucketCoreModule | |||||||
|       new Version("4.31.1"), |       new Version("4.31.1"), | ||||||
|       new Version("4.31.2"), |       new Version("4.31.2"), | ||||||
|       new Version("4.32.0", new LiquibaseMigration("update/gitbucket-core_4.32.xml")), |       new Version("4.32.0", new LiquibaseMigration("update/gitbucket-core_4.32.xml")), | ||||||
|       new Version("4.33.0"), |       new Version("4.33.0") | ||||||
|       new Version( |  | ||||||
|         "4.34.0", |  | ||||||
|         new Migration() { |  | ||||||
|           override def migrate(moduleId: String, version: String, context: util.Map[String, AnyRef]): Unit = { |  | ||||||
|             implicit val formats = Serialization.formats(NoTypeHints) |  | ||||||
|             import JDBCUtil._ |  | ||||||
|  |  | ||||||
|             val conn = context.get(Solidbase.CONNECTION).asInstanceOf[Connection] |  | ||||||
|             val list = conn.select("SELECT * FROM ACTIVITY ORDER BY ACTIVITY_ID") { |  | ||||||
|               rs => |  | ||||||
|                 Activity( |  | ||||||
|                   activityId = UUID.randomUUID().toString, |  | ||||||
|                   userName = rs.getString("USER_NAME"), |  | ||||||
|                   repositoryName = rs.getString("REPOSITORY_NAME"), |  | ||||||
|                   activityUserName = rs.getString("ACTIVITY_USER_NAME"), |  | ||||||
|                   activityType = rs.getString("ACTIVITY_TYPE"), |  | ||||||
|                   message = rs.getString("MESSAGE"), |  | ||||||
|                   additionalInfo = { |  | ||||||
|                     val additionalInfo = rs.getString("ADDITIONAL_INFO") |  | ||||||
|                     if (rs.wasNull()) None else Some(additionalInfo) |  | ||||||
|                   }, |  | ||||||
|                   activityDate = rs.getTimestamp("ACTIVITY_DATE") |  | ||||||
|                 ) |  | ||||||
|             } |  | ||||||
|             Using.resource(new FileOutputStream(ActivityLog, true)) { out => |  | ||||||
|               list.foreach { activity => |  | ||||||
|                 out.write((write(activity) + "\n").getBytes(StandardCharsets.UTF_8)) |  | ||||||
|               } |  | ||||||
|             } |  | ||||||
|           } |  | ||||||
|         }, |  | ||||||
|         new LiquibaseMigration("update/gitbucket-core_4.34.xml") |  | ||||||
|       ) |  | ||||||
|     ) |     ) | ||||||
|   | |||||||
| @@ -35,7 +35,6 @@ class AccountController | |||||||
|     with WebHookService |     with WebHookService | ||||||
|     with PrioritiesService |     with PrioritiesService | ||||||
|     with RepositoryCreationService |     with RepositoryCreationService | ||||||
|     with RequestCache |  | ||||||
|  |  | ||||||
| trait AccountControllerBase extends AccountManagementControllerBase { | trait AccountControllerBase extends AccountManagementControllerBase { | ||||||
|   self: AccountService |   self: AccountService | ||||||
|   | |||||||
| @@ -52,7 +52,6 @@ class ApiController | |||||||
|     with ReferrerAuthenticator |     with ReferrerAuthenticator | ||||||
|     with ReadableUsersAuthenticator |     with ReadableUsersAuthenticator | ||||||
|     with WritableUsersAuthenticator |     with WritableUsersAuthenticator | ||||||
|     with RequestCache |  | ||||||
|  |  | ||||||
| trait ApiControllerBase extends ControllerBase { | trait ApiControllerBase extends ControllerBase { | ||||||
|  |  | ||||||
|   | |||||||
| @@ -22,7 +22,6 @@ class DashboardController | |||||||
|     with WebHookPullRequestReviewCommentService |     with WebHookPullRequestReviewCommentService | ||||||
|     with MilestonesService |     with MilestonesService | ||||||
|     with UsersAuthenticator |     with UsersAuthenticator | ||||||
|     with RequestCache |  | ||||||
|  |  | ||||||
| trait DashboardControllerBase extends ControllerBase { | trait DashboardControllerBase extends ControllerBase { | ||||||
|   self: IssuesService with PullRequestService with RepositoryService with AccountService with UsersAuthenticator => |   self: IssuesService with PullRequestService with RepositoryService with AccountService with UsersAuthenticator => | ||||||
|   | |||||||
| @@ -29,7 +29,6 @@ class IndexController | |||||||
|     with AccessTokenService |     with AccessTokenService | ||||||
|     with AccountFederationService |     with AccountFederationService | ||||||
|     with OpenIDConnectService |     with OpenIDConnectService | ||||||
|     with RequestCache |  | ||||||
|  |  | ||||||
| trait IndexControllerBase extends ControllerBase { | trait IndexControllerBase extends ControllerBase { | ||||||
|   self: RepositoryService |   self: RepositoryService | ||||||
| @@ -79,7 +78,7 @@ trait IndexControllerBase extends ControllerBase { | |||||||
|       } |       } | ||||||
|       .getOrElse { |       .getOrElse { | ||||||
|         gitbucket.core.html.index( |         gitbucket.core.html.index( | ||||||
|           getRecentPublicActivities(), |           getRecentActivities(), | ||||||
|           getVisibleRepositories(None, withoutPhysicalInfo = true), |           getVisibleRepositories(None, withoutPhysicalInfo = true), | ||||||
|           showBannerToCreatePersonalAccessToken = false |           showBannerToCreatePersonalAccessToken = false | ||||||
|         ) |         ) | ||||||
| @@ -162,7 +161,7 @@ trait IndexControllerBase extends ControllerBase { | |||||||
|  |  | ||||||
|   get("/activities.atom") { |   get("/activities.atom") { | ||||||
|     contentType = "application/atom+xml; type=feed" |     contentType = "application/atom+xml; type=feed" | ||||||
|     xml.feed(getRecentPublicActivities()) |     xml.feed(getRecentActivities()) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   post("/sidebar-collapse") { |   post("/sidebar-collapse") { | ||||||
|   | |||||||
| @@ -30,7 +30,6 @@ class IssuesController | |||||||
|     with WebHookPullRequestReviewCommentService |     with WebHookPullRequestReviewCommentService | ||||||
|     with CommitsService |     with CommitsService | ||||||
|     with PrioritiesService |     with PrioritiesService | ||||||
|     with RequestCache |  | ||||||
|  |  | ||||||
| trait IssuesControllerBase extends ControllerBase { | trait IssuesControllerBase extends ControllerBase { | ||||||
|   self: IssuesService |   self: IssuesService | ||||||
|   | |||||||
| @@ -36,7 +36,6 @@ class PullRequestsController | |||||||
|     with MergeService |     with MergeService | ||||||
|     with ProtectedBranchService |     with ProtectedBranchService | ||||||
|     with PrioritiesService |     with PrioritiesService | ||||||
|     with RequestCache |  | ||||||
|  |  | ||||||
| trait PullRequestsControllerBase extends ControllerBase { | trait PullRequestsControllerBase extends ControllerBase { | ||||||
|   self: RepositoryService |   self: RepositoryService | ||||||
|   | |||||||
| @@ -2,14 +2,7 @@ package gitbucket.core.controller | |||||||
|  |  | ||||||
| import java.io.File | import java.io.File | ||||||
|  |  | ||||||
| import gitbucket.core.service.{ | import gitbucket.core.service.{AccountService, ActivityService, PaginationHelper, ReleaseService, RepositoryService} | ||||||
|   AccountService, |  | ||||||
|   ActivityService, |  | ||||||
|   PaginationHelper, |  | ||||||
|   ReleaseService, |  | ||||||
|   RepositoryService, |  | ||||||
|   RequestCache |  | ||||||
| } |  | ||||||
| import gitbucket.core.util._ | import gitbucket.core.util._ | ||||||
| import gitbucket.core.util.Directory._ | import gitbucket.core.util.Directory._ | ||||||
| import gitbucket.core.util.Implicits._ | import gitbucket.core.util.Implicits._ | ||||||
| @@ -29,7 +22,6 @@ class ReleaseController | |||||||
|     with ReadableUsersAuthenticator |     with ReadableUsersAuthenticator | ||||||
|     with ReferrerAuthenticator |     with ReferrerAuthenticator | ||||||
|     with WritableUsersAuthenticator |     with WritableUsersAuthenticator | ||||||
|     with RequestCache |  | ||||||
|  |  | ||||||
| trait ReleaseControllerBase extends ControllerBase { | trait ReleaseControllerBase extends ControllerBase { | ||||||
|   self: RepositoryService |   self: RepositoryService | ||||||
|   | |||||||
| @@ -30,10 +30,8 @@ class RepositorySettingsController | |||||||
|     with ProtectedBranchService |     with ProtectedBranchService | ||||||
|     with CommitStatusService |     with CommitStatusService | ||||||
|     with DeployKeyService |     with DeployKeyService | ||||||
|     with ActivityService |  | ||||||
|     with OwnerAuthenticator |     with OwnerAuthenticator | ||||||
|     with UsersAuthenticator |     with UsersAuthenticator | ||||||
|     with RequestCache |  | ||||||
|  |  | ||||||
| trait RepositorySettingsControllerBase extends ControllerBase { | trait RepositorySettingsControllerBase extends ControllerBase { | ||||||
|   self: RepositoryService |   self: RepositoryService | ||||||
| @@ -42,7 +40,6 @@ trait RepositorySettingsControllerBase extends ControllerBase { | |||||||
|     with ProtectedBranchService |     with ProtectedBranchService | ||||||
|     with CommitStatusService |     with CommitStatusService | ||||||
|     with DeployKeyService |     with DeployKeyService | ||||||
|     with ActivityService |  | ||||||
|     with OwnerAuthenticator |     with OwnerAuthenticator | ||||||
|     with UsersAuthenticator => |     with UsersAuthenticator => | ||||||
|  |  | ||||||
| @@ -100,7 +97,9 @@ trait RepositorySettingsControllerBase extends ControllerBase { | |||||||
|       "events" -> webhookEvents, |       "events" -> webhookEvents, | ||||||
|       "ctype" -> label("ctype", text()), |       "ctype" -> label("ctype", text()), | ||||||
|       "token" -> optional(trim(label("token", text(maxlength(100))))) |       "token" -> optional(trim(label("token", text(maxlength(100))))) | ||||||
|     )((url, events, ctype, token) => WebHookForm(url, events, WebHookContentType.valueOf(ctype), token)) |     )( | ||||||
|  |       (url, events, ctype, token) => WebHookForm(url, events, WebHookContentType.valueOf(ctype), token) | ||||||
|  |     ) | ||||||
|  |  | ||||||
|   // for rename repository |   // for rename repository | ||||||
|   case class RenameRepositoryForm(repositoryName: String) |   case class RenameRepositoryForm(repositoryName: String) | ||||||
| @@ -252,10 +251,9 @@ trait RepositorySettingsControllerBase extends ControllerBase { | |||||||
|    * Send the test request to registered web hook URLs. |    * Send the test request to registered web hook URLs. | ||||||
|    */ |    */ | ||||||
|   ajaxPost("/:owner/:repository/settings/hooks/test")(ownerOnly { repository => |   ajaxPost("/:owner/:repository/settings/hooks/test")(ownerOnly { repository => | ||||||
|     def _headers(h: Array[org.apache.http.Header]): Array[Array[String]] = |     def _headers(h: Array[org.apache.http.Header]): Array[Array[String]] = h.map { h => | ||||||
|       h.map { h => |       Array(h.getName, h.getValue) | ||||||
|         Array(h.getName, h.getValue) |     } | ||||||
|       } |  | ||||||
|  |  | ||||||
|     Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { |     Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { | ||||||
|       git => |       git => | ||||||
| @@ -373,15 +371,7 @@ trait RepositorySettingsControllerBase extends ControllerBase { | |||||||
|   post("/:owner/:repository/settings/rename", renameForm)(ownerOnly { (form, repository) => |   post("/:owner/:repository/settings/rename", renameForm)(ownerOnly { (form, repository) => | ||||||
|     if (context.settings.repositoryOperation.rename || context.loginAccount.get.isAdmin) { |     if (context.settings.repositoryOperation.rename || context.loginAccount.get.isAdmin) { | ||||||
|       if (repository.name != form.repositoryName) { |       if (repository.name != form.repositoryName) { | ||||||
|         // Update database and move git repository |  | ||||||
|         renameRepository(repository.owner, repository.name, repository.owner, form.repositoryName) |         renameRepository(repository.owner, repository.name, repository.owner, form.repositoryName) | ||||||
|         // Record activity log |  | ||||||
|         recordRenameRepositoryActivity( |  | ||||||
|           repository.owner, |  | ||||||
|           form.repositoryName, |  | ||||||
|           repository.name, |  | ||||||
|           context.loginAccount.get.userName |  | ||||||
|         ) |  | ||||||
|       } |       } | ||||||
|       redirect(s"/${repository.owner}/${form.repositoryName}") |       redirect(s"/${repository.owner}/${form.repositoryName}") | ||||||
|     } else Forbidden() |     } else Forbidden() | ||||||
| @@ -394,15 +384,7 @@ trait RepositorySettingsControllerBase extends ControllerBase { | |||||||
|     if (context.settings.repositoryOperation.transfer || context.loginAccount.get.isAdmin) { |     if (context.settings.repositoryOperation.transfer || context.loginAccount.get.isAdmin) { | ||||||
|       // Change repository owner |       // Change repository owner | ||||||
|       if (repository.owner != form.newOwner) { |       if (repository.owner != form.newOwner) { | ||||||
|         // Update database and move git repository |  | ||||||
|         renameRepository(repository.owner, repository.name, form.newOwner, repository.name) |         renameRepository(repository.owner, repository.name, form.newOwner, repository.name) | ||||||
|         // Record activity log |  | ||||||
|         recordRenameRepositoryActivity( |  | ||||||
|           form.newOwner, |  | ||||||
|           repository.name, |  | ||||||
|           repository.owner, |  | ||||||
|           context.loginAccount.get.userName |  | ||||||
|         ) |  | ||||||
|       } |       } | ||||||
|       redirect(s"/${form.newOwner}/${repository.name}") |       redirect(s"/${form.newOwner}/${repository.name}") | ||||||
|     } else Forbidden() |     } else Forbidden() | ||||||
| @@ -453,34 +435,32 @@ trait RepositorySettingsControllerBase extends ControllerBase { | |||||||
|   /** |   /** | ||||||
|    * Provides duplication check for web hook url. |    * Provides duplication check for web hook url. | ||||||
|    */ |    */ | ||||||
|   private def webHook(needExists: Boolean): Constraint = |   private def webHook(needExists: Boolean): Constraint = new Constraint() { | ||||||
|     new Constraint() { |     override def validate(name: String, value: String, messages: Messages): Option[String] = | ||||||
|       override def validate(name: String, value: String, messages: Messages): Option[String] = |       if (getWebHook(params("owner"), params("repository"), value).isDefined != needExists) { | ||||||
|         if (getWebHook(params("owner"), params("repository"), value).isDefined != needExists) { |         Some(if (needExists) { | ||||||
|           Some(if (needExists) { |           "URL had not been registered yet." | ||||||
|             "URL had not been registered yet." |  | ||||||
|           } else { |  | ||||||
|             "URL had been registered already." |  | ||||||
|           }) |  | ||||||
|         } else { |         } else { | ||||||
|           None |           "URL had been registered already." | ||||||
|         } |         }) | ||||||
|     } |       } else { | ||||||
|  |         None | ||||||
|   private def webhookEvents = |  | ||||||
|     new ValueType[Set[WebHook.Event]] { |  | ||||||
|       def convert(name: String, params: Map[String, Seq[String]], messages: Messages): Set[WebHook.Event] = { |  | ||||||
|         WebHook.Event.values.flatMap { t => |  | ||||||
|           params.get(name + "." + t.name).map(_ => t) |  | ||||||
|         }.toSet |  | ||||||
|       } |       } | ||||||
|       def validate(name: String, params: Map[String, Seq[String]], messages: Messages): Seq[(String, String)] = |   } | ||||||
|         if (convert(name, params, messages).isEmpty) { |  | ||||||
|           Seq(name -> messages("error.required").format(name)) |   private def webhookEvents = new ValueType[Set[WebHook.Event]] { | ||||||
|         } else { |     def convert(name: String, params: Map[String, Seq[String]], messages: Messages): Set[WebHook.Event] = { | ||||||
|           Nil |       WebHook.Event.values.flatMap { t => | ||||||
|         } |         params.get(name + "." + t.name).map(_ => t) | ||||||
|  |       }.toSet | ||||||
|     } |     } | ||||||
|  |     def validate(name: String, params: Map[String, Seq[String]], messages: Messages): Seq[(String, String)] = | ||||||
|  |       if (convert(name, params, messages).isEmpty) { | ||||||
|  |         Seq(name -> messages("error.required").format(name)) | ||||||
|  |       } else { | ||||||
|  |         Nil | ||||||
|  |       } | ||||||
|  |   } | ||||||
|  |  | ||||||
| //  /** | //  /** | ||||||
| //   * Provides Constraint to validate the collaborator name. | //   * Provides Constraint to validate the collaborator name. | ||||||
| @@ -500,77 +480,70 @@ trait RepositorySettingsControllerBase extends ControllerBase { | |||||||
|   /** |   /** | ||||||
|    * Duplicate check for the rename repository name. |    * Duplicate check for the rename repository name. | ||||||
|    */ |    */ | ||||||
|   private def renameRepositoryName: Constraint = |   private def renameRepositoryName: Constraint = new Constraint() { | ||||||
|     new Constraint() { |     override def validate( | ||||||
|       override def validate( |       name: String, | ||||||
|         name: String, |       value: String, | ||||||
|         value: String, |       params: Map[String, Seq[String]], | ||||||
|         params: Map[String, Seq[String]], |       messages: Messages | ||||||
|         messages: Messages |     ): Option[String] = { | ||||||
|       ): Option[String] = { |       for { | ||||||
|         for { |         repoName <- params.optionValue("repository") if repoName != value | ||||||
|           repoName <- params.optionValue("repository") if repoName != value |         userName <- params.optionValue("owner") | ||||||
|           userName <- params.optionValue("owner") |         _ <- getRepositoryNamesOfUser(userName).find(_ == value) | ||||||
|           _ <- getRepositoryNamesOfUser(userName).find(_ == value) |       } yield { | ||||||
|         } yield { |         "Repository already exists." | ||||||
|           "Repository already exists." |  | ||||||
|         } |  | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|  |    * | ||||||
|    */ |    */ | ||||||
|   private def featureOption: Constraint = |   private def featureOption: Constraint = new Constraint() { | ||||||
|     new Constraint() { |     override def validate( | ||||||
|       override def validate( |       name: String, | ||||||
|         name: String, |       value: String, | ||||||
|         value: String, |       params: Map[String, Seq[String]], | ||||||
|         params: Map[String, Seq[String]], |       messages: Messages | ||||||
|         messages: Messages |     ): Option[String] = | ||||||
|       ): Option[String] = |       if (Seq("DISABLE", "PRIVATE", "PUBLIC", "ALL").contains(value)) None else Some("Option is invalid.") | ||||||
|         if (Seq("DISABLE", "PRIVATE", "PUBLIC", "ALL").contains(value)) None else Some("Option is invalid.") |   } | ||||||
|     } |  | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|    * Provides Constraint to validate the repository transfer user. |    * Provides Constraint to validate the repository transfer user. | ||||||
|    */ |    */ | ||||||
|   private def transferUser: Constraint = |   private def transferUser: Constraint = new Constraint() { | ||||||
|     new Constraint() { |     override def validate(name: String, value: String, messages: Messages): Option[String] = | ||||||
|       override def validate(name: String, value: String, messages: Messages): Option[String] = |       getAccountByUserName(value) match { | ||||||
|         getAccountByUserName(value) match { |         case None => Some("User does not exist.") | ||||||
|           case None => Some("User does not exist.") |         case Some(x) => | ||||||
|           case Some(x) => |           if (x.userName == params("owner")) { | ||||||
|             if (x.userName == params("owner")) { |             Some("This is current repository owner.") | ||||||
|               Some("This is current repository owner.") |           } else { | ||||||
|             } else { |             params.get("repository").flatMap { repositoryName => | ||||||
|               params.get("repository").flatMap { repositoryName => |               getRepositoryNamesOfUser(x.userName).find(_ == repositoryName).map { _ => | ||||||
|                 getRepositoryNamesOfUser(x.userName).find(_ == repositoryName).map { _ => |                 "User already has same repository." | ||||||
|                   "User already has same repository." |  | ||||||
|                 } |  | ||||||
|               } |               } | ||||||
|             } |             } | ||||||
|         } |           } | ||||||
|     } |       } | ||||||
|  |   } | ||||||
|  |  | ||||||
|   private def mergeOptions = |   private def mergeOptions = new ValueType[Seq[String]] { | ||||||
|     new ValueType[Seq[String]] { |     override def convert(name: String, params: Map[String, Seq[String]], messages: Messages): Seq[String] = { | ||||||
|       override def convert(name: String, params: Map[String, Seq[String]], messages: Messages): Seq[String] = { |       params.getOrElse("mergeOptions", Nil) | ||||||
|         params.getOrElse("mergeOptions", Nil) |     } | ||||||
|       } |     override def validate(name: String, params: Map[String, Seq[String]], messages: Messages): Seq[(String, String)] = { | ||||||
|       override def validate( |       val mergeOptions = params.getOrElse("mergeOptions", Nil) | ||||||
|         name: String, |       if (mergeOptions.isEmpty) { | ||||||
|         params: Map[String, Seq[String]], |         Seq("mergeOptions" -> "At least one option must be enabled.") | ||||||
|         messages: Messages |       } else if (!mergeOptions.forall(x => Seq("merge-commit", "squash", "rebase").contains(x))) { | ||||||
|       ): Seq[(String, String)] = { |         Seq("mergeOptions" -> "mergeOptions are invalid.") | ||||||
|         val mergeOptions = params.getOrElse("mergeOptions", Nil) |       } else { | ||||||
|         if (mergeOptions.isEmpty) { |         Nil | ||||||
|           Seq("mergeOptions" -> "At least one option must be enabled.") |  | ||||||
|         } else if (!mergeOptions.forall(x => Seq("merge-commit", "squash", "rebase").contains(x))) { |  | ||||||
|           Seq("mergeOptions" -> "mergeOptions are invalid.") |  | ||||||
|         } else { |  | ||||||
|           Nil |  | ||||||
|         } |  | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -60,7 +60,6 @@ class RepositoryViewerController | |||||||
|     with WebHookPullRequestService |     with WebHookPullRequestService | ||||||
|     with WebHookPullRequestReviewCommentService |     with WebHookPullRequestReviewCommentService | ||||||
|     with ProtectedBranchService |     with ProtectedBranchService | ||||||
|     with RequestCache |  | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * The repository viewer. |  * The repository viewer. | ||||||
|   | |||||||
| @@ -24,7 +24,6 @@ class WikiController | |||||||
|     with WebHookService |     with WebHookService | ||||||
|     with ReadableUsersAuthenticator |     with ReadableUsersAuthenticator | ||||||
|     with ReferrerAuthenticator |     with ReferrerAuthenticator | ||||||
|     with RequestCache |  | ||||||
|  |  | ||||||
| trait WikiControllerBase extends ControllerBase { | trait WikiControllerBase extends ControllerBase { | ||||||
|   self: WikiService |   self: WikiService | ||||||
|   | |||||||
| @@ -1,9 +1,5 @@ | |||||||
| package gitbucket.core.model | package gitbucket.core.model | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * ActivityComponent has been deprecated, but keep it for binary compatibility. |  | ||||||
|  */ |  | ||||||
| @deprecated("ActivityComponent has been deprecated, but keep it for binary compatibility.", "4.34.0") |  | ||||||
| trait ActivityComponent extends TemplateComponent { self: Profile => | trait ActivityComponent extends TemplateComponent { self: Profile => | ||||||
|   import profile.api._ |   import profile.api._ | ||||||
|   import self._ |   import self._ | ||||||
| @@ -11,7 +7,14 @@ trait ActivityComponent extends TemplateComponent { self: Profile => | |||||||
|   lazy val Activities = TableQuery[Activities] |   lazy val Activities = TableQuery[Activities] | ||||||
|  |  | ||||||
|   class Activities(tag: Tag) extends Table[Activity](tag, "ACTIVITY") with BasicTemplate { |   class Activities(tag: Tag) extends Table[Activity](tag, "ACTIVITY") with BasicTemplate { | ||||||
|     def * = ??? |     val activityId = column[Int]("ACTIVITY_ID", O AutoInc) | ||||||
|  |     val activityUserName = column[String]("ACTIVITY_USER_NAME") | ||||||
|  |     val activityType = column[String]("ACTIVITY_TYPE") | ||||||
|  |     val message = column[String]("MESSAGE") | ||||||
|  |     val additionalInfo = column[String]("ADDITIONAL_INFO") | ||||||
|  |     val activityDate = column[java.util.Date]("ACTIVITY_DATE") | ||||||
|  |     def * = | ||||||
|  |       (userName, repositoryName, activityUserName, activityType, message, additionalInfo.?, activityDate, activityId) <> (Activity.tupled, Activity.unapply) | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -23,5 +26,5 @@ case class Activity( | |||||||
|   message: String, |   message: String, | ||||||
|   additionalInfo: Option[String], |   additionalInfo: Option[String], | ||||||
|   activityDate: java.util.Date, |   activityDate: java.util.Date, | ||||||
|   activityId: String |   activityId: Int = 0 | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -45,7 +45,7 @@ trait CoreProfile | |||||||
|     with Profile |     with Profile | ||||||
|     with AccessTokenComponent |     with AccessTokenComponent | ||||||
|     with AccountComponent |     with AccountComponent | ||||||
|     with ActivityComponent // ActivityComponent has been deprecated, but keep it for binary compatibility |     with ActivityComponent | ||||||
|     with CollaboratorComponent |     with CollaboratorComponent | ||||||
|     with CommitCommentComponent |     with CommitCommentComponent | ||||||
|     with CommitStatusComponent |     with CommitStatusComponent | ||||||
|   | |||||||
| @@ -17,9 +17,7 @@ trait AccountService { | |||||||
|   def authenticate(settings: SystemSettings, userName: String, password: String)( |   def authenticate(settings: SystemSettings, userName: String, password: String)( | ||||||
|     implicit s: Session |     implicit s: Session | ||||||
|   ): Option[Account] = { |   ): Option[Account] = { | ||||||
|     val account = if (password.isEmpty) { |     val account = if (settings.ldapAuthentication) { | ||||||
|       None |  | ||||||
|     } else if (settings.ldapAuthentication) { |  | ||||||
|       ldapAuthentication(settings, userName, password) |       ldapAuthentication(settings, userName, password) | ||||||
|     } else { |     } else { | ||||||
|       defaultAuthentication(userName, password) |       defaultAuthentication(userName, password) | ||||||
|   | |||||||
| @@ -3,169 +3,65 @@ package gitbucket.core.service | |||||||
| import gitbucket.core.model.Activity | import gitbucket.core.model.Activity | ||||||
| import gitbucket.core.util.JGitUtil | import gitbucket.core.util.JGitUtil | ||||||
| import gitbucket.core.model.Profile._ | import gitbucket.core.model.Profile._ | ||||||
| import gitbucket.core.util.Directory._ | import gitbucket.core.model.Profile.profile.blockingApi._ | ||||||
| import org.json4s._ |  | ||||||
| import org.json4s.jackson.Serialization |  | ||||||
| import org.json4s.jackson.Serialization.{read, write} |  | ||||||
|  |  | ||||||
| import scala.util.Using |  | ||||||
| import java.io.FileOutputStream |  | ||||||
| import java.nio.charset.StandardCharsets |  | ||||||
| import java.util.UUID |  | ||||||
|  |  | ||||||
| import gitbucket.core.controller.Context |  | ||||||
| import org.apache.commons.io.input.ReversedLinesFileReader |  | ||||||
|  |  | ||||||
| import scala.collection.mutable.ListBuffer |  | ||||||
|  |  | ||||||
| trait ActivityService { | trait ActivityService { | ||||||
|   self: RequestCache => |  | ||||||
|  |  | ||||||
|   private implicit val formats = Serialization.formats(NoTypeHints) |   def deleteOldActivities(limit: Int)(implicit s: Session): Int = { | ||||||
|  |     Activities.map(_.activityId).sortBy(_ desc).drop(limit).firstOption.map { id => | ||||||
|   private def writeLog(activity: Activity): Unit = { |       Activities.filter(_.activityId <= id.bind).delete | ||||||
|     Using.resource(new FileOutputStream(ActivityLog, true)) { out => |     } getOrElse 0 | ||||||
|       out.write((write(activity) + "\n").getBytes(StandardCharsets.UTF_8)) |  | ||||||
|     } |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   def getActivitiesByUser(activityUserName: String, isPublic: Boolean)(implicit context: Context): List[Activity] = { |   def getActivitiesByUser(activityUserName: String, isPublic: Boolean)(implicit s: Session): List[Activity] = | ||||||
|     if (!ActivityLog.exists()) { |     Activities | ||||||
|       List.empty |       .join(Repositories) | ||||||
|     } else { |       .on((t1, t2) => t1.byRepository(t2.userName, t2.repositoryName)) | ||||||
|       val list = new ListBuffer[Activity] |       .filter { | ||||||
|       Using.resource(new ReversedLinesFileReader(ActivityLog, StandardCharsets.UTF_8)) { reader => |         case (t1, t2) => | ||||||
|         var json: String = null |           if (isPublic) { | ||||||
|         while (list.length < 50 && { json = reader.readLine(); json } != null) { |             (t1.activityUserName === activityUserName.bind) && (t2.isPrivate === false.bind) | ||||||
|           val activity = read[Activity](json) |           } else { | ||||||
|           if (activity.activityUserName == activityUserName) { |             (t1.activityUserName === activityUserName.bind) | ||||||
|             if (isPublic == false) { |  | ||||||
|               list += activity |  | ||||||
|             } else { |  | ||||||
|               if (!getRepositoryInfoFromCache(activity.userName, activity.repositoryName) |  | ||||||
|                     .map(_.isPrivate) |  | ||||||
|                     .getOrElse(true)) { |  | ||||||
|                 list += activity |  | ||||||
|               } |  | ||||||
|             } |  | ||||||
|           } |           } | ||||||
|         } |  | ||||||
|       } |       } | ||||||
|       list.toList |       .sortBy { case (t1, t2) => t1.activityId desc } | ||||||
|     } |       .map { case (t1, t2) => t1 } | ||||||
|   } |       .take(30) | ||||||
|  |       .list | ||||||
|  |  | ||||||
|   def getRecentPublicActivities()(implicit context: Context): List[Activity] = { |   def getRecentActivities()(implicit s: Session): List[Activity] = | ||||||
|     if (!ActivityLog.exists()) { |     Activities | ||||||
|       List.empty |       .join(Repositories) | ||||||
|     } else { |       .on((t1, t2) => t1.byRepository(t2.userName, t2.repositoryName)) | ||||||
|       val list = new ListBuffer[Activity] |       .filter { case (t1, t2) => t2.isPrivate === false.bind } | ||||||
|       Using.resource(new ReversedLinesFileReader(ActivityLog, StandardCharsets.UTF_8)) { reader => |       .sortBy { case (t1, t2) => t1.activityId desc } | ||||||
|         var json: String = null |       .map { case (t1, t2) => t1 } | ||||||
|         while (list.length < 50 && { json = reader.readLine(); json } != null) { |       .take(30) | ||||||
|           val activity = read[Activity](json) |       .list | ||||||
|           if (!getRepositoryInfoFromCache(activity.userName, activity.repositoryName) |  | ||||||
|                 .map(_.isPrivate) |  | ||||||
|                 .getOrElse(true)) { |  | ||||||
|             list += activity |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|       list.toList |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   def getRecentActivitiesByOwners(owners: Set[String])(implicit context: Context): List[Activity] = { |   def getRecentActivitiesByOwners(owners: Set[String])(implicit s: Session): List[Activity] = | ||||||
|     if (!ActivityLog.exists()) { |     Activities | ||||||
|       List.empty |       .join(Repositories) | ||||||
|     } else { |       .on((t1, t2) => t1.byRepository(t2.userName, t2.repositoryName)) | ||||||
|       val list = new ListBuffer[Activity] |       .filter { case (t1, t2) => (t2.isPrivate === false.bind) || (t2.userName inSetBind owners) } | ||||||
|       Using.resource(new ReversedLinesFileReader(ActivityLog, StandardCharsets.UTF_8)) { reader => |       .sortBy { case (t1, t2) => t1.activityId desc } | ||||||
|         var json: String = null |       .map { case (t1, t2) => t1 } | ||||||
|         while (list.length < 50 && { json = reader.readLine(); json } != null) { |       .take(30) | ||||||
|           val activity = read[Activity](json) |       .list | ||||||
|           if (owners.contains(activity.userName)) { |  | ||||||
|             list += activity |  | ||||||
|           } else if (!getRepositoryInfoFromCache(activity.userName, activity.repositoryName) |  | ||||||
|                        .map(_.isPrivate) |  | ||||||
|                        .getOrElse(true)) { |  | ||||||
|             list += activity |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|       list.toList |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   def recordCreateRepositoryActivity(userName: String, repositoryName: String, activityUserName: String): Unit = { |   def recordCreateRepositoryActivity(userName: String, repositoryName: String, activityUserName: String)( | ||||||
|     writeLog( |     implicit s: Session | ||||||
|       Activity( |   ): Unit = | ||||||
|         userName, |     Activities insert Activity( | ||||||
|         repositoryName, |       userName, | ||||||
|         activityUserName, |       repositoryName, | ||||||
|         "create_repository", |       activityUserName, | ||||||
|         s"[user:${activityUserName}] created [repo:${userName}/${repositoryName}]", |       "create_repository", | ||||||
|         None, |       s"[user:${activityUserName}] created [repo:${userName}/${repositoryName}]", | ||||||
|         currentDate, |       None, | ||||||
|         UUID.randomUUID().toString |       currentDate | ||||||
|       ) |  | ||||||
|     ) |     ) | ||||||
|   } |  | ||||||
|  |  | ||||||
|   def recordDeleteRepositoryActivity(userName: String, repositoryName: String, activityUserName: String): Unit = { |  | ||||||
|     writeLog( |  | ||||||
|       Activity( |  | ||||||
|         userName, |  | ||||||
|         repositoryName, |  | ||||||
|         activityUserName, |  | ||||||
|         "delete_repository", |  | ||||||
|         s"[user:${activityUserName}] deleted [repo:${userName}/${repositoryName}]", |  | ||||||
|         None, |  | ||||||
|         currentDate, |  | ||||||
|         UUID.randomUUID().toString |  | ||||||
|       ) |  | ||||||
|     ) |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   def recordTransferRepositoryActivity( |  | ||||||
|     userName: String, |  | ||||||
|     repositoryName: String, |  | ||||||
|     oldUserName: String, |  | ||||||
|     activityUserName: String |  | ||||||
|   ): Unit = { |  | ||||||
|     writeLog( |  | ||||||
|       Activity( |  | ||||||
|         userName, |  | ||||||
|         repositoryName, |  | ||||||
|         activityUserName, |  | ||||||
|         "transfer_repository", |  | ||||||
|         s"[user:${activityUserName}] transfered [repo:${oldUserName}/${repositoryName}] to [repo:${userName}/${repositoryName}]", |  | ||||||
|         None, |  | ||||||
|         currentDate, |  | ||||||
|         UUID.randomUUID().toString |  | ||||||
|       ) |  | ||||||
|     ) |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   def recordRenameRepositoryActivity( |  | ||||||
|     userName: String, |  | ||||||
|     repositoryName: String, |  | ||||||
|     oldRepositoryName: String, |  | ||||||
|     activityUserName: String |  | ||||||
|   ): Unit = { |  | ||||||
|     writeLog( |  | ||||||
|       Activity( |  | ||||||
|         userName, |  | ||||||
|         repositoryName, |  | ||||||
|         activityUserName, |  | ||||||
|         "rename_repository", |  | ||||||
|         s"[user:${activityUserName}] renamed [repo:${userName}/${oldRepositoryName}] at [repo:${userName}/${repositoryName}]", |  | ||||||
|         None, |  | ||||||
|         currentDate, |  | ||||||
|         UUID.randomUUID().toString |  | ||||||
|       ) |  | ||||||
|     ) |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   def recordCreateIssueActivity( |   def recordCreateIssueActivity( | ||||||
|     userName: String, |     userName: String, | ||||||
| @@ -173,20 +69,16 @@ trait ActivityService { | |||||||
|     activityUserName: String, |     activityUserName: String, | ||||||
|     issueId: Int, |     issueId: Int, | ||||||
|     title: String |     title: String | ||||||
|   ): Unit = { |   )(implicit s: Session): Unit = | ||||||
|     writeLog( |     Activities insert Activity( | ||||||
|       Activity( |       userName, | ||||||
|         userName, |       repositoryName, | ||||||
|         repositoryName, |       activityUserName, | ||||||
|         activityUserName, |       "open_issue", | ||||||
|         "open_issue", |       s"[user:${activityUserName}] opened issue [issue:${userName}/${repositoryName}#${issueId}]", | ||||||
|         s"[user:${activityUserName}] opened issue [issue:${userName}/${repositoryName}#${issueId}]", |       Some(title), | ||||||
|         Some(title), |       currentDate | ||||||
|         currentDate, |  | ||||||
|         UUID.randomUUID().toString |  | ||||||
|       ) |  | ||||||
|     ) |     ) | ||||||
|   } |  | ||||||
|  |  | ||||||
|   def recordCloseIssueActivity( |   def recordCloseIssueActivity( | ||||||
|     userName: String, |     userName: String, | ||||||
| @@ -194,20 +86,16 @@ trait ActivityService { | |||||||
|     activityUserName: String, |     activityUserName: String, | ||||||
|     issueId: Int, |     issueId: Int, | ||||||
|     title: String |     title: String | ||||||
|   ): Unit = { |   )(implicit s: Session): Unit = | ||||||
|     writeLog( |     Activities insert Activity( | ||||||
|       Activity( |       userName, | ||||||
|         userName, |       repositoryName, | ||||||
|         repositoryName, |       activityUserName, | ||||||
|         activityUserName, |       "close_issue", | ||||||
|         "close_issue", |       s"[user:${activityUserName}] closed issue [issue:${userName}/${repositoryName}#${issueId}]", | ||||||
|         s"[user:${activityUserName}] closed issue [issue:${userName}/${repositoryName}#${issueId}]", |       Some(title), | ||||||
|         Some(title), |       currentDate | ||||||
|         currentDate, |  | ||||||
|         UUID.randomUUID().toString |  | ||||||
|       ) |  | ||||||
|     ) |     ) | ||||||
|   } |  | ||||||
|  |  | ||||||
|   def recordClosePullRequestActivity( |   def recordClosePullRequestActivity( | ||||||
|     userName: String, |     userName: String, | ||||||
| @@ -215,20 +103,16 @@ trait ActivityService { | |||||||
|     activityUserName: String, |     activityUserName: String, | ||||||
|     issueId: Int, |     issueId: Int, | ||||||
|     title: String |     title: String | ||||||
|   ): Unit = { |   )(implicit s: Session): Unit = | ||||||
|     writeLog( |     Activities insert Activity( | ||||||
|       Activity( |       userName, | ||||||
|         userName, |       repositoryName, | ||||||
|         repositoryName, |       activityUserName, | ||||||
|         activityUserName, |       "close_issue", | ||||||
|         "close_issue", |       s"[user:${activityUserName}] closed pull request [pullreq:${userName}/${repositoryName}#${issueId}]", | ||||||
|         s"[user:${activityUserName}] closed pull request [pullreq:${userName}/${repositoryName}#${issueId}]", |       Some(title), | ||||||
|         Some(title), |       currentDate | ||||||
|         currentDate, |  | ||||||
|         UUID.randomUUID().toString |  | ||||||
|       ) |  | ||||||
|     ) |     ) | ||||||
|   } |  | ||||||
|  |  | ||||||
|   def recordReopenIssueActivity( |   def recordReopenIssueActivity( | ||||||
|     userName: String, |     userName: String, | ||||||
| @@ -236,20 +120,16 @@ trait ActivityService { | |||||||
|     activityUserName: String, |     activityUserName: String, | ||||||
|     issueId: Int, |     issueId: Int, | ||||||
|     title: String |     title: String | ||||||
|   ): Unit = { |   )(implicit s: Session): Unit = | ||||||
|     writeLog( |     Activities insert Activity( | ||||||
|       Activity( |       userName, | ||||||
|         userName, |       repositoryName, | ||||||
|         repositoryName, |       activityUserName, | ||||||
|         activityUserName, |       "reopen_issue", | ||||||
|         "reopen_issue", |       s"[user:${activityUserName}] reopened issue [issue:${userName}/${repositoryName}#${issueId}]", | ||||||
|         s"[user:${activityUserName}] reopened issue [issue:${userName}/${repositoryName}#${issueId}]", |       Some(title), | ||||||
|         Some(title), |       currentDate | ||||||
|         currentDate, |  | ||||||
|         UUID.randomUUID().toString |  | ||||||
|       ) |  | ||||||
|     ) |     ) | ||||||
|   } |  | ||||||
|  |  | ||||||
|   def recordReopenPullRequestActivity( |   def recordReopenPullRequestActivity( | ||||||
|     userName: String, |     userName: String, | ||||||
| @@ -257,20 +137,16 @@ trait ActivityService { | |||||||
|     activityUserName: String, |     activityUserName: String, | ||||||
|     issueId: Int, |     issueId: Int, | ||||||
|     title: String |     title: String | ||||||
|   ): Unit = { |   )(implicit s: Session): Unit = | ||||||
|     writeLog( |     Activities insert Activity( | ||||||
|       Activity( |       userName, | ||||||
|         userName, |       repositoryName, | ||||||
|         repositoryName, |       activityUserName, | ||||||
|         activityUserName, |       "reopen_issue", | ||||||
|         "reopen_issue", |       s"[user:${activityUserName}] reopened pull request [issue:${userName}/${repositoryName}#${issueId}]", | ||||||
|         s"[user:${activityUserName}] reopened pull request [issue:${userName}/${repositoryName}#${issueId}]", |       Some(title), | ||||||
|         Some(title), |       currentDate | ||||||
|         currentDate, |  | ||||||
|         UUID.randomUUID().toString |  | ||||||
|       ) |  | ||||||
|     ) |     ) | ||||||
|   } |  | ||||||
|  |  | ||||||
|   def recordCommentIssueActivity( |   def recordCommentIssueActivity( | ||||||
|     userName: String, |     userName: String, | ||||||
| @@ -278,20 +154,16 @@ trait ActivityService { | |||||||
|     activityUserName: String, |     activityUserName: String, | ||||||
|     issueId: Int, |     issueId: Int, | ||||||
|     comment: String |     comment: String | ||||||
|   ): Unit = { |   )(implicit s: Session): Unit = | ||||||
|     writeLog( |     Activities insert Activity( | ||||||
|       Activity( |       userName, | ||||||
|         userName, |       repositoryName, | ||||||
|         repositoryName, |       activityUserName, | ||||||
|         activityUserName, |       "comment_issue", | ||||||
|         "comment_issue", |       s"[user:${activityUserName}] commented on issue [issue:${userName}/${repositoryName}#${issueId}]", | ||||||
|         s"[user:${activityUserName}] commented on issue [issue:${userName}/${repositoryName}#${issueId}]", |       Some(cut(comment, 200)), | ||||||
|         Some(cut(comment, 200)), |       currentDate | ||||||
|         currentDate, |  | ||||||
|         UUID.randomUUID().toString |  | ||||||
|       ) |  | ||||||
|     ) |     ) | ||||||
|   } |  | ||||||
|  |  | ||||||
|   def recordCommentPullRequestActivity( |   def recordCommentPullRequestActivity( | ||||||
|     userName: String, |     userName: String, | ||||||
| @@ -299,20 +171,16 @@ trait ActivityService { | |||||||
|     activityUserName: String, |     activityUserName: String, | ||||||
|     issueId: Int, |     issueId: Int, | ||||||
|     comment: String |     comment: String | ||||||
|   ): Unit = { |   )(implicit s: Session): Unit = | ||||||
|     writeLog( |     Activities insert Activity( | ||||||
|       Activity( |       userName, | ||||||
|         userName, |       repositoryName, | ||||||
|         repositoryName, |       activityUserName, | ||||||
|         activityUserName, |       "comment_issue", | ||||||
|         "comment_issue", |       s"[user:${activityUserName}] commented on pull request [pullreq:${userName}/${repositoryName}#${issueId}]", | ||||||
|         s"[user:${activityUserName}] commented on pull request [pullreq:${userName}/${repositoryName}#${issueId}]", |       Some(cut(comment, 200)), | ||||||
|         Some(cut(comment, 200)), |       currentDate | ||||||
|         currentDate, |  | ||||||
|         UUID.randomUUID().toString |  | ||||||
|       ) |  | ||||||
|     ) |     ) | ||||||
|   } |  | ||||||
|  |  | ||||||
|   def recordCommentCommitActivity( |   def recordCommentCommitActivity( | ||||||
|     userName: String, |     userName: String, | ||||||
| @@ -320,40 +188,32 @@ trait ActivityService { | |||||||
|     activityUserName: String, |     activityUserName: String, | ||||||
|     commitId: String, |     commitId: String, | ||||||
|     comment: String |     comment: String | ||||||
|   ): Unit = { |   )(implicit s: Session): Unit = | ||||||
|     writeLog( |     Activities insert Activity( | ||||||
|       Activity( |       userName, | ||||||
|         userName, |       repositoryName, | ||||||
|         repositoryName, |       activityUserName, | ||||||
|         activityUserName, |       "comment_commit", | ||||||
|         "comment_commit", |       s"[user:${activityUserName}] commented on commit [commit:${userName}/${repositoryName}@${commitId}]", | ||||||
|         s"[user:${activityUserName}] commented on commit [commit:${userName}/${repositoryName}@${commitId}]", |       Some(cut(comment, 200)), | ||||||
|         Some(cut(comment, 200)), |       currentDate | ||||||
|         currentDate, |  | ||||||
|         UUID.randomUUID().toString |  | ||||||
|       ) |  | ||||||
|     ) |     ) | ||||||
|   } |  | ||||||
|  |  | ||||||
|   def recordCreateWikiPageActivity( |   def recordCreateWikiPageActivity( | ||||||
|     userName: String, |     userName: String, | ||||||
|     repositoryName: String, |     repositoryName: String, | ||||||
|     activityUserName: String, |     activityUserName: String, | ||||||
|     pageName: String |     pageName: String | ||||||
|   ): Unit = { |   )(implicit s: Session): Unit = | ||||||
|     writeLog( |     Activities insert Activity( | ||||||
|       Activity( |       userName, | ||||||
|         userName, |       repositoryName, | ||||||
|         repositoryName, |       activityUserName, | ||||||
|         activityUserName, |       "create_wiki", | ||||||
|         "create_wiki", |       s"[user:${activityUserName}] created the [repo:${userName}/${repositoryName}] wiki", | ||||||
|         s"[user:${activityUserName}] created the [repo:${userName}/${repositoryName}] wiki", |       Some(pageName), | ||||||
|         Some(pageName), |       currentDate | ||||||
|         currentDate, |  | ||||||
|         UUID.randomUUID().toString |  | ||||||
|       ) |  | ||||||
|     ) |     ) | ||||||
|   } |  | ||||||
|  |  | ||||||
|   def recordEditWikiPageActivity( |   def recordEditWikiPageActivity( | ||||||
|     userName: String, |     userName: String, | ||||||
| @@ -361,20 +221,16 @@ trait ActivityService { | |||||||
|     activityUserName: String, |     activityUserName: String, | ||||||
|     pageName: String, |     pageName: String, | ||||||
|     commitId: String |     commitId: String | ||||||
|   ): Unit = { |   )(implicit s: Session): Unit = | ||||||
|     writeLog( |     Activities insert Activity( | ||||||
|       Activity( |       userName, | ||||||
|         userName, |       repositoryName, | ||||||
|         repositoryName, |       activityUserName, | ||||||
|         activityUserName, |       "edit_wiki", | ||||||
|         "edit_wiki", |       s"[user:${activityUserName}] edited the [repo:${userName}/${repositoryName}] wiki", | ||||||
|         s"[user:${activityUserName}] edited the [repo:${userName}/${repositoryName}] wiki", |       Some(pageName + ":" + commitId), | ||||||
|         Some(pageName + ":" + commitId), |       currentDate | ||||||
|         currentDate, |  | ||||||
|         UUID.randomUUID().toString |  | ||||||
|       ) |  | ||||||
|     ) |     ) | ||||||
|   } |  | ||||||
|  |  | ||||||
|   def recordPushActivity( |   def recordPushActivity( | ||||||
|     userName: String, |     userName: String, | ||||||
| @@ -382,27 +238,23 @@ trait ActivityService { | |||||||
|     activityUserName: String, |     activityUserName: String, | ||||||
|     branchName: String, |     branchName: String, | ||||||
|     commits: List[JGitUtil.CommitInfo] |     commits: List[JGitUtil.CommitInfo] | ||||||
|   ): Unit = { |   )(implicit s: Session): Unit = | ||||||
|     writeLog( |     Activities insert Activity( | ||||||
|       Activity( |       userName, | ||||||
|         userName, |       repositoryName, | ||||||
|         repositoryName, |       activityUserName, | ||||||
|         activityUserName, |       "push", | ||||||
|         "push", |       s"[user:${activityUserName}] pushed to [branch:${userName}/${repositoryName}#${branchName}] at [repo:${userName}/${repositoryName}]", | ||||||
|         s"[user:${activityUserName}] pushed to [branch:${userName}/${repositoryName}#${branchName}] at [repo:${userName}/${repositoryName}]", |       Some( | ||||||
|         Some( |         commits | ||||||
|           commits |           .take(5) | ||||||
|             .take(5) |           .map { commit => | ||||||
|             .map { commit => |             commit.id + ":" + commit.shortMessage | ||||||
|               commit.id + ":" + commit.shortMessage |           } | ||||||
|             } |           .mkString("\n") | ||||||
|             .mkString("\n") |       ), | ||||||
|         ), |       currentDate | ||||||
|         currentDate, |  | ||||||
|         UUID.randomUUID().toString |  | ||||||
|       ) |  | ||||||
|     ) |     ) | ||||||
|   } |  | ||||||
|  |  | ||||||
|   def recordCreateTagActivity( |   def recordCreateTagActivity( | ||||||
|     userName: String, |     userName: String, | ||||||
| @@ -410,20 +262,16 @@ trait ActivityService { | |||||||
|     activityUserName: String, |     activityUserName: String, | ||||||
|     tagName: String, |     tagName: String, | ||||||
|     commits: List[JGitUtil.CommitInfo] |     commits: List[JGitUtil.CommitInfo] | ||||||
|   ): Unit = { |   )(implicit s: Session): Unit = | ||||||
|     writeLog( |     Activities insert Activity( | ||||||
|       Activity( |       userName, | ||||||
|         userName, |       repositoryName, | ||||||
|         repositoryName, |       activityUserName, | ||||||
|         activityUserName, |       "create_tag", | ||||||
|         "create_tag", |       s"[user:${activityUserName}] created tag [tag:${userName}/${repositoryName}#${tagName}] at [repo:${userName}/${repositoryName}]", | ||||||
|         s"[user:${activityUserName}] created tag [tag:${userName}/${repositoryName}#${tagName}] at [repo:${userName}/${repositoryName}]", |       None, | ||||||
|         None, |       currentDate | ||||||
|         currentDate, |  | ||||||
|         UUID.randomUUID().toString |  | ||||||
|       ) |  | ||||||
|     ) |     ) | ||||||
|   } |  | ||||||
|  |  | ||||||
|   def recordDeleteTagActivity( |   def recordDeleteTagActivity( | ||||||
|     userName: String, |     userName: String, | ||||||
| @@ -431,80 +279,61 @@ trait ActivityService { | |||||||
|     activityUserName: String, |     activityUserName: String, | ||||||
|     tagName: String, |     tagName: String, | ||||||
|     commits: List[JGitUtil.CommitInfo] |     commits: List[JGitUtil.CommitInfo] | ||||||
|   ): Unit = { |   )(implicit s: Session): Unit = | ||||||
|     writeLog( |     Activities insert Activity( | ||||||
|       Activity( |       userName, | ||||||
|         userName, |       repositoryName, | ||||||
|         repositoryName, |       activityUserName, | ||||||
|         activityUserName, |       "delete_tag", | ||||||
|         "delete_tag", |       s"[user:${activityUserName}] deleted tag ${tagName} at [repo:${userName}/${repositoryName}]", | ||||||
|         s"[user:${activityUserName}] deleted tag ${tagName} at [repo:${userName}/${repositoryName}]", |       None, | ||||||
|         None, |       currentDate | ||||||
|         currentDate, |  | ||||||
|         UUID.randomUUID().toString |  | ||||||
|       ) |  | ||||||
|     ) |     ) | ||||||
|   } |  | ||||||
|  |  | ||||||
|   def recordCreateBranchActivity( |   def recordCreateBranchActivity( | ||||||
|     userName: String, |     userName: String, | ||||||
|     repositoryName: String, |     repositoryName: String, | ||||||
|     activityUserName: String, |     activityUserName: String, | ||||||
|     branchName: String |     branchName: String | ||||||
|   ): Unit = { |   )(implicit s: Session): Unit = | ||||||
|     writeLog( |     Activities insert Activity( | ||||||
|       Activity( |       userName, | ||||||
|         userName, |       repositoryName, | ||||||
|         repositoryName, |       activityUserName, | ||||||
|         activityUserName, |       "create_branch", | ||||||
|         "create_branch", |       s"[user:${activityUserName}] created branch [branch:${userName}/${repositoryName}#${branchName}] at [repo:${userName}/${repositoryName}]", | ||||||
|         s"[user:${activityUserName}] created branch [branch:${userName}/${repositoryName}#${branchName}] at [repo:${userName}/${repositoryName}]", |       None, | ||||||
|         None, |       currentDate | ||||||
|         currentDate, |  | ||||||
|         UUID.randomUUID().toString |  | ||||||
|       ) |  | ||||||
|     ) |     ) | ||||||
|   } |  | ||||||
|  |  | ||||||
|   def recordDeleteBranchActivity( |   def recordDeleteBranchActivity( | ||||||
|     userName: String, |     userName: String, | ||||||
|     repositoryName: String, |     repositoryName: String, | ||||||
|     activityUserName: String, |     activityUserName: String, | ||||||
|     branchName: String |     branchName: String | ||||||
|   ): Unit = { |   )(implicit s: Session): Unit = | ||||||
|     writeLog( |     Activities insert Activity( | ||||||
|       Activity( |       userName, | ||||||
|         userName, |       repositoryName, | ||||||
|         repositoryName, |       activityUserName, | ||||||
|         activityUserName, |       "delete_branch", | ||||||
|         "delete_branch", |       s"[user:${activityUserName}] deleted branch ${branchName} at [repo:${userName}/${repositoryName}]", | ||||||
|         s"[user:${activityUserName}] deleted branch ${branchName} at [repo:${userName}/${repositoryName}]", |       None, | ||||||
|         None, |       currentDate | ||||||
|         currentDate, |  | ||||||
|         UUID.randomUUID().toString |  | ||||||
|       ) |  | ||||||
|     ) |     ) | ||||||
|   } |  | ||||||
|  |  | ||||||
|   def recordForkActivity( |   def recordForkActivity(userName: String, repositoryName: String, activityUserName: String, forkedUserName: String)( | ||||||
|     userName: String, |     implicit s: Session | ||||||
|     repositoryName: String, |   ): Unit = | ||||||
|     activityUserName: String, |     Activities insert Activity( | ||||||
|     forkedUserName: String |       userName, | ||||||
|   ): Unit = { |       repositoryName, | ||||||
|     writeLog( |       activityUserName, | ||||||
|       Activity( |       "fork", | ||||||
|         userName, |       s"[user:${activityUserName}] forked [repo:${userName}/${repositoryName}] to [repo:${forkedUserName}/${repositoryName}]", | ||||||
|         repositoryName, |       None, | ||||||
|         activityUserName, |       currentDate | ||||||
|         "fork", |  | ||||||
|         s"[user:${activityUserName}] forked [repo:${userName}/${repositoryName}] to [repo:${forkedUserName}/${repositoryName}]", |  | ||||||
|         None, |  | ||||||
|         currentDate, |  | ||||||
|         UUID.randomUUID().toString |  | ||||||
|       ) |  | ||||||
|     ) |     ) | ||||||
|   } |  | ||||||
|  |  | ||||||
|   def recordPullRequestActivity( |   def recordPullRequestActivity( | ||||||
|     userName: String, |     userName: String, | ||||||
| @@ -512,20 +341,16 @@ trait ActivityService { | |||||||
|     activityUserName: String, |     activityUserName: String, | ||||||
|     issueId: Int, |     issueId: Int, | ||||||
|     title: String |     title: String | ||||||
|   ): Unit = { |   )(implicit s: Session): Unit = | ||||||
|     writeLog( |     Activities insert Activity( | ||||||
|       Activity( |       userName, | ||||||
|         userName, |       repositoryName, | ||||||
|         repositoryName, |       activityUserName, | ||||||
|         activityUserName, |       "open_pullreq", | ||||||
|         "open_pullreq", |       s"[user:${activityUserName}] opened pull request [pullreq:${userName}/${repositoryName}#${issueId}]", | ||||||
|         s"[user:${activityUserName}] opened pull request [pullreq:${userName}/${repositoryName}#${issueId}]", |       Some(title), | ||||||
|         Some(title), |       currentDate | ||||||
|         currentDate, |  | ||||||
|         UUID.randomUUID().toString |  | ||||||
|       ) |  | ||||||
|     ) |     ) | ||||||
|   } |  | ||||||
|  |  | ||||||
|   def recordMergeActivity( |   def recordMergeActivity( | ||||||
|     userName: String, |     userName: String, | ||||||
| @@ -533,20 +358,16 @@ trait ActivityService { | |||||||
|     activityUserName: String, |     activityUserName: String, | ||||||
|     issueId: Int, |     issueId: Int, | ||||||
|     message: String |     message: String | ||||||
|   ): Unit = { |   )(implicit s: Session): Unit = | ||||||
|     writeLog( |     Activities insert Activity( | ||||||
|       Activity( |       userName, | ||||||
|         userName, |       repositoryName, | ||||||
|         repositoryName, |       activityUserName, | ||||||
|         activityUserName, |       "merge_pullreq", | ||||||
|         "merge_pullreq", |       s"[user:${activityUserName}] merged pull request [pullreq:${userName}/${repositoryName}#${issueId}]", | ||||||
|         s"[user:${activityUserName}] merged pull request [pullreq:${userName}/${repositoryName}#${issueId}]", |       Some(message), | ||||||
|         Some(message), |       currentDate | ||||||
|         currentDate, |  | ||||||
|         UUID.randomUUID().toString |  | ||||||
|       ) |  | ||||||
|     ) |     ) | ||||||
|   } |  | ||||||
|  |  | ||||||
|   def recordReleaseActivity( |   def recordReleaseActivity( | ||||||
|     userName: String, |     userName: String, | ||||||
| @@ -554,20 +375,16 @@ trait ActivityService { | |||||||
|     activityUserName: String, |     activityUserName: String, | ||||||
|     releaseName: String, |     releaseName: String, | ||||||
|     tagName: String |     tagName: String | ||||||
|   ): Unit = { |   )(implicit s: Session): Unit = | ||||||
|     writeLog( |     Activities insert Activity( | ||||||
|       Activity( |       userName, | ||||||
|         userName, |       repositoryName, | ||||||
|         repositoryName, |       activityUserName, | ||||||
|         activityUserName, |       "release", | ||||||
|         "release", |       s"[user:${activityUserName}] released [release:${userName}/${repositoryName}/${tagName}:${releaseName}] at [repo:${userName}/${repositoryName}]", | ||||||
|         s"[user:${activityUserName}] released [release:${userName}/${repositoryName}/${tagName}:${releaseName}] at [repo:${userName}/${repositoryName}]", |       None, | ||||||
|         None, |       currentDate | ||||||
|         currentDate, |  | ||||||
|         UUID.randomUUID().toString |  | ||||||
|       ) |  | ||||||
|     ) |     ) | ||||||
|   } |  | ||||||
|  |  | ||||||
|   private def cut(value: String, length: Int): String = |   private def cut(value: String, length: Int): String = | ||||||
|     if (value.length > length) value.substring(0, length) + "..." else value |     if (value.length > length) value.substring(0, length) + "..." else value | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| package gitbucket.core.service | package gitbucket.core.service | ||||||
|  |  | ||||||
|  | import gitbucket.core.api.JsonFormat | ||||||
| import gitbucket.core.controller.Context | import gitbucket.core.controller.Context | ||||||
| import gitbucket.core.util._ | import gitbucket.core.util._ | ||||||
| import gitbucket.core.util.SyntaxSugars._ | import gitbucket.core.util.SyntaxSugars._ | ||||||
| @@ -8,11 +9,14 @@ import gitbucket.core.model.Profile._ | |||||||
| import gitbucket.core.model.Profile.profile.blockingApi._ | import gitbucket.core.model.Profile.profile.blockingApi._ | ||||||
| import gitbucket.core.model.Profile.dateColumnType | import gitbucket.core.model.Profile.dateColumnType | ||||||
| import gitbucket.core.plugin.PluginRegistry | import gitbucket.core.plugin.PluginRegistry | ||||||
|  | import gitbucket.core.service.WebHookService.WebHookPushPayload | ||||||
| import gitbucket.core.util.Directory.{getRepositoryDir, getRepositoryFilesDir, getTemporaryDir, getWikiRepositoryDir} | import gitbucket.core.util.Directory.{getRepositoryDir, getRepositoryFilesDir, getTemporaryDir, getWikiRepositoryDir} | ||||||
| import gitbucket.core.util.JGitUtil.FileInfo | import gitbucket.core.util.JGitUtil.{CommitInfo, FileInfo} | ||||||
| import org.apache.commons.io.FileUtils | import org.apache.commons.io.FileUtils | ||||||
| import org.eclipse.jgit.api.Git | import org.eclipse.jgit.api.Git | ||||||
|  | import org.eclipse.jgit.dircache.{DirCache, DirCacheBuilder} | ||||||
| import org.eclipse.jgit.lib.{Repository => _, _} | import org.eclipse.jgit.lib.{Repository => _, _} | ||||||
|  | import org.eclipse.jgit.transport.{ReceiveCommand, ReceivePack} | ||||||
| import scala.util.Using | import scala.util.Using | ||||||
|  |  | ||||||
| trait RepositoryService { | trait RepositoryService { | ||||||
| @@ -115,6 +119,15 @@ trait RepositoryService { | |||||||
|             } |             } | ||||||
|             .update(newUserName, newRepositoryName) |             .update(newUserName, newRepositoryName) | ||||||
|  |  | ||||||
|  |           // Updates activity fk before deleting repository because activity is sorted by activityId | ||||||
|  |           // and it can't be changed by deleting-and-inserting record. | ||||||
|  |           Activities.filter(_.byRepository(oldUserName, oldRepositoryName)).list.foreach { activity => | ||||||
|  |             Activities | ||||||
|  |               .filter(_.activityId === activity.activityId.bind) | ||||||
|  |               .map(x => (x.userName, x.repositoryName)) | ||||||
|  |               .update(newUserName, newRepositoryName) | ||||||
|  |           } | ||||||
|  |  | ||||||
|           deleteRepositoryOnModel(oldUserName, oldRepositoryName) |           deleteRepositoryOnModel(oldUserName, oldRepositoryName) | ||||||
|  |  | ||||||
|           RepositoryWebHooks.insertAll( |           RepositoryWebHooks.insertAll( | ||||||
| @@ -200,6 +213,50 @@ trait RepositoryService { | |||||||
|             collaborators.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _* |             collaborators.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _* | ||||||
|           ) |           ) | ||||||
|  |  | ||||||
|  |           // Update activity messages | ||||||
|  |           Activities | ||||||
|  |             .filter { t => | ||||||
|  |               (t.message like s"%:${oldUserName}/${oldRepositoryName}]%") || | ||||||
|  |               (t.message like s"%:${oldUserName}/${oldRepositoryName}#%") || | ||||||
|  |               (t.message like s"%:${oldUserName}/${oldRepositoryName}@%") | ||||||
|  |             } | ||||||
|  |             .map { t => | ||||||
|  |               t.activityId -> t.message | ||||||
|  |             } | ||||||
|  |             .list | ||||||
|  |             .foreach { | ||||||
|  |               case (activityId, message) => | ||||||
|  |                 Activities | ||||||
|  |                   .filter(_.activityId === activityId.bind) | ||||||
|  |                   .map(_.message) | ||||||
|  |                   .update( | ||||||
|  |                     message | ||||||
|  |                       .replace( | ||||||
|  |                         s"[repo:${oldUserName}/${oldRepositoryName}]", | ||||||
|  |                         s"[repo:${newUserName}/${newRepositoryName}]" | ||||||
|  |                       ) | ||||||
|  |                       .replace( | ||||||
|  |                         s"[branch:${oldUserName}/${oldRepositoryName}#", | ||||||
|  |                         s"[branch:${newUserName}/${newRepositoryName}#" | ||||||
|  |                       ) | ||||||
|  |                       .replace( | ||||||
|  |                         s"[tag:${oldUserName}/${oldRepositoryName}#", | ||||||
|  |                         s"[tag:${newUserName}/${newRepositoryName}#" | ||||||
|  |                       ) | ||||||
|  |                       .replace( | ||||||
|  |                         s"[pullreq:${oldUserName}/${oldRepositoryName}#", | ||||||
|  |                         s"[pullreq:${newUserName}/${newRepositoryName}#" | ||||||
|  |                       ) | ||||||
|  |                       .replace( | ||||||
|  |                         s"[issue:${oldUserName}/${oldRepositoryName}#", | ||||||
|  |                         s"[issue:${newUserName}/${newRepositoryName}#" | ||||||
|  |                       ) | ||||||
|  |                       .replace( | ||||||
|  |                         s"[commit:${oldUserName}/${oldRepositoryName}@", | ||||||
|  |                         s"[commit:${newUserName}/${newRepositoryName}@" | ||||||
|  |                       ) | ||||||
|  |                   ) | ||||||
|  |             } | ||||||
|           // Move git repository |           // Move git repository | ||||||
|           defining(getRepositoryDir(oldUserName, oldRepositoryName)) { dir => |           defining(getRepositoryDir(oldUserName, oldRepositoryName)) { dir => | ||||||
|             if (dir.isDirectory) { |             if (dir.isDirectory) { | ||||||
| @@ -247,7 +304,7 @@ trait RepositoryService { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   private def deleteRepositoryOnModel(userName: String, repositoryName: String)(implicit s: Session): Unit = { |   private def deleteRepositoryOnModel(userName: String, repositoryName: String)(implicit s: Session): Unit = { | ||||||
| //    Activities.filter(_.byRepository(userName, repositoryName)).delete |     Activities.filter(_.byRepository(userName, repositoryName)).delete | ||||||
|     Collaborators.filter(_.byRepository(userName, repositoryName)).delete |     Collaborators.filter(_.byRepository(userName, repositoryName)).delete | ||||||
|     CommitComments.filter(_.byRepository(userName, repositoryName)).delete |     CommitComments.filter(_.byRepository(userName, repositoryName)).delete | ||||||
|     IssueLabels.filter(_.byRepository(userName, repositoryName)).delete |     IssueLabels.filter(_.byRepository(userName, repositoryName)).delete | ||||||
|   | |||||||
| @@ -1,11 +1,9 @@ | |||||||
| package gitbucket.core.service | package gitbucket.core.service | ||||||
|  |  | ||||||
| import gitbucket.core.model.{Account, Issue, Repository, Session} | import gitbucket.core.model.{Session, Issue, Account} | ||||||
| import gitbucket.core.util.Implicits | import gitbucket.core.util.Implicits | ||||||
| import gitbucket.core.controller.Context | import gitbucket.core.controller.Context | ||||||
| import Implicits.request2Session | import Implicits.request2Session | ||||||
| import gitbucket.core.model.Profile.{Accounts, Repositories} |  | ||||||
| import gitbucket.core.model.Profile.profile.blockingApi._ |  | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * This service is used for a view helper mainly. |  * This service is used for a view helper mainly. | ||||||
| @@ -25,41 +23,21 @@ trait RequestCache | |||||||
|   private implicit def context2Session(implicit context: Context): Session = |   private implicit def context2Session(implicit context: Context): Session = | ||||||
|     request2Session(context.request) |     request2Session(context.request) | ||||||
|  |  | ||||||
|   def getIssueFromCache(userName: String, repositoryName: String, issueId: String)( |   def getIssue(userName: String, repositoryName: String, issueId: String)(implicit context: Context): Option[Issue] = { | ||||||
|     implicit context: Context |  | ||||||
|   ): Option[Issue] = { |  | ||||||
|     context.cache(s"issue.${userName}/${repositoryName}#${issueId}") { |     context.cache(s"issue.${userName}/${repositoryName}#${issueId}") { | ||||||
|       super.getIssue(userName, repositoryName, issueId) |       super.getIssue(userName, repositoryName, issueId) | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   def getAccountByUserNameFromCache(userName: String)(implicit context: Context): Option[Account] = { |   def getAccountByUserName(userName: String)(implicit context: Context): Option[Account] = { | ||||||
|     context.cache(s"account.${userName}") { |     context.cache(s"account.${userName}") { | ||||||
|       super.getAccountByUserName(userName) |       super.getAccountByUserName(userName) | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   def getAccountByMailAddressFromCache(mailAddress: String)(implicit context: Context): Option[Account] = { |   def getAccountByMailAddress(mailAddress: String)(implicit context: Context): Option[Account] = { | ||||||
|     context.cache(s"account.${mailAddress}") { |     context.cache(s"account.${mailAddress}") { | ||||||
|       super.getAccountByMailAddress(mailAddress) |       super.getAccountByMailAddress(mailAddress) | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   def getRepositoryInfoFromCache(userName: String, repositoryName: String)( |  | ||||||
|     implicit context: Context |  | ||||||
|   ): Option[Repository] = { |  | ||||||
|     context.cache(s"repository.${userName}/${repositoryName}") { |  | ||||||
|       Repositories |  | ||||||
|         .join(Accounts) |  | ||||||
|         .on(_.userName === _.userName) |  | ||||||
|         .filter { |  | ||||||
|           case (t1, t2) => |  | ||||||
|             t1.byRepository(userName, repositoryName) && t2.removed === false.bind |  | ||||||
|         } |  | ||||||
|         .map { |  | ||||||
|           case (t1, t2) => t1 |  | ||||||
|         } |  | ||||||
|         .firstOption |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -240,8 +240,7 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl: | |||||||
|     with WebHookPullRequestService |     with WebHookPullRequestService | ||||||
|     with WebHookPullRequestReviewCommentService |     with WebHookPullRequestReviewCommentService | ||||||
|     with CommitsService |     with CommitsService | ||||||
|     with SystemSettingsService |     with SystemSettingsService { | ||||||
|     with RequestCache { |  | ||||||
|  |  | ||||||
|   private val logger = LoggerFactory.getLogger(classOf[CommitLogHook]) |   private val logger = LoggerFactory.getLogger(classOf[CommitLogHook]) | ||||||
|   private var existIds: Seq[String] = Nil |   private var existIds: Seq[String] = Nil | ||||||
|   | |||||||
| @@ -2,10 +2,11 @@ package gitbucket.core.servlet | |||||||
|  |  | ||||||
| import java.io.{File, FileOutputStream} | import java.io.{File, FileOutputStream} | ||||||
|  |  | ||||||
|  | import akka.event.Logging | ||||||
| import com.typesafe.config.ConfigFactory | import com.typesafe.config.ConfigFactory | ||||||
| import gitbucket.core.GitBucketCoreModule | import gitbucket.core.GitBucketCoreModule | ||||||
| import gitbucket.core.plugin.PluginRegistry | import gitbucket.core.plugin.PluginRegistry | ||||||
| import gitbucket.core.service.SystemSettingsService | import gitbucket.core.service.{ActivityService, SystemSettingsService} | ||||||
| import gitbucket.core.util.DatabaseConfig | import gitbucket.core.util.DatabaseConfig | ||||||
| import gitbucket.core.util.Directory._ | import gitbucket.core.util.Directory._ | ||||||
| import gitbucket.core.util.JDBCUtil._ | import gitbucket.core.util.JDBCUtil._ | ||||||
| @@ -20,6 +21,8 @@ import javax.servlet.{ServletContextEvent, ServletContextListener} | |||||||
|  |  | ||||||
| import org.apache.commons.io.{FileUtils, IOUtils} | import org.apache.commons.io.{FileUtils, IOUtils} | ||||||
| import org.slf4j.LoggerFactory | import org.slf4j.LoggerFactory | ||||||
|  | import akka.actor.{Actor, ActorSystem, Props} | ||||||
|  | import com.typesafe.akka.extension.quartz.QuartzSchedulerExtension | ||||||
|  |  | ||||||
| import scala.jdk.CollectionConverters._ | import scala.jdk.CollectionConverters._ | ||||||
| import scala.util.Using | import scala.util.Using | ||||||
| @@ -32,23 +35,23 @@ class InitializeListener extends ServletContextListener with SystemSettingsServi | |||||||
|  |  | ||||||
|   private val logger = LoggerFactory.getLogger(classOf[InitializeListener]) |   private val logger = LoggerFactory.getLogger(classOf[InitializeListener]) | ||||||
|  |  | ||||||
| //  // ActorSystem for Quartz scheduler |   // ActorSystem for Quartz scheduler | ||||||
| //  private val system = ActorSystem( |   private val system = ActorSystem( | ||||||
| //    "job", |     "job", | ||||||
| //    ConfigFactory.parseString(""" |     ConfigFactory.parseString(""" | ||||||
| //      |akka { |       |akka { | ||||||
| //      |  daemonic = on |       |  daemonic = on | ||||||
| //      |  coordinated-shutdown.run-by-jvm-shutdown-hook = off |       |  coordinated-shutdown.run-by-jvm-shutdown-hook = off | ||||||
| //      |  quartz { |       |  quartz { | ||||||
| //      |    schedules { |       |    schedules { | ||||||
| //      |      Daily { |       |      Daily { | ||||||
| //      |        expression = "0 0 0 * * ?" |       |        expression = "0 0 0 * * ?" | ||||||
| //      |      } |       |      } | ||||||
| //      |    } |       |    } | ||||||
| //      |  } |       |  } | ||||||
| //      |} |       |} | ||||||
| //    """.stripMargin) |     """.stripMargin) | ||||||
| //  ) |   ) | ||||||
|  |  | ||||||
|   override def contextInitialized(event: ServletContextEvent): Unit = { |   override def contextInitialized(event: ServletContextEvent): Unit = { | ||||||
|     val dataDir = event.getServletContext.getInitParameter("gitbucket.home") |     val dataDir = event.getServletContext.getInitParameter("gitbucket.home") | ||||||
| @@ -92,10 +95,10 @@ class InitializeListener extends ServletContextListener with SystemSettingsServi | |||||||
|       PluginRegistry.initialize(event.getServletContext, loadSystemSettings(), conn) |       PluginRegistry.initialize(event.getServletContext, loadSystemSettings(), conn) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| //    // Start Quartz scheduler |     // Start Quartz scheduler | ||||||
| //    val scheduler = QuartzSchedulerExtension(system) |     val scheduler = QuartzSchedulerExtension(system) | ||||||
| // |  | ||||||
| //    scheduler.schedule("Daily", system.actorOf(Props[DeleteOldActivityActor]), "DeleteOldActivity") |     scheduler.schedule("Daily", system.actorOf(Props[DeleteOldActivityActor]), "DeleteOldActivity") | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private def checkVersion(manager: JDBCVersionManager, conn: java.sql.Connection): Unit = { |   private def checkVersion(manager: JDBCVersionManager, conn: java.sql.Connection): Unit = { | ||||||
| @@ -169,8 +172,8 @@ class InitializeListener extends ServletContextListener with SystemSettingsServi | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   override def contextDestroyed(event: ServletContextEvent): Unit = { |   override def contextDestroyed(event: ServletContextEvent): Unit = { | ||||||
| //    // Shutdown Quartz scheduler |     // Shutdown Quartz scheduler | ||||||
| //    system.terminate() |     system.terminate() | ||||||
|     // Shutdown plugins |     // Shutdown plugins | ||||||
|     PluginRegistry.shutdown(event.getServletContext, loadSystemSettings()) |     PluginRegistry.shutdown(event.getServletContext, loadSystemSettings()) | ||||||
|     // Close datasource |     // Close datasource | ||||||
| @@ -178,3 +181,21 @@ class InitializeListener extends ServletContextListener with SystemSettingsServi | |||||||
|   } |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | class DeleteOldActivityActor extends Actor with SystemSettingsService with ActivityService { | ||||||
|  |  | ||||||
|  |   private val logger = Logging(context.system, this) | ||||||
|  |  | ||||||
|  |   def receive = { | ||||||
|  |     case s: String => { | ||||||
|  |       loadSystemSettings().activityLogLimit.foreach { limit => | ||||||
|  |         if (limit > 0) { | ||||||
|  |           Database() withTransaction { implicit session => | ||||||
|  |             val rows = deleteOldActivities(limit) | ||||||
|  |             logger.info(s"Deleted ${rows} activity logs") | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|   | |||||||
| @@ -29,8 +29,6 @@ object Directory { | |||||||
|  |  | ||||||
|   val GitBucketConf = new File(GitBucketHome, "gitbucket.conf") |   val GitBucketConf = new File(GitBucketHome, "gitbucket.conf") | ||||||
|  |  | ||||||
|   val ActivityLog = new File(GitBucketHome, "activity.log") |  | ||||||
|  |  | ||||||
|   val RepositoryHome = s"${GitBucketHome}/repositories" |   val RepositoryHome = s"${GitBucketHome}/repositories" | ||||||
|  |  | ||||||
|   val DatabaseHome = s"${GitBucketHome}/data" |   val DatabaseHome = s"${GitBucketHome}/data" | ||||||
|   | |||||||
| @@ -17,7 +17,7 @@ trait AvatarImageProvider { self: RequestCache => | |||||||
|  |  | ||||||
|     val src = if (mailAddress.isEmpty) { |     val src = if (mailAddress.isEmpty) { | ||||||
|       // by user name |       // by user name | ||||||
|       getAccountByUserNameFromCache(userName).map { account => |       getAccountByUserName(userName).map { account => | ||||||
|         if (account.image.isEmpty && context.settings.gravatar) { |         if (account.image.isEmpty && context.settings.gravatar) { | ||||||
|           s"""https://www.gravatar.com/avatar/${StringUtil.md5(account.mailAddress.toLowerCase)}?s=${size}&d=retro&r=g""" |           s"""https://www.gravatar.com/avatar/${StringUtil.md5(account.mailAddress.toLowerCase)}?s=${size}&d=retro&r=g""" | ||||||
|         } else { |         } else { | ||||||
| @@ -28,7 +28,7 @@ trait AvatarImageProvider { self: RequestCache => | |||||||
|       } |       } | ||||||
|     } else { |     } else { | ||||||
|       // by mail address |       // by mail address | ||||||
|       getAccountByMailAddressFromCache(mailAddress).map { account => |       getAccountByMailAddress(mailAddress).map { account => | ||||||
|         if (account.image.isEmpty && context.settings.gravatar) { |         if (account.image.isEmpty && context.settings.gravatar) { | ||||||
|           s"""https://www.gravatar.com/avatar/${StringUtil.md5(account.mailAddress.toLowerCase)}?s=${size}&d=retro&r=g""" |           s"""https://www.gravatar.com/avatar/${StringUtil.md5(account.mailAddress.toLowerCase)}?s=${size}&d=retro&r=g""" | ||||||
|         } else { |         } else { | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ trait LinkConverter { self: RequestCache => | |||||||
|     val userName = repository.repository.userName |     val userName = repository.repository.userName | ||||||
|     val repositoryName = repository.repository.repositoryName |     val repositoryName = repository.repository.repositoryName | ||||||
|  |  | ||||||
|     getIssueFromCache(userName, repositoryName, issueId.toString) match { |     getIssue(userName, repositoryName, issueId.toString) match { | ||||||
|       case Some(issue) => |       case Some(issue) => | ||||||
|         s"""<a href="${context.path}/${userName}/${repositoryName}/${if (issue.isPullRequest) "pull" else "issues"}/${issueId}"><strong>${StringUtil |         s"""<a href="${context.path}/${userName}/${repositoryName}/${if (issue.isPullRequest) "pull" else "issues"}/${issueId}"><strong>${StringUtil | ||||||
|           .escapeHtml(title)}</strong> #${issueId}</a>""" |           .escapeHtml(title)}</strong> #${issueId}</a>""" | ||||||
| @@ -43,7 +43,7 @@ trait LinkConverter { self: RequestCache => | |||||||
|     escaped |     escaped | ||||||
|     // convert username/project@SHA to link |     // convert username/project@SHA to link | ||||||
|       .replaceBy("(?<=(^|\\W))([a-zA-Z0-9\\-_]+)/([a-zA-Z0-9\\-_\\.]+)@([a-f0-9]{40})(?=(\\W|$))".r) { m => |       .replaceBy("(?<=(^|\\W))([a-zA-Z0-9\\-_]+)/([a-zA-Z0-9\\-_\\.]+)@([a-f0-9]{40})(?=(\\W|$))".r) { m => | ||||||
|         getAccountByUserNameFromCache(m.group(2)).map { _ => |         getAccountByUserName(m.group(2)).map { _ => | ||||||
|           s"""<code><a href="${context.path}/${m.group(2)}/${m.group(3)}/commit/${m.group(4)}">${m.group(2)}/${m.group( |           s"""<code><a href="${context.path}/${m.group(2)}/${m.group(3)}/commit/${m.group(4)}">${m.group(2)}/${m.group( | ||||||
|             3 |             3 | ||||||
|           )}@${m.group(4).substring(0, 7)}</a></code>""" |           )}@${m.group(4).substring(0, 7)}</a></code>""" | ||||||
| @@ -53,7 +53,7 @@ trait LinkConverter { self: RequestCache => | |||||||
|       // convert username/project#Num to link |       // convert username/project#Num to link | ||||||
|       .replaceBy(("(?<=(^|\\W))([a-zA-Z0-9\\-_]+)/([a-zA-Z0-9\\-_\\.]+)" + issueIdPrefix + "([0-9]+)(?=(\\W|$))").r) { |       .replaceBy(("(?<=(^|\\W))([a-zA-Z0-9\\-_]+)/([a-zA-Z0-9\\-_\\.]+)" + issueIdPrefix + "([0-9]+)(?=(\\W|$))").r) { | ||||||
|         m => |         m => | ||||||
|           getIssueFromCache(m.group(2), m.group(3), m.group(4)) match { |           getIssue(m.group(2), m.group(3), m.group(4)) match { | ||||||
|             case Some(issue) if (issue.isPullRequest) => |             case Some(issue) if (issue.isPullRequest) => | ||||||
|               Some(s"""<a href="${context.path}/${m.group(2)}/${m.group(3)}/pull/${m.group(4)}">${m.group(2)}/${m.group( |               Some(s"""<a href="${context.path}/${m.group(2)}/${m.group(3)}/pull/${m.group(4)}">${m.group(2)}/${m.group( | ||||||
|                 3 |                 3 | ||||||
| @@ -68,7 +68,7 @@ trait LinkConverter { self: RequestCache => | |||||||
|  |  | ||||||
|       // convert username@SHA to link |       // convert username@SHA to link | ||||||
|       .replaceBy(("(?<=(^|\\W))([a-zA-Z0-9\\-_]+)@([a-f0-9]{40})(?=(\\W|$))").r) { m => |       .replaceBy(("(?<=(^|\\W))([a-zA-Z0-9\\-_]+)@([a-f0-9]{40})(?=(\\W|$))").r) { m => | ||||||
|         getAccountByUserNameFromCache(m.group(2)).map { _ => |         getAccountByUserName(m.group(2)).map { _ => | ||||||
|           s"""<code><a href="${context.path}/${m.group(2)}/${repository.name}/commit/${m.group(3)}">${m.group(2)}@${m |           s"""<code><a href="${context.path}/${m.group(2)}/${repository.name}/commit/${m.group(3)}">${m.group(2)}@${m | ||||||
|             .group(3) |             .group(3) | ||||||
|             .substring(0, 7)}</a></code>""" |             .substring(0, 7)}</a></code>""" | ||||||
| @@ -77,7 +77,7 @@ trait LinkConverter { self: RequestCache => | |||||||
|  |  | ||||||
|       // convert username#Num to link |       // convert username#Num to link | ||||||
|       .replaceBy(("(?<=(^|\\W))([a-zA-Z0-9\\-_]+)" + issueIdPrefix + "([0-9]+)(?=(\\W|$))").r) { m => |       .replaceBy(("(?<=(^|\\W))([a-zA-Z0-9\\-_]+)" + issueIdPrefix + "([0-9]+)(?=(\\W|$))").r) { m => | ||||||
|         getIssueFromCache(m.group(2), repository.name, m.group(3)) match { |         getIssue(m.group(2), repository.name, m.group(3)) match { | ||||||
|           case Some(issue) if (issue.isPullRequest) => |           case Some(issue) if (issue.isPullRequest) => | ||||||
|             Some(s"""<a href="${context.path}/${m.group(2)}/${repository.name}/pull/${m.group(3)}">${m.group(2)}#${m |             Some(s"""<a href="${context.path}/${m.group(2)}/${repository.name}/pull/${m.group(3)}">${m.group(2)}#${m | ||||||
|               .group(3)}</a>""") |               .group(3)}</a>""") | ||||||
| @@ -92,7 +92,7 @@ trait LinkConverter { self: RequestCache => | |||||||
|       // convert issue id to link |       // convert issue id to link | ||||||
|       .replaceBy(("(?<=(^|\\W))(GH-|(?<!&)" + issueIdPrefix + ")([0-9]+)(?=(\\W|$))").r) { m => |       .replaceBy(("(?<=(^|\\W))(GH-|(?<!&)" + issueIdPrefix + ")([0-9]+)(?=(\\W|$))").r) { m => | ||||||
|         val prefix = if (m.group(2) == "issue:") "#" else m.group(2) |         val prefix = if (m.group(2) == "issue:") "#" else m.group(2) | ||||||
|         getIssueFromCache(repository.owner, repository.name, m.group(3)) match { |         getIssue(repository.owner, repository.name, m.group(3)) match { | ||||||
|           case Some(issue) if (issue.isPullRequest) => |           case Some(issue) if (issue.isPullRequest) => | ||||||
|             Some(s"""<a href="${context.path}/${repository.owner}/${repository.name}/pull/${m.group(3)}">${prefix}${m |             Some(s"""<a href="${context.path}/${repository.owner}/${repository.name}/pull/${m.group(3)}">${prefix}${m | ||||||
|               .group(3)}</a>""") |               .group(3)}</a>""") | ||||||
| @@ -106,7 +106,7 @@ trait LinkConverter { self: RequestCache => | |||||||
|  |  | ||||||
|       // convert @username to link |       // convert @username to link | ||||||
|       .replaceBy("(?<=(^|\\W))@([a-zA-Z0-9\\-_\\.]+)(?=(\\W|$))".r) { m => |       .replaceBy("(?<=(^|\\W))@([a-zA-Z0-9\\-_\\.]+)(?=(\\W|$))".r) { m => | ||||||
|         getAccountByUserNameFromCache(m.group(2)).map { _ => |         getAccountByUserName(m.group(2)).map { _ => | ||||||
|           s"""<a href="${context.path}/${m.group(2)}">@${m.group(2)}</a>""" |           s"""<a href="${context.path}/${m.group(2)}">@${m.group(2)}</a>""" | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ package gitbucket.core.view | |||||||
| import java.text.SimpleDateFormat | import java.text.SimpleDateFormat | ||||||
| import java.util.{Date, Locale, TimeZone} | import java.util.{Date, Locale, TimeZone} | ||||||
|  |  | ||||||
|  | import com.nimbusds.jose.util.JSONObjectUtils | ||||||
| import gitbucket.core.controller.Context | import gitbucket.core.controller.Context | ||||||
| import gitbucket.core.model.CommitState | import gitbucket.core.model.CommitState | ||||||
| import gitbucket.core.model.PullRequest | import gitbucket.core.model.PullRequest | ||||||
| @@ -195,7 +196,7 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache | |||||||
|  |  | ||||||
|   import scala.util.matching.Regex._ |   import scala.util.matching.Regex._ | ||||||
|   implicit class RegexReplaceString(private val s: String) extends AnyVal { |   implicit class RegexReplaceString(private val s: String) extends AnyVal { | ||||||
|     def replaceAll(pattern: String)(replacer: Match => String): String = { |     def replaceAll(pattern: String, replacer: (Match) => String): String = { | ||||||
|       pattern.r.replaceAllIn(s, (m: Match) => replacer(m).replace("$", "\\$")) |       pattern.r.replaceAllIn(s, (m: Match) => replacer(m).replace("$", "\\$")) | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| @@ -203,64 +204,50 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache | |||||||
|   /** |   /** | ||||||
|    * Convert link notations in the activity message. |    * Convert link notations in the activity message. | ||||||
|    */ |    */ | ||||||
|   // format: off |  | ||||||
|   def activityMessage(message: String)(implicit context: Context): Html = |   def activityMessage(message: String)(implicit context: Context): Html = | ||||||
|     Html( |     Html( | ||||||
|       message |       message | ||||||
|         .replaceAll("\\[issue:([^\\s]+?)/([^\\s]+?)#((\\d+))\\]"){ m => |         .replaceAll( | ||||||
|           if (getRepositoryInfoFromCache(m.group(1), m.group(2)).isDefined) { |           "\\[issue:([^\\s]+?)/([^\\s]+?)#((\\d+))\\]", | ||||||
|             s"""<a href="${context.path}/${m.group(1)}/${m.group(2)}/issues/${m.group(3)}">${m.group(1)}/${m.group(2)}#${m.group(3)}</a>""" |           s"""<a href="${context.path}/$$1/$$2/issues/$$3">$$1/$$2#$$3</a>""" | ||||||
|           } else { |         ) | ||||||
|             s"${m.group(1)}/${m.group(2)}#${m.group(3)}" |         .replaceAll( | ||||||
|           } |           "\\[pullreq:([^\\s]+?)/([^\\s]+?)#((\\d+))\\]", | ||||||
|         } |           s"""<a href="${context.path}/$$1/$$2/pull/$$3">$$1/$$2#$$3</a>""" | ||||||
|         .replaceAll("\\[pullreq:([^\\s]+?)/([^\\s]+?)#((\\d+))\\]"){ m => |         ) | ||||||
|           if (getRepositoryInfoFromCache(m.group(1), m.group(2)).isDefined) { |         .replaceAll("\\[repo:([^\\s]+?)/([^\\s]+?)\\]", s"""<a href="${context.path}/$$1/$$2\">$$1/$$2</a>""") | ||||||
|             s"""<a href="${context.path}/${m.group(1)}/${m.group(2)}/pull/${m.group(3)}">${m.group(1)}/${m.group(2)}#${m.group(3)}</a>""" |         .replaceAll( | ||||||
|           } else { |           "\\[branch:([^\\s]+?)/([^\\s]+?)#([^\\s]+?)\\]", | ||||||
|             s"${m.group(1)}/${m.group(2)}#${m.group(3)}" |           (m: Match) => | ||||||
|           } |             s"""<a href="${context.path}/${m.group(1)}/${m.group(2)}/tree/${encodeRefName(m.group(3))}">${StringUtil | ||||||
|         } |               .escapeHtml( | ||||||
|         .replaceAll("\\[repo:([^\\s]+?)/([^\\s]+?)\\]") { m => |                 m.group(3) | ||||||
|           if (getRepositoryInfoFromCache(m.group(1), m.group(2)).isDefined) { |               )}</a>""" | ||||||
|             s"""<a href="${context.path}/${m.group(1)}/${m.group(2)}">${m.group(1)}/${m.group(2)}</a>""" |         ) | ||||||
|           } else { |         .replaceAll( | ||||||
|             s"${m.group(1)}/${m.group(2)}" |           "\\[tag:([^\\s]+?)/([^\\s]+?)#([^\\s]+?)\\]", | ||||||
|           } |           (m: Match) => | ||||||
|         } |             s"""<a href="${context.path}/${m.group(1)}/${m.group(2)}/tree/${encodeRefName(m.group(3))}">${StringUtil | ||||||
|         .replaceAll("\\[branch:([^\\s]+?)/([^\\s]+?)#([^\\s]+?)\\]") { m => |               .escapeHtml( | ||||||
|           if (getRepositoryInfoFromCache(m.group(1), m.group(2)).isDefined) { |                 m.group(3) | ||||||
|             s"""<a href="${context.path}/${m.group(1)}/${m.group(2)}/tree/${encodeRefName(m.group(3))}">${StringUtil.escapeHtml(m.group(3))}</a>""" |               )}</a>""" | ||||||
|           } else { |         ) | ||||||
|             StringUtil.escapeHtml(m.group(3)) |         .replaceAll("\\[user:([^\\s]+?)\\]", (m: Match) => user(m.group(1)).body) | ||||||
|           } |         .replaceAll( | ||||||
|         } |           "\\[commit:([^\\s]+?)/([^\\s]+?)\\@([^\\s]+?)\\]", | ||||||
|         .replaceAll("\\[tag:([^\\s]+?)/([^\\s]+?)#([^\\s]+?)\\]") { m => |           (m: Match) => | ||||||
|           if (getRepositoryInfoFromCache(m.group(1), m.group(2)).isDefined) { |             s"""<a href="${context.path}/${m.group(1)}/${m.group(2)}/commit/${m.group(3)}">${m.group(1)}/${m | ||||||
|             s"""<a href="${context.path}/${m.group(1)}/${m.group(2)}/tree/${encodeRefName(m.group(3))}">${StringUtil.escapeHtml(m.group(3))}</a>""" |               .group(2)}@${m.group(3).substring(0, 7)}</a>""" | ||||||
|           } else { |         ) | ||||||
|             StringUtil.escapeHtml(m.group(3)) |         .replaceAll( | ||||||
|           } |           "\\[release:([^\\s]+?)/([^\\s]+?)/([^\\s]+?):(.+)\\]", | ||||||
|         } |           (m: Match) => | ||||||
|         .replaceAll("\\[user:([^\\s]+?)\\]") { m => |             s"""<a href="${context.path}/${m.group(1)}/${m.group(2)}/releases/${encodeRefName(m.group(3))}">${StringUtil | ||||||
|           user(m.group(1)).body |               .escapeHtml( | ||||||
|         } |                 m.group(4) | ||||||
|         .replaceAll("\\[commit:([^\\s]+?)/([^\\s]+?)\\@([^\\s]+?)\\]") { m => |               )}</a>""" | ||||||
|           if (getRepositoryInfoFromCache(m.group(1), m.group(2)).isDefined) { |         ) | ||||||
|             s"""<a href="${context.path}/${m.group(1)}/${m.group(2)}/commit/${m.group(3)}">${m.group(1)}/${m.group(2)}@${m.group(3).substring(0, 7)}</a>""" |  | ||||||
|           } else { |  | ||||||
|             s"${m.group(1)}/${m.group(2)}@${m.group(3).substring(0, 7)}" |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|         .replaceAll("\\[release:([^\\s]+?)/([^\\s]+?)/([^\\s]+?):(.+)\\]") { m => |  | ||||||
|           if (getRepositoryInfoFromCache(m.group(1), m.group(2)).isDefined) { |  | ||||||
|             s"""<a href="${context.path}/${m.group(1)}/${m.group(2)}/releases/${encodeRefName(m.group(3))}">${StringUtil.escapeHtml(m.group(4))}</a>""" |  | ||||||
|           } else { |  | ||||||
|             StringUtil.escapeHtml(m.group(4)) |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|     ) |     ) | ||||||
|   // format: off |  | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|    * Remove html tags from the given Html instance. |    * Remove html tags from the given Html instance. | ||||||
| @@ -346,9 +333,9 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache | |||||||
|     content: Html |     content: Html | ||||||
|   )(implicit context: Context): Html = |   )(implicit context: Context): Html = | ||||||
|     (if (mailAddress.isEmpty) { |     (if (mailAddress.isEmpty) { | ||||||
|        getAccountByUserNameFromCache(userName) |        getAccountByUserName(userName) | ||||||
|      } else { |      } else { | ||||||
|        getAccountByMailAddressFromCache(mailAddress) |        getAccountByMailAddress(mailAddress) | ||||||
|      }).map { account => |      }).map { account => | ||||||
|       Html(s"""<a href="${url(account.userName)}" class="${styleClass}">${content}</a>""") |       Html(s"""<a href="${url(account.userName)}" class="${styleClass}">${content}</a>""") | ||||||
|     } getOrElse content |     } getOrElse content | ||||||
|   | |||||||
| @@ -7,24 +7,21 @@ | |||||||
|   @activities.map { activity => |   @activities.map { activity => | ||||||
|     <div class="block"> |     <div class="block"> | ||||||
|       @(activity.activityType match { |       @(activity.activityType match { | ||||||
|         case "open_issue"          => simpleActivity(activity) |         case "open_issue"        => detailActivity(activity, "issue-opened") | ||||||
|         case "comment_issue"       => simpleActivity(activity) |         case "comment_issue"     => detailActivity(activity, "comment-discussion") | ||||||
|         case "comment_commit"      => simpleActivity(activity) |         case "comment_commit"    => detailActivity(activity, "comment-discussion") | ||||||
|         case "close_issue"         => simpleActivity(activity) |         case "close_issue"       => detailActivity(activity, "issue-closed") | ||||||
|         case "reopen_issue"        => simpleActivity(activity) |         case "reopen_issue"      => detailActivity(activity, "issue-reopened") | ||||||
|         case "open_pullreq"        => simpleActivity(activity) |         case "open_pullreq"      => detailActivity(activity, "git-pull-request") | ||||||
|         case "merge_pullreq"       => simpleActivity(activity) |         case "merge_pullreq"     => detailActivity(activity, "git-merge") | ||||||
|         case "release"             => simpleActivity(activity) |         case "release"           => detailActivity(activity, "package") | ||||||
|         case "create_repository"   => simpleActivity(activity) |         case "create_repository" => simpleActivity(activity, "repo") | ||||||
|         case "delete_repository"   => simpleActivity(activity) |         case "create_branch"     => simpleActivity(activity, "git-branch") | ||||||
|         case "rename_repository"   => simpleActivity(activity) |         case "delete_branch"     => simpleActivity(activity, "circle-slash") | ||||||
|         case "transfer_repository" => simpleActivity(activity) |         case "create_tag"        => simpleActivity(activity, "tag") | ||||||
|         case "create_branch"       => simpleActivity(activity) |         case "delete_tag"        => simpleActivity(activity, "circle-slash") | ||||||
|         case "delete_branch"       => simpleActivity(activity) |         case "fork"              => simpleActivity(activity, "repo-forked") | ||||||
|         case "create_tag"          => simpleActivity(activity) |         case "push"  => customActivity(activity, "git-commit"){ | ||||||
|         case "delete_tag"          => simpleActivity(activity) |  | ||||||
|         case "fork"                => simpleActivity(activity) |  | ||||||
|         case "push"  => customActivity(activity){ |  | ||||||
|           <div class="small activity-message"> |           <div class="small activity-message"> | ||||||
|             {activity.additionalInfo.get.split("\n").reverse.take(4).zipWithIndex.map{ case (commit, i) => |             {activity.additionalInfo.get.split("\n").reverse.take(4).zipWithIndex.map{ case (commit, i) => | ||||||
|               if(i == 3){ |               if(i == 3){ | ||||||
| @@ -40,12 +37,12 @@ | |||||||
|             }} |             }} | ||||||
|           </div> |           </div> | ||||||
|         } |         } | ||||||
|         case "create_wiki" => customActivity(activity){ |         case "create_wiki" => customActivity(activity, "book"){ | ||||||
|           <div class="small activity-message"> |           <div class="small activity-message"> | ||||||
|             Created <a href={s"${context.path}/${activity.userName}/${activity.repositoryName}/wiki/${activity.additionalInfo.get}"}>{activity.additionalInfo.get}</a>. |             Created <a href={s"${context.path}/${activity.userName}/${activity.repositoryName}/wiki/${activity.additionalInfo.get}"}>{activity.additionalInfo.get}</a>. | ||||||
|           </div> |           </div> | ||||||
|         } |         } | ||||||
|         case "edit_wiki" => customActivity(activity){ |         case "edit_wiki" => customActivity(activity, "book"){ | ||||||
|           activity.additionalInfo.get.split(":") match { |           activity.additionalInfo.get.split(":") match { | ||||||
|             case Array(pageName, commitId) => |             case Array(pageName, commitId) => | ||||||
|               <div class="small activity-message"> |               <div class="small activity-message"> | ||||||
| @@ -63,7 +60,26 @@ | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| @customActivity(activity: gitbucket.core.model.Activity)(additionalInfo: Any) = { | @detailActivity(activity: gitbucket.core.model.Activity, image: String) = { | ||||||
|  |   @* | ||||||
|  |   <div class="activity-icon-large"><i class="mega-octicon octicon-@image"></i></div> | ||||||
|  |   *@ | ||||||
|  |   <div> | ||||||
|  |     <div class="muted small">@gitbucket.core.helper.html.datetimeago(activity.activityDate)</div> | ||||||
|  |     <div class="strong"> | ||||||
|  |       @helpers.avatarLink(activity.activityUserName, 16) | ||||||
|  |       @helpers.activityMessage(activity.message) | ||||||
|  |     </div> | ||||||
|  |     @activity.additionalInfo.map { additionalInfo => | ||||||
|  |       <div class=" activity-message">@additionalInfo</div> | ||||||
|  |     } | ||||||
|  |   </div> | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @customActivity(activity: gitbucket.core.model.Activity, image: String)(additionalInfo: Any) = { | ||||||
|  |   @* | ||||||
|  |   <div class="activity-icon-large"><i class="mega-octicon octicon-@image"></i></div> | ||||||
|  |   *@ | ||||||
|   <div> |   <div> | ||||||
|     <div class="muted small">@gitbucket.core.helper.html.datetimeago(activity.activityDate)</div> |     <div class="muted small">@gitbucket.core.helper.html.datetimeago(activity.activityDate)</div> | ||||||
|     <div class="strong"> |     <div class="strong"> | ||||||
| @@ -74,7 +90,10 @@ | |||||||
|   </div> |   </div> | ||||||
| } | } | ||||||
|  |  | ||||||
| @simpleActivity(activity: gitbucket.core.model.Activity) = { | @simpleActivity(activity: gitbucket.core.model.Activity, image: String) = { | ||||||
|  |   @* | ||||||
|  |   <div class="activity-icon-small"><i class="octicon octicon-@image"></i></div> | ||||||
|  |   *@ | ||||||
|   <div> |   <div> | ||||||
|     <span class="muted small">@gitbucket.core.helper.html.datetimeago(activity.activityDate)</span> |     <span class="muted small">@gitbucket.core.helper.html.datetimeago(activity.activityDate)</span> | ||||||
|     <div> |     <div> | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ import scala.util.Using | |||||||
| class MergeServiceSpec extends FunSpec { | class MergeServiceSpec extends FunSpec { | ||||||
|   val service = new MergeService with AccountService with ActivityService with IssuesService with LabelsService |   val service = new MergeService with AccountService with ActivityService with IssuesService with LabelsService | ||||||
|   with MilestonesService with RepositoryService with PrioritiesService with PullRequestService with CommitsService |   with MilestonesService with RepositoryService with PrioritiesService with PullRequestService with CommitsService | ||||||
|   with WebHookPullRequestService with WebHookPullRequestReviewCommentService with RequestCache {} |   with WebHookPullRequestService with WebHookPullRequestReviewCommentService {} | ||||||
|   val branch = "master" |   val branch = "master" | ||||||
|   val issueId = 10 |   val issueId = 10 | ||||||
|   def initRepository(owner: String, name: String): File = { |   def initRepository(owner: String, name: String): File = { | ||||||
|   | |||||||
| @@ -18,8 +18,7 @@ class PullRequestServiceSpec | |||||||
|     with PrioritiesService |     with PrioritiesService | ||||||
|     with WebHookService |     with WebHookService | ||||||
|     with WebHookPullRequestService |     with WebHookPullRequestService | ||||||
|     with WebHookPullRequestReviewCommentService |     with WebHookPullRequestReviewCommentService { | ||||||
|     with RequestCache { |  | ||||||
|  |  | ||||||
|   def swap(r: (Issue, PullRequest)) = (r._2 -> r._1) |   def swap(r: (Issue, PullRequest)) = (r._2 -> r._1) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -99,7 +99,7 @@ trait ServiceSpecBase extends MockitoSugar { | |||||||
|   lazy val dummyService = new RepositoryService with AccountService with ActivityService with IssuesService |   lazy val dummyService = new RepositoryService with AccountService with ActivityService with IssuesService | ||||||
|   with MergeService with PullRequestService with CommitsService with CommitStatusService with LabelsService |   with MergeService with PullRequestService with CommitsService with CommitStatusService with LabelsService | ||||||
|   with MilestonesService with PrioritiesService with WebHookService with WebHookPullRequestService |   with MilestonesService with PrioritiesService with WebHookService with WebHookPullRequestService | ||||||
|   with WebHookPullRequestReviewCommentService with RequestCache { |   with WebHookPullRequestReviewCommentService { | ||||||
|     override def fetchAsPullRequest( |     override def fetchAsPullRequest( | ||||||
|       userName: String, |       userName: String, | ||||||
|       repositoryName: String, |       repositoryName: String, | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ import gitbucket.core.model.WebHookContentType | |||||||
| class WebHookServiceSpec extends FunSuite with ServiceSpecBase { | class WebHookServiceSpec extends FunSuite with ServiceSpecBase { | ||||||
|   lazy val service = new WebHookPullRequestService with AccountService with ActivityService with RepositoryService |   lazy val service = new WebHookPullRequestService with AccountService with ActivityService with RepositoryService | ||||||
|   with MergeService with PullRequestService with IssuesService with CommitsService with LabelsService |   with MergeService with PullRequestService with IssuesService with CommitsService with LabelsService | ||||||
|   with MilestonesService with PrioritiesService with WebHookPullRequestReviewCommentService with RequestCache |   with MilestonesService with PrioritiesService with WebHookPullRequestReviewCommentService | ||||||
|  |  | ||||||
|   test("WebHookPullRequestService.getPullRequestsByRequestForWebhook") { |   test("WebHookPullRequestService.getPullRequestsByRequestForWebhook") { | ||||||
|     withTestDB { implicit session => |     withTestDB { implicit session => | ||||||
|   | |||||||
| @@ -169,9 +169,8 @@ class AvatarImageProviderSpec extends FunSpec with MockitoSugar { | |||||||
|       context: Context |       context: Context | ||||||
|     ): Html = getAvatarImageHtml(userName, size, mailAddress, tooltip) |     ): Html = getAvatarImageHtml(userName, size, mailAddress, tooltip) | ||||||
|  |  | ||||||
|     override def getAccountByMailAddressFromCache(mailAddress: String)(implicit context: Context): Option[Account] = |     override def getAccountByMailAddress(mailAddress: String)(implicit context: Context): Option[Account] = account | ||||||
|       account |     override def getAccountByUserName(userName: String)(implicit context: Context): Option[Account] = account | ||||||
|     override def getAccountByUserNameFromCache(userName: String)(implicit context: Context): Option[Account] = account |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user