diff --git a/.gitignore b/.gitignore index f9b58c5b1c..6950edc0ba 100644 --- a/.gitignore +++ b/.gitignore @@ -64,3 +64,9 @@ bin/ *.tmp /tmp* *.bak + +# Gradle files +.gradle/ + +# other things that really ought to go under target/ +ehcache.disk.store.dir/ diff --git a/build.sh b/build.sh index a9c72d363b..599242657b 100755 --- a/build.sh +++ b/build.sh @@ -2,4 +2,4 @@ set -a # make sure the env vars are exported source <(etc/scripts/allocate-jboss-ports) -exec mvn "$@" +exec xvfb-run -a mvn "$@" diff --git a/etc/scripts/cargowait.sh b/etc/scripts/cargowait.sh new file mode 100755 index 0000000000..79ee8226fc --- /dev/null +++ b/etc/scripts/cargowait.sh @@ -0,0 +1,41 @@ +#!/bin/bash +#set -x + +clean=clean +extraArgs= +while getopts "nhd:" opt; do + case ${opt} in + n) + echo ">> -n specified. Will NOT run maven goal 'clean'." >&2 + clean='' + ;; + h) + echo ">> run this script to prepare a functional test war and start cargo in waiting mode" >&2 + echo ">>>> -n if you don't want to run maven clean goal. Useful when you have run functional-test-db.snapshot.sh." >&2 + echo ">>>> -d if you want to pass in extra arguments when running functional test" >&2 + exit 0; + ;; + d) + extraArgs=$OPTARG + echo ">> extra parameter passed to functional-test module execution:$extraArgs" + ;; + \?) + echo "Invalid option: -$OPTARG" >&2 + exit 1; + ;; + esac +done + +scriptBaseDir=$(dirname $0) +serverModuleRoot=$(readlink -f "${scriptBaseDir}/../..") + +cd ${serverModuleRoot} + +echo ">> Now working in $(pwd)" +echo + +# this will build zanata war and prepare war overlay for functional test +mvn ${clean} package -Dchromefirefox -DskipTests -Dfunctional-test -pl zanata-model,zanata-war,zanata-test-war; + +# this will start cargo container and deploy above generated overlay war and then pause +mvn ${clean} process-test-resources package cargo:run -Dfunctional-test -Dmysql.port=13306 -pl functional-test ${extraArgs}; diff --git a/etc/scripts/functional-test-db-snapshot.sh b/etc/scripts/functional-test-db-snapshot.sh new file mode 100755 index 0000000000..837f2cccc1 --- /dev/null +++ b/etc/scripts/functional-test-db-snapshot.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# once you have run cargowait.sh script and cargo has started, you can run this +# script to take a snapshot of current database and store it as +# target/database.sql. +# subsequent cargowait.sh execution can then use -n option (no clean) to make +# use of this script. + +scriptBaseDir=$(dirname $0) + +functionalTestTarget=${scriptBaseDir}/../../functional-test/target + +targetPath=$(readlink -f ${functionalTestTarget}) + +${targetPath}/mysql-dist/bin/mysqldump --port=13306 --user=root --password=root --socket=${targetPath}/mdb/mysql.sock root > ${targetPath}/database.sql + +ls -ltr ${targetPath}/database.sql diff --git a/etc/scripts/standalone.cli.messaging.config b/etc/scripts/standalone.cli.messaging.config new file mode 100644 index 0000000000..ff6d955a41 --- /dev/null +++ b/etc/scripts/standalone.cli.messaging.config @@ -0,0 +1,63 @@ +# run jboss-cli.sh --file to apply messaging changes + +connect + +# jboss cli specification for an operation request +#[/node-type=node-name (/node-type=node-name)*] : operation-name [( [parameter-name=parameter-value (,parameter-name=parameter-value)*] )] + +# add messaging extension if not already exist +if (outcome == failed) of /extension=org.jboss.as.messaging:read-resource() + /extension=org.jboss.as.messaging:add() +end-if + +if (outcome == failed) of /subsystem=messaging:read-resource() + /subsystem=messaging:add() +end-if + +batch + +/subsystem=ejb3:write-attribute(name="default-resource-adapter-name", value="hornetq-ra") +/subsystem=ejb3:write-attribute(name="default-mdb-instance-pool", value="mdb-strict-max-pool") + +/subsystem=messaging/hornetq-server=default:add(persistence-enabled=true, security-enabled=false, journal-type=NIO, journal-min-files=2) +#/subsystem=messaging/hornetq-server=default/:write-attribute(name=persistence-enabled, value=true) +#/subsystem=messaging/hornetq-server=default/:write-attribute(name=security-enabled, value=false) +#/subsystem=messaging/hornetq-server=default/:write-attribute(name=journal-type, value=NIO) +#/subsystem=messaging/hornetq-server=default/:write-attribute(name=journal-min-files, value=2) + +/subsystem=messaging/hornetq-server=default/address-setting=#:add(address-full-policy="PAGE", \ + dead-letter-address="jms.queue.DLQ", expiry-address="jms.queue.ExpiryQueue", expiry-delay=-1L, \ + last-value-queue=false, max-delivery-attempts=3, max-size-bytes=10485760L, message-counter-history-day-limit=10, \ + page-max-cache-size=5, page-size-bytes=2097152L, redelivery-delay=5000L, redistribution-delay=5000L, send-to-dla-on-no-route=false) + +/subsystem=messaging/hornetq-server=default/in-vm-connector=in-vm:add(server-id=0) +/subsystem=messaging/hornetq-server=default/in-vm-acceptor=in-vm:add(server-id=0) + +/subsystem=messaging/hornetq-server=default/connection-factory=InVmConnectionFactory:add(connector={"in-vm"=>undefined}, entries = ["java:/ConnectionFactory"]) +/subsystem=messaging/hornetq-server=default/pooled-connection-factory=hornetq-ra:add(connector={"in-vm"=>undefined}, entries=["java:/JmsXA","java:jboss/DefaultJMSConnectionFactory"]) +/subsystem=messaging/hornetq-server=default/pooled-connection-factory=hornetq-ra/:write-attribute(name=min-pool-size,value=2) +/subsystem=messaging/hornetq-server=default/pooled-connection-factory=hornetq-ra/:write-attribute(name=max-pool-size,value=10) + +/subsystem=messaging/hornetq-server=default/security-setting=#:add() +/subsystem=messaging/hornetq-server=default/security-setting=#/role=guest:add(consume=true, create-durable-queue=false, create-non-durable-queue=true, delete-durable-queue=false, delete-non-durable-queue=true, manage=false, send=true) + +jms-queue add --queue-address=ExpiryQueue --durable=true --entries=["java:/jms/queue/ExpiryQueue"] +jms-queue add --queue-address=DLQ --durable=true --entries=["java:/jms/queue/DLQ"] +jms-queue add --queue-address=MailsQueue --durable=true --entries=["java:/jms/queue/MailsQueue"] + +# if we want to add netty as remote connector +#/subsystem=messaging/hornetq-server=default/remote-connector=netty/:add(socket-binding=messaging) +#/subsystem=messaging/hornetq-server=default/remote-connector=netty-throughput/:add(socket-binding=messaging-throughput) +#/subsystem=messaging/hornetq-server=default/remote-connector=netty-throughput/param=batch-delay/:add(value=50) +#/subsystem=messaging/hornetq-server=default/remote-connector=netty-throughput/param=direct-deliver/:add(value=false) + +#/subsystem=messaging/hornetq-server=default/remote-acceptor=netty/:add(socket-binding=messaging) +#/subsystem=messaging/hornetq-server=default/remote-acceptor=netty-throughput/:add(socket-binding=messaging-throughput) +#/subsystem=messaging/hornetq-server=default/remote-acceptor=netty-throughput/param=batch-delay:add(value=50) +#/subsystem=messaging/hornetq-server=default/remote-acceptor=netty-throughput/param=direct-deliver:add(value=false) + +#/socket-binding-group=standard-sockets/socket-binding=messaging:add(port="${jboss.messaging.port,env.JBOSS_MESSAGING_PORT:5445}") +#/socket-binding-group=standard-sockets/socket-binding=messaging-group:add(port=0, multicast-address="${jboss.messaging.group.address:231.7.7.7}", multicast-port="${jboss.messaging.group.port:9876}") +#/socket-binding-group=standard-sockets/socket-binding=messaging-throughput:add(port="${jboss.messaging.throughput.port,env.JBOSS_MESSAGING_THROUGHPUT_PORT:5455}") + +run-batch diff --git a/functional-test/etc/mysql-socket.groovy b/functional-test/etc/mysql-socket.groovy new file mode 100644 index 0000000000..259a4292d9 --- /dev/null +++ b/functional-test/etc/mysql-socket.groovy @@ -0,0 +1,17 @@ +// This script sets the Maven project property 'mysql.socket' +// to a short path under /tmp (derived from the project build directory) +// and optionally deletes the socket file if it exists. + +import java.security.MessageDigest + +def messageDigest = MessageDigest.getInstance("MD5") +messageDigest.update(project.build.directory.bytes); +String md5Hex = messageDigest.digest().encodeHex() + +File mysqlSocket = new File(System.getProperty('java.io.tmpdir'), 'mysql-'+md5Hex+'.socket') +project.properties['mysql.socket'] = mysqlSocket.absolutePath + +if (project.properties['delete'] == 'true') { + log.info('Deleting mysql.socket file: ' + mysqlSocket) + mysqlSocket.delete(); +} diff --git a/functional-test/pom.xml b/functional-test/pom.xml index b40e4387e2..3044f6eb96 100644 --- a/functional-test/pom.xml +++ b/functional-test/pom.xml @@ -4,7 +4,7 @@ org.zanata server - 3.6.0-SNAPSHOT + 3.7.0-SNAPSHOT functional-test @@ -12,20 +12,19 @@ functional-test - 2.39.0 + 2.44.0 - false localhost - jboss72x 0 + 8180 - http://downloads.sourceforge.net/project/zanata/server/zanata-server.zip + 10090 + 10099 ${project.build.directory}/cargo/installs ${project.build.directory}/jboss/container - ../../cargo/installs/zanata-server zanata @@ -50,7 +49,6 @@ /opt/google/chrome/google-chrome chrome - :0 ${project.build.directory}/webdriver.log ${project.build.directory}/screenshots 10 @@ -61,14 +59,15 @@ -Xnoagent -Djava.compiler=NONE - **/AggregateTestSuite.java + **/BasicAcceptanceTestSuite.java + ${default.test.patterns} ${project.build.directory}/zanataindex ${project.build.directory}/zanatastats 2552 org.jboss.as - jboss-as-ejb-client-bom - 7.1.1.Final + jboss-as-controller-client + 7.2.0.Final standalone.xml @@ -77,7 +76,7 @@ org.apache.httpcomponents httpclient - 4.3.3 + 4.3.4 org.apache.httpcomponents @@ -153,6 +152,10 @@ org.slf4j slf4j-api + + org.slf4j + slf4j-log4j12 + org.subethamail @@ -221,6 +224,7 @@ org.jboss.resteasy jaxrs-api + ${resteasy.scope} @@ -250,17 +254,33 @@ org.jboss.resteasy resteasy-jaxb-provider + + + javax.xml.bind + jaxb-api + + + org.jboss.logging + jboss-logging + + org.zanata zanata-adapter-po + + + javax.xml.bind + jaxb-api + + - javax.xml.bind - jaxb-api + org.jboss.spec.javax.xml.bind + jboss-jaxb-api_2.2_spec @@ -276,13 +296,25 @@ ${jndi-remote-client.groupId} ${jndi-remote-client.artifactId} ${jndi-remote-client.version} - pom test + + + javax.xml.bind + jaxb-api + + org.jboss jboss-remote-naming + 2.0.1.Final test + + + org.jboss.remoting + jboss-remoting + + @@ -313,19 +345,6 @@ jbosseap6 - - - - maven-dependency-plugin - - - org.jboss.as:jboss-as-ejb-client-bom - - - - - - wildfly8 @@ -336,51 +355,19 @@ - wildfly8x - http://download.jboss.org/wildfly/${wildfly.version}/wildfly-${wildfly.version}.zip standalone_wildfly.xml org.wildfly - wildfly-ejb-client-bom + wildfly-controller-client ${wildfly.version} - maven-dependency-plugin - - - org.wildfly:wildfly-ejb-client-bom - - - - - org.codehaus.cargo - cargo-maven2-plugin - - - - - cargo-install - prepare-package - - install - - - - - - org.codehaus.gmaven - gmaven-plugin + maven-antrun-plugin - unpack-wildfly-modules + install-wildfly-modules package - - execute - - - ${pom.basedir}/src/etc/wildflyModules.groovy - @@ -403,11 +390,63 @@ + + + skipFuncTests + + + skipFuncTests + + + + true + + + + + allFuncTests + + + allFuncTests + true + + + + **/DetailedTestSuite.java + + + + + maven-antrun-plugin + + + check-tests-enabled + validate + + run + + + + + + + + + + + + + + + run-functional-test - functional-test + !skipFuncTests @@ -458,7 +497,7 @@ mysql.classifier ${mysql.port} - ${project.build.directory}/mdb + ${mysql.socket} @@ -502,6 +541,13 @@ package ALTER DATABASE ${ds.database} CHARACTER SET utf8 COLLATE utf8_general_ci; + + + + + ${project.build.directory}/database.sql + + continue @@ -510,17 +556,11 @@ org.codehaus.cargo cargo-maven2-plugin - - - - ${cargo.installation} - ${user.home}/Downloads - ${cargo.extract.dir} - - ${cargo.servlet.port} + ${cargo.jboss.management-http.port} + ${cargo.jboss.management-native.port} @@ -528,7 +568,7 @@ war ${project.basedir}/../zanata-test-war/target/${zanata.test.war.name}.war http://${cargo.host}:${cargo.servlet.port}/${context.path}/ - 300000 + 600000 ${context.path} @@ -537,12 +577,43 @@ + + cargo-install + prepare-package + cargo-start pre-integration-test start + + + + + + -XX:PermSize=512m -XX:MaxPermSize=1024 + -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled + ${cargo.debug.jvm.args} + + + + + + + default-cli + + + + + + -XX:PermSize=512m -XX:MaxPermSize=1024 + -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled + ${cargo.debug.jvm.args} + + + + cargo-stop @@ -556,21 +627,6 @@ maven-failsafe-plugin - 2.11 - - - integration-test - - integration-test - - - - verify - - verify - - - false true @@ -578,13 +634,11 @@ ${include.test.patterns} - - **/FeatureTest.java listener - org.zanata.util.ScreenshotEnabledTestRunListener,org.zanata.util.FeatureInventoryRecorder + org.zanata.util.ScreenshotEnabledTestRunListener,org.zanata.util.FeatureInventoryRecorder,org.zanata.util.TestLogger @@ -608,6 +662,30 @@ + + JBOSS_MANAGEMENT_HTTP_PORT + + + env.JBOSS_MANAGEMENT_HTTP_PORT + + + + ${env.JBOSS_MANAGEMENT_HTTP_PORT} + + + + + JBOSS_MANAGEMENT_NATIVE_PORT + + + env.JBOSS_MANAGEMENT_NATIVE_PORT + + + + ${env.JBOSS_MANAGEMENT_NATIVE_PORT} + + + SMTP_PORT @@ -670,10 +748,57 @@ ${project.build.directory}/feature-inventory + + + org.codehaus.gmaven + gmaven-plugin + + + clean + clean + + execute + + + + true + + + ${project.basedir}/etc/mysql-socket.groovy + + + + initialize + initialize + + execute + + + + false + + + ${project.basedir}/etc/mysql-socket.groovy + + + + + + commons-io + commons-io + 2.4 + + + + maven-antrun-plugin + + init-no-appserver + initialize + generate-resources @@ -682,21 +807,48 @@ ===== Properties that can be set for functional test ===== - -Dfunctional-test : to activate functional test run + -DskipFuncTests : to skip running functional tests, and/or: + -DskipArqTests : to skip Arquillian integration tests (if building zanata-war) + + Unless skipping tests, you must choose an appserver: + -Dappserver=jbosseap6 -Dcargo.installation=http://example.com/jbosseap632.zip -Dcargo.basename=jbosseap632 + or -Dappserver=wildfly8 + NB: cargo.basename needs to match the basename of the file given in cargo.installation. + For example, if cargo.installation is http://example.com/download/jboss-6.3.2.zip, cargo.basename should be jboss-6.3.2. + appserver.dir.name is top-level dir inside zip. For jbosseap6, default is jboss-eap-6.3. Override for later versions. + + -DallFuncTests to enable all functional tests (defaults to smoke tests) + + -Dcargo.debug.jvm.args : If not set by default will listen to port 8787. Need to set to empty on jenkins + -Dzanata.target.version=version of zanata to deploy. Default is: ${project.parent.version} -Dzanata.instance.url=http://${cargo.host}:${cargo.servlet.port}/${context.path} -Dzanata.apikey=b6d7044e9ee3b2447c28fb7c50d86d98 -Dzanata.sample.projects.basedir=${project.build.testOutputDirectory}/sample-projects - -Dcargo.debug.jvm.args : If not set by default will listen to port 8787. Need to set to empty on jenkins - -Dinclude.test.patterns=test filter pattern. Can be used to control what test to run. Default is **/*AggregateTestSuite.java. + -Dinclude.test.patterns=test filter pattern. Can be used to control what test to run. Default is ${default.test.patterns} -Dwebdriver.type=run tests in htmlUnit, chrome or firefox. For chrome, see also webdriver.chrome.* Default is chrome. - -Dwebdriver.display=display to run test browser in, for Xnest or otherwise. Default is :0. -Dwebdriver.chrome.bin=full path to chrome binary. - -Dwebdriver.chrome.driver=full path to chromedriver binary. + -Dwebdriver.chrome.driver=full path to chromedriver binary. Default is chromedriver on $PATH -Dwebdriver.wait=global wait time in seconds for element searches. Default is 10. + NB: set env var DISPLAY to run test browser in alternative display, for Xnest/Xvfb/Xvnc. eg: xvfb-run -e mvn verify -Dappserver=wildfly8 ========================================================== - to ask cargo to start up then wait so that tests can be run manually: mvn clean package cargo:run -Dfunctional-test -Dmysql.port=13306 + to ask cargo to start up then wait so that tests can be run manually: mvn clean package cargo:run -Dappserver=wildfly8 -Dmysql.port=13306 + + + + + + + preIT-no-appserver + prepare-package + run + + + + @@ -745,13 +897,8 @@ - ${cargo.container} installed - ${project.build.directory}/container.log - false - ${project.build.directory}/cargo.log - ${project.build.directory}/ehcache WARN @@ -760,7 +907,7 @@ ${cargo.port.offset} - 0 + 120000 @@ -792,12 +939,6 @@ standalone - - - -XX:PermSize=512m -XX:MaxPermSize=1024 - -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled - ${cargo.debug.jvm.args} - ${cargo.port.offset} 8109 diff --git a/functional-test/src/etc/wildflyModules.groovy b/functional-test/src/etc/wildflyModules.groovy deleted file mode 100644 index 240212f518..0000000000 --- a/functional-test/src/etc/wildflyModules.groovy +++ /dev/null @@ -1,51 +0,0 @@ - -def download(String address, File destFile) { - def file = new FileOutputStream(destFile) - def out = new BufferedOutputStream(file) - out << new URL(address).openStream() - out.close() -} - -def mojarraUrl = "http://sourceforge.net/projects/zanata/files/wildfly/wildfly-8.1.0.Final-module-mojarra-2.1.28.zip/download" -def hibernateUrl = "http://sourceforge.net/projects/zanata/files/wildfly/wildfly-8.1.0.Final-module-hibernate-main-4.2.15.Final.zip/download" - -File mojarraZip = new File(project.build.directory, "mojarra.zip") -File hibernateZip = new File(project.build.directory, "hibernate.zip") - -download(mojarraUrl, mojarraZip) -download(hibernateUrl, hibernateZip) - - -def ant = new AntBuilder() - -def mojarraModuleDest = project.build.directory + "/mojarra" -ant.unzip(src: mojarraZip.absolutePath, dest: mojarraModuleDest, overwrite: "true" ) -def hibernateModuleDest = project.build.directory + "/hibernate" -ant.unzip(src: hibernateZip.absolutePath, dest: hibernateModuleDest, overwrite: "true" ) - -File wildFlyInstallRoot = new File(project.properties['cargo.extract.dir']) -def dirFilter = { - it.isDirectory(); -} as FileFilter - -def listSubDirs = { File it -> - it.listFiles(dirFilter) -} - -def subDirs = listSubDirs(wildFlyInstallRoot) -File wildFlyRoot; -while (subDirs.size() == 1) { - wildFlyRoot = subDirs[0] - subDirs = listSubDirs(wildFlyRoot) -} - -assert wildFlyRoot != null -println "Wild Fly root is at $wildFlyRoot" - -ant.copy(todir: wildFlyRoot, overwrite: true) { - fileset(dir: mojarraModuleDest) -} - -ant.copy(todir: wildFlyRoot, overwrite: true) { - fileset(dir: hibernateModuleDest) -} diff --git a/functional-test/src/main/java/org/zanata/page/AbstractPage.java b/functional-test/src/main/java/org/zanata/page/AbstractPage.java index 64588efa8e..c3d76bc899 100644 --- a/functional-test/src/main/java/org/zanata/page/AbstractPage.java +++ b/functional-test/src/main/java/org/zanata/page/AbstractPage.java @@ -28,10 +28,12 @@ import org.hamcrest.Description; import org.hamcrest.Matcher; +import org.hamcrest.StringDescription; import org.openqa.selenium.*; import org.openqa.selenium.support.PageFactory; import org.openqa.selenium.support.pagefactory.AjaxElementLocatorFactory; import org.openqa.selenium.support.ui.FluentWait; +import org.zanata.util.ShortString; import org.zanata.util.WebElementUtil; import com.google.common.base.Function; @@ -44,13 +46,12 @@ @Slf4j public class AbstractPage { private final WebDriver driver; - private final FluentWait ajaxWaitForSec; public AbstractPage(final WebDriver driver) { PageFactory.initElements(new AjaxElementLocatorFactory(driver, 10), this); this.driver = driver; - ajaxWaitForSec = WebElementUtil.waitForAMoment(driver); + assert driver instanceof JavascriptExecutor; waitForPageSilence(); } @@ -73,12 +74,24 @@ public WebDriver getDriver() { return driver; } + public JavascriptExecutor getExecutor() { + return (JavascriptExecutor) getDriver(); + } + public String getUrl() { return driver.getCurrentUrl(); } + protected void logWaiting(String msg) { + log.info("Waiting for {}", msg); + } + + protected void logFinished(String msg) { + log.debug("Finished {}", msg); + } + public FluentWait waitForAMoment() { - return ajaxWaitForSec; + return WebElementUtil.waitForAMoment(driver); } /** @@ -93,29 +106,62 @@ public void waitForPage(List elementBys) { } public Alert switchToAlert() { - return waitForAMoment().until(new Function() { + return waitForAMoment().withMessage("alert").until(new Function() { @Override public Alert apply(WebDriver driver) { try { return getDriver().switchTo().alert(); - } - catch (NoAlertPresentException noAlertPresent) { + } catch (NoAlertPresentException noAlertPresent) { return null; } } }); } + /** + * @deprecated Use the overload which includes a message + */ + @Deprecated protected

P refreshPageUntil(P currentPage, Predicate predicate) { - waitForAMoment().until(predicate); + return refreshPageUntil(currentPage, predicate, null); + } + + /** + * @param currentPage + * @param predicate + * @param message description of predicate + * @param

+ * @return + */ + protected

P refreshPageUntil(P currentPage, + Predicate predicate, String message) { + waitForAMoment().withMessage(message).until(predicate); PageFactory.initElements(driver, currentPage); return currentPage; } + /** + * @deprecated Use the overload which includes a message + */ + @Deprecated protected

T refreshPageUntil(P currentPage, Function function) { - T done = waitForAMoment().until(function); + return refreshPageUntil(currentPage, function, null); + } + + /** + * + * @param currentPage + * @param function + * @param message description of function + * @param

+ * @param + * @return + */ + protected

T refreshPageUntil(P currentPage, + Function function, String message) { + T done = waitForAMoment().withMessage(message).until(function); PageFactory.initElements(driver, currentPage); return done; } @@ -131,59 +177,120 @@ protected

T refreshPageUntil(P currentPage, */ public void waitFor(final Callable callable, final Matcher matcher) { - waitForAMoment().until(new Predicate() { - @Override - public boolean apply(WebDriver input) { - try { - T result = callable.call(); - if (!matcher.matches(result)) { - matcher.describeMismatch(result, - new Description.NullDescription()); + waitForAMoment().withMessage(StringDescription.toString(matcher)).until( + new Predicate() { + @Override + public boolean apply(WebDriver input) { + try { + T result = callable.call(); + if (!matcher.matches(result)) { + matcher.describeMismatch(result, + new Description.NullDescription()); + } + return matcher.matches(result); + } catch (Exception e) { + log.warn("exception", e); + return false; + } } - return matcher.matches(result); - } - catch (Exception e) { - log.warn("exception", e); - return false; - } - } - }); + }); + } + + /** + * Normally a page has no outstanding ajax requests when it has + * finished an operation, but some pages use long polling to + * "push" changes to the user, eg for the editor's event service. + * @return + */ + protected int getExpectedBackgroundRequests() { + return 0; + } + + + public void execAndWaitForNewPage(Runnable runnable) { + final WebElement oldPage = driver.findElement(By.tagName("html")); + runnable.run(); + String msg = "new page load"; + logWaiting(msg); + waitForAMoment().withMessage(msg).until( + new Predicate() { + @Override + public boolean apply(WebDriver input) { + try { + // ignore result + oldPage.getAttribute("class"); + // if we get here, the old page is still there + return false; + } catch (StaleElementReferenceException e) { + // http://www.obeythetestinggoat.com/how-to-get-selenium-to-wait-for-page-load-after-a-click.html + // + // This exception means the new page has loaded + // (or started to). + String script = "return document.readyState === " + + "'complete' && window.javascriptFinished"; + Boolean documentComplete = + (Boolean) getExecutor().executeScript( + script); + // TODO wait for ajax? + // NB documentComplete might be null/undefined + return documentComplete == Boolean.TRUE; + } + } + }); + logFinished(msg); + } + + /** + * Wait for any AJAX and timeout requests to return. + */ + public void waitForAbsolutePageSilence() { + waitForPageSilence(true); } /** - * Wait for jQuery and Ajax calls to be 0 - * If either are not defined, they can be assumed to be 0. + * Wait for any AJAX (but not timeout) requests to return. */ public void waitForPageSilence() { - // Wait for jQuery calls to be 0 - waitForAMoment().until(new Predicate() { + waitForPageSilence(false); + } + + /** + * Wait for any AJAX/timeout requests to return. + */ + private void waitForPageSilence(boolean includingTimeouts) { + final String script; + if (includingTimeouts) { + script = "return XMLHttpRequest.active + window.timeoutCounter"; + } else { + script = "return XMLHttpRequest.active"; + } + // Wait for AJAX/timeout requests to be 0 + waitForAMoment().withMessage("page silence").until(new Predicate() { @Override public boolean apply(WebDriver input) { - int ajaxCalls; - int jQueryCalls; - try { - jQueryCalls = Integer.parseInt( - ((JavascriptExecutor) getDriver()) - .executeScript("return jQuery.active") - .toString() - ); - } - catch (WebDriverException jCall) { - jQueryCalls = 0; + Long outstanding = (Long) getExecutor().executeScript(script); + if (outstanding == null) { + if (log.isWarnEnabled()) { + String url = getDriver().getCurrentUrl(); + String pageSource = ShortString.shorten(getDriver().getPageSource(), 2000); + log.warn("XMLHttpRequest.active is null. Is AjaxCounterBean missing? URL: {}\nPartial page source follows:\n{}", url, pageSource); + } + return true; } - - try { - ajaxCalls = Integer.parseInt( - ((JavascriptExecutor) getDriver()) - .executeScript( - "return Ajax.activeRequestCount") - .toString() - ); + if (outstanding < 0) { + throw new RuntimeException("XMLHttpRequest.active " + + "and/or window.timeoutCounter " + + "is negative. Please ensure that " + + "AjaxCounterBean's script is run before " + + "any other JavaScript in the page."); } - catch (WebDriverException jCall) { - ajaxCalls = 0; + int expected = getExpectedBackgroundRequests(); + if (outstanding < expected) { + log.warn("Expected at least {} background requests, but actual count is {}", expected, outstanding, new Throwable()); + } else { + log.debug("Waiting: outstanding = {}, expected = {}", outstanding, expected); } - return ajaxCalls + jQueryCalls == 0; + return outstanding <= expected; } }); } @@ -194,13 +301,15 @@ public boolean apply(WebDriver input) { * @return target WebElement */ public WebElement waitForWebElement(final By elementBy) { + String msg = "element ready " + elementBy; + logWaiting(msg); waitForPageSilence(); - return waitForAMoment().until(new Function() { + return waitForAMoment().withMessage(msg).until(new Function() { @Override public WebElement apply(WebDriver input) { - WebElement targetElement = waitForElementExists(elementBy); + WebElement targetElement = getDriver().findElement(elementBy); if (!elementIsReady(targetElement)) { - throw new NoSuchElementException("Waiting for element"); + return null; } return targetElement; } @@ -215,14 +324,15 @@ public WebElement apply(WebDriver input) { */ public WebElement waitForWebElement(final WebElement parentElement, final By elementBy) { + String msg = "element ready " + elementBy; + logWaiting(msg); waitForPageSilence(); - return waitForAMoment().until(new Function() { + return waitForAMoment().withMessage(msg).until(new Function() { @Override public WebElement apply(WebDriver input) { - WebElement targetElement = waitForElementExists(parentElement, - elementBy); + WebElement targetElement = parentElement.findElement(elementBy); if (!elementIsReady(targetElement)) { - throw new NoSuchElementException("Waiting for element"); + return null; } return targetElement; } @@ -237,8 +347,11 @@ public WebElement apply(WebDriver input) { * @return target WebElement */ public WebElement waitForElementExists(final By elementBy) { + String msg = "element exists " + elementBy; + logWaiting(msg); waitForPageSilence(); - return waitForAMoment().until(new Function() { + return waitForAMoment().withMessage(msg).until( + new Function() { @Override public WebElement apply(WebDriver input) { return getDriver().findElement(elementBy); @@ -255,8 +368,10 @@ public WebElement apply(WebDriver input) { */ public WebElement waitForElementExists(final WebElement parentElement, final By elementBy) { + String msg = "element exists " + elementBy; + logWaiting(msg); waitForPageSilence(); - return waitForAMoment().until(new Function() { + return waitForAMoment().withMessage(msg).until(new Function() { @Override public WebElement apply(WebDriver input) { return parentElement.findElement(elementBy); diff --git a/functional-test/src/main/java/org/zanata/page/BasePage.java b/functional-test/src/main/java/org/zanata/page/BasePage.java index 4936cdae8b..c13ec85138 100644 --- a/functional-test/src/main/java/org/zanata/page/BasePage.java +++ b/functional-test/src/main/java/org/zanata/page/BasePage.java @@ -104,14 +104,16 @@ private void clickNavMenuItem(final WebElement menuItem) { getDriver().findElement(By.id("nav-main")) .findElement(By.tagName("a")).click(); } - waitForAMoment().until(new Predicate() { - @Override - public boolean apply(WebDriver input) { - return menuItem.isDisplayed(); - } - }); + waitForAMoment().withMessage("displayed: " + menuItem).until( + new Predicate() { + @Override + public boolean apply(WebDriver input) { + return menuItem.isDisplayed(); + } + }); // The notifications can sometimes get in the way - waitForAMoment().until(ExpectedConditions.elementToBeClickable(menuItem)); + waitForAMoment().withMessage("clickable: " + menuItem).until( + ExpectedConditions.elementToBeClickable(menuItem)); menuItem.click(); } @@ -230,14 +232,12 @@ public

P goToPage(String navLinkText, Class

pageClass) { * @param locator */ public void clickLinkAfterAnimation(By locator) { - JavascriptExecutor executor = (JavascriptExecutor) getDriver(); - executor.executeScript("arguments[0].click();", getDriver() + getExecutor().executeScript("arguments[0].click();", getDriver() .findElement(locator)); } public void clickLinkAfterAnimation(WebElement element) { - JavascriptExecutor executor = (JavascriptExecutor) getDriver(); - executor.executeScript("arguments[0].click();", element); + getExecutor().executeScript("arguments[0].click();", element); } public HelpPage goToHelp() { @@ -263,8 +263,9 @@ public ProjectsPage submitSearch() { } public BasePage waitForSearchListContains(final String expected) { - log.info("Wait for Project search list contains {}", expected); - waitForAMoment().until(new Predicate() { + String msg = "Project search list contains " + expected; + logWaiting(msg); + waitForAMoment().withMessage(msg).until(new Predicate() { @Override public boolean apply(WebDriver input) { return getProjectSearchAutocompleteItems().contains(expected); @@ -281,29 +282,34 @@ public List getProjectSearchAutocompleteItems() { public ProjectVersionsPage clickSearchEntry(final String searchEntry) { log.info("Click Projects search result {}", searchEntry); + String msg = "search result " + searchEntry; WebElement searchItem = - waitForAMoment().until(new Function() { - @Override - public WebElement apply(WebDriver driver) { - List items = - WebElementUtil.getSearchAutocompleteResults( - driver, "general-search-form", - "projectAutocomplete"); - - for (WebElement item : items) { - if (item.getText().equals(searchEntry)) { - return item; + waitForAMoment().withMessage(msg).until( + new Function() { + @Override + public WebElement apply(WebDriver driver) { + List items = + WebElementUtil + .getSearchAutocompleteResults( + driver, + "general-search-form", + "projectAutocomplete"); + + for (WebElement item : items) { + if (item.getText().equals(searchEntry)) { + return item; + } + } + return null; } - } - return null; - } - }); + }); searchItem.click(); return new ProjectVersionsPage(getDriver()); } public void clickWhenTabEnabled(final WebElement tab) { - waitForAMoment().until(new Predicate() { + String msg = "Clickable tab: " + tab; + waitForAMoment().withMessage(msg).until(new Predicate() { @Override public boolean apply(WebDriver input) { waitForPageSilence(); @@ -314,7 +320,7 @@ public boolean apply(WebDriver input) { tab.click(); clicked = true; } - } catch(WebDriverException wde) { + } catch (WebDriverException wde) { return false; } return clicked; @@ -323,10 +329,15 @@ public boolean apply(WebDriver input) { } public String getHtmlSource(WebElement webElement) { - return (String) ((JavascriptExecutor) getDriver()).executeScript( + return (String) getExecutor().executeScript( "return arguments[0].innerHTML;", webElement); } + + public boolean isValid() { + return (getDriver().findElements(By.id("home"))).size() > 0; + } + public void clickElement(By findby) { scrollIntoView(waitForWebElement(findby)); waitForWebElement(findby).click(); diff --git a/functional-test/src/main/java/org/zanata/page/CorePage.java b/functional-test/src/main/java/org/zanata/page/CorePage.java index 3e8d2aab58..a114a744ea 100644 --- a/functional-test/src/main/java/org/zanata/page/CorePage.java +++ b/functional-test/src/main/java/org/zanata/page/CorePage.java @@ -81,7 +81,7 @@ protected void clickAndExpectErrors(WebElement button) { public boolean apply(WebDriver input) { return getErrors().size() > 0; } - }); + }, "errors > 0"); } public List getErrors() { @@ -115,49 +115,47 @@ public List getErrors(final int expectedNumber) { public boolean apply(WebDriver input) { return getErrors().size() == expectedNumber; } - }); + }, "errors = " + expectedNumber); return getErrors(); } /** - * Wait until an expected error is visible + * Wait until at least one error is visible * - * @param expected The expected error string * @return The full list of visible errors */ - public List expectError(final String expected) { - log.info("Expect error {}", expected); - waitForAMoment().until(new Predicate() { - @Override - public boolean apply(WebDriver input) { - return getErrors().contains(expected); - } - }); + public List expectErrors() { + waitForPageSilence(); return getErrors(); } + public String getNotificationMessage(By elementBy) { + log.info("Query notification message: " + elementBy); + WebElement message = waitForElementExists(elementBy); + return message.getText(); + } + public String getNotificationMessage() { - log.info("Query notification message"); - List messages = waitForElementExists(By.id("messages")) - .findElements(By.tagName("li")); - return messages.size() > 0 ? messages.get(0).getText() : ""; + return getNotificationMessage(By.cssSelector("#messages li")); } public boolean expectNotification(final String notification) { - log.info("Wait for notification {}", notification); - return waitForAMoment().until(new Function() { - @Override - public Boolean apply(WebDriver driver) { - List messages = getDriver() - .findElement(By.id("messages")) - .findElements(By.tagName("li")); - List notifications = new ArrayList(); - for (WebElement message : messages) { - notifications.add(message.getText().trim()); - } - return notifications.contains(notification); - } - }); + String msg = "notification " + notification; + logWaiting(msg); + return waitForAMoment().withMessage(msg).until( + new Function() { + @Override + public Boolean apply(WebDriver driver) { + List messages = getDriver() + .findElement(By.id("messages")) + .findElements(By.tagName("li")); + List notifications = new ArrayList(); + for (WebElement message : messages) { + notifications.add(message.getText().trim()); + } + return notifications.contains(notification); + } + }); } public void assertNoCriticalErrors() { @@ -193,6 +191,15 @@ public void defocus() { } } + /** + * Force the blur 'unfocus' process on a given element + */ + public void defocus(By elementBy) { + log.info("Force unfocus"); + WebElement element = getDriver().findElement(elementBy); + getExecutor().executeScript("arguments[0].blur()", element); + } + /* The system sometimes moves too fast for the Ajax pages, so provide a * pause */ @@ -205,11 +212,11 @@ public void slightPause() { } public void scrollIntoView(WebElement targetElement) { - ((JavascriptExecutor) getDriver()).executeScript( + getExecutor().executeScript( "arguments[0].scrollIntoView(true);", targetElement); } public void scrollToTop() { - ((JavascriptExecutor) getDriver()).executeScript("scroll(0, 0);"); + getExecutor().executeScript("scroll(0, 0);"); } } diff --git a/functional-test/src/main/java/org/zanata/page/WebDriverFactory.java b/functional-test/src/main/java/org/zanata/page/WebDriverFactory.java index c8c333f255..656e7ebc88 100644 --- a/functional-test/src/main/java/org/zanata/page/WebDriverFactory.java +++ b/functional-test/src/main/java/org/zanata/page/WebDriverFactory.java @@ -23,20 +23,29 @@ import java.io.File; import java.io.IOException; import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import org.openqa.selenium.JavascriptExecutor; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriverService; import org.openqa.selenium.firefox.FirefoxBinary; import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.selenium.firefox.FirefoxProfile; +import org.openqa.selenium.logging.LogEntries; +import org.openqa.selenium.logging.LogEntry; +import org.openqa.selenium.logging.LogType; +import org.openqa.selenium.logging.LoggingPreferences; +import org.openqa.selenium.logging.Logs; import org.openqa.selenium.remote.Augmenter; +import org.openqa.selenium.remote.CapabilityType; import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.remote.RemoteWebDriver; import org.openqa.selenium.remote.service.DriverService; import org.openqa.selenium.support.events.EventFiringWebDriver; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.zanata.util.PropertiesHolder; import com.google.common.base.Strings; -import com.google.common.collect.ImmutableMap; import lombok.extern.slf4j.Slf4j; import org.zanata.util.ScreenshotDirForTest; import org.zanata.util.TestEventForScreenshotListener; @@ -55,12 +64,11 @@ public enum WebDriverFactory { private DriverService driverService; private TestEventForScreenshotListener eventListener; private int webdriverWait; - public WebDriver getDriver() { return driver; } - public WebDriver createDriver() { + private WebDriver createDriver() { WebDriver driver = createPlainDriver(); webdriverWait = Integer.parseInt(PropertiesHolder .getProperty(webDriverWait.value())); @@ -69,6 +77,61 @@ public WebDriver createDriver() { return driver; } + private String[] getLogTypes() { + return new String[]{ + LogType.BROWSER, + // CLIENT doesn't seem to log anything +// LogType.CLIENT, + // useful, but verbose: +// LogType.DRIVER, +// LogType.PERFORMANCE, + // PROFILER and SERVER don't seem to work +// LogType.PROFILER, +// LogType.SERVER + }; + } + + /** + * Retrieves all the outstanding WebDriver logs of the specified type. + * @param type a log type from org.openqa.selenium.logging.LogType + * (but they don't all seem to work) + */ + public LogEntries getLogs(String type) { + Logs logs = getDriver().manage().logs(); + LogEntries logEntries = logs.get(type); + return logEntries; + } + + /** + * Logs all the outstanding WebDriver logs of the specified type. + * @param type a log type from org.openqa.selenium.logging.LogType + * (but they don't all seem to work) + */ + public void logLogs(String type) { + String logName = WebDriverFactory.class.getName() + "." + type; + Logger log = LoggerFactory.getLogger(logName); + int logCount = 0; + for (LogEntry logEntry : getLogs(type)) { + ++logCount; + if (logEntry.getLevel().intValue() >= Level.SEVERE.intValue()) { + log.error(logEntry.toString()); + } else if (logEntry.getLevel().intValue() >= Level.WARNING.intValue()) { + log.warn(logEntry.toString()); + } else { + log.info(logEntry.toString()); + } + } + if (logCount == 0) { + log.info("no messages found for LogType.{}", type); + } + } + + public void logLogs() { + for (String type : getLogTypes()) { + logLogs(type); + } + } + public String getHostUrl() { return PropertiesHolder.getProperty(zanataInstance.value()); } @@ -107,24 +170,15 @@ private WebDriver createPlainDriver() { } private WebDriver configureChromeDriver() { - driverService = - new ChromeDriverService.Builder() - .usingDriverExecutable( - new File(PropertiesHolder.properties - .getProperty("webdriver.chrome.driver"))) - .usingAnyFreePort() - .withEnvironment( - ImmutableMap - .of("DISPLAY", - PropertiesHolder.properties - .getProperty("webdriver.display"))) - .withLogFile( - new File(PropertiesHolder.properties - .getProperty("webdriver.log"))).build(); + System.setProperty(ChromeDriverService.CHROME_DRIVER_LOG_PROPERTY, + PropertiesHolder.getProperty("webdriver.log")); + driverService = ChromeDriverService.createDefaultService(); DesiredCapabilities capabilities = DesiredCapabilities.chrome(); capabilities .setCapability("chrome.binary", PropertiesHolder.properties .getProperty("webdriver.chrome.bin")); + enableLogging(capabilities); + try { driverService.start(); } catch (IOException e) { @@ -147,13 +201,22 @@ private WebDriver configureFirefoxDriver() { } else { firefoxBinary = new FirefoxBinary(); } + DesiredCapabilities capabilities = DesiredCapabilities.firefox(); + enableLogging(capabilities); + /* * TODO: Evaluate current timeout Timeout the connection in 30 seconds * firefoxBinary.setTimeout(TimeUnit.SECONDS.toMillis(30)); */ - firefoxBinary.setEnvironmentProperty("DISPLAY", - PropertiesHolder.properties.getProperty("webdriver.display")); - return new FirefoxDriver(firefoxBinary, makeFirefoxProfile()); + return new FirefoxDriver(firefoxBinary, makeFirefoxProfile(), capabilities); + } + + private void enableLogging(DesiredCapabilities capabilities) { + LoggingPreferences logs = new LoggingPreferences(); + for (String type : getLogTypes()) { + logs.enable(type, Level.INFO); + } + capabilities.setCapability(CapabilityType.LOGGING_PREFS, logs); } private FirefoxProfile makeFirefoxProfile() { diff --git a/functional-test/src/main/java/org/zanata/page/account/EditProfilePage.java b/functional-test/src/main/java/org/zanata/page/account/EditProfilePage.java index f55adc9acd..addb2d5246 100644 --- a/functional-test/src/main/java/org/zanata/page/account/EditProfilePage.java +++ b/functional-test/src/main/java/org/zanata/page/account/EditProfilePage.java @@ -47,7 +47,7 @@ public EditProfilePage enterName(String name) { log.info("Enter name {}", name); waitForWebElement(nameField).clear(); waitForWebElement(nameField).sendKeys(name); - defocus(); + defocus(nameField); return new EditProfilePage(getDriver()); } @@ -62,7 +62,7 @@ public EditProfilePage enterEmail(String email) { log.info("Enter email {}", email); waitForWebElement(emailField).clear(); waitForWebElement(emailField).sendKeys(email); - defocus(); + defocus(emailField); return new EditProfilePage(getDriver()); } diff --git a/functional-test/src/main/java/org/zanata/page/account/RegisterPage.java b/functional-test/src/main/java/org/zanata/page/account/RegisterPage.java index 110960e899..f78fbc8d66 100644 --- a/functional-test/src/main/java/org/zanata/page/account/RegisterPage.java +++ b/functional-test/src/main/java/org/zanata/page/account/RegisterPage.java @@ -53,7 +53,7 @@ public class RegisterPage extends CorePage { private By nameField = By.id("loginForm:name"); private By emailField = By.id("loginForm:emailField:email"); - private By usernameField = By.id("loginForm:usernameField:username"); + public By usernameField = By.id("loginForm:usernameField:username"); private By passwordField = By.id("loginForm:passwordField:password"); private By signUpButton = By.xpath("//input[@value='Sign Up']"); private By showHideToggleButton = By.className("js-form-password-toggle"); diff --git a/functional-test/src/main/java/org/zanata/page/account/ResetPasswordPage.java b/functional-test/src/main/java/org/zanata/page/account/ResetPasswordPage.java index e930616a6f..04c16be537 100644 --- a/functional-test/src/main/java/org/zanata/page/account/ResetPasswordPage.java +++ b/functional-test/src/main/java/org/zanata/page/account/ResetPasswordPage.java @@ -24,6 +24,7 @@ import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.zanata.page.BasePage; +import org.zanata.page.utility.HomePage; /** * @author Damian Jansen () { + @Override + public boolean apply(WebDriver driver) { + List < WebElement > searchResults = + WebElementUtil.getSearchAutocompleteResults( + driver, + "addLanguageForm", + "localeAutocomplete"); + boolean clickedLanguage = false; + for (WebElement searchResult : searchResults) { + if (searchResult.getText().contains(language)) { + searchResult.click(); + clickedLanguage = true; + break; + } + } + return clickedLanguage; + } + }); return new AddLanguagePage(getDriver()); } - public AddLanguagePage disableLanguageByDefault() { - log.info("Click Disable by default"); - if (waitForWebElement(enabledByDefaultCheckbox).isSelected()) { - waitForWebElement(enabledByDefaultCheckbox).click(); + public AddLanguagePage waitForPluralsWarning() { + log.info("Expect plurals warning"); + waitForWebElement(pluralsWarning); + return new AddLanguagePage(getDriver()); + } + + public AddLanguagePage enableLanguageByDefault(boolean enable) { + log.info("Click Enable by default to:" + enable); + Checkbox enabledByDefault = Checkbox.of(waitForWebElement(enabledByDefaultCheckbox)); + + if (enable) { + enabledByDefault.check(); + } else { + enabledByDefault.uncheck(); } + return new AddLanguagePage(getDriver()); } @@ -79,25 +106,27 @@ public Map getLanguageDetails() { waitForAMoment().until(new Predicate() { @Override public boolean apply(WebDriver input) { - List thisElement = getDriver() - .findElements(By.className("prop")); - return !thisElement.get(NAME_ROW) - .findElements(By.tagName("span")) - .get(VALUE_COLUMN).getText().isEmpty(); + return !waitForElementExists(languageInfo) + .findElements(By.className("l--push-top-half")) + .get(0).findElement(languageInfoItem) + .getText().isEmpty(); } }); - for (WebElement item : getDriver().findElements(By.className("prop"))) { - map.put(item.findElements(By.tagName("span")) - .get(NAME_COLUMN).getText(), - item.findElements(By.tagName("span")) - .get(VALUE_COLUMN).getText()); + for (WebElement item : waitForElementExists(languageInfo) + .findElements(By.className("l--push-top-half"))) { + String name = item.getText(); + String value = item.findElement(languageInfoItem).getText(); + // Truncate name at value + int cutoff = name.lastIndexOf(value); + name = name.substring(0, cutoff).trim(); + map.put(name, value); } return map; } - public ManageLanguagePage saveLanguage() { + public LanguagesPage saveLanguage() { log.info("Click Save"); clickAndCheckErrors(waitForWebElement(saveButton)); - return new ManageLanguagePage(getDriver()); + return new LanguagesPage(getDriver()); } } diff --git a/functional-test/src/main/java/org/zanata/page/administration/AdministrationPage.java b/functional-test/src/main/java/org/zanata/page/administration/AdministrationPage.java index 245ed6f075..61f95c7aef 100644 --- a/functional-test/src/main/java/org/zanata/page/administration/AdministrationPage.java +++ b/functional-test/src/main/java/org/zanata/page/administration/AdministrationPage.java @@ -53,12 +53,6 @@ public ServerConfigurationPage goToServerConfigPage() { return new ServerConfigurationPage(getDriver()); } - public ManageLanguagePage goToManageLanguagePage() { - log.info("Click Manage Languages"); - clickLinkAfterAnimation(MANAGE_LANGUAGE_LINK); - return new ManageLanguagePage(getDriver()); - } - public ManageUserPage goToManageUserPage() { log.info("Click Manage Users"); clickLinkAfterAnimation(MANAGE_USER_LINK); diff --git a/functional-test/src/main/java/org/zanata/page/administration/EditRoleAssignmentPage.java b/functional-test/src/main/java/org/zanata/page/administration/EditRoleAssignmentPage.java index f3656ebfdd..5ac7253d09 100644 --- a/functional-test/src/main/java/org/zanata/page/administration/EditRoleAssignmentPage.java +++ b/functional-test/src/main/java/org/zanata/page/administration/EditRoleAssignmentPage.java @@ -32,11 +32,11 @@ @Slf4j public class EditRoleAssignmentPage extends BasePage { - private By policySelect = By.id("projectForm:policyNameField:policyName"); - private By patternField = By.id("projectForm:identityPatternField:identityPattern"); - private By roleSelect = By.id("projectForm:roleField:roles"); - private By saveButton = By.id("projectForm:save"); - private By cancelButton = By.id("projectForm:cancel"); + private By policySelect = By.id("role-rule-form:policyNameField:policyName"); + private By patternField = By.id("role-rule-form:identityPatternField:identityPattern"); + private By roleSelect = By.id("role-rule-form:roleField:roles"); + private By saveButton = By.id("role-rule-form:save"); + private By cancelButton = By.id("role-rule-form:cancel"); public EditRoleAssignmentPage(WebDriver driver) { super(driver); @@ -61,10 +61,10 @@ public EditRoleAssignmentPage selectRole(String role) { return new EditRoleAssignmentPage(getDriver()); } - public EditRoleAssignmentPage saveRoleAssignment() { + public RoleAssignmentsPage saveRoleAssignment() { log.info("Click Save"); waitForWebElement(saveButton).click(); - return new EditRoleAssignmentPage(getDriver()); + return new RoleAssignmentsPage(getDriver()); } public RoleAssignmentsPage cancelEditRoleAssignment() { diff --git a/functional-test/src/main/java/org/zanata/page/administration/ManageLanguagePage.java b/functional-test/src/main/java/org/zanata/page/administration/ManageLanguagePage.java deleted file mode 100644 index 2b43783982..0000000000 --- a/functional-test/src/main/java/org/zanata/page/administration/ManageLanguagePage.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2010, Red Hat, Inc. and individual contributors as indicated by the - * @author tags. See the copyright.txt file in the distribution for a full - * listing of individual contributors. - * - * This is free software; you can redistribute it and/or modify it under the - * terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This software is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this software; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF - * site: http://www.fsf.org. - */ -package org.zanata.page.administration; - -import com.google.common.base.Function; -import com.google.common.base.Optional; -import com.google.common.base.Predicate; -import com.google.common.collect.Iterables; -import lombok.extern.slf4j.Slf4j; -import org.openqa.selenium.By; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.zanata.page.BasePage; -import org.zanata.util.TableRow; -import org.zanata.util.WebElementUtil; - -import java.util.List; - -@Slf4j -public class ManageLanguagePage extends BasePage { - - public static final int LOCALE_COLUMN = 0; - public static final int ENABLED_COLUMN = 3; - - private By languageTable = By.id("languageForm:threads"); - private By addLanguageButton = By.linkText("Add New Language"); - - public ManageLanguagePage(WebDriver driver) { - super(driver); - } - - public AddLanguagePage addNewLanguage() { - waitForWebElement(addLanguageButton).click(); - return new AddLanguagePage(getDriver()); - } - - public ManageLanguageTeamMemberPage manageTeamMembersFor( - final String localeId) { - log.info("Click team {}", localeId); - TableRow matchedRow = findRowByLocale(localeId); - - log.debug("for locale [{}] found table row: {}", localeId, matchedRow); - List cells = matchedRow.getCells(); - int teamMemberCellIndex = cells.size() - 1; - WebElement teamMemberCell = cells.get(teamMemberCellIndex); - WebElement teamMemberButton = - teamMemberCell.findElement(By - .xpath(".//input[@value='Team Members']")); - teamMemberButton.click(); - return new ManageLanguageTeamMemberPage(getDriver()); - } - - private TableRow findRowByLocale(final String localeId) { - TableRow matchedRow = - waitForAMoment().until(new Function() { - @Override - public TableRow apply(WebDriver driver) { - List tableRows = - WebElementUtil.getTableRows(getDriver(), - languageTable); - Optional matchedRow = - Iterables.tryFind(tableRows, - new Predicate() { - @Override - public boolean - apply(TableRow input) { - List cellContents = - input.getCellContents(); - String localeCell = - cellContents.get( - LOCALE_COLUMN) - .trim(); - return localeCell - .equalsIgnoreCase(localeId); - } - }); - - // we keep looking for the locale until timeout - return matchedRow.isPresent() ? matchedRow.get() : null; - } - }); - return matchedRow; - } - - public ManageLanguagePage enableLanguageByDefault(String localeId) { - log.info("Click to enable {}", localeId); - TableRow matchedRow = findRowByLocale(localeId); - - WebElement enabledCell = matchedRow.getCells().get(ENABLED_COLUMN); - WebElement enabledCheckbox = - enabledCell.findElement(By.tagName("input")); - if (!enabledCheckbox.isSelected()) { - enabledCheckbox.click(); - getDriver().switchTo().alert().accept(); - } - - return new ManageLanguagePage(getDriver()); - } - - public boolean languageIsEnabled(String localeId) { - log.info("Query is language enabled {}", localeId); - return findRowByLocale(localeId).getCells().get(ENABLED_COLUMN) - .findElement(By.tagName("input")).isSelected(); - } - - public List getLanguageLocales() { - log.info("Query list of languages"); - return WebElementUtil.getColumnContents(getDriver(), languageTable, - LOCALE_COLUMN); - } -} diff --git a/functional-test/src/main/java/org/zanata/page/administration/ManageLanguageTeamMemberPage.java b/functional-test/src/main/java/org/zanata/page/administration/ManageLanguageTeamMemberPage.java deleted file mode 100644 index ecb115c50b..0000000000 --- a/functional-test/src/main/java/org/zanata/page/administration/ManageLanguageTeamMemberPage.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright 2014, Red Hat, Inc. and individual contributors as indicated by the - * @author tags. See the copyright.txt file in the distribution for a full - * listing of individual contributors. - * - * This is free software; you can redistribute it and/or modify it under the - * terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This software is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this software; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF - * site: http://www.fsf.org. - */ -package org.zanata.page.administration; - -import com.google.common.base.Function; -import com.google.common.base.Predicate; -import lombok.extern.slf4j.Slf4j; -import org.openqa.selenium.By; -import org.openqa.selenium.NoSuchElementException; -import org.openqa.selenium.WebDriver; -import org.zanata.page.BasePage; -import org.zanata.util.Checkbox; -import org.zanata.util.TableRow; -import org.zanata.util.WebElementUtil; - -import java.util.Collections; -import java.util.List; - -/** - * @author Patrick Huang pahuang@redhat.com - */ -@Slf4j -public class ManageLanguageTeamMemberPage extends BasePage { - - private By memberPanel = By.id("memberPanel"); - private By memberPanelRows = By.id("memberPanel:threads"); - private By joinLanguageTeamButton = By.linkText("Join Language Team"); - private By addTeamMemberButton = By.id("addTeamMemberLink"); - private By addUserPanel = By.id("userAddPanel_container"); - private By addUserSearchInput = By.id("searchForm:searchField"); - private By addUserSearchButton = By.id("searchForm:searchBtn"); - private By personTable = By.id("resultForm:personTable"); - private By addSelectedButton = By.id("resultForm:addSelectedBtn"); - private By closeSearchButton = By.id("searchForm:closeBtn"); - - public static final int USERNAME_COLUMN = 0; - public static final int SEARCH_RESULT_PERSON_COLUMN = 0; - public static final int ISTRANSLATOR_COLUMN = 2; - - public ManageLanguageTeamMemberPage(WebDriver driver) { - super(driver); - } - - private String getMembersInfo() { - log.info("Query members info"); - return waitForWebElement(memberPanel) - .findElement(By.xpath(".//p")).getText(); - } - - public List getMemberUsernames() { - log.info("Query username list"); - if (getMembersInfo().contains("0 members")) { - log.info("no members yet for this language"); - return Collections.emptyList(); - } - List usernameColumn = WebElementUtil.getColumnContents( - getDriver(), - memberPanelRows, - USERNAME_COLUMN); - log.info("username column: {}", usernameColumn); - return usernameColumn; - } - - public ManageLanguageTeamMemberPage joinLanguageTeam() { - log.info("Click Join"); - waitForWebElement(joinLanguageTeamButton).click(); - // we need to wait for this join to finish before returning the page - waitForAMoment().until(new Function() { - @Override - public Boolean apply(WebDriver driver) { - return driver.findElements(joinLanguageTeamButton).isEmpty(); - } - }); - return new ManageLanguageTeamMemberPage(getDriver()); - } - - public ManageLanguageTeamMemberPage clickAddTeamMember() { - log.info("Click Add Team Member"); - waitForWebElement(addTeamMemberButton).click(); - return this; - } - - public ManageLanguageTeamMemberPage searchPersonAndAddToTeam( - final String personName) { - log.info("Enter username search {}", personName); - // Wait for the search field under the add user panel - waitForWebElement(waitForElementExists(addUserPanel), addUserSearchInput) - .sendKeys(personName); - - log.info("Click search button"); - waitForWebElement(addUserSearchButton).click(); - - TableRow firstRow = tryGetFirstRowInSearchPersonResult(personName); - - final String personUsername = firstRow.getCellContents() - .get(SEARCH_RESULT_PERSON_COLUMN); - log.info("Username to be added: {}", personUsername); - log.info("Set checked as translator"); - Checkbox.of(firstRow.getCells() - .get(ISTRANSLATOR_COLUMN) - .findElement(By.tagName("input"))) - .check(); - log.info("Click Add Selected"); - waitForWebElement(addSelectedButton).click(); - log.info("Click Close"); - waitForWebElement(closeSearchButton).click(); - return confirmAdded(personName); - } - - private TableRow tryGetFirstRowInSearchPersonResult(final String personName) { - // we want to wait until search result comes back - return waitForAMoment().until(new Function>() { - @Override - public List apply(WebDriver input) { - log.debug("waiting for search result refresh..."); - List tableRows = WebElementUtil - .getTableRows(getDriver(), - waitForWebElement(personTable)); - if (tableRows.isEmpty()) { - log.debug("waiting for search result refresh..."); - throw new NoSuchElementException(""); - } - if (!tableRows.get(0).getCellContents() - .get(SEARCH_RESULT_PERSON_COLUMN).contains(personName)) { - throw new NoSuchElementException("User not in pos 0"); - } - return tableRows; - } - }).get(0); - } - - private ManageLanguageTeamMemberPage confirmAdded( - final String personUsername) { - // we need to wait for the page to refresh - return refreshPageUntil(this, new Predicate() { - @Override - public boolean apply(WebDriver driver) { - List usernameColumn = WebElementUtil - .getColumnContents(getDriver(), - memberPanelRows, - USERNAME_COLUMN); - log.info("username column: {}", usernameColumn); - return usernameColumn.contains(personUsername); - } - }); - } -} diff --git a/functional-test/src/main/java/org/zanata/page/administration/ManageSearchPage.java b/functional-test/src/main/java/org/zanata/page/administration/ManageSearchPage.java index 0132f1ab31..04c995022b 100644 --- a/functional-test/src/main/java/org/zanata/page/administration/ManageSearchPage.java +++ b/functional-test/src/main/java/org/zanata/page/administration/ManageSearchPage.java @@ -42,11 +42,11 @@ public class ManageSearchPage extends BasePage { private static final int SELECT_ALL_COLUMN = 0; - private By classesTable = By.id("form:classList"); + private By classesTable = By.id("form:actions"); private By abortButton = By.id("form:cancel"); private By selectAllButton = By.id("form:selectAll"); private By performButton = By.id("form:reindex"); - private By cancelButton = By.id("form:cancel"); + private By cancelButton = By.linkText("Abort"); private By noOpsLabel = By.id("noOperationsRunning"); private By abortedLabel = By.id("aborted"); private By completedLabel = By.id("completed"); @@ -84,12 +84,13 @@ public ManageSearchPage clickSelectAll() { public boolean allActionsSelected() { log.info("Query all actions selected"); - List tableRows = - WebElementUtil.getTableRows(getDriver(), classesTable); + List tableRows = WebElementUtil.getTableRows(getDriver(), + waitForWebElement(waitForElementExists(classesTable), + By.tagName("table"))); for (TableRow tableRow : tableRows) { // column 2, 3, 4 are checkboxes for purge, reindex and optimize - for (int i = 2; i <= 4; i++) { - WebElement checkBox = tableRow.getCells().get(i).findElement(By.tagName("input")); + for (int i = 1; i <= 3; i++) { + WebElement checkBox = tableRow.getHeaders().get(i).findElement(By.tagName("input")); if (!Checkbox.of(checkBox).checked()) { return false; } diff --git a/functional-test/src/main/java/org/zanata/page/administration/ManageUserAccountPage.java b/functional-test/src/main/java/org/zanata/page/administration/ManageUserAccountPage.java index b3edc098e4..4255b03267 100644 --- a/functional-test/src/main/java/org/zanata/page/administration/ManageUserAccountPage.java +++ b/functional-test/src/main/java/org/zanata/page/administration/ManageUserAccountPage.java @@ -40,7 +40,7 @@ public class ManageUserAccountPage extends BasePage { private By passwordField = By.id("userdetailForm:passwordField:password"); private By passwordConfirmField = By.id("userdetailForm:passwordConfirmField:confirm"); - private By enabledField = By.id("userdetailForm:enabledField:enabled"); + private By enabledField = By.id("userdetailForm:enabled"); private By saveButton = By.id("userdetailForm:userdetailSave"); private By cancelButton = By.id("userdetailForm:userdetailCancel"); diff --git a/functional-test/src/main/java/org/zanata/page/administration/ManageUserPage.java b/functional-test/src/main/java/org/zanata/page/administration/ManageUserPage.java index 60ef9f0721..5122b66f3f 100644 --- a/functional-test/src/main/java/org/zanata/page/administration/ManageUserPage.java +++ b/functional-test/src/main/java/org/zanata/page/administration/ManageUserPage.java @@ -32,6 +32,7 @@ import org.zanata.util.TableRow; import org.zanata.util.WebElementUtil; +import java.util.ArrayList; import java.util.List; /** @@ -41,11 +42,7 @@ @Slf4j public class ManageUserPage extends BasePage { - public static final int USERNAME_COLUMN = 0; - public static final int EDITBUTTON_COLUMN = 4; - - private By userTable = By.id("usermanagerForm:userList"); - private By userEditButton = By.xpath(".//button[contains(text(), 'Edit')]"); + private By userTable = By.id("usermanagerForm"); public ManageUserPage(WebDriver driver) { super(driver); @@ -53,41 +50,41 @@ public ManageUserPage(WebDriver driver) { public ManageUserAccountPage editUserAccount(String username) { log.info("Click edit on {}", username); - WebElement editCell = findRowByUserName(username).getCells() - .get(EDITBUTTON_COLUMN); - waitForWebElement(editCell, userEditButton).click(); + findRowByUserName(username).click(); return new ManageUserAccountPage(getDriver()); } - private TableRow findRowByUserName(final String username) { - return waitForAMoment().until(new Function() { - @Override - public TableRow apply(WebDriver driver) { - List tableRows = WebElementUtil - .getTableRows(getDriver(), - waitForWebElement(userTable)); - Optional matchedRow = Iterables.tryFind(tableRows, - new Predicate() { - @Override - public boolean apply(TableRow input) { - List cellContents = - input.getCellContents(); - String localeCell = cellContents - .get(USERNAME_COLUMN) - .trim(); - return localeCell.equalsIgnoreCase(username); - } - }); - - // we keep looking for the user until timeout - return matchedRow.isPresent() ? matchedRow.get() : null; + private WebElement findRowByUserName(final String username) { + for (WebElement listItem : getRows()) { + if (getListItemUsername(listItem).equals(username)) { + return listItem; } - }); + } + return null; + } + + public List getRows() { + return waitForWebElement(userTable) + .findElements(By.className("list__item--actionable")); + } + + public String getListItemUsername(WebElement listItem) { + String listItemText = listItem.findElement(By.tagName("h3")).getText(); + return listItemText.substring(0, listItemText + .lastIndexOf(getListItemRoles(listItem))).trim(); + } + + public String getListItemRoles(WebElement listItem) { + return listItem.findElement(By.tagName("h3")) + .findElement(By.className("txt--meta")).getText(); } public List getUserList() { log.info("Query user list"); - return WebElementUtil.getColumnContents(getDriver(), userTable, - USERNAME_COLUMN); + List names = new ArrayList<>(); + for (WebElement element : getRows()) { + names.add(getListItemUsername(element)); + } + return names; } } diff --git a/functional-test/src/main/java/org/zanata/page/administration/RoleAssignmentsPage.java b/functional-test/src/main/java/org/zanata/page/administration/RoleAssignmentsPage.java index 795a872083..5c11fc8c2b 100644 --- a/functional-test/src/main/java/org/zanata/page/administration/RoleAssignmentsPage.java +++ b/functional-test/src/main/java/org/zanata/page/administration/RoleAssignmentsPage.java @@ -37,7 +37,7 @@ public class RoleAssignmentsPage extends BasePage { private By newRuleButton = By.linkText("New Rule"); - private By roleTable = By.tagName("table"); + private By roleTable = By.className("list--stats"); public RoleAssignmentsPage(WebDriver driver) { super(driver); @@ -51,12 +51,12 @@ public EditRoleAssignmentPage clickCreateNew() { public List getRulesByPattern() { log.info("Query role rules"); - List names = new ArrayList(); - List tableRows = WebElementUtil.getTableRows(getDriver(), + List ret = new ArrayList<>(); + List names = WebElementUtil.elementsToText(getDriver(), roleTable); - for (TableRow tableRow : tableRows) { - names.add(tableRow.getCells().get(1).getText()); + for (String name : names) { + ret.add(name.substring(name.lastIndexOf(':') + 1).trim()); } - return names; + return ret; } } diff --git a/functional-test/src/main/java/org/zanata/page/administration/TranslationMemoryPage.java b/functional-test/src/main/java/org/zanata/page/administration/TranslationMemoryPage.java index c4a44096ca..f9066cd43d 100644 --- a/functional-test/src/main/java/org/zanata/page/administration/TranslationMemoryPage.java +++ b/functional-test/src/main/java/org/zanata/page/administration/TranslationMemoryPage.java @@ -30,6 +30,7 @@ import org.zanata.util.TableRow; import org.zanata.util.WebElementUtil; +import java.util.ArrayList; import java.util.List; /** @@ -38,21 +39,25 @@ */ @Slf4j public class TranslationMemoryPage extends BasePage { - private static final int ID_COLUMN = 0; - private static final int DESCRIPTION_COLUMN = 1; - private static final int ENTRIES_COLUMN = 2; - private static final int IMPORT_COLUMN = 4; - private static final int EXPORT_COLUMN = 5; - private static final int ACTIONS_COLUMN = 6; public static final String ID_UNAVAILABLE = "This Id is not available"; public static final String UPLOAD_ERROR = "There was an error uploading the file"; + public static final String NO_MEMORIES = + "No Translation Memories have been created."; + private By listItemCount = By.className("badge"); + private By listItemDescription = By.className("list__item__meta"); + private By dropDownMenu = By.id("moreActions"); private By createTmLink = By.id("createTmLink"); - private By tmList = By.id("main_content:form:tmTable"); + private By tmList = By.id("tmList"); private By filenameInput = By.name("uploadedFile"); private By uploadButton = By.name("uploadBtn"); + private By listDropDownMenu = By.className("dropdown__toggle"); + private By listImportButton = By.linkText("Import"); + private By listExportButton = By.linkText("Export"); + private By listClearButton = By.linkText("Clear"); + private By listDeleteButton = By.linkText("Delete"); public TranslationMemoryPage(WebDriver driver) { super(driver); @@ -60,14 +65,20 @@ public TranslationMemoryPage(WebDriver driver) { public TranslationMemoryEditPage clickCreateNew() { log.info("Click Create New"); - waitForWebElement(createTmLink).click(); + waitForWebElement(dropDownMenu).click(); + clickLinkAfterAnimation(createTmLink); return new TranslationMemoryEditPage(getDriver()); } + public TranslationMemoryPage clickOptions(String tmName) { + log.info("Click Options dropdown for {}", tmName); + waitForWebElement(findRowByTMName(tmName), listDropDownMenu).click(); + return new TranslationMemoryPage(getDriver()); + } + public TranslationMemoryPage clickImport(String tmName) { log.info("Click Import"); - findRowByTMName(tmName).getCells().get(IMPORT_COLUMN) - .findElement(By.tagName("a")).click(); + waitForWebElement(findRowByTMName(tmName), listImportButton).click(); return new TranslationMemoryPage(getDriver()); } @@ -92,25 +103,29 @@ public Alert expectFailedUpload() { public TranslationMemoryPage clickClearTMAndAccept(String tmName) { log.info("Click and accept Clear {}", tmName); - clickTMAction(tmName, 0).accept(); + waitForWebElement(findRowByTMName(tmName), listClearButton).click(); + switchToAlert().accept(); return new TranslationMemoryPage(getDriver()); } public TranslationMemoryPage clickClearTMAndCancel(String tmName) { log.info("Click and Cancel Clear {}", tmName); - clickTMAction(tmName, 0).dismiss(); + waitForWebElement(findRowByTMName(tmName), listClearButton).click(); + switchToAlert().dismiss(); return new TranslationMemoryPage(getDriver()); } public TranslationMemoryPage clickDeleteTmAndAccept(String tmName) { log.info("Click and accept Delete {}", tmName); - clickTMAction(tmName, 1).accept(); + waitForWebElement(findRowByTMName(tmName), listDeleteButton).click(); + switchToAlert().accept(); return new TranslationMemoryPage(getDriver()); } public TranslationMemoryPage clickDeleteTmAndCancel(String tmName) { log.info("Click and cancel Delete {}", tmName); - clickTMAction(tmName, 1).dismiss(); + waitForWebElement(findRowByTMName(tmName), listDeleteButton).click(); + switchToAlert().dismiss(); return new TranslationMemoryPage(getDriver()); } @@ -122,19 +137,21 @@ public TranslationMemoryPage dismissError() { public List getListedTranslationMemorys() { log.info("Query translation memory names"); - return WebElementUtil.getColumnContents(getDriver(), tmList, - ID_COLUMN); + List names = new ArrayList<>(); + for (WebElement listElement : getTMList()) { + names.add(getListEntryName(listElement)); + } + return names; } public String getDescription(String tmName) { log.info("Query description {}", tmName); - return findRowByTMName(tmName).getCells().get(DESCRIPTION_COLUMN) - .getText(); + return getListEntryDescription(findRowByTMName(tmName)); } public String getNumberOfEntries(String tmName) { log.info("Query number of entries {}", tmName); - return findRowByTMName(tmName).getCells().get(ENTRIES_COLUMN).getText(); + return getListEntryCount(findRowByTMName(tmName)); } public String waitForExpectedNumberOfEntries(final String tmName, @@ -151,40 +168,58 @@ public String apply(WebDriver driver) { public boolean canDelete(String tmName) { log.info("Query can delete {}", tmName); - return findRowByTMName(tmName).getCells().get(ACTIONS_COLUMN) - .findElements(By.tagName("input")).get(1).isEnabled(); + String disabled = waitForWebElement( + findRowByTMName(tmName), listDeleteButton) + .getAttribute("disabled"); + + return null == disabled || disabled.equals("false"); } + /* + * Check to see if the TM list is empty + */ + private boolean noTmsCreated() { + for (WebElement element : waitForWebElement(tmList) + .findElements(By.tagName("p"))) { + if (element.getText().equals(NO_MEMORIES)) { + return true; + } + } + return false; + } /* * Get a row from the TM table that corresponds with tmName */ - private TableRow findRowByTMName(final String tmName) { - return waitForAMoment().until(new Function() { - @Override - public TableRow apply(WebDriver driver) { - List tableRows = WebElementUtil - .getTableRows(getDriver(), tmList); - Optional matchedRow = - Iterables.tryFind(tableRows, new Predicate() { - @Override - public boolean apply(TableRow input) { - List cellContents = input - .getCellContents(); - String localeCell = cellContents.get(ID_COLUMN) - .trim(); - return localeCell.equalsIgnoreCase(tmName); - } - }); - - // Keep looking for the TM entry until timeout - return matchedRow.isPresent() ? matchedRow.get() : null; + private WebElement findRowByTMName(final String tmName) { + for (WebElement listElement : getTMList()) { + if (getListEntryName(listElement).equals(tmName)) { + return listElement; } - }); + } + return null; } - private Alert clickTMAction(String tmName, int position) { - findRowByTMName(tmName).getCells().get(ACTIONS_COLUMN) - .findElements(By.tagName("input")).get(position).click(); - return switchToAlert(); + private List getTMList() { + if (noTmsCreated()) { + log.info("TM list is empty"); + return new ArrayList<>(); + } + return waitForWebElement(waitForWebElement(tmList), + By.className("list--stats")) + .findElements(By.className("list__item--actionable")); + } + + private String getListEntryName(WebElement listElement) { + String title = listElement.findElement(By.tagName("h3")).getText().trim(); + return title.substring(0, title.lastIndexOf(getListEntryCount(listElement))).trim(); + } + + private String getListEntryDescription(WebElement listElement) { + return waitForWebElement(listElement, listItemDescription).getText(); + } + + private String getListEntryCount(WebElement listElement) { + return listElement.findElement(By.tagName("h3")) + .findElement(listItemCount).getText(); } } diff --git a/functional-test/src/main/java/org/zanata/page/dashboard/DashboardBasePage.java b/functional-test/src/main/java/org/zanata/page/dashboard/DashboardBasePage.java index e2a89fb94c..9f3c672763 100644 --- a/functional-test/src/main/java/org/zanata/page/dashboard/DashboardBasePage.java +++ b/functional-test/src/main/java/org/zanata/page/dashboard/DashboardBasePage.java @@ -35,6 +35,11 @@ public class DashboardBasePage extends BasePage { private By activityTab = By.id("activity_tab"); private By projectsTab = By.id("projects_tab"); private By settingsTab = By.id("settings_tab"); + + private By activityTabBody = By.id("activity"); + private By projectsTabBody = By.id("projects"); + private By settingsTabBody = By.id("settings"); + private By settingsAccountTab = By.id("account_tab"); private By settingsProfileTab = By.id("profile_tab"); private By settingsClientTab = By.id("client_tab"); @@ -62,6 +67,7 @@ public String getUserFullName() { public DashboardActivityTab gotoActivityTab() { log.info("Click Activity tab"); + waitForElementExists(activityTabBody); clickWhenTabEnabled(waitForWebElement(activityTab)); return new DashboardActivityTab(getDriver()); } @@ -75,12 +81,14 @@ public boolean activityTabIsSelected() { public DashboardProjectsTab gotoProjectsTab() { log.info("Click Projects tab"); + waitForElementExists(projectsTabBody); clickWhenTabEnabled(waitForWebElement(projectsTab)); return new DashboardProjectsTab(getDriver()); } public DashboardBasePage goToSettingsTab() { log.info("Click Settings tab"); + waitForElementExists(settingsTabBody); clickWhenTabEnabled(waitForWebElement(settingsTab)); return new DashboardBasePage(getDriver()); } diff --git a/functional-test/src/main/java/org/zanata/page/groups/CreateVersionGroupPage.java b/functional-test/src/main/java/org/zanata/page/groups/CreateVersionGroupPage.java index 1d972aa49c..cda863f4eb 100644 --- a/functional-test/src/main/java/org/zanata/page/groups/CreateVersionGroupPage.java +++ b/functional-test/src/main/java/org/zanata/page/groups/CreateVersionGroupPage.java @@ -42,7 +42,7 @@ public class CreateVersionGroupPage extends BasePage { "letters, numbers, periods, underscores and hyphens."; private By groupIdField = By.id("group-form:slugField:slug"); - private By groupNameField = By.id("group-form:nameField:name"); + public By groupNameField = By.id("group-form:nameField:name"); private By groupDescriptionField = By.id("group-form:descriptionField:description"); private By saveButton = By.id("group-form:group-create-new"); private By createNewButton = By.id("group-form:group-create-new"); diff --git a/functional-test/src/main/java/org/zanata/page/groups/VersionGroupPage.java b/functional-test/src/main/java/org/zanata/page/groups/VersionGroupPage.java index e606e5b444..015a3837a5 100644 --- a/functional-test/src/main/java/org/zanata/page/groups/VersionGroupPage.java +++ b/functional-test/src/main/java/org/zanata/page/groups/VersionGroupPage.java @@ -57,6 +57,12 @@ public class VersionGroupPage extends BasePage { private By projectsTab = By.id("projects_tab"); private By maintainersTab = By.id("maintainers_tab"); private By settingsTab = By.id("settings_tab"); + + private By languagesTabBody = By.id("languages"); + private By projectsTabBody = By.id("projects"); + private By maintainersTabBody = By.id("maintainers"); + private By settingsTabBody = By.id("settings"); + private By settingsLanguagesTab = By.id("settings-languages_tab"); public VersionGroupPage(final WebDriver driver) { @@ -146,37 +152,38 @@ public VersionLanguagesPage clickOnProjectVersionLinkOnRow(int row) { return new VersionLanguagesPage(getDriver()); } - public void clickOnTab(String tabId) { - waitForWebElement(By.id(tabId)).click(); - } - public VersionGroupPage clickAddProjectVersionsButton() { log.info("Click Add Project Version"); - waitForWebElement(waitForElementExists(projectForm), //parent + // parent + waitForWebElement(waitForElementExists(projectForm), By.className("button--primary")).click(); return new VersionGroupPage(getDriver()); } public VersionGroupPage clickLanguagesTab() { log.info("Click Languages tab"); + waitForElementExists(languagesTabBody); clickWhenTabEnabled(waitForWebElement(languagesTab)); return new VersionGroupPage(getDriver()); } public VersionGroupPage clickProjectsTab() { log.info("Click Projects tab"); + waitForElementExists(projectsTabBody); clickWhenTabEnabled(waitForWebElement(projectsTab)); return new VersionGroupPage(getDriver()); } public VersionGroupPage clickMaintainersTab() { log.info("Click Maintainers tab"); + waitForElementExists(maintainersTabBody); clickWhenTabEnabled(waitForWebElement(maintainersTab)); return new VersionGroupPage(getDriver()); } public VersionGroupPage clickSettingsTab() { log.info("Click Settings tab"); + waitForElementExists(settingsTabBody); clickWhenTabEnabled(waitForWebElement(settingsTab)); return new VersionGroupPage(getDriver()); } @@ -190,23 +197,23 @@ public VersionGroupPage clickLanguagesSettingsTab() { public Boolean isLanguagesTabActive() { log.info("Query is languages tab displayed"); final WebElement languagesTab = waitForWebElement(By.id("languages")); - waitForAMoment().until( new Predicate() { + waitForAMoment().until(new Predicate() { @Override public boolean apply(WebDriver webDriver) { return languagesTab.getAttribute("class").contains("is-active"); } - } ); + }); return languagesTab.getAttribute("class").contains("is-active"); } public Boolean isProjectsTabActive() { final WebElement languagesTab = waitForElementExists(By.id("projects")); - waitForAMoment().until( new Predicate() { + waitForAMoment().until(new Predicate() { @Override public boolean apply(WebDriver webDriver) { return languagesTab.getAttribute("class").contains("is-active"); } - } ); + }); return languagesTab.getAttribute("class").contains("is-active"); } diff --git a/functional-test/src/main/java/org/zanata/page/languages/ContactTeamPage.java b/functional-test/src/main/java/org/zanata/page/languages/ContactTeamPage.java index 6f7b4e8c06..e2c97ad341 100644 --- a/functional-test/src/main/java/org/zanata/page/languages/ContactTeamPage.java +++ b/functional-test/src/main/java/org/zanata/page/languages/ContactTeamPage.java @@ -24,7 +24,6 @@ import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.zanata.page.BasePage; -import org.zanata.util.WebElementUtil; /** * @author Damian Jansen djansen@redhat.com @@ -48,9 +47,7 @@ public ContactTeamPage enterSubject(String subject) { public ContactTeamPage enterMessage(String message) { log.info("Enter message {}", message); - WebElementUtil.setRichTextEditorContent(getDriver(), - waitForWebElement(messageField), - message); + waitForWebElement(messageField).sendKeys(message); return new ContactTeamPage(getDriver()); } diff --git a/functional-test/src/main/java/org/zanata/page/languages/LanguagePage.java b/functional-test/src/main/java/org/zanata/page/languages/LanguagePage.java index cec02c81bd..e2fa8075c6 100644 --- a/functional-test/src/main/java/org/zanata/page/languages/LanguagePage.java +++ b/functional-test/src/main/java/org/zanata/page/languages/LanguagePage.java @@ -20,10 +20,23 @@ */ package org.zanata.page.languages; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import javax.annotation.Nullable; + import lombok.extern.slf4j.Slf4j; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; import org.zanata.page.BasePage; +import org.zanata.util.Checkbox; + +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.collect.Sets; /** * @author Damian Jansen djansen@redhat.com @@ -33,15 +46,215 @@ public class LanguagePage extends BasePage { private By contactCoordinatorsButton = By.linkText("Contact Team Coordinators"); + private By saveButton = By.id("save-button"); + private By moreActions = By.className("dropdown__toggle"); + private By membersTabMoreActions = By.id("members-more-actions"); + private By enableByDefault = By.id("enable-by-default"); + private By membersTab = By.id("members_tab"); + private By settingsTab = By.id("settings_tab"); + private By joinLanguageTeamButton = By.linkText("Join Language Team"); + private By addTeamMemberLink = By.id("addTeamMemberLink"); + private By addUserSearchInput = By.id("searchForm:searchField"); + private By addUserSearchButton = By.id("searchForm:searchBtn"); + private By personTable = By.id("resultForm:searchResults"); + private By addSelectedButton = By.id("addSelectedBtn"); + + public static final int IS_TRANSLATOR_COLUMN = 0; + public static final int IS_REVIEWER_COLUMN = 1; + public static final int IS_COORDINATOR_COLUMN = 2; + public LanguagePage(WebDriver driver) { super(driver); } + public LanguagePage clickMoreActions() { + log.info("Click More Actions"); + waitForWebElement(moreActions).click(); + return new LanguagePage(getDriver()); + } + + public LanguagePage clickMembersTabMoreActions() { + log.info("Click Members tab More Actions"); + waitForWebElement(membersTabMoreActions).click(); + return new LanguagePage(getDriver()); + } + public ContactTeamPage clickContactCoordinatorsButton() { log.info("Click Contact Coordinators button"); waitForWebElement(contactCoordinatorsButton).click(); return new ContactTeamPage(getDriver()); } + public LanguagePage gotoSettingsTab() { + waitForWebElement(settingsTab).click(); + return new LanguagePage(getDriver()); + } + + public LanguagePage gotoMembersTab() { + waitForWebElement(membersTab).click(); + return new LanguagePage(getDriver()); + } + + public LanguagePage enableLanguageByDefault(boolean enable) { + Checkbox checkbox = Checkbox.of(waitForWebElement(enableByDefault)); + if (enable) { + checkbox.check(); + } else { + checkbox.uncheck(); + } + return new LanguagePage(getDriver()); + } + + public LanguagePage saveSettings() { + waitForWebElement(saveButton).click(); + return new LanguagePage(getDriver()); + } + + public List getMemberUsernames() { + log.info("Query username list"); + if (getMemberCount().equals("0")) { + log.info("No members yet for this language"); + return Collections.emptyList(); + } + List names = new ArrayList<>(); + for (WebElement listEntry : waitForWebElement(By.id("members-form")) + .findElements(By.className("list__item--actionable"))) { + names.add(listEntry.findElement(By.tagName("h3")).getText().trim()); + } + return names; + } + + private String getMemberCount() { + log.info("Query members info"); + return waitForWebElement(By.id("members-size")).getText().trim(); + } + + public LanguagePage joinLanguageTeam() { + log.info("Click Join"); + waitForWebElement(joinLanguageTeamButton).click(); + // we need to wait for this join to finish before returning the page + waitForAMoment().until(new Function() { + @Override + public Boolean apply(WebDriver driver) { + return driver.findElements(joinLanguageTeamButton).isEmpty(); + } + }); + return new LanguagePage(getDriver()); + } + + public LanguagePage clickAddTeamMember() { + log.info("Click Add Team Member"); + waitForWebElement(addTeamMemberLink).click(); + return this; + } + + /* + * Convenience function for adding a language team member + */ + public LanguagePage searchPersonAndAddToTeam( + final String personName, TeamPermission... permissions) { + // Convenience! + enterUsername(personName); + clickSearch(); + clickAddUserRoles(personName, permissions); + clickAddSelectedButton(); + return confirmAdded(personName); + } + + private LanguagePage enterUsername(String username) { + log.info("Enter username search {}", username); + waitForWebElement(addUserSearchInput).sendKeys(username); + return new LanguagePage(getDriver()); + } + + private LanguagePage clickSearch() { + log.info("Click Search"); + waitForWebElement(addUserSearchButton).click(); + return new LanguagePage(getDriver()); + } + + private LanguagePage clickAddUserRoles(final String username, TeamPermission... permissions) { + log.info("Click user permissions"); + // if permissions is empty, default add as translator + Set permissionToAdd = Sets.newHashSet(permissions); + permissionToAdd.add(TeamPermission.Translator); + + for (final TeamPermission permission : permissionToAdd) { + log.info("Set checked as {}", permission.name()); + waitForAMoment().until(new Predicate() { + @Override + public boolean apply(@Nullable WebDriver webDriver) { + WebElement input = getSearchedForUser(username) + .findElement(By.className("list--horizontal")) + .findElements(By.tagName("li")) + .get(permission.columnIndex) + .findElement(By.tagName("input")); + Checkbox checkbox = Checkbox.of(input); + checkbox.check(); + return checkbox.checked(); + } + }); + } + return new LanguagePage(getDriver()); + } + + private LanguagePage confirmAdded(final String personUsername) { + // we need to wait for the page to refresh + refreshPageUntil(this, new Predicate() { + @Override + public boolean apply(WebDriver driver) { + return getMemberUsernames().contains(personUsername); + } + }); + return new LanguagePage(getDriver()); + } + + + private WebElement getSearchedForUser(final String username) { + return waitForAMoment().until(new Function() { + @Override + public WebElement apply(WebDriver input) { + WebElement list = waitForWebElement(personTable) + .findElement(By.className("list--slat")); + List rows = list + .findElements(By.className("txt--meta")); + rows.addAll(list + .findElements(By.className("txt--mini"))); + for (WebElement row : rows) { + if (getListItemUsername(row).equals(username)) { + return row; + } + } + return null; + } + }); + } + + private String getListItemUsername(WebElement listItem) { + String fullname = listItem.findElements( + By.className("bx--inline-block")) + .get(0).getText(); + return fullname.substring(fullname.indexOf('[') + 1, fullname.indexOf(']')); + } + + public LanguagePage clickAddSelectedButton() { + log.info("Click Add Selected"); + waitForWebElement(addSelectedButton).click(); + return new LanguagePage(getDriver()); + } + + + public static enum TeamPermission { + Translator(IS_TRANSLATOR_COLUMN), Reviewer(IS_REVIEWER_COLUMN), Coordinator(IS_COORDINATOR_COLUMN); + private final int columnIndex; + + TeamPermission(int columnIndex) { + this.columnIndex = columnIndex; + } + + } + + + } diff --git a/functional-test/src/main/java/org/zanata/page/languages/LanguagesPage.java b/functional-test/src/main/java/org/zanata/page/languages/LanguagesPage.java index c581efcd4e..f4d227c152 100644 --- a/functional-test/src/main/java/org/zanata/page/languages/LanguagesPage.java +++ b/functional-test/src/main/java/org/zanata/page/languages/LanguagesPage.java @@ -21,12 +21,17 @@ package org.zanata.page.languages; import lombok.extern.slf4j.Slf4j; + +import org.apache.commons.codec.language.bm.Languages; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; import org.zanata.page.BasePage; +import org.zanata.page.administration.AddLanguagePage; import org.zanata.util.TableRow; import org.zanata.util.WebElementUtil; +import java.util.ArrayList; import java.util.List; /** @@ -35,7 +40,11 @@ @Slf4j public class LanguagesPage extends BasePage { - private By languagesList = By.id("tribesForm:latestTribes"); + private By moreActions = By.id("more-actions"); + + private By addLanguageLink = By.linkText("Add New Language"); + + private By enabledByDefaultLabel = By.className("label"); public LanguagesPage(WebDriver driver) { super(driver); @@ -43,19 +52,70 @@ public LanguagesPage(WebDriver driver) { public LanguagePage selectLanguage(String language) { log.info("Select {} from the language list", language); - List tableRowList = WebElementUtil - .getTableRows(getDriver(), waitForWebElement(languagesList)); - boolean clicked = false; - for (TableRow tableRow : tableRowList) { - if (tableRow.getCells().get(0).getText().equals(language)) { - tableRow.getCells().get(0).findElement(By.tagName("a")).click(); - clicked = true; - break; + findRowByLocale(language).click(); + return new LanguagePage(getDriver()); + } + + private WebElement findRowByLocale(final String localeId) { + for (WebElement row : getRows()) { + if (getShortLocale(row).equals(localeId)) { + return row; } } - if (!clicked) { - throw new RuntimeException(language + " not found"); + throw new RuntimeException("Did not find locale " + localeId); + } + + private String getShortLocale(WebElement row) { + String locale = getListEntryLocale(row); + return locale.substring(0, locale.indexOf('[')).trim(); + } + + private List getRows() { + return waitForWebElement(waitForElementExists(By.id("languageForm")), + By.className("list--stats")) + .findElements(By.tagName("li")); + } + + private String getListEntryLocale(WebElement listElement) { + return listElement.findElement(By.className("list__item__meta")).getText().trim(); + } + + public List getLanguageLocales() { + log.info("Query list of languages"); + List names = new ArrayList<>(); + for (WebElement listItem : getRows()) { + names.add(getShortLocale(listItem)); } + return names; + } + + public LanguagesPage clickMoreActions() { + log.info("Click More Actions dropdown"); + waitForWebElement(moreActions).click(); + return new LanguagesPage(getDriver()); + } + + public AddLanguagePage addNewLanguage() { + log.info("Click Add New Language"); + waitForWebElement(addLanguageLink).click(); + return new AddLanguagePage(getDriver()); + } + + public LanguagePage gotoLanguagePage(String localeId) { + log.info("Click language {}", localeId); + findRowByLocale(localeId).click(); return new LanguagePage(getDriver()); } + + public boolean languageIsEnabledByDefault(String localeId) { + log.info("Query is language enabled by default {}", localeId); + // Search for enabledByDefaultLabel label + for (WebElement label: findRowByLocale(localeId).findElements(enabledByDefaultLabel)) { + if (label.getText().trim().equals("Default")) { + return true; + } + } + return false; + } + } diff --git a/functional-test/src/main/java/org/zanata/page/projects/ProjectBasePage.java b/functional-test/src/main/java/org/zanata/page/projects/ProjectBasePage.java index 7ad056b2e8..e8a8cd52e9 100644 --- a/functional-test/src/main/java/org/zanata/page/projects/ProjectBasePage.java +++ b/functional-test/src/main/java/org/zanata/page/projects/ProjectBasePage.java @@ -44,6 +44,12 @@ public class ProjectBasePage extends BasePage { private By maintainersTab = By.id("maintainers_tab"); private By aboutTab = By.id("about_tab"); private By settingsTab = By.id("settings_tab"); + + private By versionsTabBody = By.id("versions"); + private By maintainersTabBody = By.id("maintainers"); + private By aboutTabBody = By.id("about"); + private By settingsTabBody = By.id("settings"); + private By settingsGeneralTab = By.id("settings-general_tab"); private By settingsPermissionTab = By.id("settings-permissions_tab"); private By settingsTranslationTab = By.id("settings-translation_tab"); @@ -65,6 +71,7 @@ public String getProjectName() { public ProjectVersionsPage gotoVersionsTab() { log.info("Click Versions tab"); + waitForElementExists(versionsTabBody); clickWhenTabEnabled(waitForWebElement(versionsTab)); waitForWebElement(By.id("versions")); return new ProjectVersionsPage(getDriver()); @@ -72,6 +79,7 @@ public ProjectVersionsPage gotoVersionsTab() { public ProjectMaintainersPage gotoMaintainersTab() { log.info("Click Maintainers tab"); + waitForElementExists(maintainersTabBody); clickWhenTabEnabled(waitForWebElement(maintainersTab)); waitForWebElement(By.id("maintainers")); return new ProjectMaintainersPage(getDriver()); @@ -79,6 +87,7 @@ public ProjectMaintainersPage gotoMaintainersTab() { public ProjectAboutPage gotoAboutTab() { log.info("Click About tab"); + waitForElementExists(aboutTabBody); clickWhenTabEnabled(waitForWebElement(aboutTab)); waitForWebElement(By.id("about")); return new ProjectAboutPage(getDriver()); @@ -91,6 +100,7 @@ public boolean settingsTabIsDisplayed() { public ProjectBasePage gotoSettingsTab() { log.info("Click Settings tab"); + waitForElementExists(settingsTabBody); clickWhenTabEnabled(waitForWebElement(settingsTab)); waitForWebElement(settingsTab); return new ProjectBasePage(getDriver()); diff --git a/functional-test/src/main/java/org/zanata/page/projects/projectsettings/ProjectGeneralTab.java b/functional-test/src/main/java/org/zanata/page/projects/projectsettings/ProjectGeneralTab.java index f483080652..f5889d847f 100644 --- a/functional-test/src/main/java/org/zanata/page/projects/projectsettings/ProjectGeneralTab.java +++ b/functional-test/src/main/java/org/zanata/page/projects/projectsettings/ProjectGeneralTab.java @@ -73,7 +73,7 @@ public ProjectGeneralTab enterProjectName(final String projectName) { log.info("Enter project name {}", projectName); waitForWebElement(projectNameField).clear(); waitForWebElement(projectNameField).sendKeys(projectName); - defocus(); + defocus(projectNameField); return new ProjectGeneralTab(getDriver()); } @@ -86,7 +86,7 @@ public ProjectGeneralTab enterDescription(String projectDescription) { log.info("Enter project description {}", projectDescription); waitForWebElement(descriptionField).clear(); waitForWebElement(descriptionField).sendKeys(projectDescription); - defocus(); + defocus(descriptionField); return new ProjectGeneralTab(getDriver()); } diff --git a/functional-test/src/main/java/org/zanata/page/projects/projectsettings/ProjectTranslationTab.java b/functional-test/src/main/java/org/zanata/page/projects/projectsettings/ProjectTranslationTab.java index fdac93074c..b044ca6a6e 100644 --- a/functional-test/src/main/java/org/zanata/page/projects/projectsettings/ProjectTranslationTab.java +++ b/functional-test/src/main/java/org/zanata/page/projects/projectsettings/ProjectTranslationTab.java @@ -50,7 +50,9 @@ public boolean isValidationLevel(String optionName, String level) { log.info("Query is {} validation level {}", optionName, level); final String optionElementID = validationNames .get(optionName).toString().concat(level); - WebElement option = waitForElementExists(By.id(optionElementID)); + WebElement option = waitForElementExists( + waitForElementExists(validationsList), + By.id(optionElementID)); return option.getAttribute("checked").equals("true"); } @@ -59,17 +61,12 @@ public ProjectTranslationTab setValidationLevel(String optionName, log.info("Click {} validation level {}", optionName, level); final String optionElementID = validationNames .get(optionName).toString().concat(level); + WebElement option = waitForElementExists( + waitForElementExists(validationsList), + By.id(optionElementID)); - WebElement option = waitForWebElement(validationsList) - .findElement(By.id(optionElementID)); - - ((JavascriptExecutor) getDriver()) - .executeScript("arguments[0].click();", option); - try { - Thread.sleep(500); - } catch (InterruptedException ie) { - // Wait for half a second before continuing - } + getExecutor().executeScript("arguments[0].click();", option); + slightPause(); return new ProjectTranslationTab(getDriver()); } diff --git a/functional-test/src/main/java/org/zanata/page/projectversion/CreateVersionPage.java b/functional-test/src/main/java/org/zanata/page/projectversion/CreateVersionPage.java index 77f521b454..b124899fb7 100644 --- a/functional-test/src/main/java/org/zanata/page/projectversion/CreateVersionPage.java +++ b/functional-test/src/main/java/org/zanata/page/projectversion/CreateVersionPage.java @@ -37,7 +37,7 @@ public class CreateVersionPage extends BasePage { "must start and end with letter or number, and contain only " + "letters, numbers, periods, underscores and hyphens."; - private By projectVersionID = By.id("create-version-form:slugField:slug"); + public By projectVersionID = By.id("create-version-form:slugField:slug"); private By projectTypeSelection = By.id("create-version-form:project-type"); private By saveButton = By.id("create-version-form:button-create"); private By copyFromPreviousVersionChk = By.id("create-version-form:copy-from-version"); @@ -77,8 +77,7 @@ private boolean isCopyFromVersionAvailable() { } private void clickCopyFromCheckbox() { - ((JavascriptExecutor) getDriver()) - .executeScript("arguments[0].click();", + getExecutor().executeScript("arguments[0].click();", waitForWebElement(copyFromPreviousVersionChk) .findElement(By.tagName("span"))); diff --git a/functional-test/src/main/java/org/zanata/page/projectversion/VersionBasePage.java b/functional-test/src/main/java/org/zanata/page/projectversion/VersionBasePage.java index f8addd8514..102166ca6b 100644 --- a/functional-test/src/main/java/org/zanata/page/projectversion/VersionBasePage.java +++ b/functional-test/src/main/java/org/zanata/page/projectversion/VersionBasePage.java @@ -33,13 +33,18 @@ @Slf4j public class VersionBasePage extends BasePage { - private By settingsTab = By.id("settings_tab"); private By settingsGeneralTab = By.id("settings-general_tab"); private By settingsLanguagesTab = By.id("settings-languages_tab"); private By settingsDocumentsTab = By.id("settings-documents_tab"); private By settingsTranslationTab = By.id("settings-translation_tab"); - private By documentsTab = By.id("documents"); - private By languageTab = By.id("languages"); + private By documentsTab = By.id("documents_tab"); + private By languageTab = By.id("languages_tab"); + private By settingsTab = By.id("settings_tab"); + + private By documentsTabBody = By.id("documents"); + private By languageTabBody = By.id("languages"); + private By settingsTabBody = By.id("settings"); + private By versionInfo = By.id("version-info"); private By versionPage = By.id("version-page"); @@ -63,14 +68,16 @@ public ProjectVersionsPage clickProjectLink(String projectName) { public VersionDocumentsPage gotoDocumentTab() { log.info("Click Documents tab"); - clickWhenTabEnabled(waitForWebElement(By.id("documents_tab"))); + waitForElementExists(documentsTabBody); + clickWhenTabEnabled(waitForWebElement(documentsTab)); waitForWebElement(By.id("documents")); return new VersionDocumentsPage(getDriver()); } public VersionLanguagesPage gotoLanguageTab() { log.info("Click Languages tab"); - clickWhenTabEnabled(waitForWebElement(By.id("languages_tab"))); + waitForElementExists(languageTabBody); + clickWhenTabEnabled(waitForWebElement(languageTab)); waitForWebElement(By.id("languages")); return new VersionLanguagesPage(getDriver()); } @@ -78,8 +85,9 @@ public VersionLanguagesPage gotoLanguageTab() { public VersionBasePage gotoSettingsTab() { log.info("Click Settings tab"); slightPause(); - clickWhenTabEnabled(waitForWebElement(By.id("settings_tab"))); - waitForWebElement(By.id("settings")); + waitForElementExists(settingsTabBody); + clickWhenTabEnabled(waitForWebElement(settingsTab)); + waitForWebElement(settingsTabBody); return new VersionBasePage(getDriver()); } diff --git a/functional-test/src/main/java/org/zanata/page/projectversion/VersionDocumentsPage.java b/functional-test/src/main/java/org/zanata/page/projectversion/VersionDocumentsPage.java index daaf57ec13..e79c925e81 100644 --- a/functional-test/src/main/java/org/zanata/page/projectversion/VersionDocumentsPage.java +++ b/functional-test/src/main/java/org/zanata/page/projectversion/VersionDocumentsPage.java @@ -65,7 +65,7 @@ public List getSourceDocumentNames() { public List apply(WebDriver input) { List fileNames = new ArrayList(); for (WebElement element : getDocumentsTabDocumentList()) { - fileNames.add(waitForWebElement(element, + fileNames.add(element.findElement( By.className("list__title")) .getText()); } diff --git a/functional-test/src/main/java/org/zanata/page/projectversion/VersionLanguagesPage.java b/functional-test/src/main/java/org/zanata/page/projectversion/VersionLanguagesPage.java index 6433a203b5..c4326c978f 100644 --- a/functional-test/src/main/java/org/zanata/page/projectversion/VersionLanguagesPage.java +++ b/functional-test/src/main/java/org/zanata/page/projectversion/VersionLanguagesPage.java @@ -22,9 +22,11 @@ import java.util.List; +import com.google.common.base.Predicate; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; +import org.zanata.page.BasePage; import org.zanata.page.webtrans.EditorPage; import com.google.common.base.Function; import com.google.common.base.Preconditions; @@ -56,29 +58,34 @@ private List getLanguageTabLocaleList() { private List getLanguageTabDocumentList() { log.info("Query documents list"); - return waitForWebElement(languageDocumentList) - .findElement(By.className("list--stats")) + return waitForWebElement(waitForElementExists(languageDocumentList), + By.className("list--stats")) .findElements(documentListItem); } public VersionLanguagesPage clickLocale(final String locale) { log.info("Click locale {}", locale); - WebElement listItem = waitForAMoment() - .until(new Function() { - @Override - public WebElement apply(WebDriver input) { - for (WebElement localeRow : getLanguageTabLocaleList()) { - WebElement link = localeRow - .findElement(By.xpath(".//a")); // Top - if (link.findElement(languageItemTitle) - .getText().equals(locale)) { - return link; - } - } - return null; + waitForAMoment().until(new Predicate() { + @Override + public boolean apply(WebDriver input) { + new BasePage(getDriver()).waitForPageSilence(); + for (WebElement localeRow : getLanguageTabLocaleList()) { + // Top + WebElement link = localeRow + .findElement(By.xpath(".//a")); + if (link.findElement(languageItemTitle) + .getText().equals(locale)) { + // Clicking too fast can often confuse + // the button: + slightPause(); + clickLinkAfterAnimation(link); + return true; } - }); - clickLinkAfterAnimation(listItem); + } + return false; + } + }); + return new VersionLanguagesPage(getDriver()); } diff --git a/functional-test/src/main/java/org/zanata/page/projectversion/versionsettings/VersionDocumentsTab.java b/functional-test/src/main/java/org/zanata/page/projectversion/versionsettings/VersionDocumentsTab.java index 1139531b73..29009d89df 100644 --- a/functional-test/src/main/java/org/zanata/page/projectversion/versionsettings/VersionDocumentsTab.java +++ b/functional-test/src/main/java/org/zanata/page/projectversion/versionsettings/VersionDocumentsTab.java @@ -31,6 +31,7 @@ import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.zanata.page.projectversion.VersionBasePage; +import org.zanata.page.utility.NamedPredicate; /** * @author Damian Jansen () { @Override - public boolean apply( WebDriver input) { + public boolean apply(WebDriver input) { return !getDriver().findElement(By.id("file-upload-component")) .isDisplayed(); } @@ -88,7 +89,7 @@ public boolean apply( WebDriver input) { public VersionDocumentsTab enterFilePath(String filePath) { log.info("Enter file path {}", filePath); // Make the hidden input element slightly not hidden - ((JavascriptExecutor)getDriver()) + getExecutor() .executeScript("arguments[0].style.visibility = 'visible'; " + "arguments[0].style.height = '1px'; " + "arguments[0].style.width = '1px'; " + @@ -113,7 +114,7 @@ public VersionDocumentsTab clickUploadDone() { public boolean sourceDocumentsContains(String document) { log.info("Query source documents contain {}", document); - for(String documentLabel : waitForAMoment() + for (String documentLabel : waitForAMoment() .until(new Function>() { @Override public List apply(WebDriver input) { @@ -159,9 +160,21 @@ public VersionDocumentsTab clickRemoveOn(String filename) { return new VersionDocumentsTab(getDriver()); } + public void expectSomeUploadItems() { + waitForAMoment().until(new NamedPredicate("expectUploadItem") { + @Override + public boolean apply(WebDriver input) { + return !getUploadListElements().isEmpty(); + } + }); + } + private List getUploadListElements() { - return waitForWebElement(filesListPanel).findElement(By.tagName("ul")) - .findElements(By.tagName("li")); + @SuppressWarnings("unchecked") + List liElements = (List) getExecutor() + .executeScript( + "return $('div.js-files-panel ul li')"); + return liElements; } public String getUploadError() { diff --git a/functional-test/src/main/java/org/zanata/page/projectversion/versionsettings/VersionTranslationTab.java b/functional-test/src/main/java/org/zanata/page/projectversion/versionsettings/VersionTranslationTab.java index 1e778aa8bb..2257e627f5 100644 --- a/functional-test/src/main/java/org/zanata/page/projectversion/versionsettings/VersionTranslationTab.java +++ b/functional-test/src/main/java/org/zanata/page/projectversion/versionsettings/VersionTranslationTab.java @@ -22,7 +22,6 @@ import lombok.extern.slf4j.Slf4j; import org.openqa.selenium.By; -import org.openqa.selenium.JavascriptExecutor; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.zanata.page.projectversion.VersionBasePage; @@ -62,8 +61,7 @@ public VersionTranslationTab setValidationLevel(String optionName, By.id("settings-translation-validation-form")) .findElement(By.id(optionElementID)); - ((JavascriptExecutor) getDriver()) - .executeScript("arguments[0].click();", option); + getExecutor().executeScript("arguments[0].click();", option); try { Thread.sleep(500); } catch (InterruptedException ie) { diff --git a/functional-test/src/main/java/org/zanata/page/utility/ContactAdminFormPage.java b/functional-test/src/main/java/org/zanata/page/utility/ContactAdminFormPage.java index 86d3dd2e78..bf8c211850 100644 --- a/functional-test/src/main/java/org/zanata/page/utility/ContactAdminFormPage.java +++ b/functional-test/src/main/java/org/zanata/page/utility/ContactAdminFormPage.java @@ -50,10 +50,7 @@ public ContactAdminFormPage inputSubject(String subject) { public ContactAdminFormPage inputMessage(String message) { log.info("Enter message {}", message); - WebElementUtil.setRichTextEditorContent( - getDriver(), - waitForWebElement(messageField), - message); + waitForWebElement(messageField).sendKeys(message); return new ContactAdminFormPage(getDriver()); } diff --git a/functional-test/src/main/java/org/zanata/page/utility/HelpPage.java b/functional-test/src/main/java/org/zanata/page/utility/HelpPage.java index a8b2f7179d..341f51c9bc 100644 --- a/functional-test/src/main/java/org/zanata/page/utility/HelpPage.java +++ b/functional-test/src/main/java/org/zanata/page/utility/HelpPage.java @@ -33,11 +33,18 @@ public class HelpPage extends BasePage { private By contactAdminLink = By.linkText("Contact Admin"); + private By moreActions = By.className("dropdown__toggle"); public HelpPage(WebDriver driver) { super(driver); } + public HelpPage clickMoreActions() { + log.info("Click More Actions"); + waitForWebElement(moreActions).click(); + return new HelpPage(getDriver()); + } + public ContactAdminFormPage clickContactAdmin() { log.info("Click Contact Admin button"); waitForWebElement(contactAdminLink).click(); diff --git a/functional-test/src/main/java/org/zanata/page/utility/NamedPredicate.java b/functional-test/src/main/java/org/zanata/page/utility/NamedPredicate.java new file mode 100644 index 0000000000..d74656bf89 --- /dev/null +++ b/functional-test/src/main/java/org/zanata/page/utility/NamedPredicate.java @@ -0,0 +1,41 @@ +/* + * Copyright 2015, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.zanata.page.utility; + +import com.google.common.base.Predicate; +import org.openqa.selenium.WebDriver; + +/** + * @author Sean Flanigan sflaniga@redhat.com + */ +public abstract class NamedPredicate implements + Predicate { + private final String name; + + public NamedPredicate(String name) { + this.name = name; + } + + @Override + public String toString() { + return "predicate: " + name; + } +} diff --git a/functional-test/src/main/java/org/zanata/page/webtrans/EditorPage.java b/functional-test/src/main/java/org/zanata/page/webtrans/EditorPage.java index eef434576b..5185878ecf 100644 --- a/functional-test/src/main/java/org/zanata/page/webtrans/EditorPage.java +++ b/functional-test/src/main/java/org/zanata/page/webtrans/EditorPage.java @@ -94,6 +94,16 @@ public boolean apply(WebDriver input) { return new EditorPage(getDriver()); } + /** + * There is usually a long poll waiting for GWTEventService events + * from the server. + * @return + */ + @Override + protected int getExpectedBackgroundRequests() { + return 1; + } + /** * First row is header: SourceTerm, TargetTerm, Action, Details. * @@ -401,10 +411,9 @@ public boolean isValidationOptionAvailable(Validations validation) { */ public boolean isValidationOptionSelected(Validations validation) { log.info("Query is validation option {} selected", validation); - return waitForWebElement(By.xpath("//*[@title='" + - getValidationTitle(validation) + "']")) - .findElement(By.tagName("input")) - .isSelected(); + return waitForElementExists(waitForElementExists(By.xpath("//*[@title='" + + getValidationTitle(validation) + "']")), + By.tagName("input")).isSelected(); } /** @@ -415,10 +424,9 @@ public boolean isValidationOptionSelected(Validations validation) { */ public EditorPage clickValidationCheckbox(Validations validation) { log.info("Click validation checkbox {}", validation); - waitForElementExists(By.xpath("//*[@title='" + - getValidationTitle(validation) + "']")) - .findElement(By.tagName("input")) - .click(); + waitForWebElement(waitForElementExists(By.xpath("//*[@title='" + + getValidationTitle(validation) + "']")), + By.tagName("input")).click(); return new EditorPage(getDriver()); } @@ -461,7 +469,8 @@ public String getFilterQuery() { private WebElement getTranslationTargetColumn() { return waitForWebElement(By.className("selected")) .findElements(By.className("transUnitCol")) - .get(1); // Right column + // Right column + .get(1); } // Find the validation messages / errors box @@ -514,7 +523,7 @@ public boolean apply(WebDriver input) { return getTranslationHistoryList().get(entry) .findElement(By.linkText("Compare")) .isDisplayed(); - } catch(IndexOutOfBoundsException ioobe) { + } catch (IndexOutOfBoundsException ioobe) { return false; } } @@ -583,7 +592,8 @@ private WebElement getCompareTab() { private List getCompareTabEntries() { return getTranslationHistoryBox() .findElements(By.className("gwt-TabLayoutPanelContent")) - .get(1) // Second tab + // Second tab + .get(1) .findElements(By.className("textFlowEntry")); } diff --git a/functional-test/src/main/java/org/zanata/util/TableRow.java b/functional-test/src/main/java/org/zanata/util/TableRow.java index b7d74563bc..08176e840e 100644 --- a/functional-test/src/main/java/org/zanata/util/TableRow.java +++ b/functional-test/src/main/java/org/zanata/util/TableRow.java @@ -36,6 +36,10 @@ public TableRow(WebElement row) { this.row = row; } + public List getHeaders() { + return row.findElements(By.xpath(".//th")); + } + public List getCells() { return row.findElements(By.xpath(".//td")); } diff --git a/functional-test/src/main/java/org/zanata/util/TestFileGenerator.java b/functional-test/src/main/java/org/zanata/util/TestFileGenerator.java index 8ce60a7528..1417914a8f 100644 --- a/functional-test/src/main/java/org/zanata/util/TestFileGenerator.java +++ b/functional-test/src/main/java/org/zanata/util/TestFileGenerator.java @@ -201,8 +201,7 @@ private static void marshall(File output, T object, Class xmlClass) { Marshaller jaxbMarshaller = jaxbContext.createMarshaller(); jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); jaxbMarshaller.marshal(object, output); - } - catch (JAXBException e) { + } catch (JAXBException e) { throw Throwables.propagate(e); } } @@ -230,7 +229,7 @@ private static class ZanataXml { private String projectVersion; @XmlElement(name = "project-type", namespace = ZanataXml.NS) private String projectType; - @XmlElementWrapper(name="locales", namespace = ZanataXml.NS) + @XmlElementWrapper(name = "locales", namespace = ZanataXml.NS) @XmlElements( @XmlElement(name = "locale", namespace = ZanataXml.NS) ) diff --git a/functional-test/src/main/java/org/zanata/util/WebElementUtil.java b/functional-test/src/main/java/org/zanata/util/WebElementUtil.java index d992e16cc1..7a23425c61 100644 --- a/functional-test/src/main/java/org/zanata/util/WebElementUtil.java +++ b/functional-test/src/main/java/org/zanata/util/WebElementUtil.java @@ -39,6 +39,7 @@ import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.ui.FluentWait; +import org.openqa.selenium.support.ui.WebDriverWait; import org.zanata.page.WebDriverFactory; import static java.util.concurrent.TimeUnit.SECONDS; @@ -145,10 +146,8 @@ public List apply(@Nullable TableRow from) { public static FluentWait waitForSeconds(WebDriver webDriver, int durationInSec) { - return new FluentWait(webDriver) - .withTimeout(durationInSec, SECONDS) - .pollingEvery(1, SECONDS) - .ignoring(NoSuchElementException.class, + return new WebDriverWait(webDriver, durationInSec).ignoring( + // TODO is ignoring this safe? StaleElementReferenceException.class); } @@ -168,8 +167,7 @@ public List apply(@Nullable WebDriver input) { WebElement table; try { table = input.findElement(by); - } - catch (NoSuchElementException noElement) { + } catch (NoSuchElementException noElement) { // Some pages don't show a table, if there's no // items to show return Collections.emptyList(); diff --git a/functional-test/src/main/java/org/zanata/util/rfc2822/InvalidEmailAddressRFC2822.java b/functional-test/src/main/java/org/zanata/util/rfc2822/InvalidEmailAddressRFC2822.java deleted file mode 100644 index d7f1526223..0000000000 --- a/functional-test/src/main/java/org/zanata/util/rfc2822/InvalidEmailAddressRFC2822.java +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright 2013, Red Hat, Inc. and individual contributors as indicated by the - * @author tags. See the copyright.txt file in the distribution for a full - * listing of individual contributors. - * - * This is free software; you can redistribute it and/or modify it under the - * terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This software is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this software; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF - * site: http://www.fsf.org. - */ -package org.zanata.util.rfc2822; - -/** - * @author Damian Jansen djansen@redhat.com - * @see RFC2822 Internet Message - * Format Standard Synopsis: This enumeration represents a collection - * of invalid email addresses, as stipulated in the RFC2822 Internet - * Message Format standard, or referred to standards. - * - * Definitions localpart: the section of an address preceding the @ symbol - * domain: the section of an address following the @ symbol label: section - * of localpart or domain between the start, @ symbol, period or end (also - * referred to as "atom") e.g. me, myself, example, com in - * me.myself@example.com quote / quoting: a section of the localpart - * contained within quotation marks - * - * Untested: - * - * RFC 2821, section 4.5.3.1 The maximum length of a "useful" email address - * is 255 characters. - * - * RFC 3696 The maximum allowable length of an email address is 320 - * characters. - */ -public enum InvalidEmailAddressRFC2822 { - - /** - * Email addresses consist of a local part, the "@" symbol, and the domain. - * - * @see "RFC 2822, section 3.4.1" - */ - PLAIN_ADDRESS("plainaddress"), - MISSING_AMPERSAT("email.example.com"), - MISSING_LOCALPART("@example.com"), - MISSING_DOMAIN("email@"), - MULTIPLE_APERSAT("email@example@example.com"), - - /** - * No periods can start or end the local part. Two periods together is - * invalid. - * - * @see "RFC 2822, section 3.4.1" - */ - LEADING_DOT(".email@example.com"), - TRAILING_DOT("email.@example.com"), - MULTIPLE_DOTS("email..email@example.com"), - - /** - * All email addresses are in 7-bit US ASCII. - * - * @see "RFC 2822, section 2.2" - */ - NON_UNICODE_CHARACTERS("あいうえお@example.com"), - - /** - * Unquoted local parts can consist of TEXT TEXT can contain: alphabetic - * numeric and symbols !#$%'*+-/=?^_`{|}~ - * - * @see "RFC 2822, section 3.4.1" - */ - INVALID_UNQUOTED_COMMA("test,user@example.com"), - INVALID_UNQUOTED_LEFT_PARENTHESES("test(user@example.com"), - INVALID_UNQUOTED_RIGHT_PARENTHESES("test)user@example.com"), - - /** - * The quoted local part starts with a quotation mark, ends with a quotation - * mark. - * - * @see "RFC 2822, section 3.4.1" - */ - INVALID_SINGLE_QUOTING("test\"user@example.com"), - - /** - * If an email is using the obsolete quoting on a per-label basis, then the - * email address consists of unquoted or quoted chunks separated by periods - * - * @see "RFC 2822, section 4.4" - */ - INVALID_QUOTING_SEPARATION("\"test\"user@example.com"), - - /** - * The contents of a quoted local part can not contain characters: 9 (TAB) - * 10 (LF) 13 (CR) 32 (space) 34 (") 91-94 ([, \, ], ^) - * - * @see "RFC 2822, section 3.4.1" - */ - INVALID_QUOTED_COMMA("\"test,user\"@example.com"), - INVALID_QUOTED_BACKSLASH("\"test\\user\"@example.com"), - INVALID_QUOTED_LEFT_BRACKET("\"test[user\"@example.com"), - INVALID_QUOTED_RIGHT_BRACKET("\"test]user\"@example.com"), - INVALID_QUOTED_CARAT("\"test^user\"@example.com"), - INVALID_QUOTED_SPACE("\"test user\"@example.com"), - INVALID_QUOTED_QUOTE("\"test\"user\"@example.com"), - INVALID_QUOTED_TAB("test.\"".concat("\t").concat("\".user@example.com")), - - /** - * If the quoted local part has a backslash, the following character is - * escaped and must not be 10 (LF), 13 (CR). - * - * @see "RFC 2822, section 3.4.1" - */ - INVALID_QUOTED_RETURN("test.\"\\".concat("\r") - .concat("\".user@example.com")), - INVALID_QUOTED_LINEFEED("test.\"\\".concat("\n").concat( - "\".user@example.com")), - - /** - * A plain domain consists of labels separated with periods. No period can - * start or end a domain name. No two periods in succession can be in a - * domain name. - * - * @see "RFC 1035, section 2.3.4" - */ - TRAILING_DOMAIN_DOT("email@example.com."), - LEADING_DOMAIN_DOT("email@.example.com"), - SUCCESSIVE_DOMAIN_DOTS("email@example..com"), - - /** - * Bracketed domains must: start with [, end with ] not contain characters 9 - * (TAB), 10 (LF), 13 (CR), 32 (space), 91-94 ([, \, ], ^) - * - * @see "RFC 2822, section 3.4.1" - */ - INCORRECTLY_BRACKETED_DOMAIN("email@[example].com"), - INVALID_DOMAIN_CHARACTER("email@[ex^ample.com]"), - INCORRECTLY_ESCAPED_DOMAIN("email@[exa\\mple.com]"), - - /** - * The maximum length of a label is 63 characters. - * - * @see "RFC 1035, section 2.3.4" - */ - DOMAIN_LABEL_LENGTH_EXCEEDED( - "email@IJUr9P6Y7Fx7rFy4sziQDT0qvSC7XKK6jrD0CNC41jorAKgFYIXLTN5ITJLohy58.com"), - - /** - * A label may contain hyphens, but no two hyphens in a row. A label must - * not start nor end with a hyphen. - * - * @see "RFC 1035, section 2.3.4" - */ - LEADING_DASH_DOMAIN("email@-example.com"), - TRAILING_DASH_DOMAIN("email@example-.com"), - MULTIPLE_DASHES_DOMAIN("email@exa--mple.com"), - LEADING_DASH_BRACKETED_DOMAIN("email@[-example.com]"), - TRAILING_DASH_BRACKETED_DOMAIN("email@[example.com-]"), - MULTIPLE_DASHES_BRACKETED_DOMAIN("email@[exa--mple.com]"), - - /** - * The contents of a bracketed domain can have a \ precede a character to - * escape it, and the following character must not be 10 (LF) or 13 (CR). - * - * @see "RFC2822, section 3.4.1" - */ - INVALID_BRACKETED_DOMAIN_RETURN("test@[\\".concat("\r").concat( - "example.com]")), - INVALID_BRACKETED_DOMAIN_LINEFEED("test@[\\".concat("\n").concat( - "example.com]")), - - /** - * The maximum length of the local part is 64 characters. - * - * @see "RFC 2821, section 4.5.3.1" - */ - LOCALPART_LENGTH_EXCEEDED( - "emailuhpealgyxntsh5upl5gqn5a4ruqs7mw6wz21j6dn72amzwozqlyua4jx16rd@example.com"), - - /** - * The top level domain must be all alphabetic. - * - * @see "RFC 3696, section 2" - */ - INVALID_ENCODED_HTML("Joe Smith "), - INVALID_FOLLOWING_TEXT("email@example.com (Joe Smith)"), INVALID_IP_FORMAT( - "email@111.222.333.44444"), - - /** - * The maximum length of a "useful" email address is 255 characters. - * - * @see "RFC 2821, section 4.5.3.1" - */ - MAX_EMAIL_LENGTH_EXCEEDED("email@" - + "Hk3yhCtbBRw3wCT76tL1ryAdfrIaaDszHqvZqnNrZPlNn3Wd7u." - + "RfpxrueSghp9dkGTGwT9s0fyJL850Sned72RD3Mm5PpEh6QJwQ." - + "3CeXyEHQEhXNOQdWhYVjGBLzlHz1sJfi4lfn7ighLXcxa5cMAK." - + "jFXsG8BVsvkODKktTXJ70bQmDWtWQzuh3oz4twumVArDGEbzS1." - + "slyaBcQqVgUdqXTBdbMY7YJxZwrzZQBBGjCl4e.com"); - - private final String address; - - private InvalidEmailAddressRFC2822(String address) { - this.address = address; - } - - public String toString() { - return address; - } -} diff --git a/functional-test/src/main/java/org/zanata/util/rfc2822/ValidEmailAddressRFC2822.java b/functional-test/src/main/java/org/zanata/util/rfc2822/ValidEmailAddressRFC2822.java deleted file mode 100644 index 2b677e8bf6..0000000000 --- a/functional-test/src/main/java/org/zanata/util/rfc2822/ValidEmailAddressRFC2822.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright 2013, Red Hat, Inc. and individual contributors as indicated by the - * @author tags. See the copyright.txt file in the distribution for a full - * listing of individual contributors. - * - * This is free software; you can redistribute it and/or modify it under the - * terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This software is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this software; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF - * site: http://www.fsf.org. - */ -package org.zanata.util.rfc2822; - -/** - * @author Damian Jansen djansen@redhat.com - * @see RFC2822 Internet Message - * Format Standard Synopsis: This enumeration represents a collection - * of valid email addresses, as stipulated in the RFC2822 Internet Message - * Format standard, or referred to standards. - * - * Definitions localpart: the section of an address preceding the @ symbol - * domain: the section of an address following the @ symbol label: section - * of localpart or domain between the start, @ symbol, period or end (also - * referred to as "atom") e.g. me, myself, example, com in - * me.myself@example.com quote / quoting: a section of the localpart - * contained within quotation marks - * - * Untested: RFC2822, section 3.4.1 The contents of a bracketed domain can - * have a \ precede a character to escape it, and the following character - * must not be 10 (LF) or 13 (CR). This allows spaces in the domain as long - * as they are escaped. - * - * RFC 2821, section 4.5.3.1 The maximum length of a "useful" email address - * is 255 characters. - * - * RFC 3696 The maximum allowable length of an email address is 320 - * characters. - */ -public enum ValidEmailAddressRFC2822 { - - /** - * Email addresses consist of a local part, the "@" symbol, and the domain. - * - * @see "RFC 2822, section 3.4.1" - */ - BASIC_EMAIL("email@example.com"), - - /** - * The local part can be unquoted, quoted in its entirety, or quoted on a - * per-label basis. The quoted local part starts with a quotation mark, ends - * with a quotation mark. - * - * @see "RFC 2822, sections 3.4.1 and 4.4" - */ - BASIC_QUOTED_EMAIL("\"email\"@example.com"), - - /** - * TEXT can contain alphabetic, numeric, and these symbols: - * !#$%'*+-/=?^_`{|}~ - * - * @see "RFC 2822, section 3.4.1" - */ - SPECIAL_CHARACTERS_LOCALPART("email.!#$%'*+-/=?^_`{|}~.dot@example.com"), - - /** - * If an email is using the obsolete quoting on a per-label basis, then the - * email address consists of unquoted or quoted chunks separated by periods. - * - * @see "RFC 2822, section 4.4" - */ - ENCLOSED_QUOTED_LABEL("dot.\"email\".dot@example.com"), - LOCALPART_WITH_EMPTY_QUOTE("dot.\"\".dot@example.com"), - - /** - * If the quoted local part has a backslash, the following character is - * escaped and must not be 10 (LF), 13 (CR). This supersedes the previous - * rule, allowing spaces and quotation marks in the email address as long as - * they are escaped. - * - * @see "RFC 2822, section 3.4.1" - */ - QUOTED_ESCAPED_SPECIAL_CHARACTERS( - "email.\"(),:;<>\\@\\[\\]\\\\\"@example.com"), - QUOTED_ESCAPED_QUOTES("email.\"\\\"\"@example.com"), - QUOTED_WITH_SPACE("\"special\\ email\"@example.com"), - - /** - * The domain can be bracketed or plain. - * - * @see "RFC 2822, section 3.4.1" - */ - BRACKETED_DOMAIN("email@[example.com]"), - BRACKETED_IPV4_DOMAIN("email@[123.45.67.89]"), - BRACKETED_IPV6_DOMAIN("email@[IPv6:2001:2d12:c4fe:5afe::1]"), - - /** - * A plain domain consists of labels separated with periods. No period can - * start or end a domain name. - * - * @see "RFC 1035, section 2.3.4" - */ - LOCALPART_MULTIPLE_LABELS("another.email@example.com"), - DOMAIN_MULTIPLE_LABELS("email@another.example.com"), - - /** - * The maximum length of a label is 63 characters. - * - * @see "RFC 1035, section 2.3.4" - */ - DOMAIN_LABEL_MAX_CHARACTERS( - "email@B3NQyUsDdzODMoymfDdifn6Wztx2wrivm80LEngHGl182frm6ifCPyv5SntbDg8.com"), - LOCALPART_LABEL_MAX_CHARACTERS( - "B3NQyUsDdzODMoymfDdifn6Wztx2wrivm80LEngHGl182frm6ifCPyv5SntbDg8@example.com"), - - /** - * A label may contain hyphens, but no two hyphens in a row. - * - * @see "RFC 1035, section 2.3.4" - */ - HYPHENATED_DOMAIN_LABEL("email@another-example.com"), - HYPHENATED_LOCALPART_LABEL("my-email@example.com"), - - /** - * The maximum length of the local part is 64 characters. - * - * @see "RFC 2821, section 4.5.3.1" - */ - LOCALPART_MAX_LENGTH( - "B3NQyUsDdzODMoymfDdifn6Wztx2wrivm.80LEngHGl182frm6ifCPyv5SntbDg8@example.com"); - - private final String address; - - private ValidEmailAddressRFC2822(String address) { - this.address = address; - } - - public String toString() { - return address; - } -} diff --git a/functional-test/src/main/java/org/zanata/workflow/AbstractWebWorkFlow.java b/functional-test/src/main/java/org/zanata/workflow/AbstractWebWorkFlow.java index 08d074cd4d..b147215012 100644 --- a/functional-test/src/main/java/org/zanata/workflow/AbstractWebWorkFlow.java +++ b/functional-test/src/main/java/org/zanata/workflow/AbstractWebWorkFlow.java @@ -20,7 +20,9 @@ */ package org.zanata.workflow; +import com.google.common.base.Predicate; import org.openqa.selenium.WebDriver; +import org.zanata.page.BasePage; import org.zanata.page.dashboard.DashboardBasePage; import org.zanata.page.utility.HomePage; import org.zanata.page.WebDriverFactory; @@ -37,7 +39,13 @@ public AbstractWebWorkFlow() { } public HomePage goToHome() { - driver.get(hostUrl); + new BasePage(driver).waitForAMoment().until(new Predicate() { + @Override + public boolean apply(WebDriver input) { + driver.get(hostUrl); + return new HomePage(driver).isValid(); + } + }); return new HomePage(driver); } diff --git a/functional-test/src/main/java/org/zanata/workflow/LanguageWorkFlow.java b/functional-test/src/main/java/org/zanata/workflow/LanguageWorkFlow.java index 62991599d9..72a30492cf 100644 --- a/functional-test/src/main/java/org/zanata/workflow/LanguageWorkFlow.java +++ b/functional-test/src/main/java/org/zanata/workflow/LanguageWorkFlow.java @@ -22,34 +22,41 @@ import java.util.List; -import org.zanata.page.administration.ManageLanguagePage; -import org.zanata.page.administration.ManageLanguageTeamMemberPage; +import org.zanata.page.languages.LanguagePage; +import org.zanata.page.languages.LanguagesPage; import lombok.extern.slf4j.Slf4j; @Slf4j public class LanguageWorkFlow extends AbstractWebWorkFlow { - public ManageLanguageTeamMemberPage addLanguageAndJoin(String localeId) { - ManageLanguageTeamMemberPage teamMemberPage = addLanguage(localeId) - .manageTeamMembersFor(localeId); - if (teamMemberPage.getMemberUsernames().contains("admin")) { + public LanguagePage addLanguageAndJoin(String localeId) { + + LanguagePage languagePage = goToHome().goToLanguages().gotoLanguagePage( + localeId).gotoMembersTab(); + + if (languagePage.getMemberUsernames().contains("admin")) { log.warn("admin has already joined the language [{}]", localeId); - return teamMemberPage; + return languagePage; } - return teamMemberPage.joinLanguageTeam(); + return languagePage.joinLanguageTeam(); } - public ManageLanguagePage addLanguage(String localeId) { - ManageLanguagePage manageLanguagePage = - goToHome().goToAdministration().goToManageLanguagePage(); - List locales = manageLanguagePage.getLanguageLocales(); + public LanguagesPage addLanguage(String localeId) { + LanguagesPage languagesPage = goToHome().goToLanguages(); + List locales = languagesPage.getLanguageLocales(); if (locales.contains(localeId)) { - log.warn("{} has already been added, enabling by default", localeId); - return manageLanguagePage.enableLanguageByDefault(localeId); + log.warn("{} has already been added, enabling by default", + localeId); + languagesPage.gotoLanguagePage(localeId).gotoSettingsTab() + .enableLanguageByDefault(true).saveSettings(); + return goToHome().goToLanguages(); } // continue to add the new language - return manageLanguagePage.addNewLanguage().enableLanguageByDefault() - .inputLanguage(localeId).saveLanguage(); + return languagesPage.clickMoreActions() + .addNewLanguage() + .enableLanguageByDefault(true) + .enterSearchLanguage(localeId) + .saveLanguage(); } } diff --git a/functional-test/src/main/java/org/zanata/workflow/LoginWorkFlow.java b/functional-test/src/main/java/org/zanata/workflow/LoginWorkFlow.java index ac63698b26..180f0abae6 100644 --- a/functional-test/src/main/java/org/zanata/workflow/LoginWorkFlow.java +++ b/functional-test/src/main/java/org/zanata/workflow/LoginWorkFlow.java @@ -79,14 +79,16 @@ private void doSignIn(String username, String password) { .waitForAMoment().until(new Predicate() { @Override public boolean apply(WebDriver driver) { - List messages = - driver.findElements(By.className("message--danger")); - if (messages.size() > 0 && messages.get(0) - .getText().contains(" Login failed ")) { - throw new IllegalAccessError("Login failed"); - } - List signIn = driver.findElements(By.id("Sign_in")); - return signIn.size() == 0; + // only enable this if you temporarily disable implicit waits: + // fail-fast logic +// List messages = +// driver.findElements(By.className("message--danger")); +// if (messages.size() > 0 && messages.get(0) +// .getText().contains(" Login failed ")) { +// throw new IllegalAccessError("Login failed"); +// } + driver.findElement(By.id("user--avatar")); + return true; } }); } diff --git a/functional-test/src/main/java/org/zanata/workflow/ProjectWorkFlow.java b/functional-test/src/main/java/org/zanata/workflow/ProjectWorkFlow.java index a6f4a582d9..8276c3245c 100644 --- a/functional-test/src/main/java/org/zanata/workflow/ProjectWorkFlow.java +++ b/functional-test/src/main/java/org/zanata/workflow/ProjectWorkFlow.java @@ -87,7 +87,8 @@ public ProjectVersionsPage createNewProject(HashMap settings) { .enterProjectId(settings.get("Project ID")) .enterDescription(settings.get("Description")) .selectProjectType(settings.get("Project Type")); - createProjectPage.slightPause(); // Unusual timing issue + // Unusual timing issue: + createProjectPage.slightPause(); return createProjectPage.pressCreateProject(); } diff --git a/functional-test/src/test/java/org/zanata/feature/account/EmailValidationTest.java b/functional-test/src/test/java/org/zanata/feature/account/EmailValidationTest.java new file mode 100644 index 0000000000..dc641021d3 --- /dev/null +++ b/functional-test/src/test/java/org/zanata/feature/account/EmailValidationTest.java @@ -0,0 +1,87 @@ +/* + * Copyright 2013, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this software; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF + * site: http://www.fsf.org. + */ +package org.zanata.feature.account; + +import lombok.extern.slf4j.Slf4j; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.zanata.feature.Feature; +import org.zanata.feature.testharness.ZanataTestCase; +import org.zanata.feature.testharness.TestPlan.DetailedTest; +import org.zanata.page.account.RegisterPage; +import org.zanata.util.AddUsersRule; +import org.zanata.workflow.BasicWorkFlow; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Damian Jansen djansen@redhat.com + */ +@Slf4j +@Category(DetailedTest.class) +public class EmailValidationTest extends ZanataTestCase { + + private RegisterPage registerPage; + + @ClassRule + public static AddUsersRule addUsersRule = new AddUsersRule(); + + @BeforeClass + public static void setUp() { + // Ensure no login + new BasicWorkFlow().goToHome().deleteCookiesAndRefresh(); + } + + @Before + public void before() { + registerPage = new BasicWorkFlow().goToHome().goToRegistration(); + } + + @Feature(summary = "The system will allow acceptable forms of an " + + "email address for registration", + tcmsTestPlanIds = 5316, tcmsTestCaseIds = 0) + @Test + public void validEmailAcceptance() throws Exception { + registerPage = registerPage.enterEmail("me@mydomain.com") + // Shift to other field + .enterName("Sam I Am"); + + assertThat(registerPage.getErrors()) + .as("Email validation errors are not shown") + .isEmpty(); + } + + @Feature(summary = "The user must enter a valid email address to " + + "register with Zanata", + tcmsTestPlanIds = 5316, tcmsTestCaseIds = 0) + @Test + public void invalidEmailRejection() throws Exception { + registerPage = registerPage.enterEmail("plaintext").registerFailure(); + + assertThat(registerPage.expectErrors()) + .contains(RegisterPage.MALFORMED_EMAIL_ERROR) + .as("The email formation error is displayed"); + } +} diff --git a/functional-test/src/test/java/org/zanata/feature/account/InvalidEmailAddressTest.java b/functional-test/src/test/java/org/zanata/feature/account/InvalidEmailAddressTest.java deleted file mode 100644 index a7ca65e745..0000000000 --- a/functional-test/src/test/java/org/zanata/feature/account/InvalidEmailAddressTest.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright 2013, Red Hat, Inc. and individual contributors as indicated by the - * @author tags. See the copyright.txt file in the distribution for a full - * listing of individual contributors. - * - * This is free software; you can redistribute it and/or modify it under the - * terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This software is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this software; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF - * site: http://www.fsf.org. - */ -package org.zanata.feature.account; - - -import lombok.extern.slf4j.Slf4j; -import org.junit.ClassRule; -import org.junit.Rule; -import org.junit.experimental.categories.Category; -import org.junit.experimental.theories.DataPoint; -import org.junit.experimental.theories.Theories; -import org.junit.experimental.theories.Theory; -import org.junit.rules.Timeout; -import org.junit.runner.RunWith; -import org.zanata.feature.Feature; -import org.zanata.feature.testharness.ZanataTestCase; -import org.zanata.feature.testharness.TestPlan.DetailedTest; -import org.zanata.page.account.RegisterPage; -import org.zanata.util.EnsureLogoutRule; -import org.zanata.util.rfc2822.InvalidEmailAddressRFC2822; -import org.zanata.workflow.BasicWorkFlow; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.zanata.util.rfc2822.InvalidEmailAddressRFC2822.*; - -/** - * @author Damian Jansen djansen@redhat.com - */ -@Slf4j -@RunWith(Theories.class) -@Category(DetailedTest.class) -public class InvalidEmailAddressTest extends ZanataTestCase { - @ClassRule - public static EnsureLogoutRule logoutRule = new EnsureLogoutRule(); - @Rule - public Timeout timeout = new Timeout(ZanataTestCase.MAX_LONG_TEST_DURATION); - - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_PLAIN_ADDRESS = PLAIN_ADDRESS; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_MISSING_AMPERSAT = - MISSING_AMPERSAT; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_MISSING_LOCALPART = - MISSING_LOCALPART; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_MISSING_DOMAIN = - MISSING_DOMAIN; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_MULTIPLE_APERSAT = - MULTIPLE_APERSAT; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_LEADING_DOT = LEADING_DOT; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_TRAILING_DOT = TRAILING_DOT; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_MULTIPLE_DOTS = MULTIPLE_DOTS; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_INVALID_UNQUOTED_COMMA = - INVALID_UNQUOTED_COMMA; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_INVALID_UNQUOTED_LEFT_PARENTHESES = - INVALID_UNQUOTED_LEFT_PARENTHESES; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_INVALID_UNQUOTED_RIGHT_PARENTHESES = - INVALID_UNQUOTED_RIGHT_PARENTHESES; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_INVALID_SINGLE_QUOTING = - INVALID_SINGLE_QUOTING; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_INVALID_QUOTING_SEPARATION = - INVALID_QUOTING_SEPARATION; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_INVALID_QUOTED_COMMA = - INVALID_QUOTED_COMMA; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_INVALID_QUOTED_BACKSLASH = - INVALID_QUOTED_BACKSLASH; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_INVALID_QUOTED_LEFT_BRACKET = - INVALID_QUOTED_LEFT_BRACKET; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_INVALID_QUOTED_RIGHT_BRACKET = - INVALID_QUOTED_RIGHT_BRACKET; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_INVALID_QUOTED_CARAT = - INVALID_QUOTED_CARAT; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_INVALID_QUOTED_SPACE = - INVALID_QUOTED_SPACE; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_INVALID_QUOTED_QUOTE = - INVALID_QUOTED_QUOTE; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_INVALID_QUOTED_RETURN = - INVALID_QUOTED_RETURN; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_INVALID_QUOTED_LINEFEED = - INVALID_QUOTED_LINEFEED; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_TRAILING_DOMAIN_DOT = - TRAILING_DOMAIN_DOT; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_LEADING_DOMAIN_DOT = - LEADING_DOMAIN_DOT; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_SUCCESSIVE_DOMAIN_DOTS = - SUCCESSIVE_DOMAIN_DOTS; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_INCORRECTLY_BRACKETED_DOMAIN = - INCORRECTLY_BRACKETED_DOMAIN; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_INVALID_DOMAIN_CHARACTER = - INVALID_DOMAIN_CHARACTER; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_INCORRECTLY_ESCAPED_DOMAIN = - INCORRECTLY_ESCAPED_DOMAIN; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_DOMAIN_LABEL_LENGTH_EXCEEDED = - DOMAIN_LABEL_LENGTH_EXCEEDED; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_LEADING_DASH_BRACKETED_DOMAIN = - LEADING_DASH_BRACKETED_DOMAIN; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_TRAILING_DASH_BRACKETED_DOMAIN = - TRAILING_DASH_BRACKETED_DOMAIN; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_MULTIPLE_DASHES_BRACKETED_DOMAIN = - MULTIPLE_DASHES_BRACKETED_DOMAIN; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_INVALID_BRACKETED_DOMAIN_RETURN = - INVALID_BRACKETED_DOMAIN_RETURN; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_INVALID_BRACKETED_DOMAIN_LINEFEED = - INVALID_BRACKETED_DOMAIN_LINEFEED; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_LOCALPART_LENGTH_EXCEEDED = - LOCALPART_LENGTH_EXCEEDED; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_INVALID_ENCODED_HTML = - INVALID_ENCODED_HTML; - @DataPoint - public static InvalidEmailAddressRFC2822 TEST_INVALID_FOLLOWING_TEXT = - INVALID_FOLLOWING_TEXT; - - // BUG982048 @DataPoint public static InvalidEmailAddressRFC2822 - // TEST_INVALID_IP_FORMAT = INVALID_IP_FORMAT; - // BUG982048 @DataPoint public static InvalidEmailAddressRFC2822 - // TEST_MAX_EMAIL_LENGTH_EXCEEDED = MAX_EMAIL_LENGTH_EXCEEDED; - // BUG982048 @DataPoint public static InvalidEmailAddressRFC2822 - // TEST_NON_UNICODE_CHARACTERS = NON_UNICODE_CHARACTERS; - // BUG982048 @DataPoint public static InvalidEmailAddressRFC2822 - // TEST_LEADING_DASH_DOMAIN = LEADING_DASH_DOMAIN; - // BUG982048 @DataPoint public static InvalidEmailAddressRFC2822 - // TEST_TRAILING_DASH_DOMAIN = TRAILING_DASH_DOMAIN; - // BUG982048 @DataPoint public static InvalidEmailAddressRFC2822 - // TEST_MULTIPLE_DASHES_DOMAIN = MULTIPLE_DASHES_DOMAIN; - - @Feature(summary = "The user must enter a valid email address to " + - "register with Zanata", - tcmsTestPlanIds = 5316, tcmsTestCaseIds = 0) - @Theory - public void invalidEmailRejection(InvalidEmailAddressRFC2822 emailAddress) - throws Exception { - log.info(testName.getMethodName() + " : " + emailAddress); - RegisterPage registerPage = new BasicWorkFlow() - .goToHome() - .goToRegistration() - .enterEmail(emailAddress.toString()) - .registerFailure(); - - assertThat(registerPage.expectError(RegisterPage.MALFORMED_EMAIL_ERROR)) - .contains(RegisterPage.MALFORMED_EMAIL_ERROR) - .as("The email formation error is displayed"); - } - -} diff --git a/functional-test/src/test/java/org/zanata/feature/account/ProfileTest.java b/functional-test/src/test/java/org/zanata/feature/account/ProfileTest.java index 66931bc50a..27f1c1de9f 100644 --- a/functional-test/src/test/java/org/zanata/feature/account/ProfileTest.java +++ b/functional-test/src/test/java/org/zanata/feature/account/ProfileTest.java @@ -64,7 +64,7 @@ public void verifyProfileData() throws Exception { .as("The correct api key is present"); assertThat(dashboardClientTab.getConfigurationDetails()) - .contains("localhost.url="+serverUrl) + .contains("localhost.url=" + serverUrl) .as("The configuration url is correct"); assertThat(dashboardClientTab.getConfigurationDetails()) @@ -129,8 +129,7 @@ public void emailValidationIsUsedOnProfileEdit() throws Exception { .typeNewAccountEmailAddress("admin@example.com") .clickUpdateEmailButton(); - assertThat(dashboardAccountTab.expectError( - DashboardAccountTab.EMAIL_TAKEN_ERROR)) + assertThat(dashboardAccountTab.expectErrors()) .contains(DashboardAccountTab.EMAIL_TAKEN_ERROR) .as("The email is rejected, being already taken"); @@ -141,8 +140,7 @@ public void emailValidationIsUsedOnProfileEdit() throws Exception { .typeNewAccountEmailAddress("test @example.com") .clickUpdateEmailButton(); - assertThat(dashboardAccountTab.expectError( - RegisterPage.MALFORMED_EMAIL_ERROR)) + assertThat(dashboardAccountTab.expectErrors()) .contains(RegisterPage.MALFORMED_EMAIL_ERROR) .as("The email is rejected, being of invalid format"); } diff --git a/functional-test/src/test/java/org/zanata/feature/account/RegisterTest.java b/functional-test/src/test/java/org/zanata/feature/account/RegisterTest.java index e6f6351f51..60dc808fa7 100644 --- a/functional-test/src/test/java/org/zanata/feature/account/RegisterTest.java +++ b/functional-test/src/test/java/org/zanata/feature/account/RegisterTest.java @@ -38,7 +38,6 @@ import org.zanata.page.utility.HomePage; import org.zanata.util.AddUsersRule; import org.zanata.util.HasEmailRule; -import org.zanata.util.rfc2822.InvalidEmailAddressRFC2822; import org.zanata.workflow.BasicWorkFlow; import static org.assertj.core.api.Assertions.assertThat; @@ -52,6 +51,7 @@ public class RegisterTest extends ZanataTestCase { @ClassRule public static AddUsersRule addUsersRule = new AddUsersRule(); + @ClassRule public static HasEmailRule emailRule = new HasEmailRule(); @@ -65,7 +65,7 @@ public void before() { // Conflicting fields - must be set for each test function to avoid // "not available" errors - fields.put("email", "test@test.com"); + fields.put("email", "test@example.com"); fields.put("username", "testusername"); fields.put("name", "test"); fields.put("password", "testpassword"); @@ -98,7 +98,7 @@ public void registerSuccessful() throws Exception { tcmsTestPlanIds = 5316, tcmsTestCaseIds = 0) @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void usernameLengthValidation() throws Exception { - fields.put("email", "length.test@test.com"); + fields.put("email", "length.test@example.com"); RegisterPage registerPage = homePage.goToRegistration(); fields.put("username", "bo"); @@ -130,31 +130,13 @@ public void usernamePreExisting() throws Exception { RegisterPage registerPage = homePage .goToRegistration() .enterUserName("admin"); - registerPage.defocus(); + registerPage.defocus(registerPage.usernameField); - assertThat(registerPage.expectError( - RegisterPage.USERNAME_UNAVAILABLE_ERROR)) + assertThat(registerPage.expectErrors()) .contains(RegisterPage.USERNAME_UNAVAILABLE_ERROR) .as("Username not available message is shown"); } - @Feature(summary = "The user must enter a valid email address to register", - tcmsTestPlanIds = 5316, tcmsTestCaseIds = 0) - @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) - public void emailValidation() throws Exception { - fields.put("email", - InvalidEmailAddressRFC2822.PLAIN_ADDRESS.toString()); - fields.put("username", "emailvalidation"); - RegisterPage registerPage = homePage - .goToRegistration() - .setFields(fields); - registerPage.defocus(); - - assertThat(registerPage.getErrors()) - .contains(RegisterPage.MALFORMED_EMAIL_ERROR) - .as("Email validation errors are shown"); - } - @Feature(summary = "The user must enter all necessary fields to register", tcmsTestPlanIds = 5316, tcmsTestCaseIds = 0) @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) diff --git a/functional-test/src/test/java/org/zanata/feature/account/UsernameValidationTest.java b/functional-test/src/test/java/org/zanata/feature/account/UsernameValidationTest.java index bcbb4b2353..2b24f349f4 100644 --- a/functional-test/src/test/java/org/zanata/feature/account/UsernameValidationTest.java +++ b/functional-test/src/test/java/org/zanata/feature/account/UsernameValidationTest.java @@ -21,14 +21,8 @@ package org.zanata.feature.account; import lombok.extern.slf4j.Slf4j; -import org.junit.Before; -import org.junit.Rule; +import org.junit.Test; import org.junit.experimental.categories.Category; -import org.junit.experimental.theories.DataPoint; -import org.junit.experimental.theories.Theories; -import org.junit.experimental.theories.Theory; -import org.junit.rules.Timeout; -import org.junit.runner.RunWith; import org.zanata.feature.Feature; import org.zanata.feature.testharness.ZanataTestCase; import org.zanata.feature.testharness.TestPlan.DetailedTest; @@ -42,85 +36,21 @@ * href="mailto:djansen@redhat.com">djansen@redhat.com */ @Slf4j -@RunWith(Theories.class) @Category(DetailedTest.class) public class UsernameValidationTest extends ZanataTestCase { - @Rule - public Timeout timeout = new Timeout(ZanataTestCase.MAX_LONG_TEST_DURATION); - - @DataPoint - public static String INVALID_PIPE = "user|name"; - @DataPoint - public static String INVALID_SLASH = "user/name"; - @DataPoint - public static String INVALID_BACKSLASH = "user\\name"; - @DataPoint - public static String INVALID_PLUS = "user+name"; - @DataPoint - public static String INVALID_ASTERISK = "user*name"; - @DataPoint - public static String INVALID_LEFT_PARENTHESES = "user(name"; - @DataPoint - public static String INVALID_RIGHT_PARENTHESES = "user)name"; - @DataPoint - public static String INVALID_DOLLAR = "user$name"; - @DataPoint - public static String INVALID_LEFT_BRACKET = "user[name"; - @DataPoint - public static String INVALID_RIGHT_BRACKET = "user]name"; - @DataPoint - public static String INVALID_COLON = "user:name"; - @DataPoint - public static String INVALID_SEMICOLON = "user;name"; - @DataPoint - public static String INVALID_APOSTROPHE = "user'name"; - @DataPoint - public static String INVALID_COMMA = "user,name"; - @DataPoint - public static String INVALID_QUESTION_MARK = "user?name"; - @DataPoint - public static String INVALID_EXCLAMATION_MARK = "user!name"; - @DataPoint - public static String INVALID_AMPERSAT = "user@name"; - @DataPoint - public static String INVALID_HASH = "user#name"; - @DataPoint - public static String INVALID_PERCENT = "user%name"; - @DataPoint - public static String INVALID_CARAT = "user^name"; - @DataPoint - public static String INVALID_EQUALS = "user=name"; - @DataPoint - public static String INVALID_PERIOD = "user.name"; - @DataPoint - public static String INVALID_LEFT_BRACE = "user{name"; - @DataPoint - public static String INVALID_RIGHT_BRACE = "user}name"; - @DataPoint - public static String INVALID_CAPITAL_A = "userAname"; - @DataPoint - public static String INVALID_CAPITAL_Z = "userZname"; - - @Before - public void setUp() { - new BasicWorkFlow().goToHome().deleteCookiesAndRefresh(); - } - @Feature(summary = "The user must enter acceptable username characters" + "to register", tcmsTestPlanIds = 5316, tcmsTestCaseIds = 0) - @Theory - public void usernameCharacterValidation(String username) throws Exception { - log.info(testName.getMethodName() + " : " + username); + @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) + public void usernameCharacterValidation() throws Exception { RegisterPage registerPage = new BasicWorkFlow() .goToHome() .goToRegistration() - .enterUserName(username); - registerPage.defocus(); + .enterUserName("user|name"); + registerPage.defocus(registerPage.usernameField); - assertThat(registerPage.expectError( - RegisterPage.USERNAME_VALIDATION_ERROR)) + assertThat(registerPage.expectErrors()) .contains(RegisterPage.USERNAME_VALIDATION_ERROR) .as("Username validation errors are shown"); } diff --git a/functional-test/src/test/java/org/zanata/feature/account/ValidEmailAddressTest.java b/functional-test/src/test/java/org/zanata/feature/account/ValidEmailAddressTest.java deleted file mode 100644 index 7ec264f11c..0000000000 --- a/functional-test/src/test/java/org/zanata/feature/account/ValidEmailAddressTest.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2013, Red Hat, Inc. and individual contributors as indicated by the - * @author tags. See the copyright.txt file in the distribution for a full - * listing of individual contributors. - * - * This is free software; you can redistribute it and/or modify it under the - * terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This software is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this software; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF - * site: http://www.fsf.org. - */ -package org.zanata.feature.account; - -import lombok.extern.slf4j.Slf4j; -import org.junit.Before; -import org.junit.Rule; -import org.junit.experimental.categories.Category; -import org.junit.experimental.theories.DataPoint; -import org.junit.experimental.theories.Theories; -import org.junit.experimental.theories.Theory; -import org.junit.rules.Timeout; -import org.junit.runner.RunWith; -import org.zanata.feature.Feature; -import org.zanata.feature.testharness.ZanataTestCase; -import org.zanata.feature.testharness.TestPlan.DetailedTest; -import org.zanata.page.account.RegisterPage; -import org.zanata.util.rfc2822.ValidEmailAddressRFC2822; -import org.zanata.workflow.BasicWorkFlow; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.zanata.util.rfc2822.ValidEmailAddressRFC2822.*; - -/** - * @author Damian Jansen djansen@redhat.com - */ -@Slf4j -@RunWith(Theories.class) -@Category(DetailedTest.class) -public class ValidEmailAddressTest extends ZanataTestCase { - - @Rule - public Timeout timeout = new Timeout(ZanataTestCase.MAX_LONG_TEST_DURATION); - - @DataPoint - public static ValidEmailAddressRFC2822 TEST_BASIC_EMAIL = BASIC_EMAIL; - @DataPoint - public static ValidEmailAddressRFC2822 TEST_SPECIAL_LOCALPART_CHARACTERS = - SPECIAL_CHARACTERS_LOCALPART; - @DataPoint - public static ValidEmailAddressRFC2822 TEST_LOCALPART_MULTIPLE_LABELS = - LOCALPART_MULTIPLE_LABELS; - @DataPoint - public static ValidEmailAddressRFC2822 TEST_DOMAIN_MULTIPLE_LABELS = - DOMAIN_MULTIPLE_LABELS; - @DataPoint - public static ValidEmailAddressRFC2822 TEST_DOMAIN_LABEL_MAX_CHARACTERS = - DOMAIN_LABEL_MAX_CHARACTERS; - @DataPoint - public static ValidEmailAddressRFC2822 TEST_LOCALPART_LABEL_MAX_CHARACTERS = - LOCALPART_LABEL_MAX_CHARACTERS; - @DataPoint - public static ValidEmailAddressRFC2822 TEST_HYPHENATED_DOMAIN_LABEL = - HYPHENATED_DOMAIN_LABEL; - @DataPoint - public static ValidEmailAddressRFC2822 TEST_HYPHENATED_LOCALPART_LABEL = - HYPHENATED_LOCALPART_LABEL; - @DataPoint - public static ValidEmailAddressRFC2822 TEST_LOCALPART_MAX_LENGTH = - LOCALPART_MAX_LENGTH; - - // BUG982048 @DataPoint public static ValidEmailAddressRFC2822 - // TEST_BASIC_QUOTED_EMAIL = BASIC_QUOTED_EMAIL; - // BUG982048 @DataPoint public static ValidEmailAddressRFC2822 - // TEST_ENCLOSED_QUOTED_LABEL = ENCLOSED_QUOTED_LABEL; - // BUG982048 @DataPoint public static ValidEmailAddressRFC2822 - // TEST_LOCALPART_EMPTY_QUOTE = LOCALPART_WITH_EMPTY_QUOTE; - // BUG982048 @DataPoint public static ValidEmailAddressRFC2822 - // TEST_QUOTED_ESCAPED_SPECIAL_CHARACTERS = - // QUOTED_ESCAPED_SPECIAL_CHARACTERS; - // BUG982048 @DataPoint public static ValidEmailAddressRFC2822 - // TEST_QUOTED_ESCAPED_QUOTES = QUOTED_ESCAPED_QUOTES; - // BUG982048 @DataPoint public static ValidEmailAddressRFC2822 - // TEST_QUOTED_WITH_SPACE = QUOTED_WITH_SPACE; - // BUG982048 @DataPoint public static ValidEmailAddressRFC2822 - // TEST_BRACKETED_DOMAIN = BRACKETED_DOMAIN; - // BUG982048 @DataPoint public static ValidEmailAddressRFC2822 - // TEST_BRACKETED_IPV4_DOMAIN = BRACKETED_IPV4_DOMAIN; - // BUG982048 @DataPoint public static ValidEmailAddressRFC2822 - // TEST_BRACKETED_IPV6_DOMAIN = BRACKETED_IPV6_DOMAIN; - - @Before - public void setUp() { - new BasicWorkFlow().goToHome().deleteCookiesAndRefresh(); - } - - @Feature(summary = "The system will allow all acceptable forms of " + - "email address for registration", - tcmsTestPlanIds = 5316, tcmsTestCaseIds = 0) - @Theory - public void validEmailAcceptance(ValidEmailAddressRFC2822 emailAddress) - throws Exception { - log.info(testName.getMethodName() + " : " + emailAddress); - RegisterPage registerPage = new BasicWorkFlow() - .goToHome() - .goToRegistration() - .enterEmail(emailAddress.toString()) - .registerFailure(); - - assertThat(RegisterPage.MALFORMED_EMAIL_ERROR) - .isNotIn(registerPage.getErrors()) - .as("Email validation errors are not shown"); - } - -} diff --git a/functional-test/src/test/java/org/zanata/feature/administration/AutoRoleAssignmentTest.java b/functional-test/src/test/java/org/zanata/feature/administration/AutoRoleAssignmentTest.java index 55b555e782..56713d8e71 100644 --- a/functional-test/src/test/java/org/zanata/feature/administration/AutoRoleAssignmentTest.java +++ b/functional-test/src/test/java/org/zanata/feature/administration/AutoRoleAssignmentTest.java @@ -54,8 +54,7 @@ public void createAutoRoleAssignment() throws Exception { .clickCreateNew() .enterIdentityPattern(".+ransla.+") .selectRole("admin") - .saveRoleAssignment() - .cancelEditRoleAssignment(); + .saveRoleAssignment(); assertThat(roleAssignmentsPage.getRulesByPattern()) .contains(".+ransla.+") diff --git a/functional-test/src/test/java/org/zanata/feature/administration/EditHomePageTest.java b/functional-test/src/test/java/org/zanata/feature/administration/EditHomePageTest.java index 0e53765684..f2b781c524 100644 --- a/functional-test/src/test/java/org/zanata/feature/administration/EditHomePageTest.java +++ b/functional-test/src/test/java/org/zanata/feature/administration/EditHomePageTest.java @@ -20,6 +20,8 @@ */ package org.zanata.feature.administration; +import org.junit.BeforeClass; +import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -28,6 +30,8 @@ import org.zanata.feature.testharness.TestPlan.DetailedTest; import org.zanata.page.utility.HomePage; import org.zanata.util.AddUsersRule; +import org.zanata.util.SampleProjectRule; +import org.zanata.workflow.BasicWorkFlow; import org.zanata.workflow.LoginWorkFlow; import static org.assertj.core.api.Assertions.assertThat; @@ -39,22 +43,32 @@ @Category(DetailedTest.class) public class EditHomePageTest extends ZanataTestCase { + @ClassRule + public static AddUsersRule addUsersRule = new AddUsersRule(); + @Rule - public AddUsersRule addUsersRule = new AddUsersRule(); + public SampleProjectRule sampleProjectRule = new SampleProjectRule(); + + @BeforeClass + public static void beforeClass() { + new BasicWorkFlow().goToHome().deleteCookiesAndRefresh(); + assertThat(new LoginWorkFlow().signIn("admin", "admin").loggedInAs()) + .isEqualTo("admin") + .as("Admin is logged in"); + } @Feature(summary = "The administrator can edit the home screen in " + "WYSIWYG mode", tcmsTestPlanIds = 5316, tcmsTestCaseIds = 0) @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void editPageContent() throws Exception { - HomePage homePage = new LoginWorkFlow() - .signIn("admin", "admin") - .goToHomePage() + HomePage homePage = new BasicWorkFlow() + .goToHome() .goToEditPageContent() - .enterText("Test") + .enterText("WYSIWYGTest") .update(); - assertThat(homePage.getMainBodyContent()).isEqualTo("Test") + assertThat(homePage.getMainBodyContent()).isEqualTo("WYSIWYGTest") .as("Homepage text has been updated"); } @@ -63,14 +77,13 @@ public void editPageContent() throws Exception { tcmsTestPlanIds = 5316, tcmsTestCaseIds = 0) @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void editPageCode() throws Exception { - HomePage homePage = new LoginWorkFlow() - .signIn("admin", "admin") - .goToHomePage() + HomePage homePage = new BasicWorkFlow() + .goToHome() .goToEditPageCode() - .enterText("Test") + .enterText("HTMLTest") .update(); - assertThat(homePage.getMainBodyContent()).isEqualTo("Test") + assertThat(homePage.getMainBodyContent()).isEqualTo("HTMLTest") .as("Homepage text has been updated"); } } diff --git a/functional-test/src/test/java/org/zanata/feature/administration/EditTranslationMemoryTest.java b/functional-test/src/test/java/org/zanata/feature/administration/EditTranslationMemoryTest.java index c95db3d0c5..aece83f5eb 100644 --- a/functional-test/src/test/java/org/zanata/feature/administration/EditTranslationMemoryTest.java +++ b/functional-test/src/test/java/org/zanata/feature/administration/EditTranslationMemoryTest.java @@ -21,9 +21,9 @@ package org.zanata.feature.administration; import java.io.File; -import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; import org.junit.Ignore; -import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; import org.openqa.selenium.Alert; @@ -47,13 +47,14 @@ @Category(DetailedTest.class) public class EditTranslationMemoryTest extends ZanataTestCase { - @Rule - public AddUsersRule addUsersRule = new AddUsersRule(); + @ClassRule + public static AddUsersRule addUsersRule = new AddUsersRule(); private TestFileGenerator testFileGenerator = new TestFileGenerator(); - @Before - public void before() { + @BeforeClass + public static void beforeClass() { + new BasicWorkFlow().goToHome().deleteCookiesAndRefresh(); assertThat(new LoginWorkFlow().signIn("admin", "admin").loggedInAs()) .isEqualTo("admin") .as("Admin is logged in"); @@ -85,28 +86,6 @@ public void createNewTranslationMemory() throws Exception { .as("The description is displayed correctly"); } - @Feature(summary = "The administrator can cancel creating a new " + - "translation memory entry", - tcmsTestPlanIds = 5316, tcmsTestCaseIds = 0) - @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) - public void abortCreate() throws Exception { - String abortName = "aborttmtest"; - String abortDescription = "abort tm description"; - - TranslationMemoryPage translationMemoryPage = new BasicWorkFlow() - .goToHome() - .goToAdministration() - .goToTranslationMemoryPage() - .clickCreateNew() - .enterMemoryID(abortName) - .enterMemoryDescription(abortDescription) - .cancelTM(); - - assertThat(translationMemoryPage.getListedTranslationMemorys()) - .doesNotContain(abortName) - .as("The Translation Memory was not created"); - } - @Feature(summary = "The administrator must use a unique identifier to " + "create a new translation memory entry", tcmsTestPlanIds = 5316, tcmsTestCaseIds = 0) @@ -127,18 +106,17 @@ public void translationMemoryIdsAreUnique() throws Exception { .enterMemoryDescription("Meh") .clickSaveAndExpectFailure(); - assertThat(translationMemoryEditPage.expectError( - TranslationMemoryPage.ID_UNAVAILABLE)) + assertThat(translationMemoryEditPage.expectErrors()) .contains(TranslationMemoryPage.ID_UNAVAILABLE) .as("The Id Is Not Available error is displayed"); translationMemoryEditPage = translationMemoryEditPage .clickSaveAndExpectFailure(); - translationMemoryEditPage.assertNoCriticalErrors(); // RHBZ-1010771 + // RHBZ-1010771 + translationMemoryEditPage.assertNoCriticalErrors(); - assertThat(translationMemoryEditPage.expectError( - TranslationMemoryPage.ID_UNAVAILABLE)) + assertThat(translationMemoryEditPage.expectErrors()) .contains(TranslationMemoryPage.ID_UNAVAILABLE) .as("The Id Is Not Available error is displayed"); } @@ -153,11 +131,13 @@ public void importTranslationMemory() throws Exception { TranslationMemoryPage tmMemoryPage = new TranslationMemoryWorkFlow() .createTranslationMemory(importTMId) + .clickOptions(importTMId) .clickImport(importTMId) .enterImportFileName(importFile.getAbsolutePath()) .clickUploadButtonAndAcknowledge(); - assertThat(tmMemoryPage.getNumberOfEntries(importTMId)).isEqualTo("1") + assertThat(tmMemoryPage.waitForExpectedNumberOfEntries(importTMId, "1")) + .isEqualTo("1") .as("The Translation Memory has one entry"); } @@ -169,6 +149,7 @@ public void rejectEmptyTranslation() throws Exception { TranslationMemoryPage tmMemoryPage = new TranslationMemoryWorkFlow() .createTranslationMemory(rejectTMId) + .clickOptions(rejectTMId) .clickImport(rejectTMId); Alert uploadError = tmMemoryPage.expectFailedUpload(); @@ -178,7 +159,8 @@ public void rejectEmptyTranslation() throws Exception { tmMemoryPage = tmMemoryPage.dismissError(); - assertThat(tmMemoryPage.getNumberOfEntries(rejectTMId)).isEqualTo("0") + assertThat(tmMemoryPage.waitForExpectedNumberOfEntries(rejectTMId, "0")) + .isEqualTo("0") .as("No change is recorded"); } @@ -195,7 +177,8 @@ public void deleteTranslationMemory() throws Exception { .contains(deleteTMId) .as("The new Translation Memory is listed"); - tmMemoryPage = tmMemoryPage.clickDeleteTmAndAccept(deleteTMId); + tmMemoryPage = tmMemoryPage.clickOptions(deleteTMId) + .clickDeleteTmAndAccept(deleteTMId); assertThat(tmMemoryPage.getListedTranslationMemorys()) .doesNotContain(deleteTMId) @@ -216,7 +199,8 @@ public void dontDeleteTranslationMemory() throws Exception { .contains(dontDeleteTMId) .as("The new Translation Memory is listed"); - tmMemoryPage = tmMemoryPage.clickDeleteTmAndCancel(dontDeleteTMId); + tmMemoryPage = tmMemoryPage.clickOptions(dontDeleteTMId) + .clickDeleteTmAndCancel(dontDeleteTMId); assertThat(tmMemoryPage.getListedTranslationMemorys()) .contains(dontDeleteTMId) @@ -227,20 +211,25 @@ public void dontDeleteTranslationMemory() throws Exception { "translation memory entry", tcmsTestPlanIds = 5316, tcmsTestCaseIds = 0) @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) + // fails intermittently + @Ignore public void clearTranslationMemory() throws Exception { String clearTMId = "cleartmtest"; File importFile = testFileGenerator.openTestFile("test-tmx.xml"); TranslationMemoryPage tmMemoryPage = new TranslationMemoryWorkFlow() .createTranslationMemory(clearTMId) + .clickOptions(clearTMId) .clickImport(clearTMId) .enterImportFileName(importFile.getAbsolutePath()) .clickUploadButtonAndAcknowledge(); - assertThat(tmMemoryPage.getNumberOfEntries(clearTMId)).isEqualTo("1") + assertThat(tmMemoryPage.waitForExpectedNumberOfEntries(clearTMId, "1")) + .isEqualTo("1") .as("The TM has one item"); - tmMemoryPage = tmMemoryPage.clickClearTMAndAccept(clearTMId); + tmMemoryPage = tmMemoryPage.clickOptions(clearTMId) + .clickClearTMAndAccept(clearTMId); assertThat(tmMemoryPage.waitForExpectedNumberOfEntries(clearTMId, "0")) .isEqualTo("0") @@ -257,16 +246,20 @@ public void dontClearTranslationMemory() throws Exception { TranslationMemoryPage tmMemoryPage = new TranslationMemoryWorkFlow() .createTranslationMemory(clearTMId) + .clickOptions(clearTMId) .clickImport(clearTMId) .enterImportFileName(importFile.getAbsolutePath()) .clickUploadButtonAndAcknowledge(); - assertThat(tmMemoryPage.getNumberOfEntries(clearTMId)).isEqualTo("1") + assertThat(tmMemoryPage.waitForExpectedNumberOfEntries(clearTMId, "1")) + .isEqualTo("1") .as("The TM has one item"); - tmMemoryPage = tmMemoryPage.clickClearTMAndCancel(clearTMId); + tmMemoryPage = tmMemoryPage.clickOptions(clearTMId) + .clickClearTMAndCancel(clearTMId); - assertThat(tmMemoryPage.getNumberOfEntries(clearTMId)).isEqualTo("1") + assertThat(tmMemoryPage.waitForExpectedNumberOfEntries(clearTMId, "1")) + .isEqualTo("1") .as("The translation memory entries count is the same"); } @@ -274,26 +267,31 @@ public void dontClearTranslationMemory() throws Exception { "entry before it can be deleted", tcmsTestPlanIds = 5316, tcmsTestCaseIds = 0) @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) - @Ignore // fails intermittently + // fails intermittently + @Ignore public void mustClearBeforeDelete() throws Exception { String forceClear = "forcecleartodelete"; File importFile = testFileGenerator.openTestFile("test-tmx.xml"); TranslationMemoryPage tmMemoryPage = new TranslationMemoryWorkFlow() .createTranslationMemory(forceClear) + .clickOptions(forceClear) .clickImport(forceClear) .enterImportFileName(importFile.getAbsolutePath()) .clickUploadButtonAndAcknowledge(); - assertThat(tmMemoryPage.getNumberOfEntries(forceClear)).isEqualTo("1") + assertThat(tmMemoryPage.waitForExpectedNumberOfEntries(forceClear, "1")) + .isEqualTo("1") .as("The TM has one item"); - assertThat(tmMemoryPage.canDelete(forceClear)).isFalse() + assertThat(tmMemoryPage.clickOptions(forceClear).canDelete(forceClear)) + .isFalse() .as("The item cannot yet be deleted"); tmMemoryPage = tmMemoryPage.clickClearTMAndAccept(forceClear); tmMemoryPage.waitForExpectedNumberOfEntries(forceClear, "0"); - assertThat(tmMemoryPage.canDelete(forceClear)).isTrue() + assertThat(tmMemoryPage.clickOptions(forceClear).canDelete(forceClear)) + .isTrue() .as("The item can be deleted"); tmMemoryPage = tmMemoryPage.clickDeleteTmAndAccept(forceClear); diff --git a/functional-test/src/test/java/org/zanata/feature/administration/ManageSearchTest.java b/functional-test/src/test/java/org/zanata/feature/administration/ManageSearchTest.java index 40bb58fe17..a749e60f45 100644 --- a/functional-test/src/test/java/org/zanata/feature/administration/ManageSearchTest.java +++ b/functional-test/src/test/java/org/zanata/feature/administration/ManageSearchTest.java @@ -21,6 +21,8 @@ package org.zanata.feature.administration; import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; @@ -30,7 +32,9 @@ import org.zanata.feature.testharness.TestPlan.DetailedTest; import org.zanata.page.administration.ManageSearchPage; import org.zanata.page.dashboard.DashboardBasePage; +import org.zanata.util.AddUsersRule; import org.zanata.util.SampleProjectRule; +import org.zanata.workflow.BasicWorkFlow; import org.zanata.workflow.LoginWorkFlow; import static org.assertj.core.api.Assertions.assertThat; @@ -38,20 +42,31 @@ @Category(DetailedTest.class) public class ManageSearchTest extends ZanataTestCase { + @ClassRule + public static AddUsersRule addUsersRule = new AddUsersRule(); + @Rule public SampleProjectRule sampleProjectRule = new SampleProjectRule(); private DashboardBasePage dashboardPage; + @BeforeClass + public static void beforeClass() { + assertThat(new LoginWorkFlow().signIn("admin", "admin").loggedInAs()) + .isEqualTo("admin") + .as("Admin is logged in"); + } + @Before public void before() { - dashboardPage = new LoginWorkFlow().signIn("admin", "admin"); + dashboardPage = new BasicWorkFlow().goToDashboard(); } @Feature(summary = "The administrator can clear and regenerate all of the " + "search indexes", tcmsTestPlanIds = 5316, tcmsTestCaseIds = 0) @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) + @Ignore("RHBZ1180948 JBoss issue") public void regenerateSearchIndexes() throws Exception { ManageSearchPage manageSearchPage = dashboardPage .goToAdministration() diff --git a/functional-test/src/test/java/org/zanata/feature/clientserver/PropertiesRoundTripTest.java b/functional-test/src/test/java/org/zanata/feature/clientserver/PropertiesRoundTripTest.java index dfb021a47f..9eceef54d1 100644 --- a/functional-test/src/test/java/org/zanata/feature/clientserver/PropertiesRoundTripTest.java +++ b/functional-test/src/test/java/org/zanata/feature/clientserver/PropertiesRoundTripTest.java @@ -88,7 +88,7 @@ public void canPushAndPullProperties() throws IOException, "properties-test", "master", "properties", Lists .newArrayList("pl")); List output = client.callWithTimeout(tempDir, - "mvn -B org.zanata:zanata-maven-plugin:push -Dzanata.srcDir=. "+ + "mvn -B org.zanata:zanata-maven-plugin:push -Dzanata.srcDir=. " + "-Dzanata.userConfig=" + userConfigPath); assertThat(client.isPushSuccessful(output)).isTrue(); diff --git a/functional-test/src/test/java/org/zanata/feature/concurrentedit/ConcurrentAccessTest.java b/functional-test/src/test/java/org/zanata/feature/concurrentedit/ConcurrentAccessTest.java index a589e9c89f..45a4a3c749 100644 --- a/functional-test/src/test/java/org/zanata/feature/concurrentedit/ConcurrentAccessTest.java +++ b/functional-test/src/test/java/org/zanata/feature/concurrentedit/ConcurrentAccessTest.java @@ -29,6 +29,8 @@ import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; +import org.jboss.resteasy.client.ClientRequest; +import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -37,14 +39,21 @@ import org.zanata.feature.testharness.TestPlan.DetailedTest; import org.zanata.rest.dto.resource.Resource; import org.zanata.util.AddUsersRule; +import org.zanata.util.Constants; +import org.zanata.util.PropertiesHolder; import org.zanata.util.ZanataRestCaller; import com.google.common.base.Function; import com.google.common.base.Throwables; import com.google.common.collect.Lists; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + import static org.assertj.core.api.Assertions.assertThat; import static org.zanata.util.ZanataRestCaller.buildSourceResource; import static org.zanata.util.ZanataRestCaller.buildTextFlow; +import static org.zanata.util.ZanataRestCaller.checkStatusAndReleaseConnection; +import static org.zanata.util.ZanataRestCaller.getStatusAndReleaseConnection; /** * @author Patrick Huang input) { throw Throwables.propagate(e); } } + + private static ClientRequest clientRequestAsAdmin(String path) { + ClientRequest clientRequest = + new ClientRequest( + PropertiesHolder.getProperty(Constants.zanataInstance + .value()) + path); + clientRequest.header("X-Auth-User", "admin"); + clientRequest.header("X-Auth-Token", + PropertiesHolder.getProperty(Constants.zanataApiKey.value())); + clientRequest.header("Content-Type", "application/xml"); + return clientRequest; + } } diff --git a/functional-test/src/test/java/org/zanata/feature/document/FileTypeUploadTest.java b/functional-test/src/test/java/org/zanata/feature/document/FileTypeUploadTest.java index 1f3d9c0f60..5f5aafdc69 100644 --- a/functional-test/src/test/java/org/zanata/feature/document/FileTypeUploadTest.java +++ b/functional-test/src/test/java/org/zanata/feature/document/FileTypeUploadTest.java @@ -21,20 +21,17 @@ package org.zanata.feature.document; import lombok.extern.slf4j.Slf4j; -import org.junit.Before; +import org.junit.BeforeClass; import org.junit.ClassRule; -import org.junit.Rule; import org.junit.experimental.categories.Category; import org.junit.experimental.theories.DataPoint; import org.junit.experimental.theories.Theories; import org.junit.experimental.theories.Theory; import org.junit.runner.RunWith; import org.zanata.feature.Feature; -import org.zanata.feature.testharness.TestPlan.BasicAcceptanceTest; import org.zanata.feature.testharness.TestPlan.DetailedTest; import org.zanata.feature.testharness.ZanataTestCase; import org.zanata.page.projectversion.VersionDocumentsPage; -import org.zanata.page.webtrans.EditorPage; import org.zanata.util.CleanDocumentStorageRule; import org.zanata.util.SampleProjectRule; import org.zanata.util.TestFileGenerator; @@ -55,26 +52,29 @@ @Category(DetailedTest.class) public class FileTypeUploadTest extends ZanataTestCase { - @Rule - public SampleProjectRule sampleProjectRule = new SampleProjectRule(); + @ClassRule + public static SampleProjectRule sampleProjectRule = new SampleProjectRule(); @ClassRule - public static CleanDocumentStorageRule documentStorageRule = - new CleanDocumentStorageRule(); + public static CleanDocumentStorageRule documentStorageRule; - @Before - public void before() { + @BeforeClass + public static void beforeClass() { + documentStorageRule = new CleanDocumentStorageRule(); new ZanataRestCaller().createProjectAndVersion("doctype-test", "doctype-upload", "File"); - new LoginWorkFlow().signIn("admin", "admin"); + assertThat(new LoginWorkFlow().signIn("admin", "admin").loggedInAs()) + .isEqualTo("admin") + .as("Admin is logged in"); } private static String testString = "Test text 1"; - private static String htmlString = ""+testString+""+ - " "; + private static String htmlString = "" + testString + + "" + " "; + @DataPoint public static File TXT_FILE = new TestFileGenerator() - .generateTestFileWithContent( "testtxtfile", ".txt", testString); + .generateTestFileWithContent("testtxtfile", ".txt", testString); @DataPoint public static File DTD_FILE = new TestFileGenerator() @@ -139,14 +139,13 @@ public void before() { .openTestFile("upload-odp.odp"); @Theory - @Category(BasicAcceptanceTest.class) @Feature(bugzilla = 980670, summary = "The administrator can upload raw files for translation", tcmsTestCaseIds = { 377743 }, - tcmsTestPlanIds = { 5316 } ) + tcmsTestPlanIds = { 5316 }) public void uploadFileTypeDocument(File testFile) throws Exception { String testFileName = testFile.getName(); - log.info("[uploadFile] "+testFileName); + log.info("[uploadFile] " + testFileName); VersionDocumentsPage versionDocumentsPage = new ProjectWorkFlow() .goToProjectByName("doctype-test") @@ -159,16 +158,8 @@ public void uploadFileTypeDocument(File testFile) throws Exception { .clickUploadDone() .gotoDocumentTab(); - assertThat(versionDocumentsPage.sourceDocumentsContains(testFileName)) + assertThat(versionDocumentsPage.waitForSourceDocsContains(testFileName)) .as("Document shows in table"); - - EditorPage editorPage = versionDocumentsPage - .gotoLanguageTab() - .translate("pl", testFileName); - - assertThat(editorPage.getMessageSourceAtRowIndex(0)) - .isEqualTo(testString) - .as("The translation source is correct"); } private static String sep() { diff --git a/functional-test/src/test/java/org/zanata/feature/document/HTMLDocumentTypeTest.java b/functional-test/src/test/java/org/zanata/feature/document/HTMLDocumentTypeTest.java index 0d48c7fbde..c94d3be49f 100644 --- a/functional-test/src/test/java/org/zanata/feature/document/HTMLDocumentTypeTest.java +++ b/functional-test/src/test/java/org/zanata/feature/document/HTMLDocumentTypeTest.java @@ -66,7 +66,7 @@ public void before() { @Feature(bugzilla = 980670, summary = "Maintainer can upload a HTML file for translation", tcmsTestCaseIds = { 377837 }, - tcmsTestPlanIds = { 5316 } ) + tcmsTestPlanIds = { 5316 }) @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void uploadHTMLFileAsMaintainer() throws Exception { File htmlfile = testFileGenerator.generateTestFileWithContent( diff --git a/functional-test/src/test/java/org/zanata/feature/document/MultiFileUploadTest.java b/functional-test/src/test/java/org/zanata/feature/document/MultiFileUploadTest.java index d3c7a94471..9e5a5682b2 100644 --- a/functional-test/src/test/java/org/zanata/feature/document/MultiFileUploadTest.java +++ b/functional-test/src/test/java/org/zanata/feature/document/MultiFileUploadTest.java @@ -24,14 +24,16 @@ import lombok.extern.slf4j.Slf4j; import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; import org.zanata.feature.testharness.ZanataTestCase; -import org.zanata.feature.testharness.TestPlan.BasicAcceptanceTest; import org.zanata.feature.testharness.TestPlan.DetailedTest; import org.zanata.page.projectversion.VersionDocumentsPage; import org.zanata.page.projectversion.versionsettings.VersionDocumentsTab; +import org.zanata.util.AddUsersRule; import org.zanata.util.CleanDocumentStorageRule; import org.zanata.util.SampleProjectRule; import org.zanata.util.TestFileGenerator; @@ -50,6 +52,9 @@ @Slf4j public class MultiFileUploadTest extends ZanataTestCase { + @ClassRule + public static AddUsersRule addUsersRule = new AddUsersRule(); + @Rule public SampleProjectRule sampleProjectRule = new SampleProjectRule(); @@ -57,16 +62,19 @@ public class MultiFileUploadTest extends ZanataTestCase { public CleanDocumentStorageRule documentStorageRule = new CleanDocumentStorageRule(); - private ZanataRestCaller zanataRestCaller; private TestFileGenerator testFileGenerator = new TestFileGenerator(); private String documentStorageDirectory; + @BeforeClass + public static void beforeClass() { + new BasicWorkFlow().goToHome().deleteCookiesAndRefresh(); + new LoginWorkFlow().signIn("admin", "admin"); + } + @Before public void before() { - new BasicWorkFlow().goToHome().deleteCookiesAndRefresh(); - zanataRestCaller = new ZanataRestCaller(); - zanataRestCaller.createProjectAndVersion("multi-upload", "multi-upload", - "file"); + new ZanataRestCaller().createProjectAndVersion("multi-upload", + "multi-upload", "file"); documentStorageDirectory = CleanDocumentStorageRule .getDocumentStoragePath() .concat(File.separator) @@ -76,43 +84,6 @@ public void before() { if (new File(documentStorageDirectory).exists()) { log.warn("Document storage directory exists (cleanup incomplete)"); } - new LoginWorkFlow().signIn("admin", "admin"); - } - - @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) - @Category(BasicAcceptanceTest.class) - public void uploadedDocumentsAreInFilesystem() { - File firstFile = testFileGenerator.generateTestFileWithContent( - "multiuploadInFilesystem", ".txt", - "This is a test file"); - File secondFile = testFileGenerator.generateTestFileWithContent( - "multiuploadInFilesystem2", ".txt", - "This is another test file"); - String testFileName = firstFile.getName(); - - VersionDocumentsTab versionDocumentsTab = new ProjectWorkFlow() - .goToProjectByName("multi-upload") - .gotoVersion("multi-upload") - .gotoSettingsTab() - .gotoSettingsDocumentsTab() - .pressUploadFileButton() - .enterFilePath(firstFile.getAbsolutePath()) - .enterFilePath(secondFile.getAbsolutePath()) - .submitUpload() - .clickUploadDone(); - - assertThat(new File(documentStorageDirectory).list().length) - .isEqualTo(2) - .as("There are two uploaded source files"); - - VersionDocumentsPage versionDocumentsPage = versionDocumentsTab - .gotoDocumentTab() - .waitForSourceDocsContains(testFileName); - - assertThat(versionDocumentsPage.getSourceDocumentNames()) - .contains(firstFile.getName()) - .contains(secondFile.getName()) - .as("The documents were uploaded"); } @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) @@ -129,6 +100,9 @@ public void removeFileFromUploadList() { .enterFilePath(keptUploadFile.getAbsolutePath()) .enterFilePath("/tmp/fakefile.txt"); + versionDocumentsTab.waitForPageSilence(); + // TODO try to eliminate this: + versionDocumentsTab.expectSomeUploadItems(); assertThat(versionDocumentsTab.getUploadList()) .contains(keptUploadFile.getName()) .contains("fakefile.txt") @@ -136,6 +110,7 @@ public void removeFileFromUploadList() { versionDocumentsTab = versionDocumentsTab.clickRemoveOn("fakefile.txt"); + versionDocumentsTab.waitForPageSilence(); assertThat(versionDocumentsTab.getUploadList()) .contains(keptUploadFile.getName()) .doesNotContain("fakefile.txt") @@ -151,4 +126,5 @@ public void removeFileFromUploadList() { .doesNotContain("fakefile.txt") .as("Only the intended file was uploaded"); } + } diff --git a/functional-test/src/test/java/org/zanata/feature/document/SubtitleDocumentTypeTest.java b/functional-test/src/test/java/org/zanata/feature/document/SubtitleDocumentTypeTest.java index 4de16bf1e9..6359fac196 100644 --- a/functional-test/src/test/java/org/zanata/feature/document/SubtitleDocumentTypeTest.java +++ b/functional-test/src/test/java/org/zanata/feature/document/SubtitleDocumentTypeTest.java @@ -20,22 +20,24 @@ */ package org.zanata.feature.document; -import org.junit.Before; +import org.junit.BeforeClass; import org.junit.ClassRule; -import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; import org.zanata.feature.testharness.TestPlan.DetailedTest; import org.zanata.feature.testharness.ZanataTestCase; -import org.zanata.page.projectversion.VersionDocumentsPage; +import org.zanata.page.projectversion.versionsettings.VersionDocumentsTab; import org.zanata.page.webtrans.EditorPage; import org.zanata.util.CleanDocumentStorageRule; import org.zanata.util.SampleProjectRule; import org.zanata.util.TestFileGenerator; import org.zanata.util.ZanataRestCaller; import org.zanata.workflow.LoginWorkFlow; +import org.zanata.workflow.ProjectWorkFlow; import java.io.File; +import java.util.HashMap; +import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; @@ -51,31 +53,29 @@ public class SubtitleDocumentTypeTest extends ZanataTestCase { @ClassRule public static SampleProjectRule sampleProjectRule = new SampleProjectRule(); - @Rule - public CleanDocumentStorageRule documentStorageRule = + @ClassRule + public static CleanDocumentStorageRule documentStorageRule = new CleanDocumentStorageRule(); - private ZanataRestCaller zanataRestCaller; - private TestFileGenerator testFileGenerator = new TestFileGenerator(); - private String sep = System.getProperty("line.separator"); + private static Map filesToTest; + private static String sep = System.getProperty("line.separator"); + + private final static String PROJECTID = "subtitle-test"; + private final static String VERSIONID = "subtitles"; - @Before - public void beforeClass() { - zanataRestCaller = new ZanataRestCaller(); - zanataRestCaller.createProjectAndVersion("subtitle-test", "subtitles", - "file"); + @BeforeClass + public static void beforeClass() { + new ZanataRestCaller().createProjectAndVersion( + PROJECTID, VERSIONID, "file"); + assertThat(new LoginWorkFlow().signIn("admin", "admin").loggedInAs()) + .isEqualTo("admin") + .as("Admin is logged in"); + uploadFiles(); } @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void similarSrtEntriesAreIndividual() throws Exception { - EditorPage editorPage = uploadAndGoToDocument(testFileGenerator - .generateTestFileWithContent("duplicationinsrtfile", ".srt", - "1" + sep + - "00:00:01,000 --> 00:00:02,000" + sep + - "Exactly the same text" + sep + sep + - "2" + sep + - "00:00:02,000 --> 00:00:03,000" + sep + - "Exactly the same text")); + EditorPage editorPage = goToEditor("duplicationinsrtfile.srt"); assertThat(editorPage.getMessageSourceAtRowIndex(0)) .isEqualTo("Exactly the same text") @@ -88,11 +88,7 @@ public void similarSrtEntriesAreIndividual() throws Exception { @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void webVttLabelsAreNotParsed() throws Exception { - EditorPage editorPage = uploadAndGoToDocument(testFileGenerator - .generateTestFileWithContent("labelledVttfile", ".vtt", - "Introduction" + sep + - "00:00:01.000 --> 00:00:02.000" + sep + - "Test subtitle 1")); + EditorPage editorPage = goToEditor("labelledVttfile.vtt"); assertThat(editorPage.getMessageSourceAtRowIndex(0)) .isEqualTo("Test subtitle 1") @@ -101,15 +97,7 @@ public void webVttLabelsAreNotParsed() throws Exception { @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void multilineSRTAreParsedCorrectly() throws Exception { - EditorPage editorPage = uploadAndGoToDocument(testFileGenerator - .generateTestFileWithContent("multilinesrtfile", ".srt", - "1" + sep + - "00:00:01,000 --> 00:00:02,000" + sep + - "Test subtitle 1" + sep + - "Test subtitle 1 line 2" + sep + sep + - "2" + sep + - "00:00:03,000 --> 00:00:04,000" + sep + - "Test subtitle 2")); + EditorPage editorPage = goToEditor("multilinesrtfile.srt"); assertThat(editorPage.getMessageSourceAtRowIndex(0)) .isEqualTo("Test subtitle 1" + sep + "Test subtitle 1 line 2") @@ -122,13 +110,7 @@ public void multilineSRTAreParsedCorrectly() throws Exception { @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void multilineVTTAreParsedCorrectly() throws Exception { - EditorPage editorPage = uploadAndGoToDocument(testFileGenerator - .generateTestFileWithContent("multilinevttfile", ".vtt", - "00:00:01.000 --> 00:00:02.000" + sep + - "Test subtitle 1" + sep + - "Test subtitle 1 line 2" + sep + sep + - "00:00:03.000 --> 00:00:04.000" + sep + - "Test subtitle 2")); + EditorPage editorPage = goToEditor("multilinevttfile.vtt"); assertThat(editorPage.getMessageSourceAtRowIndex(0)) .isEqualTo("Test subtitle 1" + sep + "Test subtitle 1 line 2") @@ -140,14 +122,7 @@ public void multilineVTTAreParsedCorrectly() throws Exception { @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void multilineSBTAreParsedCorrectly() throws Exception { - EditorPage editorPage = uploadAndGoToDocument(testFileGenerator - .generateTestFileWithContent("multilinesbtfile", ".sbt", - "00:04:35.03,00:04:38.82" + sep + - "Test subtitle 1" + sep + - "Test subtitle 1 line 2" + sep + sep + - "2" + sep + - "00:04:39.03,00:04:44.82" + sep + - "Test subtitle 2")); + EditorPage editorPage = goToEditor("multilinesbtfile.sbt"); assertThat(editorPage.getMessageSourceAtRowIndex(0)) .isEqualTo("Test subtitle 1" + sep + "Test subtitle 1 line 2") @@ -159,13 +134,7 @@ public void multilineSBTAreParsedCorrectly() throws Exception { @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void multilineSubAreParsedCorrectly() throws Exception { - EditorPage editorPage = uploadAndGoToDocument(testFileGenerator - .generateTestFileWithContent("multilinesubfile", ".sub", - "00:04:35.03,00:04:38.82" + sep + - "Test subtitle 1" + sep + - "Test subtitle 1 line 2" + sep + sep + - "00:04:39.03,00:04:44.82" + sep + - "Test subtitle 2")); + EditorPage editorPage = goToEditor("multilinesubfile.sub"); assertThat(editorPage.getMessageSourceAtRowIndex(0)) .isEqualTo("Test subtitle 1" + sep + "Test subtitle 1 line 2") @@ -177,12 +146,7 @@ public void multilineSubAreParsedCorrectly() throws Exception { @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void formattingInSrtEntries() throws Exception { - EditorPage editorPage = uploadAndGoToDocument( - testFileGenerator.generateTestFileWithContent( - "formattedsrtfile", - ".srt", - "1" + sep + "00:00:01,000 --> 00:00:02,000" + sep + - "Exactly the same text {u}and more{/u}")); + EditorPage editorPage = goToEditor("formattedsrtfile.srt"); assertThat(editorPage.getMessageSourceAtRowIndex(0)) .isEqualTo("Exactly the same text {u}and more{/u}") @@ -191,12 +155,7 @@ public void formattingInSrtEntries() throws Exception { @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void formattingInVttEntries() throws Exception { - EditorPage editorPage = uploadAndGoToDocument( - testFileGenerator.generateTestFileWithContent( - "formattedvttfile", - ".vtt", - "00:00:01.000 --> 00:00:02.000" + sep + - "Exactly the same text {u}and more{/u}")); + EditorPage editorPage = goToEditor("formattedvttfile.vtt"); assertThat(editorPage.getMessageSourceAtRowIndex(0)) .isEqualTo("Exactly the same text {u}and more{/u}") @@ -205,12 +164,7 @@ public void formattingInVttEntries() throws Exception { @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void formattingInSbtEntries() throws Exception { - EditorPage editorPage = uploadAndGoToDocument( - testFileGenerator.generateTestFileWithContent( - "formattedsbtfile", - ".sbt", - "00:04:35.03,00:04:38.82" + sep + - "Exactly the same text {u}and more{/u}")); + EditorPage editorPage = goToEditor("formattedsbtfile.sbt"); assertThat(editorPage.getMessageSourceAtRowIndex(0)) .isEqualTo("Exactly the same text {u}and more{/u}") @@ -219,51 +173,116 @@ public void formattingInSbtEntries() throws Exception { @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void formattingInSubEntries() throws Exception { - EditorPage editorPage = uploadAndGoToDocument( - testFileGenerator.generateTestFileWithContent( - "formattedsubfile", - ".sub", - "00:04:35.03,00:04:38.82" + sep + - "Exactly the same text {u}and more{/u}")); + EditorPage editorPage = goToEditor("formattedsubfile.sub"); assertThat(editorPage.getMessageSourceAtRowIndex(0)) .isEqualTo("Exactly the same text {u}and more{/u}") .as("The translation source is correct"); } - /* - * Upload and open the test file in the editor for verification - */ - private EditorPage uploadAndGoToDocument(File testFile) { - VersionDocumentsPage versionDocumentsPage = new LoginWorkFlow() - .signIn("admin", "admin") - .goToProjects() - .goToProject("subtitle-test") - .gotoVersion("subtitles") + private EditorPage goToEditor(String filename) { + return new ProjectWorkFlow().goToProjectByName(PROJECTID) + .gotoVersion(VERSIONID) + .translate("pl", filesToTest.get(filename).getName()); + } + + private static void uploadFiles() { + filesToTest = new HashMap<>(); + TestFileGenerator testFileGenerator = new TestFileGenerator(); + addTestFile("duplicationinsrtfile.srt", + testFileGenerator.generateTestFileWithContent( + "duplicationinsrtfile", ".srt", + "1" + sep + + "00:00:01,000 --> 00:00:02,000" + sep + + "Exactly the same text" + sep + sep + + "2" + sep + + "00:00:02,000 --> 00:00:03,000" + sep + + "Exactly the same text")); + + addTestFile("labelledVttfile.vtt", + testFileGenerator.generateTestFileWithContent( + "labelledVttfile", ".vtt", + "Introduction" + sep + + "00:00:01.000 --> 00:00:02.000" + sep + + "Test subtitle 1")); + addTestFile("multilinesrtfile.srt", + testFileGenerator.generateTestFileWithContent( + "multilinesrtfile", ".srt", + "1" + sep + + "00:00:01,000 --> 00:00:02,000" + sep + + "Test subtitle 1" + sep + + "Test subtitle 1 line 2" + sep + sep + + "2" + sep + + "00:00:03,000 --> 00:00:04,000" + sep + + "Test subtitle 2")); + addTestFile("multilinevttfile.vtt", + testFileGenerator.generateTestFileWithContent( + "multilinevttfile", ".vtt", + "00:00:01.000 --> 00:00:02.000" + sep + + "Test subtitle 1" + sep + + "Test subtitle 1 line 2" + sep + sep + + "00:00:03.000 --> 00:00:04.000" + sep + + "Test subtitle 2")); + addTestFile("multilinesbtfile.sbt", + testFileGenerator.generateTestFileWithContent( + "multilinesbtfile", ".sbt", + "00:04:35.03,00:04:38.82" + sep + + "Test subtitle 1" + sep + + "Test subtitle 1 line 2" + sep + sep + + "2" + sep + + "00:04:39.03,00:04:44.82" + sep + + "Test subtitle 2")); + addTestFile("multilinesubfile.sub", + testFileGenerator.generateTestFileWithContent( + "multilinesubfile", ".sub", + "00:04:35.03,00:04:38.82" + sep + + "Test subtitle 1" + sep + + "Test subtitle 1 line 2" + sep + sep + + "00:04:39.03,00:04:44.82" + sep + + "Test subtitle 2")); + addTestFile("formattedsrtfile.srt", + testFileGenerator.generateTestFileWithContent( + "formattedsrtfile", + ".srt", + "1" + sep + "00:00:01,000 --> 00:00:02,000" + sep + + "Exactly the same text {u}and more{/u}")); + addTestFile("formattedvttfile.vtt", + testFileGenerator.generateTestFileWithContent( + "formattedvttfile", + ".vtt", + "00:00:01.000 --> 00:00:02.000" + sep + + "Exactly the same text {u}and more{/u}")); + addTestFile("formattedsbtfile.sbt", + testFileGenerator.generateTestFileWithContent( + "formattedsbtfile", + ".sbt", + "00:04:35.03,00:04:38.82" + sep + + "Exactly the same text {u}and more{/u}")); + addTestFile("formattedsubfile.sub", + testFileGenerator.generateTestFileWithContent( + "formattedsubfile", ".sub", + "00:04:35.03,00:04:38.82" + sep + + "Exactly the same text {u}and more{/u}")); + VersionDocumentsTab versionDocumentsTab = new ProjectWorkFlow() + .goToProjectByName(PROJECTID) + .gotoVersion(VERSIONID) .gotoSettingsTab() .gotoSettingsDocumentsTab() - .pressUploadFileButton() - .enterFilePath(testFile.getAbsolutePath()) - .submitUpload() - .clickUploadDone() - .gotoDocumentTab(); - - assertThat(versionDocumentsPage.sourceDocumentsContains(testFile - .getName())).as("Document shows in table"); - - return versionDocumentsPage - .gotoLanguageTab() - .translate("pl", testFile.getName()); + .pressUploadFileButton(); + for (File file : filesToTest.values()) { + versionDocumentsTab = versionDocumentsTab + .enterFilePath(file.getAbsolutePath()); + } + versionDocumentsTab.submitUpload().clickUploadDone(); } - private boolean storageIsClean(String documentStorageDirectory) { - File testDir; - try { - testDir = new File(documentStorageDirectory); - return testDir.listFiles().equals(null) || - testDir.listFiles().length == 0; - } catch (NullPointerException npe) { - return true; - } + private static void addTestFile(String key, File testFile) { + assertThat(filesToTest.containsKey(key)) + .isFalse() + .as("Test file entry is unique"); + filesToTest.put(key, testFile); + assertThat(filesToTest.get(key).exists()) + .isTrue() + .as("File was created"); } } diff --git a/functional-test/src/test/java/org/zanata/feature/document/UploadTest.java b/functional-test/src/test/java/org/zanata/feature/document/UploadTest.java index 57a876f962..4245a98ea0 100644 --- a/functional-test/src/test/java/org/zanata/feature/document/UploadTest.java +++ b/functional-test/src/test/java/org/zanata/feature/document/UploadTest.java @@ -24,6 +24,8 @@ import lombok.extern.slf4j.Slf4j; import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; @@ -33,6 +35,7 @@ import org.zanata.feature.testharness.TestPlan.DetailedTest; import org.zanata.page.projectversion.VersionDocumentsPage; import org.zanata.page.projectversion.versionsettings.VersionDocumentsTab; +import org.zanata.util.AddUsersRule; import org.zanata.util.CleanDocumentStorageRule; import org.zanata.util.SampleProjectRule; import org.zanata.util.TestFileGenerator; @@ -52,6 +55,9 @@ @Slf4j public class UploadTest extends ZanataTestCase { + @ClassRule + public static AddUsersRule addUsersRule = new AddUsersRule(); + @Rule public SampleProjectRule sampleProjectRule = new SampleProjectRule(); @@ -59,15 +65,18 @@ public class UploadTest extends ZanataTestCase { public CleanDocumentStorageRule documentStorageRule = new CleanDocumentStorageRule(); - private ZanataRestCaller zanataRestCaller; private TestFileGenerator testFileGenerator = new TestFileGenerator(); private String documentStorageDirectory; + @BeforeClass + public static void beforeClass() { + new BasicWorkFlow().goToHome().deleteCookiesAndRefresh(); + new LoginWorkFlow().signIn("admin", "admin"); + } + @Before public void before() { - new BasicWorkFlow().goToHome().deleteCookiesAndRefresh(); - zanataRestCaller = new ZanataRestCaller(); - zanataRestCaller.createProjectAndVersion("uploadtest", + new ZanataRestCaller().createProjectAndVersion("uploadtest", "txt-upload", "file"); documentStorageDirectory = CleanDocumentStorageRule .getDocumentStoragePath() @@ -78,46 +87,6 @@ public void before() { if (new File(documentStorageDirectory).exists()) { log.warn("Document storage directory exists (cleanup incomplete)"); } - new LoginWorkFlow().signIn("admin", "admin"); - } - - @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) - @Category(BasicAcceptanceTest.class) - public void uploadedDocumentIsInFilesystem() { - File originalFile = - testFileGenerator.generateTestFileWithContent( - "uploadedDocumentIsInFilesystem", ".txt", - "This is a test file"); - String testFileName = originalFile.getName(); - - VersionDocumentsTab versionDocumentsTab = new ProjectWorkFlow() - .goToProjectByName("uploadtest") - .gotoVersion("txt-upload") - .gotoSettingsTab() - .gotoSettingsDocumentsTab() - .pressUploadFileButton() - .enterFilePath(originalFile.getAbsolutePath()) - .submitUpload() - .clickUploadDone(); - - assertThat(new File(documentStorageDirectory).list().length) - .isEqualTo(1) - .as("There is only one uploaded source file"); - - File newlyCreatedFile = new File(documentStorageDirectory, - testFileGenerator - .getFirstFileNameInDirectory(documentStorageDirectory)); - - assertThat(testFileGenerator.getTestFileContent(newlyCreatedFile)) - .isEqualTo("This is a test file") - .as("The contents of the file were also uploaded"); - VersionDocumentsPage versionDocumentsPage = versionDocumentsTab - .gotoDocumentTab() - .waitForSourceDocsContains(testFileName); - - assertThat(versionDocumentsPage.sourceDocumentsContains(testFileName)) - .isTrue() - .as("Document shows in table"); } @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) diff --git a/functional-test/src/test/java/org/zanata/feature/editor/TranslateHTMLTest.java b/functional-test/src/test/java/org/zanata/feature/editor/TranslateHTMLTest.java index 6403e46b21..a1b3ca36f1 100644 --- a/functional-test/src/test/java/org/zanata/feature/editor/TranslateHTMLTest.java +++ b/functional-test/src/test/java/org/zanata/feature/editor/TranslateHTMLTest.java @@ -23,6 +23,8 @@ import java.io.File; import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; import org.junit.Rule; import org.junit.experimental.categories.Category; import org.junit.experimental.theories.DataPoint; @@ -56,14 +58,14 @@ public class TranslateHTMLTest extends ZanataTestCase { @Rule public Timeout timeout = new Timeout(ZanataTestCase.MAX_LONG_TEST_DURATION); - @Rule - public SampleProjectRule sampleProjectRule = new SampleProjectRule(); + @ClassRule + public static SampleProjectRule sampleProjectRule = new SampleProjectRule(); - @Rule - public CleanDocumentStorageRule documentStorageRule = + @ClassRule + public static CleanDocumentStorageRule documentStorageRule = new CleanDocumentStorageRule(); - private ZanataRestCaller zanataRestCaller; + private ZanataRestCaller zanataRestCaller = new ZanataRestCaller(); private TestFileGenerator testFileGenerator = new TestFileGenerator(); @DataPoint @@ -71,16 +73,17 @@ public class TranslateHTMLTest extends ZanataTestCase { @DataPoint public static String TEST_html = "html"; - @Before - public void before() { - zanataRestCaller = new ZanataRestCaller(); + @BeforeClass + public static void beforeClass() { new BasicWorkFlow().goToHome().deleteCookiesAndRefresh(); assumeFalse( "", new File(CleanDocumentStorageRule.getDocumentStoragePath() .concat(File.separator).concat("documents") .concat(File.separator)).exists()); - new LoginWorkFlow().signIn("admin", "admin"); + assertThat(new LoginWorkFlow().signIn("admin", "admin").loggedInAs()) + .isEqualTo("admin") + .as("Admin is logged in"); } @Feature(summary = "The user can translate HyperText Markup Language files", @@ -90,11 +93,11 @@ public void translateBasicHTMLFile(String extension) { File testfile = testFileGenerator.generateTestFileWithContent( "basichtml", "." + extension, "Line One

Line Two

Line Three"); - zanataRestCaller.createProjectAndVersion(extension+"-translate", + zanataRestCaller.createProjectAndVersion(extension + "-translate", extension, "file"); EditorPage editorPage = new ProjectWorkFlow() - .goToProjectByName(extension+"-translate") + .goToProjectByName(extension + "-translate") .gotoVersion(extension) .gotoSettingsTab() .gotoSettingsDocumentsTab() diff --git a/functional-test/src/test/java/org/zanata/feature/editor/TranslateOpenOfficeTest.java b/functional-test/src/test/java/org/zanata/feature/editor/TranslateOpenOfficeTest.java index bbf4d7fa4e..9a172f5727 100644 --- a/functional-test/src/test/java/org/zanata/feature/editor/TranslateOpenOfficeTest.java +++ b/functional-test/src/test/java/org/zanata/feature/editor/TranslateOpenOfficeTest.java @@ -23,6 +23,8 @@ import java.io.File; import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; import org.junit.Rule; import org.junit.experimental.categories.Category; import org.junit.experimental.theories.DataPoint; @@ -56,14 +58,14 @@ public class TranslateOpenOfficeTest extends ZanataTestCase { @Rule public Timeout timeout = new Timeout(ZanataTestCase.MAX_LONG_TEST_DURATION); - @Rule - public SampleProjectRule sampleProjectRule = new SampleProjectRule(); + @ClassRule + public static SampleProjectRule sampleProjectRule = new SampleProjectRule(); - @Rule - public CleanDocumentStorageRule documentStorageRule = + @ClassRule + public static CleanDocumentStorageRule documentStorageRule = new CleanDocumentStorageRule(); - private ZanataRestCaller zanataRestCaller; + private ZanataRestCaller zanataRestCaller = new ZanataRestCaller(); private TestFileGenerator testFileGenerator = new TestFileGenerator(); @DataPoint @@ -73,16 +75,17 @@ public class TranslateOpenOfficeTest extends ZanataTestCase { @DataPoint public static String TEST_ODP = "odp"; - @Before - public void before() { - zanataRestCaller = new ZanataRestCaller(); + @BeforeClass + public static void beforeClass() { new BasicWorkFlow().goToHome().deleteCookiesAndRefresh(); assumeFalse( "", new File(CleanDocumentStorageRule.getDocumentStoragePath() .concat(File.separator).concat("documents") .concat(File.separator)).exists()); - new LoginWorkFlow().signIn("admin", "admin"); + assertThat(new LoginWorkFlow().signIn("admin", "admin").loggedInAs()) + .isEqualTo("admin") + .as("Admin is logged in"); } @Feature(summary = "The user can translate OpenOffice files", @@ -91,10 +94,10 @@ public void before() { public void translateBasicOpenOfficeFile(String extension) { File testfile = testFileGenerator .openTestFile("test-" + extension + "." + extension); - zanataRestCaller.createProjectAndVersion(extension+"-translate", + zanataRestCaller.createProjectAndVersion(extension + "-translate", extension, "file"); EditorPage editorPage = new ProjectWorkFlow() - .goToProjectByName(extension+"-translate") + .goToProjectByName(extension + "-translate") .gotoVersion(extension) .gotoSettingsTab() .gotoSettingsDocumentsTab() diff --git a/functional-test/src/test/java/org/zanata/feature/editor/TranslateTextTest.java b/functional-test/src/test/java/org/zanata/feature/editor/TranslateTextTest.java index 4ac4cd89f1..d308b10a0b 100644 --- a/functional-test/src/test/java/org/zanata/feature/editor/TranslateTextTest.java +++ b/functional-test/src/test/java/org/zanata/feature/editor/TranslateTextTest.java @@ -76,7 +76,7 @@ public void before() { @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void translateBasicTextFile() { File testfile = testFileGenerator.generateTestFileWithContent( - "basictext",".txt", + "basictext", ".txt", "Line One\nLine Two\nLine Three"); zanataRestCaller.createProjectAndVersion("txt-translate", "txt", "file"); EditorPage editorPage = new ProjectWorkFlow() diff --git a/functional-test/src/test/java/org/zanata/feature/editor/TranslationHistoryTest.java b/functional-test/src/test/java/org/zanata/feature/editor/TranslationHistoryTest.java index 1ca4d6a30b..b587e3934f 100644 --- a/functional-test/src/test/java/org/zanata/feature/editor/TranslationHistoryTest.java +++ b/functional-test/src/test/java/org/zanata/feature/editor/TranslationHistoryTest.java @@ -66,7 +66,8 @@ public void showTranslationHistory() { } @Test - @Ignore // fails intermittently + // fails intermittently + @Ignore public void compareTranslationHistory() { new LoginWorkFlow().signIn("admin", "admin"); EditorPage editorPage = new ProjectWorkFlow() diff --git a/functional-test/src/test/java/org/zanata/feature/glossary/GlossaryDeleteTest.java b/functional-test/src/test/java/org/zanata/feature/glossary/GlossaryDeleteTest.java index b224e1ac26..9f57856ef0 100644 --- a/functional-test/src/test/java/org/zanata/feature/glossary/GlossaryDeleteTest.java +++ b/functional-test/src/test/java/org/zanata/feature/glossary/GlossaryDeleteTest.java @@ -57,11 +57,11 @@ public class GlossaryDeleteTest extends ZanataTestCase { private String userConfigPath; private String basicUserConfigPath; - private String pushCommand = "mvn --batch-mode zanata:glossary-push "+ - "-Dglossary.lang=hi -Dzanata.glossaryFile=compendium.csv "+ + private String pushCommand = "mvn --batch-mode zanata:glossary-push " + + "-Dglossary.lang=hi -Dzanata.glossaryFile=compendium.csv " + "-Dzanata.userConfig="; - private String deleteCommand = "mvn --batch-mode zanata:glossary-delete "+ + private String deleteCommand = "mvn --batch-mode zanata:glossary-delete " + "-Dzanata.lang=hi -Dzanata.userConfig="; @Before diff --git a/functional-test/src/test/java/org/zanata/feature/glossary/GlossaryPushTest.java b/functional-test/src/test/java/org/zanata/feature/glossary/GlossaryPushTest.java index ccd7e29e68..51240a7ea4 100644 --- a/functional-test/src/test/java/org/zanata/feature/glossary/GlossaryPushTest.java +++ b/functional-test/src/test/java/org/zanata/feature/glossary/GlossaryPushTest.java @@ -57,12 +57,12 @@ public class GlossaryPushTest extends ZanataTestCase { private String userConfigPath; private String basicUserConfigPath; - private String pushCommand = "mvn --batch-mode zanata:glossary-push "+ - "-Dglossary.lang=fr -Dzanata.glossaryFile=compendium_fr.po "+ + private String pushCommand = "mvn --batch-mode zanata:glossary-push " + + "-Dglossary.lang=fr -Dzanata.glossaryFile=compendium_fr.po " + "-Dzanata.userConfig="; - private String pushCSVCommand = "mvn --batch-mode zanata:glossary-push "+ - "-Dzanata.glossaryFile=compendium_invalid.csv -Dglossary.lang=hi "+ + private String pushCSVCommand = "mvn --batch-mode zanata:glossary-push " + + "-Dzanata.glossaryFile=compendium_invalid.csv -Dglossary.lang=hi " + "-Dzanata.userConfig="; @Before diff --git a/functional-test/src/test/java/org/zanata/feature/infrastructure/RetryRuleTest.java b/functional-test/src/test/java/org/zanata/feature/infrastructure/RetryRuleTest.java deleted file mode 100644 index 294a982364..0000000000 --- a/functional-test/src/test/java/org/zanata/feature/infrastructure/RetryRuleTest.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2013, Red Hat, Inc. and individual contributors as indicated by the - * @author tags. See the copyright.txt file in the distribution for a full - * listing of individual contributors. - * - * This is free software; you can redistribute it and/or modify it under the - * terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This software is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this software; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF - * site: http://www.fsf.org. - */ - -package org.zanata.feature.infrastructure; - -import org.hamcrest.Matchers; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.zanata.feature.testharness.ZanataTestCase; -import org.zanata.feature.testharness.TestPlan.BasicAcceptanceTest; -import org.zanata.util.RetryRule; - -import static org.hamcrest.MatcherAssert.assertThat; - -/** - * @author Damian Jansen djansen@redhat.com - */ -@Category(BasicAcceptanceTest.class) -public class RetryRuleTest extends ZanataTestCase { - - @Rule - public RetryRule retryRule = new RetryRule(2); - - @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) - public void retryPassAfterFail() { - // Fail on the first execution, but pass on the second - assertThat("Current try is greater than 1", retryRule.currentTry(), - Matchers.greaterThan(1)); - // Can only pass on second execution - assertThat("This is the second try", retryRule.currentTry(), - Matchers.equalTo(2)); - } - - @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) - public void passWillPass() { - assertThat("A normal passing test will pass", true); - assertThat("And pass on the first try", retryRule.currentTry(), - Matchers.equalTo(1)); - } - - @Test(expected = AssertionError.class) - public void retryFailsWhenAllTriesFail() throws Exception { - // Fail the first execution - if (retryRule.currentTry() == 1) { - throw new Exception(); - } - // Passes on the second execution, expect-fails on the third - assertThat("The execution count is correct", retryRule.currentTry(), - Matchers.equalTo(2)); - // Fails on the second execution - throw new Exception(); - } -} diff --git a/functional-test/src/test/java/org/zanata/feature/language/AddLanguageTest.java b/functional-test/src/test/java/org/zanata/feature/language/AddLanguageTest.java index bf56632a83..974c090bc5 100644 --- a/functional-test/src/test/java/org/zanata/feature/language/AddLanguageTest.java +++ b/functional-test/src/test/java/org/zanata/feature/language/AddLanguageTest.java @@ -24,6 +24,8 @@ import java.util.List; import java.util.Map; +import org.junit.BeforeClass; +import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -31,8 +33,10 @@ import org.zanata.feature.testharness.ZanataTestCase; import org.zanata.feature.testharness.TestPlan.DetailedTest; import org.zanata.page.administration.AddLanguagePage; -import org.zanata.page.administration.ManageLanguagePage; +import org.zanata.page.languages.LanguagesPage; +import org.zanata.util.AddUsersRule; import org.zanata.util.SampleProjectRule; +import org.zanata.workflow.BasicWorkFlow; import org.zanata.workflow.LoginWorkFlow; import static org.assertj.core.api.Assertions.assertThat; @@ -44,39 +48,50 @@ @Category(DetailedTest.class) public class AddLanguageTest extends ZanataTestCase { + @ClassRule + public static AddUsersRule addUsersRule = new AddUsersRule(); + @Rule public SampleProjectRule sampleProjectRule = new SampleProjectRule(); + @BeforeClass + public static void beforeClass() { + new BasicWorkFlow().goToHome().deleteCookiesAndRefresh(); + assertThat(new LoginWorkFlow().signIn("admin", "admin").loggedInAs()) + .isEqualTo("admin") + .as("Admin is logged in"); + } + @Feature(summary = "The administrator can add a language to Zanata", tcmsTestPlanIds = 5316, tcmsTestCaseIds = 181709) @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) - public void addLanguageAsEnabled() throws Exception { + public void addLanguageAsEnabledByDefault() throws Exception { String language = "Goa'uld"; String languageDisplayName = "goa'uld[Goa'uld]"; - ManageLanguagePage manageLanguagePage = new LoginWorkFlow() - .signIn("admin", "admin") - .goToHomePage() - .goToAdministration() - .goToManageLanguagePage(); + LanguagesPage languagesPage = new BasicWorkFlow() + .goToHome() + .goToLanguages(); - assertThat(manageLanguagePage.getLanguageLocales()) + assertThat(languagesPage.getLanguageLocales()) .doesNotContain(language) .as("The language is not listed"); - manageLanguagePage = manageLanguagePage + languagesPage = languagesPage + .clickMoreActions() .addNewLanguage() - .inputLanguage(language) + .enterSearchLanguage(language) + .waitForPluralsWarning() .saveLanguage(); - assertThat(manageLanguagePage.getLanguageLocales()) + assertThat(languagesPage.getLanguageLocales()) .contains(language) .as("The language is listed"); - assertThat(manageLanguagePage.languageIsEnabled(language)) + assertThat(languagesPage.languageIsEnabledByDefault(language)) .isTrue() .as("The language is enabled by default"); - List enabledLocaleList = manageLanguagePage + List enabledLocaleList = languagesPage .goToHomePage() .goToProjects() .goToProject("about fedora") @@ -93,33 +108,34 @@ public void addLanguageAsEnabled() throws Exception { @Feature(summary = "The administrator can add a disabled language to Zanata", tcmsTestPlanIds = 5316, tcmsTestCaseIds = 181709) @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) - public void addLanguageAsDisabled() throws Exception { + public void addLanguageAsDisabledByDefault() throws Exception { String language = "Klingon"; String languageDisplayName = "klingon[Klingon]"; - ManageLanguagePage manageLanguagePage = new LoginWorkFlow() - .signIn("admin", "admin") + LanguagesPage languagesPage = new BasicWorkFlow() + .goToHome() .goToHomePage() - .goToAdministration() - .goToManageLanguagePage(); + .goToLanguages(); - assertThat(manageLanguagePage.getLanguageLocales()) + assertThat(languagesPage.getLanguageLocales()) .doesNotContain(language) .as("The language is not listed"); - manageLanguagePage = manageLanguagePage + languagesPage = languagesPage + .clickMoreActions() .addNewLanguage() - .inputLanguage(language) - .disableLanguageByDefault() + .enterSearchLanguage(language) + .waitForPluralsWarning() + .enableLanguageByDefault(false) .saveLanguage(); - assertThat(manageLanguagePage.getLanguageLocales()) + assertThat(languagesPage.getLanguageLocales()) .contains(language) .as("The language is listed"); - assertThat(manageLanguagePage.languageIsEnabled(language)) + assertThat(languagesPage.languageIsEnabledByDefault(language)) .isFalse() .as("The language is disabled by default"); - List enabledLocaleList = manageLanguagePage.goToHomePage() + List enabledLocaleList = languagesPage.goToHomePage() .goToProjects() .goToProject("about fedora") .gotoSettingsTab() @@ -137,19 +153,19 @@ public void addLanguageAsDisabled() throws Exception { @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void addKnownLanguage() throws Exception { String language = "ru-RU"; - ManageLanguagePage manageLanguagePage = new LoginWorkFlow() - .signIn("admin", "admin") - .goToHomePage() - .goToAdministration() - .goToManageLanguagePage(); + LanguagesPage languagesPage = new BasicWorkFlow() + .goToHome() + .goToLanguages(); - assertThat(manageLanguagePage.getLanguageLocales()) + assertThat(languagesPage.getLanguageLocales()) .doesNotContain(language) .as("The language is not listed"); - AddLanguagePage addLanguagePage = manageLanguagePage + AddLanguagePage addLanguagePage = languagesPage + .clickMoreActions() .addNewLanguage() - .inputLanguage("ru-RU"); + .enterSearchLanguage("ru-RU") + .selectSearchLanguage("ru-RU"); Map languageInfo = addLanguagePage.getLanguageDetails(); diff --git a/functional-test/src/test/java/org/zanata/feature/language/ContactLanguageTeamTest.java b/functional-test/src/test/java/org/zanata/feature/language/ContactLanguageTeamTest.java index d78886e7f4..bbf575ffe1 100644 --- a/functional-test/src/test/java/org/zanata/feature/language/ContactLanguageTeamTest.java +++ b/functional-test/src/test/java/org/zanata/feature/language/ContactLanguageTeamTest.java @@ -56,6 +56,7 @@ public void translatorContactsLanguageTeamCoordinator() throws Exception { .signIn("translator", "translator") .goToLanguages() .selectLanguage("fr") + .clickMoreActions() .clickContactCoordinatorsButton() .enterSubject("contact test") .enterMessage("I love Zanata") @@ -64,7 +65,7 @@ public void translatorContactsLanguageTeamCoordinator() throws Exception { List messages = emailRule.getMessages(); assertThat(messages.size()) - .isEqualTo(1) + .isGreaterThanOrEqualTo(1) .as("One email was sent"); WiserMessage wiserMessage = messages.get(0); diff --git a/functional-test/src/test/java/org/zanata/feature/language/JoinLanguageTeamTest.java b/functional-test/src/test/java/org/zanata/feature/language/JoinLanguageTeamTest.java index dc9b8d98a9..fd4d9b21cf 100644 --- a/functional-test/src/test/java/org/zanata/feature/language/JoinLanguageTeamTest.java +++ b/functional-test/src/test/java/org/zanata/feature/language/JoinLanguageTeamTest.java @@ -20,17 +20,22 @@ */ package org.zanata.feature.language; -import org.junit.ClassRule; +import java.util.concurrent.TimeUnit; + +import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; +import org.subethamail.wiser.WiserMessage; import org.zanata.feature.Feature; import org.zanata.feature.testharness.ZanataTestCase; import org.zanata.feature.testharness.TestPlan.DetailedTest; -import org.zanata.page.administration.ManageLanguageTeamMemberPage; +import org.zanata.page.languages.LanguagePage; +import org.zanata.util.HasEmailRule; import org.zanata.util.SampleProjectRule; import org.zanata.workflow.LoginWorkFlow; import static org.assertj.core.api.Assertions.assertThat; +import static org.zanata.util.HasEmailRule.getEmailContent; /** * @author Damian Jansen @@ -39,23 +44,34 @@ @Category(DetailedTest.class) public class JoinLanguageTeamTest extends ZanataTestCase { - @ClassRule - public static SampleProjectRule sampleProjectRule = new SampleProjectRule(); + @Rule + public SampleProjectRule sampleProjectRule = new SampleProjectRule(); + + @Rule + public HasEmailRule hasEmailRule = new HasEmailRule(); @Feature(summary = "The administrator can add a member to a language team", tcmsTestPlanIds = 5316, tcmsTestCaseIds = 181703) @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void translatorJoinsLanguageTeam() throws Exception { - ManageLanguageTeamMemberPage manageTeamMemberPage = new LoginWorkFlow() - .signIn("admin", "admin") - .goToAdministration() - .goToManageLanguagePage() - .manageTeamMembersFor("pl") + LanguagePage languagePage = new LoginWorkFlow() + .signIn("admin", "admin").goToLanguages() + .gotoLanguagePage("pl") + .gotoMembersTab() + .clickMembersTabMoreActions() .clickAddTeamMember() - .searchPersonAndAddToTeam("translator"); + .searchPersonAndAddToTeam("translator", + LanguagePage.TeamPermission.Translator, + LanguagePage.TeamPermission.Reviewer); - assertThat(manageTeamMemberPage.getMemberUsernames()) + assertThat(languagePage.getMemberUsernames()) .contains("translator") .as("Translator is a listed member of the pl team"); + assertThat(hasEmailRule.emailsArrivedWithinTimeout(1, 5, + TimeUnit.SECONDS)) + .isTrue(); + WiserMessage emailMessage = hasEmailRule.getMessages().get(0); + assertThat(getEmailContent(emailMessage)) + .contains("Administrator has changed your permissions"); } } diff --git a/functional-test/src/test/java/org/zanata/feature/manual/CopyTransTuningTest.java b/functional-test/src/test/java/org/zanata/feature/manual/CopyTransTuningTest.java index d5707d757e..97367a3679 100644 --- a/functional-test/src/test/java/org/zanata/feature/manual/CopyTransTuningTest.java +++ b/functional-test/src/test/java/org/zanata/feature/manual/CopyTransTuningTest.java @@ -94,10 +94,10 @@ public void setUp() throws Exception { TranslationsResource translation4 = buildTranslationResource(generateTextFlowTargets( numOfTextFlows)); - translations = new Pair[] { Pair.of(message1, translation1)/*, - Pair.of(message2, translation2), - Pair.of(message3, translation3), - Pair.of(message4, translation4)*/ }; + translations = new Pair[] { Pair.of(message1, translation1) }; + // , Pair.of(message2, translation2), + // Pair.of(message3, translation3), + // Pair.of(message4, translation4) }; } private static TextFlow[] generateTextFlows(int numOfTextFlows) { diff --git a/functional-test/src/test/java/org/zanata/feature/misc/ContactAdminTest.java b/functional-test/src/test/java/org/zanata/feature/misc/ContactAdminTest.java index 673aa709ff..eae2edb54d 100644 --- a/functional-test/src/test/java/org/zanata/feature/misc/ContactAdminTest.java +++ b/functional-test/src/test/java/org/zanata/feature/misc/ContactAdminTest.java @@ -58,8 +58,10 @@ public class ContactAdminTest extends ZanataTestCase { public void testContactAdmin() { DashboardBasePage dashboard = new LoginWorkFlow().signIn("translator", "translator"); - ContactAdminFormPage contactAdminFormPage = - dashboard.goToHelp().clickContactAdmin(); + ContactAdminFormPage contactAdminFormPage = dashboard + .goToHelp() + .clickMoreActions() + .clickContactAdmin(); HelpPage helpPage = contactAdminFormPage .inputSubject("hello admin") @@ -74,7 +76,7 @@ public void testContactAdmin() { List messages = emailRule.getMessages(); assertThat(messages.size()) - .isEqualTo(1) + .isGreaterThanOrEqualTo(1) .as("One email was sent"); WiserMessage wiserMessage = messages.get(0); diff --git a/functional-test/src/test/java/org/zanata/feature/misc/RateLimitRestAndUITest.java b/functional-test/src/test/java/org/zanata/feature/misc/RateLimitRestAndUITest.java index ec14cb48fc..1b1c030046 100644 --- a/functional-test/src/test/java/org/zanata/feature/misc/RateLimitRestAndUITest.java +++ b/functional-test/src/test/java/org/zanata/feature/misc/RateLimitRestAndUITest.java @@ -44,7 +44,6 @@ import org.zanata.util.Constants; import org.zanata.util.PropertiesHolder; import org.zanata.util.ZanataRestCaller; -import org.zanata.workflow.BasicWorkFlow; import org.zanata.workflow.LoginWorkFlow; import com.google.common.base.Function; import com.google.common.base.Throwables; diff --git a/functional-test/src/test/java/org/zanata/feature/project/CreateProjectTest.java b/functional-test/src/test/java/org/zanata/feature/project/CreateProjectTest.java index 3d6dd88555..6d1e4b83f8 100644 --- a/functional-test/src/test/java/org/zanata/feature/project/CreateProjectTest.java +++ b/functional-test/src/test/java/org/zanata/feature/project/CreateProjectTest.java @@ -23,6 +23,7 @@ import java.util.HashMap; +import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -33,6 +34,7 @@ import org.zanata.page.projects.ProjectBasePage; import org.zanata.page.projects.ProjectVersionsPage; import org.zanata.util.AddUsersRule; +import org.zanata.workflow.BasicWorkFlow; import org.zanata.workflow.LoginWorkFlow; import org.zanata.workflow.ProjectWorkFlow; @@ -49,15 +51,19 @@ public class CreateProjectTest extends ZanataTestCase { @ClassRule public static AddUsersRule addUsersRule = new AddUsersRule(); + @BeforeClass + public static void beforeClass() { + new BasicWorkFlow().goToHome().deleteCookiesAndRefresh(); + assertThat(new LoginWorkFlow().signIn("admin", "admin").loggedInAs()) + .isEqualTo("admin") + .as("Admin is logged in"); + } + @Feature(summary = "The user can create a project", tcmsTestPlanIds = 5316, tcmsTestCaseIds = 144262) @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) @Category(BasicAcceptanceTest.class) public void createABasicProject() throws Exception { - assertThat(new LoginWorkFlow().signIn("admin", "admin").loggedInAs()) - .isEqualTo("admin") - .as("User logs in"); - ProjectVersionsPage projectVersionsPage = new ProjectWorkFlow() .createNewSimpleProject("basicproject", "basicproject"); @@ -75,10 +81,6 @@ public void createABasicProjectWithDescription() throws Exception { projectSettings.put("Name", "Project With Description Test"); projectSettings.put("Description", "Project Description!"); - assertThat(new LoginWorkFlow().signIn("admin", "admin").loggedInAs()) - .isEqualTo("admin") - .as("Admin can log in"); - ProjectBasePage projectPage = new ProjectWorkFlow().createNewProject(projectSettings); diff --git a/functional-test/src/test/java/org/zanata/feature/project/EditProjectGeneralTest.java b/functional-test/src/test/java/org/zanata/feature/project/EditProjectGeneralTest.java index fabb4ea1bc..e695d8e373 100644 --- a/functional-test/src/test/java/org/zanata/feature/project/EditProjectGeneralTest.java +++ b/functional-test/src/test/java/org/zanata/feature/project/EditProjectGeneralTest.java @@ -20,6 +20,8 @@ */ package org.zanata.feature.project; +import org.junit.BeforeClass; +import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -29,9 +31,11 @@ import org.zanata.page.projects.projectsettings.ProjectGeneralTab; import org.zanata.page.projects.ProjectVersionsPage; import org.zanata.page.projects.ProjectsPage; +import org.zanata.util.AddUsersRule; import org.zanata.util.SampleProjectRule; import org.zanata.workflow.BasicWorkFlow; import org.zanata.workflow.LoginWorkFlow; +import org.zanata.workflow.ProjectWorkFlow; import static org.assertj.core.api.Assertions.assertThat; @@ -42,17 +46,25 @@ @Category(DetailedTest.class) public class EditProjectGeneralTest extends ZanataTestCase { + @ClassRule + public static AddUsersRule addUsersRule = new AddUsersRule(); + @Rule public SampleProjectRule sampleProjectRule = new SampleProjectRule(); + @BeforeClass + public static void beforeClass() { + assertThat(new LoginWorkFlow().signIn("admin", "admin").loggedInAs()) + .isEqualTo("admin") + .as("Admin is logged in"); + } + @Feature(summary = "The administrator can set a project to read-only", tcmsTestPlanIds = 5316, tcmsTestCaseIds = 135848) @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void setAProjectToReadOnly() throws Exception { - ProjectsPage projectsPage = new LoginWorkFlow() - .signIn("admin", "admin") - .goToProjects() - .goToProject("about fedora") + ProjectsPage projectsPage = new ProjectWorkFlow() + .goToProjectByName("about fedora") .gotoSettingsTab() .gotoSettingsGeneral() .lockProject() @@ -81,10 +93,8 @@ public void setAProjectToReadOnly() throws Exception { tcmsTestPlanIds = 5316, tcmsTestCaseIds = 0) @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void setAProjectToWritable() throws Exception { - assertThat(new LoginWorkFlow() - .signIn("admin", "admin") - .goToProjects() - .goToProject("about fedora") + assertThat(new ProjectWorkFlow() + .goToProjectByName("about fedora") .gotoSettingsTab() .gotoSettingsGeneral() .lockProject() @@ -115,86 +125,13 @@ public void setAProjectToWritable() throws Exception { .as("The project is now displayed"); } - @Feature(summary = "The administrator can set a project to obsolete", - tcmsTestPlanIds = 5316, tcmsTestCaseIds = 135846) - @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) - public void setAProjectObsolete() throws Exception { - ProjectsPage projectsPage = new LoginWorkFlow() - .signIn("admin", "admin") - .goToProjects() - .goToProject("about fedora") - .gotoSettingsTab() - .gotoSettingsGeneral() - .archiveProject() - .goToProjects(); - - assertThat(projectsPage.getProjectNamesOnCurrentPage()) - .doesNotContain("about fedora") - .as("The project is not displayed"); - - projectsPage = projectsPage.setActiveFilterEnabled(false) - .setReadOnlyFilterEnabled(false) - .setObsoleteFilterEnabled(true); - - projectsPage.waitForProjectVisibility("about fedora", true); - - assertThat(projectsPage.getProjectNamesOnCurrentPage()) - .contains("about fedora") - .as("The project is now displayed"); - - projectsPage.logout(); - - assertThat(new LoginWorkFlow() - .signIn("translator", "translator") - .goToProjects() - .getProjectNamesOnCurrentPage()) - .doesNotContain("about fedora") - .as("User cannot navigate to the obsolete project"); - } - - @Feature(summary = "The administrator can set an obsolete project " + - "to active", - tcmsTestPlanIds = 5316, tcmsTestCaseIds = 0) - @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) - public void setAnObsoleteProjectAsActive() throws Exception { - ProjectGeneralTab projectGeneralTab = new LoginWorkFlow() - .signIn("admin", "admin") - .goToProjects() - .goToProject("about fedora") - .gotoSettingsTab() - .gotoSettingsGeneral() - .archiveProject() - .goToProjects() - .setObsoleteFilterEnabled(true) - .goToProject("about fedora") - .gotoSettingsTab() - .gotoSettingsGeneral() - .unarchiveProject(); - - assertThat(projectGeneralTab.isArchiveButtonAvailable()) - .isTrue() - .as("The archive button is now available"); - - projectGeneralTab.logout(); - - assertThat(new LoginWorkFlow() - .signIn("translator", "translator") - .goToProjects() - .goToProject("about fedora") - .getProjectName()) - .isEqualTo("about fedora") - .as("Translator can view the project"); - } - @Feature(summary = "The administrator can change a project's name", tcmsTestPlanIds = 5316, tcmsTestCaseIds = 198431) @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void changeProjectName() throws Exception { String replacementText = "a new name"; - ProjectVersionsPage projectVersionsPage = new LoginWorkFlow() - .signIn("admin", "admin") - .goToProjects() - .goToProject("about fedora") + ProjectVersionsPage projectVersionsPage = new ProjectWorkFlow() + .goToProjectByName("about fedora") .gotoSettingsTab() .gotoSettingsGeneral() .enterProjectName(replacementText) @@ -212,10 +149,8 @@ public void changeProjectName() throws Exception { @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void changeProjectDescription() throws Exception { String replacementText = "a new description"; - ProjectVersionsPage projectVersionsPage = new LoginWorkFlow() - .signIn("admin", "admin") - .goToProjects() - .goToProject("about fedora"); + ProjectVersionsPage projectVersionsPage = new ProjectWorkFlow() + .goToProjectByName("about fedora"); assertThat(projectVersionsPage.getContentAreaParagraphs()) .doesNotContain(replacementText) @@ -236,10 +171,8 @@ public void changeProjectDescription() throws Exception { tcmsTestPlanIds = 5316, tcmsTestCaseIds = 198431) @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void changeProjectType() throws Exception { - ProjectGeneralTab projectGeneralTab = new LoginWorkFlow() - .signIn("admin", "admin") - .goToProjects() - .goToProject("about fedora") + ProjectGeneralTab projectGeneralTab = new ProjectWorkFlow() + .goToProjectByName("about fedora") .gotoSettingsTab() .gotoSettingsGeneral() .selectProjectType("Properties") @@ -259,14 +192,12 @@ public void changeProjectType() throws Exception { tcmsTestPlanIds = 5316, tcmsTestCaseIds = 198431) @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void changeSourceLinks() throws Exception { - ProjectVersionsPage projectVersionsPage = new LoginWorkFlow() - .signIn("admin", "admin") - .goToProjects() - .goToProject("about fedora") + ProjectVersionsPage projectVersionsPage = new ProjectWorkFlow() + .goToProjectByName("about fedora") .gotoSettingsTab() .gotoSettingsGeneral() .enterHomePage("http://www.example.com") - .enterRepository("http://www.test.com") + .enterRepository("http://git.example.com") .updateProject() .goToProjects() .goToProject("about fedora"); @@ -276,7 +207,7 @@ public void changeSourceLinks() throws Exception { .as("The homepage is correct"); assertThat(projectVersionsPage.getGitUrl()) - .isEqualTo("http://www.test.com") + .isEqualTo("http://git.example.com") .as("The git url is correct"); } } diff --git a/functional-test/src/test/java/org/zanata/feature/project/EditProjectLanguagesTest.java b/functional-test/src/test/java/org/zanata/feature/project/EditProjectLanguagesTest.java index f637ceae7d..27401f6e42 100644 --- a/functional-test/src/test/java/org/zanata/feature/project/EditProjectLanguagesTest.java +++ b/functional-test/src/test/java/org/zanata/feature/project/EditProjectLanguagesTest.java @@ -26,6 +26,8 @@ import java.util.List; import org.assertj.core.api.Assertions; +import org.junit.BeforeClass; +import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -33,8 +35,10 @@ import org.zanata.feature.testharness.TestPlan.DetailedTest; import org.zanata.feature.testharness.ZanataTestCase; import org.zanata.page.projects.projectsettings.ProjectLanguagesTab; +import org.zanata.util.AddUsersRule; import org.zanata.util.SampleProjectRule; import org.zanata.workflow.LoginWorkFlow; +import org.zanata.workflow.ProjectWorkFlow; /** * @author Damian Jansen @@ -43,17 +47,25 @@ @Category(DetailedTest.class) public class EditProjectLanguagesTest extends ZanataTestCase { + @ClassRule + public static AddUsersRule addUsersRule = new AddUsersRule(); + @Rule public SampleProjectRule sampleProjectRule = new SampleProjectRule(); + @BeforeClass + public static void beforeClass() { + assertThat(new LoginWorkFlow().signIn("admin", "admin").loggedInAs()) + .isEqualTo("admin") + .as("Admin is logged in"); + } + @Feature(summary = "The administrator can edit the project languages", tcmsTestPlanIds = 5316, tcmsTestCaseIds = 0) @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void editProjectLanguages() throws Exception { - ProjectLanguagesTab projectLanguagesTab = new LoginWorkFlow() - .signIn("admin", "admin") - .goToProjects() - .goToProject("about fedora") + ProjectLanguagesTab projectLanguagesTab = new ProjectWorkFlow() + .goToProjectByName("about fedora") .gotoSettingsTab() .gotoSettingsLanguagesTab() .expectEnabledLocaleListCount(3); diff --git a/functional-test/src/test/java/org/zanata/feature/project/EditProjectValidationsTest.java b/functional-test/src/test/java/org/zanata/feature/project/EditProjectValidationsTest.java index d81610920f..2fec2ddcfb 100644 --- a/functional-test/src/test/java/org/zanata/feature/project/EditProjectValidationsTest.java +++ b/functional-test/src/test/java/org/zanata/feature/project/EditProjectValidationsTest.java @@ -20,6 +20,8 @@ */ package org.zanata.feature.project; +import org.junit.BeforeClass; +import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -27,8 +29,10 @@ import org.zanata.feature.testharness.ZanataTestCase; import org.zanata.feature.testharness.TestPlan.DetailedTest; import org.zanata.page.projects.projectsettings.ProjectTranslationTab; +import org.zanata.util.AddUsersRule; import org.zanata.util.SampleProjectRule; import org.zanata.workflow.LoginWorkFlow; +import org.zanata.workflow.ProjectWorkFlow; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assume.assumeTrue; @@ -40,19 +44,27 @@ @Category(DetailedTest.class) public class EditProjectValidationsTest extends ZanataTestCase { + @ClassRule + public static AddUsersRule addUsersRule = new AddUsersRule(); + @Rule public SampleProjectRule sampleProjectRule = new SampleProjectRule(); + @BeforeClass + public static void beforeClass() { + assertThat(new LoginWorkFlow().signIn("admin", "admin").loggedInAs()) + .isEqualTo("admin") + .as("Admin is logged in"); + } + @Feature(summary = "The administrator can change the validation levels " + "for a project", tcmsTestPlanIds = 5316, tcmsTestCaseIds = 0) @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void setValidationOptions() throws Exception { - ProjectTranslationTab projectTranslationTab = new LoginWorkFlow() - .signIn("admin", "admin") - .goToProjects() - .goToProject("about fedora") + ProjectTranslationTab projectTranslationTab = new ProjectWorkFlow() + .goToProjectByName("about fedora") .gotoSettingsTab() .gotoSettingsTranslationTab(); @@ -62,16 +74,23 @@ public void setValidationOptions() throws Exception { .as("The level for tabs is currently Warning"); projectTranslationTab = projectTranslationTab - .setValidationLevel("HTML/XML tags", "Error") - .setValidationLevel("Java variables", "Error") - .setValidationLevel("Leading/trailing newline (\\n)", "Error") - .setValidationLevel("Printf variables", "Error") - .setValidationLevel("Tab characters (\\t)", "Error") + .setValidationLevel("HTML/XML tags", "Error"); + projectTranslationTab.reload(); + projectTranslationTab = projectTranslationTab + .setValidationLevel("Java variables", "Error"); + projectTranslationTab.reload(); + projectTranslationTab = projectTranslationTab + .setValidationLevel("Leading/trailing newline (\\n)", "Error"); + projectTranslationTab.reload(); + projectTranslationTab = projectTranslationTab + .setValidationLevel("Printf variables", "Error"); + projectTranslationTab.reload(); + projectTranslationTab = projectTranslationTab + .setValidationLevel("Tab characters (\\t)", "Error"); + projectTranslationTab.reload(); + projectTranslationTab = projectTranslationTab .setValidationLevel("XML entity reference", "Error"); - - assumeTrue("RHBZ1017458", projectTranslationTab.hasNoCriticalErrors()); - projectTranslationTab = projectTranslationTab.goToHomePage() .goToProjects() .goToProject("about fedora") @@ -109,10 +128,8 @@ public void setValidationOptions() throws Exception { tcmsTestPlanIds = 5316, tcmsTestCaseIds = 0) @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void printfAndPositionalPrintfAreExclusive() throws Exception { - ProjectTranslationTab projectTranslationTab = new LoginWorkFlow() - .signIn("admin", "admin") - .goToProjects() - .goToProject("about fedora") + ProjectTranslationTab projectTranslationTab = new ProjectWorkFlow() + .goToProjectByName("about fedora") .gotoSettingsTab() .gotoSettingsTranslationTab() .setValidationLevel( diff --git a/functional-test/src/test/java/org/zanata/feature/project/EditWebHooksTest.java b/functional-test/src/test/java/org/zanata/feature/project/EditWebHooksTest.java index b436c180de..f17a51be02 100644 --- a/functional-test/src/test/java/org/zanata/feature/project/EditWebHooksTest.java +++ b/functional-test/src/test/java/org/zanata/feature/project/EditWebHooksTest.java @@ -20,6 +20,8 @@ */ package org.zanata.feature.project; +import org.junit.BeforeClass; +import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -27,8 +29,11 @@ import org.zanata.feature.testharness.TestPlan.DetailedTest; import org.zanata.feature.testharness.ZanataTestCase; import org.zanata.page.projects.projectsettings.ProjectWebHooksTab; +import org.zanata.util.AddUsersRule; import org.zanata.util.SampleProjectRule; +import org.zanata.workflow.BasicWorkFlow; import org.zanata.workflow.LoginWorkFlow; +import org.zanata.workflow.ProjectWorkFlow; import static org.assertj.core.api.Assertions.assertThat; /** @@ -37,18 +42,27 @@ @Category(DetailedTest.class) public class EditWebHooksTest extends ZanataTestCase { + @ClassRule + public static AddUsersRule addUsersRule = new AddUsersRule(); + @Rule public SampleProjectRule sampleProjectRule = new SampleProjectRule(); + @BeforeClass + public static void beforeClass() { + new BasicWorkFlow().goToHome().deleteCookiesAndRefresh(); + assertThat(new LoginWorkFlow().signIn("admin", "admin").loggedInAs()) + .isEqualTo("admin") + .as("Admin is logged in"); + } + @Feature(summary = "The maintainer can add WebHooks for a project", tcmsTestPlanIds = 5316, tcmsTestCaseIds = 0) @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void addWebHook() throws Exception { String testUrl = "http://www.example.com"; - ProjectWebHooksTab projectWebHooksTab = new LoginWorkFlow() - .signIn("admin", "admin") - .goToProjects() - .goToProject("about fedora") + ProjectWebHooksTab projectWebHooksTab = new ProjectWorkFlow() + .goToProjectByName("about fedora") .gotoSettingsTab() .gotoSettingsWebHooksTab() .enterUrl(testUrl) @@ -64,10 +78,8 @@ public void addWebHook() throws Exception { @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void removeWebHook() throws Exception { String testUrl = "http://www.example.com"; - ProjectWebHooksTab projectWebHooksTab = new LoginWorkFlow() - .signIn("admin", "admin") - .goToProjects() - .goToProject("about fedora") + ProjectWebHooksTab projectWebHooksTab = new ProjectWorkFlow() + .goToProjectByName("about fedora") .gotoSettingsTab() .gotoSettingsWebHooksTab() .enterUrl(testUrl) diff --git a/functional-test/src/test/java/org/zanata/feature/project/SetProjectVisibilityTest.java b/functional-test/src/test/java/org/zanata/feature/project/SetProjectVisibilityTest.java new file mode 100644 index 0000000000..49f91185ca --- /dev/null +++ b/functional-test/src/test/java/org/zanata/feature/project/SetProjectVisibilityTest.java @@ -0,0 +1,97 @@ +package org.zanata.feature.project; + +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.zanata.feature.Feature; +import org.zanata.feature.testharness.ZanataTestCase; +import org.zanata.page.projects.ProjectsPage; +import org.zanata.page.projects.projectsettings.ProjectGeneralTab; +import org.zanata.util.AddUsersRule; +import org.zanata.util.SampleProjectRule; +import org.zanata.workflow.LoginWorkFlow; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Damian Jansen djansen@redhat.com + */ +public class SetProjectVisibilityTest extends ZanataTestCase { + + @ClassRule + public static AddUsersRule addUsersRule = new AddUsersRule(); + + @Rule + public SampleProjectRule sampleProjectRule = new SampleProjectRule(); + + @Feature(summary = "The administrator can set a project to obsolete", + tcmsTestPlanIds = 5316, tcmsTestCaseIds = 135846) + @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) + public void setAProjectObsolete() throws Exception { + ProjectsPage projectsPage = new LoginWorkFlow() + .signIn("admin", "admin") + .goToProjects() + .goToProject("about fedora") + .gotoSettingsTab() + .gotoSettingsGeneral() + .archiveProject() + .goToProjects(); + + assertThat(projectsPage.getProjectNamesOnCurrentPage()) + .doesNotContain("about fedora") + .as("The project is not displayed"); + + projectsPage = projectsPage.setActiveFilterEnabled(false) + .setReadOnlyFilterEnabled(false) + .setObsoleteFilterEnabled(true); + + projectsPage.waitForProjectVisibility("about fedora", true); + + assertThat(projectsPage.getProjectNamesOnCurrentPage()) + .contains("about fedora") + .as("The project is now displayed"); + + projectsPage.logout(); + + assertThat(new LoginWorkFlow() + .signIn("translator", "translator") + .goToProjects() + .getProjectNamesOnCurrentPage()) + .doesNotContain("about fedora") + .as("User cannot navigate to the obsolete project"); + } + + @Feature(summary = "The administrator can set an obsolete project " + + "to active", + tcmsTestPlanIds = 5316, tcmsTestCaseIds = 0) + @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) + public void setAnObsoleteProjectAsActive() throws Exception { + ProjectGeneralTab projectGeneralTab = new LoginWorkFlow() + .signIn("admin", "admin") + .goToProjects() + .goToProject("about fedora") + .gotoSettingsTab() + .gotoSettingsGeneral() + .archiveProject() + .goToProjects() + .setObsoleteFilterEnabled(true) + .goToProject("about fedora") + .gotoSettingsTab() + .gotoSettingsGeneral() + .unarchiveProject(); + + assertThat(projectGeneralTab.isArchiveButtonAvailable()) + .isTrue() + .as("The archive button is now available"); + + projectGeneralTab.logout(); + + assertThat(new LoginWorkFlow() + .signIn("translator", "translator") + .goToProjects() + .goToProject("about fedora") + .getProjectName()) + .isEqualTo("about fedora") + .as("Translator can view the project"); + } +} diff --git a/functional-test/src/test/java/org/zanata/feature/projectversion/CreateProjectVersionTest.java b/functional-test/src/test/java/org/zanata/feature/projectversion/CreateProjectVersionTest.java index 56b6c38457..6debd921d3 100644 --- a/functional-test/src/test/java/org/zanata/feature/projectversion/CreateProjectVersionTest.java +++ b/functional-test/src/test/java/org/zanata/feature/projectversion/CreateProjectVersionTest.java @@ -21,7 +21,10 @@ package org.zanata.feature.projectversion; +import org.junit.BeforeClass; import org.junit.ClassRule; +import org.junit.Ignore; +import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; import org.zanata.feature.Feature; @@ -31,6 +34,7 @@ import org.zanata.page.projects.ProjectVersionsPage; import org.zanata.page.projectversion.CreateVersionPage; import org.zanata.page.projectversion.VersionLanguagesPage; +import org.zanata.util.AddUsersRule; import org.zanata.util.SampleProjectRule; import org.zanata.workflow.LoginWorkFlow; import org.zanata.workflow.ProjectWorkFlow; @@ -45,17 +49,25 @@ public class CreateProjectVersionTest extends ZanataTestCase { @ClassRule - public static SampleProjectRule sampleProjectRule = new SampleProjectRule(); + public static AddUsersRule addUsersRule = new AddUsersRule(); + + @Rule + public SampleProjectRule sampleProjectRule = new SampleProjectRule(); + + @BeforeClass + public static void beforeClass() { + assertThat(new LoginWorkFlow().signIn("admin", "admin").loggedInAs()) + .isEqualTo("admin") + .as("Admin is logged in"); + } @Feature(summary = "The administrator can create a project version", tcmsTestPlanIds = 5316, tcmsTestCaseIds = 136517) @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) @Category(BasicAcceptanceTest.class) public void createASimpleProjectVersion() throws Exception { - VersionLanguagesPage versionLanguagesPage = new LoginWorkFlow() - .signIn("admin", "admin") - .goToProjects() - .goToProject("about fedora") + VersionLanguagesPage versionLanguagesPage = new ProjectWorkFlow() + .goToProjectByName("about fedora") .clickCreateVersionLink() .disableCopyFromVersion() .inputVersionId("my-aboutfedora-version") @@ -70,13 +82,11 @@ public void createASimpleProjectVersion() throws Exception { tcmsTestPlanIds = 5316, tcmsTestCaseIds = 0) @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void idFieldMustNotBeEmpty() throws Exception { - CreateVersionPage createVersionPage = new LoginWorkFlow() - .signIn("admin", "admin") - .goToProjects() - .goToProject("about fedora") + CreateVersionPage createVersionPage = new ProjectWorkFlow() + .goToProjectByName("about fedora") .clickCreateVersionLink() .inputVersionId(""); - createVersionPage.defocus(); + createVersionPage.defocus(createVersionPage.projectVersionID); assertThat(createVersionPage.getErrors()) .contains("value is required") @@ -88,38 +98,33 @@ public void idFieldMustNotBeEmpty() throws Exception { tcmsTestPlanIds = 5316, tcmsTestCaseIds = 0) @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void idStartsAndEndsWithAlphanumeric() throws Exception { - CreateVersionPage createVersionPage = new LoginWorkFlow() - .signIn("admin", "admin") - .goToProjects() - .goToProject("about fedora") + CreateVersionPage createVersionPage = new ProjectWorkFlow() + .goToProjectByName("about fedora") .clickCreateVersionLink() .inputVersionId("-A"); - createVersionPage.defocus(); + createVersionPage.defocus(createVersionPage.projectVersionID); - assertThat(createVersionPage.expectError( - CreateVersionPage.VALIDATION_ERROR)) + assertThat(createVersionPage.expectErrors()) .contains(CreateVersionPage.VALIDATION_ERROR) .as("The input is rejected"); createVersionPage = createVersionPage.inputVersionId("B-"); - createVersionPage.defocus(); + createVersionPage.defocus(createVersionPage.projectVersionID); - assertThat(createVersionPage.expectError( - CreateVersionPage.VALIDATION_ERROR)) + assertThat(createVersionPage.expectErrors()) .contains(CreateVersionPage.VALIDATION_ERROR) .as("The input is rejected"); createVersionPage = createVersionPage.inputVersionId("_C_"); - createVersionPage.defocus(); + createVersionPage.defocus(createVersionPage.projectVersionID); createVersionPage = createVersionPage.waitForNumErrors(1); - assertThat(createVersionPage.expectError( - CreateVersionPage.VALIDATION_ERROR)) + assertThat(createVersionPage.expectErrors()) .contains(CreateVersionPage.VALIDATION_ERROR) .as("The input is rejected"); createVersionPage = createVersionPage.inputVersionId("A-B_C"); - createVersionPage.defocus(); + createVersionPage.defocus(createVersionPage.projectVersionID); createVersionPage = createVersionPage.waitForNumErrors(0); assertThat(createVersionPage.getErrors()) @@ -131,15 +136,9 @@ public void idStartsAndEndsWithAlphanumeric() throws Exception { "when a project version is created", tcmsTestPlanIds = 5316, tcmsTestCaseIds = 0) @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) + @Ignore("intermittently failing; see rhbz1168447") public void versionCounterIsUpdated() throws Exception { String projectName = "version nums"; - - assertThat(new LoginWorkFlow() - .signIn("translator", "translator") - .loggedInAs()) - .isEqualTo("translator") - .as("Login as translator"); - assertThat(new ProjectWorkFlow() .createNewSimpleProject("version-nums", projectName) .getProjectName()) diff --git a/functional-test/src/test/java/org/zanata/feature/projectversion/EditVersionValidationsTest.java b/functional-test/src/test/java/org/zanata/feature/projectversion/EditVersionValidationsTest.java index a1798b1278..b2a9cfb5fe 100644 --- a/functional-test/src/test/java/org/zanata/feature/projectversion/EditVersionValidationsTest.java +++ b/functional-test/src/test/java/org/zanata/feature/projectversion/EditVersionValidationsTest.java @@ -20,6 +20,8 @@ */ package org.zanata.feature.projectversion; +import org.junit.BeforeClass; +import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -28,6 +30,7 @@ import org.zanata.feature.testharness.TestPlan.DetailedTest; import org.zanata.page.projectversion.versionsettings.VersionTranslationTab; import org.zanata.page.webtrans.EditorPage; +import org.zanata.util.AddUsersRule; import org.zanata.util.SampleProjectRule; import org.zanata.workflow.LoginWorkFlow; import org.zanata.workflow.ProjectWorkFlow; @@ -42,18 +45,26 @@ @Category(DetailedTest.class) public class EditVersionValidationsTest extends ZanataTestCase { + @ClassRule + public static AddUsersRule addUsersRule = new AddUsersRule(); + @Rule public SampleProjectRule sampleProjectRule = new SampleProjectRule(); + @BeforeClass + public static void beforeClass() { + assertThat(new LoginWorkFlow().signIn("admin", "admin").loggedInAs()) + .isEqualTo("admin") + .as("Admin is logged in"); + } + @Feature(summary = "The administrator can set validation options for " + "a project version", tcmsTestPlanIds = 5316, tcmsTestCaseIds = 0) @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void setValidationOptions() throws Exception { - VersionTranslationTab versionTranslationTab = new LoginWorkFlow() - .signIn("admin", "admin") - .goToProjects() - .goToProject("about fedora") + VersionTranslationTab versionTranslationTab = new ProjectWorkFlow() + .goToProjectByName("about fedora") .gotoVersion("master") .gotoSettingsTab() .gotoSettingsTranslationTab(); @@ -86,10 +97,8 @@ public void setValidationOptions() throws Exception { tcmsTestPlanIds = 5316, tcmsTestCaseIds = 0) @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void verifyValidationsAreErrors() throws Exception { - VersionTranslationTab versionTranslationTab = new LoginWorkFlow() - .signIn("admin", "admin") - .goToProjects() - .goToProject("about fedora") + VersionTranslationTab versionTranslationTab = new ProjectWorkFlow() + .goToProjectByName("about fedora") .gotoVersion("master") .gotoSettingsTab() .gotoSettingsTranslationTab() @@ -125,10 +134,8 @@ public void verifyValidationsAreErrors() throws Exception { tcmsTestPlanIds = 5316, tcmsTestCaseIds = 0) @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void userCannotTurnOffEnforcedValidations() throws Exception { - VersionTranslationTab versionTranslationTab = new LoginWorkFlow() - .signIn("admin", "admin") - .goToProjects() - .goToProject("about fedora") + VersionTranslationTab versionTranslationTab = new ProjectWorkFlow() + .goToProjectByName("about fedora") .gotoVersion("master") .gotoSettingsTab() .gotoSettingsTranslationTab() @@ -158,10 +165,8 @@ public void userCannotTurnOffEnforcedValidations() throws Exception { tcmsTestPlanIds = 5316, tcmsTestCaseIds = 0) @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void printfAndPositionalPrintfAreExclusive() throws Exception { - VersionTranslationTab versionTranslationTab = new LoginWorkFlow() - .signIn("admin", "admin") - .goToProjects() - .goToProject("about fedora") + VersionTranslationTab versionTranslationTab = new ProjectWorkFlow() + .goToProjectByName("about fedora") .gotoVersion("master") .gotoSettingsTab() .gotoSettingsTranslationTab() @@ -197,10 +202,8 @@ public void printfAndPositionalPrintfAreExclusive() throws Exception { tcmsTestPlanIds = 5316, tcmsTestCaseIds = 0) @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) public void userCanEnableADisabledValidation() throws Exception { - VersionTranslationTab versionTranslationTab = new LoginWorkFlow() - .signIn("admin", "admin") - .goToProjects() - .goToProject("about fedora") + VersionTranslationTab versionTranslationTab = new ProjectWorkFlow() + .goToProjectByName("about fedora") .gotoVersion("master") .gotoSettingsTab() .gotoSettingsTranslationTab() diff --git a/functional-test/src/test/java/org/zanata/feature/projectversion/VersionFilteringTest.java b/functional-test/src/test/java/org/zanata/feature/projectversion/VersionFilteringTest.java index aae2245b4c..196c691bf6 100644 --- a/functional-test/src/test/java/org/zanata/feature/projectversion/VersionFilteringTest.java +++ b/functional-test/src/test/java/org/zanata/feature/projectversion/VersionFilteringTest.java @@ -21,7 +21,7 @@ package org.zanata.feature.projectversion; -import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; import org.zanata.feature.Feature; @@ -44,8 +44,8 @@ @Category(DetailedTest.class) public class VersionFilteringTest extends ZanataTestCase { - @ClassRule - public static SampleProjectRule sampleProjectRule = new SampleProjectRule(); + @Rule + public SampleProjectRule sampleProjectRule = new SampleProjectRule(); private ZanataRestCaller zanataRestCaller; diff --git a/functional-test/src/test/java/org/zanata/feature/rest/CopyTransTest.java b/functional-test/src/test/java/org/zanata/feature/rest/CopyTransTest.java index 000e62cd86..d5a937fd6b 100644 --- a/functional-test/src/test/java/org/zanata/feature/rest/CopyTransTest.java +++ b/functional-test/src/test/java/org/zanata/feature/rest/CopyTransTest.java @@ -71,7 +71,7 @@ public void testPushTranslationAndCopyTrans() { } restCaller.asyncPushSource(projectSlug, iterationSlug, sourceResource, false); restCaller.asyncPushTarget(projectSlug, iterationSlug, docId, - new LocaleId("pl"), transResource, "import"); + new LocaleId("pl"), transResource, "import", false); assertThat(true, Matchers.is(true)); @@ -79,7 +79,7 @@ public void testPushTranslationAndCopyTrans() { restCaller.createProjectAndVersion(projectSlug, "2", projectType); restCaller.asyncPushSource(projectSlug, "2", sourceResource, false); restCaller.asyncPushTarget(projectSlug, "2", docId, new LocaleId("pl"), - transResource, "import"); + transResource, "import", false); // push to old version again restCaller.asyncPushSource(projectSlug, iterationSlug, sourceResource, @@ -114,7 +114,7 @@ void testPushTranslationRepeatedly() { false); LocaleId localeId = new LocaleId("pl"); restCaller.asyncPushTarget(projectSlug, iterationSlug, docId, - localeId, transResource, "auto"); + localeId, transResource, "auto", false); restCaller.runCopyTrans(projectSlug, iterationSlug, docId); assertThat(true, Matchers.is(true)); @@ -131,10 +131,10 @@ void testPushTranslationRepeatedly() { // push updated source (same resId different content) restCaller.asyncPushSource(projectSlug, iterationSlug, updatedSource, false); - restCaller.asyncPushTarget(projectSlug, iterationSlug, docId, localeId, updatedTransResource, "auto"); + restCaller.asyncPushTarget(projectSlug, iterationSlug, docId, localeId, updatedTransResource, "auto", false); // push again restCaller.asyncPushSource(projectSlug, iterationSlug, updatedSource, false); - restCaller.asyncPushTarget(projectSlug, iterationSlug, docId, localeId, updatedTransResource, "auto"); + restCaller.asyncPushTarget(projectSlug, iterationSlug, docId, localeId, updatedTransResource, "auto", false); } } diff --git a/functional-test/src/test/java/org/zanata/feature/security/SecurityTest.java b/functional-test/src/test/java/org/zanata/feature/security/SecurityTest.java index 3b7ffc4cf4..fb0b99a752 100644 --- a/functional-test/src/test/java/org/zanata/feature/security/SecurityTest.java +++ b/functional-test/src/test/java/org/zanata/feature/security/SecurityTest.java @@ -20,15 +20,18 @@ */ package org.zanata.feature.security; + import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; +import org.openqa.selenium.By; import org.subethamail.wiser.WiserMessage; import org.zanata.feature.Feature; import org.zanata.feature.testharness.ZanataTestCase; import org.zanata.feature.testharness.TestPlan.BasicAcceptanceTest; import org.zanata.feature.testharness.TestPlan.DetailedTest; import org.zanata.page.account.ResetPasswordPage; +import org.zanata.page.utility.HomePage; import org.zanata.util.AddUsersRule; import org.zanata.util.EnsureLogoutRule; import org.zanata.util.HasEmailRule; @@ -73,7 +76,7 @@ public void signInSuccessful() { public void signInFailure() { assertThat(new LoginWorkFlow() .signInFailure("nosuchuser", "password") - .expectError("Login failed")) + .expectErrors()) .contains("Login failed") .as("Log in error message is shown"); } @@ -87,10 +90,10 @@ public void resetPasswordSuccessful() { .clickSignInLink() .goToResetPassword() .enterUserName("admin") - .enterEmail("admin@example.com") - .resetPassword(); + .enterEmail("admin@example.com"); + HomePage homePage = resetPasswordPage.resetPassword(); - assertThat(resetPasswordPage.getNotificationMessage()) + assertThat(homePage.getNotificationMessage()) .isEqualTo("You will soon receive an email with a link to " + "reset your password."); @@ -119,8 +122,10 @@ public void resetPasswordFailureForInvalidAccount() { .enterEmail("nosuchuser@nosuchdomain.com") .resetFailure(); - assertThat(resetPasswordPage.getNotificationMessage()) - .isEqualTo("No such account found") + assertThat( + resetPasswordPage.getNotificationMessage(By + .id("passwordResetRequestForm:messages"))) + .isEqualTo("No account found.") .as("A no such account message is displayed"); } @@ -137,16 +142,17 @@ public void invalidResetPasswordFieldEntries() { .enterEmail("b") .resetFailure(); - assertThat(resetPasswordPage.expectError("not a well-formed email address")) + assertThat(resetPasswordPage.expectErrors()) .contains("not a well-formed email address") .as("Invalid email error is displayed"); - String error = resetPasswordPage.getErrors().get(0); // Both are valid, but show seemingly at random - assertThat(error.equals("size must be between 3 and 20") || - error.equals("must match ^[a-z\\d_]{3,20}$")) - .isTrue() - .as("Invalid email error is displayed"); + assertThat(resetPasswordPage.getErrors().get(0)) + .isIn("Between 3 and 20 lowercase letters, numbers and " + + "underscores only", + "size must be between 3 and 20", + "must match ^[a-z\\d_]{3,20}$") + .as("Invalid username error is displayed"); } @Feature(summary = "The user must enter both an account name and email " + @@ -161,16 +167,9 @@ public void emptyResetPasswordFieldEntries() { .clearFields() .resetFailure(); - assertThat(resetPasswordPage.expectError("may not be empty")) - .contains("may not be empty") - .as("Empty email error is displayed"); - - // All are valid, but may show at random - String error = resetPasswordPage.getErrors().get(0); - assertThat(error.equals("size must be between 3 and 20") || - error.equals("may not be empty") || - error.equals("must match ^[a-z\\d_]{3,20}$")) - .as("The regex match for the reset password field has failed"); + assertThat(resetPasswordPage.expectErrors()) + .contains("value is required") + .as("value is required error is displayed"); } } diff --git a/functional-test/src/test/java/org/zanata/feature/testharness/TestPlan.java b/functional-test/src/test/java/org/zanata/feature/testharness/TestPlan.java index d39b482af0..816d690637 100644 --- a/functional-test/src/test/java/org/zanata/feature/testharness/TestPlan.java +++ b/functional-test/src/test/java/org/zanata/feature/testharness/TestPlan.java @@ -38,7 +38,6 @@ import org.zanata.feature.glossary.GlossaryPushTest; import org.zanata.feature.glossary.InvalidGlossaryPushTest; import org.zanata.feature.googleopenid.GoogleOpenIDTest; -import org.zanata.feature.infrastructure.RetryRuleTest; import org.zanata.feature.language.AddLanguageTest; import org.zanata.feature.language.ContactLanguageTeamTest; import org.zanata.feature.language.JoinLanguageTeamTest; @@ -70,11 +69,10 @@ */ ChangePasswordTest.class, InactiveUserLoginTest.class, - InvalidEmailAddressTest.class, ProfileTest.class, RegisterTest.class, UsernameValidationTest.class, - ValidEmailAddressTest.class, + EmailValidationTest.class, /* * Administration @@ -144,12 +142,6 @@ */ GoogleOpenIDTest.class, - /* - * Infrastructure - * Test infrastructure validation - */ - RetryRuleTest.class, - /* * Language * Participation in an management of language teams @@ -176,7 +168,8 @@ EditProjectGeneralTest.class, EditProjectLanguagesTest.class, EditProjectValidationsTest.class, - + EditWebHooksTest.class, + SetProjectVisibilityTest.class, /* * Project Version * Creation and management of Project Versions @@ -203,7 +196,6 @@ * Creation and management of Version Groups */ VersionGroupTest.class, - VersionGroupIDValidationTest.class, VersionGroupUrlTest.class }) public class TestPlan { diff --git a/functional-test/src/test/java/org/zanata/feature/testharness/ZanataTestCase.java b/functional-test/src/test/java/org/zanata/feature/testharness/ZanataTestCase.java index 6ac4dc8036..659acb8442 100644 --- a/functional-test/src/test/java/org/zanata/feature/testharness/ZanataTestCase.java +++ b/functional-test/src/test/java/org/zanata/feature/testharness/ZanataTestCase.java @@ -30,7 +30,7 @@ import org.junit.Before; import org.junit.Rule; import org.junit.rules.TestName; -import org.zanata.util.RetryRule; +import org.zanata.page.WebDriverFactory; /** * Global application of rules to Zanata functional tests @@ -44,9 +44,6 @@ public class ZanataTestCase { public final static int MAX_SHORT_TEST_DURATION = 120000; public final static int MAX_LONG_TEST_DURATION = 300000; - @Rule - public RetryRule retryRule = new RetryRule(1); - @Rule public TestName testName = new TestName(); @@ -76,7 +73,7 @@ public void testExit() { Duration duration = new Duration(testFunctionStart, new DateTime()); PeriodFormatter periodFormatter = new PeriodFormatterBuilder() .appendLiteral("Finished " - .concat(getTestDescription()).concat(" in " )) + .concat(getTestDescription()).concat(" in ")) .printZeroAlways() .appendMinutes() .appendSuffix(" minutes, ") @@ -86,5 +83,7 @@ public void testExit() { .appendSuffix("ms") .toFormatter(); log.info(periodFormatter.print(duration.toPeriod())); + WebDriverFactory.INSTANCE.logLogs(); } + } diff --git a/functional-test/src/test/java/org/zanata/feature/versionGroup/VersionGroupIDValidationTest.java b/functional-test/src/test/java/org/zanata/feature/versionGroup/VersionGroupIDValidationTest.java deleted file mode 100644 index 1087b59412..0000000000 --- a/functional-test/src/test/java/org/zanata/feature/versionGroup/VersionGroupIDValidationTest.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2013, Red Hat, Inc. and individual contributors as indicated by the - * @author tags. See the copyright.txt file in the distribution for a full - * listing of individual contributors. - * - * This is free software; you can redistribute it and/or modify it under the - * terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This software is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this software; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF - * site: http://www.fsf.org. - */ -package org.zanata.feature.versionGroup; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.Rule; -import org.junit.experimental.categories.Category; -import org.junit.experimental.theories.DataPoint; -import org.junit.experimental.theories.Theories; -import org.junit.experimental.theories.Theory; -import org.junit.rules.Timeout; -import org.junit.runner.RunWith; -import org.zanata.feature.testharness.ZanataTestCase; -import org.zanata.feature.testharness.TestPlan.DetailedTest; -import org.zanata.page.groups.CreateVersionGroupPage; -import org.zanata.util.AddUsersRule; -import org.zanata.workflow.BasicWorkFlow; -import org.zanata.workflow.LoginWorkFlow; - -/** - * @author Damian Jansen djansen@redhat.com - */ -@RunWith(Theories.class) -@Category(DetailedTest.class) -public class VersionGroupIDValidationTest extends ZanataTestCase { - - @Rule - public Timeout timeout = new Timeout(ZanataTestCase.MAX_LONG_TEST_DURATION); - - @ClassRule - public static AddUsersRule addUsersRule = new AddUsersRule(); - - @DataPoint - public static String INVALID_CHARACTER_PIPE = "Group|ID"; - @DataPoint - public static String INVALID_CHARACTER_SLASH = "Group/ID"; - @DataPoint - public static String INVALID_CHARACTER_BACKSLASH = "Group\\ID"; - @DataPoint - public static String INVALID_CHARACTER_PLUS = "Group+ID"; - @DataPoint - public static String INVALID_CHARACTER_ASTERISK = "Group*ID"; - @DataPoint - public static String INVALID_CHARACTER_LEFT_PARENTHESES = "Group(ID"; - @DataPoint - public static String INVALID_CHARACTER_RIGHT_PARENTHESES = "Group)ID"; - @DataPoint - public static String INVALID_CHARACTER_DOLLAR = "Group$ID"; - @DataPoint - public static String INVALID_CHARACTER_LEFT_BRACKET = "Group[ID"; - @DataPoint - public static String INVALID_CHARACTER_RIGHT_BRACKET = "Group]ID"; - @DataPoint - public static String INVALID_CHARACTER_COLON = "Group:ID"; - @DataPoint - public static String INVALID_CHARACTER_SEMICOLON = "Group;ID"; - @DataPoint - public static String INVALID_CHARACTER_APOSTROPHE = "Group'ID"; - @DataPoint - public static String INVALID_CHARACTER_COMMA = "Group,ID"; - @DataPoint - public static String INVALID_CHARACTER_QUESTION = "Group?ID"; - @DataPoint - public static String INVALID_CHARACTER_EXCLAMATION = "Group!ID"; - @DataPoint - public static String INVALID_CHARACTER_AMPERSAT = "Group@ID"; - @DataPoint - public static String INVALID_CHARACTER_HASH = "Group#ID"; - @DataPoint - public static String INVALID_CHARACTER_PERCENT = "Group%ID"; - @DataPoint - public static String INVALID_CHARACTER_CARAT = "Group^ID"; - @DataPoint - public static String INVALID_CHARACTER_EQUALS = "Group=ID"; - @DataPoint - public static String MUST_START_ALPHANUMERIC = "-GroupID"; - @DataPoint - public static String MUST_END_ALPHANUMERIC = "GroupID-"; - - private static CreateVersionGroupPage groupPage; - - @BeforeClass - public static void loginToStart() { - new LoginWorkFlow().signIn("admin", "admin"); - } - - @Before - public void goToGroupPage() { - groupPage = new BasicWorkFlow() - .goToHome() - .goToGroups() - .createNewGroup(); - } - - @Theory - public void inputValidationForID(String inputText) { - groupPage.waitForPageSilence(); - groupPage = groupPage - .inputGroupId(inputText) - .inputGroupName(inputText) - .saveGroupFailure(); - - assertThat(groupPage.expectError( - CreateVersionGroupPage.VALIDATION_ERROR)) - .contains(CreateVersionGroupPage.VALIDATION_ERROR) - .as("Validation error is displayed for input:" + inputText); - } -} diff --git a/functional-test/src/test/java/org/zanata/feature/versionGroup/VersionGroupTest.java b/functional-test/src/test/java/org/zanata/feature/versionGroup/VersionGroupTest.java index cc1a0b95c9..c3de323a8d 100644 --- a/functional-test/src/test/java/org/zanata/feature/versionGroup/VersionGroupTest.java +++ b/functional-test/src/test/java/org/zanata/feature/versionGroup/VersionGroupTest.java @@ -21,6 +21,8 @@ package org.zanata.feature.versionGroup; import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -28,11 +30,12 @@ import org.zanata.feature.testharness.ZanataTestCase; import org.zanata.feature.testharness.TestPlan.BasicAcceptanceTest; import org.zanata.feature.testharness.TestPlan.DetailedTest; -import org.zanata.page.dashboard.DashboardBasePage; import org.zanata.page.groups.CreateVersionGroupPage; import org.zanata.page.groups.VersionGroupPage; import org.zanata.page.groups.VersionGroupsPage; +import org.zanata.util.AddUsersRule; import org.zanata.util.SampleProjectRule; +import org.zanata.workflow.BasicWorkFlow; import org.zanata.workflow.LoginWorkFlow; import static org.zanata.util.FunctionalTestHelper.assumeTrue; @@ -45,13 +48,24 @@ @Category(DetailedTest.class) public class VersionGroupTest extends ZanataTestCase { + @ClassRule + public static AddUsersRule addUsersRule = new AddUsersRule(); + @Rule public SampleProjectRule sampleProjectRule = new SampleProjectRule(); - private DashboardBasePage dashboardPage; + + private VersionGroupsPage versionGroupsPageBase; + + @BeforeClass + public static void beforeClass() { + assertThat(new LoginWorkFlow().signIn("admin", "admin").loggedInAs()) + .isEqualTo("admin") + .as("Admin is logged in"); + } @Before public void before() { - dashboardPage = new LoginWorkFlow().signIn("admin", "admin"); + versionGroupsPageBase = new BasicWorkFlow().goToHome().goToGroups(); } @Feature(summary = "The administrator can create a basic group", @@ -61,8 +75,7 @@ public void before() { public void createABasicGroup() throws Exception { String groupID = "basic-group"; String groupName = "A Basic Group"; - VersionGroupsPage versionGroupsPage = dashboardPage - .goToGroups() + VersionGroupsPage versionGroupsPage = versionGroupsPageBase .createNewGroup() .inputGroupId(groupID) .inputGroupName(groupName) @@ -86,8 +99,7 @@ public void requiredFields() throws Exception { String groupID = "verifyRequiredFieldsGroupID"; String groupName = "verifyRequiredFieldsGroupName"; - CreateVersionGroupPage groupPage = dashboardPage - .goToGroups() + CreateVersionGroupPage groupPage = versionGroupsPageBase .createNewGroup() .saveGroupFailure(); @@ -119,14 +131,13 @@ public void requiredFields() throws Exception { public void groupDescriptionFieldSize() throws Exception { String groupID = "verifyDescriptionFieldSizeID"; String groupName = "verifyDescriptionFieldSizeName"; - String groupDescription ="This text is to test that the description " + + String groupDescription = "This text is to test that the description " + "field takes no more than exactly 100 characters - actually."; assumeTrue("Description length is greater than 100 characters", groupDescription.length() == 101); - CreateVersionGroupPage groupPage = dashboardPage - .goToGroups() + CreateVersionGroupPage groupPage = versionGroupsPageBase .createNewGroup() .inputGroupId(groupID) .inputGroupName(groupName) @@ -157,8 +168,7 @@ public void groupDescriptionFieldSize() throws Exception { public void addANewProjectVersionToAnEmptyGroup() throws Exception { String groupID = "add-version-to-empty-group"; String groupName = "AddVersionToEmptyGroup"; - VersionGroupPage versionGroupPage = dashboardPage - .goToGroups() + VersionGroupPage versionGroupPage = versionGroupsPageBase .createNewGroup() .inputGroupId(groupID) .inputGroupName(groupName) @@ -182,8 +192,7 @@ public void addANewProjectVersionToAnEmptyGroup() throws Exception { public void groupIdCharactersAreAcceptable() throws Exception { String groupID = "test-_.1"; String groupName = "TestValidIdCharacters"; - VersionGroupPage versionGroupPage = dashboardPage - .goToGroups() + VersionGroupPage versionGroupPage = versionGroupsPageBase .createNewGroup() .inputGroupId(groupID) .inputGroupName(groupName) @@ -194,4 +203,22 @@ public void groupIdCharactersAreAcceptable() throws Exception { .isEqualTo(groupName) .as("The group was created"); } + + @Feature(summary = "The administrator must use numbers, letters, periods, " + + "underscores and hyphens to create a group", + tcmsTestPlanIds = 5316, tcmsTestCaseIds = 396261) + @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) + public void inputValidationForID() throws Exception { + String inputText = "group|name"; + CreateVersionGroupPage groupPage = versionGroupsPageBase + .createNewGroup() + .inputGroupId(inputText) + .inputGroupName(inputText); + groupPage.defocus(groupPage.groupNameField); + groupPage.saveGroupFailure(); + + assertThat(groupPage.expectErrors()) + .contains(CreateVersionGroupPage.VALIDATION_ERROR) + .as("Validation error is displayed for " + inputText); + } } diff --git a/functional-test/src/test/java/org/zanata/feature/versionGroup/VersionGroupUrlTest.java b/functional-test/src/test/java/org/zanata/feature/versionGroup/VersionGroupUrlTest.java index baa96dd02a..8533544d9a 100644 --- a/functional-test/src/test/java/org/zanata/feature/versionGroup/VersionGroupUrlTest.java +++ b/functional-test/src/test/java/org/zanata/feature/versionGroup/VersionGroupUrlTest.java @@ -20,16 +20,19 @@ */ package org.zanata.feature.versionGroup; -import org.junit.Before; +import org.assertj.core.api.Assertions; +import org.junit.BeforeClass; +import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; import org.zanata.feature.testharness.ZanataTestCase; import org.zanata.feature.testharness.TestPlan.DetailedTest; -import org.zanata.page.dashboard.DashboardBasePage; import org.zanata.page.groups.VersionGroupPage; +import org.zanata.util.AddUsersRule; import org.zanata.util.SampleProjectRule; import org.zanata.util.ZanataRestCaller; +import org.zanata.workflow.BasicWorkFlow; import org.zanata.workflow.LoginWorkFlow; import static org.hamcrest.MatcherAssert.assertThat; @@ -52,16 +55,18 @@ @Category(DetailedTest.class) public class VersionGroupUrlTest extends ZanataTestCase { + @ClassRule + public static AddUsersRule addUsersRule = new AddUsersRule(); + @Rule public SampleProjectRule sampleProjectRule = new SampleProjectRule(); - private DashboardBasePage dashboardPage; - - private ZanataRestCaller restCaller; - - @Before - public void setUp() { - dashboardPage = new LoginWorkFlow().signIn("admin", "admin"); + @BeforeClass + public static void beforeClass() { + Assertions.assertThat( + new LoginWorkFlow().signIn("admin", "admin").loggedInAs()) + .isEqualTo("admin") + .as("Admin is logged in"); } @Test(timeout = ZanataTestCase.MAX_SHORT_TEST_DURATION) @@ -111,7 +116,7 @@ private void testSettingsTabClick(VersionGroupPage versionGroupPage) { private VersionGroupPage createVersionGroup() { String groupID = "test-group"; String groupName = "A Test group"; - return dashboardPage + return new BasicWorkFlow().goToHome() .goToGroups() .createNewGroup() .inputGroupId(groupID) @@ -121,12 +126,11 @@ private VersionGroupPage createVersionGroup() { } private void createProject() { - restCaller = new ZanataRestCaller(); // create another project and version String projectSlug = "base"; String iterationSlug = "master"; String projectType = "gettext"; - restCaller.createProjectAndVersion(projectSlug, iterationSlug, - projectType); + new ZanataRestCaller().createProjectAndVersion(projectSlug, + iterationSlug, projectType); } } diff --git a/functional-test/src/test/java/org/zanata/util/AddUsersRule.java b/functional-test/src/test/java/org/zanata/util/AddUsersRule.java index 2c14b4a36e..d8735e105a 100644 --- a/functional-test/src/test/java/org/zanata/util/AddUsersRule.java +++ b/functional-test/src/test/java/org/zanata/util/AddUsersRule.java @@ -21,9 +21,7 @@ package org.zanata.util; import org.junit.rules.ExternalResource; -import org.zanata.common.LocaleId; import com.google.common.base.Throwables; -import com.google.common.collect.Sets; import lombok.extern.slf4j.Slf4j; import static org.zanata.util.SampleDataResourceClient.deleteExceptEssentialData; @@ -38,6 +36,8 @@ @Slf4j public class AddUsersRule extends ExternalResource { + public static final int MAX_ERRORS = 3; + @Override protected void before() throws Exception { deleteExceptEssentialData(); @@ -48,11 +48,18 @@ protected void before() throws Exception { @Override protected void after() { - try { - deleteExceptEssentialData(); - } - catch (Exception e) { - throw Throwables.propagate(e); + for (int i = 0; ; i++) { + try { + deleteExceptEssentialData(); + break; + } catch (Exception e) { + if (i >= MAX_ERRORS) { + throw Throwables.propagate(e); + } else { +// log.warn("exception cleaning up (enable DEBUG for stack trace)", e.toString()); + log.warn("exception cleaning up", e); + } + } } } } diff --git a/functional-test/src/test/java/org/zanata/util/CleanDatabaseRule.java b/functional-test/src/test/java/org/zanata/util/CleanDatabaseRule.java index bc4fd352a8..9642fef4d0 100644 --- a/functional-test/src/test/java/org/zanata/util/CleanDatabaseRule.java +++ b/functional-test/src/test/java/org/zanata/util/CleanDatabaseRule.java @@ -41,8 +41,7 @@ protected void before() throws Throwable { protected void after() { try { deleteExceptEssentialData(); - } - catch (Exception e) { + } catch (Exception e) { throw Throwables.propagate(e); } } diff --git a/functional-test/src/test/java/org/zanata/util/CleanDocumentStorageRule.java b/functional-test/src/test/java/org/zanata/util/CleanDocumentStorageRule.java index 1738bd63bd..6101330350 100644 --- a/functional-test/src/test/java/org/zanata/util/CleanDocumentStorageRule.java +++ b/functional-test/src/test/java/org/zanata/util/CleanDocumentStorageRule.java @@ -79,8 +79,7 @@ public static String getDocumentStoragePath() { storagePath = (String) remoteContext .lookup("zanata/files/document-storage-directory"); - } - catch (NamingException e) { + } catch (NamingException e) { // wildfly uses 'http-remoting:' not 'remote:' String httpPort = System.getenv("JBOSS_HTTP_PORT"); int httpPortNum = httpPort != null ? parseInt(httpPort) : 8180; diff --git a/functional-test/src/test/java/org/zanata/util/HasEmailRule.java b/functional-test/src/test/java/org/zanata/util/HasEmailRule.java index 39f584d9f2..cb8528a497 100644 --- a/functional-test/src/test/java/org/zanata/util/HasEmailRule.java +++ b/functional-test/src/test/java/org/zanata/util/HasEmailRule.java @@ -21,48 +21,99 @@ package org.zanata.util; import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; import javax.mail.internet.MimeMultipart; +import org.junit.rules.ExternalResource; import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; import org.subethamail.wiser.Wiser; import org.subethamail.wiser.WiserMessage; import com.google.common.base.Throwables; +import com.google.common.util.concurrent.Uninterruptibles; +import lombok.extern.slf4j.Slf4j; /** * @author Patrick Huang * pahuang@redhat.com */ -public class HasEmailRule implements TestRule { - private final Wiser wiser = new Wiser(); +@Slf4j +public class HasEmailRule extends ExternalResource { + private volatile static Wiser wiser; - public HasEmailRule() { - String port = PropertiesHolder.getProperty("smtp.port"); - wiser.setPort(Integer.parseInt(port)); + @Override + protected void before() throws Throwable { + super.before(); + if (HasEmailRule.wiser == null) { + String port = PropertiesHolder.getProperty("smtp.port"); + HasEmailRule.wiser = new Wiser(Integer.parseInt(port)); + HasEmailRule.wiser.start(); + // NB we never call wiser.stop() because we want the email + // server to stay running for all tests in this VM + } } @Override - public Statement apply(final Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - wiser.start(); - try { - base.evaluate(); - } finally { - wiser.getMessages().clear(); - wiser.stop(); - } - } - }; + protected void after() { + wiser.getMessages().clear(); + super.after(); } public List getMessages() { return wiser.getMessages(); } + /** + * Email may arrive a little bit late therefore this method can be used to + * poll and wait until timeout. + * + * @param expectedEmailNum + * expected arriving email numbers + * @param timeoutDuration + * timeout duration + * @param timeoutUnit + * timeout time unit + * @return true if the expected number of emails has arrived or false if it + * fails within the timeout period + */ + public boolean emailsArrivedWithinTimeout(final int expectedEmailNum, + final long timeoutDuration, final TimeUnit timeoutUnit) { + final CountDownLatch countDownLatch = new CountDownLatch(1); + + // poll every half second + final int sleepFor = 500; + final TimeUnit sleepUnit = TimeUnit.MILLISECONDS; + final long sleepTime = sleepUnit.convert(sleepFor, sleepUnit); + final long timeoutTime = sleepUnit.convert(timeoutDuration, timeoutUnit); + Runnable runnable = new Runnable() { + + @Override + public void run() { + long slept = 0; + while (wiser.getMessages().size() < expectedEmailNum + && slept < timeoutTime) { + + log.debug("current arrived email:{}", wiser.getMessages() + .size()); + Uninterruptibles.sleepUninterruptibly(sleepFor, sleepUnit); + slept += sleepTime; + } + countDownLatch.countDown(); + } + }; + Executors.newFixedThreadPool(1).submit(runnable); + try { + return countDownLatch.await(timeoutDuration, timeoutUnit); + } catch (InterruptedException e) { + log.warn("interrupted", e); + return wiser.getMessages().size() == expectedEmailNum; + } + } + public static String getEmailContent(WiserMessage wiserMessage) { try { return ((MimeMultipart) wiserMessage.getMimeMessage().getContent()) diff --git a/functional-test/src/test/java/org/zanata/util/SampleProjectRule.java b/functional-test/src/test/java/org/zanata/util/SampleProjectRule.java index 665432c5d3..8e11b1c45d 100644 --- a/functional-test/src/test/java/org/zanata/util/SampleProjectRule.java +++ b/functional-test/src/test/java/org/zanata/util/SampleProjectRule.java @@ -27,6 +27,8 @@ import static org.zanata.util.SampleDataResourceClient.*; /** + * To ensure test isolation, this rule should be used as + * @org.junit.Rule, never as @org.junit.ClassRule. * @author Patrick Huang pahuang@redhat.com */ diff --git a/functional-test/src/test/java/org/zanata/util/TestLogger.java b/functional-test/src/test/java/org/zanata/util/TestLogger.java new file mode 100644 index 0000000000..317c055450 --- /dev/null +++ b/functional-test/src/test/java/org/zanata/util/TestLogger.java @@ -0,0 +1,61 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this software; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF + * site: http://www.fsf.org. + */ +package org.zanata.util; + +import lombok.extern.slf4j.Slf4j; + +import org.junit.runner.Description; +import org.junit.runner.notification.Failure; +import org.junit.runner.notification.RunListener; + +/** + * @author Sean Flanigan + * sflaniga@redhat.com + */ +@Slf4j +public class TestLogger extends RunListener { + @Override + public void testStarted(Description description) throws Exception { + log.info("Test {} starting", description); + } + + @Override + public void testFinished(Description description) throws Exception { + log.info("Test {} finished", description); + } + + @Override + public void testFailure(Failure failure) throws Exception { + log.error("FAILED test " + failure, failure.getException()); + } + + @Override + public void testAssumptionFailure(Failure failure) { + log.error("FAILED ASSUMPTION in test " + failure, + failure.getException()); + } + + @Override + public void testIgnored(Description description) throws Exception { + log.error("Test {} IGNORED", description); + } + +} diff --git a/functional-test/src/test/java/org/zanata/util/ZanataRestCaller.java b/functional-test/src/test/java/org/zanata/util/ZanataRestCaller.java index 4136ec3c13..f8cd8d6159 100644 --- a/functional-test/src/test/java/org/zanata/util/ZanataRestCaller.java +++ b/functional-test/src/test/java/org/zanata/util/ZanataRestCaller.java @@ -219,8 +219,7 @@ public void runCopyTrans(String projectSlug, String iterationSlug, Thread.sleep(1000); log.debug("copyTrans completion: {}", copyTransStatus.getPercentageComplete()); copyTransStatus = resource.getCopyTransStatus(projectSlug, iterationSlug, docId); - } - catch (InterruptedException e) { + } catch (InterruptedException e) { throw Throwables.propagate(e); } } @@ -249,8 +248,7 @@ public void asyncPushSource(String projectSlug, String iterationSlug, while (copyTransStatus.isInProgress()) { try { Thread.sleep(1000); - } - catch (InterruptedException e) { + } catch (InterruptedException e) { throw Throwables.propagate(e); } copyTransStatus = copyTransResource.getCopyTransStatus(projectSlug, iterationSlug, sourceResource.getName()); @@ -273,8 +271,7 @@ private ProcessStatus waitUntilFinished( processStatus.getMessages()); try { Thread.sleep(1000); - } - catch (InterruptedException e) { + } catch (InterruptedException e) { throw Throwables.propagate(e); } processStatus = resource.getProcessStatus(processId); @@ -288,13 +285,14 @@ private ProcessStatus waitUntilFinished( public void asyncPushTarget(String projectSlug, String iterationSlug, String docId, LocaleId localeId, TranslationsResource transResource, - String mergeType) { + String mergeType, boolean assignCreditToUploader) { IAsynchronousProcessResource resource = zanataProxyFactory.getAsynchronousProcessResource(); ProcessStatus processStatus = resource.startTranslatedDocCreationOrUpdate(docId, projectSlug, iterationSlug, localeId, transResource, - Collections.emptySet(), mergeType); + Collections.emptySet(), mergeType, + assignCreditToUploader); processStatus = waitUntilFinished(resource, processStatus); log.info("finished async translation({}-{}) push: {}", projectSlug, iterationSlug, processStatus.getStatusCode()); diff --git a/functional-test/src/test/resources/conf/standalone.xml b/functional-test/src/test/resources/conf/standalone.xml index 370068f227..d795536928 100644 --- a/functional-test/src/test/resources/conf/standalone.xml +++ b/functional-test/src/test/resources/conf/standalone.xml @@ -14,6 +14,7 @@ + @@ -26,6 +27,7 @@ + @@ -63,7 +65,7 @@ + pattern="%d{HH:mm:ss,SSS} %-5p [%c] (%t) %s%E%n" /> @@ -134,6 +136,10 @@ + + + + + + + true + false + NIO + 102400 + 2 + + + + + + + + + + + + + + + + + + jms.queue.DLQ + jms.queue.ExpiryQueue + 5000 + 2 + 10485760 + BLOCK + 10 + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + true + + + + true + + + + + @@ -26,6 +27,7 @@ + @@ -82,7 +84,7 @@ - + @@ -119,10 +121,10 @@ - + - + @@ -177,6 +179,10 @@ + + + + @@ -423,6 +429,86 @@ + + + true + false + NIO + 2 + + + + + + + + + + + + + + + + + + + + + jms.queue.DLQ + jms.queue.ExpiryQueue + -1 + 5000 + 3 + 10485760 + 2097152 + 5 + PAGE + 10 + false + 5000 + false + + + + + + + + + + + + + + 2 + 10 + + + + + + + + + + + + + + true + + + + true + + + + true + + + + diff --git a/functional-test/src/test/resources/log4j.xml b/functional-test/src/test/resources/log4j.xml index 51d1f060c4..be51424094 100644 --- a/functional-test/src/test/resources/log4j.xml +++ b/functional-test/src/test/resources/log4j.xml @@ -1,10 +1,11 @@ + - + diff --git a/functional-test/src/test/resources/setup.properties b/functional-test/src/test/resources/setup.properties index 31bc94103b..9733c32b91 100644 --- a/functional-test/src/test/resources/setup.properties +++ b/functional-test/src/test/resources/setup.properties @@ -6,7 +6,6 @@ webdriver.chrome.driver=${webdriver.chrome.driver} #this decides what web driver type we intended to use. webdriver.type=${webdriver.type} webdriver.log=${webdriver.log} -webdriver.display=${webdriver.display} webdriver.screenshot.dir=${webdriver.screenshot.dir} webdriver.wait=${webdriver.wait} zanata.instance.url=${zanata.instance.url} diff --git a/pom.xml b/pom.xml index f9a0f1e110..cf73c18374 100644 --- a/pom.xml +++ b/pom.xml @@ -2,13 +2,13 @@ 4.0.0 server - 3.6.0-SNAPSHOT + 3.7.0-SNAPSHOT Zanata server modules pom org.zanata zanata-parent - 21-SNAPSHOT + 21 ../parent @@ -24,16 +24,23 @@ 30 false ${project.build.directory}/delombok/org/zanata + 3 + false + false + + true + 18.0 2.6.1 50.1.1 ${project.build.sourceDirectory}/org/zanata 3.6.2 + ${lucene.version} 2.3.1.Final 1.2.1 0.22 - 3.6.0-SNAPSHOT + 3.7.0-SNAPSHOT 3.3.1 3.3.1 @@ -41,29 +48,29 @@ 4.5.0.Final + 1.1.5.Final + 1.2.2 + 2.0.0 2.0.0-alpha-5 provided - - 4.2.8.Final + 4.2.17.Final compile + 4.4.4.Final ${hibernate.version} false - 4.3.1.Final + 4.3.2.Final ${project.build.directory}/concordion - + 7.2.0.Final mysql @@ -71,42 +78,35 @@ 5.1.26 compile + 2.3.7.Final + + ${project.build.directory}/cargo/installs + + + + ${user.home}/Downloads + - org.jboss.shrinkwrap - shrinkwrap-bom - 1.2.2 - import - pom + shrinkwrap-api + ${shrinkwrap.api.version} - - org.jboss.shrinkwrap.resolver - shrinkwrap-resolver-bom - 2.0.0 - import - pom - - - - org.jboss.arquillian - arquillian-bom - 1.1.5.Final - import - pom + org.jboss.shrinkwrap + shrinkwrap-impl-base + ${shrinkwrap.api.version} - org.jboss.spec.javax.servlet - jboss-servlet-api_3.0_spec - 1.0.1.Final + org.jboss.shrinkwrap + shrinkwrap-spi + ${shrinkwrap.api.version} @@ -115,43 +115,96 @@ 1.13 - - org.jboss.resteasy - resteasy-bom - ${resteasy.version} + org.richfaces + richfaces + ${richfaces.version} + + + + org.jboss.spec + jboss-javaee-6.0 + 3.0.2.Final pom import - - org.richfaces - richfaces - ${richfaces.version} + antlr + antlr + 2.7.7 + provided + + commons-codec + commons-codec + 1.9 + - - org.jboss.as - jboss-as-parent - ${jboss.as.version} - pom - import + dom4j + dom4j + 1.6.1 + provided + + + xml-apis + xml-apis + + + + + + com.sun.faces + jsf-impl + 2.1.28 + provided - org.jboss.seam - bom + jboss-seam + ejb + ${seam.version} + + + javax.el + el-api + + + + + + org.jboss.seam + jboss-seam-debug ${seam.version} - pom - import + + + + org.jboss.seam + jboss-seam-mail + ${seam.version} + + + + org.jboss.seam + jboss-seam-ui + ${seam.version} + + + + org.jboss.security + jboss-negotiation-common + 2.3.6.Final + provided + + + + org.jboss.web + jbossweb + 7.5.4.Final + provided @@ -166,10 +219,16 @@ + + org.apache.httpcomponents + httpclient + 4.4 + + org.apache.httpcomponents httpcore - 4.2.3 + 4.4 @@ -293,14 +352,52 @@ zanata-common-api ${zanata.api.version} + + javax.annotation + jsr250-api + javax.servlet servlet-api + + javax.xml.bind + jaxb-api + + + org.jboss.resteasy + resteasy-jaxb-provider + + + org.jboss.resteasy + resteasy-jaxrs + + + org.jboss.resteasy + resteasy-multipart-provider + + + + + + org.zanata + zanata-common-api + ${zanata.api.compat.version} + compat + test + javax.annotation jsr250-api + + javax.servlet + servlet-api + + + javax.xml.bind + jaxb-api + @@ -562,13 +659,6 @@ 0.9.14 - - org.hibernate - hibernate-search - ${hibernate.search.version} - ${hibernate.search.scope} - - org.hibernate hibernate-search-orm @@ -581,6 +671,12 @@ hibernate-search-engine ${hibernate.search.version} ${hibernate.search.scope} + + + org.jboss.logging + jboss-logging + + @@ -608,12 +704,25 @@ org.hibernate.common hibernate-commons-annotations 4.0.4.Final + + + org.jboss.logging + jboss-logging + + org.hibernate hibernate-core ${hibernate.version} + + + org.jboss.logging + jboss-logging + + + @@ -632,6 +741,12 @@ org.hibernate hibernate-validator ${hibernate.validator.version} + + + org.jboss.logging + jboss-logging + + @@ -646,12 +761,6 @@ 3.18.2-GA - - org.jboss.el - jboss-el - 1.0_02.CR6 - - org.jboss.logging jboss-logging @@ -689,11 +798,47 @@ + + org.apache.lucene + lucene-core + ${lucene.version} + + + + org.apache.lucene + lucene-analyzers + ${lucene.version} + + + + org.apache.lucene + lucene-facet + ${lucene.version} + + + + org.apache.lucene + lucene-phonetic + ${lucene.version} + + + + org.apache.lucene + lucene-smartcn + ${lucene.version} + + + + org.apache.lucene + lucene-stempel + ${lucene.version} + + org.apache.solr solr-core - 3.6.2 + ${solr.version} commons-httpclient @@ -729,7 +874,7 @@ org.apache.solr solr-solrj - 3.6.2 + ${solr.version} commons-httpclient @@ -741,7 +886,11 @@ - + + org.apache.solr + solr-analysis-extras + ${solr.version} + net.customware.gwt.presenter gwt-presenter @@ -762,13 +911,6 @@ - - javax.validation - validation-api - 1.0.0.GA - compile - - org.concordion concordion @@ -933,6 +1075,18 @@ + + javax.annotation:jsr250-api + + + javax.el:el-api + + + javax.xml.bind:jaxb-api + + + org.jboss.spec.javax.ws.rs:jboss-jaxrs-api_1.1_spec + org.jboss.spec.javax.servlet:jboss-servlet-api_3.1_spec @@ -954,12 +1108,94 @@ + + + maven-antrun-plugin + 1.8 + + + init-no-appserver + + none + run + + + + + + + + + + + + + + + + + + org.codehaus.cargo + cargo-maven2-plugin + 1.4.5 + + + ${cargo.container} + + + ${cargo.installation} + ${download.dir} + ${cargo.extract.dir} + + + + + + + + + cargo-install + + none + + install + + + + maven-failsafe-plugin - 2.12 + 2.18.1 + + + integration-test + + integration-test + + + + verify + + verify + + + org.apache.maven.plugins @@ -980,6 +1216,12 @@ xml + + + org.zanata.util.CoverageIgnore + + true + @@ -1014,7 +1256,7 @@ com.jcabi jcabi-mysql-maven-plugin - 0.4 + 0.9 org.codehaus.gmaven @@ -1030,6 +1272,28 @@ + + + it.test + + + it.test + + + + + + maven-failsafe-plugin + + false + false + + + + + + delombok @@ -1088,17 +1352,91 @@ + + jbosseap6 + + + appserver + jbosseap6 + + + + jboss72x + + jboss-eap-6.3 + + ${cargo.extract.dir}/${cargo.basename}/${appserver.dir.name} + + + wildfly8 + true appserver wildfly8 + wildfly8x + 8.1.0.Final + + wildfly8 + http://download.jboss.org/wildfly/${wildfly.version}/wildfly-${wildfly.version}.zip + + wildfly-${wildfly.version} + + ${cargo.extract.dir}/${appserver.dir.name}/${appserver.dir.name} + + 8.1.0.Final + 2.1.28 + wildfly-${module.wildfly.version}-module-mojarra-${mojarra.module.version}.zip + 4.2.15.Final + wildfly-${module.wildfly.version}-module-hibernate-main-${hibernate.module.version}.zip + + + + + + maven-antrun-plugin + + + install-wildfly-modules + + none + + run + + + + + + + + + + + + + + + + diff --git a/zanata-dist/etc/assembly.xml b/zanata-dist/etc/assembly.xml deleted file mode 100644 index bf21efd6c4..0000000000 --- a/zanata-dist/etc/assembly.xml +++ /dev/null @@ -1,46 +0,0 @@ - - zanata-dist - - zip - tar.gz - - false - - - etc/sql - - *.sql - - sql - - - - - - org.zanata:zanata-war:war:jaas - - zanata${dashClassifier?}.${artifact.extension} - - - - org.zanata:zanata-war:war:kerberos - - zanata${dashClassifier?}.${artifact.extension} - - - - org.zanata:zanata-war:war:fedora - - zanata${dashClassifier?}.${artifact.extension} - - - - org.zanata:zanata-war:war:internal - - zanata${dashClassifier?}.${artifact.extension} - - - diff --git a/zanata-dist/etc/sql/config_setup.sql b/zanata-dist/etc/sql/config_setup.sql deleted file mode 100644 index b3a6cb2a62..0000000000 --- a/zanata-dist/etc/sql/config_setup.sql +++ /dev/null @@ -1,30 +0,0 @@ -/* - * ==================================================================================================================== - * Zanata Initial Setup Script - * - * Edit these variables for Zanata's initial setup. - * Note: Existing values will get overridden and some elements (e.g. Login Config Xml) will not insert - * a configuration record for null values. - * ==================================================================================================================== - */ -set @LOGIN_CONFIG_URL = null; -set @AUTH_TYPE = ''; /* Valid values: INTERNAL, KERBEROS, FEDORA_OPENID, JAAS */ - -/* - * ==================================================================================================================== - * End of Configurable elements. Do not edit this script beyond this marker. - * ==================================================================================================================== - */ - -/* Change to a custom Login Config Url */ -insert ignore into HApplicationConfiguration(creationDate, lastChanged, versionNum, config_key, config_value) -values(CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), 0, 'zanata.login-config.url', @LOGIN_CONFIG_URL) -on duplicate key update config_value = @LOGIN_CONFIG_URL, versionNum=versionNum+1, lastChanged=CURRENT_TIMESTAMP(); - -/* Change the Authentication type. Valid values are: INTERNAL, FEDORA_OPENID, KERBEROS, OTHER */ -insert into HApplicationConfiguration(creationDate, lastChanged, versionNum, config_key, config_value) -values(CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), 0, 'zanata.security.authType', @AUTH_TYPE) -on duplicate key update config_value = @AUTH_TYPE, versionNum=versionNum+1, lastChanged=CURRENT_TIMESTAMP(); - -/* Cleanup */ -delete from HApplicationConfiguration where config_key = 'zanata.login-config.url' and config_value = ''; \ No newline at end of file diff --git a/zanata-dist/pom.xml b/zanata-dist/pom.xml deleted file mode 100644 index 45bb56181b..0000000000 --- a/zanata-dist/pom.xml +++ /dev/null @@ -1,69 +0,0 @@ - - 4.0.0 - - server - org.zanata - 1.5-SNAPSHOT - - - zanata-dist - Packages all server artifacts for distribution. - Zanata Server distribution packages. - pom - - - - - maven-assembly-plugin - - - - false - - etc/assembly.xml - - - package - - single - - - - - - - - - - org.zanata - zanata-war - war - jaas - ${project.version} - - - org.zanata - zanata-war - war - kerberos - ${project.version} - - - org.zanata - zanata-war - war - fedora - ${project.version} - - - org.zanata - zanata-war - war - internal - ${project.version} - - - - diff --git a/zanata-model/pom.xml b/zanata-model/pom.xml index 4caf3b8c8a..85e8ddc087 100644 --- a/zanata-model/pom.xml +++ b/zanata-model/pom.xml @@ -4,7 +4,7 @@ org.zanata server - 3.6.0-SNAPSHOT + 3.7.0-SNAPSHOT zanata-model Zanata model @@ -34,7 +34,6 @@ org.projectlombok:lombok - org.hibernate:hibernate-search @@ -190,15 +189,11 @@ org.apache.lucene lucene-core - - ${lucene.version} org.apache.lucene lucene-analyzers - - ${lucene.version} @@ -253,7 +248,7 @@ org.hibernate - hibernate-search + hibernate-search-orm diff --git a/zanata-model/src/main/java/org/zanata/hibernate/search/ConfigurableNgramAnalyzer.java b/zanata-model/src/main/java/org/zanata/hibernate/search/ConfigurableNgramAnalyzer.java index b6b8735b97..17b326c57b 100644 --- a/zanata-model/src/main/java/org/zanata/hibernate/search/ConfigurableNgramAnalyzer.java +++ b/zanata-model/src/main/java/org/zanata/hibernate/search/ConfigurableNgramAnalyzer.java @@ -69,7 +69,8 @@ public ConfigurableNgramAnalyzer(int ngramMinLength, int ngramMaxLength, this.foldCase = foldCase; } - @SuppressWarnings("resource") // caller should close + // caller should close the TokenStream + @SuppressWarnings("resource") @Override public TokenStream tokenStream(String fieldName, Reader reader) { TokenStream tokenStream; diff --git a/zanata-model/src/main/java/org/zanata/hibernate/search/TextContainerAnalyzerDiscriminator.java b/zanata-model/src/main/java/org/zanata/hibernate/search/TextContainerAnalyzerDiscriminator.java index c53ace9dca..29bad4c62c 100644 --- a/zanata-model/src/main/java/org/zanata/hibernate/search/TextContainerAnalyzerDiscriminator.java +++ b/zanata-model/src/main/java/org/zanata/hibernate/search/TextContainerAnalyzerDiscriminator.java @@ -83,9 +83,8 @@ public static String getAnalyzerDefinitionName(String localeId) { if (langCode.equalsIgnoreCase("zh") || langCode.equalsIgnoreCase("ja") || langCode.equalsIgnoreCase("ko")) { return "UnigramAnalyzer"; - } - // All other languages - else { + } else { + // All other languages return "StandardAnalyzer"; } } diff --git a/zanata-model/src/main/java/org/zanata/hibernate/search/TimeCachedFilter.java b/zanata-model/src/main/java/org/zanata/hibernate/search/TimeCachedFilter.java index 71b474fe8b..7086b19715 100644 --- a/zanata-model/src/main/java/org/zanata/hibernate/search/TimeCachedFilter.java +++ b/zanata-model/src/main/java/org/zanata/hibernate/search/TimeCachedFilter.java @@ -48,7 +48,8 @@ public abstract class TimeCachedFilter extends Filter { /** * Expiry time for docIdSet as a Java timestamp (ms) */ - private long expiryTime = Long.MIN_VALUE; // force fetch on first use + // force fetch on first use + private long expiryTime = Long.MIN_VALUE; private final long validityPeriodInMs; public TimeCachedFilter(long validityPeriodInMs) { diff --git a/zanata-model/src/main/java/org/zanata/model/HDocument.java b/zanata-model/src/main/java/org/zanata/model/HDocument.java index acf84db49a..959d7ff19d 100644 --- a/zanata-model/src/main/java/org/zanata/model/HDocument.java +++ b/zanata-model/src/main/java/org/zanata/model/HDocument.java @@ -223,12 +223,8 @@ public ContentType getContentType() { } @OneToMany - @JoinColumn(name = "document_id", insertable = false, updatable = false/* - * , - * nullable - * = - * true - */) + @JoinColumn(name = "document_id", insertable = false, updatable = false) + // , nullable = true @MapKey(name = "resId") /** * NB Don't modify this collection. Add to the TextFlows list instead. diff --git a/zanata-model/src/main/java/org/zanata/model/HLocale.java b/zanata-model/src/main/java/org/zanata/model/HLocale.java index 20e7401223..a9fa9e4c05 100644 --- a/zanata-model/src/main/java/org/zanata/model/HLocale.java +++ b/zanata-model/src/main/java/org/zanata/model/HLocale.java @@ -34,6 +34,7 @@ import javax.validation.constraints.NotNull; import lombok.EqualsAndHashCode; +import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.ToString; @@ -61,12 +62,25 @@ public class HLocale extends ModelEntityBase implements Serializable { private static final long serialVersionUID = 1L; private @Nonnull LocaleId localeId; + + @Getter private boolean active; + + @Getter private boolean enabledByDefault; private Set supportedProjects; private Set supportedIterations; private Set members; + @Getter + private String pluralForms; + + @Getter + private String displayName; + + @Getter + private String nativeName; + public HLocale(@Nonnull LocaleId localeId) { this.localeId = localeId; } @@ -88,18 +102,6 @@ LocaleId getLocaleId() { return localeId; } - public boolean isActive() { - return active; - } - - public boolean isEnabledByDefault() { - return enabledByDefault; - } - - public void setEnabledByDefault(boolean enabledByDefault) { - this.enabledByDefault = enabledByDefault; - } - @OneToMany(cascade = CascadeType.ALL, mappedBy = "id.supportedLanguage") @Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL) public Set getMembers() { @@ -130,10 +132,24 @@ public Set getSupportedIterations() { } public String retrieveNativeName() { - return asULocale().getDisplayName(asULocale()); + if (nativeName == null || nativeName.equals("")) { + return retrieveDefaultNativeName(); + } + return nativeName; } public String retrieveDisplayName() { + if (displayName == null || displayName.equals("")) { + return retrieveDefaultDisplayName(); + } + return displayName; + } + + public String retrieveDefaultNativeName() { + return asULocale().getDisplayName(asULocale()); + } + + public String retrieveDefaultDisplayName() { return asULocale().getDisplayName(); } diff --git a/zanata-model/src/main/java/org/zanata/util/OkapiUtil.java b/zanata-model/src/main/java/org/zanata/util/OkapiUtil.java index de33f0ff72..5b65e054c1 100644 --- a/zanata-model/src/main/java/org/zanata/util/OkapiUtil.java +++ b/zanata-model/src/main/java/org/zanata/util/OkapiUtil.java @@ -111,7 +111,7 @@ public static long countWords(String s, String bcp47Locale) { * @return A string with all tmx mark-up content stripped out. Essentially a * plain text version of the string. */ - public static String removeFormattingMarkup(/* final */String content) { + public static String removeFormattingMarkup(String content) { // The content must be a fully formed element with optional // attributes and with no leading/trailing whitespace assert content.startsWith(""); @@ -125,8 +125,8 @@ public static String removeFormattingMarkup(/* final */String content) { content.getBytes())); StringBuilder writer = new StringBuilder(); - int ignoreLevel = 0; // Nesting level. When this is > 0 it means we - // are ignoring events + // Nesting level. When this is > 0 it means we are ignoring events + int ignoreLevel = 0; while (reader.hasNext()) { XMLEvent nextEv = reader.nextEvent(); diff --git a/zanata-model/src/main/java/org/zanata/util/ZanataEntities.java b/zanata-model/src/main/java/org/zanata/util/ZanataEntities.java index 3b8eea666c..4c2692f7e2 100644 --- a/zanata-model/src/main/java/org/zanata/util/ZanataEntities.java +++ b/zanata-model/src/main/java/org/zanata/util/ZanataEntities.java @@ -25,6 +25,7 @@ import org.zanata.model.Activity; import org.zanata.model.HAccount; import org.zanata.model.HAccountActivationKey; +import org.zanata.model.HAccountResetPasswordKey; import org.zanata.model.HDocument; import org.zanata.model.HDocumentHistory; import org.zanata.model.HGlossaryEntry; @@ -33,6 +34,7 @@ import org.zanata.model.HLocale; import org.zanata.model.HLocaleMember; import org.zanata.model.HPerson; +import org.zanata.model.HPersonEmailValidationKey; import org.zanata.model.HProject; import org.zanata.model.HProjectIteration; import org.zanata.model.HTermComment; @@ -41,6 +43,7 @@ import org.zanata.model.HTextFlowTarget; import org.zanata.model.HTextFlowTargetHistory; import org.zanata.model.HTextFlowTargetReviewComment; +import org.zanata.model.WebHook; import org.zanata.model.po.HPoHeader; import org.zanata.model.po.HPoTargetHeader; import org.zanata.model.po.HPotEntryData; @@ -90,10 +93,12 @@ public static List entitiesForRemoval() { builder.add(HLocaleMember.class, HLocale.class); builder.add(HIterationGroup.class); // project - builder.add(HProjectIteration.class, HProject.class); + builder.add(HProjectIteration.class, WebHook.class, HProject.class); // account builder.add(HAccountActivationKey.class, HCredentials.class, + HPersonEmailValidationKey.class, HPerson.class, + HAccountResetPasswordKey.class, HAccount.class); entitiesForDelete = builder.build(); diff --git a/zanata-model/src/test/java/org/zanata/util/OkapiUtilTest.java b/zanata-model/src/test/java/org/zanata/util/OkapiUtilTest.java index 7390d4bda7..f75addd026 100644 --- a/zanata-model/src/test/java/org/zanata/util/OkapiUtilTest.java +++ b/zanata-model/src/test/java/org/zanata/util/OkapiUtilTest.java @@ -27,10 +27,12 @@ public class OkapiUtilTest { "/var/lib/ricci", - "https://cdn.redhat.com", // NB okapi says 2, but looks like 4. perhaps + // NB okapi says 2, but looks like 4. perhaps // something.ext counts as one word? + "https://cdn.redhat.com", - "/etc/rhsm/rhsm.conf", // NB okapi says 3, but looks like 4 + // NB okapi says 3, but looks like 4 + "/etc/rhsm/rhsm.conf", }; // @formatter:on // These counts represent the expected word counts for the strings listed diff --git a/zanata-model/src/test/resources/log4j.xml b/zanata-model/src/test/resources/log4j.xml new file mode 100644 index 0000000000..0c25c70101 --- /dev/null +++ b/zanata-model/src/test/resources/log4j.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/zanata-overlay/.gitignore b/zanata-overlay/.gitignore new file mode 100644 index 0000000000..08eb0a07a6 --- /dev/null +++ b/zanata-overlay/.gitignore @@ -0,0 +1 @@ +!bin/ \ No newline at end of file diff --git a/zanata-overlay/build.gradle b/zanata-overlay/build.gradle new file mode 100644 index 0000000000..c4024ea9c1 --- /dev/null +++ b/zanata-overlay/build.gradle @@ -0,0 +1,103 @@ +defaultTasks 'package' +apply plugin: 'java' + +repositories { + mavenCentral() +} + +dependencies { + runtime "mysql:mysql-connector-java:5.1.29" + runtime "org.codehaus.groovy:groovy-all:2.1.5" +} + +final def BUILD_DIR = "target" + +task 'package'(dependsOn: 'copyToLib') << { + def distros = findAvailableDistros() + def zanataVersion = readProjectVersion() + + distros.each { distro -> + def distroName = "zanata-$zanataVersion-$distro" + def distroDir = "$BUILD_DIR/$distroName" + // Make a build directory fro the distribution + ant.mkdir(dir: distroDir) + // Copy the common files for all distributions + ant.copy(todir: distroDir) { + fileset(dir: "common") + } + // Make sure executable files have the right permissions + ant.chmod(dir: "$distroDir/", perm: "ug+x", includes: "**/*.sh") + // Copy specific distribution files + ant.copy(todir: distroDir) { + fileset(dir: "distros/$distro") + } + // Copy extra needed files + ant.copy(todir: "$distroDir/standalone/deployments") { + fileset(dir: "$BUILD_DIR/deps") { + include(name: "mysql-connector-java-*.jar") + } + globmapper(from: "mysql-connector-java-*.jar", + to: "mysql-connector-java.jar") + } + ant.copy(todir: "$distroDir/bin/zanata-installer") { + fileset(dir: "$BUILD_DIR/deps") { + include(name: "groovy-all-*.jar") + } + } + // Download remote dependencies + downloadRemoteDeps(distro, distroDir) + // Replace templates + replaceTemplates(distroDir, zanataVersion) + // Zip it up + ant.zip(destfile: "$BUILD_DIR/${distroName}.zip", basedir: distroDir) + // Cleanup + //ant.delete(dir: "$BUILD_DIR/deps") + ant.delete(dir: distroDir) + } +} + +task copyToLib(type: Copy) { + into "$BUILD_DIR/deps" + from configurations.runtime +} + +List findAvailableDistros() { + new File("distros").listFiles() + .findAll { !it.name.startsWith(".") && it.isDirectory() } + .collect { it.name } +} + +String readProjectVersion() { + (new groovy.util.XmlParser()).parse(new File("../pom.xml")).version.text() +} + +def replaceTemplates(String distroDir, String zanataVersion) { + def engine = new groovy.text.SimpleTemplateEngine() + def templateFile = new File(distroDir, + "bin/zanata-installer/installer.properties") + def template = engine.createTemplate(templateFile) + def result = template.make(['version': zanataVersion]) + templateFile.newWriter() << result +} + +def downloadRemoteDeps(String distro, String distroDir) { + def config = new groovy.util.ConfigSlurper(). + parse(new File('config/remote-deps.groovy').toURL()) + config."$distro"?.each { depName, value -> + def dep = config."$distro"."$depName" + def destFile = new File("$distroDir/${dep.toFile}") + download(dep.url, destFile) + if (dep.extract) { + ant.unzip(src: "${destFile.absolutePath}", + dest: "${destFile.parent}") + ant.delete(file: "${destFile.absolutePath}") + } + } +} + +def download(String url, File destFile) { + def file = new FileOutputStream(destFile) + def out = new BufferedOutputStream(file) + out << new URL(url).openStream() + out.close() +} diff --git a/zanata-overlay/common/bin/zanata-installer/InstallZanata.groovy b/zanata-overlay/common/bin/zanata-installer/InstallZanata.groovy new file mode 100644 index 0000000000..1ad25aef7d --- /dev/null +++ b/zanata-overlay/common/bin/zanata-installer/InstallZanata.groovy @@ -0,0 +1,131 @@ +import groovy.xml.XmlUtil + +import java.nio.file.Files + +/** + * Zanata installer script. + * Downloads Zanata and configures certain parts of the application for first + * use. + * @author Carlos Munoz camunoz@redhat.com + */ +Properties installerProps = new Properties() +File propertiesFile = new File('installer.properties') +propertiesFile.withInputStream { + installerProps.load(it) +} +def ZANATA_VERSION = installerProps['zanata.version'] +final JBOSS_CFG_DIR = "../../standalone/configuration" +final CUSTOM_STANDALONE_XML_LOC = "${JBOSS_CFG_DIR}/standalone-zanata.xml" +final ORIGINAL_STANDALONE_XML_LOC = "${JBOSS_CFG_DIR}/standalone.xml" +final WAR_FILE_LOC = "../../standalone/deployments/zanata.war" + +boolean askYesNoQuestion(String question) { + def reader = new BufferedReader(new InputStreamReader(System.in)) + println "" + println question + def answer = reader.readLine() + return answer.trim().equalsIgnoreCase("y") +} + +println "=====================================================" +println " Welcome to the Zanata Installer. This will install" +println " Zanata ${ZANATA_VERSION} onto your JBoss or Wildfly" +println "" +println " Note: This installer will change your standalone.xml" +println " file." +println "=====================================================" + +def downloadWarFile = true +if( new File(WAR_FILE_LOC).exists() ) { + downloadWarFile = askYesNoQuestion("It seems Zanata is already installed. Do you wish to download it again? (y/N)") +} + +def dbHost +def dbPort +def dbSchema +def dbUsername +def dbPassword + +// Download the war file +if( downloadWarFile ) { + def fileUrl = "http://sourceforge.net/projects/zanata/files/webapp/zanata-war-${ZANATA_VERSION}.war/download" + println "Downloading $fileUrl" + println "This might take a few minutes." + + try { + def file = new FileOutputStream(WAR_FILE_LOC) + def out = new BufferedOutputStream(file) + out << new URL(fileUrl).openStream() + out.close() + println "Downloaded Zanata ${ZANATA_VERSION}..." + } catch (Exception ex) { + println "Could not download zanata-war-${ZANATA_VERSION}.war" + } +} + +// Move the original standalone and copy the custom one +def xmlFile = new File(ORIGINAL_STANDALONE_XML_LOC) +def xmlBackupFile = new File("${JBOSS_CFG_DIR}/standalone.xml.original") +def customXmlFile = new File(CUSTOM_STANDALONE_XML_LOC) + +// If the installer has already been ran +def modifyStandaloneXml = true +if(xmlBackupFile.exists()) { + modifyStandaloneXml = askYesNoQuestion("It looks like you have already run this installer. " + + "If you continue, your current standalone.xml file will be backed up and modified. " + + "Do you wish to continue? (y/N)") +} + +if(modifyStandaloneXml) { + println "Please provide the following information:" + println "(If blank, the default value will apply)" + println "" + + def reader = new BufferedReader(new InputStreamReader(System.in)) + + println "Database Host (default: 'localhost'):" + dbHost = reader.readLine() + if(dbHost.isEmpty()) dbHost = 'localhost' + + println "Database port (default: '3306'):" + dbPort = reader.readLine() + if(dbPort.isEmpty()) dbPort = '3306' + + println "Database schema (default: 'zanata'):" + dbSchema = reader.readLine() + if(dbSchema.isEmpty()) dbSchema = 'zanata' + + println "Database Username (default: ''):" + dbUsername = reader.readLine() + + println "Database Password (default: ''):" + dbPassword = reader.readLine() + + xmlFile.renameTo(xmlBackupFile) // backup original file + Files.copy(customXmlFile.toPath(), xmlFile.toPath()) // standalone-zanata.xml -> standalone.xml + + // Update zanata datasource + def dsXml = new XmlParser().parse(CUSTOM_STANDALONE_XML_LOC) + + def zanataDs = + dsXml.profile.subsystem. + find { s -> s.datasources }.datasources.datasource + .find { + it.@'jndi-name' == "java:jboss/datasources/zanataDatasource" + } + + zanataDs.'connection-url'.replaceNode { + 'connection-url'( + "jdbc:mysql://${dbHost}:${dbPort}/${dbSchema}?characterEncoding=UTF-8") + } + zanataDs.security.replaceNode { node -> + security { + 'user-name'(dbUsername) + password(dbPassword) + } + } + + XmlUtil.serialize(dsXml, new FileOutputStream(CUSTOM_STANDALONE_XML_LOC)) + println "Configured zanata in $CUSTOM_STANDALONE_XML_LOC" +} +println "Done!" diff --git a/zanata-overlay/common/bin/zanata-installer/install.bat b/zanata-overlay/common/bin/zanata-installer/install.bat new file mode 100755 index 0000000000..920b70b165 --- /dev/null +++ b/zanata-overlay/common/bin/zanata-installer/install.bat @@ -0,0 +1 @@ +java -cp .;groovy-all-2.1.5.jar groovy.lang.GroovyShell InstallZanata.groovy diff --git a/zanata-overlay/common/bin/zanata-installer/install.sh b/zanata-overlay/common/bin/zanata-installer/install.sh new file mode 100755 index 0000000000..dc4b877ff7 --- /dev/null +++ b/zanata-overlay/common/bin/zanata-installer/install.sh @@ -0,0 +1,2 @@ +#!/bin/sh +exec java -cp '.:groovy-all-2.1.5.jar' groovy.lang.GroovyShell InstallZanata.groovy diff --git a/zanata-overlay/common/bin/zanata-installer/installer.properties b/zanata-overlay/common/bin/zanata-installer/installer.properties new file mode 100644 index 0000000000..c02cc31529 --- /dev/null +++ b/zanata-overlay/common/bin/zanata-installer/installer.properties @@ -0,0 +1 @@ +zanata.version=$version diff --git a/zanata-overlay/config/remote-deps.groovy b/zanata-overlay/config/remote-deps.groovy new file mode 100644 index 0000000000..cdddbceb65 --- /dev/null +++ b/zanata-overlay/config/remote-deps.groovy @@ -0,0 +1,32 @@ +/** + * This is where all Remote dependencies are configured for each of the Zanata + * overlay distributions. + * Each distribution under the 'distros' folder may have a configuration entry + * on this file. Each of those entries must have the following structure: + * + * <> { + * <> { + * url = "Url where the dependency is to be downloaded from" + * toFile = "File (on the distro build folder) where the downloaded dependecny should reside" + * extract = true|false // Indicates if the dependency is to be extracted in place. If it is, the original file will be removed after extraction. + * } + * + * ... More dependencies + * } + * + * The dependency name is not relevant, just an identifier. + */ + +'wildfly-8.1' { + hibernatemodule { + url = "http://sourceforge.net/projects/zanata/files/wildfly/wildfly-8.1.0.Final-module-hibernate-main-4.2.15.Final.zip/download" + toFile = "/hibernate-module.zip" + extract = true + } + + jsfmodule { + url = "http://sourceforge.net/projects/zanata/files/wildfly/wildfly-8.1.0.Final-module-mojarra-2.1.28.zip/download" + toFile = "/mojarra-module.zip" + extract = true + } +} \ No newline at end of file diff --git a/zanata-overlay/distros/eap-6/standalone/configuration/standalone-zanata.xml b/zanata-overlay/distros/eap-6/standalone/configuration/standalone-zanata.xml new file mode 100644 index 0000000000..ee7c3d676c --- /dev/null +++ b/zanata-overlay/distros/eap-6/standalone/configuration/standalone-zanata.xml @@ -0,0 +1,479 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE + h2 + + sa + sa + + + + jdbc:mysql://localhost:3306/zanata?characterEncoding=UTF-8 + com.mysql.jdbc.Driver + mysql-connector-java.jar + + 0 + 20 + FailingConnectionOnly + + + root + + + NOWARN + + + + + org.h2.jdbcx.JdbcDataSource + + + + + + + + + false + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + false + NIO + 102400 + 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + jms.queue.DLQ + jms.queue.ExpiryQueue + 5000 + 2 + 10485760 + 2097152 + BLOCK + 10 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + true + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + ${jboss.bind.address:127.0.0.1} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/zanata-overlay/distros/wildfly-8.1/standalone/configuration/standalone-zanata.xml b/zanata-overlay/distros/wildfly-8.1/standalone/configuration/standalone-zanata.xml new file mode 100644 index 0000000000..a4edfd67c3 --- /dev/null +++ b/zanata-overlay/distros/wildfly-8.1/standalone/configuration/standalone-zanata.xml @@ -0,0 +1,566 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE + h2 + + sa + sa + + + + jdbc:mysql://localhost:3306/zanata?characterEncoding=UTF-8 + com.mysql.jdbc.Driver + mysql-connector-java.jar + + 0 + 20 + FailingConnectionOnly + + + root + + + NOWARN + + + + + org.h2.jdbcx.JdbcDataSource + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + false + NIO + 102400 + 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + jms.queue.DLQ + jms.queue.ExpiryQueue + 5000 + 2 + 10485760 + 2097152 + BLOCK + 10 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + true + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${jboss.bind.address:127.0.0.1} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/zanata-test-war/pom.xml b/zanata-test-war/pom.xml index 9f803371ed..e25d6d1349 100644 --- a/zanata-test-war/pom.xml +++ b/zanata-test-war/pom.xml @@ -1,13 +1,10 @@ - + 4.0.0 org.zanata server - 3.6.0-SNAPSHOT + 3.7.0-SNAPSHOT zanata-test-war zanata-test-war @@ -134,7 +131,7 @@ run-functional-test - functional-test + !skipFuncTests diff --git a/zanata-test-war/src/main/java/org/zanata/rest/SampleDataResourceImpl.java b/zanata-test-war/src/main/java/org/zanata/rest/SampleDataResourceImpl.java index d3b0f41a3b..5169246303 100644 --- a/zanata-test-war/src/main/java/org/zanata/rest/SampleDataResourceImpl.java +++ b/zanata-test-war/src/main/java/org/zanata/rest/SampleDataResourceImpl.java @@ -57,7 +57,7 @@ public Response addLanguage(final String localeId) { new RunAsOperation() { @Override public void execute() { - sampleProjectProfile.makeLanguage(new LocaleId(localeId)); + sampleProjectProfile.makeLanguage(true, new LocaleId(localeId)); } }.addRole("admin").run(); return Response.ok().build(); diff --git a/zanata-test-war/src/main/java/org/zanata/util/SampleProjectProfile.java b/zanata-test-war/src/main/java/org/zanata/util/SampleProjectProfile.java index 33d62c5154..64d9852827 100644 --- a/zanata-test-war/src/main/java/org/zanata/util/SampleProjectProfile.java +++ b/zanata-test-war/src/main/java/org/zanata/util/SampleProjectProfile.java @@ -50,6 +50,7 @@ import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; +import com.ibm.icu.util.ULocale; import lombok.extern.slf4j.Slf4j; /** @@ -74,33 +75,11 @@ public class SampleProjectProfile { private HPerson admin; public void deleteExceptEssentialData() { - EntityCleaner.deleteAll(entityManager, Lists.newArrayList( - TransMemoryUnitVariant.class, TransMemoryUnit.class, - TransMemory.class, - Activity.class, - // glossary - HTermComment.class, HGlossaryTerm.class, HGlossaryEntry.class, - // tex flows and targets - HPoTargetHeader.class, HTextFlowTargetHistory.class, - HTextFlowTarget.class, HTextFlow.class, - // documents - HDocumentHistory.class, HDocument.class, - // locales - HLocaleMember.class, HLocale.class, - // version group - HIterationGroup.class, - // project - HProjectIteration.class, HProject.class, - // account - HAccountActivationKey.class, HPersonEmailValidationKey.class, - HAccountResetPasswordKey.class, HCredentials.class, - HPerson.class, HAccount.class, - // account role - HRoleAssignmentRule.class - )); - enUSLocale = - forLocale(false, LocaleId.EN_US).makeAndPersist(entityManager, - HLocale.class); + EntityCleaner.deleteAll(entityManager, ZanataEntities + .entitiesForRemoval()); + + ULocale uLocale = new ULocale(LocaleId.EN_US.getId()); + enUSLocale = makeLanguage(false, LocaleId.EN_US); List configurations = entityManager .createQuery("from HApplicationConfiguration", @@ -147,28 +126,41 @@ private void purgeLuceneIndexes() { } public void makeSampleLanguages() { - makeLanguage(LocaleId.FR); + makeLanguage(true, LocaleId.FR); + + makeLanguage(true, new LocaleId("hi")); - makeLanguage(new LocaleId("hi")); + makeLanguage(true, new LocaleId("pl")); + } - makeLanguage(new LocaleId("pl")); + public HLocale makeLanguage(boolean enabledByDefault, LocaleId localeId, + String displayName, String nativeName) { + return forLocale(enabledByDefault, localeId, displayName, nativeName) + .makeAndPersist(entityManager, + HLocale.class); } - public void makeLanguage(LocaleId localeId) { - forLocale(true, localeId).makeAndPersist(entityManager, - HLocale.class); + public HLocale makeLanguage(boolean enabledByDefault, LocaleId localeId) { + ULocale uLocale = new ULocale(localeId.getId()); + return forLocale(enabledByDefault, localeId, uLocale.getDisplayName(), + uLocale.getDisplayName(uLocale)).makeAndPersist(entityManager, + HLocale.class); } private static EntityMaker forLocale(boolean enabledByDefault, - LocaleId localeId) { + LocaleId localeId, String displayName, String nativeName) { return EntityMakerBuilder .builder() .addFieldOrPropertyMaker(HLocale.class, "active", - FixedValueMaker.ALWAYS_TRUE_MAKER) + FixedValueMaker.ALWAYS_TRUE_MAKER) + .addFieldOrPropertyMaker(HLocale.class, "displayName", + FixedValueMaker.fix(displayName)) + .addFieldOrPropertyMaker(HLocale.class, "nativeName", + FixedValueMaker.fix(nativeName)) .addFieldOrPropertyMaker(HLocale.class, "enabledByDefault", - FixedValueMaker.fix(enabledByDefault)) + FixedValueMaker.fix(enabledByDefault)) .addConstructorParameterMaker(HLocale.class, 0, - FixedValueMaker.fix(localeId)).build(); + FixedValueMaker.fix(localeId)).build(); } public void makeSampleUsers() { diff --git a/zanata-war/pom.xml b/zanata-war/pom.xml index 5dfecd434b..174ce3cb1f 100644 --- a/zanata-war/pom.xml +++ b/zanata-war/pom.xml @@ -4,7 +4,7 @@ org.zanata server - 3.6.0-SNAPSHOT + 3.7.0-SNAPSHOT zanata-war war @@ -35,7 +35,7 @@ ${org.projectlombok:lombok:jar} 2.1.5 - 6 + 7 @@ -102,7 +102,7 @@ - javax.xml.bind:jaxb-api + org.jboss.spec.javax.xml.bind:jboss-jaxb-api_2.2_spec com.mattbertolini:liquibase-slf4j org.opensymphony.quartz:quartz @@ -110,8 +110,6 @@ org.jboss.shrinkwrap.resolver:shrinkwrap-resolver-api org.jboss.resteasy:resteasy-multipart-provider - - com.google.protobuf:protobuf-java com.lowagie:itext @@ -121,21 +119,18 @@ com.google.guava:guava-gwt - - javax.el:el-api + + org.jboss.spec.javax.el:jboss-el-api_2.2_spec javax.servlet.jsp:jsp-api com.sun.faces:jsf-impl org.apache.solr:solr-core org.apache.solr:solr-solrj - - org.drools:drools-compiler org.antlr:antlr org.hibernate.common:hibernate-commons-annotations org.hibernate:hibernate-search-analyzers - org.hibernate:hibernate-search org.hibernate:hibernate-testing org.jboss.arquillian.junit:arquillian-junit-container @@ -161,7 +156,9 @@ - + + + com.ning.maven.plugins @@ -174,32 +171,16 @@ + + META-INF/.* com/lowagie/text/pdf/fonts/cmap_info.txt - - META-INF/components.xml - - META-INF/faces-config.xml - - META-INF/javamail.providers - - META-INF/richfaces/resource-mappings.properties - - META-INF/seam-deployment.properties build.properties seam.properties - - - validate - - check - - - @@ -973,6 +954,81 @@ + + skipArqTests + + + skipArqTests + + + + true + + + + + run-arq-tests + + + !skipArqTests + + + + + + maven-antrun-plugin + + + init-no-appserver + initialize + + + preIT-no-appserver + prepare-package + run + + + + + + + + + + + org.codehaus.cargo + cargo-maven2-plugin + + + cargo-install + prepare-package + + + + + + maven-failsafe-plugin + + + **/*ITCase.java + + 1 + true + false + alphabetical + + org.jboss.logmanager.LogManager + ${appserver.home} + + none:none + + + + + + jbosseap6 @@ -986,19 +1042,18 @@ maven-antrun-plugin + prepare-arquillian-container - pre-integration-test + package run - - + + tofile="${appserver.home}/standalone/configuration/standalone-arquillian.xml" /> @@ -1018,31 +1073,14 @@ - maven-failsafe-plugin - - - - container-tests - - integration-test - verify - - - - **/*ITCase.java - - none:none - alphabetical - 1 - true - - jbossas-managed - - - - + + + + jbossas-managed + + @@ -1052,6 +1090,7 @@ org.jboss.as jboss-as-arquillian-common + ${jboss.as.version} test @@ -1078,14 +1117,28 @@ + + org.jboss.osgi.metadata + jbosgi-metadata + 2.2.0.Final + + + + org.jboss.osgi.vfs + jbosgi-vfs30 + 1.2.1.Final + + org.jboss.as jboss-as-controller-client + ${jboss.as.version} test org.jboss.as jboss-as-controller + ${jboss.as.version} test @@ -1099,36 +1152,10 @@ wildfly8 - - ${project.build.directory}/wildfly/wildfly-${wildfly.version} - - maven-dependency-plugin - - - unpack - pre-integration-test - - unpack - - - - - org.wildfly - wildfly-dist - ${wildfly.version} - zip - false - ${project.build.directory}/wildfly - - - - - @@ -1142,78 +1169,42 @@ maven-antrun-plugin + + install-wildfly-modules + package + + + prepare-arquillian-container - pre-integration-test + package run - + - - - - - + tofile="${appserver.home}/standalone/configuration/standalone.xml" /> - maven-failsafe-plugin - - - - container-tests - - integration-test - verify - - - - **/*ITCase.java - - none:none - alphabetical - 1 - true - - wildfly81 - org.jboss.logmanager.LogManager - - ${wildfly.home} - - false - - - + + + + wildfly81 + + - - io.undertow - undertow-core - 1.0.15.Final - provided - org.wildfly wildfly-arquillian-container-managed @@ -1293,6 +1284,12 @@ org.zanata zanata-adapter-po + + + javax.xml.bind + jaxb-api + + @@ -1311,8 +1308,8 @@ org.zanata zanata-common-api - ${zanata.api.compat.version} compat + test @@ -1360,6 +1357,7 @@ org.jboss.resteasy resteasy-jaxrs + ${resteasy.version} ${resteasy.scope} @@ -1380,6 +1378,12 @@ org.jboss.resteasy resteasy-jaxb-provider ${resteasy.scope} + + + javax.xml.bind + jaxb-api + + org.jboss.resteasy @@ -1393,12 +1397,16 @@ ${resteasy.scope} - - servlet-api - - - javax.servlet - + javax.servlet + servlet-api + + + org.jboss.logging + jboss-logging + + + org.jboss.resteasy + resteasy-jaxrs @@ -1428,38 +1436,11 @@ org.jboss jboss-dmr + 1.2.1.Final test - - - - org.drools - drools-core - - - com.google.protobuf - protobuf-java - - - org.drools - drools-compiler - - - ecj - org.eclipse.jdt.core.compiler - - - org.antlr - antlr - - - antlr - antlr - - - - + org.antlr antlr @@ -1470,13 +1451,11 @@ antlr antlr - provided dom4j dom4j - provided @@ -1507,7 +1486,6 @@ com.sun.faces jsf-impl - provided @@ -1534,6 +1512,10 @@ ejb3-persistence org.hibernate + + org.jboss.logging + jboss-logging + @@ -1549,6 +1531,13 @@ + + io.undertow + undertow-core + 1.0.15.Final + provided + + io.undertow undertow-servlet @@ -1697,6 +1686,19 @@ ${lucene.version} + + org.apache.maven + maven-artifact + 3.2.1 + + + + org.codehaus.plexus + plexus-utils + + + + org.apache.solr @@ -1717,10 +1719,9 @@ solr-solrj - org.hibernate - hibernate-search + hibernate-search-orm org.hibernate @@ -1746,15 +1747,12 @@ org.hibernate.javax.persistence hibernate-jpa-2.0-api + provided org.hibernate hibernate-search-engine - - org.hibernate - hibernate-search-orm - org.hibernate hibernate-search-infinispan @@ -1763,6 +1761,8 @@ org.infinispan infinispan-core + + 5.2.4.Final provided @@ -1777,11 +1777,11 @@ org.jboss.marshalling jboss-marshalling-river provided - + - javax.el - el-api + org.jboss.spec.javax.el + jboss-el-api_2.2_spec provided @@ -1850,12 +1850,21 @@ org.jboss.web jbossweb - provided org.jboss.security jboss-negotiation-common + + + + org.jboss.spec.javax.jms + jboss-jms-api_1.1_spec + provided + + + org.jboss.spec.javax.ejb + jboss-ejb-api_3.1_spec provided @@ -1889,62 +1898,74 @@ org.jboss.shrinkwrap.resolver shrinkwrap-resolver-api-maven + ${shrinkwrap.resolver.version} test org.jboss.shrinkwrap.resolver shrinkwrap-resolver-api + ${shrinkwrap.resolver.version} test org.jboss.arquillian.config arquillian-config-api + ${arquillian.version} test org.jboss.arquillian.container arquillian-container-test-api + ${arquillian.version} test org.jboss.arquillian.container arquillian-container-test-impl-base + ${arquillian.version} test org.jboss.arquillian.container arquillian-container-spi + ${arquillian.version} test org.jboss.arquillian.core arquillian-core-spi + ${arquillian.version} test org.jboss.arquillian.core arquillian-core-api + ${arquillian.version} test org.jboss.arquillian.junit arquillian-junit-container + ${arquillian.version} test org.jboss.arquillian.junit arquillian-junit-core + ${arquillian.version} test org.jboss.arquillian.test arquillian-test-api + ${arquillian.version} test org.jboss.arquillian.test arquillian-test-spi + ${arquillian.version} test @@ -1974,6 +1995,7 @@ org.jboss.arquillian.protocol arquillian-protocol-servlet + ${arquillian.version} test @@ -2053,6 +2075,12 @@ org.apache.httpcomponents httpclient + + + commons-logging + commons-logging + + @@ -2088,7 +2116,7 @@ se.jiderhamn classloader-leak-prevention - 1.9.3 + 1.11.0 javax.servlet @@ -2136,11 +2164,12 @@ org.jboss.resteasy jaxrs-api + ${resteasy.version} ${resteasy.scope} - javax.xml.bind - jaxb-api + org.jboss.spec.javax.xml.bind + jboss-jaxb-api_2.2_spec provided @@ -2209,9 +2238,10 @@ - javax.xml.bind - jaxb-api + org.jboss.spec.javax.xml.bind + jboss-jaxb-api_2.2_spec sources + 1.0.4.Final provided diff --git a/zanata-war/src/main/java/org/zanata/ApplicationConfiguration.java b/zanata-war/src/main/java/org/zanata/ApplicationConfiguration.java index ecc08dae81..d94ce43056 100644 --- a/zanata-war/src/main/java/org/zanata/ApplicationConfiguration.java +++ b/zanata-war/src/main/java/org/zanata/ApplicationConfiguration.java @@ -122,6 +122,8 @@ public class ApplicationConfiguration implements Serializable { private Optional openIdProvider; // Cache the OpenId provider + private String serverPath; + @Create public void load() { log.info("Reloading configuration"); @@ -236,18 +238,21 @@ public String getRegisterPath() { public String getServerPath() { String configuredValue = databaseBackedConfig.getServerHost(); + if (configuredValue != null) { + serverPath = configuredValue; + } // Try to determine a server path if one is not configured - if (configuredValue == null) { + if (serverPath == null) { HttpServletRequest request = ServletContexts.instance().getRequest(); if (request != null) { - configuredValue = + serverPath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath(); } } - return configuredValue; + return serverPath; } public String getDocumentFileStorageLocation() { diff --git a/zanata-war/src/main/java/org/zanata/ZanataInit.java b/zanata-war/src/main/java/org/zanata/ZanataInit.java index 6e9a532036..c844ab91bb 100644 --- a/zanata-war/src/main/java/org/zanata/ZanataInit.java +++ b/zanata-war/src/main/java/org/zanata/ZanataInit.java @@ -23,6 +23,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.lang.management.ManagementFactory; import java.lang.reflect.Proxy; import java.nio.file.FileSystem; import java.nio.file.FileSystems; @@ -35,6 +36,13 @@ import java.util.logging.Level; import java.util.logging.Logger; +import javax.management.AttributeNotFoundException; +import javax.management.InstanceNotFoundException; +import javax.management.MBeanException; +import javax.management.MBeanServer; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import javax.management.ReflectionException; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.LinkRef; @@ -43,9 +51,12 @@ import javax.naming.NamingException; import javax.servlet.ServletContext; +import com.google.common.base.Optional; +import lombok.Value; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; @@ -70,6 +81,11 @@ @Scope(ScopeType.STATELESS) @Slf4j public class ZanataInit { + private static final DefaultArtifactVersion MIN_EAP_VERSION = + new DefaultArtifactVersion("6.3.3"); + private static final DefaultArtifactVersion MIN_WILDFLY_VERSION = + new DefaultArtifactVersion("8.1.0"); + static { // Prevent JBoss/WildFly from warning about gwt-servlet's @@ -90,13 +106,14 @@ public class ZanataInit { @Observer("org.jboss.seam.postInitialization") public void initZanata() throws Exception { + AppServerVersion appServerVersion = checkJBossVersion(); ServletContext servletContext = ServletLifecycle.getCurrentServletContext(); String appServerHome = servletContext.getRealPath("/"); File manifestFile = new File(appServerHome, "META-INF/MANIFEST.MF"); - VersionInfo ver; + VersionInfo zanataVersion; Attributes atts = null; if (manifestFile.canRead()) { Manifest mf = new Manifest(); @@ -108,13 +125,13 @@ public void initZanata() throws Exception { } atts = mf.getMainAttributes(); } - ver = VersionUtility.getVersionInfo(atts, ZanataInit.class); + zanataVersion = VersionUtility.getVersionInfo(atts, ZanataInit.class); - this.applicationConfiguration.setVersion(ver.getVersionNo()); - this.applicationConfiguration.setBuildTimestamp(ver.getBuildTimeStamp()); - this.applicationConfiguration.setScmDescribe(ver.getScmDescribe()); + this.applicationConfiguration.setVersion(zanataVersion.getVersionNo()); + this.applicationConfiguration.setBuildTimestamp(zanataVersion.getBuildTimeStamp()); + this.applicationConfiguration.setScmDescribe(zanataVersion.getScmDescribe()); - logBanner(ver); + logBanner(zanataVersion, appServerVersion); if (this.applicationConfiguration.isDebug()) { log.info("debug: enabled"); @@ -158,6 +175,70 @@ public void initZanata() throws Exception { log.info("Started Zanata..."); } + private Optional getEAPVersion() + throws AttributeNotFoundException, MBeanException, + ReflectionException, InstanceNotFoundException, + MalformedObjectNameException { + try { + MBeanServer server = ManagementFactory.getPlatformMBeanServer(); + ObjectName name = new ObjectName("jboss.as:management-root=server"); + String productName = (String) (server.getAttribute(name, "productName")); + if (productName == null || !productName.equals("EAP")) { + return Optional.absent(); + } + String version = (String) (server.getAttribute(name, "productVersion")); + log.info("EAP productVersion: {}", version); + return Optional.of(new DefaultArtifactVersion(version)); + } catch (Exception e) { + log.debug(e.toString(), e); + return Optional.absent(); + } + } + + private Optional getASReleaseVersion() + throws AttributeNotFoundException, MBeanException, + ReflectionException, InstanceNotFoundException, + MalformedObjectNameException { + try { + MBeanServer server = ManagementFactory.getPlatformMBeanServer(); + ObjectName name = new ObjectName("jboss.as:management-root=server"); + String version = (String) (server.getAttribute(name, "releaseVersion")); + if (version == null) { + return Optional.absent(); + } + log.info("JBoss AS releaseVersion: {}", version); + return Optional.of(new DefaultArtifactVersion(version)); + } catch (Exception e) { + log.debug(e.toString(), e); + return Optional.absent(); + } + } + + private AppServerVersion checkJBossVersion() + throws MalformedObjectNameException, AttributeNotFoundException, + MBeanException, ReflectionException, InstanceNotFoundException { + Optional eapVersion = getEAPVersion(); + Optional asReleaseVersion = + getASReleaseVersion(); + if (eapVersion.isPresent()) { + if (eapVersion.get().compareTo(MIN_EAP_VERSION) < 0) { + log.warn("EAP version is {}. Please upgrade to {} or later.", + eapVersion.get(), MIN_EAP_VERSION); + } + } else { + if (asReleaseVersion.isPresent()) { + if (asReleaseVersion.get().compareTo(MIN_WILDFLY_VERSION) < 0) { + log.warn("WildFly version is {}. Please upgrade to {} or later.", + asReleaseVersion.get(), MIN_WILDFLY_VERSION); + } + } else { + log.warn("Unknown app server. This application requires EAP >= {} or WildFly >= {}", + MIN_EAP_VERSION, MIN_WILDFLY_VERSION); + } + } + return new AppServerVersion(eapVersion, asReleaseVersion); + } + private void checkLuceneLocks(File indexDir) throws IOException, ZanataInitializationException { if (!indexDir.exists()) { @@ -334,16 +415,26 @@ private static void list(Context ctx, String indent, StringBuffer buffer, } } - private void logBanner(VersionInfo ver) { + private void logBanner(VersionInfo ver, AppServerVersion appServerVersion) { log.info("============================================"); log.info(" _____ _ "); log.info(" /__ / ____ _____ ____ _/ /_____ _ "); log.info(" / / / __ `/ __ \\/ __ `/ __/ __ `/ "); log.info(" / /__/ /_/ / / / / /_/ / /_/ /_/ / "); log.info(" /____/\\__,_/_/ /_/\\__,_/\\__/\\__,_/ "); - log.info(" Version: " + ver.getVersionNo()); + log.info(" Application version: " + ver.getVersionNo()); + if (appServerVersion.eapVersion.isPresent()) { + log.info(" EAP version: " + appServerVersion.eapVersion.get()); + } + log.info(" AS version: " + appServerVersion.asVersion.orNull()); log.info(" SCM: " + ver.getScmDescribe()); log.info(" Red Hat Inc 2008-2014"); log.info("============================================"); } + + @Value private static class AppServerVersion { + Optional eapVersion; + Optional asVersion; + } + } diff --git a/zanata-war/src/main/java/org/zanata/action/AdminStatsAction.java b/zanata-war/src/main/java/org/zanata/action/AdminStatsAction.java index 29d994c445..9622137534 100644 --- a/zanata-war/src/main/java/org/zanata/action/AdminStatsAction.java +++ b/zanata-war/src/main/java/org/zanata/action/AdminStatsAction.java @@ -22,6 +22,8 @@ import java.io.Serializable; +import lombok.Getter; +import lombok.Setter; import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; @@ -47,22 +49,26 @@ public class AdminStatsAction implements Serializable { private static final long serialVersionUID = 1L; @In - ProjectDAO projectDAO; + private ProjectDAO projectDAO; @In - ProjectIterationDAO projectIterationDAO; + private ProjectIterationDAO projectIterationDAO; @In - PersonDAO personDAO; + private PersonDAO personDAO; @In - TextFlowDAO textFlowDAO; + private TextFlowDAO textFlowDAO; @In - TextFlowTargetDAO textFlowTargetDAO; + private TextFlowTargetDAO textFlowTargetDAO; @In - DocumentDAO documentDAO; + private DocumentDAO documentDAO; + + @Getter + @Setter + private boolean documentReady; public int getTotalProjectCount() { return projectDAO.getTotalProjectCount(); diff --git a/zanata-war/src/main/java/org/zanata/action/AjaxCounterBean.java b/zanata-war/src/main/java/org/zanata/action/AjaxCounterBean.java new file mode 100644 index 0000000000..2ca45aa065 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/action/AjaxCounterBean.java @@ -0,0 +1,95 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.zanata.action; + +import lombok.extern.slf4j.Slf4j; +import org.jboss.seam.annotations.AutoCreate; +import org.jboss.seam.annotations.In; +import org.jboss.seam.annotations.Name; + +import javax.servlet.http.HttpServletRequest; + +/** + * @author Sean Flanigan sflaniga@redhat.com + */ +@AutoCreate +@Name("ajaxCounter") +@Slf4j +public class AjaxCounterBean { + // http://stackoverflow.com/questions/4410218/trying-to-keep-track-of-number-of-outstanding-ajax-requests-in-firefox + private static final String AJAX_COUNTER_SCRIPT = "\n"; + + @In + private HttpServletRequest httpRequest; + + public String getAjaxCounterScript() { + String propName = "zanata.countAjax"; + if (Boolean.getBoolean(propName)) { + return AJAX_COUNTER_SCRIPT; + } + return ""; + } + + public String getJavascriptFinishedScript() { + String propName = "zanata.countAjax"; + String scriptUrl = httpRequest.getContextPath() + + "/javax.faces.resource/test/finished.js.seam?ln=script"; + if (Boolean.getBoolean(propName)) { + return ""; + } + return ""; + } +} diff --git a/zanata-war/src/main/java/org/zanata/action/AuthenticatedAccountHome.java b/zanata-war/src/main/java/org/zanata/action/AuthenticatedAccountHome.java index 3dcb6c27f6..c14bbe348a 100644 --- a/zanata-war/src/main/java/org/zanata/action/AuthenticatedAccountHome.java +++ b/zanata-war/src/main/java/org/zanata/action/AuthenticatedAccountHome.java @@ -49,7 +49,7 @@ public class AuthenticatedAccountHome extends EntityHome { @Override public Object getId() { - if( authenticatedAccount == null ) { + if (authenticatedAccount == null) { return null; } return authenticatedAccount.getId(); diff --git a/zanata-war/src/main/java/org/zanata/action/DashboardAction.java b/zanata-war/src/main/java/org/zanata/action/DashboardAction.java index 91a69ca527..959e3349c1 100644 --- a/zanata-war/src/main/java/org/zanata/action/DashboardAction.java +++ b/zanata-war/src/main/java/org/zanata/action/DashboardAction.java @@ -195,14 +195,13 @@ public boolean isUserReviewer() { public String getLastTranslatorMessage(HProject project) { HPerson lastTrans = projectDAO.getLastTranslator(project); - if( lastTrans != null ) { + if (lastTrans != null) { String username = lastTrans.getName(); - if(username == null || username.trim().isEmpty()) { - if( lastTrans.getAccount() != null ) { + if (username == null || username.trim().isEmpty()) { + if (lastTrans.getAccount() != null) { username = lastTrans.getAccount().getUsername(); } - } - else { + } else { username = lastTrans.getName(); } return msgs diff --git a/zanata-war/src/main/java/org/zanata/action/InactiveAccountAction.java b/zanata-war/src/main/java/org/zanata/action/InactiveAccountAction.java index 7b55becfef..a84bb91b9f 100644 --- a/zanata-war/src/main/java/org/zanata/action/InactiveAccountAction.java +++ b/zanata-war/src/main/java/org/zanata/action/InactiveAccountAction.java @@ -3,6 +3,8 @@ import java.io.Serializable; import java.util.Date; +import lombok.Getter; +import lombok.Setter; import org.apache.commons.lang.StringUtils; import org.hibernate.validator.constraints.Email; import org.jboss.seam.ScopeType; @@ -19,6 +21,7 @@ import org.zanata.model.HAccount; import org.zanata.model.HAccountActivationKey; import org.zanata.model.HPerson; +import org.zanata.security.AuthenticationManager; import org.zanata.security.AuthenticationType; import org.zanata.security.ZanataCredentials; import org.zanata.security.ZanataOpenId; @@ -48,49 +51,61 @@ public class InactiveAccountAction implements Serializable { @In private AccountActivationKeyDAO accountActivationKeyDAO; + @Getter + @Setter + @Email + @NotDuplicateEmail(message = "This email address is already taken.") private String email; private HAccount account; private static final long serialVersionUID = 1L; - public void init() { - if (credentials.getAuthType() == AuthenticationType.OPENID) { - // NB: Maybe we can get the authenticated openid from somewhere else - account = + private HAccount getAccount() { + if(account == null) { + if (credentials.getAuthType() == AuthenticationType.OPENID) { + // NB: Maybe we can get the authenticated openid from somewhere else + account = credentialsDAO.findByUser( - zanataOpenId.getAuthResult().getAuthenticatedId()) - .getAccount(); - } else { - account = accountDAO.getByUsername(credentials.getUsername()); + zanataOpenId.getAuthResult().getAuthenticatedId()) + .getAccount(); + } else { + account = accountDAO.getByUsername(credentials.getUsername()); + } } + return account; } public void sendActivationEmail() { - HAccountActivationKey key = account.getAccountActivationKey(); - key.setCreationDate(new Date()); + HAccount account = getAccount(); + if (account != null) { + HAccountActivationKey key = accountActivationKeyDAO + .findByAccountIdAndKeyHash(account.getId(), + account.getAccountActivationKey().getKeyHash()); + key.setCreationDate(new Date()); - accountActivationKeyDAO.makePersistent(key); - accountActivationKeyDAO.flush(); + accountActivationKeyDAO.makePersistent(key); + accountActivationKeyDAO.flush(); - String message = + String message = emailServiceImpl.sendActivationEmail( - account.getPerson().getName(), - account.getPerson().getEmail(), - account.getAccountActivationKey().getKeyHash()); - FacesMessages.instance().add(message); + account.getPerson().getName(), + account.getPerson().getEmail(), + account.getAccountActivationKey().getKeyHash()); + FacesMessages.instance().add(message); + } } @Transactional public String changeEmail() { if (validateEmail(email)) { HPerson person = - personDAO.findById(account.getPerson().getId(), true); + personDAO.findById(getAccount().getPerson().getId(), true); person.setEmail(email); personDAO.makePersistent(person); personDAO.flush(); - account.getPerson().setEmail(email); + getAccount().getPerson().setEmail(email); FacesMessages.instance().add("Email updated."); sendActivationEmail(); @@ -102,27 +117,17 @@ public String changeEmail() { private boolean validateEmail(String email) { if (StringUtils.isEmpty(email)) { FacesMessages.instance().addToControl("email", - "#{msgs['javax.faces.component.UIInput.REQUIRED']}"); + "#{msgs['javax.faces.component.UIInput.REQUIRED']}"); return false; } HPerson person = personDAO.findByEmail(email); - if (person != null && !person.getAccount().equals(account)) { + if (person != null && !person.getAccount().equals(getAccount())) { FacesMessages.instance().addToControl("email", - "This email address is already taken"); + "This email address is already taken"); return false; } return true; } - - @Email - @NotDuplicateEmail(message = "This email address is already taken.") - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } } diff --git a/zanata-war/src/main/java/org/zanata/action/LanguageTeamAction.java b/zanata-war/src/main/java/org/zanata/action/LanguageAction.java similarity index 51% rename from zanata-war/src/main/java/org/zanata/action/LanguageTeamAction.java rename to zanata-war/src/main/java/org/zanata/action/LanguageAction.java index ff8c87f2be..3caf438754 100644 --- a/zanata-war/src/main/java/org/zanata/action/LanguageTeamAction.java +++ b/zanata-war/src/main/java/org/zanata/action/LanguageAction.java @@ -23,11 +23,9 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.List; +import javax.faces.event.ValueChangeEvent; -import lombok.Getter; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; - +import org.apache.commons.lang.StringUtils; import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; @@ -42,17 +40,29 @@ import org.zanata.dao.LocaleDAO; import org.zanata.dao.LocaleMemberDAO; import org.zanata.dao.PersonDAO; +import org.zanata.events.LanguageTeamPermissionChangedEvent; +import org.zanata.i18n.Messages; import org.zanata.model.HAccount; import org.zanata.model.HLocale; import org.zanata.model.HLocaleMember; import org.zanata.model.HPerson; +import org.zanata.rest.service.ResourceUtils; import org.zanata.service.LanguageTeamService; import org.zanata.service.LocaleService; +import org.zanata.ui.AbstractListFilter; +import org.zanata.ui.InMemoryListFilter; +import org.zanata.util.ServiceLocator; + +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; -@Name("languageTeamAction") +import static org.zanata.events.LanguageTeamPermissionChangedEvent.LANGUAGE_TEAM_PERMISSION_CHANGED; + +@Name("languageAction") @Scope(ScopeType.PAGE) @Slf4j -public class LanguageTeamAction implements Serializable { +public class LanguageAction implements Serializable { private static final long serialVersionUID = 1L; @In @@ -61,9 +71,6 @@ public class LanguageTeamAction implements Serializable { @In private LocaleDAO localeDAO; - @In - private LocaleMemberDAO localeMemberDAO; - @In private PersonDAO personDAO; @@ -73,6 +80,15 @@ public class LanguageTeamAction implements Serializable { @In(required = false, value = JpaIdentityStore.AUTHENTICATED_USER) private HAccount authenticatedAccount; + @In + private Messages msgs; + + @In + private LocaleMemberDAO localeMemberDAO; + + @In + private ResourceUtils resourceUtils; + @Getter @Setter private String language; @@ -80,22 +96,54 @@ public class LanguageTeamAction implements Serializable { @Getter @Setter private String searchTerm; + + private HLocale locale; + private List searchResults; + @Getter + private AbstractListFilter membersFilter = + new InMemoryListFilter() { + @Override + protected List fetchAll() { + ServiceLocator serviceLocator = ServiceLocator.instance(); + LocaleMemberDAO localeMemberDAO = + serviceLocator.getInstance(LocaleMemberDAO.class); + + return localeMemberDAO.findAllByLocale( + new LocaleId(language)); + } + + @Override + protected boolean include(HLocaleMember elem, + String filter) { + return StringUtils.containsIgnoreCase( + elem.getPerson().getName(), filter); + } + }; + public List getSearchResults() { if (searchResults == null) { - searchResults = new ArrayList(); + searchResults = new ArrayList<>(); } return searchResults; } + public void reset() { + membersFilter.reset(); + } + public boolean isUserInTeam() { - return authenticatedAccount != null - && this.isPersonInTeam(this.authenticatedAccount.getId()); + if(authenticatedAccount != null) { + return languageTeamServiceImpl + .getLanguageMemberships(authenticatedAccount.getUsername()).contains(getLocale()); + } + return false; + } - @Restrict("#{s:hasPermission(languageTeamAction.locale, 'manage-language-team')}") + @Restrict("#{s:hasPermission(languageAction.locale, 'manage-language-team')}") public void addSelected() { for (SelectablePerson selectablePerson : getSearchResults()) { if (selectablePerson.isSelected()) { @@ -105,6 +153,47 @@ public void addSelected() { selectablePerson.isCoordinator); } } + resetLocale(); + } + + public String getPluralsPlaceholder() { + String pluralForms = resourceUtils.getPluralForms(new LocaleId(language), false, true); + return msgs.format("jsf.language.plurals.placeholder", pluralForms); + } + + public String getExamplePluralForms() { + String pluralForms = resourceUtils.getPluralForms(new LocaleId(language), false, true); + return msgs.format("jsf.language.plurals.example", pluralForms); + } + + public boolean isValidPluralForms(String pluralForms, String componentId) { + if(StringUtils.isEmpty(pluralForms)) { + return true; + } + if(resourceUtils.isValidPluralForms(pluralForms)) { + return true; + } + + FacesMessages.instance().addToControl(componentId, + msgs.format("jsf.language.plurals.invalid", pluralForms)); + return false; + } + + public void validatePluralForms(ValueChangeEvent e) { + isValidPluralForms((String) e.getNewValue(), e.getComponent().getId()); + } + + @Restrict("#{s:hasRole('admin')}") + public void saveSettings() { + HLocale hLocale = getLocale(); + if(!isValidPluralForms(hLocale.getPluralForms(), "pluralForms")) { + return; + } + hLocale.setDisplayName(hLocale.getDisplayName().trim()); + hLocale.setNativeName(hLocale.getNativeName().trim()); + localeDAO.makePersistent(getLocale()); + FacesMessages.instance() + .add(msgs.format("jsf.language.updated", getLocale().getLocaleId())); } public HLocale getLocale() { @@ -115,9 +204,8 @@ public HLocale getLocale() { * to access the 'members' collection from inside the security * listener's postLoad method to evaluate rules. */ - HLocale locale = - localeServiceImpl.getByLocaleId(new LocaleId(language)); - if (locale != null) { + if(locale == null) { + locale = localeServiceImpl.getByLocaleId(new LocaleId(language)); locale.getMembers(); } return locale; @@ -129,8 +217,7 @@ public List getLocaleMembers() { @Transactional @Restrict("#{s:hasRole('admin')}") - public void joinTribe() { - log.debug("starting join tribe"); + public void joinLanguageTeam() { if (authenticatedAccount == null) { log.error("failed to load auth person"); return; @@ -139,106 +226,124 @@ public void joinTribe() { languageTeamServiceImpl.joinOrUpdateRoleInLanguageTeam( this.language, authenticatedAccount.getPerson().getId(), true, true, true); - Events.instance().raiseEvent("personJoinedTribe"); - log.info("{} joined tribe {}", + resetLocale(); + log.info("{} joined language team {}", authenticatedAccount.getUsername(), this.language); - // FIXME use localizable string - FacesMessages.instance().add( - "You are now a member of the {0} language team", - getLocale().retrieveNativeName()); + FacesMessages.instance().add(msgs.format("jsf.MemberOfTeam", + getLocale().retrieveNativeName())); } catch (Exception e) { FacesMessages.instance().add(Severity.ERROR, e.getMessage()); } } + /** + * Set locale=null to force refresh members list + */ + private void resetLocale() { + locale = null; + } + @Transactional - public void leaveTribe() { - log.debug("starting leave tribe"); + public void leaveLanguageTeam() { if (authenticatedAccount == null) { log.error("failed to load auth person"); return; } languageTeamServiceImpl.leaveLanguageTeam(this.language, authenticatedAccount.getPerson().getId()); - Events.instance().raiseEvent("personLeftTribe"); - log.info("{} left tribe {}", authenticatedAccount.getUsername(), + resetLocale(); + log.info("{} left language team {}", authenticatedAccount.getUsername(), this.language); - // FIXME use localizable string - FacesMessages.instance().add("You have left the {0} language team", - getLocale().retrieveNativeName()); + FacesMessages.instance().add(msgs.format("jsf.LeftTeam", + getLocale().retrieveNativeName())); } - @Restrict("#{s:hasPermission(languageTeamAction.locale, 'manage-language-team')}") + @Restrict("#{s:hasPermission(languageAction.locale, 'manage-language-team')}") public void saveTeamCoordinator(HLocaleMember member) { - this.localeDAO.makePersistent(getLocale()); - this.localeDAO.flush(); - if (member.isCoordinator()) { - FacesMessages.instance().add( - "{0} has been made a Team Coordinator", - member.getPerson().getAccount().getUsername()); - } else { - // TODO i18n - FacesMessages.instance().add( - "{0} has been removed as Team Coordinator", - member.getPerson().getAccount().getUsername()); + savePermission(member, msgs.get("jsf.Coordinator"), member.isCoordinator()); + if (Events.exists()) { + HPerson doneByPerson = authenticatedAccount.getPerson(); + LanguageTeamPermissionChangedEvent changedEvent = + new LanguageTeamPermissionChangedEvent( + member.getPerson(), getLocale().getLocaleId(), + doneByPerson) + .changedCoordinatorPermission(member); + Events.instance() + .raiseTransactionSuccessEvent( + LANGUAGE_TEAM_PERMISSION_CHANGED, + changedEvent); } } - @Restrict("#{s:hasPermission(languageTeamAction.locale, 'manage-language-team')}") + @Restrict("#{s:hasPermission(languageAction.locale, 'manage-language-team')}") public void saveTeamReviewer(HLocaleMember member) { - this.localeDAO.makePersistent(getLocale()); - this.localeDAO.flush(); - if (member.isReviewer()) { - FacesMessages.instance().add("{0} has been made a Team Reviewer", - member.getPerson().getAccount().getUsername()); - } else { - // TODO i18n - FacesMessages.instance().add( - "{0} has been removed from as Team Reviewer", - member.getPerson().getAccount().getUsername()); + savePermission(member, msgs.get("jsf.Reviewer"), member.isReviewer()); + if (Events.exists()) { + HPerson doneByPerson = authenticatedAccount.getPerson(); + LanguageTeamPermissionChangedEvent changedEvent = + new LanguageTeamPermissionChangedEvent( + member.getPerson(), getLocale().getLocaleId(), + doneByPerson) + .changedReviewerPermission(member); + Events.instance() + .raiseTransactionSuccessEvent( + LANGUAGE_TEAM_PERMISSION_CHANGED, + changedEvent); } } - @Restrict("#{s:hasPermission(languageTeamAction.locale, 'manage-language-team')}") + @Restrict("#{s:hasPermission(languageAction.locale, 'manage-language-team')}") public void saveTeamTranslator(HLocaleMember member) { - this.localeDAO.makePersistent(getLocale()); - this.localeDAO.flush(); - if (member.isReviewer()) { - FacesMessages.instance().add("{0} has been made a Team Translator", - member.getPerson().getAccount().getUsername()); + savePermission(member, msgs.get("jsf.Translator"), member.isTranslator()); + if (Events.exists()) { + HPerson doneByPerson = authenticatedAccount.getPerson(); + LanguageTeamPermissionChangedEvent changedEvent = + new LanguageTeamPermissionChangedEvent( + member.getPerson(), getLocale().getLocaleId(), + doneByPerson) + .changedTranslatorPermission(member); + Events.instance() + .raiseTransactionSuccessEvent( + LANGUAGE_TEAM_PERMISSION_CHANGED, + changedEvent); + } + } + + private void savePermission(HLocaleMember member, String permissionDesc, + boolean isPermissionGranted) { + languageTeamServiceImpl.joinOrUpdateRoleInLanguageTeam( + this.language, member.getPerson().getId(), + member.isTranslator(), member.isReviewer(), member.isCoordinator()); + resetLocale(); + HPerson person = member.getPerson(); + if (isPermissionGranted) { + FacesMessages.instance().add( + msgs.format("jsf.AddedAPermission", + person.getAccount().getUsername(), permissionDesc)); } else { - // TODO i18n FacesMessages.instance().add( - "{0} has been removed from as Team Translator", - member.getPerson().getAccount().getUsername()); + msgs.format("jsf.RemovedAPermission", + person.getAccount().getUsername(), permissionDesc)); } } private void addTeamMember(final Long personId, boolean isTranslator, - boolean isReviewer, boolean isCoordinator) { + boolean isReviewer, boolean isCoordinator) { this.languageTeamServiceImpl.joinOrUpdateRoleInLanguageTeam( this.language, personId, isTranslator, isReviewer, isCoordinator); } - @Restrict("#{s:hasPermission(languageTeamAction.locale, 'manage-language-team')}") + @Restrict("#{s:hasPermission(languageAction.locale, 'manage-language-team')}") public void removeMembership(HLocaleMember member) { this.languageTeamServiceImpl.leaveLanguageTeam(this.language, member .getPerson().getId()); - } - - public boolean isPersonInTeam(final Long personId) { - for (HLocaleMember lm : getLocale().getMembers()) { - if (lm.getPerson().getId().equals(personId)) { - return true; - } - } - return false; + resetLocale(); } private HLocaleMember getLocaleMember(final Long personId) { - for (HLocaleMember lm : getLocale().getMembers()) { + for (HLocaleMember lm : getLocaleMembers()) { if (lm.getPerson().getId().equals(personId)) { return lm; } @@ -252,7 +357,7 @@ public void searchForTeamMembers() { this.personDAO.findAllContainingName(this.searchTerm); for (HPerson person : results) { HLocaleMember localeMember = getLocaleMember(person.getId()); - boolean isMember = localeMember == null ? false : true; + boolean isMember = localeMember != null; boolean isReviewer = false; boolean isTranslator = false; boolean isCoordinator = false; diff --git a/zanata-war/src/main/java/org/zanata/action/LanguageManagerAction.java b/zanata-war/src/main/java/org/zanata/action/LanguageManagerAction.java index 9cd3d617a1..a8c6c83eb5 100644 --- a/zanata-war/src/main/java/org/zanata/action/LanguageManagerAction.java +++ b/zanata-war/src/main/java/org/zanata/action/LanguageManagerAction.java @@ -20,10 +20,19 @@ */ package org.zanata.action; -import com.google.common.base.Function; -import com.google.common.base.Predicate; -import com.google.common.collect.Collections2; -import com.ibm.icu.util.ULocale; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import javax.annotation.Nullable; +import javax.faces.model.SelectItem; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; import org.apache.commons.lang.StringUtils; import org.jboss.seam.ScopeType; @@ -37,19 +46,20 @@ import org.zanata.model.HLocale; import org.zanata.rest.service.ResourceUtils; import org.zanata.service.LocaleService; +import org.zanata.ui.AbstractAutocomplete; +import org.zanata.ui.FilterUtil; +import org.zanata.ui.autocomplete.LocaleAutocomplete; -import javax.annotation.Nullable; -import javax.faces.model.SelectItem; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.collect.Collections2; +import com.google.common.collect.Lists; +import com.ibm.icu.util.ULocale; @Name("languageManagerAction") @Scope(ScopeType.PAGE) -@Restrict("#{s:hasRole('admin')}") -public class LanguageManagerAction implements Serializable { +public class LanguageManagerAction extends AbstractAutocomplete + implements Serializable { private static final long serialVersionUID = 1L; private static final int LENGTH_LIMIT = 254; @@ -65,119 +75,54 @@ public class LanguageManagerAction implements Serializable { @In private Map msgs; - private String language; - + @Getter + @Setter private ULocale uLocale; - private List localeStringList; - + @Getter + @Setter private boolean enabledByDefault = true; // cache this so it is called only once private List allLocales; + @Getter private String languageNameValidationMessage; + @Getter private String languageNameWarningMessage; @Create public void onCreate() { - fectchLocaleFromJava(); - } - - public String getLanguage() { - return language; - } - - public ULocale getuLocale() { - return uLocale; - } - - public void setuLocale(ULocale uLocale) { - this.uLocale = uLocale; - } - - public void setLanguage(String language) { - this.language = language; - } - - public boolean isEnabledByDefault() { - return enabledByDefault; - } - - public void setEnabledByDefault(boolean enabledByDefault) { - this.enabledByDefault = enabledByDefault; - } - - public String getLanguageNameValidationMessage() { - return languageNameValidationMessage; - } - - public String getLanguageNameWarningMessage() { - return languageNameWarningMessage; + allLocales = localeServiceImpl.getAllJavaLanguages(); } - public void updateLanguage() { - if (this.language.trim().length() > 0) { - this.uLocale = new ULocale(this.language); - this.isLanguageNameValid(); + public void updateLanguage(String language) { + if (!StringUtils.isEmpty(language)) { + uLocale = new ULocale(language); + isLanguageNameValid(); } else { - this.uLocale = null; + uLocale = null; } } + @Restrict("#{s:hasRole('admin')}") public String save() { if (!isLanguageNameValid()) { return null; // not success } - LocaleId locale = new LocaleId(language); + LocaleId locale = new LocaleId(getQuery()); localeServiceImpl.save(locale, enabledByDefault); return "success"; } - public void fectchLocaleFromJava() { - List locale = localeServiceImpl.getAllJavaLanguages(); - List localeList = new ArrayList(); - for (LocaleId var : locale) { - SelectItem op = new SelectItem(var.getId(), var.getId()); - localeList.add(op); - } - localeStringList = localeList; - } - - public List getLocaleStringList() { - return localeStringList; - } - - public List suggestLocales(final String query) { - if (allLocales == null) { - allLocales = localeServiceImpl.getAllJavaLanguages(); - } - - Collection filtered = - Collections2.filter(allLocales, new Predicate() { - @Override - public boolean apply(LocaleId input) { - return input.getId().startsWith(query); - } - }); - - return new ArrayList(Collections2.transform(filtered, - new Function() { - @Override - public HLocale apply(@Nullable LocaleId from) { - return new HLocale(from); - } - })); - } - public boolean isLanguageNameValid() { - this.languageNameValidationMessage = null; // reset - this.languageNameWarningMessage = null; // reset + languageNameValidationMessage = null; // reset + languageNameWarningMessage = null; // reset - if (StringUtils.isEmpty(language) || language.length() > LENGTH_LIMIT) { - this.uLocale = null; - this.languageNameValidationMessage = + if (StringUtils.isEmpty(getQuery()) || getQuery().length() > LENGTH_LIMIT) { + uLocale = null; + languageNameValidationMessage = msgs.get("jsf.language.validation.Invalid"); return false; } @@ -189,35 +134,82 @@ public boolean isLanguageNameValid() { // Check that locale Id is syntactically valid LocaleId localeId; try { - localeId = new LocaleId(language); + localeId = new LocaleId(getQuery()); } catch (IllegalArgumentException iaex) { - this.languageNameValidationMessage = + languageNameValidationMessage = msgs.get("jsf.language.validation.Invalid"); return false; } // check for already registered languages if (localeServiceImpl.localeExists(localeId)) { - this.languageNameValidationMessage = + languageNameValidationMessage = msgs.get("jsf.language.validation.Existing"); return false; } // Check for plural forms - if (resourceUtils.getPluralForms(localeId, false) == null) { - this.languageNameWarningMessage = + if (resourceUtils.getPluralForms(localeId, true, false) == null) { + languageNameWarningMessage = msgs.get("jsf.language.validation.UnknownPluralForm"); } // Check for similar already registered languages (warning) List similarLangs = localeDAO.findBySimilarLocaleId(localeId); if (similarLangs.size() > 0) { - this.languageNameWarningMessage = + languageNameWarningMessage = msgs.get("jsf.language.validation.SimilarLocaleFound") + similarLangs.get(0).getLocaleId().getId(); } - return true; } + @Override + public List suggest() { + if (StringUtils.isEmpty(getQuery())) { + return Collections.EMPTY_LIST; + } + + Collection locales = Collections2.transform(allLocales, + new Function() { + @Override + public HLocale apply(@Nullable LocaleId from) { + return new HLocale(from); + } + }); + + Collection filtered = + Collections2.filter(locales, new Predicate() { + @Override + public boolean apply(HLocale input) { + return StringUtils.containsIgnoreCase(input + .getLocaleId().getId(), getQuery()); + } + }); + + if(filtered.isEmpty()) { + updateLanguage(getQuery()); + } + return Lists.newArrayList(filtered); + } + + @Override + public void onSelectItemAction() { + if (StringUtils.isEmpty(getSelectedItem())) { + return; + } + updateLanguage(getSelectedItem()); + } + + public void replaceUnderscore(String language) { + setQuery(language); + updateLanguage(language); + } + + public void resetValue() { + setQuery(""); + uLocale = null; + languageNameValidationMessage = null; // reset + languageNameWarningMessage = null; // reset + } } diff --git a/zanata-war/src/main/java/org/zanata/action/LanguageSearchAction.java b/zanata-war/src/main/java/org/zanata/action/LanguageSearchAction.java deleted file mode 100644 index fc1ef26a01..0000000000 --- a/zanata-war/src/main/java/org/zanata/action/LanguageSearchAction.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2010, Red Hat, Inc. and individual contributors as indicated by the - * @author tags. See the copyright.txt file in the distribution for a full - * listing of individual contributors. - * - * This is free software; you can redistribute it and/or modify it under the - * terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This software is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this software; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF - * site: http://www.fsf.org. - */ -package org.zanata.action; - -import java.io.Serializable; -import java.util.List; - -import org.jboss.seam.ScopeType; -import org.jboss.seam.annotations.In; -import org.jboss.seam.annotations.Name; -import org.jboss.seam.annotations.Scope; -import org.jboss.seam.annotations.datamodel.DataModel; -import org.jboss.seam.annotations.datamodel.DataModelSelection; -import org.jboss.seam.annotations.security.Restrict; -import org.jboss.seam.core.Events; -import org.zanata.dao.LocaleDAO; -import org.zanata.model.HLocale; -import org.zanata.service.LocaleService; - -@Name("languageSearchAction") -@Scope(ScopeType.PAGE) -@Restrict("#{s:hasRole('admin')}") -public class LanguageSearchAction implements Serializable { - private static final long serialVersionUID = 1L; - @In - private LocaleService localeServiceImpl; - @In - private LocaleDAO localeDAO; - @DataModel - List allLanguages; - @DataModelSelection - HLocale selectedLanguage; - - public void loadSupportedLanguage() { - allLanguages = localeServiceImpl.getAllLocales(); - } - - public HLocale getSelectedLanguage() { - return selectedLanguage; - } - - public String manageMembers(String locale) { - return ""; - } - - public void selectedLocaleChanged() { - selectedLanguage = localeDAO.makePersistent(selectedLanguage); - localeDAO.flush(); - } - - public void selectedLocaleActivatedOrDeactivated() { - selectedLanguage = localeDAO.makePersistent(selectedLanguage); - localeDAO.flush(); - - if (selectedLanguage.isActive()) { - Events.instance().raiseEvent("enableLanguage"); - } else { - Events.instance().raiseEvent("disableLanguage"); - } - } - -} diff --git a/zanata-war/src/main/java/org/zanata/action/LanguagesAction.java b/zanata-war/src/main/java/org/zanata/action/LanguagesAction.java new file mode 100644 index 0000000000..b8ed5442dc --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/action/LanguagesAction.java @@ -0,0 +1,136 @@ +/* + * Copyright 2010, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this software; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF + * site: http://www.fsf.org. + */ +package org.zanata.action; + +import java.io.Serializable; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import org.apache.commons.lang.StringUtils; +import org.jboss.seam.ScopeType; +import org.jboss.seam.annotations.In; +import org.jboss.seam.annotations.Name; +import org.jboss.seam.annotations.Scope; +import org.zanata.model.HLocale; +import org.zanata.security.ZanataIdentity; +import org.zanata.service.LanguageTeamService; +import org.zanata.service.LocaleService; +import org.zanata.ui.InMemoryListFilter; + +import com.google.common.collect.Lists; +import lombok.Getter; + +@Name("languagesAction") +@Scope(ScopeType.PAGE) +public class LanguagesAction extends InMemoryListFilter implements + Serializable { + private static final long serialVersionUID = 1L; + @In + private LocaleService localeServiceImpl; + + @In + private LanguageTeamService languageTeamServiceImpl; + + @In + private ZanataIdentity identity; + + private List allLanguages; + + @Getter + private SortingType LanguageSortingList = new SortingType( + Lists.newArrayList(SortingType.SortOption.ALPHABETICAL, + SortingType.SortOption.LOCALE_ID, + SortingType.SortOption.MEMBERS)); + + private final LanguageComparator languageComparator = + new LanguageComparator(getLanguageSortingList()); + + public boolean isUserTeamMember(HLocale locale) { + if(identity != null) { + return languageTeamServiceImpl + .getLanguageMemberships(identity.getAccountUsername()).contains(locale); + } + return false; + + } + + /** + * Sort language list + */ + public void sortLanguageList() { + Collections.sort(allLanguages, languageComparator); + this.reset(); + } + + @Override + protected List fetchAll() { + if (allLanguages == null) { + if(identity != null + && identity.hasRole("admin")) { + allLanguages = localeServiceImpl.getAllLocales(); + } else { + allLanguages = localeServiceImpl.getSupportedLocales(); + } + } + return allLanguages; + } + + @Override + protected boolean include(HLocale elem, String filter) { + return StringUtils.containsIgnoreCase(elem.retrieveDisplayName(), + filter) + || StringUtils.containsIgnoreCase(elem.getLocaleId().getId(), + filter); + } + + // sort by name or locale id + private class LanguageComparator implements Comparator { + private SortingType sortingType; + + public LanguageComparator(SortingType sortingType) { + this.sortingType = sortingType; + } + + @Override + public int compare(HLocale o1, HLocale o2) { + SortingType.SortOption selectedSortOption = + sortingType.getSelectedSortOption(); + + if (!selectedSortOption.isAscending()) { + HLocale temp = o1; + o1 = o2; + o2 = temp; + } + + if (selectedSortOption.equals(SortingType.SortOption.ALPHABETICAL)) { + return o1.retrieveDisplayName().compareTo( + o2.retrieveDisplayName()); + } else if (selectedSortOption + .equals(SortingType.SortOption.LOCALE_ID)) { + return o1.getLocaleId().getId().compareTo( + o2.getLocaleId().getId()); + } else { + return o1.getMembers().size() - o2.getMembers().size(); + } + } + } +} diff --git a/zanata-war/src/main/java/org/zanata/action/PasswordResetAction.java b/zanata-war/src/main/java/org/zanata/action/PasswordResetAction.java index cb51ef6a31..ae2f1a57bd 100644 --- a/zanata-war/src/main/java/org/zanata/action/PasswordResetAction.java +++ b/zanata-war/src/main/java/org/zanata/action/PasswordResetAction.java @@ -5,6 +5,8 @@ import javax.persistence.EntityManager; import javax.validation.constraints.Size; +import lombok.Getter; +import lombok.Setter; import org.hibernate.validator.constraints.NotEmpty; import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.Begin; @@ -18,6 +20,7 @@ import org.jboss.seam.security.RunAsOperation; import org.jboss.seam.security.management.IdentityManager; import org.zanata.exception.KeyNotFoundException; +import org.zanata.i18n.Messages; import org.zanata.model.HAccountResetPasswordKey; @Name("passwordReset") @@ -32,35 +35,30 @@ public class PasswordResetAction implements Serializable { @In private IdentityManager identityManager; + @In + private Messages msgs; + + @Getter private String activationKey; + @Getter + @Setter + @NotEmpty + @Size(min = 6, max = 20) private String password; + + @Getter private String passwordConfirm; + @Getter private HAccountResetPasswordKey key; - public void setPassword(String password) { - this.password = password; - } - - @NotEmpty - @Size(min = 6, max = 20) - // @Pattern(regex="(?=^.{6,}$)((?=.*\\d)|(?=.*\\W+))(?![.\\n])(?=.*[A-Z])(?=.*[a-z]).*$", - // message="Password is not secure enough!") - public - String getPassword() { - return password; - } public void setPasswordConfirm(String passwordConfirm) { this.passwordConfirm = passwordConfirm; validatePasswordsMatch(); } - public String getPasswordConfirm() { - return passwordConfirm; - } - public boolean validatePasswordsMatch() { if (password == null || !password.equals(passwordConfirm)) { FacesMessages.instance().addToControl("passwordConfirm", @@ -70,10 +68,6 @@ public boolean validatePasswordsMatch() { return true; } - public String getActivationKey() { - return activationKey; - } - public void setActivationKey(String activationKey) { this.activationKey = activationKey; key = @@ -81,10 +75,6 @@ public void setActivationKey(String activationKey) { getActivationKey()); } - private HAccountResetPasswordKey getKey() { - return key; - } - @Begin(join = true) public void validateActivationKey() { @@ -128,14 +118,12 @@ public void execute() { entityManager.remove(getKey()); if (passwordChanged) { - FacesMessages - .instance() - .add("Your password has been successfully changed. Please sign in with your new password."); + FacesMessages.instance() + .add(msgs.get("jsf.password.change.success")); return "/account/login.xhtml"; } else { - FacesMessages - .instance() - .add("There was a problem changing the password. Please try again."); + FacesMessages.instance() + .add(msgs.get("jsf.password.change.failed")); return null; } diff --git a/zanata-war/src/main/java/org/zanata/action/PasswordResetRequestAction.java b/zanata-war/src/main/java/org/zanata/action/PasswordResetRequestAction.java index f286a8f79a..3b70f99f2e 100644 --- a/zanata-war/src/main/java/org/zanata/action/PasswordResetRequestAction.java +++ b/zanata-war/src/main/java/org/zanata/action/PasswordResetRequestAction.java @@ -1,13 +1,10 @@ package org.zanata.action; -import java.io.Serializable; - -import javax.validation.constraints.Pattern; -import javax.validation.constraints.Size; - +import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; - +import org.hibernate.validator.constraints.Email; import org.hibernate.validator.constraints.NotEmpty; import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.End; @@ -15,12 +12,20 @@ import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; import org.jboss.seam.faces.FacesMessages; +import org.zanata.dao.AccountActivationKeyDAO; import org.zanata.dao.AccountDAO; +import org.zanata.i18n.Messages; import org.zanata.model.HAccount; +import org.zanata.model.HAccountActivationKey; import org.zanata.model.HAccountResetPasswordKey; import org.zanata.service.EmailService; import org.zanata.service.UserAccountService; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; +import java.io.Serializable; +import java.util.Date; + @Name("passwordResetRequest") @NoArgsConstructor @Scope(ScopeType.EVENT) @@ -30,67 +35,102 @@ public class PasswordResetRequestAction implements Serializable { @In private AccountDAO accountDAO; + @In private EmailService emailServiceImpl; + @In private UserAccountService userAccountServiceImpl; + @In + private Messages msgs; + + @In + private AccountActivationKeyDAO accountActivationKeyDAO; + + @Setter + @Getter + @NotEmpty + @Size(min = 3, max = 20) + @Pattern(regexp = "^[a-z\\d_]{3,20}$", + message = "{validation.username.constraints}") private String username; + + @Setter + @Getter + @NotEmpty + @Email private String email; + + @Setter + @Getter private String activationKey; private HAccount account; - public HAccount getAccount() { - return account; - } - public void setUsername(String username) { - this.username = username; - } + public String requestReset() { + if(getAccount() == null) { + return getAccountNoFoundMessage(); + } - @NotEmpty - @Size(min = 3, max = 20) - @Pattern(regexp = "^[a-z\\d_]{3,20}$") - public String getUsername() { - return username; - } + HAccountResetPasswordKey key = + userAccountServiceImpl.requestPasswordReset(getAccount()); + + if(key == null) { + return getAccountNoFoundMessage(); + } + + if(isAccountWaitingForActivation()) { + FacesMessages.instance().add(msgs.get("jsf.account.notActivated")); + return null; + } - public void setEmail(String email) { - this.email = email; + String message = emailServiceImpl.sendPasswordResetEmail( + getAccount().getPerson(), key.getKeyHash()); + FacesMessages.instance().add(message); + return "home"; } - @org.hibernate.validator.constraints.Email - @NotEmpty - public String getEmail() { - return email; + private String getAccountNoFoundMessage() { + FacesMessages.instance().add(msgs.get("jsf.account.notFound")); + return null; } @End - public String requestReset() { - account = accountDAO.getByUsernameAndEmail(username, email); - HAccountResetPasswordKey key = - userAccountServiceImpl.requestPasswordReset(account); - - if (key == null) { - FacesMessages.instance().add("No such account found"); - return null; - } else { - String message = - emailServiceImpl.sendPasswordResetEmail(account.getPerson(), - key.getKeyHash()); - FacesMessages.instance().add(message); - return "/home.xhtml"; + public String sendActivationEmail(String username, String email) { + HAccount account = accountDAO.getByUsernameAndEmail(username, email); + if(account != null) { + HAccountActivationKey key = account.getAccountActivationKey(); + if(key != null) { + key.setCreationDate(new Date()); + + accountActivationKeyDAO.makePersistent(key); + accountActivationKeyDAO.flush(); + + String message = + emailServiceImpl.sendActivationEmail( + account.getPerson().getName(), + account.getPerson().getEmail(), + account.getAccountActivationKey().getKeyHash()); + FacesMessages.instance().add(message); + } } - + return "/home.xhtml"; } - public String getActivationKey() { - return activationKey; + public boolean isAccountWaitingForActivation() { + HAccount account = getAccount(); + if (account == null) { + return false; + } + return account.getAccountActivationKey() != null; } - public void setActivationKey(String activationKey) { - this.activationKey = activationKey; + public HAccount getAccount() { + if(account == null) { + account = accountDAO.getByUsernameAndEmail(username, email); + } + return account; } - } diff --git a/zanata-war/src/main/java/org/zanata/action/ProjectIterationZipFileAction.java b/zanata-war/src/main/java/org/zanata/action/ProjectIterationZipFileAction.java index 217b83353a..93597de293 100644 --- a/zanata-war/src/main/java/org/zanata/action/ProjectIterationZipFileAction.java +++ b/zanata-war/src/main/java/org/zanata/action/ProjectIterationZipFileAction.java @@ -1,6 +1,7 @@ package org.zanata.action; import java.io.Serializable; +import java.text.DecimalFormat; import lombok.Getter; @@ -57,14 +58,27 @@ public void prepareIterationZipFile(boolean isPoProject, translationArchiveServiceImpl.startBuildingTranslationFileArchive( projectSlug, versionSlug, localeId, Identity.instance() .getCredentials().getUsername(), zipFilePrepHandle); - } - catch (Exception e) { + } catch (Exception e) { throw new RuntimeException(e); } } @End public void cancelFileDownload() { - zipFilePrepHandle.cancel(true); + if(zipFilePrepHandle != null) { + zipFilePrepHandle.cancel(true); + } + } + final DecimalFormat PERCENT_FORMAT = new DecimalFormat("###.##"); + + public String getCompletedPercentage() { + if(zipFilePrepHandle != null) { + double completedPercent = + (double) zipFilePrepHandle.getCurrentProgress() / (double) zipFilePrepHandle + .getMaxProgress() * 100; + + return PERCENT_FORMAT.format(completedPercent) + "%"; + } + return "0%"; } } diff --git a/zanata-war/src/main/java/org/zanata/action/ReindexAction.java b/zanata-war/src/main/java/org/zanata/action/ReindexAction.java index 06f8eac859..ce05c6f382 100644 --- a/zanata-war/src/main/java/org/zanata/action/ReindexAction.java +++ b/zanata-war/src/main/java/org/zanata/action/ReindexAction.java @@ -1,6 +1,7 @@ package org.zanata.action; import java.io.Serializable; +import java.text.DecimalFormat; import java.util.List; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; @@ -29,18 +30,38 @@ public class ReindexAction implements Serializable { private static final long serialVersionUID = 1L; + private final DecimalFormat PERCENT_FORMAT = new DecimalFormat("###.##"); + @In - SearchIndexManager searchIndexManager; + private SearchIndexManager searchIndexManager; public List getClasses() { return searchIndexManager.getReindexOptions(); } - public void selectAll(boolean selected) { + public boolean isAnyOptionSelected() { for (ReindexClassOptions opts : searchIndexManager.getReindexOptions()) { - opts.setPurge(selected); - opts.setReindex(selected); - opts.setOptimize(selected); + if(opts.isOptimize() || opts.isPurge() || opts.isReindex()) { + return true; + } + } + return false; + } + + public boolean isSelectAll() { + for (ReindexClassOptions opts : searchIndexManager.getReindexOptions()) { + if(!opts.isOptimize() || !opts.isPurge() || !opts.isReindex()) { + return false; + } + } + return true; + } + + public void setSelectAll(boolean selectAll) { + for (ReindexClassOptions opts : searchIndexManager.getReindexOptions()) { + opts.setPurge(selectAll); + opts.setReindex(selectAll); + opts.setOptimize(selectAll); } } @@ -136,6 +157,17 @@ public int getReindexProgress() { } } + public String getProgressPercentage() { + if (searchIndexManager.getProcessHandle() == null) { + return "0"; + } else { + double completedPercent = + (double) getReindexProgress() / (double) getReindexCount() + * 100; + return PERCENT_FORMAT.format(completedPercent); + } + } + public void reindexDatabase() { if (searchIndexManager.getProcessHandle() == null || searchIndexManager.getProcessHandle().isDone()) { diff --git a/zanata-war/src/main/java/org/zanata/action/ReindexClassOptions.java b/zanata-war/src/main/java/org/zanata/action/ReindexClassOptions.java index c2e002dd1c..18311dc4ff 100644 --- a/zanata-war/src/main/java/org/zanata/action/ReindexClassOptions.java +++ b/zanata-war/src/main/java/org/zanata/action/ReindexClassOptions.java @@ -2,6 +2,9 @@ import java.io.Serializable; +import lombok.Getter; +import lombok.Setter; + /** * Stores options for the lucene indexing * @@ -10,8 +13,17 @@ public class ReindexClassOptions implements Serializable { private static final long serialVersionUID = 1L; private Class clazz; + + @Getter + @Setter private boolean purge = false; + + @Getter + @Setter private boolean reindex = false; + + @Getter + @Setter private boolean optimize = false; public ReindexClassOptions(Class indexableClass) { @@ -22,30 +34,6 @@ public String getClassName() { return clazz.getSimpleName(); } - public boolean isPurge() { - return purge; - } - - public void setPurge(boolean shouldPurge) { - this.purge = shouldPurge; - } - - public boolean isReindex() { - return reindex; - } - - public void setReindex(boolean shouldReindex) { - this.reindex = shouldReindex; - } - - public boolean isOptimize() { - return optimize; - } - - public void setOptimize(boolean optimize) { - this.optimize = optimize; - } - public void setSelectAll(boolean selectAll) { setPurge(selectAll); setReindex(selectAll); diff --git a/zanata-war/src/main/java/org/zanata/action/SendEmailAction.java b/zanata-war/src/main/java/org/zanata/action/SendEmailAction.java index dd419feae1..a84ffd9df9 100644 --- a/zanata-war/src/main/java/org/zanata/action/SendEmailAction.java +++ b/zanata-war/src/main/java/org/zanata/action/SendEmailAction.java @@ -37,7 +37,9 @@ import org.jboss.seam.annotations.Scope; import org.jboss.seam.faces.FacesMessages; import org.jboss.seam.international.LocaleSelector; +import org.jboss.seam.security.NotLoggedInException; import org.jboss.seam.security.management.JpaIdentityStore; +import org.jboss.seam.web.ServletContexts; import org.zanata.common.LocaleId; import org.zanata.email.EmailStrategy; import org.zanata.model.HAccount; @@ -45,12 +47,14 @@ import org.zanata.model.HPerson; import org.zanata.model.HProjectIteration; import org.zanata.seam.scope.ConversationScopeMessages; +import org.zanata.security.UserRedirectBean; import org.zanata.service.EmailService; import org.zanata.service.LocaleService; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; +import org.zanata.util.UrlUtil; import org.zanata.webtrans.shared.model.ProjectIterationId; import org.zanata.email.ContactAdminEmailStrategy; @@ -96,7 +100,7 @@ public class SendEmailAction implements Serializable { @In private EmailService emailServiceImpl; - @In(required = true, value = JpaIdentityStore.AUTHENTICATED_USER) + @In(value = JpaIdentityStore.AUTHENTICATED_USER, required = false) private HAccount authenticatedAccount; @In @@ -139,6 +143,12 @@ public class SendEmailAction implements Serializable { @In private ConversationScopeMessages conversationScopeMessages; + @In + private UrlUtil urlUtil; + + @In + private UserRedirectBean userRedirect; + private List groupMaintainers; public static final String SUCCESS = "success"; @@ -147,8 +157,11 @@ public class SendEmailAction implements Serializable { @Create public void onCreate() { if (authenticatedAccount == null) { - log.error("SendEmailAction failed to load authenticated account"); - return; + log.warn("accessing SendEmailAction without authenticated account"); + String encodedLocalUrl = urlUtil.getEncodedLocalUrl( + ServletContexts.instance().getRequest()); + userRedirect.setUrl(UrlUtil.decodeString(encodedLocalUrl)); + throw new NotLoggedInException(); } fromName = authenticatedAccount.getPerson().getName(); fromLoginName = authenticatedAccount.getUsername(); @@ -175,92 +188,111 @@ public String send() { localeSelector.setLocale(new Locale("en")); try { - if (emailType.equals(EMAIL_TYPE_CONTACT_ADMIN)) { - EmailStrategy strategy = new ContactAdminEmailStrategy( - fromLoginName, fromName, replyEmail, - subject, htmlMessage); - - String msg = emailServiceImpl.sendToAdmins(strategy); - - FacesMessages.instance().add(msg); - conversationScopeMessages.setMessage( - FacesMessage.SEVERITY_INFO, msg); - return SUCCESS; - } else if (emailType.equals(EMAIL_TYPE_CONTACT_COORDINATOR)) { - String localeNativeName = locale.retrieveNativeName(); - - EmailStrategy strategy = new ContactLanguageCoordinatorEmailStrategy( - fromLoginName, fromName, replyEmail, subject, - locale.getLocaleId().getId(), - localeNativeName, htmlMessage); - String msg = emailServiceImpl.sendToLanguageCoordinators( - locale, strategy); - - FacesMessages.instance().add(msg); - conversationScopeMessages.setMessage( - FacesMessage.SEVERITY_INFO, msg); - return SUCCESS; - } else if (emailType.equals(EMAIL_TYPE_REQUEST_JOIN)) { - String localeNativeName = locale.retrieveNativeName(); - - EmailStrategy strategy = new RequestToJoinLanguageEmailStrategy( - fromLoginName, fromName, replyEmail, - locale.getLocaleId().getId(), - localeNativeName, htmlMessage, - languageJoinUpdateRoleAction.getRequestAsTranslator(), - languageJoinUpdateRoleAction.getRequestAsReviewer(), - languageJoinUpdateRoleAction.getRequestAsCoordinator()); - String msg = emailServiceImpl.sendToLanguageCoordinators( - locale, strategy); - FacesMessages.instance().add(msg); - conversationScopeMessages.setMessage( - FacesMessage.SEVERITY_INFO, msg); - return SUCCESS; - } else if (emailType.equals(EMAIL_TYPE_REQUEST_ROLE)) { - String localeNativeName = locale.retrieveNativeName(); - - EmailStrategy strategy = new RequestRoleLanguageEmailStrategy( - fromLoginName, fromName, replyEmail, - locale.getLocaleId().getId(), - localeNativeName, htmlMessage, - languageJoinUpdateRoleAction.requestingTranslator(), - languageJoinUpdateRoleAction.requestingReviewer(), - languageJoinUpdateRoleAction.requestingCoordinator()); - String msg = emailServiceImpl.sendToLanguageCoordinators( - locale, strategy); - FacesMessages.instance().add(msg); - conversationScopeMessages.setMessage( - FacesMessage.SEVERITY_INFO, msg); - return SUCCESS; - } else if (emailType.equals(EMAIL_TYPE_REQUEST_TO_JOIN_GROUP)) { - String groupSlug = versionGroupJoinAction.getSlug(); - String groupName = versionGroupJoinAction.getGroupName(); - Collection projectIterIds = Lists.newArrayList(); - - for (VersionGroupJoinAction.SelectableProject version : versionGroupJoinAction.getProjectVersions()) { - if (version.isSelected()) { - HProjectIteration projIter = - version.getProjectIteration(); - projectIterIds.add(new ProjectIterationId( - projIter.getProject().getSlug(), - projIter.getSlug(), - projIter.getProjectType())); - } + switch (emailType) { + case EMAIL_TYPE_CONTACT_ADMIN: { + EmailStrategy strategy = new ContactAdminEmailStrategy( + fromLoginName, fromName, replyEmail, + subject, htmlMessage); + + String msg = emailServiceImpl.sendToAdmins(strategy); + + FacesMessages.instance().add(msg); + conversationScopeMessages.setMessage( + FacesMessage.SEVERITY_INFO, msg); + return SUCCESS; + } + case EMAIL_TYPE_CONTACT_COORDINATOR: { + String localeNativeName = locale.retrieveNativeName(); + + EmailStrategy strategy = + new ContactLanguageCoordinatorEmailStrategy( + fromLoginName, fromName, replyEmail, + subject, + locale.getLocaleId().getId(), + localeNativeName, htmlMessage); + String msg = emailServiceImpl.sendToLanguageCoordinators( + locale, strategy); + + FacesMessages.instance().add(msg); + conversationScopeMessages.setMessage( + FacesMessage.SEVERITY_INFO, msg); + return SUCCESS; } + case EMAIL_TYPE_REQUEST_JOIN: { + String localeNativeName = locale.retrieveNativeName(); + + EmailStrategy strategy = + new RequestToJoinLanguageEmailStrategy( + fromLoginName, fromName, replyEmail, + locale.getLocaleId().getId(), + localeNativeName, htmlMessage, + languageJoinUpdateRoleAction + .getRequestAsTranslator(), + languageJoinUpdateRoleAction + .getRequestAsReviewer(), + languageJoinUpdateRoleAction + .getRequestAsCoordinator()); + String msg = emailServiceImpl.sendToLanguageCoordinators( + locale, strategy); + FacesMessages.instance().add(msg); + conversationScopeMessages.setMessage( + FacesMessage.SEVERITY_INFO, msg); + return SUCCESS; + } + case EMAIL_TYPE_REQUEST_ROLE: { + String localeNativeName = locale.retrieveNativeName(); + + EmailStrategy strategy = + new RequestRoleLanguageEmailStrategy( + fromLoginName, fromName, replyEmail, + locale.getLocaleId().getId(), + localeNativeName, htmlMessage, + languageJoinUpdateRoleAction + .requestingTranslator(), + languageJoinUpdateRoleAction + .requestingReviewer(), + languageJoinUpdateRoleAction + .requestingCoordinator()); + String msg = emailServiceImpl.sendToLanguageCoordinators( + locale, strategy); + FacesMessages.instance().add(msg); + conversationScopeMessages.setMessage( + FacesMessage.SEVERITY_INFO, msg); + return SUCCESS; + } + case EMAIL_TYPE_REQUEST_TO_JOIN_GROUP: { + String groupSlug = versionGroupJoinAction.getSlug(); + String groupName = versionGroupJoinAction.getGroupName(); + Collection projectIterIds = + Lists.newArrayList(); + + for (VersionGroupJoinAction.SelectableProject version : versionGroupJoinAction + .getProjectVersions()) { + if (version.isSelected()) { + HProjectIteration projIter = + version.getProjectIteration(); + projectIterIds.add(new ProjectIterationId( + projIter.getProject().getSlug(), + projIter.getSlug(), + projIter.getProjectType())); + } + } - EmailStrategy strategy = new RequestToJoinVersionGroupEmailStrategy( - fromLoginName, fromName, replyEmail, - groupName, groupSlug, - projectIterIds, htmlMessage); - String msg = - emailServiceImpl - .sendToVersionGroupMaintainers( - groupMaintainers, strategy); - conversationScopeMessages.setMessage( - FacesMessage.SEVERITY_INFO, msg); - return SUCCESS; - } else { - throw new Exception("Invalid email type: " + emailType); + EmailStrategy strategy = + new RequestToJoinVersionGroupEmailStrategy( + fromLoginName, fromName, replyEmail, + groupName, groupSlug, + projectIterIds, htmlMessage); + String msg = + emailServiceImpl + .sendToVersionGroupMaintainers( + groupMaintainers, strategy); + conversationScopeMessages.setMessage( + FacesMessage.SEVERITY_INFO, msg); + return SUCCESS; + } + default: + throw new Exception("Invalid email type: " + emailType); } } catch (Exception e) { FacesMessages.instance().add( @@ -276,9 +308,6 @@ public String send() { } } - /** - * @return string 'canceled' - */ public void cancel() { log.info( "Canceled sending email: fromName '{}', fromLoginName '{}', replyEmail '{}', subject '{}', message '{}'", diff --git a/zanata-war/src/main/java/org/zanata/action/ServerConfigurationBean.java b/zanata-war/src/main/java/org/zanata/action/ServerConfigurationBean.java index 8e9a187fde..4537be6fa1 100644 --- a/zanata-war/src/main/java/org/zanata/action/ServerConfigurationBean.java +++ b/zanata-war/src/main/java/org/zanata/action/ServerConfigurationBean.java @@ -248,13 +248,12 @@ private void persistPropertiesToDatabase(List> propertie } private void persistPropertyToDatabase(PropertyWithKey property) { - HApplicationConfiguration registerUrlValue = - applicationConfigurationDAO + HApplicationConfiguration value = applicationConfigurationDAO .findByKey(property.getKey()); try { ServerConfigurationService.persistApplicationConfig( - property.getKey(), - registerUrlValue, property.get(), applicationConfigurationDAO); + property.getKey(), value, property.get(), + applicationConfigurationDAO); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { diff --git a/zanata-war/src/main/java/org/zanata/action/SortingType.java b/zanata-war/src/main/java/org/zanata/action/SortingType.java index 8d9c3f5797..4666989c07 100644 --- a/zanata-war/src/main/java/org/zanata/action/SortingType.java +++ b/zanata-war/src/main/java/org/zanata/action/SortingType.java @@ -36,7 +36,9 @@ public enum SortOption { ALPHABETICAL("Alphabetical", true), LAST_ACTIVITY("Last activity", false), LAST_SOURCE_UPDATE("Last source updated", false), LAST_TRANSLATED("Last translated", false), LAST_UPDATED_BY_YOU( - "Last updated by you", false), Entry("Entry", false); + "Last updated by you", false), Entry("Entry", false), + LOCALE_ID("Locale code", true), MEMBERS("Members", true), + CREATED_DATE("Created date", true); @Getter String display; diff --git a/zanata-war/src/main/java/org/zanata/action/SupportedLanguageAction.java b/zanata-war/src/main/java/org/zanata/action/SupportedLanguageAction.java deleted file mode 100644 index 0282ae7a57..0000000000 --- a/zanata-war/src/main/java/org/zanata/action/SupportedLanguageAction.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.zanata.action; - -import java.io.Serializable; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -import org.jboss.seam.ScopeType; -import org.jboss.seam.annotations.In; -import org.jboss.seam.annotations.Name; -import org.jboss.seam.annotations.Scope; -import org.zanata.model.HLocale; -import org.zanata.service.LocaleService; - -@Name("supportedLanguageAction") -@Scope(ScopeType.STATELESS) -public class SupportedLanguageAction implements Serializable { - private static final long serialVersionUID = 1L; - - @In - private LocaleService localeServiceImpl; - - static class TribeComparator implements Comparator { - public int compare(HLocale aZanataLocale, HLocale bZanataLocale) { - String aDisplayName = aZanataLocale.retrieveDisplayName(); - String bDisplayName = bZanataLocale.retrieveDisplayName(); - int comparison = aDisplayName.compareTo(bDisplayName); - if (comparison == 0) { - String aNativeName = aZanataLocale.retrieveNativeName(); - String bNativeName = bZanataLocale.retrieveNativeName(); - comparison = aNativeName.compareTo(bNativeName); - if (comparison == 0) - // if all else fails, fall back on numerical ID sort - return aZanataLocale.getLocaleId().getId() - .compareTo(bZanataLocale.getLocaleId().getId()); - return comparison; - } - return comparison; - } - } - - public List getSupportedLanguages() { - // NB ULocale data isn't stored in the database, so we have - // to do a post-select sort. - List tribes = localeServiceImpl.getSupportedLocales(); - - // This Comparator isn't complete enough for general use, but it should - // work - Collections.sort(tribes, new TribeComparator()); - return tribes; - } - -} diff --git a/zanata-war/src/main/java/org/zanata/action/TranslationMemoryAction.java b/zanata-war/src/main/java/org/zanata/action/TranslationMemoryAction.java index 3f6e679c40..fc5026c6f7 100644 --- a/zanata-war/src/main/java/org/zanata/action/TranslationMemoryAction.java +++ b/zanata-war/src/main/java/org/zanata/action/TranslationMemoryAction.java @@ -23,31 +23,33 @@ import static org.jboss.seam.international.StatusMessage.Severity.ERROR; import java.io.Serializable; +import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; -import javax.faces.event.ValueChangeEvent; - import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Out; +import org.jboss.seam.annotations.Scope; import org.jboss.seam.annotations.Transactional; import org.jboss.seam.annotations.security.Restrict; import org.jboss.seam.faces.FacesMessages; -import org.jboss.seam.framework.EntityHome; import org.zanata.async.AsyncTaskHandle; import org.zanata.async.AsyncTaskHandleManager; import org.zanata.dao.TransMemoryDAO; import org.zanata.exception.EntityMissingException; import org.zanata.model.tm.TransMemory; import org.zanata.rest.service.TranslationMemoryResourceService; -import org.zanata.service.SlugEntityService; + +import com.google.common.collect.Lists; /** * Controller class for the Translation Memory UI. @@ -57,17 +59,16 @@ */ @Name("translationMemoryAction") @Restrict("#{s:hasRole('admin')}") +@Scope(ScopeType.PAGE) @Slf4j -public class TranslationMemoryAction extends EntityHome { +public class TranslationMemoryAction implements Serializable { + @In private TransMemoryDAO transMemoryDAO; @In private TranslationMemoryResourceService translationMemoryResource; - @In - private SlugEntityService slugEntityServiceImpl; - @In private AsyncTaskHandleManager asyncTaskHandleManager; @@ -80,6 +81,14 @@ public class TranslationMemoryAction extends EntityHome { @Out(scope = ScopeType.PAGE, required = false) private Future lastTaskResult; + @Getter + private SortingType tmSortingList = new SortingType( + Lists.newArrayList(SortingType.SortOption.ALPHABETICAL, + SortingType.SortOption.CREATED_DATE)); + + private final TMComparator tmComparator = + new TMComparator(getTmSortingList()); + /** * Stores the last process error, but only for the duration of the event. */ @@ -92,22 +101,11 @@ public List getAllTranslationMemories() { return transMemoryList; } - public void verifySlugAvailable(ValueChangeEvent e) { - String slug = (String) e.getNewValue(); - validateSlug(slug, e.getComponent().getId()); - } - - public boolean validateSlug(String slug, String componentId) { - if (!slugEntityServiceImpl.isSlugAvailable(slug, TransMemory.class)) { - FacesMessages.instance().addToControl(componentId, - "This Id is not available"); - return false; - } - return true; + public void sortTMList() { + Collections.sort(transMemoryList, tmComparator); } public void clearTransMemory(final String transMemorySlug) { - String name = "TranslationMemoryAction.clearTransMemory: "+transMemorySlug; AsyncTaskHandle handle = new AsyncTaskHandle(); asyncTaskHandleManager.registerTaskHandle(handle, new ClearTransMemoryProcessKey(transMemorySlug)); @@ -177,7 +175,7 @@ public boolean deleteTransMemoryDisabled(String transMemorySlug) { public boolean isTablePollEnabled() { // Poll is enabled only when there is something being cleared - for (TransMemory tm : transMemoryList) { + for (TransMemory tm : getAllTranslationMemories()) { if (isTransMemoryBeingCleared(tm.getSlug())) { return true; } @@ -185,6 +183,10 @@ public boolean isTablePollEnabled() { return false; } + public boolean isTranslationMemoryEmpty(String tmSlug) { + return getTranslationMemorySize(tmSlug) <= 0; + } + public long getTranslationMemorySize(String tmSlug) { return transMemoryDAO.getTranslationMemorySize(tmSlug); } @@ -194,16 +196,6 @@ public String cancel() { return "cancel"; } - @Override - @Transactional - public String persist() { - if (!validateSlug(getInstance().getSlug(), "slug")) { - return null; - } - - return super.persist(); - } - /** * Represents a key to index a translation memory clear process. * @@ -215,4 +207,30 @@ public String persist() { private class ClearTransMemoryProcessKey implements Serializable { private String slug; } + + private class TMComparator implements Comparator { + private SortingType sortingType; + + public TMComparator(SortingType sortingType) { + this.sortingType = sortingType; + } + + @Override + public int compare(TransMemory o1, TransMemory o2) { + SortingType.SortOption selectedSortOption = + sortingType.getSelectedSortOption(); + + if (!selectedSortOption.isAscending()) { + TransMemory temp = o1; + o1 = o2; + o2 = temp; + } + + if (selectedSortOption.equals(SortingType.SortOption.ALPHABETICAL)) { + return o1.getSlug().compareToIgnoreCase(o2.getSlug()); + } else { + return o1.getCreationDate().compareTo(o2.getCreationDate()); + } + } + } } diff --git a/zanata-war/src/main/java/org/zanata/action/TranslationMemoryHome.java b/zanata-war/src/main/java/org/zanata/action/TranslationMemoryHome.java new file mode 100644 index 0000000000..e4418ab3df --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/action/TranslationMemoryHome.java @@ -0,0 +1,71 @@ +/* + * Copyright 2010, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this software; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF + * site: http://www.fsf.org. + */ +package org.zanata.action; + +import javax.faces.event.ValueChangeEvent; + +import lombok.extern.slf4j.Slf4j; + +import org.jboss.seam.annotations.In; +import org.jboss.seam.annotations.Name; +import org.jboss.seam.annotations.Transactional; +import org.jboss.seam.annotations.security.Restrict; +import org.jboss.seam.faces.FacesMessages; +import org.jboss.seam.framework.EntityHome; +import org.zanata.model.tm.TransMemory; +import org.zanata.service.SlugEntityService; + +/** + * Controller class for the Translation Memory UI. + * + * @author Carlos Munoz camunoz@redhat.com + */ +@Name("translationMemoryHome") +@Restrict("#{s:hasRole('admin')}") +@Slf4j +public class TranslationMemoryHome extends EntityHome { + @In + private SlugEntityService slugEntityServiceImpl; + + public void verifySlugAvailable(ValueChangeEvent e) { + String slug = (String) e.getNewValue(); + validateSlug(slug, e.getComponent().getId()); + } + + public boolean validateSlug(String slug, String componentId) { + if (!slugEntityServiceImpl.isSlugAvailable(slug, TransMemory.class)) { + FacesMessages.instance().addToControl(componentId, + "This Id is not available"); + return false; + } + return true; + } + + @Override + @Transactional + public String persist() { + if (!validateSlug(getInstance().getSlug(), "slug")) { + return null; + } + return super.persist(); + } +} diff --git a/zanata-war/src/main/java/org/zanata/action/UserAction.java b/zanata-war/src/main/java/org/zanata/action/UserAction.java index 0bb1858bac..4321b4755d 100644 --- a/zanata-war/src/main/java/org/zanata/action/UserAction.java +++ b/zanata-war/src/main/java/org/zanata/action/UserAction.java @@ -26,6 +26,7 @@ import javax.persistence.NoResultException; import javax.persistence.PersistenceException; +import org.apache.commons.lang.StringUtils; import org.hibernate.exception.ConstraintViolationException; import org.jboss.seam.Component; import org.jboss.seam.ScopeType; @@ -41,8 +42,12 @@ import org.zanata.dao.AccountDAO; import org.zanata.dao.PersonDAO; import org.zanata.i18n.Messages; +import org.zanata.model.HPerson; +import org.zanata.model.HProjectIteration; import org.zanata.service.EmailService; import org.zanata.service.UserAccountService; +import org.zanata.ui.AbstractListFilter; +import org.zanata.ui.InMemoryListFilter; import lombok.Getter; import lombok.Setter; @@ -70,27 +75,41 @@ public class UserAction extends @In private EntityManager entityManager; - @In - private ApplicationConfiguration applicationConfiguration; - @In private Messages msgs; - @In - private PersonDAO personDAO; - @In private UserAccountService userAccountServiceImpl; @In private EmailService emailServiceImpl; - private boolean newUserFlag; + @In + private PersonDAO personDAO; - private UserPagedListDataModel userPagedListDataModel = new UserPagedListDataModel(); + private boolean newUserFlag; private String originalUsername; + @Getter + private AbstractListFilter userFilter = + new AbstractListFilter() { + AccountDAO accountDAO = + (AccountDAO) Component.getInstance(AccountDAO.class, + ScopeType.STATELESS); + + @Override + protected List fetchRecords(int start, int max, + String filter) { + return accountDAO.getUserNames(filter, start, max); + } + + @Override + protected long fetchTotalRecords(String filter) { + return accountDAO.getUserCount(filter); + } + }; + public void deleteUser(String userName) { try { identityManager.deleteUser(userName); @@ -99,27 +118,17 @@ public void deleteUser(String userName) { entityManager.flush(); } catch (PersistenceException e) { if (e.getCause() instanceof ConstraintViolationException) { - FacesMessages - .instance() - .add(StatusMessage.Severity.ERROR, - msgs.get( - "jsf.UserManager.delete.constraintViolation.error")); + FacesMessages.instance() + .add(StatusMessage.Severity.ERROR, msgs.get( + "jsf.UserManager.delete.constraintViolation.error")); } } } - public DataModel getUserPagedListDataModel() { - return userPagedListDataModel; - } - public String getEmail(String username) { return personDAO.findEmail(username); } - // This is readonly field in UI. - public void setEmail(String email) { - } - public String getName(String username) { return personDAO.findByUsername(username).getName(); } @@ -185,25 +194,4 @@ private boolean isNewUsernameValid(String username) { return true; } } - - public class UserPagedListDataModel extends PagedListDataModel { - @Getter - @Setter - private String filter; - - @Override - public DataPage fetchPage(int startRow, int pageSize) { - AccountDAO accountDAO = - (AccountDAO) Component.getInstance(AccountDAO.class, - ScopeType.STATELESS); - - List userList = - accountDAO.getUserNames(filter, startRow, pageSize); - - int listSize = accountDAO.getUserCount(filter); - - return new DataPage(listSize, startRow, userList); - } - } - } diff --git a/zanata-war/src/main/java/org/zanata/action/UserSettingsAction.java b/zanata-war/src/main/java/org/zanata/action/UserSettingsAction.java index 0a5f809676..a453c0f8ce 100644 --- a/zanata-war/src/main/java/org/zanata/action/UserSettingsAction.java +++ b/zanata-war/src/main/java/org/zanata/action/UserSettingsAction.java @@ -28,17 +28,15 @@ import java.util.List; import javax.faces.context.ExternalContext; -import javax.mail.internet.InternetAddress; import javax.persistence.EntityManager; import javax.servlet.http.HttpServletRequest; import javax.validation.constraints.Size; -import com.googlecode.totallylazy.collections.PersistentMap; import lombok.Getter; -import lombok.RequiredArgsConstructor; import lombok.Setter; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang.StringEscapeUtils; import org.hibernate.validator.constraints.Email; import org.hibernate.validator.constraints.NotEmpty; import org.jboss.seam.ScopeType; @@ -55,7 +53,6 @@ import org.zanata.dao.AccountDAO; import org.zanata.dao.CredentialsDAO; import org.zanata.dao.PersonDAO; -import org.zanata.email.EmailStrategy; import org.zanata.i18n.Messages; import org.zanata.model.HAccount; import org.zanata.model.HLocale; @@ -218,16 +215,13 @@ public String getAccountUsername() { * google, yahoo, fedora, openid for everything else */ public String getCredentialsType(HCredentials credentials) { - if( new GoogleOpenIdProvider().accepts(credentials.getUser()) ) { + if (new GoogleOpenIdProvider().accepts(credentials.getUser())) { return "google"; - } - else if( new FedoraOpenIdProvider().accepts(credentials.getUser()) ) { + } else if (new FedoraOpenIdProvider().accepts(credentials.getUser())) { return "fedora"; - } - else if( new YahooOpenIdProvider().accepts(credentials.getUser()) ) { + } else if (new YahooOpenIdProvider().accepts(credentials.getUser())) { return "yahoo"; - } - else { + } else { return "openid"; } } @@ -261,11 +255,10 @@ public void verifyCredentials(String providerTypeStr) { OpenIdProviderType.valueOf(providerTypeStr); HOpenIdCredentials newCreds = new HOpenIdCredentials(); newCreds.setAccount(authenticatedAccount); - if( providerType == OpenIdProviderType.Generic ) { + if (providerType == OpenIdProviderType.Generic) { authenticationManager.openIdAuthenticate(openId, providerType, new CredentialsCreationCallback(newCreds)); - } - else { + } else { authenticationManager.openIdAuthenticate(providerType, new CredentialsCreationCallback(newCreds)); } @@ -311,6 +304,14 @@ private String getKeyPrefix() { return serverName.replace(".", "_"); } + /** + * return javascript safe message + */ + public String getRegenerateAPiKeyMsg() { + String msg = msgs.get("jsf.apikey.ConfirmGenerate"); + return StringEscapeUtils.escapeJavaScript(msg); + } + public void regenerateApiKey() { HAccount account = accountDAO.findById(authenticatedAccount.getId()); @@ -323,10 +324,10 @@ public void regenerateApiKey() { public void updateProfile() { HPerson person = personDAO.findById(authenticatedAccount.getPerson().getId()); - person.setName( accountName ); + person.setName(accountName); // Update the injected object as well. // TODO When more fields are added, we'll need a better solution - authenticatedAccount.getPerson().setName( accountName ); + authenticatedAccount.getPerson().setName(accountName); personDAO.makePersistent(person); FacesMessages.instance().add( msgs.get("jsf.dashboard.settings.profileUpdated.message")); diff --git a/zanata-war/src/main/java/org/zanata/action/VersionGroupHomeAction.java b/zanata-war/src/main/java/org/zanata/action/VersionGroupHomeAction.java index 1b31db7c5d..e87a277b71 100644 --- a/zanata-war/src/main/java/org/zanata/action/VersionGroupHomeAction.java +++ b/zanata-war/src/main/java/org/zanata/action/VersionGroupHomeAction.java @@ -389,7 +389,7 @@ public DisplayUnit getStatisticFigureForProject( } public WordStatistic getStatisticsForLocale(LocaleId localeId) { - if( !localeStats.containsKey(localeId) ) { + if (!localeStats.containsKey(localeId)) { WordStatistic statistic = new WordStatistic(); for (Map.Entry entry : statisticMap .entrySet()) { @@ -405,7 +405,7 @@ public WordStatistic getStatisticsForLocale(LocaleId localeId) { } public WordStatistic getStatisticForProject(Long projectIterationId) { - if( !projectStats.containsKey(projectIterationId) ) { + if (!projectStats.containsKey(projectIterationId)) { WordStatistic statistic = new WordStatistic(); for (Map.Entry entry : statisticMap .entrySet()) { diff --git a/zanata-war/src/main/java/org/zanata/action/VersionHome.java b/zanata-war/src/main/java/org/zanata/action/VersionHome.java index e2f0139a9a..fe69f871ef 100644 --- a/zanata-war/src/main/java/org/zanata/action/VersionHome.java +++ b/zanata-war/src/main/java/org/zanata/action/VersionHome.java @@ -150,8 +150,10 @@ private void setDefaultCopyFromVersion() { this.copyFromVersionSlug = otherVersions.get(0).getVersion().getSlug(); + copyFromVersion = true; + } else { + copyFromVersion = false; } - copyFromVersion = true; } public void init(boolean isNewInstance) { diff --git a/zanata-war/src/main/java/org/zanata/action/VersionHomeAction.java b/zanata-war/src/main/java/org/zanata/action/VersionHomeAction.java index 124751f146..b6b5b66d8f 100644 --- a/zanata-war/src/main/java/org/zanata/action/VersionHomeAction.java +++ b/zanata-war/src/main/java/org/zanata/action/VersionHomeAction.java @@ -930,16 +930,17 @@ public void uploadTranslationFile(HLocale hLocale) { extensions = Collections. emptySet(); } List warnings = - translationServiceImpl - .translateAllInDoc( - projectSlug, - versionSlug, - translationFileUpload.getDocId(), - hLocale.getLocaleId(), - transRes, - extensions, - translationFileUpload.isMergeTranslations() ? MergeType.AUTO - : MergeType.IMPORT); + translationServiceImpl.translateAllInDoc( + projectSlug, + versionSlug, + translationFileUpload.getDocId(), + hLocale.getLocaleId(), + transRes, + extensions, + translationFileUpload.isMergeTranslations() ? + MergeType.AUTO + : MergeType.IMPORT, + translationFileUpload.isAssignCreditToUploader()); StringBuilder infoMsg = new StringBuilder("File ").append( @@ -1146,5 +1147,7 @@ public static class TranslationFileUploadHelper implements Serializable { private String fileName; private boolean mergeTranslations = true; // Merge by default + + private boolean assignCreditToUploader = false; } } diff --git a/zanata-war/src/main/java/org/zanata/async/AsyncMethodInterceptor.java b/zanata-war/src/main/java/org/zanata/async/AsyncMethodInterceptor.java index 3999262dcd..77e00b905e 100644 --- a/zanata-war/src/main/java/org/zanata/async/AsyncMethodInterceptor.java +++ b/zanata-war/src/main/java/org/zanata/async/AsyncMethodInterceptor.java @@ -42,7 +42,7 @@ public class AsyncMethodInterceptor implements OptimizedInterceptor { @Override public Object aroundInvoke(final InvocationContext ctx) throws Exception { - if( ctx.getMethod().getAnnotation(Async.class) == null ) { + if (ctx.getMethod().getAnnotation(Async.class) == null) { return ctx.proceed(); } @@ -74,7 +74,7 @@ public Object aroundInvoke(final InvocationContext ctx) throws Exception { public Object call() throws Throwable { interceptorRan.set(true); try { - if( handle.isPresent() ) { + if (handle.isPresent()) { handle.get().startTiming(); } Object target = @@ -86,8 +86,7 @@ public Object call() throws Throwable { } catch (InvocationTargetException itex) { // exception thrown from the invoked method throw itex.getCause(); - } - finally { + } finally { interceptorRan.remove(); if (handle.isPresent()) { handle.get().finishTiming(); @@ -99,7 +98,7 @@ public Object call() throws Throwable { ListenableFuture futureResult = taskManager.startTask(asyncTask); - if( handle.isPresent() ) { + if (handle.isPresent()) { handle.get().setFutureResult(futureResult); } return futureResult; diff --git a/zanata-war/src/main/java/org/zanata/async/AsyncTaskHandleManager.java b/zanata-war/src/main/java/org/zanata/async/AsyncTaskHandleManager.java index 60197a67ae..06a9ced260 100644 --- a/zanata-war/src/main/java/org/zanata/async/AsyncTaskHandleManager.java +++ b/zanata-war/src/main/java/org/zanata/async/AsyncTaskHandleManager.java @@ -72,13 +72,13 @@ public synchronized Serializable registerTaskHandle(AsyncTaskHandle handle) { return autoGenKey; } - void taskFinished( AsyncTaskHandle taskHandle ) { + void taskFinished(AsyncTaskHandle taskHandle) { synchronized (handlesByKey) { // TODO This operation is O(n). Maybe we can do better? for (Map.Entry entry : handlesByKey .entrySet()) { - if( entry.getValue().equals( taskHandle ) ) { - handlesByKey.remove( entry.getKey() ); + if (entry.getValue().equals(taskHandle)) { + handlesByKey.remove(entry.getKey()); finishedTasks.put(entry.getKey(), entry.getValue()); } } @@ -86,7 +86,7 @@ void taskFinished( AsyncTaskHandle taskHandle ) { } public AsyncTaskHandle getHandleByKey(Serializable key) { - if( handlesByKey.containsKey(key) ) { + if (handlesByKey.containsKey(key)) { return handlesByKey.get(key); } return finishedTasks.getIfPresent(key); diff --git a/zanata-war/src/main/java/org/zanata/async/AsyncTaskManager.java b/zanata-war/src/main/java/org/zanata/async/AsyncTaskManager.java index 5e23c59335..bd243c0eca 100644 --- a/zanata-war/src/main/java/org/zanata/async/AsyncTaskManager.java +++ b/zanata-war/src/main/java/org/zanata/async/AsyncTaskManager.java @@ -34,11 +34,13 @@ import org.jboss.seam.annotations.AutoCreate; import org.jboss.seam.annotations.Create; import org.jboss.seam.annotations.Destroy; +import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; import org.jboss.seam.contexts.Lifecycle; import org.jboss.seam.security.RunAsOperation; import org.zanata.action.AuthenticationEvents; +import org.zanata.config.AsyncConfig; import org.zanata.dao.AccountDAO; import org.zanata.model.HAccount; import org.zanata.security.ZanataIdentity; @@ -59,9 +61,13 @@ public class AsyncTaskManager { private ExecutorService scheduler; + @In + private AsyncConfig asyncConfig; + @Create public void init() { - scheduler = Executors.newFixedThreadPool(3); + scheduler = + Executors.newFixedThreadPool(asyncConfig.getThreadPoolSize()); } @Destroy @@ -95,7 +101,7 @@ public void execute() { taskFuture.set(returnValue); } catch (Throwable t) { taskFuture.setException(t); - log.error( + log.warn( "Exception when executing an asynchronous task.", t); } } diff --git a/zanata-war/src/main/java/org/zanata/config/AsyncConfig.java b/zanata-war/src/main/java/org/zanata/config/AsyncConfig.java new file mode 100644 index 0000000000..85823bd81d --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/config/AsyncConfig.java @@ -0,0 +1,60 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this software; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF + * site: http://www.fsf.org. + */ +package org.zanata.config; + +import com.google.common.annotations.VisibleForTesting; +import lombok.NoArgsConstructor; +import org.jboss.seam.ScopeType; +import org.jboss.seam.annotations.AutoCreate; +import org.jboss.seam.annotations.In; +import org.jboss.seam.annotations.Name; +import org.jboss.seam.annotations.Scope; + +/** + * Holds all configuration values related to the asynchronous execution of + * tasks in the system. + * + * @author Carlos Munoz camunoz@redhat.com + */ +@Name("asyncConfig") +@Scope(ScopeType.STATELESS) +@AutoCreate +@NoArgsConstructor +public class AsyncConfig { + + public static final String THREAD_POOL_SIZE = "async.threadpool.size"; + + @In + private ConfigStore systemPropertyConfigStore; + + public AsyncConfig(ConfigStore systemPropertyConfigStore) { + this.systemPropertyConfigStore = systemPropertyConfigStore; + } + + public int getThreadPoolSize() { + try { + return new Integer(systemPropertyConfigStore.get(THREAD_POOL_SIZE)); + } catch (NumberFormatException e) { + return 10; // Default value + } + } +} diff --git a/zanata-war/src/main/java/org/zanata/config/ConfigStore.java b/zanata-war/src/main/java/org/zanata/config/ConfigStore.java new file mode 100644 index 0000000000..d5e9e3b346 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/config/ConfigStore.java @@ -0,0 +1,32 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this software; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF + * site: http://www.fsf.org. + */ +package org.zanata.config; + +/** + * Base implementation for all Configuration stores in the system. + * + * @author Carlos Munoz camunoz@redhat.com + */ +public interface ConfigStore { + + String get(String propertyName); +} diff --git a/zanata-war/src/main/java/org/zanata/config/SystemPropertyConfigStore.java b/zanata-war/src/main/java/org/zanata/config/SystemPropertyConfigStore.java new file mode 100644 index 0000000000..d6537727e0 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/config/SystemPropertyConfigStore.java @@ -0,0 +1,45 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this software; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF + * site: http://www.fsf.org. + */ +package org.zanata.config; + +import org.jboss.seam.ScopeType; +import org.jboss.seam.annotations.AutoCreate; +import org.jboss.seam.annotations.Name; +import org.jboss.seam.annotations.Scope; +import org.jboss.seam.annotations.intercept.BypassInterceptors; + +/** + * Property Store that delegates to system properties. + * + * @author Carlos Munoz camunoz@redhat.com + */ +@Name("systemPropertyConfigStore") +@Scope(ScopeType.STATELESS) +@AutoCreate +@BypassInterceptors +public class SystemPropertyConfigStore implements ConfigStore { + + @Override + public String get(String propertyName) { + return System.getProperty(propertyName); + } +} diff --git a/zanata-war/src/main/java/org/zanata/dao/AccountActivationKeyDAO.java b/zanata-war/src/main/java/org/zanata/dao/AccountActivationKeyDAO.java index e680eef21b..565f50b7b1 100644 --- a/zanata-war/src/main/java/org/zanata/dao/AccountActivationKeyDAO.java +++ b/zanata-war/src/main/java/org/zanata/dao/AccountActivationKeyDAO.java @@ -25,6 +25,7 @@ import org.jboss.seam.annotations.AutoCreate; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; +import org.zanata.model.HAccount; import org.zanata.model.HAccountActivationKey; @Name("accountActivationKeyDAO") @@ -41,4 +42,13 @@ public AccountActivationKeyDAO(Session session) { super(HAccountActivationKey.class, session); } + public HAccountActivationKey findByAccountIdAndKeyHash(Long accountId, String keyHash) { + return (HAccountActivationKey) getSession() + .createQuery( + "from HAccountActivationKey key where key.account.id = :accountId and key.keyHash= :keyHash") + .setLong("accountId", accountId) + .setString("keyHash", keyHash) + .setComment("AccountDAO.getByUsernameAndEmail").uniqueResult(); + } + } diff --git a/zanata-war/src/main/java/org/zanata/dao/AccountResetPasswordKeyDAO.java b/zanata-war/src/main/java/org/zanata/dao/AccountResetPasswordKeyDAO.java index 8cd463d5d2..ac4f2cdffb 100644 --- a/zanata-war/src/main/java/org/zanata/dao/AccountResetPasswordKeyDAO.java +++ b/zanata-war/src/main/java/org/zanata/dao/AccountResetPasswordKeyDAO.java @@ -41,4 +41,11 @@ public AccountResetPasswordKeyDAO(Session session) { super(HAccountResetPasswordKey.class, session); } + public HAccountResetPasswordKey findByAccount(Long accountId) { + return (HAccountResetPasswordKey) getSession() + .createQuery( + "from HAccountResetPasswordKey key where key.account.id = :accountId") + .setLong("accountId", accountId) + .setComment("AccountResetPasswordKeyDAO.findByAccount").uniqueResult(); + } } diff --git a/zanata-war/src/main/java/org/zanata/dao/ActivityDAO.java b/zanata-war/src/main/java/org/zanata/dao/ActivityDAO.java index 9d396ba634..fb896c4223 100644 --- a/zanata-war/src/main/java/org/zanata/dao/ActivityDAO.java +++ b/zanata-war/src/main/java/org/zanata/dao/ActivityDAO.java @@ -29,7 +29,6 @@ import org.jboss.seam.annotations.AutoCreate; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; -import org.zanata.apicompat.common.ContentState; import org.zanata.common.ActivityType; import org.zanata.model.Activity; import org.zanata.model.type.EntityType; diff --git a/zanata-war/src/main/java/org/zanata/dao/LocaleMemberDAO.java b/zanata-war/src/main/java/org/zanata/dao/LocaleMemberDAO.java index 466f8ceb30..a3bc75b03e 100644 --- a/zanata-war/src/main/java/org/zanata/dao/LocaleMemberDAO.java +++ b/zanata-war/src/main/java/org/zanata/dao/LocaleMemberDAO.java @@ -52,7 +52,7 @@ public List findAllByLocale(LocaleId localeId) { Query query = getSession() .createQuery( - "from HLocaleMember as m where m.id.supportedLanguage.localeId = :localeId"); + "from HLocaleMember as m where m.id.supportedLanguage.localeId = :localeId order by lower(m.id.person.name)"); query.setParameter("localeId", localeId); query.setComment("LocaleMemberDAO.findAllByLocale"); return query.list(); diff --git a/zanata-war/src/main/java/org/zanata/dao/ProjectDAO.java b/zanata-war/src/main/java/org/zanata/dao/ProjectDAO.java index b5b87d39b0..40c5968313 100644 --- a/zanata-war/src/main/java/org/zanata/dao/ProjectDAO.java +++ b/zanata-war/src/main/java/org/zanata/dao/ProjectDAO.java @@ -330,7 +330,7 @@ public int getTranslatedProjectCount(HAccount account) { "where tft.translator = :translator" ) .setParameter("translator", account); - return ((Long)q.uniqueResult()).intValue(); + return ((Long) q.uniqueResult()).intValue(); } @@ -353,7 +353,7 @@ public Date getLastTranslatedDate(HProject project, HAccount account) { .setParameter("translator", account) .setParameter("project", project) .setCacheable(true); - return (Date)q.uniqueResult(); + return (Date) q.uniqueResult(); } /** @@ -370,7 +370,7 @@ public Date getLastTranslatedDate(HProject project) { ) .setParameter("project", project) .setCacheable(true); - return (Date)q.uniqueResult(); + return (Date) q.uniqueResult(); } /** @@ -390,10 +390,9 @@ public HPerson getLastTranslator(HProject project) { .setMaxResults(1) .setCacheable(true); List results = q.list(); - if( results.isEmpty() ) { + if (results.isEmpty()) { return null; - } - else { + } else { return (HPerson) results.get(0); } } @@ -432,6 +431,6 @@ public int getMaintainedProjectCount(HPerson maintainer, String filter) { ) .setParameter("maintainer", maintainer) .setParameter("filter", "%" + sqlFilter + "%"); - return ((Long)q.uniqueResult()).intValue(); + return ((Long) q.uniqueResult()).intValue(); } } diff --git a/zanata-war/src/main/java/org/zanata/email/LanguageTeamPermissionChangeEmailStrategy.java b/zanata-war/src/main/java/org/zanata/email/LanguageTeamPermissionChangeEmailStrategy.java new file mode 100644 index 0000000000..1d4f284bf3 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/email/LanguageTeamPermissionChangeEmailStrategy.java @@ -0,0 +1,86 @@ +package org.zanata.email; + +import java.util.List; +import javax.mail.internet.InternetAddress; + +import org.zanata.events.LanguageTeamPermissionChangedEvent; +import org.zanata.i18n.Messages; +import com.google.common.collect.Lists; +import com.googlecode.totallylazy.collections.PersistentMap; +import lombok.RequiredArgsConstructor; + +/** + * @author Patrick Huang pahuang@redhat.com + */ +@RequiredArgsConstructor +public class LanguageTeamPermissionChangeEmailStrategy extends EmailStrategy { + private final LanguageTeamPermissionChangedEvent changedEvent; + private final Messages msgs; + private final String contactCoordinatorLink; + + @Override + public String getSubject(Messages msgs) { + return msgs.format("jsf.email.languageteam.permission.Subject", + changedEvent.getLanguage()); + } + + @Override + public String getBodyResourceName() { + return "org/zanata/email/templates/language_team_permission_changed.vm"; + } + + @Override + public PersistentMap makeContext( + PersistentMap genericContext, + InternetAddress[] toAddresses) { + PersistentMap context = super.makeContext( + genericContext, toAddresses); + List oldPermissions = Lists.newArrayList(); + if (changedEvent.hasNoOldPermissions()) { + oldPermissions + .add( + msgs.get("jsf.email.languageteam.permission.old.notInTeam")); + } else { + transformPermissionToDescription( + changedEvent.getOldPermission(), oldPermissions); + } + + List newPermissions = Lists.newArrayList(); + if (changedEvent.hasNoNewPermissions()) { + newPermissions.add(msgs + .get("jsf.email.languageteam.permission.new.notInTeam")); + } else { + transformPermissionToDescription( + changedEvent.getNewPermission(), newPermissions); + } + + return context + .insert("language", changedEvent.getLanguage()) + .insert("changedByName", changedEvent.getChangedByName()) + .insert("oldPermissions", oldPermissions) + .insert("newPermissions", newPermissions) + .insert("contactCoordinatorLink", contactCoordinatorLink) + .insert("toName", toAddresses[0].getPersonal()); + } + + private void transformPermissionToDescription( + List permissionList, List permissionDescriptions) { + if (changedEvent.translatorPermissionOf( + permissionList)) { + permissionDescriptions.add( + msgs.get("jsf.email.languageteam.permission.isTranslator")); + } + if (changedEvent.reviewerPermissionOf( + permissionList)) { + permissionDescriptions.add( + msgs.get("jsf.email.languageteam.permission.isReviewer")); + } + if (changedEvent.coordinatorPermissionOf( + permissionList)) { + permissionDescriptions + .add( + msgs.get("jsf.email.languageteam.permission.isCoordinator")); + } + } +} diff --git a/zanata-war/src/main/java/org/zanata/events/DocumentStatisticUpdatedEvent.java b/zanata-war/src/main/java/org/zanata/events/DocumentStatisticUpdatedEvent.java index bedd646c94..afb42d5537 100644 --- a/zanata-war/src/main/java/org/zanata/events/DocumentStatisticUpdatedEvent.java +++ b/zanata-war/src/main/java/org/zanata/events/DocumentStatisticUpdatedEvent.java @@ -25,20 +25,18 @@ import org.zanata.common.ContentState; import org.zanata.common.LocaleId; -import org.zanata.ui.model.statistic.WordStatistic; @Data public final class DocumentStatisticUpdatedEvent { public static final String EVENT_NAME = "org.zanata.event.DocumentStatisticUpdated"; - private final WordStatistic oldStats; - private final WordStatistic newStats; - private final Long projectIterationId; private final Long documentId; private final LocaleId localeId; + private final int wordCount; + private final ContentState previousState; private final ContentState newState; } diff --git a/zanata-war/src/main/java/org/zanata/events/LanguageTeamPermissionChangedEvent.java b/zanata-war/src/main/java/org/zanata/events/LanguageTeamPermissionChangedEvent.java new file mode 100644 index 0000000000..cbe54fe8eb --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/events/LanguageTeamPermissionChangedEvent.java @@ -0,0 +1,135 @@ +package org.zanata.events; + +import java.io.Serializable; +import java.util.List; + +import lombok.Getter; +import lombok.ToString; + +import org.zanata.common.LocaleId; +import org.zanata.model.HLocale; +import org.zanata.model.HLocaleMember; +import org.zanata.model.HPerson; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; + +/** + * @author Patrick Huang pahuang@redhat.com + */ +@Getter +@ToString +public class LanguageTeamPermissionChangedEvent implements Serializable { + public static final String LANGUAGE_TEAM_PERMISSION_CHANGED = + "languageTeamPermissionChanged"; + private static final long serialVersionUID = -1L; + + private final LocaleId language; + private final String name; + private final String email; + private final String changedByName; + private List oldPermission = ImmutableList.of(false, false, false); + private List newPermission = ImmutableList.of(false, false, false); + + public LanguageTeamPermissionChangedEvent(HPerson person, LocaleId language, + HPerson doneByPerson) { + name = person.getName(); + email = person.getEmail(); + this.language = language; + changedByName = doneByPerson.getName(); + } + + public LanguageTeamPermissionChangedEvent joiningTheTeam( + boolean isTranslator, boolean isReviewer, boolean isCoordinator) { + return this.oldPermission(false, false, false) + .newPermission(isTranslator, isReviewer, isCoordinator); + } + + public LanguageTeamPermissionChangedEvent updatingPermissions( + HLocaleMember oldMembership, boolean isTranslator, + boolean isReviewer, boolean isCoordinator) { + return this.oldPermission(oldMembership.isTranslator(), + oldMembership.isReviewer(), oldMembership.isCoordinator()) + .newPermission(isTranslator, isReviewer, isCoordinator); + } + + public LanguageTeamPermissionChangedEvent changedTranslatorPermission( + HLocaleMember localeMember) { + return this.oldPermission(!localeMember.isTranslator(), + localeMember.isReviewer(), + localeMember.isCoordinator()) + .newPermission(localeMember.isTranslator(), + localeMember.isReviewer(), + localeMember.isCoordinator()); + } + + public LanguageTeamPermissionChangedEvent changedReviewerPermission( + HLocaleMember localeMember) { + return this.oldPermission(localeMember.isTranslator(), + !localeMember.isReviewer(), + localeMember.isCoordinator()) + .newPermission(localeMember.isTranslator(), + localeMember.isReviewer(), + localeMember.isCoordinator()); + } + + public LanguageTeamPermissionChangedEvent changedCoordinatorPermission( + HLocaleMember localeMember) { + return this.oldPermission(localeMember.isTranslator(), + localeMember.isReviewer(), + !localeMember.isCoordinator()) + .newPermission(localeMember.isTranslator(), + localeMember.isReviewer(), + localeMember.isCoordinator()); + } + + private LanguageTeamPermissionChangedEvent oldPermission( + boolean isTranslator, boolean isReviewer, boolean isCoordinator) { + oldPermission = + ImmutableList.of(isTranslator, isReviewer, isCoordinator); + return this; + } + + private LanguageTeamPermissionChangedEvent newPermission( + boolean isTranslator, boolean isReviewer, boolean isCoordinator) { + newPermission = + ImmutableList.of(isTranslator, isReviewer, isCoordinator); + return this; + } + + // when user has no roles assigned or was not part of the team + public boolean hasNoOldPermissions() { + return Iterables.frequency(oldPermission, Boolean.TRUE) == 0; + } + + // when user has no roles assigned or is removed from the team + public boolean hasNoNewPermissions() { + return Iterables.frequency(newPermission, Boolean.TRUE) == 0; + } + + public boolean hasPermissionsChanged() { + return !oldPermission.equals(newPermission); + } + + public boolean translatorPermissionOf(List permissionList) { + return getPermission(permissionList, Permission.translator); + } + + public boolean reviewerPermissionOf(List permissionList) { + return getPermission(permissionList, Permission.reviewer); + } + + public boolean coordinatorPermissionOf(List permissionList) { + return getPermission(permissionList, Permission.coordinator); + } + + private static Boolean getPermission(List permissionList, + Permission permission) { + return permissionList.get(permission.ordinal()); + } + + private static enum Permission { + translator, reviewer, coordinator + } +} diff --git a/zanata-war/src/main/java/org/zanata/file/TranslationDocumentUpload.java b/zanata-war/src/main/java/org/zanata/file/TranslationDocumentUpload.java index d6845458d1..e66b0eb31b 100644 --- a/zanata-war/src/main/java/org/zanata/file/TranslationDocumentUpload.java +++ b/zanata-war/src/main/java/org/zanata/file/TranslationDocumentUpload.java @@ -82,7 +82,8 @@ public class TranslationDocumentUpload { public Response tryUploadTranslationFile(GlobalDocumentId id, String localeId, - String mergeType, DocumentFileUploadForm uploadForm) { + String mergeType, boolean assignCreditToUploader, + DocumentFileUploadForm uploadForm) { try { failIfTranslationUploadNotValid(id, localeId, uploadForm); @@ -151,7 +152,8 @@ public class TranslationDocumentUpload { translationServiceImpl.translateAllInDoc( id.getProjectSlug(), id.getVersionSlug(), id.getDocId(), locale.getLocaleId(), transRes, - extensions, mergeTypeFromString(mergeType)); + extensions, mergeTypeFromString(mergeType), + assignCreditToUploader); return transUploadResponse(totalChunks, warnings); } catch (FileNotFoundException e) { diff --git a/zanata-war/src/main/java/org/zanata/log4j/ZanataSMTPAppender.java b/zanata-war/src/main/java/org/zanata/log4j/ZanataSMTPAppender.java index 8ed4aa9212..60dfb857a3 100644 --- a/zanata-war/src/main/java/org/zanata/log4j/ZanataSMTPAppender.java +++ b/zanata-war/src/main/java/org/zanata/log4j/ZanataSMTPAppender.java @@ -91,9 +91,7 @@ InternetAddress[] parseAddress(String addressStr) { errorHandler.error("Could not parse address [" + addressStr + "].", e, ErrorCode.ADDRESS_PARSE_FAILURE); return null; - } - - catch (NullPointerException e) { + } catch (NullPointerException e) { errorHandler.error("Could not parse address [" + addressStr + "].", e, ErrorCode.ADDRESS_PARSE_FAILURE); return null; diff --git a/zanata-war/src/main/java/org/zanata/notification/DeadLetterQueueReceiver.java b/zanata-war/src/main/java/org/zanata/notification/DeadLetterQueueReceiver.java new file mode 100644 index 0000000000..a99c070888 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/notification/DeadLetterQueueReceiver.java @@ -0,0 +1,62 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this software; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF + * site: http://www.fsf.org. + */ +package org.zanata.notification; + +import javax.ejb.ActivationConfigProperty; +import javax.ejb.MessageDriven; +import javax.jms.Message; +import javax.jms.MessageListener; + +import lombok.extern.slf4j.Slf4j; + +import org.jboss.seam.annotations.Name; + +/** + * Consumer of Dead Letter Queue (all unsuccessful message will be dropped into + * the DLQ and handled here). + * + * @author Patrick Huang pahuang@redhat.com + */ +@MessageDriven(activationConfig = { + @ActivationConfigProperty( + propertyName = "destinationType", + propertyValue = "javax.jms.Queue" + ), + @ActivationConfigProperty( + propertyName = "destination", + propertyValue = "jms/queue/DLQ" + ), + @ActivationConfigProperty( + propertyName = "maxSession", + propertyValue = "1") +}) +@Name("deadLetterQueueReceiver") +@Slf4j +public class DeadLetterQueueReceiver implements MessageListener { + + @Override + public void onMessage(Message message) { + // right now we just log the content of the message + log.error("dead message: {}", MessageUnwrapper.unwrap(message)); + } + +} diff --git a/zanata-war/src/main/java/org/zanata/notification/EmailQueueMessageReceiver.java b/zanata-war/src/main/java/org/zanata/notification/EmailQueueMessageReceiver.java new file mode 100644 index 0000000000..67ab4c2ec4 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/notification/EmailQueueMessageReceiver.java @@ -0,0 +1,123 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this software; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF + * site: http://www.fsf.org. + */ +package org.zanata.notification; + +import java.io.Serializable; +import java.util.Collections; +import java.util.Map; + +import javax.ejb.ActivationConfigProperty; +import javax.ejb.MessageDriven; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.ObjectMessage; + +import lombok.extern.slf4j.Slf4j; + +import org.jboss.seam.annotations.In; +import org.jboss.seam.annotations.Name; +import org.zanata.events.LanguageTeamPermissionChangedEvent; + +import com.google.common.base.Throwables; +import com.google.common.collect.ImmutableMap; + +import static com.google.common.base.Strings.nullToEmpty; +import static org.zanata.notification.NotificationManager.MessagePropertiesKey; + +/** + * JMS EmailsQueue consumer. It will base on + * org.zanata.notification.NotificationManager.MessagePropertiesKey#objectType + * value to find a message payload handler. The objectType value is normally the + * canonical name of the event class. + * + * @author Patrick Huang pahuang@redhat.com + */ +@MessageDriven(activationConfig = { + @ActivationConfigProperty( + propertyName = "destinationType", + propertyValue = "javax.jms.Queue" + ), + @ActivationConfigProperty( + propertyName = "destination", + propertyValue = "jms/queue/MailsQueue" + ) +}) +@Name("emailQueueMessageReceiver") +@Slf4j +public class EmailQueueMessageReceiver implements MessageListener { + + private static Map handlers = Collections + .emptyMap(); + + @In("languageTeamPermissionChangeJmsMessagePayloadHandler") + private LanguageTeamPermissionChangeJmsMessagePayloadHandler languageTeamHandler; + + @Override + public void onMessage(Message message) { + if (message instanceof ObjectMessage) { + try { + String objectType = + nullToEmpty( + message.getStringProperty( + MessagePropertiesKey.objectType.name())); + + JmsMessagePayloadHandler jmsMessagePayloadHandler = + getHandlers().get(objectType); + if (jmsMessagePayloadHandler != null) { + log.debug("found handler for message object type [{}]", + objectType); + jmsMessagePayloadHandler.handle(((ObjectMessage) message) + .getObject()); + } else { + log.warn("can not find handler for message:{}", message); + } + + } catch (JMSException e) { + log.warn("error handling jms message: {}", message); + Throwables.propagate(e); + } + } + } + + public Map getHandlers() { + if (handlers.isEmpty()) { + synchronized (this) { + if (handlers.isEmpty()) { + handlers = + ImmutableMap + . builder() + .put(LanguageTeamPermissionChangedEvent.class + .getCanonicalName(), + languageTeamHandler) + .build(); + } + } + log.info("email queue payload handlers: {}", handlers); + } + return handlers; + } + + public static interface JmsMessagePayloadHandler { + void handle(Serializable data); + } +} diff --git a/zanata-war/src/main/java/org/zanata/notification/ExpiryQueueReceiver.java b/zanata-war/src/main/java/org/zanata/notification/ExpiryQueueReceiver.java new file mode 100644 index 0000000000..04d29df15e --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/notification/ExpiryQueueReceiver.java @@ -0,0 +1,57 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this software; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF + * site: http://www.fsf.org. + */ +package org.zanata.notification; + +import javax.ejb.ActivationConfigProperty; +import javax.ejb.MessageDriven; +import javax.jms.Message; +import javax.jms.MessageListener; + +import org.jboss.seam.annotations.Name; +import lombok.extern.slf4j.Slf4j; + +@MessageDriven(activationConfig = { + @ActivationConfigProperty( + propertyName = "destinationType", + propertyValue = "javax.jms.Queue" + ), + @ActivationConfigProperty( + propertyName = "destination", + propertyValue = "jms/queue/ExpiryQueue" + ), + @ActivationConfigProperty( + propertyName = "maxSession", + propertyValue = "1") +}) +/** + * Consumer for JMS ExpiryQueue. + * + * @author Patrick Huang + * pahuang@redhat.com + */ +@Name("expiryQueueReceiver") +@Slf4j +public class ExpiryQueueReceiver implements MessageListener { + @Override + public void onMessage(Message message) { + log.warn("JMS message expired: {}", MessageUnwrapper.unwrap(message)); + } +} diff --git a/zanata-war/src/main/java/org/zanata/notification/LanguageTeamPermissionChangeJmsMessagePayloadHandler.java b/zanata-war/src/main/java/org/zanata/notification/LanguageTeamPermissionChangeJmsMessagePayloadHandler.java new file mode 100644 index 0000000000..7be3f15617 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/notification/LanguageTeamPermissionChangeJmsMessagePayloadHandler.java @@ -0,0 +1,97 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this software; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF + * site: http://www.fsf.org. + */ +package org.zanata.notification; + +import java.io.Serializable; + +import javax.mail.internet.InternetAddress; + +import org.jboss.seam.ScopeType; +import org.jboss.seam.annotations.AutoCreate; +import org.jboss.seam.annotations.In; +import org.jboss.seam.annotations.Name; +import org.jboss.seam.annotations.Scope; +import org.zanata.ApplicationConfiguration; +import org.zanata.email.Addresses; +import org.zanata.email.EmailBuilder; +import org.zanata.email.LanguageTeamPermissionChangeEmailStrategy; +import org.zanata.events.LanguageTeamPermissionChangedEvent; +import org.zanata.i18n.Messages; +import lombok.extern.slf4j.Slf4j; + +/** + * Handles language team permissions change JMS message. This will build and + * send out an email to the person affected. + * N.B. We can only have application or + * stateless scope beans in here. + * + * @see EmailQueueMessageReceiver + * @author Patrick Huang pahuang@redhat.com + */ +@Name("languageTeamPermissionChangeJmsMessagePayloadHandler") +@Scope(ScopeType.STATELESS) +@AutoCreate +@Slf4j +public class LanguageTeamPermissionChangeJmsMessagePayloadHandler implements + EmailQueueMessageReceiver.JmsMessagePayloadHandler { + @In + private EmailBuilder emailBuilder; + + @In + private Messages msgs; + + @In + private ApplicationConfiguration applicationConfiguration; + + @Override + public void handle(Serializable data) { + if (!(data instanceof LanguageTeamPermissionChangedEvent)) { + log.error("can not handle data other than type {}", + LanguageTeamPermissionChangedEvent.class); + return; + } + LanguageTeamPermissionChangedEvent changedEvent = + LanguageTeamPermissionChangedEvent.class.cast(data); + log.debug("language team permission change data:{}", changedEvent); + + if (!changedEvent.hasPermissionsChanged()) { + // permission didn't really changed + return; + } + + String receivedReason = + msgs.format("jsf.email.languageteam.permission.ReceivedReason", + changedEvent.getLanguage()); + String contactTeamCoordinatorLink = + applicationConfiguration.getServerPath() + + "/language/contact/" + changedEvent.getLanguage(); + LanguageTeamPermissionChangeEmailStrategy emailStrategy = + new LanguageTeamPermissionChangeEmailStrategy( + changedEvent, msgs, contactTeamCoordinatorLink); + InternetAddress to = + Addresses.getAddress(changedEvent.getEmail(), + changedEvent.getName()); + + emailBuilder.sendMessage(emailStrategy, receivedReason, to); + } + +} diff --git a/zanata-war/src/main/java/org/zanata/notification/MessageUnwrapper.java b/zanata-war/src/main/java/org/zanata/notification/MessageUnwrapper.java new file mode 100644 index 0000000000..c39c6dffbb --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/notification/MessageUnwrapper.java @@ -0,0 +1,106 @@ +package org.zanata.notification; + +import java.io.Serializable; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import javax.jms.Message; +import javax.jms.ObjectMessage; +import javax.jms.TextMessage; + +import com.google.common.base.Objects; +import com.google.common.collect.ImmutableMap; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; + +/** + * Unwraps a JMS message. Extract payload for ObjectMessage and TextMessage and + * all properties. + * + * @author Patrick Huang pahuang@redhat.com + */ +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +class MessageUnwrapper { + private final Serializable payload; + private final Map properties; + + static MessageUnwrapper unwrap(final Message message) { + + Serializable payload = extractPayload(message); + Map properties = extractProperties(message); + // we may want to add more interested properties. e.g. reply address + return new MessageUnwrapper(payload, properties); + } + + private static Map extractProperties(final Message message) { + List propNames = + tryDoOrNullOnException(new Callable>() { + @Override + public List call() throws Exception { + return Collections.list(message.getPropertyNames()); + } + }); + + ImmutableMap.Builder builder = + ImmutableMap.builder(); + for (String propName : propNames) { + builder.put(propName, getPropertyValue(message, propName)); + } + return builder.build(); + } + + private static Object getPropertyValue(final Message message, + final String propertyName) { + return tryDoOrNullOnException(new Callable() { + @Override + public Object call() throws Exception { + return message.getObjectProperty(propertyName); + } + }); + } + + /** + * try to extract message payload by message type. + * + * @return message payload + */ + private static Serializable extractPayload(final Message message) { + // note: this is NOT a complete list of message types + if (message instanceof ObjectMessage) { + return tryDoOrNullOnException(new Callable() { + @Override + public Serializable call() throws Exception { + return ((ObjectMessage) message).getObject(); + } + }); + } else if (message instanceof TextMessage) { + return tryDoOrNullOnException(new Callable() { + @Override + public Serializable call() throws Exception { + return ((TextMessage) message).getText(); + } + }); + } else { + // generally this is not going to give us much information + return message.toString(); + } + } + + static T tryDoOrNullOnException(Callable callable) { + try { + return callable.call(); + } catch (Exception e) { + return null; + } + } + + @Override + public String toString() { + return Objects.toStringHelper(this) + .add("payload", payload) + .add("properties", properties) + .toString(); + } +} diff --git a/zanata-war/src/main/java/org/zanata/notification/NotificationManager.java b/zanata-war/src/main/java/org/zanata/notification/NotificationManager.java new file mode 100644 index 0000000000..733ff79744 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/notification/NotificationManager.java @@ -0,0 +1,87 @@ +/* + * Copyright 2014, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this software; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF + * site: http://www.fsf.org. + */ +package org.zanata.notification; + +import java.io.Serializable; + +import javax.jms.JMSException; +import javax.jms.ObjectMessage; +import javax.jms.QueueSender; +import javax.jms.QueueSession; + +import lombok.extern.slf4j.Slf4j; + +import org.jboss.seam.ScopeType; +import org.jboss.seam.annotations.In; +import org.jboss.seam.annotations.Name; +import org.jboss.seam.annotations.Observer; +import org.jboss.seam.annotations.Scope; +import org.jboss.seam.annotations.Startup; +import org.zanata.events.LanguageTeamPermissionChangedEvent; + +import com.google.common.base.Throwables; + +/** + * Centralized place to handle all events that needs to send out notifications. + * + * @author Patrick Huang pahuang@redhat.com + */ +@Name("notificationManager") +@Scope(ScopeType.APPLICATION) +@Startup +@Slf4j +public class NotificationManager implements Serializable { + private static final long serialVersionUID = -1L; + + // JMS EmailQueue Producer. + @In + private QueueSender mailQueueSender; + + @In + private QueueSession queueSession; + + @Observer(LanguageTeamPermissionChangedEvent.LANGUAGE_TEAM_PERMISSION_CHANGED) + public + void onLanguageTeamPermissionChanged( + final LanguageTeamPermissionChangedEvent event) { + try { + ObjectMessage message = + queueSession.createObjectMessage(event); + message.setObjectProperty(MessagePropertiesKey.objectType.name(), + event.getClass().getCanonicalName()); + mailQueueSender.send(message); + } catch (JMSException e) { + throw Throwables.propagate(e); + } + } + + /* + * we use this as property key in the JMS message to denote what type of + * message/event this is and the queue consumer can base on this value to + * find appropriate handler to handle the message payload. + * + * @seeorg.zanata.notification.EmailQueueMessageReceiver + */ + static enum MessagePropertiesKey { + objectType + } +} diff --git a/zanata-war/src/main/java/org/zanata/rest/editor/dto/Locale.java b/zanata-war/src/main/java/org/zanata/rest/editor/dto/Locale.java index f64a3e6d62..7b0939c888 100644 --- a/zanata-war/src/main/java/org/zanata/rest/editor/dto/Locale.java +++ b/zanata-war/src/main/java/org/zanata/rest/editor/dto/Locale.java @@ -1,6 +1,7 @@ package org.zanata.rest.editor.dto; -import java.io.Serializable;import java.lang.String; +import java.io.Serializable; +import java.lang.String; import javax.validation.constraints.NotNull; import javax.xml.bind.annotation.XmlAttribute; diff --git a/zanata-war/src/main/java/org/zanata/rest/editor/dto/TransUnitStatus.java b/zanata-war/src/main/java/org/zanata/rest/editor/dto/TransUnitStatus.java index 1998ccde43..66a6f975fe 100644 --- a/zanata-war/src/main/java/org/zanata/rest/editor/dto/TransUnitStatus.java +++ b/zanata-war/src/main/java/org/zanata/rest/editor/dto/TransUnitStatus.java @@ -1,6 +1,8 @@ package org.zanata.rest.editor.dto; -import java.io.Serializable;import java.lang.Long;import java.lang.String; +import java.io.Serializable; +import java.lang.Long; +import java.lang.String; import javax.validation.constraints.NotNull; @@ -8,7 +10,6 @@ import org.codehaus.jackson.annotate.JsonPropertyOrder; import org.codehaus.jackson.map.annotate.JsonSerialize; import org.zanata.common.ContentState; -import org.zanata.common.LocaleId; @JsonIgnoreProperties(ignoreUnknown = true) @JsonPropertyOrder({ "id", "resId", "status"}) diff --git a/zanata-war/src/main/java/org/zanata/rest/editor/dto/TranslationData.java b/zanata-war/src/main/java/org/zanata/rest/editor/dto/TranslationData.java index c1ec3f6fde..c7f803581c 100644 --- a/zanata-war/src/main/java/org/zanata/rest/editor/dto/TranslationData.java +++ b/zanata-war/src/main/java/org/zanata/rest/editor/dto/TranslationData.java @@ -81,13 +81,10 @@ public void setPlural(boolean plural) { @JsonIgnore public List getContents() { - if (content != null) { - return Lists.newArrayList(content); - } else if (contents != null) { - return contents; - } else { - return Collections.emptyList(); + if(contents == null && content == null) { + return Collections.emptyList(); } + return isPlural() ? contents : Lists.newArrayList(content); } @JsonIgnore diff --git a/zanata-war/src/main/java/org/zanata/rest/editor/dto/User.java b/zanata-war/src/main/java/org/zanata/rest/editor/dto/User.java index 14c15647e7..f0a03847ce 100644 --- a/zanata-war/src/main/java/org/zanata/rest/editor/dto/User.java +++ b/zanata-war/src/main/java/org/zanata/rest/editor/dto/User.java @@ -1,7 +1,8 @@ package org.zanata.rest.editor.dto; import java.io.Serializable; -import java.lang.String;import java.util.HashSet; +import java.lang.String; +import java.util.HashSet; import java.util.Set; import javax.validation.constraints.NotNull; diff --git a/zanata-war/src/main/java/org/zanata/rest/editor/service/TransUnitService.java b/zanata-war/src/main/java/org/zanata/rest/editor/service/TransUnitService.java index 4419827d71..97bcb59adb 100644 --- a/zanata-war/src/main/java/org/zanata/rest/editor/service/TransUnitService.java +++ b/zanata-war/src/main/java/org/zanata/rest/editor/service/TransUnitService.java @@ -77,8 +77,7 @@ public Response get(String localeId, String ids) { if (result.length < 2 || result[1] == null) { tu = transUnitUtils.buildTransUnitFull(textFlow, null, locale.getLocaleId()); - } - else { + } else { HTextFlowTarget textFlowTarget = (HTextFlowTarget) result[1]; tu = transUnitUtils.buildTransUnitFull(textFlow, textFlowTarget, locale.getLocaleId()); diff --git a/zanata-war/src/main/java/org/zanata/rest/service/AsynchronousProcessResourceService.java b/zanata-war/src/main/java/org/zanata/rest/service/AsynchronousProcessResourceService.java index 86e4af89ab..f444dbdf9d 100644 --- a/zanata-war/src/main/java/org/zanata/rest/service/AsynchronousProcessResourceService.java +++ b/zanata-war/src/main/java/org/zanata/rest/service/AsynchronousProcessResourceService.java @@ -161,7 +161,8 @@ public ProcessStatus startTranslatedDocCreationOrUpdate( final String idNoSlash, final String projectSlug, final String iterationSlug, final LocaleId locale, final TranslationsResource translatedDoc, - final Set extensions, final String merge) { + final Set extensions, final String merge, + final boolean assignCreditToUploader) { // check security (cannot be on @Restrict as it refers to method // parameters) identity.checkPermission("modify-translation", this.localeServiceImpl @@ -182,12 +183,11 @@ public ProcessStatus startTranslatedDocCreationOrUpdate( final String id = URIHelper.convertFromDocumentURIId(idNoSlash); final MergeType finalMergeType = mergeType; - String name = "TranslatedDocCreationOrUpdate: "+projectSlug+"-"+iterationSlug+"-"+idNoSlash+"-"+locale; AsyncTaskHandle handle = new AsyncTaskHandle(); Serializable taskId = asyncTaskHandleManager.registerTaskHandle(handle); translationServiceImpl.translateAllInDocAsync(projectSlug, iterationSlug, id, locale, translatedDoc, extensions, - finalMergeType, true, handle); + finalMergeType, assignCreditToUploader, true, handle); return this.getProcessStatus(taskId.toString()); } diff --git a/zanata-war/src/main/java/org/zanata/rest/service/FileService.java b/zanata-war/src/main/java/org/zanata/rest/service/FileService.java index d2b464f725..02ffe2fcc1 100644 --- a/zanata-war/src/main/java/org/zanata/rest/service/FileService.java +++ b/zanata-war/src/main/java/org/zanata/rest/service/FileService.java @@ -120,10 +120,14 @@ public Response uploadSourceFile(String projectSlug, String iterationSlug, public Response uploadTranslationFile(String projectSlug, String iterationSlug, String localeId, String docId, String merge, DocumentFileUploadForm uploadForm) { + + //assignCreditToUploader is not supported from here + boolean assignCreditToUploader = false; + GlobalDocumentId id = new GlobalDocumentId(projectSlug, iterationSlug, docId); return translationUploader.tryUploadTranslationFile(id, localeId, - merge, uploadForm); + merge, assignCreditToUploader , uploadForm); } @Override diff --git a/zanata-war/src/main/java/org/zanata/rest/service/ProjectIterationService.java b/zanata-war/src/main/java/org/zanata/rest/service/ProjectIterationService.java index 9ca97ded42..62e369e019 100644 --- a/zanata-war/src/main/java/org/zanata/rest/service/ProjectIterationService.java +++ b/zanata-war/src/main/java/org/zanata/rest/service/ProjectIterationService.java @@ -166,14 +166,12 @@ public Response put(ProjectIteration projectIteration) { if (hProject == null) { return Response.status(Status.NOT_FOUND) .entity("Project '" + projectSlug + "' not found.").build(); - } - // Project is Obsolete - else if (Objects.equal(hProject.getStatus(), OBSOLETE)) { + } else if (Objects.equal(hProject.getStatus(), OBSOLETE)) { + // Project is Obsolete return Response.status(Status.NOT_FOUND) .entity("Project '" + projectSlug + "' not found.").build(); - } - // Project is ReadOnly - else if (Objects.equal(hProject.getStatus(), READONLY)) { + } else if (Objects.equal(hProject.getStatus(), READONLY)) { + // Project is ReadOnly return Response.status(Status.FORBIDDEN) .entity("Project '" + projectSlug + "' is read-only.") .build(); @@ -202,16 +200,14 @@ else if (Objects.equal(hProject.getStatus(), READONLY)) { response = Response.created(uri.getAbsolutePath()); changed = true; - } - // Iteration is Obsolete - else if (Objects.equal(hProjectIteration.getStatus(), OBSOLETE)) { + } else if (Objects.equal(hProjectIteration.getStatus(), OBSOLETE)) { + // Iteration is Obsolete return Response .status(Status.FORBIDDEN) .entity("Project Iiteration '" + projectSlug + ":" + iterationSlug + "' is obsolete.").build(); - } - // Iteration is ReadOnly - else if (Objects.equal(hProjectIteration.getStatus(), READONLY)) { + } else if (Objects.equal(hProjectIteration.getStatus(), READONLY)) { + // Iteration is ReadOnly return Response .status(Status.FORBIDDEN) .entity("Project Iteration '" + projectSlug + ":" diff --git a/zanata-war/src/main/java/org/zanata/rest/service/ProjectService.java b/zanata-war/src/main/java/org/zanata/rest/service/ProjectService.java index 19adc6c7bb..2d2e4dd05c 100644 --- a/zanata-war/src/main/java/org/zanata/rest/service/ProjectService.java +++ b/zanata-war/src/main/java/org/zanata/rest/service/ProjectService.java @@ -121,15 +121,13 @@ public Response put(Project project) { identity.checkPermission(hProject, "insert"); response = Response.created(uri.getAbsolutePath()); - } - // Project is obsolete - else if (Objects.equal(hProject.getStatus(), OBSOLETE)) { + } else if (Objects.equal(hProject.getStatus(), OBSOLETE)) { + // Project is obsolete return Response.status(Status.FORBIDDEN) .entity("Project '" + projectSlug + "' is obsolete.") .build(); - } - // Project is ReadOnly - else if (Objects.equal(hProject.getStatus(), READONLY)) { + } else if (Objects.equal(hProject.getStatus(), READONLY)) { + // Project is ReadOnly return Response.status(Status.FORBIDDEN) .entity("Project '" + projectSlug + "' is read-only.") .build(); diff --git a/zanata-war/src/main/java/org/zanata/rest/service/ResourceUtils.java b/zanata-war/src/main/java/org/zanata/rest/service/ResourceUtils.java index c85d23a703..9274ce0463 100644 --- a/zanata-war/src/main/java/org/zanata/rest/service/ResourceUtils.java +++ b/zanata-war/src/main/java/org/zanata/rest/service/ResourceUtils.java @@ -23,6 +23,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import javax.annotation.Nonnull; import javax.annotation.PostConstruct; import javax.persistence.EntityManager; import javax.ws.rs.WebApplicationException; @@ -44,6 +45,7 @@ import org.zanata.common.LocaleId; import org.zanata.common.MergeType; import org.zanata.common.ResourceType; +import org.zanata.dao.LocaleDAO; import org.zanata.model.HDocument; import org.zanata.model.HLocale; import org.zanata.model.HPerson; @@ -102,6 +104,8 @@ public class ResourceUtils { private static final String CONTENT_TYPE_HDR = HeaderFields.KEY_ContentType; private static final String PLURAL_FORMS_HDR = "Plural-Forms"; + private static final Pattern PLURAL_FORM_PATTERN = + Pattern.compile("nplurals=[0-9]+;\\s?plural=*"); private static final Pattern NPLURALS_TAG_PATTERN = Pattern .compile("nplurals="); private static final Pattern NPLURALS_PATTERN = Pattern @@ -109,13 +113,16 @@ public class ResourceUtils { private static final String PLURALS_FILE = "pluralforms.properties"; private static final String DEFAULT_PLURAL_FORM = "nplurals=1; plural=0"; - // private static int MAX_TARGET_CONTENTS = 6; + public static final int MAX_TARGET_CONTENTS = 6; private static Properties pluralForms; @In private EntityManager entityManager; + @In + private LocaleDAO localeDAO; + @PostConstruct public void create() { try { @@ -846,7 +853,7 @@ private HTextFlowTarget getLastTranslatedTarget( HTextFlowTarget lastTranslated = null; for (HTextFlowTarget trans : translations) { - if (trans.getLastModifiedBy() != null + if (trans.getTranslator() != null && trans.getLastChanged().after(lastUpdate)) { lastTranslated = trans; lastUpdate = trans.getLastChanged(); @@ -872,21 +879,21 @@ private String getLastTranslator(final HTextFlowTarget lastTranslated, String lastTranslator = this.getHeaderLastTranslator(headerEntries); if (lastTranslated != null) { - HPerson lastModifiedBy = lastTranslated.getLastModifiedBy(); + HPerson lastTranslatedBy = lastTranslated.getTranslator(); Date lastModifiedDate = lastTranslated.getLastChanged(); // Last translated target is more recent than the Revision Date on // the // Header - if (lastModifiedBy != null && lastModifiedDate != null + if (lastTranslatedBy != null && lastModifiedDate != null && lastModifiedDate.after(headerRevisionDate)) { lastTranslator = - lastModifiedBy.getName() + " <" - + lastModifiedBy.getEmail() + ">"; - } else if (lastModifiedBy != null && lastModifiedDate == null) { + lastTranslatedBy.getName() + " <" + + lastTranslatedBy.getEmail() + ">"; + } else if (lastTranslatedBy != null && lastModifiedDate == null) { lastTranslator = - lastModifiedBy.getName() + " <" - + lastModifiedBy.getEmail() + ">"; + lastTranslatedBy.getName() + " <" + + lastTranslatedBy.getEmail() + ">"; } } @@ -954,18 +961,28 @@ public String getPluralForms(HLocale locale) { * default value if there is no plural form information for the provided * locale id. * - * @see {@link ResourceUtils#getPluralForms(org.zanata.common.LocaleId, boolean)} + * @see {@link ResourceUtils#getPluralForms(org.zanata.common.LocaleId, boolean, boolean)} */ public String getPluralForms(LocaleId localeId) { - return getPluralForms(localeId, true); + return getPluralForms(localeId, true, true); } /** * Returns the appropriate plural from for a given locale Id. * + * From HLocale.plurals if available, + * else from pluralforms.properties + * * @return A default value if useDefault is True. Otherwise, null. */ - public String getPluralForms(LocaleId localeId, boolean useDefault) { + public String getPluralForms(LocaleId localeId, boolean checkDB, boolean useDefault) { + if(checkDB) { + String dbPluralForms = getPluralFormsFromDB(localeId); + if (StringUtils.isNotEmpty(dbPluralForms)) { + return dbPluralForms; + } + } + final char[] alternateSeparators = { '.', '@' }; String javaLocale = localeId.toJavaName().toLowerCase(); @@ -1007,50 +1024,135 @@ private int getNPluralForms(String entries, HLocale targetLocale) { return getNPluralForms(entries, targetLocale.getLocaleId()); } + /** + * return plural count info from + * 1) Header entry if available, else + * 2) HLocale.plurals if available, else + * 3) pluralforms.properties + * + * @param entries - HPoTargetHeader.entries + * @param localeId - locale identifier + * + */ int getNPluralForms(String entries, LocaleId localeId) { - int nPlurals = 1; - + String pluralForms; try { + if (entries == null || entries.isEmpty()) { + pluralForms = getPluralFormsFromDB(localeId); + return StringUtils.isEmpty(pluralForms) ? + getNPluralForms(localeId) : + getNPluralForms(pluralForms); + } + Properties headerList = new Properties(); - String pluralForms; - if (entries != null && !entries.isEmpty()) { - headerList.load(new StringReader(entries)); - if (headerList.containsKey(PLURAL_FORMS_HDR)) { - pluralForms = headerList.getProperty(PLURAL_FORMS_HDR); - } else { - pluralForms = getPluralForms(localeId); - } - } else { - pluralForms = getPluralForms(localeId); + headerList.load(new StringReader(entries)); + + if (!headerList.containsKey(PLURAL_FORMS_HDR)) { + pluralForms = getPluralFormsFromDB(localeId); + return StringUtils.isEmpty(pluralForms) ? + getNPluralForms(localeId) : + getNPluralForms(pluralForms); } - if (pluralForms == null) { + + pluralForms = headerList.getProperty(PLURAL_FORMS_HDR); + + if (StringUtils.isEmpty(pluralForms)) { log.error("No plural forms for locale {} found in {}", localeId, PLURALS_FILE); throw new RuntimeException( "No plural forms found; contact admin. Locale: " + localeId); } - Matcher nPluralsMatcher = NPLURALS_PATTERN.matcher(pluralForms); - String nPluralsString = ""; - while (nPluralsMatcher.find()) { - nPluralsString = nPluralsMatcher.group(); - Matcher nPluralsValueMatcher = - NPLURALS_TAG_PATTERN.matcher(nPluralsString); - nPluralsString = nPluralsValueMatcher.replaceAll(""); - break; - } - if (nPluralsString != null && !nPluralsString.isEmpty()) { - nPlurals = Integer.parseInt(nPluralsString); - } + return getNPluralForms(pluralForms); } catch (Exception e) { log.error("Error getting nPlurals:" + entries); } + return 1; + } + + /** + * @return plural forms from HLocale, null if not found + */ + String getPluralFormsFromDB(LocaleId localeId) { + HLocale hLocale = localeDAO.findByLocaleId(localeId); + if(hLocale != null && StringUtils.isNotEmpty(hLocale.getPluralForms())) { + return hLocale.getPluralForms(); + } + return null; + } - // nPlurals = (nPlurals > MAX_TARGET_CONTENTS || nPlurals < 1) ? 1 : - // nPlurals; + /** + * Get plurals count from pluralforms.properties + * + * @param localeId + */ + int getNPluralForms(@Nonnull LocaleId localeId) { + String pluralForms = getPluralForms(localeId); + + if (pluralForms == null) { + log.error("No plural forms for locale {} found in {}", + localeId, PLURALS_FILE); + throw new RuntimeException( + "No plural forms found; contact admin. Locale: " + + localeId); + } + return getNPluralForms(pluralForms); + } + + /** + * Process pluralforms string and return plural count. + * + * @param pluralForms + */ + int getNPluralForms(@Nonnull String pluralForms) { + int nPlurals = 1; + + Matcher nPluralsMatcher = NPLURALS_PATTERN.matcher(pluralForms); + String nPluralsString = ""; + while (nPluralsMatcher.find()) { + nPluralsString = nPluralsMatcher.group(); + Matcher nPluralsValueMatcher = + NPLURALS_TAG_PATTERN.matcher(nPluralsString); + nPluralsString = nPluralsValueMatcher.replaceAll(""); + break; + } + if (StringUtils.isNotEmpty(nPluralsString)) { + nPlurals = Integer.parseInt(nPluralsString); + } return nPlurals; } + /** + * Return if pluralForms is valid (positive value) + * + * @param pluralForms + */ + public boolean isValidPluralForms(@Nonnull String pluralForms) { + + if(!PLURAL_FORM_PATTERN.matcher(pluralForms).find()) { + return false; + } + + Matcher nPluralsMatcher = NPLURALS_PATTERN.matcher(pluralForms); + String nPluralsString = ""; + while (nPluralsMatcher.find()) { + nPluralsString = nPluralsMatcher.group(); + Matcher nPluralsValueMatcher = + NPLURALS_TAG_PATTERN.matcher(nPluralsString); + nPluralsString = nPluralsValueMatcher.replaceAll(""); + break; + } + try { + if (StringUtils.isNotEmpty(nPluralsString)) { + int count = Integer.parseInt(nPluralsString); + return count >= 1 && count <= MAX_TARGET_CONTENTS; + } + } catch (Exception e) { + //invalid string for integer + } + return false; + } + /** * * @param fromHeader @@ -1265,7 +1367,7 @@ public void transferToTextFlowTarget(HTextFlowTarget from, to.setState(mapContentState(apiVersion, from.getState())); to.setRevision(from.getVersionNum()); to.setTextFlowRevision(from.getTextFlowRevision()); - HPerson translator = from.getLastModifiedBy(); + HPerson translator = from.getTranslator(); if (translator != null) { to.setTranslator(new Person(translator.getEmail(), translator .getName())); diff --git a/zanata-war/src/main/java/org/zanata/rest/service/TranslatedDocResourceService.java b/zanata-war/src/main/java/org/zanata/rest/service/TranslatedDocResourceService.java index 6e3896895d..6812554912 100644 --- a/zanata-war/src/main/java/org/zanata/rest/service/TranslatedDocResourceService.java +++ b/zanata-war/src/main/java/org/zanata/rest/service/TranslatedDocResourceService.java @@ -264,11 +264,14 @@ public Response putTranslations(String idNoSlash, LocaleId locale, return response.build(); } + //assignCreditToUploader is not supported from here + boolean assignCreditToUploader = false; + // Translate List warnings = this.translationServiceImpl.translateAllInDoc(projectSlug, iterationSlug, id, locale, messageBody, extensions, - mergeType); + mergeType, assignCreditToUploader); // Regenerate etag in case it has changed etag = diff --git a/zanata-war/src/main/java/org/zanata/security/AuthenticationManager.java b/zanata-war/src/main/java/org/zanata/security/AuthenticationManager.java index 3d7cdaa30a..f359afebef 100644 --- a/zanata-war/src/main/java/org/zanata/security/AuthenticationManager.java +++ b/zanata-war/src/main/java/org/zanata/security/AuthenticationManager.java @@ -305,21 +305,21 @@ public void onLoginCompleted(AuthenticationType authType) { userAccountServiceImpl.runRoleAssignmentRules(authenticatedAccount, authenticatedCredentials, authType.name()); } + // make sure server path is populated. Here we are sure servlet request + // is available. In cases where it's not in database and + // there is no servlet request, the value will not be null. + // e.g. EmailBuilder triggered by JMS message + applicationConfiguration.getServerPath(); } public boolean isAccountWaitingForActivation(String username) { HAccount account = accountDAO.getByUsername(username); - if (account != null && account.getAccountActivationKey() != null) { - return true; - } - return false; + return account != null && account.getAccountActivationKey() != null; } public boolean isAccountEnabled(String username) { - if (StringUtils.isEmpty(username)) { - return false; - } - return identityStore.isUserEnabled(username); + return !StringUtils.isEmpty(username) && + identityStore.isUserEnabled(username); } public boolean isAuthenticatedAccountWaitingForActivation() { diff --git a/zanata-war/src/main/java/org/zanata/security/JBossSSOLoginModule.java b/zanata-war/src/main/java/org/zanata/security/JBossSSOLoginModule.java index 67a02a7b0b..48cdd04c6e 100644 --- a/zanata-war/src/main/java/org/zanata/security/JBossSSOLoginModule.java +++ b/zanata-war/src/main/java/org/zanata/security/JBossSSOLoginModule.java @@ -111,8 +111,7 @@ public boolean login() throws LoginException { //parsedResponse.get("email"); log.info("JBoss.org user " + username + " successfully authenticated"); - } - else { + } else { log.info("JBoss.org user " + username + " failed authentication"); } diff --git a/zanata-war/src/main/java/org/zanata/security/ZanataOpenId.java b/zanata-war/src/main/java/org/zanata/security/ZanataOpenId.java index f45bb65189..1bcc4cfa96 100644 --- a/zanata-war/src/main/java/org/zanata/security/ZanataOpenId.java +++ b/zanata-war/src/main/java/org/zanata/security/ZanataOpenId.java @@ -309,13 +309,12 @@ public void afterOpenIdAuth(OpenIdAuthenticationResult result) { Identity.instance().acceptExternallyAuthenticatedPrincipal( (new OpenIdPrincipal(result.getAuthenticatedId()))); this.loginImmediate(); - } - // If the user hasn't been registered yet - else if (authenticatedAccount == null) { - credentials.setUsername(result.getAuthenticatedId()); // this is - // the - // full - // open id + } else if(authenticatedAccount != null) { + credentials.setUsername(authenticatedAccount.getUsername()); + } else if (authenticatedAccount == null) { + // If the user hasn't been registered yet + // this is the full open id + credentials.setUsername(result.getAuthenticatedId()); } } } diff --git a/zanata-war/src/main/java/org/zanata/security/permission/CustomPermissionResolver.java b/zanata-war/src/main/java/org/zanata/security/permission/CustomPermissionResolver.java index 0e14621de7..cabd62a57c 100644 --- a/zanata-war/src/main/java/org/zanata/security/permission/CustomPermissionResolver.java +++ b/zanata-war/src/main/java/org/zanata/security/permission/CustomPermissionResolver.java @@ -57,8 +57,7 @@ public boolean hasPermission(Object target, String action) { Object[] targetArray; if (target instanceof MultiTargetList) { targetArray = ((MultiTargetList) target).toArray(); - } - else { + } else { targetArray = new Object[] { target }; } diff --git a/zanata-war/src/main/java/org/zanata/service/SearchIndexManager.java b/zanata-war/src/main/java/org/zanata/service/SearchIndexManager.java index e437390596..54ebc5653c 100644 --- a/zanata-war/src/main/java/org/zanata/service/SearchIndexManager.java +++ b/zanata-war/src/main/java/org/zanata/service/SearchIndexManager.java @@ -5,8 +5,6 @@ import java.util.LinkedHashMap; import java.util.List; -import javax.persistence.EntityManagerFactory; - import lombok.extern.slf4j.Slf4j; import org.jboss.seam.ScopeType; @@ -37,18 +35,16 @@ public class SearchIndexManager implements Serializable { private static final long serialVersionUID = 1L; @In - EntityManagerFactory entityManagerFactory; - - @In - AsyncTaskHandleManager asyncTaskHandleManager; + private AsyncTaskHandleManager asyncTaskHandleManager; @In - IndexingService indexingServiceImpl; + private IndexingService indexingServiceImpl; // we use a list to ensure predictable order private final List> indexables = new ArrayList>(); private final LinkedHashMap, ReindexClassOptions> indexingOptions = new LinkedHashMap, ReindexClassOptions>(); + private Class currentClass; private AsyncTaskHandle handle; @@ -111,6 +107,8 @@ public AsyncTaskHandle getProcessHandle() { return handle; } + //TODO: current class is not implemented now, should be getting the value + // from indexingServiceImpl public String getCurrentClassName() { if (currentClass == null) { return "none"; @@ -128,8 +126,7 @@ public void startProcess() { asyncTaskHandleManager.registerTaskHandle(handle); try { indexingServiceImpl.startIndexing(indexingOptions, handle); - } - catch (Exception e) { + } catch (Exception e) { // If this happens, it's because of a problem with the async // framework throw new RuntimeException(e); diff --git a/zanata-war/src/main/java/org/zanata/service/TranslationService.java b/zanata-war/src/main/java/org/zanata/service/TranslationService.java index 0243a9a971..736984c8cb 100644 --- a/zanata-war/src/main/java/org/zanata/service/TranslationService.java +++ b/zanata-war/src/main/java/org/zanata/service/TranslationService.java @@ -91,6 +91,9 @@ List revertTranslations(LocaleId localeId, * Indicates how to handle the translations. AUTO will merge the * new translations with the provided ones. IMPORT will overwrite * all existing translations with the new ones. + * @param assignCreditToUploader + * The translator field for all uploaded translations will be + * set to the user who performs the upload. * @param lock * If true, no other caller will be allowed to translate All for * the same project, iteration, document and locale. @@ -102,7 +105,7 @@ List revertTranslations(LocaleId localeId, public Future> translateAllInDocAsync(String projectSlug, String iterationSlug, String docId, LocaleId locale, TranslationsResource translations, Set extensions, - MergeType mergeType, boolean lock, + MergeType mergeType, boolean assignCreditToUploader, boolean lock, AsyncTaskHandle handle); /** @@ -124,17 +127,22 @@ public Future> translateAllInDocAsync(String projectSlug, * Indicates how to handle the translations. AUTO will merge the * new translations with the provided ones. IMPORT will overwrite * all existing translations with the new ones. + * @param assignCreditToUploader + * The translator field for all uploaded translations will be + * set to the user who performs the upload. * @return A list of warnings about text flow targets that (a) could not be * matched to any text flows in the source document or (b) whose * states don't match their contents. */ List translateAllInDoc(String projectSlug, String iterationSlug, String docId, LocaleId locale, TranslationsResource translations, - Set extensions, MergeType mergeType, AsyncTaskHandle handle); + Set extensions, MergeType mergeType, + boolean assignCreditToUploader, AsyncTaskHandle handle); List translateAllInDoc(String projectSlug, String iterationSlug, String docId, LocaleId locale, TranslationsResource translations, - Set extensions, MergeType mergeType); + Set extensions, MergeType mergeType, + boolean assignCreditToUploader); public interface TranslationResult { boolean isTranslationSuccessful(); diff --git a/zanata-war/src/main/java/org/zanata/service/impl/CopyTransServiceImpl.java b/zanata-war/src/main/java/org/zanata/service/impl/CopyTransServiceImpl.java index 13c7019f3d..4ca8e29f1c 100644 --- a/zanata-war/src/main/java/org/zanata/service/impl/CopyTransServiceImpl.java +++ b/zanata-war/src/main/java/org/zanata/service/impl/CopyTransServiceImpl.java @@ -147,7 +147,7 @@ private void copyTransForDocumentLocale(HDocument document, } // If there have been any changes to the document stats, reset them - if( numCopied > 0 ) { + if (numCopied > 0) { translationStateCacheImpl.clearDocumentStatistics(document.getId(), targetLocale.getLocaleId()); } @@ -211,7 +211,7 @@ public void copyTransForDocument(HDocument document, Optional taskHandleOpt = Optional.fromNullable(handle); - if( taskHandleOpt.isPresent() ) { + if (taskHandleOpt.isPresent()) { prepareCopyTransHandle(document, taskHandleOpt.get()); } @@ -269,7 +269,7 @@ public void copyTransForIteration(HProjectIteration iteration, Optional taskHandleOpt = Optional.fromNullable(handle); - if( taskHandleOpt.isPresent() ) { + if (taskHandleOpt.isPresent()) { prepareCopyTransHandle(iteration, taskHandleOpt.get()); } @@ -295,18 +295,18 @@ public void copyTransForIteration(HProjectIteration iteration, } } - private void prepareCopyTransHandle( HProjectIteration iteration, CopyTransTaskHandle handle ) { - if( !handle.isPrepared() ) { + private void prepareCopyTransHandle(HProjectIteration iteration, CopyTransTaskHandle handle) { + if (!handle.isPrepared()) { // TODO Progress should be handle as long - handle.setMaxProgress( (int)getMaxProgress(iteration) ); + handle.setMaxProgress((int) getMaxProgress(iteration)); handle.setPrepared(); } } - private void prepareCopyTransHandle( HDocument document, CopyTransTaskHandle handle ) { - if( !handle.isPrepared() ) { + private void prepareCopyTransHandle(HDocument document, CopyTransTaskHandle handle) { + if (!handle.isPrepared()) { // TODO Progress should be handle as long - handle.setMaxProgress( (int)getMaxProgress(document) ); + handle.setMaxProgress((int) getMaxProgress(document)); handle.setPrepared(); } } diff --git a/zanata-war/src/main/java/org/zanata/service/impl/CopyVersionServiceImpl.java b/zanata-war/src/main/java/org/zanata/service/impl/CopyVersionServiceImpl.java index 28baba6ac1..7327c836a2 100644 --- a/zanata-war/src/main/java/org/zanata/service/impl/CopyVersionServiceImpl.java +++ b/zanata-war/src/main/java/org/zanata/service/impl/CopyVersionServiceImpl.java @@ -98,7 +98,7 @@ public void copyVersion(@Nonnull String projectSlug, return; } - if( taskHandleOpt.isPresent() ) { + if (taskHandleOpt.isPresent()) { prepareCopyVersionHandle(version, taskHandleOpt.get()); } @@ -362,7 +362,7 @@ JPACopier. copyBean(tft, "textFlow", for (HTextFlowTargetHistory history : tft.getHistory().values()) { HTextFlowTargetHistory newHistory = JPACopier. copyBean(history, - "textFlowTarget"); + "textFlowTarget", "content"); newHistory.setTextFlowTarget(copy); copy.getHistory().put(newHistory.getVersionNum(), newHistory); } diff --git a/zanata-war/src/main/java/org/zanata/service/impl/DocumentServiceImpl.java b/zanata-war/src/main/java/org/zanata/service/impl/DocumentServiceImpl.java index 24780cdf8b..641de67467 100644 --- a/zanata-war/src/main/java/org/zanata/service/impl/DocumentServiceImpl.java +++ b/zanata-war/src/main/java/org/zanata/service/impl/DocumentServiceImpl.java @@ -62,6 +62,7 @@ import org.zanata.service.TranslationStateCache; import org.zanata.service.VersionStateCache; import org.zanata.ui.model.statistic.WordStatistic; +import org.zanata.util.StatisticsUtil; import com.google.common.annotations.VisibleForTesting; import lombok.extern.slf4j.Slf4j; @@ -244,23 +245,32 @@ private void processWebHookDocumentMilestoneEvent( Collection contentStates, String message, int percentMilestone) { - boolean shouldPublish = - hasContentStateReachedMilestone(event.getOldStats(), - event.getNewStats(), contentStates, percentMilestone); - - if (shouldPublish) { - HProjectIteration version = - projectIterationDAO.findById(event.getProjectIterationId()); - HProject project = version.getProject(); - - if (!project.getWebHooks().isEmpty()) { - HDocument document = documentDAO.getById(event.getDocumentId()); - DocumentMilestoneEvent milestoneEvent = - new DocumentMilestoneEvent(project.getSlug(), - version.getSlug(), document.getDocId(), - event.getLocaleId(), message); - for (WebHook webHook : project.getWebHooks()) { - publishDocumentMilestoneEvent(webHook, milestoneEvent); + HProjectIteration version = + projectIterationDAO.findById(event.getProjectIterationId()); + HProject project = version.getProject(); + + if (!project.getWebHooks().isEmpty()) { + WordStatistic stats = + translationStateCacheImpl.getDocumentStatistics( + event.getDocumentId(), event.getLocaleId()); + + WordStatistic oldStats = StatisticsUtil.copyWordStatistic(stats); + if(oldStats != null) { + oldStats.decrement(event.getNewState(), event.getWordCount()); + oldStats.increment(event.getPreviousState(), event.getWordCount()); + + boolean shouldPublish = hasContentStateReachedMilestone(oldStats, stats, + contentStates, percentMilestone); + + if (shouldPublish) { + HDocument document = documentDAO.getById(event.getDocumentId()); + DocumentMilestoneEvent milestoneEvent = + new DocumentMilestoneEvent(project.getSlug(), + version.getSlug(), document.getDocId(), + event.getLocaleId(), message); + for (WebHook webHook : project.getWebHooks()) { + publishDocumentMilestoneEvent(webHook, milestoneEvent); + } } } } @@ -321,9 +331,11 @@ private void clearStatsCacheForUpdatedDocument(HDocument document) { @VisibleForTesting public void init(ProjectIterationDAO projectIterationDAO, - DocumentDAO documentDAO, Messages msgs) { + DocumentDAO documentDAO, + TranslationStateCache translationStateCacheImpl, Messages msgs) { this.projectIterationDAO = projectIterationDAO; this.documentDAO = documentDAO; + this.translationStateCacheImpl = translationStateCacheImpl; this.msgs = msgs; } } diff --git a/zanata-war/src/main/java/org/zanata/service/impl/IndexingServiceImpl.java b/zanata-war/src/main/java/org/zanata/service/impl/IndexingServiceImpl.java index 924cac5add..470e602434 100644 --- a/zanata-war/src/main/java/org/zanata/service/impl/IndexingServiceImpl.java +++ b/zanata-war/src/main/java/org/zanata/service/impl/IndexingServiceImpl.java @@ -83,7 +83,6 @@ public Future startIndexing( if (!handle.isCancelled() && indexingOptions.get(clazz).isPurge()) { log.info("purging index for {}", clazz); - // currentClass = clazz; session.purgeAll(clazz); handle.increaseProgress(1); } diff --git a/zanata-war/src/main/java/org/zanata/service/impl/LanguageTeamServiceImpl.java b/zanata-war/src/main/java/org/zanata/service/impl/LanguageTeamServiceImpl.java index 55cbc717d0..9e17d9057e 100644 --- a/zanata-war/src/main/java/org/zanata/service/impl/LanguageTeamServiceImpl.java +++ b/zanata-war/src/main/java/org/zanata/service/impl/LanguageTeamServiceImpl.java @@ -6,11 +6,15 @@ import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; +import org.jboss.seam.core.Events; +import org.jboss.seam.security.management.JpaIdentityStore; import org.zanata.common.LocaleId; import org.zanata.dao.LocaleDAO; import org.zanata.dao.LocaleMemberDAO; import org.zanata.dao.PersonDAO; +import org.zanata.events.LanguageTeamPermissionChangedEvent; import org.zanata.exception.ZanataServiceException; +import org.zanata.model.HAccount; import org.zanata.model.HLocale; import org.zanata.model.HLocaleMember; import org.zanata.model.HLocaleMember.HLocaleMemberPk; @@ -20,26 +24,17 @@ @Name("languageTeamServiceImpl") @Scope(ScopeType.STATELESS) public class LanguageTeamServiceImpl implements LanguageTeamService { + @In private PersonDAO personDAO; - private LocaleDAO localeDAO; - - private LocaleMemberDAO localeMemberDAO; - @In - public void setPersonDAO(PersonDAO personDAO) { - this.personDAO = personDAO; - } + private LocaleDAO localeDAO; @In - public void setLocaleDAO(LocaleDAO localeDAO) { - this.localeDAO = localeDAO; - } + private LocaleMemberDAO localeMemberDAO; - @In - public void setLocaleMemberDAO(LocaleMemberDAO localeMemberDAO) { - this.localeMemberDAO = localeMemberDAO; - } + @In(required = false, value = JpaIdentityStore.AUTHENTICATED_USER, scope = ScopeType.SESSION) + private HAccount authenticatedAccount; public List getLanguageMemberships(String userName) { return personDAO.getLanguageMembershipByUsername(userName); @@ -54,6 +49,9 @@ public void joinOrUpdateRoleInLanguageTeam(String locale, Long personId, boolean alreadyJoined = localeMemberDAO.isLocaleMember(personId, localeId); HLocaleMember localeMember; + LanguageTeamPermissionChangedEvent permissionChangedEvent; + + HPerson authenticatedUser = authenticatedAccount.getPerson(); if (!alreadyJoined) { if (currentPerson.getLanguageMemberships().size() >= MAX_NUMBER_MEMBERSHIP) { throw new ZanataServiceException( @@ -66,15 +64,30 @@ public void joinOrUpdateRoleInLanguageTeam(String locale, Long personId, new HLocaleMember(currentPerson, lang, isTranslator, isReviewer, isCoordinator); lang.getMembers().add(localeMember); + permissionChangedEvent = + new LanguageTeamPermissionChangedEvent(currentPerson, + localeId, authenticatedUser) + .joiningTheTeam(isTranslator, isReviewer, + isCoordinator); } else { localeMember = localeMemberDAO.findByPersonAndLocale(personId, localeId); + permissionChangedEvent = + new LanguageTeamPermissionChangedEvent(currentPerson, + localeId, authenticatedUser) + .updatingPermissions(localeMember, + isTranslator, isReviewer, isCoordinator); localeMember.setTranslator(isTranslator); localeMember.setReviewer(isReviewer); localeMember.setCoordinator(isCoordinator); } localeMemberDAO.makePersistent(localeMember); localeMemberDAO.flush(); + if (Events.exists()) { + Events.instance().raiseTransactionSuccessEvent( + LanguageTeamPermissionChangedEvent.LANGUAGE_TEAM_PERMISSION_CHANGED, + permissionChangedEvent); + } } public boolean leaveLanguageTeam(String locale, Long personId) { @@ -88,6 +101,17 @@ public boolean leaveLanguageTeam(String locale, Long personId) { localeMemberDAO.makeTransient(membership); lang.getMembers().remove(membership); localeMemberDAO.flush(); + if (Events.exists()) { + HPerson doneByPerson = authenticatedAccount.getPerson(); + Events.instance() + .raiseTransactionSuccessEvent( + LanguageTeamPermissionChangedEvent.LANGUAGE_TEAM_PERMISSION_CHANGED, + new LanguageTeamPermissionChangedEvent( + currentPerson, lang.getLocaleId(), + doneByPerson) + .updatingPermissions(membership, false, + false, false)); + } return true; } diff --git a/zanata-war/src/main/java/org/zanata/service/impl/LocaleServiceImpl.java b/zanata-war/src/main/java/org/zanata/service/impl/LocaleServiceImpl.java index 8d642808c2..2bcb256ee8 100644 --- a/zanata-war/src/main/java/org/zanata/service/impl/LocaleServiceImpl.java +++ b/zanata-war/src/main/java/org/zanata/service/impl/LocaleServiceImpl.java @@ -120,8 +120,10 @@ public void setVersionGroupDAO(VersionGroupDAO versionGroupDAO) { public List getAllLocales() { List hSupportedLanguages = localeDAO.findAll(); - if (hSupportedLanguages == null) - return new ArrayList(); + if (hSupportedLanguages == null) { + return Collections.EMPTY_LIST; + } + Collections.sort(hSupportedLanguages, ComparatorUtil.LOCALE_COMPARATOR); return hSupportedLanguages; } diff --git a/zanata-war/src/main/java/org/zanata/service/impl/StatisticsServiceImpl.java b/zanata-war/src/main/java/org/zanata/service/impl/StatisticsServiceImpl.java index dd77c87435..1a9b8cf06b 100644 --- a/zanata-war/src/main/java/org/zanata/service/impl/StatisticsServiceImpl.java +++ b/zanata-war/src/main/java/org/zanata/service/impl/StatisticsServiceImpl.java @@ -362,7 +362,7 @@ public ContributionStatistics getContributionStatistics(String projectSlug, if (localeStatistics.containsKey(localeId)) { stats = localeStatistics.get(localeId); } else { - stats = new BaseContributionStatistic(0,0,0,0); + stats = new BaseContributionStatistic(0, 0, 0, 0); } stats.set(state, count); localeStatistics.put(localeId, stats); diff --git a/zanata-war/src/main/java/org/zanata/service/impl/TranslationArchiveServiceImpl.java b/zanata-war/src/main/java/org/zanata/service/impl/TranslationArchiveServiceImpl.java index 9c70bebf6f..225596c5da 100644 --- a/zanata-war/src/main/java/org/zanata/service/impl/TranslationArchiveServiceImpl.java +++ b/zanata-war/src/main/java/org/zanata/service/impl/TranslationArchiveServiceImpl.java @@ -102,7 +102,7 @@ public String buildTranslationFileArchive(String projectSlug, Optional> handleOpt = Optional.fromNullable(handle); - if( handleOpt.isPresent() ) { + if (handleOpt.isPresent()) { prepareHandle(handleOpt.get(), projectSlug, iterationSlug); } boolean isPoProject = isPoProject(projectSlug, iterationSlug); @@ -166,7 +166,7 @@ public String buildTranslationFileArchive(String projectSlug, poWriter.writePo(zipOutput, "UTF-8", res, translationResource); zipOutput.closeEntry(); - if( handleOpt.isPresent() ) { + if (handleOpt.isPresent()) { handleOpt.get().increaseProgress(1); } } diff --git a/zanata-war/src/main/java/org/zanata/service/impl/TranslationServiceImpl.java b/zanata-war/src/main/java/org/zanata/service/impl/TranslationServiceImpl.java index e436267a7f..7ffe9a8425 100644 --- a/zanata-war/src/main/java/org/zanata/service/impl/TranslationServiceImpl.java +++ b/zanata-war/src/main/java/org/zanata/service/impl/TranslationServiceImpl.java @@ -78,7 +78,6 @@ import org.zanata.rest.dto.resource.TranslationsResource; import org.zanata.rest.service.ResourceUtils; import org.zanata.security.ZanataIdentity; -import org.zanata.service.ActivityService; import org.zanata.service.LocaleService; import org.zanata.service.LockManagerService; import org.zanata.service.TranslationMergeService; @@ -207,6 +206,7 @@ private List translate(LocaleId localeId, log.warn(validationMessage); result.isSuccess = false; result.errorMessage = validationMessage; + results.add(result); continue; } } @@ -501,7 +501,7 @@ private static boolean ensureContentsSize(HTextFlowTarget target, Future> translateAllInDocAsync(String projectSlug, String iterationSlug, String docId, LocaleId locale, TranslationsResource translations, Set extensions, - MergeType mergeType, boolean lock, + MergeType mergeType, boolean assignCreditToUploader, boolean lock, AsyncTaskHandle handle) { // Lock this document for push Lock transLock = null; @@ -515,7 +515,8 @@ Future> translateAllInDocAsync(String projectSlug, try { messages = this.translateAllInDoc(projectSlug, iterationSlug, docId, - locale, translations, extensions, mergeType, handle); + locale, translations, extensions, mergeType, + assignCreditToUploader, handle); } finally { if (lock) { lockManagerServiceImpl.release(transLock); @@ -563,9 +564,11 @@ private String validateTranslations(ContentState newState, public List translateAllInDoc(final String projectSlug, final String iterationSlug, final String docId, final LocaleId locale, final TranslationsResource translations, - final Set extensions, final MergeType mergeType) { + final Set extensions, final MergeType mergeType, + final boolean assignCreditToUploader) { return translateAllInDoc(projectSlug, iterationSlug, docId, locale, - translations, extensions, mergeType, null); + translations, extensions, mergeType, assignCreditToUploader, + null); } @Override @@ -573,7 +576,7 @@ public List translateAllInDoc(final String projectSlug, final String iterationSlug, final String docId, final LocaleId locale, final TranslationsResource translations, final Set extensions, final MergeType mergeType, - AsyncTaskHandle handle) { + final boolean assignCreditToUploader, AsyncTaskHandle handle) { final HProjectIteration hProjectIteration = projectIterationDAO.getBySlug(projectSlug, iterationSlug); @@ -660,6 +663,7 @@ protected Boolean work() throws Exception { work.setHandleOp(handleOp); work.setProjectIterationId(hProjectIteration.getId()); work.setBatch(batch); + work.setAssignCreditToUploader(assignCreditToUploader); changed |= work.workInTransaction(); } catch (Exception e) { throw new ZanataServiceException("Error during translation.", @@ -728,6 +732,7 @@ private final class SaveBatchWork extends Work { private Optional handleOp; private Long projectIterationId; private List batch; + private boolean assignCreditToUploader; @Override protected Boolean work() throws Exception { @@ -824,23 +829,14 @@ public String apply(TextFlowTarget input) { changed = true; Long actorId; - if (incomingTarget.getTranslator() != null) { - String email = - incomingTarget.getTranslator().getEmail(); - HPerson hPerson = personDAO.findByEmail(email); - if (hPerson == null) { - hPerson = new HPerson(); - hPerson.setEmail(email); - hPerson.setName(incomingTarget.getTranslator() - .getName()); - personDAO.makePersistent(hPerson); - } + if(assignCreditToUploader){ + HPerson hPerson = authenticatedAccount.getPerson(); hTarget.setTranslator(hPerson); hTarget.setLastModifiedBy(hPerson); actorId = hPerson.getId(); } else { hTarget.setTranslator(null); - hTarget.setLastModifiedBy(null); + hTarget.setLastModifiedBy(authenticatedAccount.getPerson()); actorId = null; } textFlowTargetDAO.makePersistent(hTarget); diff --git a/zanata-war/src/main/java/org/zanata/service/impl/TranslationStateCacheImpl.java b/zanata-war/src/main/java/org/zanata/service/impl/TranslationStateCacheImpl.java index 9c33c9adab..60a7bb8dd2 100644 --- a/zanata-war/src/main/java/org/zanata/service/impl/TranslationStateCacheImpl.java +++ b/zanata-war/src/main/java/org/zanata/service/impl/TranslationStateCacheImpl.java @@ -189,18 +189,8 @@ public void textFlowStateUpdated(TextFlowTargetStateEvent event) { new DocumentLocaleKey(event.getDocumentId(), event.getLocaleId()); - WordStatistic stats = documentStatisticCache.get(key); - - if (stats != null) { - HTextFlow textFlow = textFlowDAO.findById( - event.getTextFlowId()); - - stats.decrement(event.getPreviousState(), - textFlow.getWordCount().intValue()); - stats.increment(event.getNewState(), - textFlow.getWordCount().intValue()); - documentStatisticCache.put(key, stats); - } + //invalidate document statistic cache + clearDocumentStatistics(event.getDocumentId(), event.getLocaleId()); // update document status information updateDocStatusCache(key, event.getTextFlowTargetId()); @@ -212,7 +202,7 @@ public void textFlowStateUpdated(TextFlowTargetStateEvent event) { private void updateDocStatusCache(DocumentLocaleKey key, Long updatedTargetId) { DocumentStatus documentStatus = docStatusCache.get(key); - if(documentStatus != null) { + if (documentStatus != null) { HTextFlowTarget target = textFlowTargetDAO.findById(updatedTargetId, false); updateDocumentStatus(documentDAO, documentStatus, diff --git a/zanata-war/src/main/java/org/zanata/service/impl/TranslationUpdatedManager.java b/zanata-war/src/main/java/org/zanata/service/impl/TranslationUpdatedManager.java index 437947f711..dbc6e99af7 100644 --- a/zanata-war/src/main/java/org/zanata/service/impl/TranslationUpdatedManager.java +++ b/zanata-war/src/main/java/org/zanata/service/impl/TranslationUpdatedManager.java @@ -1,8 +1,6 @@ package org.zanata.service.impl; -import com.google.common.annotations.VisibleForTesting; import org.jboss.seam.ScopeType; -import org.jboss.seam.annotations.AutoCreate; import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Observer; @@ -11,11 +9,9 @@ import org.zanata.dao.TextFlowDAO; import org.zanata.events.DocumentStatisticUpdatedEvent; import org.zanata.events.TextFlowTargetStateEvent; -import org.zanata.service.DocumentService; import org.zanata.service.TranslationStateCache; -import org.zanata.ui.model.statistic.WordStatistic; -import org.zanata.util.StatisticsUtil; +import com.google.common.annotations.VisibleForTesting; import lombok.extern.slf4j.Slf4j; /** @@ -51,21 +47,14 @@ public void textFlowStateUpdated(TextFlowTargetStateEvent event) { // Fire asynchronous event public void publishAsyncEvent(TextFlowTargetStateEvent event) { if (Events.exists()) { - WordStatistic stats = - translationStateCacheImpl.getDocumentStatistics( - event.getDocumentId(), event.getLocaleId()); - int wordCount = textFlowDAO.getWordCount(event.getTextFlowId()); - WordStatistic oldStats = StatisticsUtil.copyWordStatistic(stats); - oldStats.decrement(event.getNewState(), wordCount); - oldStats.increment(event.getPreviousState(), wordCount); - Events.instance().raiseAsynchronousEvent( DocumentStatisticUpdatedEvent.EVENT_NAME, - new DocumentStatisticUpdatedEvent(oldStats, stats, + new DocumentStatisticUpdatedEvent( event.getProjectIterationId(), event.getDocumentId(), event.getLocaleId(), + wordCount, event.getPreviousState(), event.getNewState())); } } diff --git a/zanata-war/src/main/java/org/zanata/service/impl/UserAccountServiceImpl.java b/zanata-war/src/main/java/org/zanata/service/impl/UserAccountServiceImpl.java index b004caf53f..1001fbb00d 100644 --- a/zanata-war/src/main/java/org/zanata/service/impl/UserAccountServiceImpl.java +++ b/zanata-war/src/main/java/org/zanata/service/impl/UserAccountServiceImpl.java @@ -23,16 +23,18 @@ import java.util.List; import java.util.regex.Pattern; +import javax.annotation.Nonnull; + import lombok.extern.slf4j.Slf4j; import org.hibernate.Query; import org.hibernate.Session; -import org.hibernate.criterion.Restrictions; import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; import org.zanata.dao.AccountDAO; +import org.zanata.dao.AccountResetPasswordKeyDAO; import org.zanata.dao.RoleAssignmentRuleDAO; import org.zanata.model.HAccount; import org.zanata.model.HAccountResetPasswordKey; @@ -55,27 +57,26 @@ public class UserAccountServiceImpl implements UserAccountService { @In private AccountDAO accountDAO; + @In + private AccountResetPasswordKeyDAO accountResetPasswordKeyDAO; + @In private RoleAssignmentRuleDAO roleAssignmentRuleDAO; @Override public void clearPasswordResetRequests(HAccount account) { - // TODO This should be done in a DAO HAccountResetPasswordKey key = - (HAccountResetPasswordKey) session - .createCriteria(HAccountResetPasswordKey.class) - .add(Restrictions.eq("account", account)) - .uniqueResult(); - if (key != null) { - session.delete(key); - session.flush(); + accountResetPasswordKeyDAO.findByAccount(account.getId()); + if(key != null) { + accountResetPasswordKeyDAO.makeTransient(key); + accountResetPasswordKeyDAO.flush(); } } @Override - public HAccountResetPasswordKey requestPasswordReset(HAccount account) { - if (account == null || !account.isEnabled() - || account.getPerson() == null) { + public HAccountResetPasswordKey requestPasswordReset( + @Nonnull HAccount account) { + if (account.getPerson() == null) { return null; } @@ -84,9 +85,11 @@ public HAccountResetPasswordKey requestPasswordReset(HAccount account) { HAccountResetPasswordKey key = new HAccountResetPasswordKey(); key.setAccount(account); key.setKeyHash(HashUtil.generateHash(account.getUsername() - + account.getPasswordHash() + account.getPerson().getEmail() - + account.getPerson().getName() + System.currentTimeMillis())); - session.persist(key); + + account.getPasswordHash() + account.getPerson().getEmail() + + account.getPerson().getName() + System.currentTimeMillis())); + + account.setAccountResetPasswordKey(key); + accountResetPasswordKeyDAO.makePersistent(key); log.info("Sent password reset key to {} ({})", account.getPerson() .getName(), account.getUsername()); diff --git a/zanata-war/src/main/java/org/zanata/servlet/FileUploadServlet.java b/zanata-war/src/main/java/org/zanata/servlet/FileUploadServlet.java index 86acd79f12..cea0366071 100644 --- a/zanata-war/src/main/java/org/zanata/servlet/FileUploadServlet.java +++ b/zanata-war/src/main/java/org/zanata/servlet/FileUploadServlet.java @@ -149,12 +149,15 @@ private void doWork(HttpServletRequest req, HttpServletResponse resp) MergeType mergeType = Boolean.parseBoolean(params.get("merge").getString()) ? MergeType.AUTO : MergeType.IMPORT; + + boolean assignCreditToUploader = Boolean.parseBoolean( + params.get("assignCreditToUploader").getString()); List warnings = translationServiceImpl.translateAllInDoc(projectSlug, versionSlug, docId, new LocaleId(params.get("targetLocale") .getString()), transRes, extensions, - mergeType); + mergeType, assignCreditToUploader); StringBuilder response = new StringBuilder(); response.append("Status code: "); diff --git a/zanata-war/src/main/java/org/zanata/servlet/MultiFileUploadServlet.java b/zanata-war/src/main/java/org/zanata/servlet/MultiFileUploadServlet.java index 2bf8cad90e..f8a2db3f49 100644 --- a/zanata-war/src/main/java/org/zanata/servlet/MultiFileUploadServlet.java +++ b/zanata-war/src/main/java/org/zanata/servlet/MultiFileUploadServlet.java @@ -272,7 +272,8 @@ public JSONArray process() throws FileUploadException { } private List getRequestItems() throws FileUploadException { - List items;// Create a factory for disk-based file items + // Create a factory for disk-based file items + List items; FileItemFactory factory = new DiskFileItemFactory(); ServletFileUpload uploadHandler = new ServletFileUpload(factory); items = uploadHandler.parseRequest(request); diff --git a/zanata-war/src/main/java/org/zanata/ui/AbstractListFilter.java b/zanata-war/src/main/java/org/zanata/ui/AbstractListFilter.java index 562a0c48d1..25f4ab701a 100644 --- a/zanata-war/src/main/java/org/zanata/ui/AbstractListFilter.java +++ b/zanata-war/src/main/java/org/zanata/ui/AbstractListFilter.java @@ -51,7 +51,7 @@ public abstract class AbstractListFilter { private long totalRecords = -1L; public List getCurrentPage() { - if( currentPageData == null ) { + if (currentPageData == null) { currentPageData = fetchRecords(getPageStartIdx(), pageSize, filter); } @@ -59,7 +59,7 @@ public List getCurrentPage() { } public long getTotalRecords() { - if( totalRecords < 0 ) { + if (totalRecords < 0) { totalRecords = fetchTotalRecords(filter); } return totalRecords; @@ -101,7 +101,7 @@ public int getPageStartIdx() { } public int getPageEndIdx() { - return Math.min(getPageStartIdx() + pageSize, (int)getTotalRecords()) - 1; + return Math.min(getPageStartIdx() + pageSize, (int) getTotalRecords()) - 1; } public void reset() { diff --git a/zanata-war/src/main/java/org/zanata/ui/InMemoryListFilter.java b/zanata-war/src/main/java/org/zanata/ui/InMemoryListFilter.java index 184c01425f..61a388ad5c 100644 --- a/zanata-war/src/main/java/org/zanata/ui/InMemoryListFilter.java +++ b/zanata-war/src/main/java/org/zanata/ui/InMemoryListFilter.java @@ -46,7 +46,7 @@ protected long fetchTotalRecords(String filter) { protected List fetchRecords(int start, int max, final String filter) { loadElements(); List filteredList = Lists.newArrayList(elements); - if( filter != null ) { + if (filter != null) { filteredList = Lists.newArrayList( Collections2.filter(elements, new Predicate() { @Override @@ -61,7 +61,7 @@ public boolean apply(T input) { } private void loadElements() { - if(elements == null) { + if (elements == null) { elements = fetchAll(); } } diff --git a/zanata-war/src/main/java/org/zanata/ui/model/statistic/AbstractStatistic.java b/zanata-war/src/main/java/org/zanata/ui/model/statistic/AbstractStatistic.java index 6cef08e48a..3ebb6e5bcf 100644 --- a/zanata-war/src/main/java/org/zanata/ui/model/statistic/AbstractStatistic.java +++ b/zanata-war/src/main/java/org/zanata/ui/model/statistic/AbstractStatistic.java @@ -29,15 +29,15 @@ protected AbstractStatistic(int approved, int needReview, int untranslated, this.rejected = rejected; } - public void increment(ContentState state, int count) { + public synchronized void increment(ContentState state, int count) { set(state, get(state) + count); } - public void decrement(ContentState state, int count) { + public synchronized void decrement(ContentState state, int count) { set(state, get(state) - count); } - public void set(ContentState state, int value) { + public synchronized void set(ContentState state, int value) { switch (state) { case Translated: translated = value; @@ -60,7 +60,7 @@ public void set(ContentState state, int value) { } } - public int get(ContentState state) { + public synchronized int get(ContentState state) { switch (state) { case Translated: return translated; @@ -78,7 +78,7 @@ public int get(ContentState state) { } } - public void add(AbstractStatistic other) { + public synchronized void add(AbstractStatistic other) { this.approved += other.approved; this.needReview += other.needReview; this.untranslated += other.untranslated; @@ -94,31 +94,31 @@ protected void set(AbstractStatistic other) { this.rejected = other.rejected; } - public int getTotal() { + public synchronized int getTotal() { return approved + needReview + untranslated + translated + rejected; } - public int getApproved() { + public synchronized int getApproved() { return approved; } - public int getNeedReview() { + public synchronized int getNeedReview() { return needReview; } - public int getUntranslated() { + public synchronized int getUntranslated() { return untranslated; } - public int getTranslated() { + public synchronized int getTranslated() { return translated; } - public int getRejected() { + public synchronized int getRejected() { return rejected; } - public double getPercentage(ContentState contentState) { + public synchronized double getPercentage(ContentState contentState) { switch (contentState) { case Translated: return getPercentTranslated(); @@ -136,23 +136,23 @@ public double getPercentage(ContentState contentState) { } } - public double getPercentTranslated() { + public synchronized double getPercentTranslated() { return getPercentage(getTranslated()); } - public double getPercentFuzzy() { + public synchronized double getPercentFuzzy() { return getPercentage(getNeedReview()); } - public double getPercentRejected() { + public synchronized double getPercentRejected() { return getPercentage(getRejected()); } - public double getPercentApproved() { + public synchronized double getPercentApproved() { return getPercentage(getApproved()); } - public double getPercentUntranslated() { + public synchronized double getPercentUntranslated() { return getPercentage(getUntranslated()); } @@ -166,7 +166,7 @@ private double getPercentage(double value) { } @Override - public boolean equals(Object obj) { + public synchronized boolean equals(Object obj) { if (obj == this) return true; if (obj == null) diff --git a/zanata-war/src/main/java/org/zanata/ui/model/statistic/WordStatistic.java b/zanata-war/src/main/java/org/zanata/ui/model/statistic/WordStatistic.java index ad99f8fa5f..1aed3c7d1b 100644 --- a/zanata-war/src/main/java/org/zanata/ui/model/statistic/WordStatistic.java +++ b/zanata-war/src/main/java/org/zanata/ui/model/statistic/WordStatistic.java @@ -1,17 +1,14 @@ package org.zanata.ui.model.statistic; -import lombok.Getter; -import lombok.Setter; - /** * @author Alex Eng aeng@redhat.com + * + * TODO: make this class immutable */ public class WordStatistic extends AbstractStatistic { private static final long serialVersionUID = -8807499518683834883L; - @Getter - @Setter private double remainingHours; public WordStatistic() { @@ -23,7 +20,7 @@ public WordStatistic(int approved, int needReview, int untranslated, super(approved, needReview, untranslated, translated, rejected); } - public String toString() { + public synchronized String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getName()); sb.append("\ntranslated-" + getTranslated()); @@ -34,4 +31,12 @@ public String toString() { return sb.toString(); } + + public synchronized double getRemainingHours() { + return remainingHours; + } + + public synchronized void setRemainingHours(double remainingHours) { + this.remainingHours = remainingHours; + } } diff --git a/zanata-war/src/main/java/org/zanata/util/CoverageIgnore.java b/zanata-war/src/main/java/org/zanata/util/CoverageIgnore.java new file mode 100644 index 0000000000..51c0297569 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/util/CoverageIgnore.java @@ -0,0 +1,15 @@ +package org.zanata.util; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Cobertura 2.0+ will ignore any method with this annotation. + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface CoverageIgnore { + String value() default ""; +} diff --git a/zanata-war/src/main/java/org/zanata/webtrans/client/Application.java b/zanata-war/src/main/java/org/zanata/webtrans/client/Application.java index 67da095514..eb68ee48bb 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/client/Application.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/client/Application.java @@ -5,6 +5,7 @@ import net.customware.gwt.presenter.client.EventBus; import org.zanata.common.LocaleId; +import org.zanata.util.CoverageIgnore; import org.zanata.webtrans.client.EventProcessor.StartCallback; import org.zanata.webtrans.client.events.NotificationEvent; import org.zanata.webtrans.client.events.TransUnitUpdatedEvent; @@ -286,6 +287,7 @@ public static String getUploadFileUrl() { return GWT.getModuleBaseURL() + "files/upload"; } + @CoverageIgnore("JSNI") public static native void redirectToUrl(String url)/*-{ $wnd.location = url; }-*/; diff --git a/zanata-war/src/main/java/org/zanata/webtrans/client/presenter/UserScriptCallbackHook.java b/zanata-war/src/main/java/org/zanata/webtrans/client/presenter/UserScriptCallbackHook.java index d1fac3b219..2e843bb312 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/client/presenter/UserScriptCallbackHook.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/client/presenter/UserScriptCallbackHook.java @@ -20,6 +20,7 @@ */ package org.zanata.webtrans.client.presenter; +import org.zanata.util.CoverageIgnore; import org.zanata.webtrans.client.events.TransUnitUpdatedEvent; import org.zanata.webtrans.client.events.TransUnitUpdatedEventHandler; @@ -36,6 +37,7 @@ public UserScriptCallbackHook() { attachCallbackMapToWindow(); } + @CoverageIgnore("JSNI") private static native void attachCallbackMapToWindow()/*-{ $wnd.zanataEvent = { description : "Add callback functions to the arrays for the available event types.\n" @@ -54,6 +56,7 @@ public void onTransUnitUpdated(TransUnitUpdatedEvent event) { triggerAllTransUnitUpdatedCallbacksWith(event); } + @CoverageIgnore("JSNI") private static native void triggerAllTransUnitUpdatedCallbacksWith( TransUnitUpdatedEvent event)/*-{ callbacks = $wnd.zanataEvent.events.transUnitUpdated.callbacks; diff --git a/zanata-war/src/main/java/org/zanata/webtrans/client/resources/WebTransMessages.java b/zanata-war/src/main/java/org/zanata/webtrans/client/resources/WebTransMessages.java index 27845d846a..a39d4d570a 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/client/resources/WebTransMessages.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/client/resources/WebTransMessages.java @@ -545,4 +545,7 @@ String undoUnsuccessful(@PluralCount int unsuccessfulCount, @DefaultMessage("Try the new alpha editor") String newEditorMessage(); + + @DefaultMessage("Anonymous") + String anonymousUser(); } diff --git a/zanata-war/src/main/java/org/zanata/webtrans/client/ui/CodeMirrorEditor.java b/zanata-war/src/main/java/org/zanata/webtrans/client/ui/CodeMirrorEditor.java index 744bfd1ff1..6d044de3fe 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/client/ui/CodeMirrorEditor.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/client/ui/CodeMirrorEditor.java @@ -20,6 +20,7 @@ import com.google.gwt.user.client.Command; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.Widget; +import org.zanata.util.CoverageIgnore; public class CodeMirrorEditor extends Composite implements TextAreaWrapper { private static CodeMirrorEditorUiBinder ourUiBinder = GWT @@ -39,6 +40,7 @@ public CodeMirrorEditor(Command onFocusCallback) { } // see http://codemirror.net/doc/manual.html#usage + @CoverageIgnore("JSNI") private native JavaScriptObject initCodeMirror(TextAreaElement element) /*-{ var self = this; @@ -110,11 +112,13 @@ private void onChange() { ValueChangeEvent.fire(this, getCodeMirrorContent()); } + @CoverageIgnore("JSNI") private native String getCodeMirrorContent() /*-{ var editor = this.@org.zanata.webtrans.client.ui.CodeMirrorEditor::codeMirror; return editor.getValue(); }-*/; + @CoverageIgnore("JSNI") private native void setCodeMirrorContent(String text) /*-{ var editor = this.@org.zanata.webtrans.client.ui.CodeMirrorEditor::codeMirror; if (editor) { @@ -122,6 +126,7 @@ private native void setCodeMirrorContent(String text) /*-{ } }-*/; + @CoverageIgnore("JSNI") private native void focusEditor() /*-{ var editor = this.@org.zanata.webtrans.client.ui.CodeMirrorEditor::codeMirror; editor.focus(); @@ -136,6 +141,7 @@ public void setReadOnly(boolean readOnly) { } } + @CoverageIgnore("JSNI") private native void setEditorOption(String option, String value) /*-{ var editor = this.@org.zanata.webtrans.client.ui.CodeMirrorEditor::codeMirror; if (editor) { @@ -143,6 +149,7 @@ private native void setEditorOption(String option, String value) /*-{ } }-*/; + @CoverageIgnore("JSNI") private native String getEditorOption(String option, String defaultValue) /*-{ var editor = this.@org.zanata.webtrans.client.ui.CodeMirrorEditor::codeMirror; if (editor) { @@ -161,6 +168,7 @@ public int getCursorPos() { return getCodeMirrorCursorPos(); } + @CoverageIgnore("JSNI") private native int getCodeMirrorCursorPos() /*-{ var editor = this.@org.zanata.webtrans.client.ui.CodeMirrorEditor::codeMirror; var pos = editor.getCursor(); @@ -172,6 +180,7 @@ public void setCursorPos(int pos) { setCodeMirrorCursorPos(pos); } + @CoverageIgnore("JSNI") private native void setCodeMirrorCursorPos(int cursorIndex) /*-{ var editor = this.@org.zanata.webtrans.client.ui.CodeMirrorEditor::codeMirror; var pos = editor.posFromIndex(cursorIndex); @@ -185,6 +194,7 @@ public void highlight(String term) { } } + @CoverageIgnore("JSNI") private native void codeMirrorHighlight(String term) /*-{ var editor = this.@org.zanata.webtrans.client.ui.CodeMirrorEditor::codeMirror; var searchCursor = editor.getSearchCursor(term, { @@ -202,6 +212,7 @@ public void refresh() { refreshCodeMirror(); } + @CoverageIgnore("JSNI") private native void refreshCodeMirror() /*-{ var editor = this.@org.zanata.webtrans.client.ui.CodeMirrorEditor::codeMirror; diff --git a/zanata-war/src/main/java/org/zanata/webtrans/client/ui/CodeMirrorReadOnlyWidget.java b/zanata-war/src/main/java/org/zanata/webtrans/client/ui/CodeMirrorReadOnlyWidget.java index 8e02147cf4..dd53b447c2 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/client/ui/CodeMirrorReadOnlyWidget.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/client/ui/CodeMirrorReadOnlyWidget.java @@ -9,6 +9,7 @@ import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.Widget; +import org.zanata.util.CoverageIgnore; public class CodeMirrorReadOnlyWidget extends Composite implements SourceContentWrapper { @@ -26,6 +27,7 @@ public CodeMirrorReadOnlyWidget() { } // see http://codemirror.net/doc/manual.html#usage + @CoverageIgnore("JSNI") private native JavaScriptObject initCodeMirror(Element element) /*-{ var self = this; var codeMirrorEditor = $wnd.CodeMirror.fromTextArea(element, { @@ -53,6 +55,7 @@ public void setText(String text) { content = text; } + @CoverageIgnore("JSNI") public native void refresh() /*-{ var codeMirror = this.@org.zanata.webtrans.client.ui.CodeMirrorReadOnlyWidget::codeMirror; if (codeMirror) { @@ -66,6 +69,7 @@ public void highlight(String term) { } } + @CoverageIgnore("JSNI") private native void codeMirrorHighlight(String term) /*-{ var codeMirror = this.@org.zanata.webtrans.client.ui.CodeMirrorReadOnlyWidget::codeMirror; var searchCursor = codeMirror.getSearchCursor(term, { diff --git a/zanata-war/src/main/java/org/zanata/webtrans/client/ui/EditorSearchField.java b/zanata-war/src/main/java/org/zanata/webtrans/client/ui/EditorSearchField.java index 8504d1ec07..75b9cd218b 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/client/ui/EditorSearchField.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/client/ui/EditorSearchField.java @@ -29,6 +29,7 @@ import com.google.gwt.uibinder.client.UiHandler; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.ui.*; +import org.zanata.util.CoverageIgnore; /** @@ -65,6 +66,7 @@ private void onValueChanged(String newValue, EditorSearchFieldListener listener) listener.onSearchFieldValueChange(newValue); } + @CoverageIgnore("JSNI") private native void initTextBox(Element wrapper, EditorSearchFieldListener listener)/*-{ var valueChangeCallback = this.@org.zanata.webtrans.client.ui.EditorSearchField::onValueChanged(Ljava/lang/String;Lorg/zanata/webtrans/client/ui/EditorSearchFieldListener;); $wnd.searchSuggestions.init(wrapper, function (newValue) { diff --git a/zanata-war/src/main/java/org/zanata/webtrans/client/ui/FileUploadDialog.java b/zanata-war/src/main/java/org/zanata/webtrans/client/ui/FileUploadDialog.java index b65859e115..1b5f97e39a 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/client/ui/FileUploadDialog.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/client/ui/FileUploadDialog.java @@ -25,6 +25,7 @@ public class FileUploadDialog extends DialogBox { private final FileUpload upload; private final CheckBox merge; + private final CheckBox myTranslations; private final Button cancelButton; private final Button uploadButton; private final Image loadingIcon; @@ -36,6 +37,7 @@ public class FileUploadDialog extends DialogBox { private final Hidden fileName; private final Hidden targetLocale; private final Hidden mergeTranslation; + private final Hidden assignCreditToUploader; public FileUploadDialog(Resources resources) { setText("File upload"); @@ -55,6 +57,9 @@ public FileUploadDialog(Resources resources) { merge = new CheckBox("Merge?"); merge.setValue(true); + myTranslations = new CheckBox("My translations?"); + myTranslations.setValue(false); + cancelButton = new Button("Cancel"); uploadButton = new Button("Upload"); @@ -66,6 +71,7 @@ public FileUploadDialog(Resources resources) { panel.add(new ListItemWidget(upload)); panel.add(new ListItemWidget(merge)); + panel.add(new ListItemWidget(myTranslations)); panel.add(new ListItemWidget(buttonPanel)); projectSlug = new Hidden("projectSlug"); @@ -74,6 +80,7 @@ public FileUploadDialog(Resources resources) { fileName = new Hidden("fileName"); targetLocale = new Hidden("targetLocale"); mergeTranslation = new Hidden("merge"); + assignCreditToUploader = new Hidden("assignCreditToUploader"); panel.add(projectSlug); panel.add(versionSlug); @@ -81,6 +88,7 @@ public FileUploadDialog(Resources resources) { panel.add(fileName); panel.add(targetLocale); panel.add(mergeTranslation); + panel.add(assignCreditToUploader); // Because we're going to add a FileUpload widget, we'll need to set // the @@ -122,6 +130,7 @@ public void onSubmitComplete(SubmitCompleteEvent event) { public void submitForm() { fileName.setValue(getUploadFileName()); mergeTranslation.setValue(merge.getValue().toString()); + assignCreditToUploader.setValue(myTranslations.getValue().toString()); loadingIcon.setVisible(true); form.submit(); } diff --git a/zanata-war/src/main/java/org/zanata/webtrans/client/ui/Highlighting.java b/zanata-war/src/main/java/org/zanata/webtrans/client/ui/Highlighting.java index d4bd8b3e21..11882a8d2f 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/client/ui/Highlighting.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/client/ui/Highlighting.java @@ -22,15 +22,18 @@ import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.dom.client.Element; +import org.zanata.util.CoverageIgnore; public class Highlighting { + @CoverageIgnore("JSNI") public static native void syntaxHighlight(String text, Element elem)/*-{ elem.innerHTML = ''; $wnd.CodeMirror.runMode(text, "visibleSpace", elem); }-*/; // From JavaScript on http://www.nsftools.com/misc/SearchAndHighlight.htm + @CoverageIgnore("JSNI") public static native void searchHighlight(String searchTerm, Element elem)/*-{ // the highlightStartTag and highlightEndTag parameters are optional var bodyText = elem.innerHTML; @@ -76,6 +79,7 @@ public static String diffAsHtml(String text1, String text2) { return diffsToHtml(diffs); } + @CoverageIgnore("JSNI") private static native JavaScriptObject diff(String text1, String text2, boolean cleanupSemantic)/*-{ if (!$wnd.diffMatchPatch) { @@ -92,6 +96,7 @@ private static native JavaScriptObject diff(String text1, String text2, }-*/; // modified diff_prettyHtml() from diff_match_patch.js + @CoverageIgnore("JSNI") private static native String diffsToHtml(JavaScriptObject diffs)/*-{ var html = []; var pattern_amp = /&/g; @@ -125,6 +130,7 @@ public static String diffAsHighlight(String text1, String text2) { // DIFF_DELETE text is hidden, DIFF_EQUAL text is highlighted, and // DIFF_INSERT text is shown plain + @CoverageIgnore("JSNI") private static native String diffsHighlight(JavaScriptObject diffs)/*-{ var html = []; var pattern_amp = /&/g; diff --git a/zanata-war/src/main/java/org/zanata/webtrans/client/ui/SearchTextBox.java b/zanata-war/src/main/java/org/zanata/webtrans/client/ui/SearchTextBox.java index f26dfb0886..f069477338 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/client/ui/SearchTextBox.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/client/ui/SearchTextBox.java @@ -22,6 +22,7 @@ import com.google.gwt.user.client.Element; import com.google.gwt.user.client.ui.TextBox; +import org.zanata.util.CoverageIgnore; /** * @author damason@redhat.com @@ -51,6 +52,7 @@ public boolean isFocused() { return isElementFocused(getElement()); } + @CoverageIgnore("JSNI") private native boolean isElementFocused(Element element)/*-{ return element.ownerDocument.activeElement === element; }-*/; diff --git a/zanata-war/src/main/java/org/zanata/webtrans/client/ui/TransHistoryItemLine.java b/zanata-war/src/main/java/org/zanata/webtrans/client/ui/TransHistoryItemLine.java index 7b3f561b1c..dd821256fb 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/client/ui/TransHistoryItemLine.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/client/ui/TransHistoryItemLine.java @@ -31,6 +31,7 @@ import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.safehtml.client.SafeHtmlTemplates; import com.google.gwt.safehtml.shared.SafeHtml; +import com.google.gwt.safehtml.shared.SafeHtmlBuilder; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.uibinder.client.UiHandler; @@ -45,6 +46,8 @@ public class TransHistoryItemLine extends Composite { .create(TransHistoryItemLineUiBinder.class); private static TransHistoryItemTemplate template = GWT .create(TransHistoryItemTemplate.class); + private static WebTransMessages messages = GWT + .create(WebTransMessages.class); private final TransHistoryItem item; private final TranslationHistoryDisplay.Listener listener; @@ -61,8 +64,6 @@ public class TransHistoryItemLine extends Composite { @UiField Anchor copyIntoEditor; @UiField - WebTransMessages messages; - @UiField SpanElement icon; public TransHistoryItemLine(TransHistoryItem item, @@ -70,10 +71,13 @@ public TransHistoryItemLine(TransHistoryItem item, ContentStateRenderer stateRenderer) { this.item = item; this.listener = listener; - // modified by person can be empty if translation is pushed from client - String person = - item.getModifiedBy().isEmpty() ? "(Someone offline)" : item - .getModifiedBy(); + // before rhbz1149968 modified by person can be empty if translation is + // pushed from client + SafeHtml anonymous = template.anonymousUser(messages.anonymousUser()); + SafeHtml person = + item.getModifiedBy().isEmpty() ? + anonymous : new SafeHtmlBuilder() + .appendHtmlConstant(item.getModifiedBy()).toSafeHtml(); heading = new InlineHTML(template.heading(person, ContentStateToStyleUtil.stateToStyle(item.getStatus()), @@ -119,11 +123,14 @@ public interface TransHistoryItemTemplate extends SafeHtmlTemplates { SafeHtml targetContent(SafeHtml message); @Template("
{0} created a {2} revision
") - SafeHtml heading(String person, String contentStateStyle, + SafeHtml heading(SafeHtml person, String contentStateStyle, String contentState); @Template("Revision {0} {1}") SafeHtml targetRevision(String versionNum, String optionalLabel); + + @Template("{0}") + SafeHtml anonymousUser(String anonymous); } } diff --git a/zanata-war/src/main/java/org/zanata/webtrans/client/ui/TransHistoryItemLine.ui.xml b/zanata-war/src/main/java/org/zanata/webtrans/client/ui/TransHistoryItemLine.ui.xml index 3d60d7819e..0d9c0c3d0b 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/client/ui/TransHistoryItemLine.ui.xml +++ b/zanata-war/src/main/java/org/zanata/webtrans/client/ui/TransHistoryItemLine.ui.xml @@ -21,8 +21,6 @@ -
diff --git a/zanata-war/src/main/java/org/zanata/webtrans/client/util/DateUtil.java b/zanata-war/src/main/java/org/zanata/webtrans/client/util/DateUtil.java index 60b96525cd..f880cb6790 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/client/util/DateUtil.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/client/util/DateUtil.java @@ -3,19 +3,25 @@ */ package org.zanata.webtrans.client.util; -import java.util.Date; +import com.google.gwt.i18n.shared.DateTimeFormat; +import com.google.gwt.i18n.shared.DefaultDateTimeFormatInfo; -import com.google.gwt.i18n.client.DateTimeFormat; -import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat; +import javax.annotation.concurrent.NotThreadSafe; +import java.util.Date; /** * * @author Alex Eng aeng@redhat.com * */ +@NotThreadSafe public class DateUtil { + private static final DefaultDateTimeFormatInfo info = + new DefaultDateTimeFormatInfo(); private final static String DATE_TIME_SHORT_PATTERN = "dd/MM/yy HH:mm"; + private static DateTimeFormat dtfShort = new DateTimeFormat(DATE_TIME_SHORT_PATTERN, info) {}; private final static String DATE_TIME_LONG_PATTERN = "dd/MM/yy HH:mm:ss"; + private static DateTimeFormat dtfLong = new DateTimeFormat(DATE_TIME_LONG_PATTERN, info) {}; /** * Format date to dd/MM/yy hh:mm a @@ -25,22 +31,7 @@ public class DateUtil { */ public static String formatShortDate(Date date) { if (date != null) { - return DateTimeFormat.getFormat(DATE_TIME_SHORT_PATTERN).format( - date); - } - return null; - } - - /** - * Format date to hh:mm:ss - * - * @param date - * @return - */ - public static String formatTime(Date date) { - if (date != null) { - return DateTimeFormat.getFormat( - PredefinedFormat.HOUR24_MINUTE_SECOND).format(date); + return dtfShort.format(date); } return null; } @@ -53,8 +44,7 @@ public static String formatTime(Date date) { */ public static String formatLongDateTime(Date date) { if (date != null) { - return DateTimeFormat.getFormat(DATE_TIME_LONG_PATTERN) - .format(date); + return dtfLong.format(date); } return null; } diff --git a/zanata-war/src/main/java/org/zanata/webtrans/client/util/JavascriptUtil.java b/zanata-war/src/main/java/org/zanata/webtrans/client/util/JavascriptUtil.java index 2242b291e1..d30b3881e7 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/client/util/JavascriptUtil.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/client/util/JavascriptUtil.java @@ -20,6 +20,8 @@ */ package org.zanata.webtrans.client.util; +import org.zanata.util.CoverageIgnore; + /** * Utilities for dealing with javascript native code. * @@ -33,6 +35,7 @@ public class JavascriptUtil { * @param varName Variable name. * @return The value (as a string) assigned to varName. */ + @CoverageIgnore("JSNI") public static native String getJavascriptValue(String varName) /*-{ return $wnd[varName]; }-*/; diff --git a/zanata-war/src/main/java/org/zanata/webtrans/client/view/AppView.java b/zanata-war/src/main/java/org/zanata/webtrans/client/view/AppView.java index 5822f71165..e0ba1e5c3b 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/client/view/AppView.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/client/view/AppView.java @@ -21,6 +21,7 @@ package org.zanata.webtrans.client.view; import org.zanata.rest.dto.stats.ContainerTranslationStatistics; +import org.zanata.util.CoverageIgnore; import org.zanata.webtrans.client.Application; import org.zanata.webtrans.client.events.NotificationEvent; import org.zanata.webtrans.client.presenter.KeyShortcutPresenter; @@ -399,9 +400,11 @@ private static String getMessageClass(NotificationEvent.Severity severity) { } // @formatter:off + @CoverageIgnore("JSNI") private static native void activateNotification(Element element)/*-{ $wnd.zanata.messages.activate(element); }-*/; + @CoverageIgnore("JSNI") private static native void deactivateNotification(Element element)/*-{ $wnd.zanata.messages.deactivate(element); }-*/; diff --git a/zanata-war/src/main/java/org/zanata/webtrans/client/view/TransFilterView.java b/zanata-war/src/main/java/org/zanata/webtrans/client/view/TransFilterView.java index b8f1e121c6..3296a32421 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/client/view/TransFilterView.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/client/view/TransFilterView.java @@ -20,6 +20,7 @@ */ package org.zanata.webtrans.client.view; +import org.zanata.util.CoverageIgnore; import org.zanata.webtrans.client.presenter.UserConfigHolder.ConfigurationState; import org.zanata.webtrans.client.resources.UiMessages; import org.zanata.webtrans.client.ui.EditorSearchField; @@ -186,6 +187,7 @@ private static void setPartiallyChecked(CheckBox checkbox, setElementIndeterminate(checkbox.getElement(), partiallyChecked); } + @CoverageIgnore("JSNI") private static native void setElementIndeterminate(Element elem, boolean indeterminate)/*-{ elem.getElementsByTagName('input')[0].indeterminate = indeterminate; diff --git a/zanata-war/src/main/java/org/zanata/webtrans/client/view/TransUnitsTableView.java b/zanata-war/src/main/java/org/zanata/webtrans/client/view/TransUnitsTableView.java index 2f3733e8dd..ee17224116 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/client/view/TransUnitsTableView.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/client/view/TransUnitsTableView.java @@ -3,6 +3,7 @@ import java.util.List; import com.google.gwt.user.client.Element; +import org.zanata.util.CoverageIgnore; import org.zanata.webtrans.client.resources.WebTransMessages; import org.zanata.webtrans.client.ui.FilterViewConfirmationDisplay; import org.zanata.webtrans.client.ui.LoadingPanel; @@ -181,6 +182,7 @@ public void ensureVisible(TargetContentsDisplay currentDisplay) { scrollToElement(root.getElement(), transUnitRow.getElement()); } + @CoverageIgnore("JSNI") private native void scrollToElement(Element scroll, Element item) /*-{ if (!item || !scroll) { return; diff --git a/zanata-war/src/main/java/org/zanata/webtrans/server/rpc/DownloadAllFilesHandler.java b/zanata-war/src/main/java/org/zanata/webtrans/server/rpc/DownloadAllFilesHandler.java index 697764cde8..ac20d831e0 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/server/rpc/DownloadAllFilesHandler.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/server/rpc/DownloadAllFilesHandler.java @@ -80,8 +80,7 @@ public DownloadAllFilesResult execute(DownloadAllFilesAction action, action.getProjectSlug(), action.getVersionSlug(), action.getLocaleId(), Identity.instance().getCredentials() .getUsername(), handle); - } - catch (Exception e) { + } catch (Exception e) { throw new ActionException(e); } diff --git a/zanata-war/src/main/java/org/zanata/webtrans/server/rpc/GetTransUnitListHandler.java b/zanata-war/src/main/java/org/zanata/webtrans/server/rpc/GetTransUnitListHandler.java index 34a411b7cd..be8e32d79e 100755 --- a/zanata-war/src/main/java/org/zanata/webtrans/server/rpc/GetTransUnitListHandler.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/server/rpc/GetTransUnitListHandler.java @@ -186,9 +186,8 @@ private List getTextFlows(GetTransUnitList action, hLocale.getLocaleId(), offset, action.getCount()); } - } - // has status and other search field filter - else { + } else { + // has status and other search field filter log.debug("Fetch TransUnits filtered by status and/or search: {}", constraints); if (!hasValidationFilter(action)) { @@ -196,9 +195,8 @@ private List getTextFlows(GetTransUnitList action, textFlowDAO.getTextFlowByDocumentIdWithConstraints( action.getDocumentId(), hLocale, constraints, offset, action.getCount()); - } - // has validation filter - else { + } else { + // has validation filter textFlows = textFlowDAO.getAllTextFlowByDocumentIdWithConstraints( action.getDocumentId(), hLocale, constraints); diff --git a/zanata-war/src/main/resources/db/changelogs/db.changelog-3.7.xml b/zanata-war/src/main/resources/db/changelogs/db.changelog-3.7.xml new file mode 100644 index 0000000000..9be10c01cc --- /dev/null +++ b/zanata-war/src/main/resources/db/changelogs/db.changelog-3.7.xml @@ -0,0 +1,30 @@ + + + + + Add pluralForms column to HLocale + + + + + + + + + Add displayName, nativeName column to HLocale + + + + + + + + + + + + + diff --git a/zanata-war/src/main/resources/db/db.changelog.xml b/zanata-war/src/main/resources/db/db.changelog.xml index 6e4446e25e..cf5824a14c 100644 --- a/zanata-war/src/main/resources/db/db.changelog.xml +++ b/zanata-war/src/main/resources/db/db.changelog.xml @@ -37,6 +37,8 @@ file="changelogs/db.changelog-3.4.xml" /> + + + #{msgs['jsf.PageTitle']} - + + + @@ -68,7 +86,11 @@ - + + + + diff --git a/zanata-war/src/main/webapp-jboss/WEB-INF/classes/META-INF/components.xml b/zanata-war/src/main/webapp-jboss/WEB-INF/classes/META-INF/components.xml index 381c396c19..23cb61e814 100644 --- a/zanata-war/src/main/webapp-jboss/WEB-INF/classes/META-INF/components.xml +++ b/zanata-war/src/main/webapp-jboss/WEB-INF/classes/META-INF/components.xml @@ -11,6 +11,7 @@ xmlns:bpm="http://jboss.org/schema/seam/bpm" xmlns:mail="http://jboss.org/schema/seam/mail" xmlns:framework="http://jboss.org/schema/seam/framework" + xmlns:jms="http://jboss.org/schema/seam/jms" xmlns:resteasy="http://jboss.org/schema/seam/resteasy" xsi:schemaLocation= "http://jboss.org/schema/seam/core http://jboss.org/schema/seam/core-2.3.xsd @@ -23,6 +24,7 @@ http://jboss.org/schema/seam/bpm http://jboss.org/schema/seam/bpm-2.3.xsd http://jboss.org/schema/seam/mail http://jboss.org/schema/seam/mail-2.3.xsd http://jboss.org/schema/seam/framework http://jboss.org/schema/seam/framework-2.3.xsd + http://jboss.org/schema/seam/jms http://jboss.org/schema/seam/jms-2.3.xsd http://jboss.org/schema/seam/resteasy http://jboss.org/schema/seam/resteasy-2.3.xsd http://jboss.org/schema/seam/components http://jboss.org/schema/seam/components-2.3.xsd"> @@ -49,6 +51,10 @@ max-request-size="5200000" url-pattern="/*" /> + + + + @@ -102,6 +108,9 @@ + diff --git a/zanata-war/src/main/webapp-jboss/WEB-INF/ejb-jar.xml b/zanata-war/src/main/webapp-jboss/WEB-INF/ejb-jar.xml new file mode 100644 index 0000000000..3e458c6dcc --- /dev/null +++ b/zanata-war/src/main/webapp-jboss/WEB-INF/ejb-jar.xml @@ -0,0 +1,20 @@ + + + + + + org.jboss.seam.ejb.SeamInterceptor + + + + + + * + org.jboss.seam.ejb.SeamInterceptor + + + + diff --git a/zanata-war/src/main/webapp-jboss/WEB-INF/jboss-ejb3.xml b/zanata-war/src/main/webapp-jboss/WEB-INF/jboss-ejb3.xml new file mode 100644 index 0000000000..db77d5635c --- /dev/null +++ b/zanata-war/src/main/webapp-jboss/WEB-INF/jboss-ejb3.xml @@ -0,0 +1,8 @@ + + + diff --git a/zanata-war/src/main/webapp/WEB-INF/layout/admin/contact_admin_modal.xhtml b/zanata-war/src/main/webapp/WEB-INF/layout/admin/contact_admin_modal.xhtml new file mode 100644 index 0000000000..a38dda18bd --- /dev/null +++ b/zanata-war/src/main/webapp/WEB-INF/layout/admin/contact_admin_modal.xhtml @@ -0,0 +1,85 @@ + diff --git a/zanata-war/src/main/webapp/WEB-INF/layout/dashboard/settings.xhtml b/zanata-war/src/main/webapp/WEB-INF/layout/dashboard/settings.xhtml index adf67081ea..254b1b3afa 100644 --- a/zanata-war/src/main/webapp/WEB-INF/layout/dashboard/settings.xhtml +++ b/zanata-war/src/main/webapp/WEB-INF/layout/dashboard/settings.xhtml @@ -6,10 +6,6 @@ xmlns:a4j="http://richfaces.org/a4j" xmlns:zanata="http://java.sun.com/jsf/composite/zanata"> - -

#{msgs['jsf.dashboard.settings.title']}

    @@ -273,6 +269,11 @@ #{msgs['jsf.dashboard.settings.clientSettings.label']} + +

+
diff --git a/zanata-war/src/main/webapp/WEB-INF/layout/language/contact_coordinator_modal.xhtml b/zanata-war/src/main/webapp/WEB-INF/layout/language/contact_coordinator_modal.xhtml new file mode 100644 index 0000000000..0510a5cf3d --- /dev/null +++ b/zanata-war/src/main/webapp/WEB-INF/layout/language/contact_coordinator_modal.xhtml @@ -0,0 +1,88 @@ + diff --git a/zanata-war/src/main/webapp/WEB-INF/layout/language/members-tab.xhtml b/zanata-war/src/main/webapp/WEB-INF/layout/language/members-tab.xhtml new file mode 100644 index 0000000000..319e6be22f --- /dev/null +++ b/zanata-war/src/main/webapp/WEB-INF/layout/language/members-tab.xhtml @@ -0,0 +1,251 @@ + + + + +
+
+
+ + + + + + +
+

#{msgs['jsf.Members']}

+
+ +
+ +
+ + + + + + + + + +

#{msgs['jsf.language.members.empty']}

+ +

+ + #{msgs['jsf.AddTeamMember']} + + +

+
+
+ + +

#{msgs['jsf.search.NoResult']}

+
+ + + +
    + +
  • + + + + +
    +
    +

    + #{member.person.name} +

    + + #{member.person.account.username} [#{member.person.email}] + +
    +
    +
      +
    • + #{msgs['jsf.Translator']} + + + + + + + + + + + +
    • +
    • + #{msgs['jsf.Reviewer']} + + + + + + + + + + + +
    • + +
    • + #{msgs['jsf.Coordinator']} + + + + + + + + + + + +
    • +
    +
    +
    +
    +
  • +
    +
+
+
+ + + +
+ + + + + + diff --git a/zanata-war/src/main/webapp/WEB-INF/layout/language/new_language_modal.xhtml b/zanata-war/src/main/webapp/WEB-INF/layout/language/new_language_modal.xhtml new file mode 100644 index 0000000000..8582532c08 --- /dev/null +++ b/zanata-war/src/main/webapp/WEB-INF/layout/language/new_language_modal.xhtml @@ -0,0 +1,158 @@ + diff --git a/zanata-war/src/main/webapp/WEB-INF/layout/language/search_user_modal.xhtml b/zanata-war/src/main/webapp/WEB-INF/layout/language/search_user_modal.xhtml new file mode 100644 index 0000000000..a0b753b7bb --- /dev/null +++ b/zanata-war/src/main/webapp/WEB-INF/layout/language/search_user_modal.xhtml @@ -0,0 +1,124 @@ + diff --git a/zanata-war/src/main/webapp/WEB-INF/layout/language/settings-tab.xhtml b/zanata-war/src/main/webapp/WEB-INF/layout/language/settings-tab.xhtml new file mode 100644 index 0000000000..b371a4f75a --- /dev/null +++ b/zanata-war/src/main/webapp/WEB-INF/layout/language/settings-tab.xhtml @@ -0,0 +1,117 @@ + + + + + + + + + + + + +
+
+

#{msgs['jsf.Settings']}

+
+ + +
+
+ #{msgs['jsf.Name']} + + + + + + + + + +
+ +
+ #{msgs['jsf.NativeName']} + + + + + + + + + +
+ +
+ + + #{languageAction.locale.asULocale().language eq '' ? msgs['jsf.notAvailable'] : languageAction.locale.asULocale().language} + +
+ +
+ + + #{languageAction.locale.asULocale().country eq '' ? msgs['jsf.notAvailable'] : languageAction.locale.asULocale().country} + +
+ +
+ + + #{languageAction.locale.asULocale().variant eq '' ? msgs['jsf.notAvailable'] : languageAction.locale.asULocale().variant} + +
+ +
+ #{msgs['jsf.language.plurals']} + #{languageAction.examplePluralForms} + + + + + + + + + +
+ +
+ +
+ +
+ +
+ +
+ + #{msgs['jsf.Save']} + +
+ +
+
+
+
diff --git a/zanata-war/src/main/webapp/WEB-INF/layout/language_detail.xhtml b/zanata-war/src/main/webapp/WEB-INF/layout/language_detail.xhtml deleted file mode 100644 index 3c1d7298e2..0000000000 --- a/zanata-war/src/main/webapp/WEB-INF/layout/language_detail.xhtml +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - - - #{msgs['jsf.Language']} - - - - - - - - - - - - - - #{msgs['jsf.language.validation.Underscores']} - #{msgs['jsf.language.validation.ReplaceUnderscores']} - - - - - - - #{msgs['jsf.EnabledByDefault']} - - - - -
- - #{msgs['jsf.CountryCode']} - - - - #{msgs['jsf.Name']} - - - - #{msgs['jsf.LanguageCode']} - - - - #{msgs['jsf.Variant']} - - - - #{msgs['jsf.NativeName']} - - -
- - -
diff --git a/zanata-war/src/main/webapp/WEB-INF/layout/language_view_inline.xhtml b/zanata-war/src/main/webapp/WEB-INF/layout/language_view_inline.xhtml deleted file mode 100644 index 573f281c06..0000000000 --- a/zanata-war/src/main/webapp/WEB-INF/layout/language_view_inline.xhtml +++ /dev/null @@ -1,21 +0,0 @@ - - -
- -
- #{tribe.retrieveNativeName()} - -
-
-

#{tribe.retrieveDisplayName()} [#{tribe.localeId.id}] - - #{tribe.memberships.size} #{msgs['jsf.Members']}

-
- -
-
diff --git a/zanata-war/src/main/webapp/WEB-INF/layout/role_rule_edit_form.xhtml b/zanata-war/src/main/webapp/WEB-INF/layout/role_rule_edit_form.xhtml deleted file mode 100644 index aef8c8ed65..0000000000 --- a/zanata-war/src/main/webapp/WEB-INF/layout/role_rule_edit_form.xhtml +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - #{msgs['jsf.rolerules.PolicyName']} - - - - - - - #{msgs['jsf.rolerules.PolicyName.tooltip']} - - - - - #{msgs['jsf.rolerules.IdentityPattern']} - - - - #{msgs['jsf.rolerules.IdentityPattern.tooltip']} - - - - - #{msgs['jsf.rolerules.RoleToAssign']} - - - - - - #{msgs['jsf.rolerules.RoleToAssign.tooltip']} - - - - -
- -
- - - -
- - - diff --git a/zanata-war/src/main/webapp/WEB-INF/layout/transmemory_edit_form.xhtml b/zanata-war/src/main/webapp/WEB-INF/layout/transmemory_edit_form.xhtml index 5db9209673..f6f6bf36b3 100644 --- a/zanata-war/src/main/webapp/WEB-INF/layout/transmemory_edit_form.xhtml +++ b/zanata-war/src/main/webapp/WEB-INF/layout/transmemory_edit_form.xhtml @@ -1,55 +1,44 @@ - + xmlns:a4j="http://richfaces.org/a4j"> - - - - #{msgs['jsf.transmemory.TransMemoryId']} +
+ #{msgs['jsf.transmemory.TransMemoryId']} + + + + + value="#{translationMemoryHome.instance.slug}" + valueChangeListener="#{translationMemoryHome.verifySlugAvailable}"> - - - - #{msgs['jsf.transmemory.TransMemoryIdExample']} - - +
- - #{msgs['jsf.Description']} +
+ #{msgs['jsf.Description']} + + value="#{translationMemoryHome.instance.description}"/> +
-
- -
- -
+
+ action="#{translationMemoryHome.persist}" + rendered="#{!translationMemoryHome.managed}" + styleClass="button--primary button--full"/> - + action="#{translationMemoryHome.update}" + rendered="#{translationMemoryHome.managed}" + styleClass="button--primary button--full"/>
- + diff --git a/zanata-war/src/main/webapp/WEB-INF/layout/version/documents-tab.xhtml b/zanata-war/src/main/webapp/WEB-INF/layout/version/documents-tab.xhtml index ff1e20b78e..ccf730657d 100644 --- a/zanata-war/src/main/webapp/WEB-INF/layout/version/documents-tab.xhtml +++ b/zanata-war/src/main/webapp/WEB-INF/layout/version/documents-tab.xhtml @@ -30,13 +30,15 @@ aria-labelledby="dropdownContent">
  • - + #{msgs['jsf.iteration.files.UploadNewSourceDocument']} - +
  • @@ -44,7 +46,7 @@ rendered="#{s:hasPermission(versionHomeAction.version, 'update')}">
  • + onclick="changeBrowserUrl(this.href, true);return false;" class="i__item--right"> #{msgs['jsf.ManageDocuments']} @@ -78,9 +80,8 @@

    - + #{msgs['jsf.iteration.files.UploadNewSourceDocument']}

    @@ -255,7 +256,7 @@ aria-labelledby="dropdownContent">
  • + onclick="changeBrowserUrl(this.href, true);return false;" class="i__item--right"> #{msgs['jsf.ManageLanguage']} diff --git a/zanata-war/src/main/webapp/WEB-INF/layout/version/languages-tab.xhtml b/zanata-war/src/main/webapp/WEB-INF/layout/version/languages-tab.xhtml index 2b4bd7e1f6..93750b895b 100644 --- a/zanata-war/src/main/webapp/WEB-INF/layout/version/languages-tab.xhtml +++ b/zanata-war/src/main/webapp/WEB-INF/layout/version/languages-tab.xhtml @@ -8,38 +8,21 @@ xmlns:rich="http://richfaces.org/rich"> + +
    @@ -63,7 +46,7 @@ aria-labelledby="dropdownContent">
  • + onclick="changeBrowserUrl(this.href, true);return false;" class="i__item--right"> #{msgs['jsf.ManageLanguage']} @@ -198,11 +181,11 @@
  • - + #{msgs['jsf.iteration.files.DownloadAll']} @@ -212,7 +195,7 @@ #{msgs['jsf.iteration.files.DownloadAllOfflinePo']} - +
  • @@ -383,11 +366,11 @@
  • - - #{msgs['jsf.Upload.Label']} + + #{msgs['jsf.Upload.Label']} +
  • @@ -461,81 +444,10 @@ id="languageTabDocumentSearchBottom" bottomPanel="true" actionBean="#{versionHomeAction.languageTabDocumentFilter}"/> -
    - - - - - - - - - - - - -
    - - - -
    -
    -
    - - - - - - - - - -
    - - - - -
    - - -
    -
    -
    + + diff --git a/zanata-war/src/main/webapp/WEB-INF/layout/version/settings-tab.xhtml b/zanata-war/src/main/webapp/WEB-INF/layout/version/settings-tab.xhtml index 40e63e9953..d393892b41 100644 --- a/zanata-war/src/main/webapp/WEB-INF/layout/version/settings-tab.xhtml +++ b/zanata-war/src/main/webapp/WEB-INF/layout/version/settings-tab.xhtml @@ -174,7 +174,7 @@
    -
    diff --git a/zanata-war/src/main/webapp/WEB-INF/layout/version/zip_download_progress_modal.xhtml b/zanata-war/src/main/webapp/WEB-INF/layout/version/zip_download_progress_modal.xhtml new file mode 100644 index 0000000000..4b20951d36 --- /dev/null +++ b/zanata-war/src/main/webapp/WEB-INF/layout/version/zip_download_progress_modal.xhtml @@ -0,0 +1,112 @@ + diff --git a/zanata-war/src/main/webapp/WEB-INF/pages.xml b/zanata-war/src/main/webapp/WEB-INF/pages.xml index b24e319a00..588fde9d52 100644 --- a/zanata-war/src/main/webapp/WEB-INF/pages.xml +++ b/zanata-war/src/main/webapp/WEB-INF/pages.xml @@ -152,9 +152,15 @@ #{applicationConfiguration.internalAuth} - - + + + + + + + + + @@ -168,10 +174,6 @@ #{authenticationManager.isAuthenticatedAccountWaitingForActivation()} - - - @@ -196,37 +198,7 @@ #{s:hasRole('admin')} - - - - - - - - - - - - - - - - - - - - - - @@ -234,28 +206,8 @@ - - - - - - - - - - - - #{s:hasPermission('seam.role', 'read')} - - - @@ -265,14 +217,6 @@ - - - - - @@ -282,25 +226,12 @@ #{s:hasPermission('seam.user', 'read')} - - - - - - - - - @@ -309,74 +240,26 @@ - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - - - - - - @@ -456,8 +339,14 @@ - - + + + + + + + + @@ -468,28 +357,6 @@ execute="#{breadcrumbs.addLocation('', msgs['jsf.EditHelpPageContent'])}" /> - - #{identity.loggedIn} - - - - - - - - - - - - - - - @@ -524,7 +391,7 @@ - #{s:hasPermission(versionHome.instance, 'insert')} + #{s:hasPermission(projectHome.instance, 'insert')} @@ -585,46 +452,15 @@ - - + + + - - - - - - - - - #{identity.loggedIn} - - - - - - + - - - - - - - - - - @@ -664,25 +500,19 @@ #{s:hasRole('admin')} - - #{s:hasRole('admin')} - - - - - + + - - + + + + diff --git a/zanata-war/src/main/webapp/WEB-INF/template/template.xhtml b/zanata-war/src/main/webapp/WEB-INF/template/template.xhtml index a7cac6568f..882ced6e07 100755 --- a/zanata-war/src/main/webapp/WEB-INF/template/template.xhtml +++ b/zanata-war/src/main/webapp/WEB-INF/template/template.xhtml @@ -15,6 +15,10 @@ + + + + @@ -38,8 +42,22 @@ - + + + + @@ -62,5 +81,10 @@ + + + + diff --git a/zanata-war/src/main/webapp/WEB-INF/template/template_nobanner.xhtml b/zanata-war/src/main/webapp/WEB-INF/template/template_nobanner.xhtml index bfd53a77fd..3237934793 100644 --- a/zanata-war/src/main/webapp/WEB-INF/template/template_nobanner.xhtml +++ b/zanata-war/src/main/webapp/WEB-INF/template/template_nobanner.xhtml @@ -16,6 +16,10 @@ + + + + @@ -42,8 +46,22 @@ #{msgs['jsf.Zanata']}: <ui:insert name="page_title"/> - + + + @@ -59,5 +77,10 @@ + + + + diff --git a/zanata-war/src/main/webapp/WEB-INF/urlrewrite.xml b/zanata-war/src/main/webapp/WEB-INF/urlrewrite.xml index 2bbfc43ca3..b5f55a3c4d 100644 --- a/zanata-war/src/main/webapp/WEB-INF/urlrewrite.xml +++ b/zanata-war/src/main/webapp/WEB-INF/urlrewrite.xml @@ -33,8 +33,8 @@ - ^/language/view/(.+)$ - /language/language.seam\?id=$1 + ^/language/view/([^/]+)(/([^?]*)\??(.*))?$ + /language/language.seam\?id=$1&$4 @@ -49,13 +49,6 @@ - - /language/contact/(.+)$ - /language/contact_coordinator.seam\?emailType=contact_coordinator&id=$1 - - - @@ -313,10 +306,6 @@ $1/admin/$2$3 - - ^(/.+)?/language/contact_coordinator.seam\?emailType=contact_coordinator&id=(.+)$ - $1/language/contact/$2 - ^(/.+)?/language/request_to_join_update_role.seam\?emailType=request_to_join_update_role_language&id=(.+)$ $1/language/join/$2 diff --git a/zanata-war/src/main/webapp/account/activate.xhtml b/zanata-war/src/main/webapp/account/activate.xhtml index 6e856dad92..dd72a44895 100644 --- a/zanata-war/src/main/webapp/account/activate.xhtml +++ b/zanata-war/src/main/webapp/account/activate.xhtml @@ -1,13 +1,8 @@ + template="../WEB-INF/template/template.xhtml"> #{msgs['jsf.ActivateAccount']} diff --git a/zanata-war/src/main/webapp/account/inactive_account.xhtml b/zanata-war/src/main/webapp/account/inactive_account.xhtml index cad4784ada..9e202448ef 100644 --- a/zanata-war/src/main/webapp/account/inactive_account.xhtml +++ b/zanata-war/src/main/webapp/account/inactive_account.xhtml @@ -1,84 +1,61 @@ + template="../WEB-INF/template/template_nobanner.xhtml"> #{msgs['jsf.InactiveAccount']} - - - - - - - - - - - - - - - - - - #{msgs['jsf.Email']} - - - - - - #{msgs['jsf.EmailToolTip']} - - - - -
    - - -