├── project ├── build.properties └── plugins.sbt ├── .gitignore ├── src └── main │ ├── scala │ ├── Plugin.scala │ └── fr │ │ └── brouillard │ │ └── gitbucket │ │ └── announce │ │ ├── service │ │ └── AnnounceService.scala │ │ └── controller │ │ └── AnnounceController.scala │ └── twirl │ └── fr │ └── brouillard │ └── gitbucket │ └── announce │ └── announce.scala.html └── README.MD /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version = 1.5.0 2 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("io.github.gitbucket" % "sbt-gitbucket-plugin" % "1.5.1") 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | target/ 3 | .settings/ 4 | .project 5 | .classpath 6 | /bin/ 7 | .cache-main 8 | *.iml 9 | *.ipr 10 | *.iws 11 | .vscode/ 12 | .metals/ 13 | .bloop/ 14 | **/metals.sbt 15 | .DS_Store 16 | -------------------------------------------------------------------------------- /src/main/scala/Plugin.scala: -------------------------------------------------------------------------------- 1 | import fr.brouillard.gitbucket.announce.controller.AnnounceController 2 | import gitbucket.core.controller.Context 3 | import gitbucket.core.plugin.{Link, PluginRegistry} 4 | import io.github.gitbucket.solidbase.model.Version 5 | 6 | class Plugin extends gitbucket.core.plugin.Plugin { 7 | override val pluginId: String = "announce" 8 | 9 | override val pluginName: String = "Announce Plugin" 10 | 11 | override val description: String = "Provide announces for gitbucket users and groups" 12 | 13 | override val versions: List[Version] = List( 14 | new Version("1.0.0") 15 | , new Version("1.1.0") 16 | , new Version("1.2.0") 17 | , new Version("1.3.0") 18 | , new Version("1.4.0") 19 | , new Version("1.5.0") 20 | , new Version("1.6.0") 21 | , new Version("1.7.0") 22 | , new Version("1.7.1") 23 | , new Version("1.7.2") 24 | , new Version("1.8.0") 25 | , new Version("1.9.0") 26 | , new Version("1.10.0") 27 | , new Version("1.11.0") 28 | , new Version("1.12.0") 29 | , new Version("1.13.0") 30 | , new Version("1.14.0") 31 | ) 32 | 33 | override val systemSettingMenus: Seq[(Context) => Option[Link]] = Seq( 34 | (ctx: Context) => Some(Link("announce", "Global Announce", "admin/announce")) 35 | ) 36 | 37 | override val controllers = Seq( 38 | "/*" -> new AnnounceController() 39 | ) 40 | } 41 | -------------------------------------------------------------------------------- /src/main/scala/fr/brouillard/gitbucket/announce/service/AnnounceService.scala: -------------------------------------------------------------------------------- 1 | package fr.brouillard.gitbucket.announce.service 2 | 3 | import gitbucket.core.model.Profile.profile.blockingApi._ 4 | import gitbucket.core.model.Account 5 | import gitbucket.core.model.Profile.{Accounts, GroupMembers} 6 | import gitbucket.core.service.AccountService 7 | 8 | object EmailAddress { 9 | private val EmailRegex = """\b[a-zA-Z0-9.!#$%&¡¯*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*\b""".r 10 | def isValid(email: String): Boolean = EmailRegex.pattern.matcher(email.toUpperCase).matches() 11 | } 12 | 13 | trait AnnounceService { 14 | self: AccountService => 15 | 16 | def getAccountByGroupName(groupName: String)(implicit s: Session): List[Account] = { 17 | val needs = GroupMembers 18 | .filter(_.groupName === groupName.bind) 19 | .sortBy(_.userName) 20 | .map(_.userName) 21 | .list 22 | 23 | Accounts 24 | .filter(t => (t.userName inSetBind needs) && (t.removed === false.bind)) 25 | .list 26 | } 27 | 28 | def getTargetAddresses(to: String)(implicit s: Session): List[String] = { 29 | to.split(",").map(_.trim).flatMap { groupAccount => 30 | val userMailAddress: List[Account] = if(groupAccount.toUpperCase == "ALL"){ 31 | getAllUsers(false) 32 | } else { 33 | getAccountByGroupName(groupAccount) 34 | } 35 | userMailAddress.collect { case x 36 | if !x.isGroupAccount && x.mailAddress.nonEmpty => 37 | x.mailAddress :: getAccountExtraMailAddresses(x.userName) 38 | } 39 | } 40 | .flatten 41 | .filter(mail => EmailAddress.isValid(mail)) 42 | .distinct 43 | .toList 44 | } 45 | } -------------------------------------------------------------------------------- /src/main/twirl/fr/brouillard/gitbucket/announce/announce.scala.html: -------------------------------------------------------------------------------- 1 | @(info: Option[Any])(implicit context: gitbucket.core.controller.Context) 2 | @import context._ 3 | @import gitbucket.core.html.main 4 | @import gitbucket.core.admin.html.menu 5 | @import gitbucket.core.helper.html.information 6 | @import gitbucket.core.view.helpers._ 7 | @import gitbucket.core.util.Directory._ 8 | @main("New announce"){ 9 | @menu("announce"){ 10 | @information(info) 11 |
12 | @if(settings.useSMTP && settings.smtp.nonEmpty){ 13 |
14 |
New announce
15 |
16 |
17 | 18 | 19 |

Enter a comma separated list of groupname the announce will be sent to, or the keyword 'ALL' to send to all users

20 |
21 |
22 | 23 | 24 |
25 | 26 |
27 | 28 |
29 |
30 |
31 |
32 | 33 |
34 | } else { 35 |
36 | SMTP settings are disabled, verify your setup. 37 |
38 | } 39 |
40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/scala/fr/brouillard/gitbucket/announce/controller/AnnounceController.scala: -------------------------------------------------------------------------------- 1 | package fr.brouillard.gitbucket.announce.controller 2 | 3 | import javax.mail.SendFailedException 4 | 5 | import fr.brouillard.gitbucket.announce.html 6 | import fr.brouillard.gitbucket.announce.service.AnnounceService 7 | import gitbucket.core.controller.ControllerBase 8 | import gitbucket.core.service.{AccountService, SystemSettingsService} 9 | import gitbucket.core.util.{AdminAuthenticator, Mailer} 10 | import io.github.gitbucket.markedj.{Marked, Options} 11 | import org.scalatra.forms._ 12 | import org.apache.commons.mail.EmailException 13 | import org.slf4j.LoggerFactory 14 | import gitbucket.core.util.Implicits._ 15 | 16 | class AnnounceController extends AnnounceControllerBase 17 | with AnnounceService with AccountService with AdminAuthenticator 18 | 19 | trait AnnounceControllerBase extends ControllerBase { 20 | self: AnnounceService with SystemSettingsService with AdminAuthenticator => 21 | 22 | private val logger = LoggerFactory.getLogger(classOf[AnnounceController]) 23 | 24 | case class AnnounceForm(content: String, subject: String, to: String) 25 | 26 | private val announceForm = mapping( 27 | "content" -> trim(label("Announce", text(required))), 28 | "subject" -> trim(label("Subject", text(required))), 29 | "to" -> trim(label("To", text(required))) 30 | )(AnnounceForm.apply) 31 | 32 | get("/admin/announce")(adminOnly { 33 | html.announce(flash.get("info")) 34 | }) 35 | 36 | post("/admin/announce", announceForm)(adminOnly { form => 37 | val systemSettings = context.settings 38 | 39 | if (systemSettings.useSMTP && systemSettings.smtp.nonEmpty) { 40 | if (logger.isDebugEnabled) { 41 | logger.debug("sending announce: {}", form.content) 42 | logger.debug("sending email subject: {}", form.subject) 43 | logger.debug("sending email content: {}", form.content) 44 | } 45 | 46 | val mailer = new Mailer(systemSettings) 47 | 48 | val opts = new Options() 49 | opts.setSanitize(true) 50 | val html = Marked.marked(form.content, opts) 51 | 52 | val bcc = getTargetAddresses(form.to) 53 | 54 | if (logger.isDebugEnabled) { 55 | logger.debug("sending email to: {}", form.to) 56 | logger.debug("sending email to EmailAddress: {}", bcc.mkString(", ")) 57 | } 58 | 59 | try { 60 | mailer.sendBcc(bcc, form.subject, form.content, Some(html), context.loginAccount) 61 | flash.update("info", "Announce has been sent.") 62 | } catch { 63 | case t: EmailException => { 64 | t.getCause match { 65 | case ex: SendFailedException => { 66 | logger.error("found invalid email address while sending notification", ex) 67 | if (ex.getInvalidAddresses() != null) { 68 | for (ia <- ex.getInvalidAddresses()) { 69 | logger.error("invalid email address: {}", ia.toString()) 70 | } 71 | } 72 | if (ex.getValidUnsentAddresses() != null) { 73 | for (ua <- ex.getValidUnsentAddresses()) { 74 | logger.error("email not sent to: {}", ua.toString()) 75 | } 76 | } 77 | flash.update("info", "Announce has been sent.") 78 | } 79 | case _ => { 80 | logger.error("failure sending email", t) 81 | flash.update("info", "Announce cannot be sent, verify log errors.") 82 | } 83 | } 84 | } 85 | case e: Exception => { 86 | logger.error("unexpected exception while sending email", e) 87 | flash.update("info", "Announce cannot be sent, verify log errors.") 88 | } 89 | } 90 | } else { 91 | flash.update("info", "Announce cannot be sent, verify SMTP settings") 92 | } 93 | 94 | redirect("/admin/announce") 95 | }) 96 | } -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # gitbucket-announce-plugin 2 | 3 | This plugin enhances [GitBucket](https://github.com/gitbucket/gitbucket) by providing an announce mechanism. 4 | 5 | ## Features 6 | 7 | ### Global Announce 8 | 9 | This feature allows to notify GitBucket users. It is available to _admin_ users via the `System Administration` menu of GitBucket. 10 | 11 | The global announce sends an email to all active users. For that, notifications and thus SMTP settings of the global GitBucket configuration have to be correctly filled in the `System Administration\System Settings\Notification email` section. 12 | 13 | It is then possible, for example, to notify every user that an upgrade of the system is planned the next Monday at midday. 14 | 15 | ## Compatibility 16 | 17 | Plugin version | GitBucket version 18 | :--------------|:----------------- 19 | 1.14.x | 4.35.x 20 | 1.13.x | 4.33.x 21 | 1.12.x | 4.32.x 22 | 1.11.x | 4.26.x 23 | 1.10.x | 4.21.x -> 4.25.x 24 | 1.9.x | 4.19.x, 4.20.x 25 | 1.8.x | 4.17.x, 4.18.x 26 | 1.7.x | 4.16.x 27 | 1.6.x | 4.10.x 28 | 1.5.x | 4.3.x 29 | 1.4.x | 4.0.0 -> 4.2.x 30 | 1.3.x | 3.11.x, 3.12.x 31 | 1.2.x | 3.11.x 32 | 1.1.x | 3.7.x -> 3.10.x 33 | 1.0.x | 3.6.x 34 | 35 | 36 | ## Usage 37 | 38 | - Open a shell window at the root of the project, hit `sbt assembly` 39 | - if you update `gitbucket-announce-plugin`, remove any existing previous copy of `gitbucket-announce-plugin` from GITBUCKET_HOME/plugins 40 | - Copy target/scala-2.12/gitbucket-announce-plugin-assembly-X.X.X.jar into GITBUCKET_HOME/plugins 41 | - Restart GitBucket 42 | 43 | ## Release Notes 44 | 45 | ### 1.14.0 46 | 47 | - [pullrequest-20](https://github.com/gitbucket-plugins/gitbucket-announce-plugin/pull/21) update to gitbucket 4.35.0 48 | 49 | 50 | ### 1.13.0 51 | 52 | - [pullrequest-19](https://github.com/gitbucket-plugins/gitbucket-announce-plugin/pull/19) Support for extra mail address 53 | 54 | 55 | ### 1.12.0 56 | 57 | - [pullrequest-18](https://github.com/gitbucket-plugins/gitbucket-announce-plugin/pull/18) update to gitbucket 4.23.0 and Scala 2.13.0 58 | 59 | ### 1.11.0 60 | 61 | - Bump sbt-gitbucket-plugin to 1.3.0 to be hosted by the [plugin registry](https://plugins.gitbucket-community.org/) 62 | 63 | ### 1.10.0 64 | 65 | - update to gitbucket 4.22.0 66 | 67 | ### 1.9.0 68 | - [pullrequest-16](https://github.com/gitbucket-plugins/gitbucket-announce-plugin/pull/16) Bump to Scalatra 2.6 & Scala 2.12.4 69 | 70 | ### 1.8.0 71 | - [issue-15](https://github.com/gitbucket-plugins/gitbucket-announce-plugin/issues/15) update to gitbucket 4.17 72 | 73 | ### 1.7.1 74 | - fix database support problem when gitbucket works with other than H2 75 | 76 | ### 1.7.0 77 | - update to gitbucket 4.16 78 | 79 | ### 1.6.0 80 | - [issue-13](https://github.com/gitbucket-plugins/gitbucket-announce-plugin/issues/13) update to gitbucket 4.10 81 | 82 | ### 1.5.0 83 | - [issue-12](https://github.com/gitbucket-plugins/gitbucket-announce-plugin/issues/12) adapt to new gitbucket version handling 84 | 85 | ### 1.4.0 86 | - [issue-11](https://github.com/gitbucket-plugins/gitbucket-announce-plugin/issues/11) enhance plugin compatibility, runs with gitbucket 4.0.0 87 | 88 | ### 1.3 89 | - [issue-10](https://github.com/gitbucket-plugins/gitbucket-announce-plugin/issues/10) allow sending to partial list of receivers, correct exception handling on email failure 90 | 91 | ### 1.2 92 | - [issue-9](https://github.com/gitbucket-plugins/gitbucket-announce-plugin/issues/9) make the plugin compatible with gitbucket 3.11 93 | 94 | ### 1.1 95 | 96 | - [issue-5](https://github.com/gitbucket-plugins/gitbucket-announce-plugin/issues/5) use new `SystemSettings.useSMTP` property introduced in gitbucket 3.7 97 | - [pullrequest-7](https://github.com/gitbucket-plugins/gitbucket-announce-plugin/pull/7) adapt markdown parser 98 | 99 | ### 1.0 100 | 101 | - introduce gitbucket-announce-plugin 102 | - global announce by email in `System Administration` menu 103 | - depends on [gitbucket/gitbucket#861](https://github.com/gitbucket/gitbucket/pull/861) 104 | --------------------------------------------------------------------------------