Merge branch 'stable-2.15' * stable-2.15: WebLinks: Don't show file weblinks for magic files Fix file list header on mobile Allow diff file picker to not text transform gr-button text Fix disabled buttons Fix patch range dropdown not updating the file list Change-Id: I1698d317485607c24cf7f9b562782428a31251dc
diff --git a/Documentation/dev-release.txt b/Documentation/dev-release.txt index 039d545..ddf7e7f 100644 --- a/Documentation/dev-release.txt +++ b/Documentation/dev-release.txt
@@ -73,27 +73,11 @@ == Create the Actual Release -To create a Gerrit release the following steps have to be done: - -. link:#build-gerrit[Build the Gerrit Release] -. link:#publish-gerrit[Publish the Gerrit Release] -.. link:#publish-to-maven-central[Publish the Gerrit artifacts to Maven Central] -.. link:#publish-to-google-storage[Publish the Gerrit WAR to Google Storage] -.. link:#push-stable[Push the Stable Branch] -.. link:#push-tag[Push the Release Tag] -.. link:#upload-documentation[Upload the Documentation] -.. link:#finalize-release-notes[Finalize Release Notes] -.. link:#update-issues[Update the Issues] -.. link:#announce[Announce on Mailing List] -. link:#increase-version[Increase Gerrit Version for Current Development] -. link:#merge-stable[Merge `stable` into `master`] - - [[update-versions]] === Update Versions and Create Release Tag Before doing the release build, the `GERRIT_VERSION` in the `version.bzl` -file must be updated, e.g. change it from `2.5-SNAPSHOT` to `2.5`. +file must be updated, e.g. change it from `$version-SNAPSHOT` to `$version`. In addition the version must be updated in a number of pom.xml files. @@ -107,13 +91,14 @@ Commit the changes and create a signed release tag on the new commit: ---- - git tag -s -m "v2.5" v2.5 + version=2.15 + git tag -s -m "v$version" "v$version" ---- Tag the plugins: ---- - git submodule foreach git tag -s -m "v2.5" v2.5 + git submodule foreach git tag -s -m "v$version" "v$version" ---- [[build-gerrit]] @@ -126,8 +111,12 @@ ./tools/maven/api.sh install ---- -* Sanity check WAR -* Test the new Gerrit version +* Verify the WAR version: ++ +---- + java -jar ~/dl/gerrit-$version.war --version +---- +* Try upgrading a test site and launching the daemon * Verify plugin versions + @@ -257,11 +246,11 @@ [[push-stable]] ==== Push the Stable Branch -* Create the stable branch `stable-2.5` in the `gerrit` project via the +* Create the stable branch `stable-$version` in the `gerrit` project via the link:https://gerrit-review.googlesource.com/#/admin/projects/gerrit,branches[ Gerrit Web UI] or by push. -* Push the commits done on `stable-2.5` to `refs/for/stable-2.5` and +* Push the commits done on `stable-$version` to `refs/for/stable-$version` and get them merged @@ -271,13 +260,13 @@ Push the new Release Tag: ---- - git push gerrit-review tag v2.5 + git push gerrit-review tag v$version ---- Push the new Release Tag on the plugins: ---- - git submodule foreach git push gerrit-review tag v2.5 + git submodule foreach git push gerrit-review tag v$version ---- @@ -314,11 +303,11 @@ Update the issues by hand. There is no script for this. Our current process is an issue should be updated to say `Status = -Submitted, FixedIn-2.5` once the change is submitted, but before the +Submitted, FixedIn-$version` once the change is submitted, but before the release. After the release is actually made, you can search in Google Code for -`Status=Submitted FixedIn=2.5` and then batch update these changes +`Status=Submitted FixedIn=$version` and then batch update these changes to say `Status=Released`. Make sure the pulldown says `All Issues` because `Status=Submitted` is considered a closed issue.
diff --git a/WORKSPACE b/WORKSPACE index ac4d04b..6458571 100644 --- a/WORKSPACE +++ b/WORKSPACE
@@ -192,18 +192,6 @@ sha1 = "de80fe047052445869b96f6def6baca7182c95af", ) -maven_jar( - name = "joda_time", - artifact = "joda-time:joda-time:2.9.9", - sha1 = "f7b520c458572890807d143670c9b24f4de90897", -) - -maven_jar( - name = "joda_convert", - artifact = "org.joda:joda-convert:1.8.1", - sha1 = "675642ac208e0b741bc9118dcbcae44c271b992a", -) - load("//lib:guava.bzl", "GUAVA_VERSION", "GUAVA_BIN_SHA1") maven_jar( @@ -922,8 +910,8 @@ # When upgrading Elasticsearch, make sure it's compatible with Lucene maven_jar( name = "elasticsearch", - artifact = "org.elasticsearch:elasticsearch:2.4.5", - sha1 = "daafe48ae06592029a2fedca1fe2ac0f5eec3185", + artifact = "org.elasticsearch:elasticsearch:2.4.6", + sha1 = "d2954e1173a608a9711f132d1768a676a8b1fb81", ) # Java REST client for Elasticsearch. @@ -942,6 +930,18 @@ ) maven_jar( + name = "joda_time", + artifact = "joda-time:joda-time:2.9.9", + sha1 = "f7b520c458572890807d143670c9b24f4de90897", +) + +maven_jar( + name = "joda_convert", + artifact = "org.joda:joda-convert:1.8.1", + sha1 = "675642ac208e0b741bc9118dcbcae44c271b992a", +) + +maven_jar( name = "compress_lzf", artifact = "com.ning:compress-lzf:1.0.2", sha1 = "62896e6fca184c79cc01a14d143f3ae2b4f4b4ae",
diff --git a/gerrit-acceptance-framework/pom.xml b/gerrit-acceptance-framework/pom.xml index 2b1dcb0..a0f2e67 100644 --- a/gerrit-acceptance-framework/pom.xml +++ b/gerrit-acceptance-framework/pom.xml
@@ -2,7 +2,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>com.google.gerrit</groupId> <artifactId>gerrit-acceptance-framework</artifactId> - <version>2.15-rc0</version> + <version>2.16-SNAPSHOT</version> <packaging>jar</packaging> <name>Gerrit Code Review - Acceptance Test Framework</name> <description>Framework for Gerrit's acceptance tests</description>
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/project/DashboardIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/project/DashboardIT.java index b140a6e..7d5072a 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/project/DashboardIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/project/DashboardIT.java
@@ -16,6 +16,7 @@ import static com.google.common.truth.Truth.assertThat; import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS; +import static java.util.stream.Collectors.toList; import com.google.gerrit.acceptance.AbstractDaemonTest; import com.google.gerrit.acceptance.NoHttpd; @@ -56,15 +57,25 @@ @Test public void getDashboard() throws Exception { - assertThat(dashboards()).isEmpty(); DashboardInfo info = createDashboard(DashboardsCollection.DEFAULT_DASHBOARD_NAME, "test"); DashboardInfo result = project().dashboard(info.id).get(); - assertThat(result.id).isEqualTo(info.id); - assertThat(result.path).isEqualTo(info.path); - assertThat(result.ref).isEqualTo(info.ref); - assertThat(result.project).isEqualTo(project.get()); - assertThat(result.definingProject).isEqualTo(project.get()); - assertThat(dashboards()).hasSize(1); + assertDashboardInfo(result, info); + } + + @Test + public void getDashboardNonDefault() throws Exception { + DashboardInfo info = createDashboard("my", "test"); + DashboardInfo result = project().dashboard(info.id).get(); + assertDashboardInfo(result, info); + } + + @Test + public void listDashboards() throws Exception { + assertThat(dashboards()).isEmpty(); + DashboardInfo info1 = createDashboard(DashboardsCollection.DEFAULT_DASHBOARD_NAME, "test1"); + DashboardInfo info2 = createDashboard(DashboardsCollection.DEFAULT_DASHBOARD_NAME, "test2"); + assertThat(dashboards().stream().map(d -> d.id).collect(toList())) + .containsExactly(info1.id, info2.id); } @Test @@ -115,6 +126,14 @@ project().dashboard(info.id).get(true); } + private void assertDashboardInfo(DashboardInfo actual, DashboardInfo expected) throws Exception { + assertThat(actual.id).isEqualTo(expected.id); + assertThat(actual.path).isEqualTo(expected.path); + assertThat(actual.ref).isEqualTo(expected.ref); + assertThat(actual.project).isEqualTo(project.get()); + assertThat(actual.definingProject).isEqualTo(project.get()); + } + private List<DashboardInfo> dashboards() throws Exception { return project().dashboards().get(); } @@ -139,9 +158,10 @@ new TestRepository<>(r).branch(canonicalRef).commit(); String content = "[dashboard]\n" - + "Description = Test\n" + + "Title = Reviewer\n" + + "Description = Own review requests\n" + "foreach = owner:self\n" - + "[section \"Mine\"]\n" + + "[section \"Open\"]\n" + "query = is:open"; cb.add(info.path, content); RevCommit c = cb.create();
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/edit/BUILD b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/edit/BUILD index 990bad6..6bfecfa 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/edit/BUILD +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/edit/BUILD
@@ -4,7 +4,4 @@ srcs = ["ChangeEditIT.java"], group = "edit", labels = ["edit"], - deps = [ - "//lib/joda:joda-time", - ], )
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/BUILD b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/BUILD index 43ec5bc..897b99f 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/BUILD +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/BUILD
@@ -16,7 +16,6 @@ srcs = ["AbstractPushForReview.java"], deps = [ "//gerrit-acceptance-tests:lib", - "//lib/joda:joda-time", ], )
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/BUILD b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/BUILD index b7ed2e8..49f00f9 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/BUILD +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/BUILD
@@ -15,7 +15,6 @@ labels = ["rest"], deps = [ ":submit_util", - "//lib/joda:joda-time", ], )
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/mail/AbstractMailIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/mail/AbstractMailIT.java index 6f4bdab..32f1ce5 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/mail/AbstractMailIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/mail/AbstractMailIT.java
@@ -23,8 +23,8 @@ import com.google.gerrit.extensions.client.Comment; import com.google.gerrit.extensions.client.Side; import com.google.gerrit.server.mail.receive.MailMessage; +import java.time.Instant; import java.util.HashMap; -import org.joda.time.DateTime; import org.junit.Ignore; @Ignore @@ -36,7 +36,7 @@ b.from(user.emailAddress); b.addTo(user.emailAddress); // Not evaluated b.subject(""); - b.dateReceived(new DateTime()); + b.dateReceived(Instant.now()); return b; }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/mail/BUILD b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/mail/BUILD index 71a6135..c3a4e20 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/mail/BUILD +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/mail/BUILD
@@ -2,7 +2,6 @@ DEPS = [ "//lib/greenmail", - "//lib/joda:joda-time", "//lib/mail", ]
diff --git a/gerrit-common/BUILD b/gerrit-common/BUILD index 4389080..d9d4392 100644 --- a/gerrit-common/BUILD +++ b/gerrit-common/BUILD
@@ -28,7 +28,6 @@ "//lib:gwtorm_client", "//lib:servlet-api-3_1", "//lib/jgit/org.eclipse.jgit:jgit", - "//lib/joda:joda-time", "//lib/log:api", ], gwt_xml = SRC + "Common.gwt.xml", @@ -53,7 +52,6 @@ "//lib:gwtorm", "//lib:servlet-api-3_1", "//lib/jgit/org.eclipse.jgit:jgit", - "//lib/joda:joda-time", "//lib/log:api", ], )
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/TimeUtil.java b/gerrit-common/src/main/java/com/google/gerrit/common/TimeUtil.java index a8e40c6..b1697dc 100644 --- a/gerrit-common/src/main/java/com/google/gerrit/common/TimeUtil.java +++ b/gerrit-common/src/main/java/com/google/gerrit/common/TimeUtil.java
@@ -15,14 +15,21 @@ package com.google.gerrit.common; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.VisibleForTesting; import java.sql.Timestamp; -import org.joda.time.DateTimeUtils; +import java.util.function.LongSupplier; /** Static utility methods for dealing with dates and times. */ -@GwtIncompatible("Unemulated org.joda.time.DateTimeUtils") +@GwtIncompatible("Unemulated Java 8 functionalities") public class TimeUtil { + private static final LongSupplier SYSTEM_CURRENT_MILLIS_SUPPLIER = System::currentTimeMillis; + + private static volatile LongSupplier currentMillisSupplier = SYSTEM_CURRENT_MILLIS_SUPPLIER; + public static long nowMs() { - return DateTimeUtils.currentTimeMillis(); + // We should rather use Instant.now(Clock).toEpochMilli() instead but this would require some + // changes in our testing code as we wouldn't have clock steps anymore. + return currentMillisSupplier.getAsLong(); } public static Timestamp nowTs() { @@ -33,5 +40,15 @@ return new Timestamp((t.getTime() / 1000) * 1000); } + @VisibleForTesting + public static void setCurrentMillisSupplier(LongSupplier customCurrentMillisSupplier) { + currentMillisSupplier = customCurrentMillisSupplier; + } + + @VisibleForTesting + public static void resetCurrentMillisSupplier() { + currentMillisSupplier = SYSTEM_CURRENT_MILLIS_SUPPLIER; + } + private TimeUtil() {} }
diff --git a/gerrit-elasticsearch/BUILD b/gerrit-elasticsearch/BUILD index fb86aaf..d278bcf 100644 --- a/gerrit-elasticsearch/BUILD +++ b/gerrit-elasticsearch/BUILD
@@ -17,10 +17,10 @@ "//lib/elasticsearch", "//lib/elasticsearch:jest", "//lib/elasticsearch:jest-common", + "//lib/elasticsearch:joda-time", "//lib/guice", "//lib/guice:guice-assistedinject", "//lib/jgit/org.eclipse.jgit:jgit", - "//lib/joda:joda-time", "//lib/log:api", "//lib/lucene:lucene-analyzers-common", "//lib/lucene:lucene-core",
diff --git a/gerrit-extension-api/pom.xml b/gerrit-extension-api/pom.xml index 7179f46..a8ae2e6 100644 --- a/gerrit-extension-api/pom.xml +++ b/gerrit-extension-api/pom.xml
@@ -2,7 +2,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>com.google.gerrit</groupId> <artifactId>gerrit-extension-api</artifactId> - <version>2.15-rc0</version> + <version>2.16-SNAPSHOT</version> <packaging>jar</packaging> <name>Gerrit Code Review - Extension API</name> <description>API for Gerrit Extensions</description>
diff --git a/gerrit-httpd/BUILD b/gerrit-httpd/BUILD index dbca10c..cc2160f 100644 --- a/gerrit-httpd/BUILD +++ b/gerrit-httpd/BUILD
@@ -77,6 +77,5 @@ "//lib/guice:guice-servlet", "//lib/jgit/org.eclipse.jgit:jgit", "//lib/jgit/org.eclipse.jgit.junit:junit", - "//lib/joda:joda-time", ], )
diff --git a/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy b/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy index 51c60af..c3b522a 100644 --- a/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy +++ b/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
@@ -21,7 +21,7 @@ * @param staticResourcePath * @param? versionInfo */ -{template .Index autoescape="strict" kind="html"} +{template .Index kind="html"} <!DOCTYPE html>{\n} <html lang="en">{\n} <meta charset="utf-8">{\n}
diff --git a/gerrit-httpd/src/test/java/com/google/gerrit/httpd/raw/ResourceServletTest.java b/gerrit-httpd/src/test/java/com/google/gerrit/httpd/raw/ResourceServletTest.java index 18256c6..6dd15bc 100644 --- a/gerrit-httpd/src/test/java/com/google/gerrit/httpd/raw/ResourceServletTest.java +++ b/gerrit-httpd/src/test/java/com/google/gerrit/httpd/raw/ResourceServletTest.java
@@ -36,9 +36,11 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.FileTime; +import java.time.LocalDateTime; +import java.time.Month; +import java.time.ZoneOffset; import java.util.concurrent.atomic.AtomicLong; import java.util.zip.GZIPInputStream; -import org.joda.time.format.ISODateTimeFormat; import org.junit.Before; import org.junit.Test; @@ -91,7 +93,12 @@ @Before public void setUp() { fs = Jimfs.newFileSystem(Configuration.unix()); - ts = new AtomicLong(ISODateTimeFormat.dateTime().parseMillis("2010-01-30T12:00:00.000-08:00")); + ts = + new AtomicLong( + LocalDateTime.of(2010, Month.JANUARY, 30, 12, 0, 0) + .atOffset(ZoneOffset.ofHours(-8)) + .toInstant() + .toEpochMilli()); } @Test
diff --git a/gerrit-pgm/BUILD b/gerrit-pgm/BUILD index 1fd3165..60663d7 100644 --- a/gerrit-pgm/BUILD +++ b/gerrit-pgm/BUILD
@@ -22,7 +22,6 @@ "//lib/guice:guice-assistedinject", "//lib/guice:guice-servlet", "//lib/jgit/org.eclipse.jgit:jgit", - "//lib/joda:joda-time", "//lib/log:api", "//lib/log:log4j", ]
diff --git a/gerrit-plugin-api/BUILD b/gerrit-plugin-api/BUILD index fe9ce19..5ed4b8c 100644 --- a/gerrit-plugin-api/BUILD +++ b/gerrit-plugin-api/BUILD
@@ -29,7 +29,6 @@ "//lib/httpcomponents:httpcore", "//lib/jgit/org.eclipse.jgit.http.server:jgit-servlet", "//lib/jgit/org.eclipse.jgit:jgit", - "//lib/joda:joda-time", "//lib/log:api", "//lib/log:log4j", "//lib/mina:sshd",
diff --git a/gerrit-plugin-api/pom.xml b/gerrit-plugin-api/pom.xml index c99220f..84df44a 100644 --- a/gerrit-plugin-api/pom.xml +++ b/gerrit-plugin-api/pom.xml
@@ -2,7 +2,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>com.google.gerrit</groupId> <artifactId>gerrit-plugin-api</artifactId> - <version>2.15-rc0</version> + <version>2.16-SNAPSHOT</version> <packaging>jar</packaging> <name>Gerrit Code Review - Plugin API</name> <description>API for Gerrit Plugins</description>
diff --git a/gerrit-plugin-gwtui/pom.xml b/gerrit-plugin-gwtui/pom.xml index 122f54a..cc9aafc 100644 --- a/gerrit-plugin-gwtui/pom.xml +++ b/gerrit-plugin-gwtui/pom.xml
@@ -2,7 +2,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>com.google.gerrit</groupId> <artifactId>gerrit-plugin-gwtui</artifactId> - <version>2.15-rc0</version> + <version>2.16-SNAPSHOT</version> <packaging>jar</packaging> <name>Gerrit Code Review - Plugin GWT UI</name> <description>Common Classes for Gerrit GWT UI Plugins</description>
diff --git a/gerrit-server/BUILD b/gerrit-server/BUILD index e124e89..e285ee5 100644 --- a/gerrit-server/BUILD +++ b/gerrit-server/BUILD
@@ -92,7 +92,6 @@ "//lib/guice:guice-servlet", "//lib/jgit/org.eclipse.jgit.archive:jgit-archive", "//lib/jgit/org.eclipse.jgit:jgit", - "//lib/joda:joda-time", "//lib/jsoup", "//lib/log:api", "//lib/log:jsonevent-layout", @@ -181,7 +180,6 @@ "//lib/guice:guice-servlet", "//lib/jgit/org.eclipse.jgit:jgit", "//lib/jgit/org.eclipse.jgit.junit:junit", - "//lib/joda:joda-time", "//lib/log:api", "//lib/log:impl_log4j", "//lib/log:log4j",
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Files.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Files.java index 21da0b8..dc180cc 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Files.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Files.java
@@ -42,6 +42,7 @@ import com.google.gerrit.server.patch.PatchListCache; import com.google.gerrit.server.patch.PatchListKey; import com.google.gerrit.server.patch.PatchListNotAvailableException; +import com.google.gerrit.server.patch.PatchListObjectTooLargeException; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import com.google.inject.Provider; @@ -240,6 +241,8 @@ try { return copy(res.files(), res.patchSetId(), resource, userId); + } catch (PatchListObjectTooLargeException e) { + log.warn("Cannot copy patch review flags: " + e.getMessage()); } catch (IOException | PatchListNotAvailableException e) { log.warn("Cannot copy patch review flags", e); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/ScheduleConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/ScheduleConfig.java index 4a87474..c5d60a3 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/ScheduleConfig.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/ScheduleConfig.java
@@ -16,16 +16,16 @@ import com.google.common.annotations.VisibleForTesting; import java.text.MessageFormat; +import java.time.DayOfWeek; +import java.time.Duration; +import java.time.LocalTime; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.time.temporal.ChronoUnit; import java.util.Locale; import java.util.concurrent.TimeUnit; import org.eclipse.jgit.lib.Config; -import org.joda.time.DateTime; -import org.joda.time.LocalDateTime; -import org.joda.time.LocalTime; -import org.joda.time.MutableDateTime; -import org.joda.time.format.DateTimeFormat; -import org.joda.time.format.DateTimeFormatter; -import org.joda.time.format.ISODateTimeFormat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,16 +49,16 @@ } public ScheduleConfig(Config rc, String section, String subsection) { - this(rc, section, subsection, DateTime.now()); + this(rc, section, subsection, ZonedDateTime.now()); } public ScheduleConfig( Config rc, String section, String subsection, String keyInterval, String keyStartTime) { - this(rc, section, subsection, keyInterval, keyStartTime, DateTime.now()); + this(rc, section, subsection, keyInterval, keyStartTime, ZonedDateTime.now()); } @VisibleForTesting - ScheduleConfig(Config rc, String section, String subsection, DateTime now) { + ScheduleConfig(Config rc, String section, String subsection, ZonedDateTime now) { this(rc, section, subsection, KEY_INTERVAL, KEY_STARTTIME, now); } @@ -69,7 +69,7 @@ String subsection, String keyInterval, String keyStartTime, - DateTime now) { + ZonedDateTime now) { this.rc = rc; this.section = section; this.subsection = subsection; @@ -122,31 +122,24 @@ String section, String subsection, String keyStartTime, - DateTime now, + ZonedDateTime now, long interval) { long delay = MISSING_CONFIG; String start = rc.getString(section, subsection, keyStartTime); try { if (start != null) { - DateTimeFormatter formatter; - MutableDateTime startTime = now.toMutableDateTime(); + DateTimeFormatter formatter = + DateTimeFormatter.ofPattern("[E ]HH:mm").withLocale(Locale.US); + LocalTime firstStartTime = LocalTime.parse(start, formatter); + ZonedDateTime startTime = now.with(firstStartTime); try { - formatter = ISODateTimeFormat.hourMinute(); - LocalTime firstStartTime = formatter.parseLocalTime(start); - startTime.hourOfDay().set(firstStartTime.getHourOfDay()); - startTime.minuteOfHour().set(firstStartTime.getMinuteOfHour()); - } catch (IllegalArgumentException e1) { - formatter = DateTimeFormat.forPattern("E HH:mm").withLocale(Locale.US); - LocalDateTime firstStartDateTime = formatter.parseLocalDateTime(start); - startTime.dayOfWeek().set(firstStartDateTime.getDayOfWeek()); - startTime.hourOfDay().set(firstStartDateTime.getHourOfDay()); - startTime.minuteOfHour().set(firstStartDateTime.getMinuteOfHour()); + DayOfWeek dayOfWeek = formatter.parse(start, DayOfWeek::from); + startTime = startTime.with(dayOfWeek); + } catch (DateTimeParseException ignored) { + // Day of week is an optional parameter. } - startTime.secondOfMinute().set(0); - startTime.millisOfSecond().set(0); - long s = startTime.getMillis(); - long n = now.getMillis(); - delay = (s - n) % interval; + startTime = startTime.truncatedTo(ChronoUnit.MINUTES); + delay = Duration.between(now, startTime).toMillis() % interval; if (delay <= 0) { delay += interval; }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java index afd78dc..2614eaf 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
@@ -56,6 +56,7 @@ import com.google.gerrit.server.patch.PatchListCache; import com.google.gerrit.server.patch.PatchListEntry; import com.google.gerrit.server.patch.PatchListNotAvailableException; +import com.google.gerrit.server.patch.PatchListObjectTooLargeException; import com.google.gerrit.server.query.change.ChangeData; import com.google.gerrit.server.query.change.InternalChangeQuery; import com.google.gwtorm.server.OrmException; @@ -425,6 +426,8 @@ p.insertions = patch.getInsertions(); patchSetAttribute.files.add(p); } + } catch (PatchListObjectTooLargeException e) { + log.warn("Cannot get patch list: " + e.getMessage()); } catch (PatchListNotAvailableException e) { log.warn("Cannot get patch list", e); } @@ -498,6 +501,8 @@ p.kind = changeKindCache.getChangeKind(db, change, patchSet); } catch (IOException | OrmException e) { log.error("Cannot load patch set data for " + patchSet.getId(), e); + } catch (PatchListObjectTooLargeException e) { + log.warn(String.format("Cannot get size information for %s: %s", pId, e.getMessage())); } catch (PatchListNotAvailableException e) { log.error(String.format("Cannot get size information for %s.", pId), e); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/ChangeAbandoned.java b/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/ChangeAbandoned.java index f9fc60a..1415f3b 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/ChangeAbandoned.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/ChangeAbandoned.java
@@ -25,6 +25,7 @@ import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.server.GpgException; import com.google.gerrit.server.patch.PatchListNotAvailableException; +import com.google.gerrit.server.patch.PatchListObjectTooLargeException; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import java.io.IOException; @@ -70,6 +71,8 @@ util.logEventListenerError(this, l, e); } } + } catch (PatchListObjectTooLargeException e) { + log.warn("Couldn't fire event: " + e.getMessage()); } catch (PatchListNotAvailableException | GpgException | IOException | OrmException e) { log.error("Couldn't fire event", e); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/ChangeMerged.java b/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/ChangeMerged.java index feaa54a..3bba164 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/ChangeMerged.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/ChangeMerged.java
@@ -25,6 +25,7 @@ import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.server.GpgException; import com.google.gerrit.server.patch.PatchListNotAvailableException; +import com.google.gerrit.server.patch.PatchListObjectTooLargeException; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import java.io.IOException; @@ -64,6 +65,8 @@ util.logEventListenerError(this, l, e); } } + } catch (PatchListObjectTooLargeException e) { + log.warn("Couldn't fire event: " + e.getMessage()); } catch (PatchListNotAvailableException | GpgException | IOException | OrmException e) { log.error("Couldn't fire event", e); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/ChangeRestored.java b/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/ChangeRestored.java index 03a6f1f..0437623 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/ChangeRestored.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/ChangeRestored.java
@@ -25,6 +25,7 @@ import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.server.GpgException; import com.google.gerrit.server.patch.PatchListNotAvailableException; +import com.google.gerrit.server.patch.PatchListObjectTooLargeException; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import java.io.IOException; @@ -63,6 +64,8 @@ util.logEventListenerError(this, l, e); } } + } catch (PatchListObjectTooLargeException e) { + log.warn("Couldn't fire event: " + e.getMessage()); } catch (PatchListNotAvailableException | GpgException | IOException | OrmException e) { log.error("Couldn't fire event", e); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/CommentAdded.java b/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/CommentAdded.java index e76a032..1676c2c 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/CommentAdded.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/CommentAdded.java
@@ -26,6 +26,7 @@ import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.server.GpgException; import com.google.gerrit.server.patch.PatchListNotAvailableException; +import com.google.gerrit.server.patch.PatchListObjectTooLargeException; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import java.io.IOException; @@ -74,6 +75,8 @@ util.logEventListenerError(this, l, e); } } + } catch (PatchListObjectTooLargeException e) { + log.warn("Couldn't fire event: " + e.getMessage()); } catch (PatchListNotAvailableException | GpgException | IOException | OrmException e) { log.error("Couldn't fire event", e); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/ReviewerAdded.java b/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/ReviewerAdded.java index e4f8572..d785f38 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/ReviewerAdded.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/ReviewerAdded.java
@@ -26,6 +26,7 @@ import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.server.GpgException; import com.google.gerrit.server.patch.PatchListNotAvailableException; +import com.google.gerrit.server.patch.PatchListObjectTooLargeException; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import java.io.IOException; @@ -67,6 +68,8 @@ util.logEventListenerError(this, l, e); } } + } catch (PatchListObjectTooLargeException e) { + log.warn("Couldn't fire event: " + e.getMessage()); } catch (PatchListNotAvailableException | GpgException | IOException | OrmException e) { log.error("Couldn't fire event", e); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/ReviewerDeleted.java b/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/ReviewerDeleted.java index 033efe2..9914563 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/ReviewerDeleted.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/ReviewerDeleted.java
@@ -26,6 +26,7 @@ import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.server.GpgException; import com.google.gerrit.server.patch.PatchListNotAvailableException; +import com.google.gerrit.server.patch.PatchListObjectTooLargeException; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import java.io.IOException; @@ -78,6 +79,8 @@ util.logEventListenerError(this, listener, e); } } + } catch (PatchListObjectTooLargeException e) { + log.warn("Couldn't fire event: " + e.getMessage()); } catch (PatchListNotAvailableException | GpgException | IOException | OrmException e) { log.error("Couldn't fire event", e); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/RevisionCreated.java b/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/RevisionCreated.java index 8a781d0..475e4b5 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/RevisionCreated.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/RevisionCreated.java
@@ -25,6 +25,7 @@ import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.server.GpgException; import com.google.gerrit.server.patch.PatchListNotAvailableException; +import com.google.gerrit.server.patch.PatchListObjectTooLargeException; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import java.io.IOException; @@ -64,6 +65,8 @@ util.logEventListenerError(this, l, e); } } + } catch (PatchListObjectTooLargeException e) { + log.warn("Couldn't fire event: " + e.getMessage()); } catch (PatchListNotAvailableException | GpgException | IOException | OrmException e) { log.error("Couldn't fire event", e); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/VoteDeleted.java b/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/VoteDeleted.java index 71a603c..5f20293 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/VoteDeleted.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/extensions/events/VoteDeleted.java
@@ -26,6 +26,7 @@ import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.server.GpgException; import com.google.gerrit.server.patch.PatchListNotAvailableException; +import com.google.gerrit.server.patch.PatchListObjectTooLargeException; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import java.io.IOException; @@ -78,6 +79,8 @@ util.logEventListenerError(this, l, e); } } + } catch (PatchListObjectTooLargeException e) { + log.warn("Couldn't fire event: " + e.getMessage()); } catch (PatchListNotAvailableException | GpgException | IOException | OrmException e) { log.error("Couldn't fire event", e); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/receive/MailMessage.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/receive/MailMessage.java index 68b3c23..0d20464 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/receive/MailMessage.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/receive/MailMessage.java
@@ -18,7 +18,7 @@ import com.google.common.collect.ImmutableList; import com.google.gerrit.common.Nullable; import com.google.gerrit.server.mail.Address; -import org.joda.time.DateTime; +import java.time.Instant; /** * A simplified representation of an RFC 2045-2047 mime email message used for representing received @@ -40,7 +40,7 @@ public abstract ImmutableList<Address> cc(); // Metadata - public abstract DateTime dateReceived(); + public abstract Instant dateReceived(); public abstract ImmutableList<String> additionalHeaders(); // Content @@ -84,7 +84,7 @@ return this; } - public abstract Builder dateReceived(DateTime val); + public abstract Builder dateReceived(Instant instant); public abstract ImmutableList.Builder<String> additionalHeadersBuilder();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/receive/RawMailParser.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/receive/RawMailParser.java index d2f91ed..57fe21f 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/receive/RawMailParser.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/receive/RawMailParser.java
@@ -33,7 +33,6 @@ import org.apache.james.mime4j.dom.TextBody; import org.apache.james.mime4j.dom.address.Mailbox; import org.apache.james.mime4j.message.DefaultMessageBuilder; -import org.joda.time.DateTime; /** Parses raw email content received through POP3 or IMAP into an internal {@link MailMessage}. */ public class RawMailParser { @@ -66,7 +65,9 @@ if (mimeMessage.getSubject() != null) { messageBuilder.subject(mimeMessage.getSubject()); } - messageBuilder.dateReceived(new DateTime(mimeMessage.getDate())); + if (mimeMessage.getDate() != null) { + messageBuilder.dateReceived(mimeMessage.getDate().toInstant()); + } // Add From, To and Cc if (mimeMessage.getFrom() != null && mimeMessage.getFrom().size() > 0) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/ChangeEmail.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/ChangeEmail.java index 53e7d22..a7826cb 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/ChangeEmail.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/ChangeEmail.java
@@ -37,6 +37,7 @@ import com.google.gerrit.server.patch.PatchList; import com.google.gerrit.server.patch.PatchListEntry; import com.google.gerrit.server.patch.PatchListNotAvailableException; +import com.google.gerrit.server.patch.PatchListObjectTooLargeException; import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException; import com.google.gerrit.server.permissions.ChangePermission; import com.google.gerrit.server.permissions.GlobalPermission; @@ -539,6 +540,9 @@ // Currently these always have a null oldId in the PatchList. return "[Octopus merge; cannot be formatted as a diff.]\n"; } + } catch (PatchListObjectTooLargeException e) { + log.warn("Cannot format patch " + e.getMessage()); + return ""; } catch (PatchListNotAvailableException e) { log.error("Cannot format patch", e); return "";
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/CommentSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/CommentSender.java index 5b7d3b7..e8f2522 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/CommentSender.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/CommentSender.java
@@ -35,6 +35,7 @@ import com.google.gerrit.server.patch.PatchFile; import com.google.gerrit.server.patch.PatchList; import com.google.gerrit.server.patch.PatchListNotAvailableException; +import com.google.gerrit.server.patch.PatchListObjectTooLargeException; import com.google.gerrit.server.util.LabelVote; import com.google.gwtorm.client.KeyUtil; import com.google.gwtorm.server.OrmException; @@ -232,6 +233,8 @@ if (repo != null) { try { patchList = getPatchList(); + } catch (PatchListObjectTooLargeException e) { + log.warn("Failed to get patch list: " + e.getMessage()); } catch (PatchListNotAvailableException e) { log.error("Failed to get patch list", e); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListCacheImpl.java index 7777400..8900a15 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListCacheImpl.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListCacheImpl.java
@@ -103,7 +103,7 @@ try { PatchList pl = fileCache.get(key, fileLoaderFactory.create(key, project)); if (pl instanceof LargeObjectTombstone) { - throw new PatchListNotAvailableException( + throw new PatchListObjectTooLargeException( "Error computing " + key + ". Previous attempt failed with LargeObjectException"); } if (key.getAlgorithm() == PatchListKey.Algorithm.OPTIMIZED_DIFF) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListObjectTooLargeException.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListObjectTooLargeException.java new file mode 100644 index 0000000..54e0e6c --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListObjectTooLargeException.java
@@ -0,0 +1,27 @@ +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.patch; + +/** + * Exception thrown when the PatchList could not be computed because previous attempts failed with + * {@code LargeObjectException}. This is not thrown on the first computation. + */ +public class PatchListObjectTooLargeException extends PatchListNotAvailableException { + private static final long serialVersionUID = 1L; + + public PatchListObjectTooLargeException(String message) { + super(message); + } +}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java index ee9c570..dde577d 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
@@ -42,17 +42,19 @@ import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.lang.reflect.Field; +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.util.io.DisabledOutputStream; -import org.joda.time.format.DateTimeFormat; -import org.joda.time.format.DateTimeFormatter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -65,7 +67,10 @@ public class OutputStreamQuery { private static final Logger log = LoggerFactory.getLogger(OutputStreamQuery.class); - private static final DateTimeFormatter dtf = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss zzz"); + private static final DateTimeFormatter dtf = + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss zzz") + .withLocale(Locale.US) + .withZone(ZoneId.systemDefault()); public enum OutputFormat { TEXT, @@ -402,7 +407,7 @@ out.print('\n'); } else if (value instanceof Long && isDateField(field)) { out.print(' '); - out.print(dtf.print(((Long) value) * 1000L)); + out.print(dtf.format(Instant.ofEpochSecond((Long) value))); out.print('\n'); } else if (isPrimitive(value)) { out.print(' ');
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Abandoned.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Abandoned.soy index 50c5fc3..623cfe26 100644 --- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Abandoned.soy +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Abandoned.soy
@@ -24,7 +24,7 @@ * @param email * @param fromName */ -{template .Abandoned autoescape="strict" kind="text"} +{template .Abandoned kind="text"} {$fromName} has abandoned this change. {if $email.changeUrl} ( {$email.changeUrl} ){/if}{\n} {\n}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/AbandonedHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/AbandonedHtml.soy index c7d4699..fb8ff78 100644 --- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/AbandonedHtml.soy +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/AbandonedHtml.soy
@@ -21,7 +21,7 @@ * @param email * @param fromName */ -{template .AbandonedHtml autoescape="strict" kind="html"} +{template .AbandonedHtml kind="html"} <p> {$fromName} <strong>abandoned</strong> this change. </p>
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/AddKey.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/AddKey.soy index aa2b27d..af99569 100644 --- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/AddKey.soy +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/AddKey.soy
@@ -21,7 +21,7 @@ * adding a new SSH or GPG key to an account. * @param email */ -{template .AddKey autoescape="strict" kind="text"} +{template .AddKey kind="text"} One or more new {$email.keyType} keys have been added to Gerrit Code Review at {sp}{$email.gerritHost}: @@ -68,4 +68,4 @@ This is a send-only email address. Replies to this message will not be read or answered. -{/template} \ No newline at end of file +{/template}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/AddKeyHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/AddKeyHtml.soy index 017fd6d..21161ea 100644 --- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/AddKeyHtml.soy +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/AddKeyHtml.soy
@@ -19,7 +19,7 @@ /** * @param email */ -{template .AddKeyHtml autoescape="strict" kind="html"} +{template .AddKeyHtml kind="html"} <p> One or more new {$email.keyType} keys have been added to Gerrit Code Review at {$email.gerritHost}:
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ChangeFooter.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ChangeFooter.soy index 37ac126..f1d201b 100644 --- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ChangeFooter.soy +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ChangeFooter.soy
@@ -21,7 +21,7 @@ * that will be appended to ALL emails related to changes. * @param email */ -{template .ChangeFooter autoescape="strict" kind="text"} +{template .ChangeFooter kind="text"} --{sp} {\n}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ChangeFooterHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ChangeFooterHtml.soy index 00f21db..dea6724 100644 --- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ChangeFooterHtml.soy +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ChangeFooterHtml.soy
@@ -20,7 +20,7 @@ * @param change * @param email */ -{template .ChangeFooterHtml autoescape="strict" kind="html"} +{template .ChangeFooterHtml kind="html"} {if $email.changeUrl or $email.settingsUrl} <p> {if $email.changeUrl}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ChangeSubject.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ChangeSubject.soy index 98de6e7..d8cffc4 100644 --- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ChangeSubject.soy +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ChangeSubject.soy
@@ -23,6 +23,6 @@ * @param change * @param shortProjectName */ -{template .ChangeSubject autoescape="strict" kind="text"} +{template .ChangeSubject kind="text"} Change in {$shortProjectName}[{$branch.shortName}]: {$change.shortSubject} {/template}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Comment.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Comment.soy index 7bedc1c..7f3062c 100644 --- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Comment.soy +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Comment.soy
@@ -25,7 +25,7 @@ * @param fromName * @param commentFiles */ -{template .Comment autoescape="strict" kind="text"} +{template .Comment kind="text"} {$fromName} has posted comments on this change. {if $email.changeUrl} ( {$email.changeUrl} ){/if}{\n} {\n}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/CommentFooter.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/CommentFooter.soy index 73fdfba..3998438 100644 --- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/CommentFooter.soy +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/CommentFooter.soy
@@ -21,5 +21,5 @@ * that will be appended to emails related to a user submitting comments on * changes. */ -{template .CommentFooter autoescape="strict" kind="text"} +{template .CommentFooter kind="text"} {/template}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/CommentFooterHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/CommentFooterHtml.soy index 7bf28e7..c54f926 100644 --- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/CommentFooterHtml.soy +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/CommentFooterHtml.soy
@@ -16,5 +16,5 @@ {namespace com.google.gerrit.server.mail.template} -{template .CommentFooterHtml autoescape="strict" kind="html"} +{template .CommentFooterHtml kind="html"} {/template}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/CommentHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/CommentHtml.soy index 870ad46..9b96d69 100644 --- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/CommentHtml.soy +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/CommentHtml.soy
@@ -24,7 +24,7 @@ * @param patchSet * @param patchSetCommentBlocks */ -{template .CommentHtml autoescape="strict" kind="html"} +{template .CommentHtml kind="html"} {let $commentHeaderStyle kind="css"} margin-bottom: 4px; {/let}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/DeleteReviewer.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/DeleteReviewer.soy index 888ee4b..fc1d60f 100644 --- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/DeleteReviewer.soy +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/DeleteReviewer.soy
@@ -24,7 +24,7 @@ * @param email * @param fromName */ -{template .DeleteReviewer autoescape="strict" kind="text"} +{template .DeleteReviewer kind="text"} {$fromName} has removed{sp} {foreach $reviewerName in $email.reviewerNames} {if not isFirst($reviewerName)},{sp}{/if} @@ -41,4 +41,4 @@ {$coverLetter} {\n} {/if} -{/template} \ No newline at end of file +{/template}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/DeleteReviewerHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/DeleteReviewerHtml.soy index 5faa411..74e5ee5 100644 --- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/DeleteReviewerHtml.soy +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/DeleteReviewerHtml.soy
@@ -20,7 +20,7 @@ * @param email * @param fromName */ -{template .DeleteReviewerHtml autoescape="strict" kind="html"} +{template .DeleteReviewerHtml kind="html"} <p> {$fromName}{sp} <strong>
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/DeleteVote.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/DeleteVote.soy index b249ded..724e90d 100644 --- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/DeleteVote.soy +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/DeleteVote.soy
@@ -23,7 +23,7 @@ * @param coverLetter * @param fromName */ -{template .DeleteVote autoescape="strict" kind="text"} +{template .DeleteVote kind="text"} {$fromName} has removed a vote on this change.{\n} {\n} Change subject: {$change.subject}{\n} @@ -34,4 +34,4 @@ {$coverLetter} {\n} {/if} -{/template} \ No newline at end of file +{/template}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/DeleteVoteHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/DeleteVoteHtml.soy index 3d76ae2..06f5456 100644 --- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/DeleteVoteHtml.soy +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/DeleteVoteHtml.soy
@@ -21,7 +21,7 @@ * @param email * @param fromName */ -{template .DeleteVoteHtml autoescape="strict" kind="html"} +{template .DeleteVoteHtml kind="html"} <p> {$fromName} <strong>removed a vote</strong> from this change. </p>
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Footer.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Footer.soy index 24db2fd..2b146ec 100644 --- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Footer.soy +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Footer.soy
@@ -22,7 +22,7 @@ * CommentFooter. * @param footers */ -{template .Footer autoescape="strict" kind="text"} +{template .Footer kind="text"} {foreach $footer in $footers} {$footer}{\n} {/foreach}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/FooterHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/FooterHtml.soy index 9f9c503..d9f13ce 100644 --- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/FooterHtml.soy +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/FooterHtml.soy
@@ -19,7 +19,7 @@ /** * @param footers */ -{template .FooterHtml autoescape="strict" kind="html"} +{template .FooterHtml kind="html"} {\n} {\n} {foreach $footer in $footers}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/HeaderHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/HeaderHtml.soy index fdc3fee..85b56ec 100644 --- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/HeaderHtml.soy +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/HeaderHtml.soy
@@ -16,5 +16,5 @@ {namespace com.google.gerrit.server.mail.template} -{template .HeaderHtml autoescape="strict" kind="html"} +{template .HeaderHtml kind="html"} {/template}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Merged.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Merged.soy index d483264..40924e6 100644 --- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Merged.soy +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Merged.soy
@@ -24,7 +24,7 @@ * @param email * @param fromName */ -{template .Merged autoescape="strict" kind="text"} +{template .Merged kind="text"} {$fromName} has submitted this change and it was merged. {if $email.changeUrl} ( {$email.changeUrl} ){/if}{\n} {\n} @@ -39,4 +39,4 @@ {$email.unifiedDiff} {\n} {/if} -{/template} \ No newline at end of file +{/template}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/MergedHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/MergedHtml.soy index 927601b..08d37cc 100644 --- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/MergedHtml.soy +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/MergedHtml.soy
@@ -21,7 +21,7 @@ * @param email * @param fromName */ -{template .MergedHtml autoescape="strict" kind="html"} +{template .MergedHtml kind="html"} <p> {$fromName} <strong>merged</strong> this change. </p>
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/NewChange.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/NewChange.soy index 9f7429f..ca24d19 100644 --- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/NewChange.soy +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/NewChange.soy
@@ -25,7 +25,7 @@ * @param patchSet * @param projectName */ -{template .NewChange autoescape="strict" kind="text"} +{template .NewChange kind="text"} {if $email.reviewerNames} Hello{sp} {foreach $reviewerName in $email.reviewerNames}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/NewChangeHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/NewChangeHtml.soy index 8026666..676f019 100644 --- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/NewChangeHtml.soy +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/NewChangeHtml.soy
@@ -24,7 +24,7 @@ * @param patchSet * @param projectName */ -{template .NewChangeHtml autoescape="strict" kind="html"} +{template .NewChangeHtml kind="html"} <p> {if $email.reviewerNames} {$fromName} would like{sp}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Private.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Private.soy index b26535b..c1ac5b6 100644 --- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Private.soy +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Private.soy
@@ -24,7 +24,7 @@ * Private template to generate "View Change" buttons. * @param email */ -{template .ViewChangeButton autoescape="strict" kind="html"} +{template .ViewChangeButton kind="html"} <a href="{$email.changeUrl}">View Change</a> {/template} @@ -32,7 +32,7 @@ * Private template to render PRE block with consistent font-sizing. * @param content */ -{template .Pre autoescape="strict" kind="html"} +{template .Pre kind="html"} {let $preStyle kind="css"} font-family: monospace,monospace; // Use this to avoid browsers scaling down // monospace text. @@ -56,7 +56,7 @@ * * @param content */ -{template .WikiFormat autoescape="strict" kind="html"} +{template .WikiFormat kind="html"} {let $blockquoteStyle kind="css"} border-left: 1px solid #aaa; margin: 10px 0; @@ -90,7 +90,7 @@ /** * @param diffLines */ -{template .UnifiedDiff autoescape="strict" kind="html"} +{template .UnifiedDiff kind="html"} {let $addStyle kind="css"} color: hsl(120, 100%, 40%); {/let}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/RegisterNewEmail.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/RegisterNewEmail.soy index 2b30ae6..2886cc0 100644 --- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/RegisterNewEmail.soy +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/RegisterNewEmail.soy
@@ -21,7 +21,7 @@ * related to registering new email accounts. * @param email */ -{template .RegisterNewEmail autoescape="strict" kind="text"} +{template .RegisterNewEmail kind="text"} Welcome to Gerrit Code Review at {$email.gerritHost}.{\n} {\n}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ReplacePatchSet.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ReplacePatchSet.soy index e41bdda..124cdf3 100644 --- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ReplacePatchSet.soy +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ReplacePatchSet.soy
@@ -26,7 +26,7 @@ * @param patchSet * @param projectName */ -{template .ReplacePatchSet autoescape="strict" kind="text"} +{template .ReplacePatchSet kind="text"} {if $email.reviewerNames and $fromEmail == $change.ownerEmail} Hello{sp} {foreach $reviewerName in $email.reviewerNames} @@ -60,4 +60,4 @@ {$patchSet.refName} {\n} {/if} -{/template} \ No newline at end of file +{/template}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ReplacePatchSetHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ReplacePatchSetHtml.soy index 05c60a1..221a4e6 100644 --- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ReplacePatchSetHtml.soy +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ReplacePatchSetHtml.soy
@@ -24,7 +24,7 @@ * @param patchSet * @param projectName */ -{template .ReplacePatchSetHtml autoescape="strict" kind="html"} +{template .ReplacePatchSetHtml kind="html"} <p> {$fromName} <strong>uploaded patch set #{$patchSet.patchSetId}</strong>{sp} to{sp}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Restored.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Restored.soy index 14ae0f3..4fc6d8c 100644 --- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Restored.soy +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Restored.soy
@@ -24,7 +24,7 @@ * @param email * @param fromName */ -{template .Restored autoescape="strict" kind="text"} +{template .Restored kind="text"} {$fromName} has restored this change. {if $email.changeUrl} ( {$email.changeUrl} ){/if}{\n} {\n} @@ -36,4 +36,4 @@ {$coverLetter} {\n} {/if} -{/template} \ No newline at end of file +{/template}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/RestoredHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/RestoredHtml.soy index ea4f615..fdc68b0 100644 --- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/RestoredHtml.soy +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/RestoredHtml.soy
@@ -20,7 +20,7 @@ * @param email * @param fromName */ -{template .RestoredHtml autoescape="strict" kind="html"} +{template .RestoredHtml kind="html"} <p> {$fromName} <strong>restored</strong> this change. </p>
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Reverted.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Reverted.soy index 7f74df9..09e32ff 100644 --- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Reverted.soy +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Reverted.soy
@@ -24,7 +24,7 @@ * @param email * @param fromName */ -{template .Reverted autoescape="strict" kind="text"} +{template .Reverted kind="text"} {$fromName} has reverted this change. {if $email.changeUrl} ( {$email.changeUrl} ){/if}{\n} {\n}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/RevertedHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/RevertedHtml.soy index d6407e7..479eae1 100644 --- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/RevertedHtml.soy +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/RevertedHtml.soy
@@ -20,7 +20,7 @@ * @param email * @param fromName */ -{template .RevertedHtml autoescape="strict" kind="html"} +{template .RevertedHtml kind="html"} <p> {$fromName} <strong>reverted</strong> this change. </p>
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/SetAssignee.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/SetAssignee.soy index ca4f267..98290e9 100644 --- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/SetAssignee.soy +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/SetAssignee.soy
@@ -25,7 +25,7 @@ * @param patchSet * @param projectName */ -{template .SetAssignee autoescape="strict" kind="text"} +{template .SetAssignee kind="text"} Hello{sp} {$email.assigneeName},
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/SetAssigneeHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/SetAssigneeHtml.soy index 31cfbd6..d057ba3 100644 --- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/SetAssigneeHtml.soy +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/SetAssigneeHtml.soy
@@ -23,7 +23,7 @@ * @param patchSet * @param projectName */ -{template .SetAssigneeHtml autoescape="strict" kind="html"} +{template .SetAssigneeHtml kind="html"} <p> {$fromName} has <strong>assigned</strong> a change to{sp} {$email.assigneeName}.{sp}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/config/ScheduleConfigTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/config/ScheduleConfigTest.java index e6f36b9..0423a53 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/config/ScheduleConfigTest.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/config/ScheduleConfigTest.java
@@ -20,15 +20,19 @@ import static java.util.concurrent.TimeUnit.MINUTES; import static org.junit.Assert.assertEquals; +import java.time.LocalDateTime; +import java.time.Month; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.util.concurrent.TimeUnit; import org.eclipse.jgit.lib.Config; -import org.joda.time.DateTime; import org.junit.Test; public class ScheduleConfigTest { // Friday June 13, 2014 10:00 UTC - private static final DateTime NOW = DateTime.parse("2014-06-13T10:00:00-00:00"); + private static final ZonedDateTime NOW = + LocalDateTime.of(2014, Month.JUNE, 13, 10, 0, 0).atOffset(ZoneOffset.UTC).toZonedDateTime(); @Test public void initialDelay() throws Exception {
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/AbstractParserTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/AbstractParserTest.java index 19ad8bb..7309437 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/AbstractParserTest.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/AbstractParserTest.java
@@ -20,9 +20,9 @@ import com.google.gerrit.reviewdb.client.Comment; import com.google.gerrit.server.mail.Address; import java.sql.Timestamp; +import java.time.Instant; import java.util.ArrayList; import java.util.List; -import org.joda.time.DateTime; import org.junit.Ignore; @Ignore @@ -85,7 +85,7 @@ MailMessage.Builder b = MailMessage.builder(); b.id("id"); b.from(new Address("Foo Bar", "foo@bar.com")); - b.dateReceived(new DateTime()); + b.dateReceived(Instant.now()); b.subject(""); return b; }
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/MetadataParserTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/MetadataParserTest.java index 84bae96..dc25939 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/MetadataParserTest.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/MetadataParserTest.java
@@ -20,8 +20,10 @@ import com.google.gerrit.server.mail.Address; import com.google.gerrit.server.mail.MetadataName; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.Month; +import java.time.ZoneOffset; import org.junit.Test; public class MetadataParserTest { @@ -31,7 +33,7 @@ // email headers of the message. MailMessage.Builder b = MailMessage.builder(); b.id(""); - b.dateReceived(new DateTime()); + b.dateReceived(Instant.now()); b.subject(""); b.addAdditionalHeader(toHeaderWithDelimiter(MetadataName.CHANGE_NUMBER) + "123"); @@ -48,8 +50,11 @@ assertThat(meta.changeNumber).isEqualTo(123); assertThat(meta.patchSet).isEqualTo(1); assertThat(meta.messageType).isEqualTo("comment"); - assertThat(meta.timestamp.getTime()) - .isEqualTo(new DateTime(2016, 10, 25, 9, 11, 35, 0, DateTimeZone.UTC).getMillis()); + assertThat(meta.timestamp.toInstant()) + .isEqualTo( + LocalDateTime.of(2016, Month.OCTOBER, 25, 9, 11, 35) + .atOffset(ZoneOffset.UTC) + .toInstant()); } @Test @@ -58,7 +63,7 @@ // the text body of the message. MailMessage.Builder b = MailMessage.builder(); b.id(""); - b.dateReceived(new DateTime()); + b.dateReceived(Instant.now()); b.subject(""); StringBuilder stringBuilder = new StringBuilder(); @@ -77,8 +82,11 @@ assertThat(meta.changeNumber).isEqualTo(123); assertThat(meta.patchSet).isEqualTo(1); assertThat(meta.messageType).isEqualTo("comment"); - assertThat(meta.timestamp.getTime()) - .isEqualTo(new DateTime(2016, 10, 25, 9, 11, 35, 0, DateTimeZone.UTC).getMillis()); + assertThat(meta.timestamp.toInstant()) + .isEqualTo( + LocalDateTime.of(2016, Month.OCTOBER, 25, 9, 11, 35) + .atOffset(ZoneOffset.UTC) + .toInstant()); } @Test @@ -87,7 +95,7 @@ // the HTML body of the message. MailMessage.Builder b = MailMessage.builder(); b.id(""); - b.dateReceived(new DateTime()); + b.dateReceived(Instant.now()); b.subject(""); StringBuilder stringBuilder = new StringBuilder(); @@ -111,7 +119,10 @@ assertThat(meta.changeNumber).isEqualTo(123); assertThat(meta.patchSet).isEqualTo(1); assertThat(meta.messageType).isEqualTo("comment"); - assertThat(meta.timestamp.getTime()) - .isEqualTo(new DateTime(2016, 10, 25, 9, 11, 35, 0, DateTimeZone.UTC).getMillis()); + assertThat(meta.timestamp.toInstant()) + .isEqualTo( + LocalDateTime.of(2016, Month.OCTOBER, 25, 9, 11, 35) + .atOffset(ZoneOffset.UTC) + .toInstant()); } }
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/RawMailParserTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/RawMailParserTest.java index 4efa817..001d12d 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/RawMailParserTest.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/RawMailParserTest.java
@@ -65,7 +65,7 @@ assertThat(have.to()).isEqualTo(want.to()); assertThat(have.from()).isEqualTo(want.from()); assertThat(have.cc()).isEqualTo(want.cc()); - assertThat(have.dateReceived().getMillis()).isEqualTo(want.dateReceived().getMillis()); + assertThat(have.dateReceived()).isEqualTo(want.dateReceived()); assertThat(have.additionalHeaders()).isEqualTo(want.additionalHeaders()); assertThat(have.subject()).isEqualTo(want.subject()); assertThat(have.textContent()).isEqualTo(want.textContent());
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/data/AttachmentMessage.java b/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/data/AttachmentMessage.java index be8d882..eb4d180 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/data/AttachmentMessage.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/data/AttachmentMessage.java
@@ -16,8 +16,9 @@ import com.google.gerrit.server.mail.Address; import com.google.gerrit.server.mail.receive.MailMessage; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; +import java.time.LocalDateTime; +import java.time.Month; +import java.time.ZoneOffset; import org.junit.Ignore; /** @@ -82,7 +83,10 @@ .htmlContent("<div dir=\"ltr\">Contains unwanted attachment</div>") .subject("Test Subject") .addAdditionalHeader("MIME-Version: 1.0") - .dateReceived(new DateTime(2016, 10, 25, 9, 11, 35, 0, DateTimeZone.UTC)); + .dateReceived( + LocalDateTime.of(2016, Month.OCTOBER, 25, 9, 11, 35) + .atOffset(ZoneOffset.UTC) + .toInstant()); return expect.build(); } }
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/data/Base64HeaderMessage.java b/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/data/Base64HeaderMessage.java index affa3bd..91dc6f1 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/data/Base64HeaderMessage.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/data/Base64HeaderMessage.java
@@ -16,8 +16,9 @@ import com.google.gerrit.server.mail.Address; import com.google.gerrit.server.mail.receive.MailMessage; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; +import java.time.LocalDateTime; +import java.time.Month; +import java.time.ZoneOffset; import org.junit.Ignore; /** Tests parsing a Base64 encoded subject. */ @@ -58,7 +59,10 @@ .addTo(new Address("ekempin", "ekempin@google.com")) .textContent(textContent) .subject("\uD83D\uDE1B test") - .dateReceived(new DateTime(2016, 10, 25, 9, 11, 35, 0, DateTimeZone.UTC)); + .dateReceived( + LocalDateTime.of(2016, Month.OCTOBER, 25, 9, 11, 35) + .atOffset(ZoneOffset.UTC) + .toInstant()); return expect.build(); } }
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/data/HtmlMimeMessage.java b/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/data/HtmlMimeMessage.java index 487e9dd..756581f 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/data/HtmlMimeMessage.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/data/HtmlMimeMessage.java
@@ -16,8 +16,9 @@ import com.google.gerrit.server.mail.Address; import com.google.gerrit.server.mail.receive.MailMessage; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; +import java.time.LocalDateTime; +import java.time.Month; +import java.time.ZoneOffset; import org.junit.Ignore; /** Tests a message containing mime/alternative (text + html) content. */ @@ -98,7 +99,10 @@ .htmlContent(unencodedHtmlContent) .subject("Change in gerrit[master]: Implement receiver class structure and bindings") .addAdditionalHeader("MIME-Version: 1.0") - .dateReceived(new DateTime(2016, 10, 25, 9, 11, 35, 0, DateTimeZone.UTC)); + .dateReceived( + LocalDateTime.of(2016, Month.OCTOBER, 25, 9, 11, 35) + .atOffset(ZoneOffset.UTC) + .toInstant()); return expect.build(); } }
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/data/NonUTF8Message.java b/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/data/NonUTF8Message.java index 9f2af0d..3fafd4b 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/data/NonUTF8Message.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/data/NonUTF8Message.java
@@ -15,8 +15,9 @@ import com.google.gerrit.server.mail.Address; import com.google.gerrit.server.mail.receive.MailMessage; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; +import java.time.LocalDateTime; +import java.time.Month; +import java.time.ZoneOffset; import org.junit.Ignore; /** Tests that non-UTF8 encodings are handled correctly. */ @@ -62,7 +63,10 @@ .addTo(new Address("ekempin", "ekempin@google.com")) .textContent(textContent) .subject("\uD83D\uDE1B test") - .dateReceived(new DateTime(2016, 10, 25, 9, 11, 35, 0, DateTimeZone.UTC)); + .dateReceived( + LocalDateTime.of(2016, Month.OCTOBER, 25, 9, 11, 35) + .atOffset(ZoneOffset.UTC) + .toInstant()); return expect.build(); } }
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/data/QuotedPrintableHeaderMessage.java b/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/data/QuotedPrintableHeaderMessage.java index 2c17859..2dc48b5 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/data/QuotedPrintableHeaderMessage.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/data/QuotedPrintableHeaderMessage.java
@@ -16,8 +16,9 @@ import com.google.gerrit.server.mail.Address; import com.google.gerrit.server.mail.receive.MailMessage; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; +import java.time.LocalDateTime; +import java.time.Month; +import java.time.ZoneOffset; import org.junit.Ignore; /** Tests parsing a quoted printable encoded subject */ @@ -59,7 +60,10 @@ .addTo(new Address("ekempin", "ekempin@google.com")) .textContent(textContent) .subject("âme vulgaire") - .dateReceived(new DateTime(2016, 10, 25, 9, 11, 35, 0, DateTimeZone.UTC)); + .dateReceived( + LocalDateTime.of(2016, Month.OCTOBER, 25, 9, 11, 35) + .atOffset(ZoneOffset.UTC) + .toInstant()); return expect.build(); } }
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/data/SimpleTextMessage.java b/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/data/SimpleTextMessage.java index ce833d5..aa5b78a 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/data/SimpleTextMessage.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/mail/receive/data/SimpleTextMessage.java
@@ -16,8 +16,9 @@ import com.google.gerrit.server.mail.Address; import com.google.gerrit.server.mail.receive.MailMessage; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; +import java.time.LocalDateTime; +import java.time.Month; +import java.time.ZoneOffset; import org.junit.Ignore; /** Tests parsing a simple text message with different headers. */ @@ -124,7 +125,10 @@ .addCc(new Address("Patrick Hiesel", "hiesel@google.com")) .textContent(textContent) .subject("Change in gerrit[master]: (Re)enable voting buttons for merged changes") - .dateReceived(new DateTime(2016, 10, 25, 9, 11, 35, 0, DateTimeZone.UTC)) + .dateReceived( + LocalDateTime.of(2016, Month.OCTOBER, 25, 9, 11, 35) + .atOffset(ZoneOffset.UTC) + .toInstant()) .addAdditionalHeader( "Authentication-Results: mx.google.com; dkim=pass header.i=@google.com;") .addAdditionalHeader(
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/notedb/ChangeBundleTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/notedb/ChangeBundleTest.java index 90e6800..33e1005 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/notedb/ChangeBundleTest.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/notedb/ChangeBundleTest.java
@@ -46,13 +46,14 @@ import com.google.gwtorm.protobuf.CodecFactory; import com.google.gwtorm.protobuf.ProtobufCodec; import java.sql.Timestamp; +import java.time.LocalDate; +import java.time.Month; +import java.time.ZoneId; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.TimeZone; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -67,6 +68,7 @@ CodecFactory.encoder(PatchSetApproval.class); private static final ProtobufCodec<PatchLineComment> PATCH_LINE_COMMENT_CODEC = CodecFactory.encoder(PatchLineComment.class); + private static final String TIMEZONE_ID = "US/Eastern"; private String systemTimeZoneProperty; private TimeZone systemTimeZone; @@ -76,10 +78,9 @@ @Before public void setUp() { - String tz = "US/Eastern"; - systemTimeZoneProperty = System.setProperty("user.timezone", tz); + systemTimeZoneProperty = System.setProperty("user.timezone", TIMEZONE_ID); systemTimeZone = TimeZone.getDefault(); - TimeZone.setDefault(TimeZone.getTimeZone(tz)); + TimeZone.setDefault(TimeZone.getTimeZone(TIMEZONE_ID)); long maxMs = ChangeRebuilderImpl.MAX_WINDOW_MS; assertThat(maxMs).isGreaterThan(1000L); TestTimeUtil.resetWithClockStep(maxMs * 2, MILLISECONDS); @@ -1517,8 +1518,11 @@ PatchSetApproval a2 = clone(a1); a2.setGranted( new Timestamp( - new DateTime(1900, 1, 1, 0, 0, 0, DateTimeZone.forTimeZone(TimeZone.getDefault())) - .getMillis())); + LocalDate.of(1900, Month.JANUARY, 1) + .atStartOfDay() + .atZone(ZoneId.of(TIMEZONE_ID)) + .toInstant() + .toEpochMilli())); // Both are ReviewDb, exact match is required. ChangeBundle b1 =
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java index 1e722fc..7234acc 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
@@ -1232,7 +1232,7 @@ long thirtyHoursInMs = MILLISECONDS.convert(30, HOURS); resetTimeWithClockStep(thirtyHoursInMs, MILLISECONDS); TestRepository<Repo> repo = createProject("repo"); - long startMs = TestTimeUtil.START.getMillis(); + long startMs = TestTimeUtil.START.toEpochMilli(); Change change1 = insert(repo, newChange(repo), null, new Timestamp(startMs)); Change change2 = insert(repo, newChange(repo), null, new Timestamp(startMs + thirtyHoursInMs)); @@ -1259,7 +1259,7 @@ long thirtyHoursInMs = MILLISECONDS.convert(30, HOURS); resetTimeWithClockStep(thirtyHoursInMs, MILLISECONDS); TestRepository<Repo> repo = createProject("repo"); - long startMs = TestTimeUtil.START.getMillis(); + long startMs = TestTimeUtil.START.toEpochMilli(); Change change1 = insert(repo, newChange(repo), null, new Timestamp(startMs)); Change change2 = insert(repo, newChange(repo), null, new Timestamp(startMs + thirtyHoursInMs)); TestTimeUtil.setClockStep(0, MILLISECONDS); @@ -1281,7 +1281,7 @@ long thirtyHoursInMs = MILLISECONDS.convert(30, HOURS); resetTimeWithClockStep(thirtyHoursInMs, MILLISECONDS); TestRepository<Repo> repo = createProject("repo"); - long startMs = TestTimeUtil.START.getMillis(); + long startMs = TestTimeUtil.START.toEpochMilli(); Change change1 = insert(repo, newChange(repo), null, new Timestamp(startMs)); Change change2 = insert(repo, newChange(repo), null, new Timestamp(startMs + thirtyHoursInMs)); TestTimeUtil.setClockStep(0, MILLISECONDS);
diff --git a/gerrit-server/src/test/java/com/google/gerrit/testutil/TestTimeUtil.java b/gerrit-server/src/test/java/com/google/gerrit/testutil/TestTimeUtil.java index dd44cb9ae..5bbe3b6 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/testutil/TestTimeUtil.java +++ b/gerrit-server/src/test/java/com/google/gerrit/testutil/TestTimeUtil.java
@@ -17,18 +17,21 @@ import static com.google.common.base.Preconditions.checkState; import static java.util.concurrent.TimeUnit.MILLISECONDS; +import com.google.gerrit.common.TimeUtil; import java.sql.Timestamp; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.Month; +import java.time.ZoneOffset; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; -import org.joda.time.DateTime; -import org.joda.time.DateTimeUtils; -import org.joda.time.DateTimeUtils.MillisProvider; -import org.joda.time.DateTimeZone; /** Static utility methods for dealing with dates and times in tests. */ public class TestTimeUtil { - public static final DateTime START = - new DateTime(2009, 9, 30, 17, 0, 0, DateTimeZone.forOffsetHours(-4)); + public static final Instant START = + LocalDateTime.of(2009, Month.SEPTEMBER, 30, 17, 0, 0) + .atOffset(ZoneOffset.ofHours(-4)) + .toInstant(); private static Long clockStepMs; private static AtomicLong clockMs; @@ -43,7 +46,7 @@ */ public static synchronized void resetWithClockStep(long clockStep, TimeUnit clockStepUnit) { // Set an arbitrary start point so tests are more repeatable. - clockMs = new AtomicLong(START.getMillis()); + clockMs = new AtomicLong(START.toEpochMilli()); setClockStep(clockStep, clockStepUnit); } @@ -56,13 +59,7 @@ public static synchronized void setClockStep(long clockStep, TimeUnit clockStepUnit) { checkState(clockMs != null, "call resetWithClockStep first"); clockStepMs = MILLISECONDS.convert(clockStep, clockStepUnit); - DateTimeUtils.setCurrentMillisProvider( - new MillisProvider() { - @Override - public long getMillis() { - return clockMs.getAndAdd(clockStepMs); - } - }); + TimeUtil.setCurrentMillisSupplier(() -> clockMs.getAndAdd(clockStepMs)); } /** @@ -89,7 +86,7 @@ /** Reset the clock to use the actual system clock. */ public static synchronized void useSystemTime() { clockMs = null; - DateTimeUtils.setCurrentMillisSystem(); + TimeUtil.resetCurrentMillisSupplier(); } private TestTimeUtil() {}
diff --git a/gerrit-war/pom.xml b/gerrit-war/pom.xml index 2dd6ba6..c43c098 100644 --- a/gerrit-war/pom.xml +++ b/gerrit-war/pom.xml
@@ -2,7 +2,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>com.google.gerrit</groupId> <artifactId>gerrit-war</artifactId> - <version>2.15-rc0</version> + <version>2.16-SNAPSHOT</version> <packaging>war</packaging> <name>Gerrit Code Review - WAR</name> <description>Gerrit WAR</description>
diff --git a/lib/elasticsearch/BUILD b/lib/elasticsearch/BUILD index c40925e..18c62af 100644 --- a/lib/elasticsearch/BUILD +++ b/lib/elasticsearch/BUILD
@@ -8,13 +8,13 @@ ":compress-lzf", ":hppc", ":jna", + ":joda-time", ":jsr166e", ":netty", ":t-digest", "//lib/jackson:jackson-core", "//lib/jackson:jackson-dataformat-cbor", "//lib/jackson:jackson-dataformat-smile", - "//lib/joda:joda-time", "//lib/lucene:lucene-codecs", "//lib/lucene:lucene-highlighter", "//lib/lucene:lucene-join", @@ -48,6 +48,19 @@ ) java_library( + name = "joda-time", + data = ["//lib:LICENSE-Apache2.0"], + exports = ["@joda_time//jar"], + runtime_deps = ["joda-convert"], +) + +java_library( + name = "joda-convert", + data = ["//lib:LICENSE-Apache2.0"], + exports = ["@joda_convert//jar"], +) + +java_library( name = "compress-lzf", data = ["//lib:LICENSE-Apache2.0"], visibility = ["//lib/elasticsearch:__pkg__"],
diff --git a/lib/joda/BUILD b/lib/joda/BUILD deleted file mode 100644 index e1a1924..0000000 --- a/lib/joda/BUILD +++ /dev/null
@@ -1,13 +0,0 @@ -java_library( - name = "joda-time", - data = ["//lib:LICENSE-Apache2.0"], - visibility = ["//visibility:public"], - exports = ["@joda_time//jar"], - runtime_deps = ["joda-convert"], -) - -java_library( - name = "joda-convert", - data = ["//lib:LICENSE-Apache2.0"], - exports = ["@joda_convert//jar"], -)
diff --git a/polygerrit-ui/app/elements/admin/gr-project-command/gr-project-command.html b/polygerrit-ui/app/elements/admin/gr-project-command/gr-project-command.html new file mode 100644 index 0000000..6bf2211 --- /dev/null +++ b/polygerrit-ui/app/elements/admin/gr-project-command/gr-project-command.html
@@ -0,0 +1,32 @@ +<!-- +Copyright (C) 2017 The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> +<link rel="import" href="../../../bower_components/polymer/polymer.html"> +<link rel="import" href="../../../styles/shared-styles.html"> +<link rel="import" href="../../shared/gr-button/gr-button.html"> + +<dom-module id="gr-project-command"> + <template> + <style include="shared-styles"> + :host { + display: block; + margin-bottom: 2em; + } + </style> + <h3>[[title]]</h3> + <gr-button on-tap="_onCommandTap">[[title]]</gr-button> + </template> + <script src="gr-project-command.js"></script> +</dom-module>
diff --git a/polygerrit-ui/app/elements/admin/gr-project-command/gr-project-command.js b/polygerrit-ui/app/elements/admin/gr-project-command/gr-project-command.js new file mode 100644 index 0000000..48789b0 --- /dev/null +++ b/polygerrit-ui/app/elements/admin/gr-project-command/gr-project-command.js
@@ -0,0 +1,34 @@ +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +(function() { + 'use strict'; + + Polymer({ + is: 'gr-project-command', + + properties: { + title: String, + }, + + /** + * Fired when command button is tapped. + * + * @event command-tap + */ + + _onCommandTap() { + this.dispatchEvent(new CustomEvent('command-tap', {bubbles: true})); + }, + }); +})();
diff --git a/polygerrit-ui/app/elements/admin/gr-project-command/gr-project-command_test.html b/polygerrit-ui/app/elements/admin/gr-project-command/gr-project-command_test.html new file mode 100644 index 0000000..8fae4f8 --- /dev/null +++ b/polygerrit-ui/app/elements/admin/gr-project-command/gr-project-command_test.html
@@ -0,0 +1,50 @@ +<!DOCTYPE html> +<!-- +Copyright (C) 2017 The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> + +<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes"> +<title>gr-project-command</title> + +<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script> +<script src="../../../bower_components/web-component-tester/browser.js"></script> +<link rel="import" href="../../../test/common-test-setup.html"/> +<link rel="import" href="gr-project-command.html"> + +<script>void(0);</script> + +<test-fixture id="basic"> + <template> + <gr-project-command></gr-project-command> + </template> +</test-fixture> + +<script> + suite('gr-project-command tests', () => { + let element; + + setup(() => { + element = fixture('basic'); + }); + + test('dispatched command-tap on button tap', done => { + element.addEventListener('command-tap', () => { + done(); + }); + MockInteractions.tap( + Polymer.dom(element.root).querySelector('gr-button')); + }); + }); +</script>
diff --git a/polygerrit-ui/app/elements/admin/gr-project-commands/gr-project-commands.html b/polygerrit-ui/app/elements/admin/gr-project-commands/gr-project-commands.html index 6c0908a..f43403a 100644 --- a/polygerrit-ui/app/elements/admin/gr-project-commands/gr-project-commands.html +++ b/polygerrit-ui/app/elements/admin/gr-project-commands/gr-project-commands.html
@@ -19,10 +19,12 @@ <link rel="import" href="../../../bower_components/iron-input/iron-input.html"> <link rel="import" href="../../../styles/gr-form-styles.html"> <link rel="import" href="../../../styles/shared-styles.html"> +<link rel="import" href="../../plugins/gr-endpoint-decorator/gr-endpoint-decorator.html"> <link rel="import" href="../../shared/gr-confirm-dialog/gr-confirm-dialog.html"> <link rel="import" href="../../shared/gr-overlay/gr-overlay.html"> <link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html"> <link rel="import" href="../gr-create-change-dialog/gr-create-change-dialog.html"> +<link rel="import" href="../gr-project-command/gr-project-command.html"> <dom-module id="gr-project-commands"> <template> @@ -47,24 +49,23 @@ <div id="loadedContent" class$="[[_computeLoadingClass(_loading)]]"> <h2 id="options">Command</h2> <div id="form"> - <fieldset> - <h3 id="createChange">Create Change</h3> - <fieldset> - <gr-button id="createNewChange" on-tap="_createNewChange"> - Create Change - </gr-button> - </fieldset> - <h3 id="runGC" hidden$="[[!_projectConfig.actions.gc.enabled]]"> - Run GC - </h3> - <fieldset> - <gr-button - on-tap="_handleRunningGC" - hidden$="[[!_projectConfig.actions.gc.enabled]]"> - Run GC - </gr-button> - </fieldset> - </fieldset> + <gr-project-command + title="Create Change" + on-command-tap="_createNewChange"> + </gr-project-command> + + <gr-project-command + title="Run GC" + hidden$="[[!_projectConfig.actions.gc.enabled]]" + on-command-tap="_handleRunningGC"> + </gr-project-command> + + <gr-endpoint-decorator name="project-command"> + <gr-endpoint-param name="config" value="[[_projectConfig]]"> + </gr-endpoint-param> + <gr-endpoint-param name="projectName" value="[[project]]"> + </gr-endpoint-param> + </gr-endpoint-decorator> </div> </div> </main>
diff --git a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js index 6c5bad3..750db92 100644 --- a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js +++ b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js
@@ -73,7 +73,7 @@ }, observers: [ - '_userChanged(params.user)', + '_paramsChanged(params.*)', ], behaviors: [ @@ -95,20 +95,28 @@ return 'Dashboard for ' + user; }, - /** - * Allows a refresh if menu item is selected again. - */ - _userChanged(user) { - if (!user) { return; } + _paramsChanged(paramsChangeRecord) { + const params = paramsChangeRecord.base; + + if (!params.user && !params.sections) { + return; + } + + const user = params.user || 'self'; + const sections = (params.sections || DEFAULT_SECTIONS).filter( + section => (user === 'self' || !section.selfOnly)); + const title = params.title || this._computeTitle(user); // NOTE: This method may be called before attachment. Fire title-change // in an async so that attachment to the DOM can take place first. - this.async( - () => this.fire('title-change', {title: this._computeTitle(user)})); + this.async(() => this.fire('title-change', {title})); + + // Return if params indicate no longer in view. + if (!user && sections === DEFAULT_SECTIONS) { + return; + } this._loading = true; - const sections = this._sectionMetadata.filter( - section => (user === 'self' || !section.selfOnly)); const queries = sections.map( section => this._dashboardQueryForSection(section, user));
diff --git a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.html b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.html index 2edf26f..40376ad 100644 --- a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.html +++ b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.html
@@ -64,17 +64,18 @@ }); test('viewing another user\'s dashboard omits selfOnly sections', () => { - element._sectionMetadata = [ - {query: '1'}, - {query: '2', selfOnly: true}, - ]; - - element.params = {user: 'self'}; + element.params = { + sections: [ + {query: '1'}, + {query: '2', selfOnly: true}, + ], + user: 'self', + }; flushAsynchronousOperations(); assert.isTrue( getChangesStub.calledWith(null, ['1', '2'], null, element.options)); - element.params = {user: 'user'}; + element.set('params.user', 'user'); flushAsynchronousOperations(); assert.isTrue( getChangesStub.calledWith(null, ['1'], null, element.options));
diff --git a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.html b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.html index e0dd5d5..57b3c10 100644 --- a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.html +++ b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.html
@@ -46,12 +46,11 @@ border-top: 1px solid #ddd; display: flex; min-height: 3.2em; - padding: .5em calc(var(--default-horizontal-margin) / 2); + padding: .5em var(--default-horizontal-margin); } .patchInfo-header-wrapper { align-items: center; display: flex; - margin: 0 .25em; width: 100%; } .patchInfo-left { @@ -141,7 +140,7 @@ <div class$="patchInfo-header [[_computeEditLoadedClass(editLoaded)]] [[_computePatchInfoClass(patchNum, allPatchSets)]]"> <div class="patchInfo-header-wrapper"> <div class="patchInfo-left"> - <span class="label">Files</span> + <h3 class="label">Files</h3> <gr-patch-range-select id="rangeSelect" comments="[[comments]]" @@ -190,7 +189,6 @@ </div> </div> <div class="fileList-header"> - <div>Files</div> <div class="rightControls"> <template is="dom-if" if="[[_fileListActionsVisible(shownFileCount, _maxFilesForBulkActions)]]">
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html index 1a164b6..f80ff0a 100644 --- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html +++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
@@ -23,6 +23,7 @@ <link rel="import" href="../../diff/gr-comment-api/gr-comment-api.html"> <link rel="import" href="../../diff/gr-diff/gr-diff.html"> <link rel="import" href="../../diff/gr-diff-cursor/gr-diff-cursor.html"> +<link rel="import" href="../../edit/gr-edit-file-controls/gr-edit-file-controls.html"> <link rel="import" href="../../shared/gr-button/gr-button.html"> <link rel="import" href="../../shared/gr-cursor-manager/gr-cursor-manager.html"> <link rel="import" href="../../shared/gr-linked-text/gr-linked-text.html"> @@ -42,7 +43,7 @@ border-top: 1px solid #ddd; display: flex; height: 2.25em; - padding: 0 calc(var(--default-horizontal-margin) / 2 + .25em); + padding: 0 var(--default-horizontal-margin); } :host(.loading) .row { opacity: .5; @@ -50,6 +51,12 @@ :host(.editLoaded) .hideOnEdit { display: none; } + .showOnEdit { + display: none; + } + :host(.editLoaded) .showOnEdit { + display: initial; + } .reviewed, .status { align-items: center; @@ -184,6 +191,10 @@ display: initial; opacity: 100; } + .editFileControls { + margin-left: 1em; + width: 4em; + } @media screen and (max-width: 50em) { .desktop { display: none; @@ -299,6 +310,12 @@ <span class="markReviewed" title="Mark as reviewed (shortcut: r)">[[_computeReviewedText(file.isReviewed)]]</span> </label> </div> + <div class="editFileControls showOnEdit"> + <gr-edit-file-controls + class$="[[_computeClass('', file.__path)]]" + file-path="[[file.__path]]" + on-edit-tap="_handleEditTap"></gr-edit-file-controls> + </div> </div> <template is="dom-if" if="[[_isFileExpanded(file.__path, _expandedFilePaths.*)]]"> @@ -338,6 +355,7 @@ </div> <!-- Empty div here exists to keep spacing in sync with file rows. --> <div class="reviewed hideOnEdit" hidden$="[[!_loggedIn]]" hidden></div> + <div class="editFileControls showOnEdit"></div> </div> <div class$="row totalChanges [[_computeExpandInlineClass(_userPrefs)]]"
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js index 68761fa..70f1d4d 100644 --- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js +++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
@@ -139,7 +139,7 @@ 'c': '_handleCKey', '[': '_handleLeftBracketKey', ']': '_handleRightBracketKey', - 'o enter': '_handleOKey', + 'o': '_handleOKey', 'n': '_handleNKey', 'p': '_handlePKey', 'r': '_handleRKey', @@ -147,6 +147,24 @@ 'esc': '_handleEscKey', }, + listeners: { + keydown: '_scopedKeydownHandler', + }, + + /** + * Iron-a11y-keys-behavior catches keyboard events globally. Some keyboard + * events must be scoped to a component level (e.g. `enter`) in order to not + * override native browser functionality. + * + * Context: Issue 7277 + */ + _scopedKeydownHandler(e) { + if (e.keyCode === 13) { + // Enter. + this._handleOKey(e); + } + }, + reload() { if (!this.changeNum || !this.patchRange.patchNum) { return Promise.resolve(); @@ -908,5 +926,10 @@ _computeReviewedText(isReviewed) { return isReviewed ? 'MARK UNREVIEWED' : 'MARK REVIEWED'; }, + + _handleEditTap(e) { + const url = Gerrit.Nav.getEditUrlForDiff(this.change, e.detail.path); + Gerrit.Nav.navigateToRelativeUrl(url); + }, }); })();
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html index b80a20f..b01c105 100644 --- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html +++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html
@@ -1238,6 +1238,20 @@ }); }); }); + + test('editing actions', () => { + element.editLoaded = true; + element.change = {_number: '42', project: 'test'}; + const navStub = sandbox.stub(Gerrit.Nav, 'navigateToRelativeUrl'); + const editControls = + Polymer.dom(element.root).querySelectorAll('.row:not(.header)') + .map(row => row.querySelector('gr-edit-file-controls')); + + // Commit message should not have edit controls. + assert.isTrue(editControls[0].classList.contains('invisible')); + MockInteractions.tap(editControls[1].$.edit); + assert.isTrue(navStub.called); + }); }); a11ySuite('basic'); </script>
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html index 4b02c3d..068cacc 100644 --- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html +++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html
@@ -56,6 +56,13 @@ padding: .5em .75em; width: 100%; } + .actions { + display: flex; + justify-content: space-between; + } + .actions gr-button { + margin-left: 1em; + } .peopleContainer, .labelsContainer { flex-shrink: 0; @@ -135,9 +142,6 @@ #savingLabel.saving { display: inline; } - #cancelButton { - float: right; - } @media screen and (max-width: 50em) { :host { max-height: none; @@ -259,33 +263,40 @@ Saving comments... </span> </section> - <section> - <gr-button - primary - disabled="[[_computeSendButtonDisabled(knownLatestState, _sendButtonLabel, diffDrafts, draft, _reviewersMutated, _labelsChanged, _includeComments)]]" - class="action send" - on-tap="_sendTapHandler">[[_sendButtonLabel]]</gr-button> - <template is="dom-if" if="[[canBeStarted]]"> + <section class="actions"> + <div class="left"> + <span + id="checkingStatusLabel" + hidden$="[[!_isState(knownLatestState, 'checking')]]"> + Checking whether patch [[patchNum]] is latest... + </span> + <span + id="notLatestLabel" + hidden$="[[!_isState(knownLatestState, 'not-latest')]]"> + Patch [[patchNum]] is not latest. + <gr-button link on-tap="_reload">Reload</gr-button> + </span> + </div> + <div class="right"> <gr-button - disabled="[[_isState(knownLatestState, 'not-latest')]]" - class="action save" - on-tap="_saveTapHandler">Save</gr-button> - </template> - <span - id="checkingStatusLabel" - hidden$="[[!_isState(knownLatestState, 'checking')]]"> - Checking whether patch [[patchNum]] is latest... - </span> - <span - id="notLatestLabel" - hidden$="[[!_isState(knownLatestState, 'not-latest')]]"> - Patch [[patchNum]] is not latest. - <gr-button link on-tap="_reload">Reload</gr-button> - </span> - <gr-button - id="cancelButton" - class="action cancel" - on-tap="_cancelTapHandler">Cancel</gr-button> + link + id="cancelButton" + class="action cancel" + on-tap="_cancelTapHandler">Cancel</gr-button> + <gr-button + link + primary + disabled="[[_computeSendButtonDisabled(knownLatestState, _sendButtonLabel, diffDrafts, draft, _reviewersMutated, _labelsChanged, _includeComments)]]" + class="action send" + on-tap="_sendTapHandler">[[_sendButtonLabel]]</gr-button> + <template is="dom-if" if="[[canBeStarted]]"> + <gr-button + link + disabled="[[_isState(knownLatestState, 'not-latest')]]" + class="action save" + on-tap="_saveTapHandler">Save</gr-button> + </template> + </div> </section> </div> <gr-js-api-interface id="jsAPI"></gr-js-api-interface>
diff --git a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html index b03f2e5..8453da9 100644 --- a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html +++ b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
@@ -57,6 +57,7 @@ console.warn('Use of uninitialized routing'); }; + const EDIT_PATCHNUM = 'edit'; const PARENT_PATCHNUM = 'PARENT'; window.Gerrit.Nav = { @@ -277,6 +278,7 @@ changeNum, project, path, + patchNum: EDIT_PATCHNUM, }); },
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router.js b/polygerrit-ui/app/elements/core/gr-router/gr-router.js index 5b9f4f6..708cb17 100644 --- a/polygerrit-ui/app/elements/core/gr-router/gr-router.js +++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
@@ -126,6 +126,16 @@ */ const LINE_ADDRESS_PATTERN = /^([ab]?)(\d+)$/; + /** + * Pattern to recognize '+' in url-encoded strings for replacement with ' '. + */ + const PLUS_PATTERN = /\+/g; + + /** + * Pattern to recognize leading '?' in window.location.search, for stripping. + */ + const QUESTION_PATTERN = /^\?*/; + // Polymer makes `app` intrinsically defined on the window by virtue of the // custom element having the id "app", but it is made explicit here. const app = document.querySelector('#app'); @@ -181,8 +191,9 @@ _generateUrl(params) { const base = this.getBaseUrl(); let url = ''; + const Views = Gerrit.Nav.View; - if (params.view === Gerrit.Nav.View.SEARCH) { + if (params.view === Views.SEARCH) { const operators = []; if (params.owner) { operators.push('owner:' + this.encodeURL(params.owner, false)); @@ -213,7 +224,7 @@ } } url = '/q/' + operators.join('+'); - } else if (params.view === Gerrit.Nav.View.CHANGE) { + } else if (params.view === Views.CHANGE) { let range = this._getPatchRangeExpression(params); if (range.length) { range = '/' + range; } if (params.project) { @@ -221,13 +232,30 @@ } else { url = `/c/${params.changeNum}${range}`; } - } else if (params.view === Gerrit.Nav.View.DASHBOARD) { - url = `/dashboard/${params.user || 'self'}`; - } else if (params.view === Gerrit.Nav.View.DIFF) { + } else if (params.view === Views.DASHBOARD) { + if (params.sections) { + // Custom dashboard. + const queryParams = params.sections.map(section => { + return encodeURIComponent(section.name) + '=' + + encodeURIComponent(section.query); + }); + if (params.title) { + queryParams.push('title=' + encodeURIComponent(params.title)); + } + const user = params.user ? params.user : ''; + url = `/dashboard/${user}?${queryParams.join('&')}`; + } else { + // User dashboard. + url = `/dashboard/${params.user || 'self'}`; + } + } else if (params.view === Views.DIFF || params.view === Views.EDIT) { let range = this._getPatchRangeExpression(params); if (range.length) { range = '/' + range; } let suffix = `${range}/${this.encodeURL(params.path, true)}`; + + if (params.view === Views.EDIT) { suffix += ',edit'; } + if (params.lineNum) { suffix += '#'; if (params.leftSide) { suffix += 'b'; } @@ -239,9 +267,6 @@ } else { url = `/c/${params.changeNum}${suffix}`; } - if (params.edit) { - url += ',edit'; - } } else { throw new Error('Can\'t generate'); } @@ -593,19 +618,101 @@ }); }, - _handleDashboardRoute(data) { - if (!data.params[0]) { - this._redirect('/dashboard/self'); - return; + /** + * Decode an application/x-www-form-urlencoded string. + * + * @param {string} qs The application/x-www-form-urlencoded string. + * @return {string} The decoded string. + */ + _decodeQueryString(qs) { + return decodeURIComponent(qs.replace(PLUS_PATTERN, ' ')); + }, + + /** + * Parse a query string (e.g. window.location.search) into an array of + * name/value pairs. + * + * @param {string} qs The application/x-www-form-urlencoded query string. + * @return {!Array<!Array<string>>} An array of name/value pairs, where each + * element is a 2-element array. + */ + _parseQueryString(qs) { + qs = qs.replace(QUESTION_PATTERN, ''); + if (!qs) { + return []; + } + const params = []; + qs.split('&').forEach(param => { + const idx = param.indexOf('='); + let name; + let value; + if (idx < 0) { + name = this._decodeQueryString(param); + value = ''; + } else { + name = this._decodeQueryString(param.substring(0, idx)); + value = this._decodeQueryString(param.substring(idx + 1)); + } + if (name) { + params.push([name, value]); + } + }); + return params; + }, + + /** + * Handle dashboard routes. These may be user, custom, or project + * dashboards. + * + * @param {!Object} data The parsed route data. + * @param {string=} opt_qs Optional query string associated with the route. + * If not given, window.location.search is used. (Used by tests). + */ + _handleDashboardRoute(data, opt_qs) { + // opt_qs may be provided by a test, and it may have a falsy value + const qs = opt_qs !== undefined ? opt_qs : window.location.search; + const queryParams = this._parseQueryString(qs); + let title = 'Custom Dashboard'; + const titleParam = queryParams.find( + elem => elem[0].toLowerCase() === 'title'); + if (titleParam) { + title = titleParam[1]; + } + const sectionParams = queryParams.filter( + elem => elem[0] && elem[1] && elem[0].toLowerCase() !== 'title'); + const sections = sectionParams.map(elem => { + return { + name: elem[0], + query: elem[1], + }; + }); + + if (sections.length > 0) { + // Custom dashboard view. + this._setParams({ + view: Gerrit.Nav.View.DASHBOARD, + user: data.params[0] || 'self', + sections, + title, + }); + return Promise.resolve(); } + if (!data.params[0] && sections.length === 0) { + // Redirect /dashboard/ -> /dashboard/self. + this._redirect('/dashboard/self'); + return Promise.resolve(); + } + + // User dashboard. We require viewing user to be logged in, else we + // redirect to login for self dashboard or simple owner search for + // other user dashboard. return this.$.restAPI.getLoggedIn().then(loggedIn => { if (!loggedIn) { if (data.params[0].toLowerCase() === 'self') { this._redirectToLogin(data.canonicalPath); } else { - // TODO: encode user or use _generateUrl. - this._redirect('/q/owner:' + data.params[0]); + this._redirect('/q/owner:' + encodeURIComponent(data.params[0])); } } else { this._setParams({
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router_test.html b/polygerrit-ui/app/elements/core/gr-router/gr-router_test.html index ca00e19..4aae65f 100644 --- a/polygerrit-ui/app/elements/core/gr-router/gr-router_test.html +++ b/polygerrit-ui/app/elements/core/gr-router/gr-router_test.html
@@ -280,6 +280,17 @@ '/c/test/+/42/2/file.cpp#b123'); }); + test('edit', () => { + const params = { + view: Gerrit.Nav.View.EDIT, + changeNum: '42', + project: 'test', + path: 'x+y/path.cpp', + }; + assert.equal(element._generateUrl(params), + '/c/test/+/42/x%252By/path.cpp,edit'); + }); + test('_getPatchRangeExpression', () => { const params = {}; let actual = element._getPatchRangeExpression(params); @@ -297,6 +308,48 @@ actual = element._getPatchRangeExpression(params); assert.equal(actual, '2..'); }); + + suite('dashboard', () => { + test('self dashboard', () => { + const params = { + view: Gerrit.Nav.View.DASHBOARD, + }; + assert.equal(element._generateUrl(params), '/dashboard/self'); + }); + + test('user dashboard', () => { + const params = { + view: Gerrit.Nav.View.DASHBOARD, + user: 'user', + }; + assert.equal(element._generateUrl(params), '/dashboard/user'); + }); + + test('custom self dashboard, no title', () => { + const params = { + view: Gerrit.Nav.View.DASHBOARD, + sections: [ + {name: 'section 1', query: 'query 1'}, + {name: 'section 2', query: 'query 2'}, + ], + }; + assert.equal( + element._generateUrl(params), + '/dashboard/?section%201=query%201§ion%202=query%202'); + }); + + test('custom user dashboard, with title', () => { + const params = { + view: Gerrit.Nav.View.DASHBOARD, + user: 'user', + sections: [{name: 'name', query: 'query'}], + title: 'custom dashboard', + }; + assert.equal( + element._generateUrl(params), + '/dashboard/user?name=query&title=custom%20dashboard'); + }); + }); }); suite('param normalization', () => { @@ -514,7 +567,7 @@ assert.isFalse(redirectStub.called); }); - test('redirects to dahsboard if logged in', () => { + test('redirects to dashboard if logged in', () => { sandbox.stub(element.$.restAPI, 'getLoggedIn') .returns(Promise.resolve(true)); const data = { @@ -625,35 +678,31 @@ }); test('no user specified', () => { - const data = {canonicalPath: '/dashboard', params: {}}; - const result = element._handleDashboardRoute(data); - assert.isNotOk(result); - assert.isFalse(setParamsStub.called); - assert.isFalse(redirectToLoginStub.called); - assert.isTrue(redirectStub.called); - assert.equal(redirectStub.lastCall.args[0], '/dashboard/self'); + const data = {canonicalPath: '/dashboard/', params: {0: ''}}; + return element._handleDashboardRoute(data, '').then(() => { + assert.isFalse(setParamsStub.called); + assert.isFalse(redirectToLoginStub.called); + assert.isTrue(redirectStub.called); + assert.equal(redirectStub.lastCall.args[0], '/dashboard/self'); + }); }); - test('own dahsboard but signed out redirects to login', () => { + test('own dashboard but signed out redirects to login', () => { sandbox.stub(element.$.restAPI, 'getLoggedIn') .returns(Promise.resolve(false)); - const data = {canonicalPath: '/dashboard', params: {0: 'seLF'}}; - const result = element._handleDashboardRoute(data); - assert.isOk(result); - return result.then(() => { + const data = {canonicalPath: '/dashboard/', params: {0: 'seLF'}}; + return element._handleDashboardRoute(data, '').then(() => { assert.isTrue(redirectToLoginStub.calledOnce); assert.isFalse(redirectStub.called); assert.isFalse(setParamsStub.called); }); }); - test('non-self dahsboard but signed out does not redirect', () => { + test('non-self dashboard but signed out does not redirect', () => { sandbox.stub(element.$.restAPI, 'getLoggedIn') .returns(Promise.resolve(false)); - const data = {canonicalPath: '/dashboard', params: {0: 'foo'}}; - const result = element._handleDashboardRoute(data); - assert.isOk(result); - return result.then(() => { + const data = {canonicalPath: '/dashboard/', params: {0: 'foo'}}; + return element._handleDashboardRoute(data, '').then(() => { assert.isFalse(redirectToLoginStub.called); assert.isFalse(setParamsStub.called); assert.isTrue(redirectStub.calledOnce); @@ -661,13 +710,11 @@ }); }); - test('dahsboard while signed in sets params', () => { + test('dashboard while signed in sets params', () => { sandbox.stub(element.$.restAPI, 'getLoggedIn') .returns(Promise.resolve(true)); - const data = {canonicalPath: '/dashboard', params: {0: 'foo'}}; - const result = element._handleDashboardRoute(data); - assert.isOk(result); - return result.then(() => { + const data = {canonicalPath: '/dashboard/', params: {0: 'foo'}}; + return element._handleDashboardRoute(data, '').then(() => { assert.isFalse(redirectToLoginStub.called); assert.isFalse(redirectStub.called); assert.isTrue(setParamsStub.calledOnce); @@ -677,6 +724,42 @@ }); }); }); + + test('custom dashboard without title', () => { + const data = {canonicalPath: '/dashboard/', params: {0: ''}}; + return element._handleDashboardRoute(data, '?a=b&c&d=e').then(() => { + assert.isFalse(redirectToLoginStub.called); + assert.isFalse(redirectStub.called); + assert.isTrue(setParamsStub.calledOnce); + assert.deepEqual(setParamsStub.lastCall.args[0], { + view: Gerrit.Nav.View.DASHBOARD, + user: 'self', + sections: [ + {name: 'a', query: 'b'}, + {name: 'd', query: 'e'}, + ], + title: 'Custom Dashboard', + }); + }); + }); + + test('custom dashboard with title', () => { + const data = {canonicalPath: '/dashboard/', params: {0: ''}}; + return element._handleDashboardRoute(data, '?a=b&c&d=&=e&title=t') + .then(() => { + assert.isFalse(redirectToLoginStub.called); + assert.isFalse(redirectStub.called); + assert.isTrue(setParamsStub.calledOnce); + assert.deepEqual(setParamsStub.lastCall.args[0], { + view: Gerrit.Nav.View.DASHBOARD, + user: 'self', + sections: [ + {name: 'a', query: 'b'}, + ], + title: 't', + }); + }); + }); }); suite('group routes', () => { @@ -1170,5 +1253,31 @@ }); }); }); + + suite('_parseQueryString', () => { + test('empty queries', () => { + assert.deepEqual(element._parseQueryString(''), []); + assert.deepEqual(element._parseQueryString('?'), []); + assert.deepEqual(element._parseQueryString('??'), []); + assert.deepEqual(element._parseQueryString('&&&'), []); + }); + + test('url decoding', () => { + assert.deepEqual(element._parseQueryString('+'), [[' ', '']]); + assert.deepEqual(element._parseQueryString('???+%3d+'), [[' = ', '']]); + assert.deepEqual( + element._parseQueryString('%6e%61%6d%65=%76%61%6c%75%65'), + [['name', 'value']]); + }); + + test('multiple parameters', () => { + assert.deepEqual( + element._parseQueryString('a=b&c=d&e=f'), + [['a', 'b'], ['c', 'd'], ['e', 'f']]); + assert.deepEqual( + element._parseQueryString('&a=b&&&e=f&'), + [['a', 'b'], ['e', 'f']]); + }); + }); }); </script>
diff --git a/polygerrit-ui/app/elements/diff/gr-syntax-lib-loader/gr-syntax-lib-loader.js b/polygerrit-ui/app/elements/diff/gr-syntax-lib-loader/gr-syntax-lib-loader.js index bfd8e90..ca3cf62 100644 --- a/polygerrit-ui/app/elements/diff/gr-syntax-lib-loader/gr-syntax-lib-loader.js +++ b/polygerrit-ui/app/elements/diff/gr-syntax-lib-loader/gr-syntax-lib-loader.js
@@ -26,6 +26,7 @@ // NOTE: intended singleton. value: { + configured: false, loading: false, callbacks: [], }, @@ -60,12 +61,13 @@ }, _getHighlightLib() { - return window.hljs; - }, + const lib = window.hljs; + if (lib && !this._state.configured) { + this._state.configured = true; - _configureHighlightLib() { - this._getHighlightLib().configure( - {classPrefix: 'gr-diff gr-syntax gr-syntax-'}); + lib.configure({classPrefix: 'gr-diff gr-syntax gr-syntax-'}); + } + return lib; }, _getLibRoot() { @@ -93,10 +95,8 @@ } script.src = src; - script.onload = function() { - this._configureHighlightLib(); - resolve(); - }.bind(this); + script.onload = resolve; + script.onerror = reject; Polymer.dom(document.head).appendChild(script); }); },
diff --git a/polygerrit-ui/app/elements/diff/gr-syntax-lib-loader/gr-syntax-lib-loader_test.html b/polygerrit-ui/app/elements/diff/gr-syntax-lib-loader/gr-syntax-lib-loader_test.html index 6ddde46..6e88ed1 100644 --- a/polygerrit-ui/app/elements/diff/gr-syntax-lib-loader/gr-syntax-lib-loader_test.html +++ b/polygerrit-ui/app/elements/diff/gr-syntax-lib-loader/gr-syntax-lib-loader_test.html
@@ -55,6 +55,7 @@ loadStub.restore(); // Because the element state is a singleton, clean it up. + element._state.configured = false; element._state.loading = false; element._state.callbacks = []; }); @@ -88,8 +89,13 @@ }); suite('preloaded', () => { + let hljsStub; + setup(() => { - window.hljs = 'test-object'; + hljsStub = { + configure: sinon.stub(), + }; + window.hljs = hljsStub; }); teardown(() => { @@ -101,7 +107,14 @@ element.get().then(firstCallHandler); flush(() => { assert.isTrue(firstCallHandler.called); - assert.isTrue(firstCallHandler.calledWith('test-object')); + assert.isTrue(firstCallHandler.calledWith(hljsStub)); + done(); + }); + }); + + test('configures hljs', done => { + element.get().then(() => { + assert.isTrue(window.hljs.configure.calledOnce); done(); }); });
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls.html b/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls.html new file mode 100644 index 0000000..bfbe11a --- /dev/null +++ b/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls.html
@@ -0,0 +1,48 @@ +<!-- +Copyright (C) 2017 The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> + +<link rel="import" href="../../../bower_components/polymer/polymer.html"> + +<link rel="import" href="../../shared/gr-button/gr-button.html"> +<link rel="import" href="../../shared/gr-dropdown/gr-dropdown.html"> + +<link rel="import" href="../../../styles/shared-styles.html"> + +<dom-module id="gr-edit-file-controls"> + <template> + <style include="shared-styles"> + :host { + align-items: center; + display: flex; + justify-content: flex-end; + } + #edit { + margin-right: .5em; + text-decoration: none; + } + </style> + <gr-button + id="edit" + link + on-tap="_handleEditTap">Edit</gr-button> + <!-- TODO(kaspern): implement more menu. --> + <gr-dropdown + id="more" + hidden + link>More</gr-dropdown> + </template> + <script src="gr-edit-file-controls.js"></script> +</dom-module> \ No newline at end of file
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls.js b/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls.js new file mode 100644 index 0000000..1c87621 --- /dev/null +++ b/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls.js
@@ -0,0 +1,34 @@ +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +(function() { + 'use strict'; + + Polymer({ + is: 'gr-edit-file-controls', + + /** + * Fired when the edit button is pressed. + * + * @event edit-tap + */ + + properties: { + filePath: String, + }, + + _handleEditTap() { + this.fire('edit-tap', {path: this.filePath}); + }, + }); +})(); \ No newline at end of file
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls_test.html b/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls_test.html new file mode 100644 index 0000000..250e208 --- /dev/null +++ b/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls_test.html
@@ -0,0 +1,56 @@ +<!-- +Copyright (C) 2017 The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> + +<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes"> +<title>gr-edit-file-controls</title> + +<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script> +<script src="../../../bower_components/web-component-tester/browser.js"></script> +<link rel="import" href="../../../test/common-test-setup.html"/> + +<link rel="import" href="gr-edit-file-controls.html"> + +<script>void(0);</script> + +<test-fixture id="basic"> + <template> + <gr-edit-file-controls></gr-edit-file-controls> + </template> +</test-fixture> + +<script> +suite('gr-edit-file-controls tests', () => { + let element; + let sandbox; + + setup(() => { + sandbox = sinon.sandbox.create(); + element = fixture('basic'); + }); + + teardown(() => { sandbox.restore(); }); + + test('edit tap emits event', () => { + const handler = sandbox.stub(); + element.addEventListener('edit-tap', handler); + element.filePath = 'foo'; + + MockInteractions.tap(element.$.edit); + assert.isTrue(handler.called); + assert.equal(handler.lastCall.args[0].detail.path, 'foo'); + }); +}); +</script> \ No newline at end of file
diff --git a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.html b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.html new file mode 100644 index 0000000..df2ac93 --- /dev/null +++ b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.html
@@ -0,0 +1,102 @@ +<!-- +Copyright (C) 2017 The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> + +<link rel="import" href="../../../bower_components/polymer/polymer.html"> + +<link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html"> +<link rel="import" href="../../../behaviors/gr-path-list-behavior/gr-path-list-behavior.html"> +<link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html"> +<link rel="import" href="../../core/gr-navigation/gr-navigation.html"> +<link rel="import" href="../../shared/gr-button/gr-button.html"> +<link rel="import" href="../../shared/gr-editable-label/gr-editable-label.html"> +<link rel="import" href="../../shared/gr-fixed-panel/gr-fixed-panel.html"> +<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html"> +<link rel="import" href="../../../styles/shared-styles.html"> + + +<dom-module id="gr-editor-view"> + <template> + <style include="shared-styles"> + :host { + background-color: var(--view-background-color); + } + gr-fixed-panel { + background-color: #fff; + border-bottom: 1px #eee solid; + z-index: 10; + } + header, + .subHeader { + align-items: center; + display: flex; + justify-content: space-between; + padding: .75em var(--default-horizontal-margin); + } + header gr-editable-label { + font-size: 1.2em; + font-weight: bold; + } + .textareaWrapper { + margin: var(--default-horizontal-margin); + } + .textareaWrapper textarea { + border: 1px solid #ddd; + border-radius: 3px; + box-sizing: border-box; + font-family: var(--monospace-font-family); + min-height: 60vh; + resize: none; + white-space: pre; + width: 100%; + } + .textareaWrapper textarea:focus { + outline: none; + } + .textareaWrapper .editButtons { + display: none; + } + .rightControls { + justify-content: flex-end + } + </style> + <gr-fixed-panel + class$="[[_computeContainerClass(_editLoaded)]]" + floating-disabled="[[_panelFloatingDisabled]]" + keep-on-scroll + ready-for-measure="[[!_loading]]"> + <header> + <gr-editable-label + label-text="File path" + value="[[_path]]" + placeholder="File path..." + on-changed="_handlePathChanged"></gr-editable-label> + <span class="rightControls"> + <gr-button + id="save" + disabled$="[[_saveDisabled]]" + primary + on-tap="_saveEdit">Save</gr-button> + <gr-button id="cancel" on-tap="_handleCancelTap">Cancel</gr-button> + </span> + </header> + </gr-fixed-panel> + <div class="textareaWrapper"> + <textarea value="{{_newContent::input}}" id="file"></textarea> + </div> + <gr-rest-api-interface id="restAPI"></gr-rest-api-interface> + </template> + <script src="gr-editor-view.js"></script> +</dom-module> \ No newline at end of file
diff --git a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.js b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.js new file mode 100644 index 0000000..86594d3 --- /dev/null +++ b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.js
@@ -0,0 +1,139 @@ +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +(function() { + 'use strict'; + + Polymer({ + is: 'gr-editor-view', + + /** + * Fired when the title of the page should change. + * + * @event title-change + */ + + properties: { + /** + * URL params passed from the router. + */ + params: { + type: Object, + observer: '_paramsChanged', + }, + + _change: Object, + _changeEditDetail: Object, + _changeNum: String, + _loggedIn: Boolean, + _path: String, + _content: String, + _newContent: String, + _saveDisabled: { + type: Boolean, + value: true, + computed: '_computeSaveDisabled(_content, _newContent)', + }, + }, + + behaviors: [ + Gerrit.KeyboardShortcutBehavior, + Gerrit.PatchSetBehavior, + Gerrit.PathListBehavior, + ], + + attached() { + this._getLoggedIn().then(loggedIn => { this._loggedIn = loggedIn; }); + }, + + _getLoggedIn() { + return this.$.restAPI.getLoggedIn(); + }, + + _paramsChanged(value) { + if (value.view !== Gerrit.Nav.View.EDIT) { return; } + + this._changeNum = value.changeNum; + this._path = value.path; + + // NOTE: This may be called before attachment (e.g. while parentElement is + // null). Fire title-change in an async so that, if attachment to the DOM + // has been queued, the event can bubble up to the handler in gr-app. + this.async(() => { + const title = `Editing ${this.computeTruncatedPath(this._path)}`; + this.fire('title-change', {title}); + }); + + const promises = []; + + promises.push(this._getChangeDetail(this._changeNum)); + promises.push(this._getFileContent(this._changeNum, this._path) + .then(fileContent => { + this._content = fileContent; + this._newContent = fileContent; + })); + return Promise.all(promises); + }, + + _getChangeDetail(changeNum) { + return this.$.restAPI.getDiffChangeDetail(changeNum).then(change => { + this._change = change; + }); + }, + + _handlePathChanged(e) { + const path = e.detail; + if (path === this._path) { return Promise.resolve(); } + return this.$.restAPI.renameFileInChangeEdit(this._changeNum, + this._path, path).then(res => { + if (!res.ok) { return; } + this._viewEditInChangeView(); + }); + }, + + _viewEditInChangeView() { + Gerrit.Nav.navigateToChange(this._change, this.EDIT_NAME); + }, + + _getFileContent(changeNum, path) { + return this.$.restAPI.getFileInChangeEdit(changeNum, path).then(res => { + if (!res.ok) { + if (res.status === 404) { + // No edits have been made yet. + return this.$.restAPI.getFileInChangeEdit(changeNum, path, true) + .then(res => res.text); + } + return ''; + } + return res.text; + }); + }, + + _saveEdit() { + return this.$.restAPI.saveChangeEdit(this._changeNum, this._path, + this._newContent).then(res => { + if (!res.ok) { return; } + this._viewEditInChangeView(); + }); + }, + + _computeSaveDisabled(content, newContent) { + return content === newContent; + }, + + _handleCancelTap() { + // TODO(kaspern): Add a confirm dialog if there are unsaved changes. + this._viewEditInChangeView(); + }, + }); +})();
diff --git a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_test.html b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_test.html new file mode 100644 index 0000000..e3e6474 --- /dev/null +++ b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_test.html
@@ -0,0 +1,183 @@ +<!-- +Copyright (C) 2017 The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> + +<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes"> +<title>gr-editor-view</title> + +<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script> +<script src="../../../bower_components/web-component-tester/browser.js"></script> +<link rel="import" href="../../../test/common-test-setup.html"/> + +<link rel="import" href="gr-editor-view.html"> + +<script>void(0);</script> + +<test-fixture id="basic"> + <template> + <gr-editor-view></gr-editor-view> + </template> +</test-fixture> + +<script> +suite('gr-editor-view tests', () => { + let element; + let sandbox; + let savePathStub; + let saveFileStub; + let changeDetailStub; + let navigateStub; + const mockParams = { + changeNum: '42', + path: 'foo/bar.baz', + }; + + setup(() => { + stub('gr-rest-api-interface', { + getLoggedIn() { return Promise.resolve(true); }, + }); + sandbox = sinon.sandbox.create(); + element = fixture('basic'); + savePathStub = sandbox.stub(element.$.restAPI, 'renameFileInChangeEdit'); + saveFileStub = sandbox.stub(element.$.restAPI, 'saveChangeEdit'); + changeDetailStub = sandbox.stub(element.$.restAPI, 'getDiffChangeDetail'); + navigateStub = sandbox.stub(element, '_viewEditInChangeView'); + }); + + teardown(() => { sandbox.restore(); }); + + suite('_paramsChanged', () => { + test('incorrect view returns immediately', () => { + element._paramsChanged( + Object.assign({}, mockParams, {view: Gerrit.Nav.View.DIFF})); + assert.notOk(element._changeNum); + }); + + test('good params proceed', () => { + changeDetailStub.returns(Promise.resolve({})); + const fileStub = sandbox.stub(element, '_getFileContent') + .returns(Promise.resolve('text')); + + const promises = element._paramsChanged( + Object.assign({}, mockParams, {view: Gerrit.Nav.View.EDIT})); + + flushAsynchronousOperations(); + assert.equal(element._changeNum, mockParams.changeNum); + assert.equal(element._path, mockParams.path); + assert.deepEqual(changeDetailStub.lastCall.args[0], + mockParams.changeNum); + assert.deepEqual(fileStub.lastCall.args, + [mockParams.changeNum, mockParams.path]); + + return promises.then(() => { + assert.equal(element._content, 'text'); + assert.equal(element._newContent, 'text'); + }); + }); + }); + + test('edit file path', done => { + element._changeNum = mockParams.changeNum; + element._path = mockParams.path; + savePathStub.onFirstCall().returns(Promise.resolve({})); + savePathStub.onSecondCall().returns(Promise.resolve({ok: true})); + + // Calling with the same path should not navigate. + element._handlePathChanged({detail: mockParams.path}).then(() => { + assert.isFalse(savePathStub.called); + // !ok response + element._handlePathChanged({detail: 'newPath'}).then(() => { + assert.isTrue(savePathStub.called); + assert.isFalse(navigateStub.called); + // ok response + element._handlePathChanged({detail: 'newPath'}).then(() => { + assert.isTrue(navigateStub.called); + done(); + }); + }); + }); + }); + + suite('edit file content', () => { + const originalText = 'file text'; + const newText = 'file text changed'; + + setup(() => { + element._changeNum = mockParams.changeNum; + element._path = mockParams.path; + element._content = originalText; + element._newContent = originalText; + flushAsynchronousOperations(); + }); + + test('initial load', () => { + assert.equal(element.$.file.value, originalText); + assert.isTrue(element.$.save.hasAttribute('disabled')); + }); + + test('file modification and save, !ok response', done => { + const saveSpy = sandbox.spy(element, '_saveEdit'); + saveFileStub.returns(Promise.resolve({ok: false})); + element._newContent = newText; + flushAsynchronousOperations(); + + assert.equal(element.$.file.value, newText); + assert.isFalse(element.$.save.hasAttribute('disabled')); + + MockInteractions.tap(element.$.save); + assert(saveSpy.called); + saveSpy.lastCall.returnValue.then(() => { + assert.isTrue(saveFileStub.called); + assert.deepEqual(saveFileStub.lastCall.args, + [mockParams.changeNum, mockParams.path, newText]); + assert.isFalse(navigateStub.called); + done(); + }); + }); + + test('file modification and save', done => { + const saveSpy = sandbox.spy(element, '_saveEdit'); + saveFileStub.returns(Promise.resolve({ok: true})); + element._newContent = newText; + flushAsynchronousOperations(); + + assert.equal(element.$.file.value, newText); + assert.isFalse(element.$.save.hasAttribute('disabled')); + + MockInteractions.tap(element.$.save); + assert.isTrue(saveSpy.called); + saveSpy.lastCall.returnValue.then(() => { + assert.isTrue(saveFileStub.called); + assert.isTrue(navigateStub.called); + done(); + }); + }); + + test('file modification and cancel', () => { + const cancelSpy = sandbox.spy(element, '_handleCancelTap'); + element._newContent = newText; + flushAsynchronousOperations(); + + assert.equal(element.$.file.value, newText); + assert.isFalse(element.$.save.hasAttribute('disabled')); + + MockInteractions.tap(element.$.cancel); + assert.isTrue(cancelSpy.called); + assert.isFalse(saveFileStub.called); + assert.isTrue(navigateStub.called); + }); + }); +}); +</script> \ No newline at end of file
diff --git a/polygerrit-ui/app/elements/gr-app.html b/polygerrit-ui/app/elements/gr-app.html index 6f8a4a1..eac4131 100644 --- a/polygerrit-ui/app/elements/gr-app.html +++ b/polygerrit-ui/app/elements/gr-app.html
@@ -46,6 +46,7 @@ <link rel="import" href="./core/gr-reporting/gr-reporting.html"> <link rel="import" href="./core/gr-router/gr-router.html"> <link rel="import" href="./diff/gr-diff-view/gr-diff-view.html"> +<link rel="import" href="./edit/gr-editor-view/gr-editor-view.html"> <link rel="import" href="./plugins/gr-endpoint-decorator/gr-endpoint-decorator.html"> <link rel="import" href="./plugins/gr-external-style/gr-external-style.html"> <link rel="import" href="./plugins/gr-plugin-host/gr-plugin-host.html"> @@ -151,11 +152,15 @@ view-state="{{_viewState.changeView}}" back-page="[[_lastSearchPage]]"></gr-change-view> </template> - <template is="dom-if" if="[[_showDiffView]]" restamp="true"> - <gr-diff-view - params="[[params]]" - change-view-state="{{_viewState.changeView}}"></gr-diff-view> + <template is="dom-if" if="[[_showEditorView]]" restamp="true"> + <gr-editor-view + params="[[params]]"></gr-editor-view> </template> + <template is="dom-if" if="[[_showDiffView]]" restamp="true"> + <gr-diff-view + params="[[params]]" + change-view-state="{{_viewState.changeView}}"></gr-diff-view> + </template> <template is="dom-if" if="[[_showSettingsView]]" restamp="true"> <gr-settings-view params="[[params]]"
diff --git a/polygerrit-ui/app/elements/gr-app.js b/polygerrit-ui/app/elements/gr-app.js index 4a38b85..8da8e16 100644 --- a/polygerrit-ui/app/elements/gr-app.js +++ b/polygerrit-ui/app/elements/gr-app.js
@@ -56,6 +56,7 @@ _showSettingsView: Boolean, _showAdminView: Boolean, _showCLAView: Boolean, + _showEditorView: Boolean, /** @type {?} */ _viewState: Object, /** @type {?} */ @@ -139,6 +140,7 @@ this.set('_showSettingsView', view === Gerrit.Nav.View.SETTINGS); this.set('_showAdminView', view === Gerrit.Nav.View.ADMIN); this.set('_showCLAView', view === Gerrit.Nav.View.AGREEMENTS); + this.set('_showEditorView', view === Gerrit.Nav.View.EDIT); if (this.params.justRegistered) { this.$.registration.open(); }
diff --git a/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper.js b/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper.js index e750c07..18eeb87 100644 --- a/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper.js +++ b/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper.js
@@ -20,6 +20,17 @@ } /** + * Add a callback to arbitrary event. + * The callback may return false to prevent event bubbling. + * @param {string} event Event name + * @param {function(Event):boolean} callback + * @return {function()} Unsubscribe function. + */ + GrEventHelper.prototype.on = function(event, callback) { + return this._listen(this.element, callback, {event}); + }; + + /** * Add a callback to element click or touch. * The callback may return false to prevent event bubbling. * @param {function(Event):boolean} callback @@ -43,6 +54,7 @@ GrEventHelper.prototype._listen = function(container, callback, opt_options) { const capture = opt_options && opt_options.capture; + const event = opt_options && opt_options.event || 'tap'; const handler = e => { if (e.path.indexOf(this.element) !== -1) { let mayContinue = true; @@ -58,9 +70,9 @@ } } }; - container.addEventListener('tap', handler, capture); + container.addEventListener(event, handler, capture); const unsubscribe = () => - container.removeEventListener('tap', handler, capture); + container.removeEventListener(event, handler, capture); this._unsubscribers.push(unsubscribe); return unsubscribe; };
diff --git a/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper_test.html b/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper_test.html index 9d42851..43c42a9 100644 --- a/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper_test.html +++ b/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper_test.html
@@ -92,5 +92,12 @@ flushAsynchronousOperations(); assert.isFalse(tapStub.called); }); + + test('on()', done => { + instance.on('foo', () => { + done(); + }); + element.fire('foo'); + }); }); </script>
diff --git a/polygerrit-ui/app/elements/plugins/gr-project-api/gr-plugin-project-command.html b/polygerrit-ui/app/elements/plugins/gr-project-api/gr-plugin-project-command.html new file mode 100644 index 0000000..87d11ad --- /dev/null +++ b/polygerrit-ui/app/elements/plugins/gr-project-api/gr-plugin-project-command.html
@@ -0,0 +1,34 @@ +<!-- +Copyright (C) 2017 The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> +<link rel="import" href="../../../bower_components/polymer/polymer.html"> +<link rel="import" href="../../admin/gr-project-command/gr-project-command.html"> + +<dom-module id="gr-plugin-project-command"> + <template> + <gr-project-command title="[[title]]"> + </gr-project-command> + </template> + <script> + Polymer({ + is: 'gr-plugin-project-command', + properties: { + title: String, + projectName: String, + config: Object, + }, + }); + </script> +</dom-module>
diff --git a/polygerrit-ui/app/elements/plugins/gr-project-api/gr-project-api.html b/polygerrit-ui/app/elements/plugins/gr-project-api/gr-project-api.html new file mode 100644 index 0000000..0106533 --- /dev/null +++ b/polygerrit-ui/app/elements/plugins/gr-project-api/gr-project-api.html
@@ -0,0 +1,23 @@ +<!-- +Copyright (C) 2017 The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> + +<link rel="import" href="../../../bower_components/polymer/polymer.html"> +<link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html"> +<link rel="import" href="gr-plugin-project-command.html"> + +<dom-module id="gr-project-api"> + <script src="gr-project-api.js"></script> +</dom-module>
diff --git a/polygerrit-ui/app/elements/plugins/gr-project-api/gr-project-api.js b/polygerrit-ui/app/elements/plugins/gr-project-api/gr-project-api.js new file mode 100644 index 0000000..a173edd --- /dev/null +++ b/polygerrit-ui/app/elements/plugins/gr-project-api/gr-project-api.js
@@ -0,0 +1,60 @@ +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +(function(window) { + 'use strict'; + + // Prevent redefinition. + if (window.GrProjectApi) { return; } + + function GrProjectApi(plugin) { + this._hook = null; + this.plugin = plugin; + } + + GrProjectApi.prototype._createHook = function(title) { + this._hook = this.plugin.hook('project-command').onAttached(element => { + const pluginCommand = + document.createElement('gr-plugin-project-command'); + pluginCommand.title = title; + element.appendChild(pluginCommand); + }); + }; + + GrProjectApi.prototype.createCommand = function(title, callback) { + if (this._hook) { + console.warn('Already set up.'); + return this._hook; + } + this._createHook(title); + this._hook.onAttached(element => { + if (callback(element.projectName, element.config) === false) { + element.hidden = true; + } + }); + return this; + }; + + GrProjectApi.prototype.onTap = function(callback) { + if (!this._hook) { + console.warn('Call createCommand first.'); + return this; + } + this._hook.onAttached(element => { + this.plugin.eventHelper(element).on('command-tap', callback); + }); + return this; + }; + + window.GrProjectApi = GrProjectApi; +})(window);
diff --git a/polygerrit-ui/app/elements/plugins/gr-project-api/gr-project-api_test.html b/polygerrit-ui/app/elements/plugins/gr-project-api/gr-project-api_test.html new file mode 100644 index 0000000..b0719f5 --- /dev/null +++ b/polygerrit-ui/app/elements/plugins/gr-project-api/gr-project-api_test.html
@@ -0,0 +1,80 @@ +<!DOCTYPE html> +<!-- +Copyright (C) 2017 The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> + +<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes"> +<title>gr-project-api</title> + +<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script> +<script src="../../../bower_components/web-component-tester/browser.js"></script> +<link rel="import" href="../../../test/common-test-setup.html"/> +<link rel="import" href="../gr-endpoint-decorator/gr-endpoint-decorator.html"> +<link rel="import" href="gr-project-api.html"> + +<script>void(0);</script> + +<test-fixture id="basic"> + <template> + <gr-endpoint-decorator name="project-command"> + </gr-endpoint-decorator> + </template> +</test-fixture> + +<script> + suite('gr-project-api tests', () => { + let sandbox; + let projectApi; + + setup(() => { + sandbox = sinon.sandbox.create(); + let plugin; + Gerrit.install(p => { plugin = p; }, '0.1', + 'http://test.com/plugins/testplugin/static/test.js'); + sandbox.stub(Gerrit, '_arePluginsLoaded').returns(true); + projectApi = plugin.project(); + }); + + teardown(() => { + projectApi = null; + sandbox.restore(); + }); + + test('exists', () => { + assert.isOk(projectApi); + }); + + test('works', done => { + const attachedStub = sandbox.stub(); + const tapStub = sandbox.stub(); + projectApi + .createCommand('foo', attachedStub) + .onTap(tapStub); + const element = fixture('basic'); + flush(() => { + assert.isTrue(attachedStub.called); + const pluginCommand = element.$$('gr-plugin-project-command'); + assert.isOk(pluginCommand); + const command = pluginCommand.$$('gr-project-command'); + assert.isOk(command); + assert.equal(command.title, 'foo'); + assert.isFalse(tapStub.called); + MockInteractions.tap(command.$$('gr-button')); + assert.isTrue(tapStub.called); + done(); + }); + }); + }); +</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-button/gr-button.html b/polygerrit-ui/app/elements/shared/gr-button/gr-button.html index c0b17af..9be6497 100644 --- a/polygerrit-ui/app/elements/shared/gr-button/gr-button.html +++ b/polygerrit-ui/app/elements/shared/gr-button/gr-button.html
@@ -116,7 +116,7 @@ color: #aaa; } </style> - <paper-button raised="[[!link]]" disabled="[[disabled]]"> + <paper-button raised="[[!link]]" disabled="[[disabled]]" tabindex="-1"> <content></content> <i class="downArrow"></i> </paper-button>
diff --git a/polygerrit-ui/app/elements/shared/gr-confirm-dialog/gr-confirm-dialog.html b/polygerrit-ui/app/elements/shared/gr-confirm-dialog/gr-confirm-dialog.html index 27c0355..575353d 100644 --- a/polygerrit-ui/app/elements/shared/gr-confirm-dialog/gr-confirm-dialog.html +++ b/polygerrit-ui/app/elements/shared/gr-confirm-dialog/gr-confirm-dialog.html
@@ -45,20 +45,23 @@ footer { padding: .5em .65em; } + gr-button { + margin-left: 1em; + } footer { display: flex; flex-shrink: 0; - justify-content: space-between; + justify-content: flex-end; } </style> <div class="container"> <header><content select=".header"></content></header> <main><content select=".main"></content></main> <footer> - <gr-button primary on-tap="_handleConfirmTap" disabled="[[disabled]]"> + <gr-button link on-tap="_handleCancelTap">Cancel</gr-button> + <gr-button link primary on-tap="_handleConfirmTap" disabled="[[disabled]]"> [[confirmLabel]] </gr-button> - <gr-button on-tap="_handleCancelTap">Cancel</gr-button> </footer> </div> </template>
diff --git a/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.html b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.html index 6eb7c8d..ef78f3a 100644 --- a/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.html +++ b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.html
@@ -92,8 +92,8 @@ label="[[labelText]]" value="{{_inputText}}"></paper-input> <div class="buttons"> - <gr-button id="cancelBtn" on-tap="_cancel">cancel</gr-button> - <gr-button id="saveBtn" on-tap="_save">save</gr-button> + <gr-button link id="cancelBtn" on-tap="_cancel">cancel</gr-button> + <gr-button link id="saveBtn" on-tap="_save">save</gr-button> </div> </div> </div>
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.html index 4133600..c5b0441 100644 --- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.html +++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.html
@@ -21,6 +21,7 @@ <link rel="import" href="../../plugins/gr-dom-hooks/gr-dom-hooks.html"> <link rel="import" href="../../plugins/gr-event-helper/gr-event-helper.html"> <link rel="import" href="../../plugins/gr-popup-interface/gr-popup-interface.html"> +<link rel="import" href="../../plugins/gr-project-api/gr-project-api.html"> <link rel="import" href="../../plugins/gr-theme-api/gr-theme-api.html"> <link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html"> <link rel="import" href="../gr-rest-api-interface/gr-rest-api-interface.html">
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js index 467e012..4e6b8cf 100644 --- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js +++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js
@@ -198,6 +198,10 @@ return new GrThemeApi(this); }; + Plugin.prototype.project = function() { + return new GrProjectApi(this); + }; + Plugin.prototype.attributeHelper = function(element) { return new GrAttributeHelper(element); };
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js index 5153fb0..c206b20 100644 --- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js +++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
@@ -765,6 +765,11 @@ // Response may be an array of changes OR an array of arrays of // changes. if (opt_query instanceof Array) { + // Normalize the response to look like a multi-query response + // when there is only one query. + if (opt_query.length === 1) { + response = [response]; + } for (const arr of response) { iterateOverChanges(arr); } @@ -1249,9 +1254,25 @@ .then(response => this.getResponseObject(response)); }, - getFileInChangeEdit(changeNum, path) { + /** + * Gets a file in a change edit. + * @param {number|string} changeNum + * @param {string} path + * @param {boolean=} opt_base If specified, file contents come from change + * edit's base patchset. + */ + getFileInChangeEdit(changeNum, path, opt_base) { const e = '/edit/' + encodeURIComponent(path); - return this.getChangeURLAndSend(changeNum, 'GET', null, e); + let payload = null; + if (opt_base) { payload = {base: true}; } + return this.getChangeURLAndSend(changeNum, 'GET', null, e, payload) + .then(res => { + if (!res.ok) { return res; } + return res.text().then(text => { + res.text = atob(text); + return res; + }); + }); }, rebaseChangeEdit(changeNum) { @@ -1279,7 +1300,8 @@ saveChangeEdit(changeNum, path, contents) { const e = '/edit/' + encodeURIComponent(path); - return this.getChangeURLAndSend(changeNum, 'PUT', null, e, contents); + return this.getChangeURLAndSend(changeNum, 'PUT', null, e, contents, null, + null, 'text/plain'); }, // Deprecated, prefer to use putChangeCommitMessage instead. @@ -1845,7 +1867,7 @@ * @param {?string} endpoint gets passed as null. * @param {?Object|number|string=} opt_payload gets passed as null, string, * Object, or number. - * @param {function(?Response, string=)=} opt_errFn + * @param {?function(?Response, string=)=} opt_errFn * @param {?=} opt_ctx * @param {?=} opt_contentType * @return {!Promise<!Object>}
diff --git a/polygerrit-ui/app/samples/project-command.html b/polygerrit-ui/app/samples/project-command.html new file mode 100644 index 0000000..8131a02 --- /dev/null +++ b/polygerrit-ui/app/samples/project-command.html
@@ -0,0 +1,42 @@ +<dom-module id="sample-project-command"> + <script> + Gerrit.install(plugin => { + // High-level API + plugin.project() + .createCommand('Bork', (projectName, projectConfig) => { + if (projectName !== 'All-Projects') { + return false; + } + }).onTap(() => { + alert('Bork, bork!'); + }); + + // Low-level API + plugin.registerCustomComponent( + 'project-command', 'project-command-low'); + }); + </script> +</dom-module> + +<!-- Low-level custom component for project command. --> +<dom-module id="project-command-low"> + <template> + <gr-project-command + title="Low-level bork" + on-command-tap="_handleCommandTap"> + </gr-project-command> + </template> + <script> + Polymer({ + is: 'project-command-low', + attached() { + console.log(this.projectName); + console.log(this.config); + this.hidden = this.projectName !== 'All-Projects'; + }, + _handleCommandTap() { + alert('(softly) bork, bork.'); + }, + }); + </script> +</dom-module>
diff --git a/polygerrit-ui/app/styles/gr-change-list-styles.html b/polygerrit-ui/app/styles/gr-change-list-styles.html index 00ea613..c479c1a 100644 --- a/polygerrit-ui/app/styles/gr-change-list-styles.html +++ b/polygerrit-ui/app/styles/gr-change-list-styles.html
@@ -45,6 +45,7 @@ .label, .number, .owner, + .assignee, .updated, .size, .status,
diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html index 7080eb7..b0310c6 100644 --- a/polygerrit-ui/app/test/index.html +++ b/polygerrit-ui/app/test/index.html
@@ -44,6 +44,7 @@ 'admin/gr-permission/gr-permission_test.html', 'admin/gr-plugin-list/gr-plugin-list_test.html', 'admin/gr-project-access/gr-project-access_test.html', + 'admin/gr-project-command/gr-project-command_test.html', 'admin/gr-project-commands/gr-project-commands_test.html', 'admin/gr-project-detail-list/gr-project-detail-list_test.html', 'admin/gr-project-list/gr-project-list_test.html', @@ -102,10 +103,13 @@ 'diff/gr-selection-action-box/gr-selection-action-box_test.html', 'diff/gr-syntax-layer/gr-syntax-layer_test.html', 'diff/gr-syntax-lib-loader/gr-syntax-lib-loader_test.html', + 'edit/gr-edit-file-controls/gr-edit-file-controls_test.html', + 'edit/gr-editor-view/gr-editor-view_test.html', 'plugins/gr-attribute-helper/gr-attribute-helper_test.html', 'plugins/gr-endpoint-decorator/gr-endpoint-decorator_test.html', 'plugins/gr-event-helper/gr-event-helper_test.html', 'plugins/gr-external-style/gr-external-style_test.html', + 'plugins/gr-project-api/gr-project-api_test.html', 'plugins/gr-plugin-host/gr-plugin-host_test.html', 'plugins/gr-popup-interface/gr-plugin-popup_test.html', 'plugins/gr-popup-interface/gr-popup-interface_test.html',
diff --git a/version.bzl b/version.bzl index 3be5283..62d841f 100644 --- a/version.bzl +++ b/version.bzl
@@ -2,4 +2,4 @@ # Used by :api_install and :api_deploy targets # when talking to the destination repository. # -GERRIT_VERSION = "2.15-rc0" +GERRIT_VERSION = "2.16-SNAPSHOT"