Skip to content

Commit 167235b

Browse files
committed
ADD plugin web settings
1 parent 47d9f5a commit 167235b

File tree

12 files changed

+498
-4
lines changed

12 files changed

+498
-4
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
1+
.project
2+
.settings
3+
.vscode
14
target
25
tmp

README

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ Useful resources:
1919
* [SAL SDK docs - Request](https://docs.atlassian.com/DAC/javadoc/sal/2.6/reference/com/atlassian/sal/api/net/Request.html)
2020
* [SAL 3.0 Upgrade Guide](https://developer.atlassian.com/server/framework/atlassian-sdk/sal-3-0-upgrade-guide/)
2121
* [Atlassian Community - REST requests from a custom plugin](https://community.atlassian.com/t5/Answers-Developer-Questions/What-is-the-recommended-way-to-send-REST-requests-from-a-custom/qaq-p/568227)
22+
* [Atlassian AUI - Soy template components](https://bitbucket.org/atlassian/aui/src/6a8e37f85a07952cc290503f54d85ff5bebc8783/src/soy/?at=5.10.x)

pom.xml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
</organization>
1717

1818
<properties>
19+
<maven.javadoc.skip>true</maven.javadoc.skip>
1920
<maven.compiler.source>1.8</maven.compiler.source>
2021
<maven.compiler.target>1.8</maven.compiler.target>
2122
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -55,6 +56,31 @@
5556
<artifactId>bitbucket-spi</artifactId>
5657
<scope>provided</scope>
5758
</dependency>
59+
<dependency>
60+
<groupId>com.atlassian.soy</groupId>
61+
<artifactId>soy-template-renderer-api</artifactId>
62+
<scope>provided</scope>
63+
</dependency>
64+
<dependency>
65+
<groupId>com.atlassian.plugins</groupId>
66+
<artifactId>atlassian-plugins-webresource</artifactId>
67+
<scope>provided</scope>
68+
</dependency>
69+
<dependency>
70+
<groupId>com.atlassian.plugins.rest</groupId>
71+
<artifactId>atlassian-rest-common</artifactId>
72+
<scope>provided</scope>
73+
</dependency>
74+
<dependency>
75+
<groupId>com.atlassian.plugins.rest</groupId>
76+
<artifactId>atlassian-rest-module</artifactId>
77+
<scope>provided</scope>
78+
</dependency>
79+
<dependency>
80+
<groupId>com.atlassian.bitbucket.server</groupId>
81+
<artifactId>bitbucket-rest-model</artifactId>
82+
<scope>provided</scope>
83+
</dependency>
5884

5985
<dependency>
6086
<groupId>com.atlassian.plugin</groupId>

src/main/java/com/adidas/bitbucket/log/OnRepositoryClone.java renamed to src/main/java/com/recuencojones/bitbucket/log/OnRepositoryClone.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,13 @@
44
import com.atlassian.bitbucket.repository.Repository;
55
import com.atlassian.event.api.EventListener;
66
import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport;
7+
78
import com.atlassian.sal.api.net.*;
9+
import com.atlassian.sal.api.pluginsettings.PluginSettings;
10+
import com.atlassian.sal.api.pluginsettings.PluginSettingsFactory;
11+
812
import com.google.gson.Gson;
13+
914
import org.slf4j.Logger;
1015
import org.slf4j.LoggerFactory;
1116

@@ -19,13 +24,20 @@ public class OnRepositoryClone {
1924
@ComponentImport
2025
private final RequestFactory requestFactory;
2126

27+
@ComponentImport
28+
private final PluginSettingsFactory pluginSettingsFactory;
29+
2230
@Inject
23-
public OnRepositoryClone(RequestFactory requestFactory) {
31+
public OnRepositoryClone(
32+
final RequestFactory requestFactory,
33+
final PluginSettingsFactory pluginSettingsFactory
34+
) {
2435
this.requestFactory = requestFactory;
36+
this.pluginSettingsFactory = pluginSettingsFactory;
2537
}
2638

2739
@EventListener
28-
public void onCloneEvent(RepositoryCloneEvent cloneEvent) {
40+
public void onCloneEvent(final RepositoryCloneEvent cloneEvent) {
2941
final Repository repository = cloneEvent.getRepository();
3042
final String projectKey = repository.getProject().getKey();
3143
final String repositorySlug = repository.getSlug();
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.recuencojones.bitbucket.log;
2+
3+
import com.atlassian.bitbucket.event.repository.RepositoryDeletedEvent;
4+
5+
import com.atlassian.bitbucket.repository.Repository;
6+
7+
import com.atlassian.event.api.EventListener;
8+
9+
import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport;
10+
11+
import com.atlassian.sal.api.pluginsettings.PluginSettings;
12+
import com.atlassian.sal.api.pluginsettings.PluginSettingsFactory;
13+
14+
import org.slf4j.Logger;
15+
import org.slf4j.LoggerFactory;
16+
17+
import javax.inject.Named;
18+
import javax.inject.Inject;
19+
20+
@Named("onRepositoryDeleted")
21+
public class OnRepositoryDeleted {
22+
private static final Logger log = LoggerFactory.getLogger(OnRepositoryClone.class);
23+
24+
@ComponentImport
25+
private final PluginSettingsFactory pluginSettingsFactory;
26+
27+
@Inject
28+
public OnRepositoryDeleted(
29+
final PluginSettingsFactory pluginSettingsFactory
30+
) {
31+
this.pluginSettingsFactory = pluginSettingsFactory;
32+
}
33+
34+
@EventListener
35+
public void onRepositoryDeleted(final RepositoryDeletedEvent event) {
36+
deleteSettings(event.getRepository());
37+
}
38+
39+
private void deleteSettings(final Repository repository) {
40+
41+
}
42+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package com.recuencojones.bitbucket.log;
2+
3+
import com.atlassian.bitbucket.repository.Repository;
4+
import com.atlassian.bitbucket.repository.RepositoryService;
5+
6+
import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport;
7+
8+
import com.atlassian.sal.api.pluginsettings.PluginSettings;
9+
import com.atlassian.sal.api.pluginsettings.PluginSettingsFactory;
10+
11+
import com.atlassian.soy.renderer.SoyException;
12+
import com.atlassian.soy.renderer.SoyTemplateRenderer;
13+
import com.atlassian.webresource.api.assembler.PageBuilderService;
14+
15+
import com.google.common.base.Strings;
16+
import com.google.common.collect.ImmutableMap;
17+
18+
import org.slf4j.Logger;
19+
import org.slf4j.LoggerFactory;
20+
21+
import javax.inject.Inject;
22+
23+
import javax.servlet.ServletException;
24+
import javax.servlet.http.HttpServlet;
25+
import javax.servlet.http.HttpServletRequest;
26+
import javax.servlet.http.HttpServletResponse;
27+
28+
import java.io.IOException;
29+
30+
import java.util.Map;
31+
32+
class RepositoryCloneSettingsServlet extends HttpServlet {
33+
private static final Logger log = LoggerFactory.getLogger(RepositoryCloneSettingsServlet.class);
34+
35+
@ComponentImport
36+
private final PluginSettingsFactory pluginSettingsFactory;
37+
38+
@ComponentImport
39+
private final RepositoryService repositoryService;
40+
41+
@ComponentImport
42+
private final SoyTemplateRenderer soyTemplateRenderer;
43+
44+
@ComponentImport
45+
private final PageBuilderService pageBuilderService;
46+
47+
@Inject
48+
public RepositoryCloneSettingsServlet(
49+
final PluginSettingsFactory pluginSettingsFactory,
50+
final RepositoryService repositoryService,
51+
final SoyTemplateRenderer soyTemplateRenderer,
52+
final PageBuilderService pageBuilderService
53+
) {
54+
this.pluginSettingsFactory = pluginSettingsFactory;
55+
this.repositoryService = repositoryService;
56+
this.soyTemplateRenderer = soyTemplateRenderer;
57+
this.pageBuilderService = pageBuilderService;
58+
}
59+
60+
@Override
61+
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
62+
String pathInfo = req.getPathInfo();
63+
if (Strings.isNullOrEmpty(pathInfo) || pathInfo.equals("/")) {
64+
resp.sendError(HttpServletResponse.SC_NOT_FOUND);
65+
return;
66+
}
67+
String[] pathParts = pathInfo.substring(1).split("/");
68+
if (pathParts.length != 2) {
69+
resp.sendError(HttpServletResponse.SC_NOT_FOUND);
70+
return;
71+
}
72+
String projectKey = pathParts[0];
73+
String repoSlug = pathParts[1];
74+
Repository repository = repositoryService.getBySlug(projectKey, repoSlug);
75+
if (repository == null) {
76+
resp.sendError(HttpServletResponse.SC_NOT_FOUND);
77+
return;
78+
}
79+
80+
doView(repository, req, resp);
81+
}
82+
83+
private void doView(Repository repository, HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
84+
render(
85+
resp,
86+
"com.recuencojones.bitbucket.log.page.repoSettingsPanel",
87+
ImmutableMap.<String, Object>builder()
88+
.put("repository", repository)
89+
.build()
90+
);
91+
}
92+
93+
private void render(HttpServletResponse resp, String templateName, Map<String, Object> data) throws ServletException, IOException {
94+
pageBuilderService.assembler()
95+
.resources()
96+
.requireContext("bitbucket.page.repository.settings.log-on-clone");
97+
98+
resp.setContentType("text/html;charset=UTF-8");
99+
try {
100+
soyTemplateRenderer.render(
101+
resp.getWriter(),
102+
"com.recuencojones.bitbucket.log-on-clone:log-on-clone-serverside-resources",
103+
templateName,
104+
data
105+
);
106+
} catch (SoyException e) {
107+
Throwable cause = e.getCause();
108+
if (cause instanceof IOException) {
109+
throw (IOException) cause;
110+
}
111+
throw new ServletException(e);
112+
}
113+
}
114+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package com.recuencojones.bitbucket.log.rest;
2+
3+
import com.atlassian.bitbucket.repository.Repository;
4+
import com.atlassian.bitbucket.rest.util.ResourcePatterns;
5+
import com.atlassian.bitbucket.rest.util.ResponseFactory;
6+
import com.atlassian.bitbucket.permission.Permission;
7+
import com.atlassian.bitbucket.permission.PermissionValidationService;
8+
9+
import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport;
10+
11+
import com.sun.jersey.spi.resource.Singleton;
12+
13+
import org.slf4j.Logger;
14+
import org.slf4j.LoggerFactory;
15+
16+
import javax.inject.Inject;
17+
18+
import javax.ws.rs.Consumes;
19+
import javax.ws.rs.GET;
20+
import javax.ws.rs.POST;
21+
import javax.ws.rs.Path;
22+
import javax.ws.rs.Produces;
23+
import javax.ws.rs.core.Context;
24+
import javax.ws.rs.core.MediaType;
25+
import javax.ws.rs.core.Response;
26+
27+
@Path(ResourcePatterns.REPOSITORY_URI + "/settings")
28+
@Consumes({ MediaType.APPLICATION_JSON })
29+
@Produces({ MediaType.APPLICATION_JSON })
30+
@Singleton
31+
public class RepositoryCloneSettingsResource {
32+
private static final Logger log = LoggerFactory.getLogger(RepositoryCloneSettingsResource.class);
33+
34+
@ComponentImport
35+
private final PermissionValidationService permissionValidationService;
36+
37+
@Inject
38+
public RepositoryCloneSettingsResource(
39+
PermissionValidationService permissionValidationService
40+
) {
41+
this.permissionValidationService = permissionValidationService;
42+
}
43+
44+
@GET
45+
public Response getSettings(@Context Repository repository) {
46+
log.info("Retrieve logging url for repository {}/{}", repository.getProject().getKey(), repository.getSlug());
47+
48+
// return ResponseFactory.ok(new RestAutoUnapproveSettings(unapproveService.getSettings(scope))).build();
49+
return ResponseFactory.ok().build();
50+
}
51+
52+
@POST
53+
public Response saveSettings(@Context Repository repository) {
54+
permissionValidationService.validateForRepository(repository, Permission.REPO_ADMIN);
55+
56+
log.info("Store logging url for repository {}/{}", repository.getProject().getKey(), repository.getSlug());
57+
58+
return ResponseFactory.ok().build();
59+
}
60+
}

src/main/resources/atlassian-plugin.xml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,49 @@
33
<description>${project.description}</description>
44
<version>${project.version}</version>
55
<vendor name="${project.organization.name}" url="${project.organization.url}" />
6+
<param name="atlassian-data-center-compatible">true</param>
67
<param name="plugin-icon">images/pluginIcon.png</param>
78
<param name="plugin-logo">images/pluginLogo.png</param>
89
</plugin-info>
910

1011
<!-- add our i18n resource -->
1112
<resource type="i18n" name="i18n" location="log-on-clone" />
13+
14+
<web-item key="repository-settings-log-on-clone-panel" name="Repository log on clone tab" weight="30" section="bitbucket.repository.settings.panel/repository-settings-addons-section">
15+
<label key="bitbucket.web.repository.settings.tab.git">Log on clone</label>
16+
<tooltip key="bitbucket.web.repository.settings.tab.git.tooltip">Log clone events to a remote service</tooltip>
17+
<link>${navBuilder.pluginServlets().path('log-on-clone', $project.key, $repository.slug).buildRelNoContext()}</link>
18+
</web-item>
19+
20+
<servlet key="log-on-clone-servlet" name="Log on Clone Servlet" class="com.recuencojones.bitbucket.log.RepositoryCloneSettingsServlet">
21+
<url-pattern>/log-on-clone/*</url-pattern>
22+
</servlet>
23+
24+
<rest key="log-on-clone-rest" path="/log-on-clone" version="1.0">
25+
<dispatcher>REQUEST</dispatcher>
26+
<dispatcher>FORWARD</dispatcher>
27+
<init-param>
28+
<param-name>extension.filter.excludes</param-name>
29+
<param-value>.*</param-value>
30+
</init-param>
31+
<package>com.recuencojones.bitbucket.log.rest</package>
32+
</rest>
33+
34+
<client-resource key="log-on-clone-serverside-resources">
35+
<directory location="/static/">
36+
<include>/**/*.soy</include>
37+
</directory>
38+
<dependency>com.atlassian.bitbucket.server.bitbucket-web:server-soy-templates</dependency>
39+
</client-resource>
40+
41+
<client-resource key="repository-settings-log-on-clone-panel-resources">
42+
<directory location="/static/">
43+
<exclude>/**/*.soy</exclude>
44+
</directory>
45+
<context>bitbucket.page.repository.settings.log-on-clone</context>
46+
<dependency>com.atlassian.bitbucket.server.bitbucket-web-api:navbuilder</dependency>
47+
<dependency>com.atlassian.bitbucket.server.bitbucket-web-api:server</dependency>
48+
<dependency>com.atlassian.bitbucket.server.bitbucket-web:global</dependency>
49+
<dependency>com.atlassian.auiplugin:aui-experimental-spinner</dependency>
50+
</client-resource>
1251
</atlassian-plugin>
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
1-
#put any key/value pairs here
2-
my.plugin.name=MyPlugin
1+
bitbucket.web.repository.log-on-clone.title=Log on clone for {0} / {1}
2+
bitbucket.web.repository.log-on-clone.heading=Log on clone
3+
bitbucket.web.repository.log-on-clone.setting.url.label=URL
4+
bitbucket.web.repository.log-on-clone.setting.url.placeholder=Logging service URL
5+
bitbucket.web.repository.log-on-clone.setting.url.description=Target url should be a webhook listening on http/https. Event will be sent with POST method.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.button-spinner {
2+
display: inline-block;
3+
position: relative;
4+
margin-left: 8px;
5+
height: 20px;
6+
width: 20px;
7+
top: 5px;
8+
}

0 commit comments

Comments
 (0)