master moved from 92a0d41c5056 to 6dcf4d936436 4 new revisions: Revision: 5087ee187803 Author: Andrew Lindesay <apl@xxxxxxxxxxxxxx> Date: Sat Aug 30 10:29:45 2014 UTC Log: simple / limited user interface for simple browsers https://code.google.com/p/haiku-depot-web-app/source/detail?r=5087ee187803 Revision: 8dbcbd8a7f4b Author: Andrew Lindesay <apl@xxxxxxxxxxxxxx> Date: Sun Aug 31 09:07:52 2014 UTCLog: sort out problems with pagination links in java as well as javascript
https://code.google.com/p/haiku-depot-web-app/source/detail?r=8dbcbd8a7f4b Revision: ccc92b6dfc59 Author: Andrew Lindesay <apl@xxxxxxxxxxxxxx> Date: Sun Aug 31 10:18:17 2014 UTC Log: ability to get localized names back for some reference data... https://code.google.com/p/haiku-depot-web-app/source/detail?r=ccc92b6dfc59 Revision: 6dcf4d936436 Author: Andrew Lindesay <apl@xxxxxxxxxxxxxx> Date: Sun Aug 31 12:05:02 2014 UTC Log: updates to integration tests for lower case pkg category names... https://code.google.com/p/haiku-depot-web-app/source/detail?r=6dcf4d936436 ============================================================================== Revision: 5087ee187803 Author: Andrew Lindesay <apl@xxxxxxxxxxxxxx> Date: Sat Aug 30 10:29:45 2014 UTC Log: simple / limited user interface for simple browsers https://code.google.com/p/haiku-depot-web-app/source/detail?r=5087ee187803 Added:/haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/MultipageConstants.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/MultipageHelper.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/MultipageLocaleResolver.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/MultipageObjectNotFoundException.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/controller/HomeController.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/controller/ViewPkgController.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/markup/NaturalLanguageChooserTag.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/markup/PaginationLinksTag.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/markup/PkgIconTag.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/markup/PkgVersionLabelTag.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/markup/PkgVersionLinkTag.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/markup/PlainTextContentTag.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/markup/RatingIndicatorTag.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/markup/TimestampTag.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/model/Pagination.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/package-info.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/singlepage/controller/EntryPointController.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/singlepage/package-info.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/support/web/RobotController.java
/haikudepotserver-webapp/src/main/webapp/WEB-INF/includes/favicons.jsp /haikudepotserver-webapp/src/main/webapp/WEB-INF/multipage.tld /haikudepotserver-webapp/src/main/webapp/WEB-INF/views/multipage/home.jsp/haikudepotserver-webapp/src/main/webapp/WEB-INF/views/multipage/includes/banner.jsp /haikudepotserver-webapp/src/main/webapp/WEB-INF/views/multipage/includes/prelude.jsp /haikudepotserver-webapp/src/main/webapp/WEB-INF/views/multipage/viewPkgVersion.jsp /haikudepotserver-webapp/src/main/webapp/WEB-INF/views/singlepage/launch.jsp
/haikudepotserver-webapp/src/main/webapp/css/multipage/banner.css/haikudepotserver-webapp/src/main/webapp/css/singlepage/addedituserrating.css
/haikudepotserver-webapp/src/main/webapp/css/singlepage/banner.css /haikudepotserver-webapp/src/main/webapp/css/singlepage/breadcrumbs.css /haikudepotserver-webapp/src/main/webapp/css/singlepage/createuser.css/haikudepotserver-webapp/src/main/webapp/css/singlepage/editpkgscreenshots.css /haikudepotserver-webapp/src/main/webapp/css/singlepage/editpkgversionlocalization.css
/haikudepotserver-webapp/src/main/webapp/css/singlepage/home.css/haikudepotserver-webapp/src/main/webapp/css/singlepage/listauthorizationpkgrules.css /haikudepotserver-webapp/src/main/webapp/css/singlepage/listrepositories.css
/haikudepotserver-webapp/src/main/webapp/css/singlepage/listusers.css /haikudepotserver-webapp/src/main/webapp/css/singlepage/main.css /haikudepotserver-webapp/src/main/webapp/css/singlepage/pkgfeedbuilder.css /haikudepotserver-webapp/src/main/webapp/css/singlepage/unsupported.css /haikudepotserver-webapp/src/main/webapp/css/singlepage/viewpkg.css /haikudepotserver-webapp/src/main/webapp/img/paginationleft.png /haikudepotserver-webapp/src/main/webapp/img/paginationleft.svg /haikudepotserver-webapp/src/main/webapp/img/paginationright.png /haikudepotserver-webapp/src/main/webapp/img/paginationright.svg /haikudepotserver-webapp/src/main/webapp/img/starhalf.png /haikudepotserver-webapp/src/main/webapp/img/staroff.png /haikudepotserver-webapp/src/main/webapp/img/staron.png/haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/multipage/model/PaginationTest.java
Deleted:/haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/web/controller/EntryPointController.java
/haikudepotserver-webapp/src/main/webapp/WEB-INF/views/entryPoint.jsp /haikudepotserver-webapp/src/main/webapp/css/addedituserrating.css /haikudepotserver-webapp/src/main/webapp/css/banner.css /haikudepotserver-webapp/src/main/webapp/css/breadcrumbs.css /haikudepotserver-webapp/src/main/webapp/css/createuser.css /haikudepotserver-webapp/src/main/webapp/css/editpkgscreenshots.css /haikudepotserver-webapp/src/main/webapp/css/editpkgversionlocalization.css /haikudepotserver-webapp/src/main/webapp/css/haikudepotserver.css /haikudepotserver-webapp/src/main/webapp/css/home.css /haikudepotserver-webapp/src/main/webapp/css/listauthorizationpkgrules.css /haikudepotserver-webapp/src/main/webapp/css/listrepositories.css /haikudepotserver-webapp/src/main/webapp/css/listusers.css /haikudepotserver-webapp/src/main/webapp/css/pkgfeedbuilder.css /haikudepotserver-webapp/src/main/webapp/css/unsupported.css /haikudepotserver-webapp/src/main/webapp/css/viewpkg.css/haikudepotserver-webapp/src/main/webapp/js/app/directive/paginationarrowdirective.js
Modified:/haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/support/ObjectNotFoundException.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/PkgApiImpl.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/Architecture.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/NaturalLanguage.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/PkgCategory.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/PkgVersion.java
/haikudepotserver-webapp/src/main/resources/jawr.properties /haikudepotserver-webapp/src/main/resources/messages.properties /haikudepotserver-webapp/src/main/resources/messages_de.properties /haikudepotserver-webapp/src/main/resources/spring/servlet-context.xml /haikudepotserver-webapp/src/main/webapp/WEB-INF/includes/unsupported.jsp /haikudepotserver-webapp/src/main/webapp/js/app/constants.js /haikudepotserver-webapp/src/main/webapp/js/app/controller/about.html/haikudepotserver-webapp/src/main/webapp/js/app/directive/paginationcontroldirective.js /haikudepotserver-webapp/src/main/webapp/js/app/directive/ratingindicatordirective.js /haikudepotserver-webapp/src/main/webapp/js/app/service/breadcrumbfactoryservice.js
/haikudepotserver-webapp/src/main/webapp/js/app/service/pkgservice.js ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/MultipageConstants.java Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,14 @@ +package org.haikuos.haikudepotserver.multipage; + +/* + * Copyright 2014, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +public class MultipageConstants { + + public final static String PATH_MULTIPAGE = "/multipage"; + + public final static String KEY_NATURALLANGUAGECODE = "natlangcode"; + +} ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/MultipageHelper.java Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,68 @@ +/* + * Copyright 2014, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +package org.haikuos.haikudepotserver.multipage; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.base.Splitter; +import com.google.common.base.Strings; +import org.apache.cayenne.ObjectContext; +import org.haikuos.haikudepotserver.dataobjects.NaturalLanguage; + +import javax.servlet.http.HttpServletRequest; +import java.util.Iterator; +import java.util.Locale; +import java.util.regex.Pattern; + +/** + * <p>Helper (static) methods for the multipage part of the system.</p> + */ + +public class MultipageHelper { + + /**+ * <p>This will look at parameters on the supplied request and will return a natural language. It will + * resort to English language if no other language is able to be derived.</p>
+ */ ++ public static NaturalLanguage deriveNaturalLanguage(ObjectContext context, HttpServletRequest request) {
+ Preconditions.checkNotNull(context); + + if(null!=request) {+ String naturalLanguageCode = request.getParameter(MultipageConstants.KEY_NATURALLANGUAGECODE);
+ + if(!Strings.isNullOrEmpty(naturalLanguageCode)) {+ Optional<NaturalLanguage> naturalLanguageOptional = NaturalLanguage.getByCode(context, naturalLanguageCode);
+ + if(!naturalLanguageOptional.isPresent()) {+ throw new IllegalStateException("the natural language for code " + naturalLanguageCode + " was not able to be found");
+ } + + return naturalLanguageOptional.get(); + } + + // see if we can deduce it from the locale. + + Locale locale = request.getLocale(); + + if(null != locale) {+ Iterator<String> langI = Splitter.on(Pattern.compile("[-_]")).split(locale.toLanguageTag()).iterator();
+ + if(langI.hasNext()) {+ Optional<NaturalLanguage> naturalLanguageOptional = NaturalLanguage.getByCode(context, langI.next());
+ + if(naturalLanguageOptional.isPresent()) { + return naturalLanguageOptional.get(); + } + } + + } + } ++ return NaturalLanguage.getByCode(context, NaturalLanguage.CODE_ENGLISH).get();
+ } + +} ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/MultipageLocaleResolver.java Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,29 @@ +/* + * Copyright 2014, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +package org.haikuos.haikudepotserver.multipage; + +import org.apache.cayenne.configuration.server.ServerRuntime; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.Locale; ++public class MultipageLocaleResolver implements org.springframework.web.servlet.LocaleResolver {
+ + @Resource + ServerRuntime serverRuntime; + + @Override + public Locale resolveLocale(HttpServletRequest request) {+ return MultipageHelper.deriveNaturalLanguage(serverRuntime.getContext(), request).toLocale();
+ } + + @Override+ public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
+ // ignore. + } +} ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/MultipageObjectNotFoundException.java Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,49 @@ +/* + * Copyright 2014, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +package org.haikuos.haikudepotserver.multipage; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * <p>This exception will return a 404 when it arises.</p> + */ + +@ResponseStatus(HttpStatus.NOT_FOUND) +public class MultipageObjectNotFoundException extends Exception { + + public String entityName; + public Object identifier; ++ public MultipageObjectNotFoundException(String entityName, Object identifier) {
+ super(); + + if(null==entityName || 0==entityName.length()) { + throw new IllegalStateException("the entity name is required"); + } + + if(null==identifier) { + throw new IllegalStateException("the identifier is required"); + } + + this.entityName = entityName; + this.identifier = identifier; + } + + public String getEntityName() { + return entityName; + } + + public Object getIdentifier() { + return identifier; + } + + @Override + public String getMessage() {+ return String.format("the entity %s was not able to be found with the identifier %s",getEntityName(),getIdentifier().toString());
+ } + +} ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/controller/HomeController.java Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,299 @@ +/* + * Copyright 2014, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +package org.haikuos.haikudepotserver.multipage.controller; + +import com.google.common.base.Optional; +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import org.apache.cayenne.ObjectContext; +import org.apache.cayenne.configuration.server.ServerRuntime; +import org.haikuos.haikudepotserver.dataobjects.Architecture; +import org.haikuos.haikudepotserver.dataobjects.PkgCategory; +import org.haikuos.haikudepotserver.dataobjects.PkgVersion; +import org.haikuos.haikudepotserver.multipage.MultipageConstants; +import org.haikuos.haikudepotserver.multipage.MultipageHelper; +import org.haikuos.haikudepotserver.multipage.model.Pagination; +import org.haikuos.haikudepotserver.pkg.PkgOrchestrationService; +import org.haikuos.haikudepotserver.pkg.model.PkgSearchSpecification; +import org.haikuos.haikudepotserver.support.AbstractSearchSpecification; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.servlet.ModelAndView; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import java.util.List; +import java.util.Set; + +/**+ * <p>Renders the home page of the multi-page (simple) view of the application.</p>
+ */ + +@Controller +@RequestMapping(MultipageConstants.PATH_MULTIPAGE) +public class HomeController { + + /** + * <p>This defines the type of display of packages that are shown.</p> + */ + + public enum ViewCriteriaType { + FEATURED, + ALL, + CATEGORIES, + MOSTRECENT, + MOSTVIEWED; + + public String getTitleKey() { + return "home.viewCriteriaType." + name().toLowerCase(); + } + + } + + // these should correspond to the single-page keys for the home page. + public final static String KEY_OFFSET = "o"; + public final static String KEY_ARCHITECTURECODE = "arch"; + public final static String KEY_PKGCATEGORYCODE = "pkgcat"; + public final static String KEY_SEARCHEXPRESSION = "srchexpr"; + public final static String KEY_VIEWCRITERIATYPECODE = "viewcrttyp"; + + public final static int PAGESIZE = 15; + + @Resource + ServerRuntime serverRuntime; + + @Resource + PkgOrchestrationService pkgOrchestrationService; + + /**+ * <p>This is the entry point for the home page. It will look at the parameters supplied and will
+ * establish what should be displayed.</p> + */ + + @RequestMapping(method = RequestMethod.GET) + public ModelAndView home( + HttpServletRequest httpServletRequest,+ @RequestParam(value=KEY_OFFSET, defaultValue = "0") Integer offset, + @RequestParam(value=KEY_ARCHITECTURECODE, defaultValue = Architecture.CODE_X86) String architectureCode, + @RequestParam(value=KEY_PKGCATEGORYCODE, required=false) String pkgCategoryCode, + @RequestParam(value=KEY_SEARCHEXPRESSION, required=false) String searchExpression, + @RequestParam(value=KEY_VIEWCRITERIATYPECODE, required=false) ViewCriteriaType viewCriteriaType) {
+ + ObjectContext context = serverRuntime.getContext(); + + // ------------------------------ + // FETCH THE DATA ++ PkgSearchSpecification searchSpecification = new PkgSearchSpecification();
+ + searchSpecification.setOffset(offset); + searchSpecification.setLimit(PAGESIZE); + searchSpecification.setExpression(searchExpression);+ searchSpecification.setExpressionType(AbstractSearchSpecification.ExpressionType.CONTAINS);
++ Optional<Architecture> architectureOptional = Architecture.getByCode(context, architectureCode);
+ + if(!architectureOptional.isPresent()) {+ throw new IllegalStateException("unable to obtain the architecture; " + architectureCode);
+ } + + searchSpecification.setArchitecture(architectureOptional.get()); + + Optional<PkgCategory> pkgCategoryOptional = Optional.absent(); + + if(null!=pkgCategoryCode) {+ pkgCategoryOptional = PkgCategory.getByCode(context, pkgCategoryCode);
+ } ++ searchSpecification.setNaturalLanguage(MultipageHelper.deriveNaturalLanguage(context, httpServletRequest));
++ switch(null==viewCriteriaType ? ViewCriteriaType.FEATURED : viewCriteriaType) {
+ + case FEATURED:+ searchSpecification.setSortOrdering(PkgSearchSpecification.SortOrdering.PROMINENCE);
+ break; + + case CATEGORIES:+ searchSpecification.setSortOrdering(PkgSearchSpecification.SortOrdering.NAME);
+ + if(!pkgCategoryOptional.isPresent()) {+ throw new IllegalStateException("the pkg category code was unable to be found; " + pkgCategoryCode);
+ } ++ searchSpecification.setPkgCategory(pkgCategoryOptional.get());
+ + break; + + case ALL:+ searchSpecification.setSortOrdering(PkgSearchSpecification.SortOrdering.NAME);
+ break; + + case MOSTVIEWED:+ searchSpecification.setSortOrdering(PkgSearchSpecification.SortOrdering.VERSIONVIEWCOUNTER);
+ break; + + case MOSTRECENT:+ searchSpecification.setSortOrdering(PkgSearchSpecification.SortOrdering.VERSIONCREATETIMESTAMP);
+ break; + + default:+ throw new IllegalStateException("unhandled view criteria type");
+ + } ++ Long totalPkgVersions = pkgOrchestrationService.total(context, searchSpecification);
+ + if(searchSpecification.getOffset() > totalPkgVersions) { + searchSpecification.setOffset(totalPkgVersions.intValue()); + } ++ List<PkgVersion> pkgVersions = pkgOrchestrationService.search(context, searchSpecification, null);
+ + // ------------------------------ + // GENERATE OUTPUT + + HomeData data = new HomeData(); + + final Set<String> excludedArchitectureCode = ImmutableSet.of( + Architecture.CODE_ANY, + Architecture.CODE_SOURCE + ); + + data.setAllArchitectures(Lists.newArrayList( + ImmutableList.copyOf( + Iterables.filter( + Architecture.getAll(context), + new Predicate<Architecture>() { + @Override+ public boolean apply(Architecture input) { + return !excludedArchitectureCode.contains(input.getCode());
+ } + } + ) + ) + )); + + data.setArchitecture(architectureOptional.get()); + + data.setAllPkgCategories(PkgCategory.getAll(context));+ data.setPkgCategory(pkgCategoryOptional.isPresent() ? pkgCategoryOptional.get() : PkgCategory.getAll(context).get(0));
++ data.setAllViewCriteriaTypes(ImmutableList.copyOf(ViewCriteriaType.values()));
+ data.setViewCriteriaType(viewCriteriaType); + + data.setSearchExpression(searchExpression); + data.setPkgVersions(pkgVersions);+ data.setPagination(new Pagination(totalPkgVersions.intValue(), offset, PAGESIZE));
+ + ModelAndView result = new ModelAndView("multipage/home"); + result.addObject("data", data); + + return result; + } + + /** + * <p>This is the data model for the page to be rendered from.</p> + */ + + public static class HomeData { + + private List<PkgVersion> pkgVersions; + + private List<Architecture> allArchitectures; + + private List<PkgCategory> allPkgCategories; + + private List<ViewCriteriaType> allViewCriteriaTypes; + + private Architecture architecture; + + private PkgCategory pkgCategory; + + private String searchExpression; + + private ViewCriteriaType viewCriteriaType; + + private Pagination pagination; + + public List<PkgVersion> getPkgVersions() { + return pkgVersions; + } + + public void setPkgVersions(List<PkgVersion> pkgVersions) { + this.pkgVersions = pkgVersions; + } + + public List<Architecture> getAllArchitectures() { + return allArchitectures; + } ++ public void setAllArchitectures(List<Architecture> allArchitectures) {
+ this.allArchitectures = allArchitectures; + } + + public Architecture getArchitecture() { + return architecture; + } + + public void setArchitecture(Architecture architecture) { + this.architecture = architecture; + } + + public String getSearchExpression() { + return searchExpression; + } + + public void setSearchExpression(String searchExpression) { + this.searchExpression = searchExpression; + } + + public List<PkgCategory> getAllPkgCategories() { + return allPkgCategories; + } ++ public void setAllPkgCategories(List<PkgCategory> allPkgCategories) {
+ this.allPkgCategories = allPkgCategories; + } + + public List<ViewCriteriaType> getAllViewCriteriaTypes() { + return allViewCriteriaTypes; + } ++ public void setAllViewCriteriaTypes(List<ViewCriteriaType> allViewCriteriaTypes) {
+ this.allViewCriteriaTypes = allViewCriteriaTypes; + } + + public PkgCategory getPkgCategory() { + return pkgCategory; + } + + public void setPkgCategory(PkgCategory pkgCategory) { + this.pkgCategory = pkgCategory; + } + + public ViewCriteriaType getViewCriteriaType() { + return viewCriteriaType; + } ++ public void setViewCriteriaType(ViewCriteriaType viewCriteriaType) {
+ this.viewCriteriaType = viewCriteriaType; + } + + public Pagination getPagination() { + return pagination; + } + + public void setPagination(Pagination pagination) { + this.pagination = pagination; + } + } + +} ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/controller/ViewPkgController.java Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,163 @@ +/* + * Copyright 2014, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +package org.haikuos.haikudepotserver.multipage.controller; + +import com.google.common.base.Optional; +import com.google.common.base.Strings; +import org.apache.cayenne.ObjectContext; +import org.apache.cayenne.configuration.server.ServerRuntime; +import org.haikuos.haikudepotserver.dataobjects.Architecture; +import org.haikuos.haikudepotserver.dataobjects.NaturalLanguage; +import org.haikuos.haikudepotserver.dataobjects.Pkg; +import org.haikuos.haikudepotserver.dataobjects.PkgVersion; +import org.haikuos.haikudepotserver.multipage.MultipageConstants; +import org.haikuos.haikudepotserver.multipage.MultipageHelper;+import org.haikuos.haikudepotserver.multipage.MultipageObjectNotFoundException;
+import org.haikuos.haikudepotserver.support.VersionCoordinates; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.util.UriComponentsBuilder; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; + +/** + * <p>'Page' for showing a version of a package.</p> + */ + +@Controller +@RequestMapping(MultipageConstants.PATH_MULTIPAGE + "/pkg") +public class ViewPkgController { + + @Resource + ServerRuntime serverRuntime; + + private String hyphenToNull(String part) { + if(null!=part && !part.equals("-")) { + return part; + } + + return null; + } ++ @RequestMapping(value = "{name}/{major}/{minor}/{micro}/{preRelease}/{revision}/{architectureCode}", method = RequestMethod.GET)
+ public ModelAndView viewPkg( + HttpServletRequest httpServletRequest, + @PathVariable(value="name") String pkgName, + @PathVariable(value="major") String major, + @PathVariable(value="minor") String minor, + @PathVariable(value="micro") String micro, + @PathVariable(value="preRelease") String preRelease, + @PathVariable(value="revision") String revisionStr,+ @PathVariable(value="architectureCode") String architectureCode) throws MultipageObjectNotFoundException {
+ + major = hyphenToNull(major); + minor = hyphenToNull(minor); + micro = hyphenToNull(micro); + preRelease = hyphenToNull(preRelease); + revisionStr = hyphenToNull(revisionStr); ++ Integer revision = null==revisionStr ? null : Integer.parseInt(revisionStr);
+ + ObjectContext context = serverRuntime.getContext(); + Optional<Pkg> pkgOptional = Pkg.getByName(context, pkgName); + + if(!pkgOptional.isPresent()) {+ throw new MultipageObjectNotFoundException(Pkg.class.getSimpleName(), pkgName); // 404
+ } ++ Optional<Architecture> architectureOptional = Architecture.getByCode(context, architectureCode);
+ + if(!architectureOptional.isPresent()) {+ throw new MultipageObjectNotFoundException(Architecture.class.getSimpleName(), architectureCode);
+ } + + VersionCoordinates coordinates = new VersionCoordinates( + Strings.emptyToNull(major), + Strings.emptyToNull(minor), + Strings.emptyToNull(micro), + Strings.emptyToNull(preRelease), + revision); + + Optional<PkgVersion> pkgVersionOptional = PkgVersion.getForPkg( + context, + pkgOptional.get(), + architectureOptional.get(), + coordinates); + + if(!pkgVersionOptional.isPresent()) {+ throw new MultipageObjectNotFoundException(PkgVersion.class.getSimpleName(), pkgName + "...");
+ } + + String homeUrl; + + {+ UriComponentsBuilder builder = UriComponentsBuilder.fromPath(MultipageConstants.PATH_MULTIPAGE); + String naturalLanguageCode = httpServletRequest.getParameter(MultipageConstants.KEY_NATURALLANGUAGECODE);
+ + if(!Strings.isNullOrEmpty(naturalLanguageCode)) {+ builder.queryParam(MultipageConstants.KEY_NATURALLANGUAGECODE, naturalLanguageCode);
+ } + + homeUrl = builder.build().toString(); + } + + ViewPkgVersionData data = new ViewPkgVersionData(); + + data.setPkgVersion(pkgVersionOptional.get());+ data.setCurrentNaturalLanguage(MultipageHelper.deriveNaturalLanguage(context, httpServletRequest));
+ data.setHomeUrl(homeUrl); + + ModelAndView result = new ModelAndView("multipage/viewPkgVersion"); + result.addObject("data", data); + + return result; + + } + + + /** + * <p>This is the data model for the page to be rendered from.</p> + */ + + public static class ViewPkgVersionData { + + private PkgVersion pkgVersion; + + private NaturalLanguage currentNaturalLanguage; + + private String homeUrl; + + public PkgVersion getPkgVersion() { + return pkgVersion; + } + + public void setPkgVersion(PkgVersion pkgVersion) { + this.pkgVersion = pkgVersion; + } + + public NaturalLanguage getCurrentNaturalLanguage() { + return currentNaturalLanguage; + } ++ public void setCurrentNaturalLanguage(NaturalLanguage currentNaturalLanguage) {
+ this.currentNaturalLanguage = currentNaturalLanguage; + } + + public String getHomeUrl() { + return homeUrl; + } + + public void setHomeUrl(String homeUrl) { + this.homeUrl = homeUrl; + } + } + + +} ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/markup/NaturalLanguageChooserTag.java Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,86 @@ +/* + * Copyright 2014, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +package org.haikuos.haikudepotserver.multipage.markup; + +import com.google.common.collect.Lists; +import org.apache.cayenne.ObjectContext; +import org.apache.cayenne.configuration.server.ServerRuntime; +import org.haikuos.haikudepotserver.dataobjects.NaturalLanguage; +import org.haikuos.haikudepotserver.multipage.MultipageConstants; +import org.haikuos.haikudepotserver.multipage.MultipageHelper; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; +import org.springframework.web.servlet.tags.RequestContextAwareTag; +import org.springframework.web.servlet.tags.form.TagWriter; + +import javax.servlet.http.HttpServletRequest; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +/**+ * <p>This tag renders a list of languages allowing the user to choose one of those languages.</p>
+ */ + +public class NaturalLanguageChooserTag extends RequestContextAwareTag { + + private ObjectContext getObjectContext() {+ return getRequestContext().getWebApplicationContext().getBean(ServerRuntime.class).getContext();
+ } + + @Override + protected int doStartTagInternal() throws Exception { + + ObjectContext context = getObjectContext(); + TagWriter tagWriter = new TagWriter(pageContext.getOut());+ List<NaturalLanguage> naturalLanguages = Lists.newArrayList(NaturalLanguage.getAll(context)); + NaturalLanguage currentNaturalLanguage = MultipageHelper.deriveNaturalLanguage(
+ context, + (HttpServletRequest) pageContext.getRequest()); ++ Collections.sort(naturalLanguages, new Comparator<NaturalLanguage>() {
+ @Override+ public int compare(NaturalLanguage o1, NaturalLanguage o2) {
+ return o1.getCode().compareTo(o2.getCode()); + } + } + ); + + tagWriter.startTag("span");+ tagWriter.writeAttribute("class","multipage-natural-language-chooser");
+ + for(int i=0;i<naturalLanguages.size();i++) { + + NaturalLanguage naturalLanguage = naturalLanguages.get(i); + + if(0 != i) { + tagWriter.appendValue(" "); + } + + if(currentNaturalLanguage != naturalLanguage) { ++ ServletUriComponentsBuilder builder = ServletUriComponentsBuilder.fromCurrentRequest(); + builder.replaceQueryParam(MultipageConstants.KEY_NATURALLANGUAGECODE, naturalLanguage.getCode());
+ + tagWriter.startTag("a");+ tagWriter.writeAttribute("href",builder.build().toString());
+ tagWriter.appendValue(naturalLanguage.getCode()); + tagWriter.endTag(); // a + + } + else { + tagWriter.startTag("strong"); + tagWriter.appendValue(naturalLanguage.getCode()); + tagWriter.endTag(); // strong + } + + } + + tagWriter.endTag(); // span + + return SKIP_BODY; + } + +} ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/markup/PaginationLinksTag.java Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,131 @@ +/* + * Copyright 2014, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +package org.haikuos.haikudepotserver.multipage.markup; + +import com.google.common.base.Joiner; +import com.google.common.collect.Lists; +import org.haikuos.haikudepotserver.multipage.model.Pagination; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; +import org.springframework.web.servlet.tags.RequestContextAwareTag; +import org.springframework.web.servlet.tags.form.TagWriter; + +import javax.servlet.jsp.JspException; +import java.util.List; + +/**+ * <p>This is a JSP tag that is able to render some hyperlinks that allow the user to browse around some linear list + * of data items. See {@link org.haikuos.haikudepotserver.multipage.model.Pagination} for the model that produces
+ * the series of page numbers.</p> + */ + +public class PaginationLinksTag extends RequestContextAwareTag { + + private final static int LINK_COUNT_DEFAULT = 10; + + private Integer linkCount; + + private Pagination pagination; + + public Pagination getPagination() { + return pagination; + } + + public void setPagination(Pagination value) { + this.pagination = value; + } + + public Integer getLinkCount() { + return linkCount; + } + + @SuppressWarnings("UnusedDeclaration") + public void setLinkCount(Integer linkCount) { + this.linkCount = linkCount; + } + + private String deriveHref(int targetPage) {+ ServletUriComponentsBuilder builder = ServletUriComponentsBuilder.fromCurrentRequest(); + builder.replaceQueryParam("o", Integer.toString(targetPage * getPagination().getMax()));
+ return builder.build().toString(); + } + + private void writeArrow( + TagWriter tagWriter, + String imageFilename, + String cssClassName, + String alt, + String href) throws JspException { + tagWriter.startTag("li"); + tagWriter.startTag("a"); + tagWriter.writeAttribute("class", cssClassName); + tagWriter.writeAttribute("href",href); + tagWriter.writeAttribute("alt",alt); + tagWriter.startTag("img"); + tagWriter.writeAttribute("src", "/img/" + imageFilename); + tagWriter.endTag(); // img + tagWriter.endTag(); // a + tagWriter.endTag(); // li + } + + @Override + protected int doStartTagInternal() throws Exception { + + TagWriter tagWriter = new TagWriter(pageContext.getOut()); + + Pagination p = getPagination(); + + List<String> ulClasses = Lists.newArrayList(); + ulClasses.add("pagination-control-container"); + + if(0==p.getPage()) { + ulClasses.add("pagination-control-on-first"); + } + + if(p.getPage() == p.getPages()-1) { + ulClasses.add("pagination-control-on-last"); + } + + tagWriter.startTag("ul"); + tagWriter.writeAttribute("class", Joiner.on(' ').join(ulClasses)); + + writeArrow( + tagWriter, + "paginationleft.png", + "pagination-control-left", + "<--", + 0==p.getPage() ? "" : deriveHref(p.getPage()-1)); ++ int[] pageNumbers = p.generateSuggestedPages(null==getLinkCount() ? LINK_COUNT_DEFAULT : getLinkCount());
+ + for(int pageNumber : pageNumbers) { + tagWriter.startTag("li"); + + if (pageNumber == p.getPage()) { + tagWriter.startTag("span");+ tagWriter.writeAttribute("class", "pagination-control-currentpage");
+ tagWriter.writeAttribute("href", ""); + } else { + tagWriter.startTag("a"); + tagWriter.writeAttribute("href", deriveHref(pageNumber)); + } + + tagWriter.appendValue(Integer.toString(pageNumber + 1)); + tagWriter.endTag(); // a + tagWriter.endTag(); // li + } + + writeArrow( + tagWriter, + "paginationright.png", + "pagination-control-right", + "-->",+ p.getPage() == p.getPages()-1 ? "" : deriveHref(p.getPage()+1));
+ + tagWriter.endTag(); // ul + + return SKIP_BODY; + } +} ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/markup/PkgIconTag.java Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,92 @@ +/* + * Copyright 2014, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +package org.haikuos.haikudepotserver.multipage.markup; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.net.MediaType; +import org.apache.cayenne.ObjectContext; +import org.haikuos.haikudepotserver.dataobjects.PkgIcon; +import org.haikuos.haikudepotserver.dataobjects.PkgVersion; +import org.springframework.web.servlet.tags.RequestContextAwareTag; +import org.springframework.web.servlet.tags.form.TagWriter; +import org.springframework.web.util.UriComponentsBuilder; + +/** + * <p>Renders HTML for a package version's icon.</p> + */ + +public class PkgIconTag extends RequestContextAwareTag { + + private PkgVersion pkgVersion; + + private int size = 16; + + public PkgVersion getPkgVersion() { + return pkgVersion; + } + + public void setPkgVersion(PkgVersion pkgVersion) { + this.pkgVersion = pkgVersion; + } + + public int getSize() { + return size; + } + + public void setSize(int size) { + this.size = size; + } + + private String getUrl() { + ObjectContext context = getPkgVersion().getObjectContext();+ Optional<org.haikuos.haikudepotserver.dataobjects.MediaType> pngOptional = + org.haikuos.haikudepotserver.dataobjects.MediaType.getByCode(context, MediaType.PNG.toString()); + Optional<PkgIcon> pkgIconOptional = pkgVersion.getPkg().getPkgIcon(pngOptional.get(), getSize());
+ + if(pkgIconOptional.isPresent()) { + return + UriComponentsBuilder.newInstance()+ .pathSegment("pkgicon", getPkgVersion().getPkg().getName() + ".png")
+ .queryParam("f","true") + .queryParam("s",Integer.toString(getSize())) + .queryParam("m",Long.toString(getPkgVersion().getPkg().getModifyTimestamp().getTime())) + .build() + .toString(); + } + else { + switch(size) { + case 16: + return "/img/generic16.png"; + + case 32: + return "/img/generic32.png"; + + default:+ throw new IllegalStateException("unknown size for default icon");
+ } + } + } + + @Override + protected int doStartTagInternal() throws Exception { + + Preconditions.checkNotNull(pkgVersion); + Preconditions.checkState(getSize()==16 || getSize()==32); + + TagWriter tagWriter = new TagWriter(pageContext.getOut()); + + tagWriter.startTag("img"); + tagWriter.writeAttribute("src", getUrl()); + tagWriter.writeAttribute("alt", "icon"); + tagWriter.endTag(); + + return SKIP_BODY; + } + +} + + ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/markup/PkgVersionLabelTag.java Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,44 @@ +/* + * Copyright 2014, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +package org.haikuos.haikudepotserver.multipage.markup; + +import com.google.common.base.Preconditions; +import com.google.common.html.HtmlEscapers; +import org.haikuos.haikudepotserver.dataobjects.PkgVersion; +import org.springframework.web.servlet.tags.RequestContextAwareTag; +import org.springframework.web.servlet.tags.form.TagWriter; + +/** + * <P>Renders the version of coordinates of a package version.</P> + */ + +public class PkgVersionLabelTag extends RequestContextAwareTag { + + private PkgVersion pkgVersion; + + public PkgVersion getPkgVersion() { + return pkgVersion; + } + + public void setPkgVersion(PkgVersion pkgVersion) { + this.pkgVersion = pkgVersion; + } + + @Override + protected int doStartTagInternal() throws Exception { + + Preconditions.checkNotNull(pkgVersion); + + TagWriter tagWriter = new TagWriter(pageContext.getOut()); + + tagWriter.startTag("span");+ tagWriter.appendValue(HtmlEscapers.htmlEscaper().escape(pkgVersion.toVersionCoordinates().toString()));
+ tagWriter.endTag(); + + return SKIP_BODY; + } + +} ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/markup/PkgVersionLinkTag.java Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,90 @@ +/* + * Copyright 2014, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +package org.haikuos.haikudepotserver.multipage.markup; + +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; +import com.google.common.html.HtmlEscapers; +import org.haikuos.haikudepotserver.dataobjects.PkgVersion; +import org.haikuos.haikudepotserver.multipage.MultipageConstants; +import org.springframework.web.servlet.tags.RequestContextAwareTag; +import org.springframework.web.util.UriComponentsBuilder; + +import javax.servlet.jsp.JspException; +import javax.servlet.jsp.JspWriter; +import java.io.IOException; + +/** + * <p>This tag renders a link to a package version.</p> + */ + +public class PkgVersionLinkTag extends RequestContextAwareTag { + + private PkgVersion pkgVersion; + + public PkgVersion getPkgVersion() { + return pkgVersion; + } + + public void setPkgVersion(PkgVersion pkgVersion) { + this.pkgVersion = pkgVersion; + } + + private String emptyToHyphen(String part) { + if(Strings.isNullOrEmpty(part)) { + return "-"; + } + + return part; + } + + @Override + protected int doStartTagInternal() throws Exception { + + Preconditions.checkNotNull(getPkgVersion()); + + JspWriter jspWriter = pageContext.getOut(); ++ UriComponentsBuilder builder = UriComponentsBuilder.fromPath(String.format(
+ "%s/pkg/%s/%s/%s/%s/%s/%s/%s", + MultipageConstants.PATH_MULTIPAGE, + pkgVersion.getPkg().getName(), + emptyToHyphen(pkgVersion.getMajor()), + emptyToHyphen(pkgVersion.getMinor()), + emptyToHyphen(pkgVersion.getMicro()), + emptyToHyphen(pkgVersion.getPreRelease()),+ null == pkgVersion.getRevision() ? "-" : pkgVersion.getRevision().toString(),
+ pkgVersion.getArchitecture().getCode())); ++ String naturalLanguageCode = pageContext.getRequest().getParameter(MultipageConstants.KEY_NATURALLANGUAGECODE);
+ + if(!Strings.isNullOrEmpty(naturalLanguageCode)) { + builder.queryParam( + MultipageConstants.KEY_NATURALLANGUAGECODE, + naturalLanguageCode); + } + + jspWriter.print("<a href=\"");+ jspWriter.print(HtmlEscapers.htmlEscaper().escape(builder.build().toString()));
+ jspWriter.print("\">"); + + return EVAL_BODY_INCLUDE; + } + + @Override + public int doEndTag() throws JspException { + JspWriter jspWriter = pageContext.getOut(); + + try { + jspWriter.print("</a>"); + } + catch(IOException ioe) {+ throw new JspException("unable to write the end of the pkg version link", ioe);
+ } + + return EVAL_PAGE; + } +} ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/markup/PlainTextContentTag.java Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,57 @@ +/* + * Copyright 2014, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +package org.haikuos.haikudepotserver.multipage.markup; + +import com.google.common.base.Function; +import com.google.common.base.Joiner; +import com.google.common.base.Splitter; +import com.google.common.base.Strings; +import com.google.common.collect.Iterables; +import com.google.common.html.HtmlEscapers; +import org.springframework.web.servlet.tags.RequestContextAwareTag; + +import java.util.regex.Pattern; + +/**+ * <p>This tag renders some text with newlines turned into valid HTML structure.</p>
+ */ + +public class PlainTextContentTag extends RequestContextAwareTag { ++ private final static Pattern PATTERN_NEWLINE = Pattern.compile("\\n\\d| \\d\\n|\\n");
+ + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + protected int doStartTagInternal() throws Exception { + + if (!Strings.isNullOrEmpty(value)) { + + pageContext.getOut().print(Joiner.on("<br/>\n").join( + Iterables.transform( + Splitter.on(PATTERN_NEWLINE).split(value), + new Function<String, String>() { + @Override + public String apply(String input) {+ return HtmlEscapers.htmlEscaper().escape(input);
+ } + } + ) + )); + } + + return SKIP_BODY; + } + +} ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/markup/RatingIndicatorTag.java Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,77 @@ +/* + * Copyright 2014, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +package org.haikuos.haikudepotserver.multipage.markup; + +import org.springframework.web.servlet.tags.RequestContextAwareTag; +import org.springframework.web.servlet.tags.form.TagWriter; + +/**+ * <p>This tag renders an HTML structure that represents a number of stars that represents a user rating value.
+ * This value has to be within 0 to 5.</p> + */ + +public class RatingIndicatorTag extends RequestContextAwareTag { + + private Float value; + + public Float getValue() { + return value; + } + + public void setValue(Float value) { + this.value = value; + } + + @Override + protected int doStartTagInternal() throws Exception { + + TagWriter tagWriter = new TagWriter(pageContext.getOut()); + + if(null!=getValue()) { + + if(getValue() < 0f || getValue() > 5f) {+ throw new IllegalStateException("the value for the rating indicator must be [0..5]");
+ } + + tagWriter.startTag("span"); + tagWriter.writeAttribute("class","rating-indicator"); + + int value = (int) (getValue() * 2f); + + for(int i=0;i<5;i++) { + tagWriter.startTag("img"); + + switch(value) { + + case 1:+ tagWriter.writeAttribute("src","/img/starhalf.png");
+ tagWriter.writeAttribute("alt","o"); + break; + + case 0: + tagWriter.writeAttribute("src","/img/staroff.png"); + tagWriter.writeAttribute("alt","."); + break; + + default: + tagWriter.writeAttribute("src","/img/staron.png"); + tagWriter.writeAttribute("alt","*"); + break; + + } + + tagWriter.endTag(); + + value = Math.max(0,value-2); + } + + tagWriter.endTag(); + } + + return SKIP_BODY; + } + +} ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/markup/TimestampTag.java Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,63 @@ +/* + * Copyright 2014, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +package org.haikuos.haikudepotserver.multipage.markup; + +import com.google.common.html.HtmlEscapers; +import org.joda.time.format.DateTimeFormatter; +import org.joda.time.format.DateTimeFormatterBuilder; +import org.springframework.web.servlet.tags.RequestContextAwareTag; +import org.springframework.web.servlet.tags.form.TagWriter; + +import java.util.Date; + +/** + * <p>This tag will render a {@link java.util.Date} as a timestamp.</p> + */ + +public class TimestampTag extends RequestContextAwareTag { + + // might be an idea to put this somewhere else if we need it again? + private final static DateTimeFormatter FORMATTER = + new DateTimeFormatterBuilder() + .appendYear(4,4) + .appendLiteral('-') + .appendMonthOfYear(2) + .appendLiteral('-') + .appendDayOfMonth(2) + .appendLiteral(' ') + .appendHourOfDay(2) + .appendLiteral(':') + .appendMinuteOfHour(2) + .appendLiteral(':') + .appendSecondOfMinute(2) + .toFormatter() + .withZoneUTC(); + + private java.util.Date value; + + public Date getValue() { + return value; + } + + public void setValue(Date value) { + this.value = value; + } + + @Override + protected int doStartTagInternal() throws Exception { + + TagWriter tagWriter = new TagWriter(pageContext.getOut()); + + if(null!=getValue()) { + tagWriter.startTag("span");+ tagWriter.appendValue(HtmlEscapers.htmlEscaper().escape(FORMATTER.print(getValue().getTime())));
+ tagWriter.endTag(); + } + + return SKIP_BODY; + } + +} ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/model/Pagination.java Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,186 @@ +/* + * Copyright 2014, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +package org.haikuos.haikudepotserver.multipage.model; + +import com.google.common.base.Preconditions; + +/**+ * <P>This object aims to provide the pagination within a list of items. It aims to be more or less
+ * like the "paginationcontroldirective.js" behaviour.</P> + */ + +public class Pagination { + + private int offset; + private int total; + private int max; + + /** + * @param total is the total number of items in the entire result set + * @param offset if the offset from 0 into the total number of items + * @param max is the maximum number of items to show on a page + */ + + public Pagination(int total, int offset, int max) { + Preconditions.checkState(offset >= 0); + Preconditions.checkState(total >= 0); + Preconditions.checkState(offset < total); + Preconditions.checkState(max >= 1); + this.offset = offset; + this.total = total; + this.max = max; + } + + // ------------------ + // ACCESSORS + + public int getOffset() { + return offset; + } + + public void setOffset(int offset) { + this.offset = offset; + } + + public int getTotal() { + return total; + } + + public void setTotal(int total) { + this.total = total; + } + + public int getMax() { + return max; + } + + public void setMax(int max) { + this.max = max; + } + + // ------------------ + // PAGE CONTROL + + /**+ * <p>This returns the current page in the pagination based on the offset.</p>
+ */ + + public int getPage() { + return (offset / max); + } + + /** + * <p>This returns the total number of pages in the pagination.</p> + */ + + public int getPages() { + return (total / max) + (0 != total % max ? 1 : 0); + } + + private int[] linearSeries(int count) { + int[] result = new int[count]; + for(int i=0;i<count;i++) { result[i] = i; } + return result; + } + + private void fanFillRight(int[] result, int startI) { + + int pages = getPages() - 1;+ int page = getPage() + 1; // assume the actual page has been set already
+ int len = result.length - startI; + + for (int i = 0; i < len; i++) { + float p = (float) i / (float) (len - 1); + float f = p * p; + result[startI + i] = Math.max( + result[(startI + i) - 1] + 1, + page + (int) (f * (float) (pages - page))); + } + + } + + private void fanFillLeft(int[] result, int startI) { ++ int page = getPage() - 1; // assume the actual page has been set already
+ + for (int i = 0; i < startI; i++) { + float p = (float) i / (float) startI; + float f = p * p; + result[startI - i] = Math.min( + result[(startI - i) + 1] - 1, + page - (int) (f * (float) page)); + } + + } + + /**+ * <p>This method will return an integer array containing item offsets that can be taken to be handy + * pages that the user might like to jump to. This can then be used to present a list of pages within + * a list of data. The returned pages try to present a set of smart pages to jump to and may not be + * strictly linear. If there is only one page then you may be returned an empty array.</p>
+ */ + + public int[] generateSuggestedPages(int count) { ++ Preconditions.checkState(count > 3 && 0==count%2,"the count of pages must be more than 3 and an even number");
+ + int pages = getPages(); + + if(1==pages) { + return new int[] { 0 }; + } + + int page = getPage(); // current page. + + if(pages <= count) { + return linearSeries(pages); + } + + int[] result = new int[count]; + int middleI = count / 2; + + if(page < middleI) { + + for(int i=0;i<=page;i++) { + result[i] = i; + } + + fanFillRight(result, page+1); + + } + else { + + int remainder = pages - page; + + if(remainder < middleI) { + + for(int i=0;i<remainder;i++) { + result[result.length - (i + 1)] = (pages - 1) - i; + } + + fanFillLeft(result,result.length-(remainder + 1)); + + } + else { + result[middleI] = page; + fanFillRight(result, middleI+1); + fanFillLeft(result, middleI-1); + } + + } + + return result; + } + + // ------------------ + // STANDARD STUFF + + @Override + public String toString() { + return "pagination; " + offset + " in " + total; + } + +} ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/package-info.java Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,6 @@ +/**+ * <p>This package is concerned with the presentation of a simplified user interface that is driven by vanilla + * web pages as opposed to the "single page" approach taken in the main user interface for the application.</p>
+ */ + +package org.haikuos.haikudepotserver.multipage; ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/singlepage/controller/EntryPointController.java Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,26 @@ +/* + * Copyright 2014, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +package org.haikuos.haikudepotserver.singlepage.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +/**+ * <p>This controller renders the default HTML entry point into the application. As this is <em>generally</em> a + * single page application, this controller will just render that single page.</p>
+ */ + +@Controller +@RequestMapping("/") +public class EntryPointController { + + @RequestMapping(method = RequestMethod.GET) + public String entryPoint() { + return "singlepage/launch"; + } + +} ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/singlepage/package-info.java Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,6 @@ +/**+ * <p>This package is concerned with elements that are strictly related to the launch of the single-page
+ * application that forms the main user interface for the application.</p> + */ + +package org.haikuos.haikudepotserver.singlepage; ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/support/web/RobotController.java Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,36 @@ +/* + * Copyright 2014, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +package org.haikuos.haikudepotserver.support.web; + +import com.google.common.net.MediaType; +import org.haikuos.haikudepotserver.multipage.MultipageConstants; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; ++// https://developers.google.com/webmasters/control-crawl-index/docs/robots_txt
+ +@Controller +public class RobotController { + + @RequestMapping(value = "/robots.txt", method = RequestMethod.GET)+ public void robotResponse(HttpServletResponse response) throws IOException {
+ response.setContentType(MediaType.PLAIN_TEXT_UTF_8.toString()); + + PrintWriter writer = response.getWriter(); + + writer.print("user-agent: *\n"); + writer.print("allow: "); + writer.print(MultipageConstants.PATH_MULTIPAGE); + writer.print("\n"); + + } + +} ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/webapp/WEB-INF/includes/favicons.jsp Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,3 @@+<link rel="icon" type="image/png" href="/img/haikudepot16.png" sizes="16x16"> +<link rel="icon" type="image/png" href="/img/haikudepot32.png" sizes="32x32"> +<link rel="icon" type="image/png" href="/img/haikudepot64.png" sizes="64x64">
======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/webapp/WEB-INF/multipage.tld Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,112 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<taglib> + <tlib-version>1.0</tlib-version> + <jsp-version>1.2</jsp-version> + <short-name>multipage</short-name> + + <tag> + <name>paginationLinks</name>+ <tag-class>org.haikuos.haikudepotserver.multipage.markup.PaginationLinksTag</tag-class>
+ <description>+ Displays a series of links that allow the user to choose one page from a number of pages
+ of data. + </description> + <attribute> + <name>pagination</name> + <required>true</required>+ <rtexprvalue>true</rtexprvalue> <!-- can use SPEL expressions -->
+ </attribute> + </tag> + + <tag> + <name>pkgIcon</name>+ <tag-class>org.haikuos.haikudepotserver.multipage.markup.PkgIconTag</tag-class>
+ <description> + Renders an image tag with the image of a package. + </description> + <attribute> + <name>pkgVersion</name> + <required>true</required>+ <rtexprvalue>true</rtexprvalue> <!-- can use SPEL expressions -->
+ </attribute> + <attribute> + <name>size</name> + <required>true</required> + </attribute> + </tag> + + <tag> + <name>pkgVersionLabel</name>+ <tag-class>org.haikuos.haikudepotserver.multipage.markup.PkgVersionLabelTag</tag-class>
+ <description> + Renders a label indicating the version of a package + </description> + <attribute> + <name>pkgVersion</name> + <required>true</required>+ <rtexprvalue>true</rtexprvalue> <!-- can use SPEL expressions -->
+ </attribute> + </tag> + + <tag> + <name>pkgVersionLink</name>+ <tag-class>org.haikuos.haikudepotserver.multipage.markup.PkgVersionLinkTag</tag-class>
+ <description>+ A hyperlink that references the page that renders the pkg version
+ </description> + <attribute> + <name>pkgVersion</name> + <required>true</required>+ <rtexprvalue>true</rtexprvalue> <!-- can use SPEL expressions -->
+ </attribute> + </tag> + + <tag> + <name>timestamp</name>+ <tag-class>org.haikuos.haikudepotserver.multipage.markup.TimestampTag</tag-class>
+ <description> + Renders a string indicating a timestamp + </description> + <attribute> + <name>value</name> + <required>true</required>+ <rtexprvalue>true</rtexprvalue> <!-- can use SPEL expressions -->
+ </attribute> + </tag> + + <tag> + <name>ratingIndicator</name>+ <tag-class>org.haikuos.haikudepotserver.multipage.markup.RatingIndicatorTag</tag-class>
+ <description>+ Renders a series of images indicating the rating aggregate [0..5]
+ </description> + <attribute> + <name>value</name> + <required>true</required>+ <rtexprvalue>true</rtexprvalue> <!-- can use SPEL expressions -->
+ </attribute> + </tag> + + <tag> + <name>naturalLanguageChooser</name>+ <tag-class>org.haikuos.haikudepotserver.multipage.markup.NaturalLanguageChooserTag</tag-class>
+ <description> + Allows the user to select their natural language. + </description> + </tag> + + <tag> + <name>plainTextContent</name>+ <tag-class>org.haikuos.haikudepotserver.multipage.markup.PlainTextContentTag</tag-class>
+ <description> + Renders text that may contain newlines niely in html form. + </description> + <attribute> + <name>value</name> + <required>true</required>+ <rtexprvalue>true</rtexprvalue> <!-- can use SPEL expressions -->
+ </attribute> + </tag> + +</taglib> ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/webapp/WEB-INF/views/multipage/home.jsp Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,170 @@+<%@ page session="false" language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
+<%@include file="/WEB-INF/views/multipage/includes/prelude.jsp"%> + +<html> + +<head> + + <title>Haiku Depot Web</title> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <%@include file="/WEB-INF/includes/favicons.jsp"%> + + <%-- will use the same CSS as the main application --%> + <jwr:style src="/bundles/app.css"></jwr:style> + +</head> + +<body> +<%@include file="includes/banner.jsp"%> + +<div class="container"> + <div class="content-container home"> + + <form method="get" action="/multipage"> + <div id="search-criteria-container"> + <div> + <select name="arch">+ <c:forEach items="${data.allArchitectures}" var="anArchitecture">
+ <option+ <c:if test="${anArchitecture == data.architecture}">
+ selected="selected" + </c:if> + value="${anArchitecture.code}">+ <c:out value="${anArchitecture.code}"></c:out>
+ </option> + </c:forEach> + </select> + </div> + <div> + <select name="viewcrttyp">+ <c:forEach items="${data.allViewCriteriaTypes}" var="aViewCriteriaType">
+ <option+ <c:if test="${aViewCriteriaType == data.viewCriteriaType}">
+ selected="selected" + </c:if> + value="${aViewCriteriaType.name()}">+ <spring:message code="${aViewCriteriaType.getTitleKey()}"></spring:message>
+ </option> + </c:forEach> + </select> + </div> + <div> + <select name="pkgcat">+ <c:forEach items="${data.allPkgCategories}" var="aPkgCategory">
+ <option+ <c:if test="${aPkgCategory == data.pkgCategory}">
+ selected="selected" + </c:if> + value="${aPkgCategory.code}">+ <spring:message code="${aPkgCategory.getTitleKey()}"></spring:message>
+ </option> + </c:forEach> + </select> + </div> + <div> + <input + type="text" + placeholder="zlib" + name="srchexpr" + value="${data.searchExpression}"> + + <button type="submit">+ <spring:message code="home.searchButton.title"></spring:message>
+ </button> + </div> + </div> + </form> + + <!-- RESULTS --> + + <div id="search-results-container"> + + <c:if test="${empty data.pkgVersions}"> + <div class="info-container">+ <strong><message key="home.noResults.title"></message>;</strong>
+ <message key="home.noResults.description"></message> + </div> + </c:if> + + <c:if test="${not empty data.pkgVersions}"> + <div class="table-general-container"> + + <div class="table-general-pagination-container">+ <multipage:paginationLinks pagination="${data.pagination}"></multipage:paginationLinks>
+ </div> + + <div class="muted"> + <c:out value="${data.pagination.total}"></c:out> + <c:choose> + <c:when test="${1==data.pagination.total}">+ <spring:message code="gen.pkg.title"></spring:message>
+ </c:when> + <c:otherwise>+ <spring:message code="gen.pkg.title.plural"></spring:message>
+ </c:otherwise> + </c:choose> + </div> + + <table class="table-general"> + <thead> + <th></th>+ <th><spring:message code="gen.pkg.title"></spring:message></th> + <th><spring:message code="home.table.rating.title"></spring:message></th> + <th><spring:message code="home.table.version.title"></spring:message></th> + <c:if test="${'MOSTRECENT'==data.viewCriteriaType.name()}"> + <th><spring:message code="home.table.approximateVersionDate.title"></spring:message></th>
+ </c:if>+ <c:if test="${'MOSTVIEWED'==data.viewCriteriaType.name()}"> + <th><spring:message code="home.table.versionViewCounter.title"></spring:message></th>
+ </c:if> + </thead> + <tbody>+ <c:forEach items="${data.pkgVersions}" var="pkgVersion">
+ <tr> + <td>+ <multipage:pkgIcon size="16" pkgVersion="${pkgVersion}"></multipage:pkgIcon>
+ </td> + <td>+ <multipage:pkgVersionLink pkgVersion="${pkgVersion}"> + <c:out value="${pkgVersion.pkg.name}"></c:out>
+ </multipage:pkgVersionLink> + </td> + <td>+ <c:if test="${not empty pkgVersion.pkg.derivedRating}"> + <multipage:ratingIndicator value="${pkgVersion.pkg.derivedRating}"></multipage:ratingIndicator>
+ </c:if> + </td> + <td>+ <multipage:pkgVersionLabel pkgVersion="${pkgVersion}"></multipage:pkgVersionLabel>
+ </td>+ <c:if test="${'MOSTRECENT'==data.viewCriteriaType.name()}">
+ <td> + <span class="muted">+ <multipage:timestamp value="${pkgVersion.createTimestamp}"></multipage:timestamp>
+ </span> + </td> + </c:if>+ <c:if test="${'MOSTVIEWED'==data.viewCriteriaType.name()}">
+ <td> + <span class="muted">+ <c:out value="${pkgVersion.viewCounter}"></c:out>
+ </span> + </td> + </c:if> + </tr> + </c:forEach> + </tbody> + </table> + + </div> + </c:if> + </div> + + </div> +</div> + +<div class="footer"></div> + +</body> + +</html> ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/webapp/WEB-INF/views/multipage/includes/banner.jsp Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,24 @@+<%@ page session="false" language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
+ +<div> + <div id="banner-container"> + + <span id="banner-title" class="multipage-banner-title">+ <div>Haiku Depot Server <span><spring:message code="multipage.banner.title.suffix"></spring:message></span></div>
+ </span> + + <div id="banner-actions" class="multipage-banner-actions"> + <div id="banner-multipage-note">+ <spring:message code="multipage.banner.note"></spring:message>;
+ <a href="/">+ <spring:message code="multipage.banner.note.full"></spring:message>
+ </a> + </div> + <div>+ <multipage:naturalLanguageChooser></multipage:naturalLanguageChooser>
+ </div> + </div> + + </div> + +</div> ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/webapp/WEB-INF/views/multipage/includes/prelude.jsp Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,5 @@+<%@ page session="false" language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"; %> +<%@ taglib prefix="jwr" uri="http://jawr.net/tags"; %> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> +<%@ taglib prefix="multipage" uri="/WEB-INF/multipage.tld"%> ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/webapp/WEB-INF/views/multipage/viewPkgVersion.jsp Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,83 @@+<%@ page session="false" language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
+<%@include file="/WEB-INF/views/multipage/includes/prelude.jsp"%> + +<html> + +<head> + + <title>Haiku Depot Web</title> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <%@include file="/WEB-INF/includes/favicons.jsp"%> + + <%-- will use the same CSS as the main application --%> + <jwr:style src="/bundles/app.css"></jwr:style> + +</head> + +<body> +<%@include file="includes/banner.jsp"%> + +<div class="container"> + + <div id="breadcrumbs-container"> + <ul> + <li> + <a href="<c:out value="${data.homeUrl}"></c:out>">+ <spring:message code="breadcrumb.home.title"></spring:message>
+ </a> + </li> + <li>+ <span ng-show="isItemActive(item)"><c:out value="${data.pkgVersion.pkg.name}"></c:out></span>
+ </li> + </ul> + </div> + + <div class="content-container"> + + <div id="pkg-title"> + <div id="pkg-title-icon">+ <multipage:pkgIcon pkgVersion="${data.pkgVersion}" size="32"/>
+ </div> + <div id="pkg-title-text">+ <h1><c:out value="${data.pkgVersion.getPkgVersionLocalizationOrFallback(data.currentNaturalLanguage).summary}"/></h1>
+ <div class="muted"> + <small> + <c:out value="${data.pkgVersion.pkg.name}"></c:out> + -+ <multipage:pkgVersionLabel pkgVersion="${data.pkgVersion}"></multipage:pkgVersionLabel>
+ -+ <c:out value="${data.pkgVersion.architecture.code}"></c:out>
+ </small> + </div> + </div> + </div> ++ <c:if test="${data.pkgVersion.isLatest && not empty data.pkgVersion.pkg.derivedRating}">
+ <div class="pkg-rating-indicator-container">+ <multipage:ratingIndicator value="${data.pkgVersion.pkg.derivedRating}"></multipage:ratingIndicator>
+ <span class="pkg-ratings-indicator-sample"> + <small> + <spring:message + code="viewPkg.derivedUserRating.sampleSize"+ arguments="${data.pkgVersion.pkg.derivedRatingSampleSize}">
+ </spring:message> + </small> + </span> + </div> + </c:if> + + <div id="pkg-description-container"> + <p>+ <multipage:plainTextContent value="${data.pkgVersion.getPkgVersionLocalizationOrFallback(data.currentNaturalLanguage).description}"></multipage:plainTextContent>
+ </p> + </div> + + </div> + +</div> + +<div class="footer"></div> + +</body> + +</html> ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/webapp/WEB-INF/views/singlepage/launch.jsp Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,44 @@+<%@ page session="false" language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"; %> +<%@ taglib prefix="jwr" uri="http://jawr.net/tags"; %> + +<%--+This is a single page application and this is essentially the 'single page'. It boots-up some libraries and other +web-resources and then this starts the java-script single page environment driven by the AngularJS library.
+--%> + +<html ng-app="haikudepotserver"> + +<head> + + <title>Haiku Depot Web</title> + + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + + <%@include file="/WEB-INF/includes/favicons.jsp"%> + + <jwr:script src="/bundles/libs.js"></jwr:script> + <jwr:script src="/bundles/app.js"></jwr:script> + <jwr:style src="/bundles/app.css"></jwr:style> + +</head> + +<body> + +<%@include file="/WEB-INF/includes/unsupported.jsp"%> + +<banner></banner> + +<div class="container"> + <div ng-view></div> +</div> + +<%-- +This IFRAME can be used by application logic to cause a download to occur. +--%> + +<iframe id="download-iframe" style="display:none"></iframe> + +</body> + +</html> ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/webapp/css/multipage/banner.css Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,35 @@ +/* +Styles in this file are targeting the simplified multi-page interface. +*/ + +/* +Overrides a setting on #banner-title +*/ + +.multipage-banner-title {+ background: url('/img/haikudepot32.png') no-repeat 32px 4px transparent;
+} + +#banner-title > div > span { + font-family: serif; + font-weight: lighter; + font-style: italic; + color: rgba(255, 255, 255, 0.50) +} + +.multipage-banner-actions { + text-align: right; +} + +#banner-multipage-note > a { + color: rgba(255,255,255,0.75); +} + +#banner-multipage-note { + font-size: 10pt; + color: rgba(255,255,255,0.75); +} + +#banner-container .multipage-natural-language-chooser > a { + color: rgba(255,255,255,0.75); +} ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/webapp/css/singlepage/addedituserrating.css Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,3 @@ +form textarea { + width: 100% +} ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/webapp/css/singlepage/banner.css Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,67 @@ +/*+These styles are related to the banner at the top of the screen containing the title of the application
+as well as the status of the authenticated user. +*/ + +#banner-container { + position: relative; + margin: 0; + background-color: #336698; + width: 100%; + height: 40px; + padding: 0px; + border-bottom: 1px solid black; +} + +#banner-title { + position: absolute; + top: 0px; + left: 0px; + padding-left: 68px; + height: 40px; + background: url('/img/haikudepot.svg') no-repeat 32px 4px transparent; + background-size: 32px 32px; +} + +#banner-title div { + position: relative; + top: 8px; + left: 0px; + color: white; + font-weight: bold; + font-size: 24px; + vertical-align: middle; +} + +#banner-actions { + position: absolute; + top: 6px; + right: 0px; + overflow: hidden; + padding-right: 32px; + color: white; + overflow: hidden; +} + +#banner-actions > .banner-actions-state-container { + margin-right: 6px; + display: inline-block; +} + +#banner-actions > .banner-actions-state-container > span { + line-height: 18px; + vertial-align: middle; +} + +#banner-container a { + color: white; +} + +#banner-actions button { + background-color: transparent; + border-color: white; + border-width: 2px; + padding-left: 2px; + padding-right: 2px; + vertical-align: middle; +} ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/webapp/css/singlepage/breadcrumbs.css Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,35 @@ +/* +These styles are to do with the breadcrumbs that appear under the banner. +*/ + +#breadcrumbs-container { + border-bottom: 1px solid black; + background: #444444; + margin: 0; + padding: 0; + padding: 4px 32px; +} + +#breadcrumbs-container ul { + padding: 0; + margin: 0; +} + +#breadcrumbs-container ul li { + display: inline; + background: url('/img/breadcrumbseparator.svg') no-repeat; + background-position: 4px 2px; + padding: 0 0 0 18px; + color: white; + font-size: 12px; +} + +#breadcrumbs-container ul li a { + color: white; +} + +#breadcrumbs-container ul li:nth-child(1) { + display: inline; + background: none; + padding: 0; +} ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/webapp/css/singlepage/createuser.css Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,4 @@ +#create-user-captcha-response-input { + width: 64px; + display: inline; +} ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/webapp/css/singlepage/editpkgscreenshots.css Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,41 @@ +#pkg-screenshots-list { + margin-top: 8px; + margin-bottom: 2px; +} + +#pkg-screenshots-list > div { + margin-top: 2px; + margin-bottom: 2px; + background-color: #EEE; +} + +#pkg-screenshots-list > div .pkg-screenshot-image-container { + position: relative; + display: inline; +} + +#pkg-screenshots-list > div .pkg-screenshot-image-container > img { + float: left; + border: 1px solid black; +} + +#pkg-screenshots-list div.pkg-screenshot-controls { + margin-left: 190px; + padding-top: 4px; + padding-bottom: 4px; +} + +#pkg-screenshots-list > div .pkg-screenshot-actions-container { + padding-top:2px; + padding-bottom:2px; + padding-left:4px; + padding-right:4px;+ position: absolute; /* assumes that this is inside a 'pkg-screenshot-title' */
+ top: 1px; + right: 1px; + background-color: rgb(253,207,49); + border-bottom-left-radius: 4px; + display: inline; +} + + ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/webapp/css/singlepage/editpkgversionlocalization.css Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,77 @@ +#edit-pkg-version-localization-container { + position: relative; + border : 1px solid black; + border-collapse: collapse; + border: 1px black solid; + width: 100% +} + +#edit-pkg-version-localization-container td { + padding-left: 20px; + padding-right: 20px; + padding-bottom: 20px; + padding-top: 20px; + vertical-align: top; +} + +#edit-pkg-version-localization-container > tr > td { + border-top: 1px solid black; + border-bottom: 1px solid black; +} + +#natural-translations-cell { + width: 16%; + border-right : 1px dotted lightgray; + border-left: 1px solid black; +} + +#natural-translations-cell div { + padding: 4px; +} + +#natural-translations-cell .selected-translation { + font-weight: bold; +} + +#english-original-cell { + width: 30%; + border-left : 1px solid black; + border-right: 1px solid black; + background-color: #EEE; +} + +#translation-cell { +} + +#translation-cell-summary { + width: 100%; + height: 64px; +} + +#translation-cell-description { + width: 100%; + height: 200px; +} + +#english-original-cell h1 { + text-align: right; + margin-top: 2px; + margin-bottom: 2px; + font-size: 10px; + color: grey; +} + +#english-original-cell h2 { + margin-top: 4px; + margin-bottom:2px; +} + +#english-original-cell p { + margin-top: 2px; + margin-bottom:8px; +} + +#edit-pkg-version-localization-actions-container { + margin-top: 16px; + margin-left: 16% +} ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/webapp/css/singlepage/home.css Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,16 @@ +/*+This specific layout works off the premise that there is only one table on this page.
+*/ + +.home .table-general > tbody > tr > td:nth-child(1) { + width:18px; + text-align: center; +} + +.home .table-general > tbody > tr > td:nth-child(3) { + width:160px; +} + +.home .table-general > tbody > tr > td:nth-child(4) { + width:50%; +} ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/webapp/css/singlepage/listauthorizationpkgrules.css Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,4 @@+.list-authorization-pkg-rules .table-general > tbody > tr > td:nth-child(4) {
+ width: 16px; + text-align: center; +} ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/webapp/css/singlepage/listrepositories.css Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,16 @@ +/*+This specific layout works off the premise that there is only one table on this page.
+*/ + +.list-repositories .table-general > tbody > tr > td:nth-child(1) { + width: 10%; + text-align: center; +} + +.list-repositories .table-general > tbody > tr > td:nth-child(2) { + width:60%; +} + +.list-repositories .table-general > tbody > tr > td:nth-child(3) { + width:30%; +} ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/webapp/css/singlepage/listusers.css Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,12 @@ +/*+This specific layout works off the premise that there is only one table on this page.
+*/ + +.list-users .table-general > tbody > tr > td:nth-child(1) { + width: 10%; + text-align: center; +} + +.list-users .table-general > tbody > tr > td:nth-child(2) { + width:90%; +} ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/webapp/css/singlepage/main.css Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,534 @@ +/* +These styles are applicable across a number of pages of the system. +*/ + +/* +================================== +GENERAL PAGE LAYOUT +*/ + +body { + font-family: sans-serif; + padding: 0; + margin: 0; +} + +img { + border: none; +} + +a, a:link, a:visited, a:active, a:hover { + text-decoration: underline; + color: blue; +} + +h1 { + font-weight: bold; + font-size: 18px; +} + +h2 { + font-weight: bold; + font-size: 16px; + color: #444; +} + +.muted { + color: gray; +} + +/* +Used by the 'highlighted-text' directive. +*/ +.highlighted { + /*background-color: yellow;*/ + /*border: 1px solid rgba(215, 215, 0, 1);*/ + background-color: #ffc8fe; + border: 1px solid rgb(255, 149, 255); +} + +small { + font-size: 10px; +} + +/*+This class is used for the spinner as well as the modal dialog. It will fill a backdrop with a dark colour and then
+the spinner or the modal dialog can be shown over the top. +*/ + +.modal-backdrop-container { + position: fixed; + left: 0px; + top: 0px; + bottom: 0px; + right: 0px; + background-color: rgba(0, 0, 0, 0.6); + z-index: 1050; +} + +/* +================================== +TEXT STYLING +*/ + +.text-error { + color: red; +} + +.text-warning { + color: darkorange; +} + +.text-success { + color: darkgreen; +} + +/* +================================== +PAGE STRUCTURE +*/ + +.content-container { + /* handled by the trailer class */ + margin: 24px 5% 0px; + width: auto; +} + +.footer { + clear:both; + margin-bottom: 32px; +} + +/* +================================== +ALERT BOXES FOR USE OUTSIDE OF FORMS +*/ + +.alert-container { + padding: 8px; + border: 1px solid red; + background-color: white; + color: red; + margin-bottom: 4px; + margin-top: 4px; +} + +.info-container { + padding: 8px; + border: 1px solid #336698; + background-color: white; + color: black; + margin-bottom: 4px; + margin-top: 4px; +} + +.onelineform-container { + padding: 8px; + border: 1px solid black; + background-color: white; + color: black; +} + +.onelineform-container form { + margin-bottom: 0; +} + +/* +================================== +FORMS +*/ + +label { + width: 30%; + display: inline-block; + text-align: right; + color: gray; + line-height: 100%; + vertical-align: top; + margin-top:5px; + font-weight: lighter; +} + +input, textarea, select { + font-size: 16px; + border: 1px solid darkgray; + padding: 2px; +} + +button { + background-color: white; + border: 1px solid black; + border-radius: 6px; + font-size: 16px; + padding-top: 2px; + padding-bottom: 2px; + padding-left: 8px; + padding-right: 8px; +} + +button.main-action { + border: 2px solid black; +} + +button[disabled] { + border: 1px solid darkgray; + color: rgba(0, 0, 0, 0.50); +} + +button.main-action[disabled] { + border: 2px solid darkgray; +} + +.form-error-container { + padding-top: 4px; + color: red; +} + +.form-control-group { + width: 69%; + display: inline-block; + line-height: 100%; + padding-bottom: 8px; +} + +/*+In a form, you may want to place some static text as one of the 'controls'. This style helps with this.
+*/ + +.form-control-group .form-control-group-static { + padding: 3px; +} + +.form-control-group.form-control-group-error input { + border: 1px solid red; +} + +/*+This is used to place a box above the form in order to show some alert text such as "your login has failed."
+*/ + +.form-alert-container { + width:69%; + margin-left: 30%; + margin-top: 16px; + margin-bottom: 16px; + padding: 8px; + border: 1px solid red; + background-color: white; + color: red; +} + +.form-info-container { + width:69%; + margin-left: 30%; + margin-top: 16px; + margin-bottom: 16px; + padding: 8px; + border: 1px solid #336698; + background-color: white; + color: #336698; +} + +/*+This resides at the bottom of the form and contains the action buttons relating to the controls.
+*/ + +.form-action-container { + width: 69%; + margin-left: 30%; + margin-top: 16px; + padding-top: 8px; + border-top: 1px solid black; +} + +/* +================================== +TABLE +*/ + +table.table-general { + width: 100%; + border: 1px solid black; + border-collapse: collapse; + clear: both; +} + +table.table-general thead th { + background: #444; + color: white; + text-align: left; + padding: 4px; +} + +table.table-general tbody td { + padding: 4px; +} + +table.table-general tbody td:nth-child(n+1) { + border-left: 1px dotted #444; +} + +table.table-general tbody tr:nth-child(even) { + background: white; +} +table.table-general tbody tr:nth-child(odd) { + background: #EEE; +} + +table.table-general tbody tr td.timestampcell { + width: 172px; +} + +table.table-general tbody tr td.selectioncell { + width: 32px; + text-align: center; +} + +.table-general-container { +} + +.table-general-pagination-container { + float: right; + margin-top: 2px; + margin-bottom: 2px; +} + +/* +================================== +ANGULARJS +*/ + +[ng\:cloak], [ng-cloak], .ng-cloak { + display: none; +} + +/* +================================== +MODAL DIALOG +Also see "modal-backdrop-container" class above. +*/ + +.modal-container { + width: 240px; + height: 320px; + border: 1px black solid; + background-color: white; + position: fixed; + left: 50%; + top: 50%; + margin-left: -120px; + margin-top: -160px; +} + +.modal-container > .modal-content-container { + padding: 16px; + position: absolute; + top: 20px; + bottom: 0; + left: 0; + right: 0; + overflow-y: auto; +} + +.modal-container > .modal-banner-container { + background-color: #336698; + color: white; + height: 19px; + border-bottom: 1px solid rgba(0,0,0,0.6); + text-align: right; +} + +/* +This deals with the close icon in the banner. +*/ + +.modal-container > .modal-banner-container img { + margin-top: 3px; + margin-right: 3px; +} + +/* +================================== +SPINNER +*/ + +/*+This material is for the spinner; it will fill the screen will white and then put a small animation on top for
+a moment. +*/ + +#spinner-container > div { + width: 120px; + margin: 0 auto; + padding-top: 120px; +} + +/* +================================== +SEARCH BAR +*/ + +#search-criteria-container { + background-color: rgb(253,207,49); + border: 1px solid black; +} + +#search-criteria-container label { + font-size: 10px; + white-space: nowrap; + margin: 5px; + width: auto; +} + +#search-criteria-container label::after { + content:":"; +} + +#search-criteria-container input { + font-size: 10px; +} + +#search-criteria-container select { + font-size: 10px; +} + +#search-criteria-container button { + font-size: 10px; + background-color: rgb(253,207,49); + border: 1px solid black; + border-radius: 3px; +} + +#search-criteria-container button[disabled] { + border: 1px solid rgba(0, 0, 0, 0.30); +} + +#search-criteria-container > div { + display: inline-block; + padding-top:2px; + padding-bottom:2px; + padding-left:4px; + padding-right:4px; +} + +/*+Within the search criteria to break-up the sections of the search criteria, this will put a dotted line
+between the sections. +*/ + +#search-criteria-container > div:nth-child(n+2) { + border-left: 1px dotted black; +} + +#search-results-container { + margin-top: 20px; +} + +/* +================================== +ACTIVE / INACTIVE INDICATORS -- SVG +*/ + +.active-indicator { + fill: firebrick; +} + +.active-indicator.active-indicator-true { + fill: darkolivegreen; +} + +/* +================================== +DATA LISTS +*/ + +dl dt { + color:gray; + float:left; + clear:left; + text-align: right; + margin-right:10px; + padding:5px; + width:190px; +} + +dl dd { + margin:2px 0; + padding:5px 0; + margin-left: 200px; +} + +/* +================================== +HIDE / SHOW FOR OWN DIRECTIVES +*/ + +.app-hide { + display: none !important; +} + +/* +================================== +PAGINATION CONTROLS +*/ + +.pagination-control-container { + display: inline-block; + padding: 0; + margin: 0; +} ++.pagination-control-container.pagination-control-on-first a.pagination-control-left > img {
+ opacity: 0.25; +} ++.pagination-control-container.pagination-control-on-last a.pagination-control-right > img {
+ opacity: 0.25; +} + +.pagination-control-container li { + min-width: 1.75em; + text-align: center; + display: inline-block; + padding: 0; +} + +.pagination-control-container > li > a,svg { + margin-left: 4px; + margin-right: 4px; +} + +.pagination-control-container > li > a.pagination-control-currentpage { + font-weight: bold; + text-decoration: none; + color: black; +} + +/* +================================== +CONTEXT-MENU+This is where the user is presented with a pop-up modal that contains some options to choose from.
+*/ + +ul.context-menu-container { + padding-left: 0; + margin-top: 0; + margin-bottom: 0; +} + +ul.context-menu-container li { + margin-top: 2px; + margin-bottom: 2px; + list-style: none; + background-color: #eeeeee; + padding: 4px; +} + +/* +================================== +RATINGS +*/ + +.rating-indicator { + vertical-align: middle; +} ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/webapp/css/singlepage/pkgfeedbuilder.css Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,21 @@ +.pkg-feed-builder .feed-url-container { + font-family: "Courier New", Courier, mono; + padding: 4px 8px; + border: 1px solid black; + background-color: #EEE; +} + +.pkg-feed-builder .pkg-lozenge-container { + padding-top: 3px; + padding-bottom: 3px; +} + +.pkg-feed-builder .pkg-lozenge-container .pkg-lozenge { + background-color: #EEE; + border-radius: 4px; + padding: 2px 6px; + display: inline-block; + margin-left: 2px; + margin-right: 2px; + border: 1px solid lightgray; +} ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/webapp/css/singlepage/unsupported.css Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,42 @@ +/*+This material refers to a panel that may appear if the browser is not supported or the javascript environment is
+not configured. +*/ + +#unsupported { + background-color: #505050; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + font-family: sans-serif; +} + +#unsupported.unsupported-hide { + display: none; +} + +#unsupported h1 { + text-align: center; +} + +#unsupported .unsupported-image { + text-align: center; +} + +#unsupported #unsupported-container { + color: white; + width: 420px; + height: 320px; + margin:0 auto; + margin-top: 72px; +} + +#unsupported #unsupported-container .unsupported-message-container { + margin-bottom: 20px; +} + +#unsupported #unsupported-container .unsupported-message-container > a { + color: white; +} ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/webapp/css/singlepage/viewpkg.css Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,160 @@ +#pkg-title { + margin-bottom: 6px; +} + +#pkg-title-feed { + float: right; +} + +#pkg-title-icon { + display: inline; + float:left; +} + +#pkg-title-text { + padding-left: 38px; +} + +#pkg-title-text h1 { + margin: 0; +} + +/* +--------------------- +USER RATINGS +*/ + +#pkg-rating-indicator-container { + margin-top: 6px; + margin-bottom: 8px; +} + +#pkg-rating-indicator-container .pkg-ratings-indicator-sample { + margin-left: 12px; + color: gray; +} + +/* +--------------------- +DESCRIPTION / META-DATA +*/ + +#pkg-metadata-container { + border-top: 1px lightgray solid; +} + +#pkg-description-container { + +} + +/* +--------------------- +USER-RATINGS +*/ + +#pkg-userratings-container { + border-top: 1px lightgray solid; +} + +#pkg-userratings-container .pkg-userratings-pagination { + float: right; + margin-top: 2px; + margin-bottom: 2px; +} + +#pkg-userratings-container .pkg-userrating { + display: inline-block; + width: 360px; + height: 82px; + padding-left: 4px; + padding-right: 4px; + padding-top: 2px; + padding-bottom: 2px; + margin-bottom: 8px; + margin-right: 8px; + vertical-align: top; + background-color: #f4f4f4; + border-left: 2px solid gray; + position: relative; +} + +#pkg-userratings-container .pkg-userrating-indicatoranduser { + height: 26px; +} + +#pkg-userratings-container .pkg-userrating-meta { + float: right; + font-size: 10px; +} + +#pkg-userratings-container .pkg-userrating-comment { + /*clear: both;*/ + font-size: 10px; + color: gray; + overflow: hidden; + text-overflow: ellipsis; + height: 48px; +} + +#pkg-userratings-container .pkg-userrating-differentversion { + position: absolute; + bottom: 0px; + right: 0px; + background-color: rgba(255,0,0,0.5); + font-size: 10px; + color: white; + padding-left: 4px; + padding-right: 4px; + padding-top: 2px; + padding-bottom: 2px; + border-top-left-radius: 3px; +} + +/* +--------------------- +ACTIONS +*/ + +#pkg-actions-container { + border-top: 1px lightgray solid; +} + +/* +--------------------- +SCREENSHOTS +*/ + +#pkg-screenshot-container { + width: 100%; + border: 1px solid black; + background-color: #444; + overflow: auto; + height: 256; + padding-left: 8px; + padding-right: 8px; + margin-top: 20px; +} + +.pkg-screenshot-item-container { + margin: 8px; + box-shadow: 2px 2px 4px black; + display: inline-block; + position: relative; +} + +.pkg-screenshot-item-controls-container a { + text-decoration: none; +} + +.pkg-screenshot-item-controls-container { + padding-top:2px; + padding-bottom:2px; + padding-left:4px; + padding-right:4px; + position: absolute; + top: 0px; + right: 0px; + background-color: rgb(253,207,49); + border-bottom-left-radius: 4px; + display: inline; +} ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/webapp/img/paginationleft.png Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,16 @@ +PNG + + +IHDR + +Vu\çsBIT|d pHYs +× +×B(xtEXtSoftwarewww.inkscape.orgî<`IDAT(»0 +C)%S± +y +Z4IA~¾ÄwjtOþ +ßàäx + +) +=IÂÌp§(e%ñàakt +Ö|¤Ù¡×׺|8ï5>Í îʨìSIEND®B` ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/webapp/img/paginationleft.svg Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns="http://www.w3.org/2000/svg"; + version="1.1" + height="12" + width="12">+ <path fill="black" d="M12 4.5 L12 7.5 L8 7.5 L8 12 L0 6 L8 0 L8 4.5"/>
+ </svg> ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/webapp/img/paginationright.png Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,11 @@ +PNG + + +IHDR + +Vu\çsBIT|d pHYs +× +×B(xtEXtSoftwarewww.inkscape.orgî<oIDAT(ѱ +ÂP +Ðg*£Ð3 +uØ°I"¤6Ï'èK9éÛw:Û2Ó'1¡/ëï~E¸ãØ"H,Z/®"v1ÚÆSD3s®9þâËáû:ÜÚµ27ã©uéÝÎÚô¸U£3ñQ,¿IEND®B` ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/webapp/img/paginationright.svg Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns="http://www.w3.org/2000/svg"; + version="1.1" + height="12" + width="12"> + <path fill="black" d="M0 4.5 L0 7.5 L4 7.5 L4 12 L12 6 L4 0 L4 4.5"/> + </svg> ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/webapp/img/starhalf.png Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,20 @@ +PNG + + +IHDRóÿabKGDÿÿÿ ½§ pHYs¯¯^ +tIMEÞ +!94ìIDAT8ËÍ=KcAäFw£Ør-´ZÑÊmea+»H +1ÅB/þ±P®° + +¬Yÿ ÜAdm² +ø¹wMPÑ$"6nÎyyw¼±T¥®ñù¯×W' +ªü¨®$êEcûv¡¶ÒãªIëZÇ^ÐÝÿèMa´ +rV¼úpþwi ¼@gÊûФÑ4(#u(õøÚ!¢,u +¾ Åe~íyðgzÂWm#3|7ÞZÊPÚ õÜ*eäÚ_ýËDOÞ +Å¡BvÞiYª@6£Áu2hK¸ì¤]:çR +ñ:@ëÐÿMoèôÅ;=y ¢² +ÎnÕ_H$V8ç¿kFl +P·aÿp{vô²â +¤Ó½ßKÇ""=Éø%â£O +Ëô¿"ëË,LRo¯=å5 8ª¾Â`vÞIYMæ¾çºlÍ99ñý +ï®î+áEÒBgIEND®B` ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/webapp/img/staroff.png Sat Aug 30 10:29:45 2014 UTC
Binary file, no diff available. ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/webapp/img/staron.png Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,15 @@ +PNG + + +IHDRóÿabKGDÿÿÿ ½§ pHYs¯¯^ +tIMEÞ +"ºÇâèAIDAT8ËͱNAEÏL ±5ÊÄd +JÁƵØ]¿Àoð`Êõ( +ØLbbgbkcD-¶ÙgÁ)v ÆƼbæÝ{_æÎ?Be5ªç=>sÑ>Àf{÷ÝTÎ2÷]DðDð²Ä+ +\&õ»'ÔÏzL7¢0a5_¹»ûnàxmeÇEѶ@]{ ïäÅÖSwkü7åx +´B +óì®þÖ +Ʊik|YTîâ®5Ae®ñ2p¼Öø8~ÛE¸ÉHìÂvÞ¢ãµ%íÞ@ü +fó×ÒäH!ã4AUw&¥0l¥ïA¶PH#9FÀqRÑ|4ÅYt + +¬ òåÚhX®Öy`ôVãÀkס.¼Üû_øäMt*Ø8ÕIEND®B` ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/multipage/model/PaginationTest.java Sat Aug 30 10:29:45 2014 UTC
@@ -0,0 +1,47 @@ +/* + * Copyright 2014, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +package org.haikuos.haikudepotserver.multipage.model; + +import org.fest.assertions.Assertions; +import org.junit.Test; + +public class PaginationTest { + + /** + * <p>Tests the special case where there's one page.</p> + */ + + @Test + public void testGenerateSuggestedPages_one() { + Pagination p = new Pagination(1,0,10);+ Assertions.assertThat(p.generateSuggestedPages(6)).isEqualTo(new int[] {0});
+ } + + @Test + public void testGenerateSuggestedPages_linear() { + Pagination p = new Pagination(50,23,10);+ Assertions.assertThat(p.generateSuggestedPages(6)).isEqualTo(new int[] {0,1,2,3,4});
+ } + + @Test + public void testGenerateSuggestedPages_fanRight() { + Pagination p = new Pagination(500,21,10);+ Assertions.assertThat(p.generateSuggestedPages(10)).isEqualTo(new int[] {0,1,2,3,4,8,14,23,34,49});
+ } + + @Test + public void testGenerateSuggestedPages_fanLeft() { + Pagination p = new Pagination(500,475,10);+ Assertions.assertThat(p.generateSuggestedPages(10)).isEqualTo(new int[] {0,13,23,31,38,43,45,46,48,49});
+ } + + @Test + public void testGenerateSuggestedPages_fanLeftAndRight() { + Pagination p = new Pagination(500,250,10);+ Assertions.assertThat(p.generateSuggestedPages(10)).isEqualTo(new int[] {0,9,16,21,23,24,27,31,38,49});
+ } + +} =======================================--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/web/controller/EntryPointController.java Sat Jul 12 08:36:41 2014 UTC
+++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2014, Andrew Lindesay - * Distributed under the terms of the MIT License. - */ - -package org.haikuos.haikudepotserver.web.controller; - -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; - -/**- * <p>This controller renders the default HTML entry point into the application. As this is <em>generally</em> a - * single page application, this controller will just render that single page.</p>
- */ - -@Controller -@RequestMapping("/") -public class EntryPointController { - - @RequestMapping(method = RequestMethod.GET) - public String entryPoint() { - return "entryPoint"; - } - -} ======================================= ***Additional files exist in this changeset.*** ============================================================================== Revision: 8dbcbd8a7f4b Author: Andrew Lindesay <apl@xxxxxxxxxxxxxx> Date: Sun Aug 31 09:07:52 2014 UTCLog: sort out problems with pagination links in java as well as javascript
https://code.google.com/p/haiku-depot-web-app/source/detail?r=8dbcbd8a7f4b Modified:/haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/markup/PaginationLinksTag.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/model/Pagination.java
/haikudepotserver-webapp/src/main/webapp/WEB-INF/includes/unsupported.jsp/haikudepotserver-webapp/src/main/webapp/js/app/directive/paginationcontroldirective.js /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/multipage/model/PaginationTest.java
=======================================--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/markup/PaginationLinksTag.java Sat Aug 30 10:29:45 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/markup/PaginationLinksTag.java Sun Aug 31 09:07:52 2014 UTC
@@ -98,7 +98,7 @@ "<--", 0==p.getPage() ? "" : deriveHref(p.getPage()-1));- int[] pageNumbers = p.generateSuggestedPages(null==getLinkCount() ? LINK_COUNT_DEFAULT : getLinkCount()); + int[] pageNumbers = p.generateSuggestedPages(null == getLinkCount() ? LINK_COUNT_DEFAULT : getLinkCount());
for(int pageNumber : pageNumbers) { tagWriter.startTag("li"); =======================================--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/model/Pagination.java Sat Aug 30 10:29:45 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/multipage/model/Pagination.java Sun Aug 31 09:07:52 2014 UTC
@@ -7,6 +7,8 @@ import com.google.common.base.Preconditions; +import java.util.Arrays; + /*** <P>This object aims to provide the pagination within a list of items. It aims to be more or less
* like the "paginationcontroldirective.js" behaviour.</P> @@ -106,7 +108,7 @@int page = getPage() - 1; // assume the actual page has been set already
- for (int i = 0; i < startI; i++) { + for (int i = 0; i <= startI; i++) { float p = (float) i / (float) startI; float f = p * p; result[startI - i] = Math.min( @@ -125,7 +127,7 @@ public int[] generateSuggestedPages(int count) {- Preconditions.checkState(count > 3 && 0==count%2,"the count of pages must be more than 3 and an even number"); + Preconditions.checkState(count > 3,"the count of pages must be more than 3");
int pages = getPages(); @@ -142,6 +144,9 @@ int[] result = new int[count]; int middleI = count / 2; + // a debugging aid to see any bad values easily. + Arrays.fill(result,-10); + if(page < middleI) { for(int i=0;i<=page;i++) { @@ -155,7 +160,7 @@ int remainder = pages - page; - if(remainder < middleI) { + if(remainder <= (result.length - middleI) - 1) { for(int i=0;i<remainder;i++) { result[result.length - (i + 1)] = (pages - 1) - i; =======================================--- /haikudepotserver-webapp/src/main/webapp/WEB-INF/includes/unsupported.jsp Sat Aug 30 10:29:45 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/WEB-INF/includes/unsupported.jsp Sun Aug 31 09:07:52 2014 UTC
@@ -31,8 +31,9 @@<a href="https://www.haiku-os.org/docs/userguide/en/applications/webpositive.html";>WebPositive</a>,
<a href="https://www.mozilla.org/firefox";>Firefox</a>und <a href="https://www.google.com/chrome/browser/";>Google Chrome</a> - funktionieren auf alle Fälle. Eine einfache Darstellung ist auch
- <a href="/multipage">zu verfügen</a>. + funktionieren auf alle Fälle. Eine + <a href="/multipage">einfache Darstellung</a> + steht auch zur Verfügung. </div> </div> =======================================--- /haikudepotserver-webapp/src/main/webapp/js/app/directive/paginationcontroldirective.js Sat Aug 30 10:29:45 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/directive/paginationcontroldirective.js Sun Aug 31 09:07:52 2014 UTC
@@ -8,8 +8,8 @@ */ angular.module('haikudepotserver').directive('paginationControl',[ - '$parse','constants', - function($parse,constants) { + '$parse', + function($parse) { return { restrict: 'E', link : function($scope,element,attributes) { @@ -34,10 +34,6 @@ if(result < 3) {throw Error('a link count of ' + result + ' is not possible for the pagination control - it must be >= 3');
} - - if(0 == result % 2) {- throw Error('a link count of ' + result + ' is not possible for the pagination control - it must be an odd number');
- } return result; } @@ -47,16 +43,174 @@ var maxExpression = attributes['max']; var pageControlEs = []; - function adjustOffset(multiplier) { + /**+ * <p>This will return an object containing the parameters of the pagination.</p>
+ */ + + function parameters() { + var offset = $scope.$eval(offsetExpression); var max = $scope.$eval(maxExpression); var total = $scope.$eval(totalExpression); - offset += multiplier * max; - if(offset >= 0 && offset < total) { - $parse(offsetExpression).assign($scope,offset);+ // it is possible that those may evaluate to strings; in which case it is necessary to
+ // parse those into numerical values. + + if(!angular.isNumber(offset)) { + offset = parseInt(''+offset,10); } + + if(!angular.isNumber(max)) { + max = parseInt(''+max,10); + } + + if(!angular.isNumber(total)) { + total = parseInt(''+total,10); + } + + if(max <= 0) {+ throw Error('the \'max\' value must be a positive integer');
+ } + + if(offset < 0) { + throw Error('the \'offset\' must be >= 0'); + } + + if(0 != total && offset >= total) { + throw Error('the \'offset\' must be < '+total); + } ++ var pages = Math.floor((total / max) + (total % max ? 1 : 0));
+ var page = Math.floor(offset / max); // current page. + + return { + offset : offset, + max : max, + total : total, + pages : pages, + page : page + }; + } + + // --------------------- + // NAVIGATION / ALGORITHMS + + /** + * <p>This is used to go back or forward a page.</p>+ * @param direction -1 go back 1 page, 1 go forward one page.
+ */ + + function pageJumpBackOrForward(direction) { + var p = parameters(); + var offset = p.offset + (direction * max); + + if(offset >= 0 && offset < p.total) { + $parse(offsetExpression).assign($scope, offset); + } + } + + /**+ * <p>This generates the set of offsets to pages that are suggested for the pagination.</p>
+ */ + + function generateSuggestedPages(params, count) { + + switch(params.pages) { + + case 0: + return []; + + case 1: + return [0]; + + default: + var result = []; + + if (params.pages <= count) { + + // linear fill + + for (var i = 0; i < params.pages; i++) { + result.push(i); + } + } + else { ++ // fill the result with rubbish so that it is easy to detect problems.
+ + for (var j = 0; j < count; j++) { + result.push(-10); + } + + function fanFillRight(startI) { + + var pages = params.pages - 1; + var page = params.page + 1; + var len = count - startI; + + for (var k = 0; k < len; k++) { + var p = k / (len - 1); + var f = p * p; + result[startI + k] = Math.max(+ result[(startI + k) - 1] + 1, + page + Math.floor(f * (pages - page)));
+ } + + } + + function fanFillLeft(startI) { ++ var page = params.page - 1; // assume the actual page has been set already
+ + for (var l = 0; l <= startI; l++) { + var p = l / startI; + var f = p * p; + result[startI - l] = Math.min(+ result[(startI - l) + 1] - 1, + page - Math.floor(f * page));
+ } + } + + var middleI = Math.floor(count / 2); + + if (params.page < middleI) { ++ for (var m = 0; m <= params.page; m++) {
+ result[m] = m; + } + + fanFillRight(params.page + 1); + + } + else { ++ var remainder = params.pages - params.page;
++ if (remainder <= (result.length - middleI) - 1) {
++ for (var n = 0; n < remainder; n++) { + result[result.length - (n + 1)] = (params.pages - 1) - n;
+ } ++ fanFillLeft(result.length - (remainder + 1));
+ + } + else { + result[middleI] = params.page; + fanFillRight(middleI + 1); + fanFillLeft(middleI - 1); + } + + } + } + + return result; + } + } + + // --------------------- + // DOM SETUP // so we need this many elements to use as page numbers. @@ -71,14 +225,14 @@ topLevelE.append(leftArrowListItemE); leftArrowAnchorE.on('click', function(event) { - $scope.$apply(function() { adjustOffset(-1); });+ $scope.$apply(function() { pageJumpBackOrForward(-1); });
event.preventDefault(); return false; }); for(var i=0;i<deriveLinkCount();i++) {- var listItemE = angular.element('<li class=\"app-hide\"></li>'); - var pageControlE = angular.element('<a href=\"\"></a>'); + var listItemE = angular.element('<li class="app-hide"></li>');
+ var pageControlE = angular.element('<a href=""></a>'); pageControlEs.push(pageControlE); listItemE.append(pageControlE); topLevelE.append(listItemE); @@ -111,196 +265,65 @@ topLevelE.append(rightArrowListItemE); rightArrowAnchorE.on('click', function(event) { - $scope.$apply(function() { adjustOffset(1); });+ $scope.$apply(function() { pageJumpBackOrForward(1); });
event.preventDefault(); return false; }); - function disableAllPageControls() { - for(var i=0;i<pageControlEs.length;i++) { - pageControlEs[i].html(''); - pageControlEs[i].parent().addClass('app-hide'); - pageControlEs[i].attr('pagination-offset',''); - } - } - - function getOffsetFromPageControlEAtIndex(i) { - var a = pageControlEs[i].attr('pagination-offset'); - - if(a && a.length) { - return parseInt(''+a,10); - } - - return -1; - } - - function refreshPageControlsWithValues(total,offset,max) { - - if(max <= 0) {- throw Error('the \'max\' value must be a positive integer');
- } - - if(offset < 0) { - throw Error('the \'offset\' must be >= 0'); - } - - if(offset >= total) { - throw Error('the \'offset\' must be < '+total); - } + // --------------------- + // REFERESH DATA- var pages = Math.floor((total / max) + (total % max ? 1 : 0));
- var page = Math.floor(offset / max); // current page. + function refreshPageControls() { + var params = parameters();// if we're on the first or the last pages when we will need to add a class to the pagination // controls so that we can control the appearance of the controls.
- if(0==page) { + if(0==params.total || 0==params.page) { topLevelE.addClass('pagination-control-on-first'); } else {topLevelE.removeClass('pagination-control-on-first');
} - if(page==pages-1) { + if(0==params.total || params.page==params.pages-1) { topLevelE.addClass('pagination-control-on-last'); } else {topLevelE.removeClass('pagination-control-on-last');
} - function setPageControl(i,pageNumber) { - if(null==pageNumber) { - pageControlEs[i].text(''); + // now render the pages in between. ++ var suggestedPages = generateSuggestedPages(params, pageControlEs.length);
+ + for(var i=0;i<pageControlEs.length;i++) { + + if(i >= suggestedPages.length) { pageControlEs[i].parent().addClass('app-hide');pageControlEs[i].removeClass('pagination-control-currentpage');
pageControlEs[i].attr('pagination-offset',''); } else { - pageControlEs[i].text('' + (pageNumber + 1));pageControlEs[i].parent().removeClass('app-hide'); - pageControlEs[i].attr('pagination-offset',''+pageNumber * max);
- if(pageNumber==page) { + if(params.page == suggestedPages[i]) {pageControlEs[i].addClass('pagination-control-currentpage');
} else {pageControlEs[i].removeClass('pagination-control-currentpage');
} - } - } -- function linearFillPageControl(startI,length,pageStart) {
- for(var i=0;i<length;i++) { - setPageControl(startI+i,pageStart+i); - } - } - - if(pages <= 1) { - disableAllPageControls(); - } - else { -- // this function with p=[0,1] should give a nice curve that passes through 0 when p=0
- // and passes through 1 when p=1 - - function ramp(p) { - return p*p; - } -- function fanFillRightPageControl(pageControlEsStartI) {
-- var pageControlEsFillLength = (pageControlEs.length - pageControlEsStartI);
- - for(var i=0;i<pageControlEsFillLength;i++) { - var p = i/(pageControlEsFillLength-1); - var f = ramp(p);- var maxPagesRightOfPage = (pages - (page + 1))-1; - var nextPage = Math.floor((page + 1) + (maxPagesRightOfPage * f)); - var lastPage = Math.floor(getOffsetFromPageControlEAtIndex(pageControlEsStartI+i-1) / max);
- nextPage = _.max([nextPage,lastPage+1]);- setPageControl(pageControlEsStartI + i,nextPage);
- } - } -- function fanFillLeftPageControl(pageControlEsStartI) {
-- var pageControlEsFillLength = pageControlEsStartI + 1;
- - for(var i=0;i<pageControlEsFillLength;i++) { - var p = i/(pageControlEsFillLength-1); - var f = ramp(p);- var nextPage = Math.floor((page - 1) - ((page - 1) * f)); - var lastPage = Math.floor(getOffsetFromPageControlEAtIndex(pageControlEsStartI-i+1) / max);
- nextPage = _.min([nextPage,lastPage-1]);- setPageControl(pageControlEsStartI - i,nextPage);
- } - } -- // if there are <= pages than controls then just show the pages linearly and hide the rest of
- // the controls that are not necessary. - - if(pages <= pageControlEs.length) { - linearFillPageControl(0,pages,0); - for(var i=pages;i<pageControlEs.length;i++) { - setPageControl(i,null); - }+ pageControlEs[i].attr('pagination-offset',''+(suggestedPages[i] * params.max)); + pageControlEs[i].text('' + (suggestedPages[i] + 1));
} - else { -- // we have more pages out there than we have page controls so, we need to put the first - // and last page into place, choose a sensible location for the current page and arrange
- // other sensible options for other pages. -- var middleI = Math.floor(pageControlEs.length / 2);
- - if(page < middleI) { // close to the left side - linearFillPageControl(0,page+1,0); - fanFillRightPageControl(page+1); - } - else { - var remainder = pages-page;- if(remainder < middleI) { // close to the right side. - linearFillPageControl(pageControlEs.length - remainder,remainder,page); - fanFillLeftPageControl((pageControlEs.length - remainder) - 1);
- } - else { - setPageControl(middleI,page); - fanFillRightPageControl(middleI+1); - fanFillLeftPageControl(middleI-1); - } - } - } } - } -- // looks at the settings bound to this and will show/hide the page numbers.
- - function refreshPageControls() { -- if(totalExpression && offsetExpression && maxExpression) {
- - var total = $scope.$eval(totalExpression); - var offset = $scope.$eval(offsetExpression); - var max = $scope.$eval(maxExpression); - if(!angular.isUndefined(total) && - !angular.isUndefined(offset) && - !angular.isUndefined(max) && - total > 0) {- refreshPageControlsWithValues(total,offset,max);
- } - else { - disableAllPageControls(); - } - } - else { - disableAllPageControls(); - } } - refreshPageControls(); + // --------------------- + // EVENT OBSERVATION if(totalExpression) { $scope.$watch(totalExpression, function () { @@ -319,6 +342,8 @@ refreshPageControls(); }); } + + refreshPageControls(); } } =======================================--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/multipage/model/PaginationTest.java Sat Aug 30 10:29:45 2014 UTC +++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/multipage/model/PaginationTest.java Sun Aug 31 09:07:52 2014 UTC
@@ -43,5 +43,17 @@ Pagination p = new Pagination(500,250,10);Assertions.assertThat(p.generateSuggestedPages(10)).isEqualTo(new int[] {0,9,16,21,23,24,27,31,38,49});
} + + @Test + public void testGenerateSuggestedPages_general_1() { + Pagination p = new Pagination(168,120,15);+ Assertions.assertThat(p.generateSuggestedPages(9)).isEqualTo(new int[] {0,4,5,6,7,8,9,10,11});
+ } + + @Test + public void testGenerateSuggestedPages_general_2() { + Pagination p = new Pagination(168,75,15);+ Assertions.assertThat(p.generateSuggestedPages(9)).isEqualTo(new int[] {0,2,3,4,5,6,7,8,11});
+ } } ============================================================================== Revision: ccc92b6dfc59 Author: Andrew Lindesay <apl@xxxxxxxxxxxxxx> Date: Sun Aug 31 10:18:17 2014 UTC Log: ability to get localized names back for some reference data various small fixes and tweaks https://code.google.com/p/haiku-depot-web-app/source/detail?r=ccc92b6dfc59 Added:/haikudepotserver-webapp/src/main/resources/db/haikudepot/migration/V1.17__Categories_code_lowercase.sql
Modified:/haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/MiscellaneousApi.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/miscellaneous/GetAllNaturalLanguagesRequest.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/miscellaneous/GetAllPkgCategoriesRequest.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/miscellaneous/GetAllUserRatingStabilitiesRequest.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/MiscellaneousApiImpl.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/NaturalLanguage.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/UserRatingStability.java
/haikudepotserver-webapp/src/main/resources/messages_de.properties/haikudepotserver-webapp/src/main/webapp/WEB-INF/views/multipage/viewPkgVersion.jsp /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/api1/MiscelaneousApiIT.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/multipage/model/PaginationTest.java
======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/resources/db/haikudepot/migration/V1.17__Categories_code_lowercase.sql Sun Aug 31 10:18:17 2014 UTC
@@ -0,0 +1,5 @@ +-- ------------------------------------------------------ +-- PKG CATEGORIES' CODES LOWER CASE +-- ------------------------------------------------------ + +UPDATE haikudepot.pkg_category SET code=LOWER(code); =======================================--- /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/MiscellaneousApi.java Thu Aug 14 11:03:33 2014 UTC +++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/MiscellaneousApi.java Sun Aug 31 10:18:17 2014 UTC
@@ -13,13 +13,15 @@ public interface MiscellaneousApi { /** - * <p>Returns a list of all of the categories.</p>+ * <p>Returns a list of all of the categories. If a natural language code is supplied in the reuqest, then + * the results' names will be localized; otherwise a database-based default will be returned.</p>
*/GetAllPkgCategoriesResult getAllPkgCategories(GetAllPkgCategoriesRequest getAllPkgCategoriesRequest);
/** - * <p>Returns a list of all of the natural languages.</p>+ * <p>Returns a list of all of the natural languages. If a natural language code is supplied in the request + * then the results' names will be localized; otherwise a database-based default will be returned.</p>
*/GetAllNaturalLanguagesResult getAllNaturalLanguages(GetAllNaturalLanguagesRequest getAllNaturalLanguagesRequest);
@@ -55,7 +57,8 @@ /*** <p>This method will return all of the possible user rating stabilities that can be used when the user
- * rates a package version.</p>+ * rates a package version. If a natural language code is supplied in the request then the results' names + * will be localized; otherwise a database-based default will be used.</p>
*/GetAllUserRatingStabilitiesResult getAllUserRatingStabilities(GetAllUserRatingStabilitiesRequest getAllUserRatingStabilitiesRequest);
=======================================--- /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/miscellaneous/GetAllNaturalLanguagesRequest.java Mon Mar 10 10:18:38 2014 UTC +++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/miscellaneous/GetAllNaturalLanguagesRequest.java Sun Aug 31 10:18:17 2014 UTC
@@ -6,4 +6,11 @@ package org.haikuos.haikudepotserver.api1.model.miscellaneous; public class GetAllNaturalLanguagesRequest { + + /**+ * <p>If supplied, the results' names will be localized by this natural language code.</p>
+ */ + + public String naturalLanguageCode; + } =======================================--- /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/miscellaneous/GetAllPkgCategoriesRequest.java Mon Mar 10 10:18:38 2014 UTC +++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/miscellaneous/GetAllPkgCategoriesRequest.java Sun Aug 31 10:18:17 2014 UTC
@@ -6,4 +6,11 @@ package org.haikuos.haikudepotserver.api1.model.miscellaneous; public class GetAllPkgCategoriesRequest { + + /**+ * <p>If supplied, the results' names will be localized by this natural language code.</p>
+ */ + + public String naturalLanguageCode; + } =======================================--- /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/miscellaneous/GetAllUserRatingStabilitiesRequest.java Mon Apr 21 10:48:33 2014 UTC +++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/miscellaneous/GetAllUserRatingStabilitiesRequest.java Sun Aug 31 10:18:17 2014 UTC
@@ -6,4 +6,11 @@ package org.haikuos.haikudepotserver.api1.model.miscellaneous; public class GetAllUserRatingStabilitiesRequest { + + /**+ * <p>If supplied, the results' names will be localized by this natural language code.</p>
+ */ + + public String naturalLanguageCode; + } =======================================--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/MiscellaneousApiImpl.java Thu Aug 14 11:03:33 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/MiscellaneousApiImpl.java Sun Aug 31 10:18:17 2014 UTC
@@ -20,6 +20,7 @@ import org.haikuos.haikudepotserver.support.RuntimeInformationService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.context.MessageSource; import org.springframework.stereotype.Component; import javax.annotation.Resource; @@ -47,20 +48,39 @@ @Resource FeedOrchestrationService feedOrchestrationService; + @Resource + MessageSource messageSource; + @Overridepublic GetAllPkgCategoriesResult getAllPkgCategories(GetAllPkgCategoriesRequest getAllPkgCategoriesRequest) {
Preconditions.checkNotNull(getAllPkgCategoriesRequest); final ObjectContext context = serverRuntime.getContext(); + final Optional<NaturalLanguage> naturalLanguageOptional =+ Strings.isNullOrEmpty(getAllPkgCategoriesRequest.naturalLanguageCode)
+ ? Optional.<NaturalLanguage>absent()+ : NaturalLanguage.getByCode(context, getAllPkgCategoriesRequest.naturalLanguageCode);
+ return new GetAllPkgCategoriesResult( Lists.transform( PkgCategory.getAll(context),new Function<PkgCategory, GetAllPkgCategoriesResult.PkgCategory>() {
@Overridepublic GetAllPkgCategoriesResult.PkgCategory apply(PkgCategory input) { - return new GetAllPkgCategoriesResult.PkgCategory(
- input.getCode(), - input.getName()); + + if(naturalLanguageOptional.isPresent()) {+ return new GetAllPkgCategoriesResult.PkgCategory(
+ input.getCode(), + messageSource.getMessage( + input.getTitleKey(), + null, // params+ naturalLanguageOptional.get().toLocale()));
+ } + else {+ return new GetAllPkgCategoriesResult.PkgCategory(
+ input.getCode(), + input.getName()); + } } } ) @@ -72,15 +92,31 @@ Preconditions.checkNotNull(getAllNaturalLanguagesRequest); final ObjectContext context = serverRuntime.getContext(); + final Optional<NaturalLanguage> naturalLanguageOptional =+ Strings.isNullOrEmpty(getAllNaturalLanguagesRequest.naturalLanguageCode)
+ ? Optional.<NaturalLanguage>absent()+ : NaturalLanguage.getByCode(context, getAllNaturalLanguagesRequest.naturalLanguageCode);
+ return new GetAllNaturalLanguagesResult( Lists.transform( NaturalLanguage.getAll(context),new Function<NaturalLanguage, GetAllNaturalLanguagesResult.NaturalLanguage>() {
@Overridepublic GetAllNaturalLanguagesResult.NaturalLanguage apply(NaturalLanguage input) { - return new GetAllNaturalLanguagesResult.NaturalLanguage(
- input.getCode(), - input.getName()); + + if(naturalLanguageOptional.isPresent()) {+ return new GetAllNaturalLanguagesResult.NaturalLanguage(
+ input.getCode(), + messageSource.getMessage( + input.getTitleKey(), + null, // params+ naturalLanguageOptional.get().toLocale()));
+ } + else {+ return new GetAllNaturalLanguagesResult.NaturalLanguage(
+ input.getCode(), + input.getName()); + } } } ) @@ -213,15 +249,31 @@ Preconditions.checkNotNull(getAllUserRatingStabilitiesRequest); final ObjectContext context = serverRuntime.getContext(); + final Optional<NaturalLanguage> naturalLanguageOptional =+ Strings.isNullOrEmpty(getAllUserRatingStabilitiesRequest.naturalLanguageCode)
+ ? Optional.<NaturalLanguage>absent()+ : NaturalLanguage.getByCode(context, getAllUserRatingStabilitiesRequest.naturalLanguageCode);
+ return new GetAllUserRatingStabilitiesResult( Lists.transform( UserRatingStability.getAll(context),new Function<UserRatingStability, GetAllUserRatingStabilitiesResult.UserRatingStability>() {
@Overridepublic GetAllUserRatingStabilitiesResult.UserRatingStability apply(UserRatingStability input) { - return new GetAllUserRatingStabilitiesResult.UserRatingStability(
- input.getCode(), - input.getName()); + + if(naturalLanguageOptional.isPresent()) {+ return new GetAllUserRatingStabilitiesResult.UserRatingStability(
+ input.getCode(), + messageSource.getMessage( + input.getTitleKey(), + null, // params+ naturalLanguageOptional.get().toLocale()));
+ } + else {+ return new GetAllUserRatingStabilitiesResult.UserRatingStability(
+ input.getCode(), + input.getName()); + } } } ) @@ -239,7 +291,9 @@new Function<Prominence, GetAllProminencesResult.Prominence>() {
@Overridepublic GetAllProminencesResult.Prominence apply(Prominence input) { - return new GetAllProminencesResult.Prominence(input.getOrdering(), input.getName()); + return new GetAllProminencesResult.Prominence(
+ input.getOrdering(), + input.getName()); } } ) =======================================--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/NaturalLanguage.java Sat Aug 30 10:29:45 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/NaturalLanguage.java Sun Aug 31 10:18:17 2014 UTC
@@ -55,6 +55,14 @@ExpressionFactory.matchExp(MediaType.CODE_PROPERTY, code))),
null)); } + + /**+ * <p>Can be used to lookup the title of this language in the localization strings.</p>
+ */ + + public String getTitleKey() { + return String.format("naturalLanguage.%s",getCode().toLowerCase()); + } public Locale toLocale() { return Locale.forLanguageTag(getCode()); =======================================--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/UserRatingStability.java Mon Apr 21 10:48:33 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/UserRatingStability.java Sun Aug 31 10:18:17 2014 UTC
@@ -44,5 +44,10 @@query.addOrdering(new Ordering(NAME_PROPERTY, SortOrder.ASCENDING));
return (List<UserRatingStability>) context.performQuery(query); } + + + public String getTitleKey() { + return String.format("userRatingStability.%s.title", getCode()); + } } =======================================--- /haikudepotserver-webapp/src/main/resources/messages_de.properties Sat Aug 30 10:29:45 2014 UTC +++ /haikudepotserver-webapp/src/main/resources/messages_de.properties Sun Aug 31 10:18:17 2014 UTC
@@ -378,7 +378,7 @@ # Multipage (non-AngularJS) Interface multipage.banner.title.suffix=Einfach-multipage.banner.note=Dies ist die vereinfachte Darstellung, gedacht für Minimal-Browser. +multipage.banner.note=Dies ist die vereinfachte Darstellung, gedacht für Minimal-Browser
multipage.banner.note.full=Standard-Darstellung # Test case =======================================--- /haikudepotserver-webapp/src/main/webapp/WEB-INF/views/multipage/viewPkgVersion.jsp Sat Aug 30 10:29:45 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/WEB-INF/views/multipage/viewPkgVersion.jsp Sun Aug 31 10:18:17 2014 UTC
@@ -39,7 +39,7 @@<multipage:pkgIcon pkgVersion="${data.pkgVersion}" size="32"/>
</div> <div id="pkg-title-text">- <h1><c:out value="${data.pkgVersion.getPkgVersionLocalizationOrFallback(data.currentNaturalLanguage).summary}"/></h1> + <h1><c:out value="${data.pkgVersion.getPkgVersionLocalizationOrFallback(data.currentNaturalLanguage.code).summary}"/></h1>
<div class="muted"> <small> <c:out value="${data.pkgVersion.pkg.name}"></c:out> @@ -68,7 +68,7 @@ <div id="pkg-description-container"> <p>- <multipage:plainTextContent value="${data.pkgVersion.getPkgVersionLocalizationOrFallback(data.currentNaturalLanguage).description}"></multipage:plainTextContent> + <multipage:plainTextContent value="${data.pkgVersion.getPkgVersionLocalizationOrFallback(data.currentNaturalLanguage.code).description}"></multipage:plainTextContent>
</p> </div> =======================================--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/api1/MiscelaneousApiIT.java Thu Aug 14 11:03:33 2014 UTC +++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/api1/MiscelaneousApiIT.java Sun Aug 31 10:18:17 2014 UTC
@@ -19,6 +19,7 @@ import org.haikuos.haikudepotserver.api1.support.ObjectNotFoundException; import org.haikuos.haikudepotserver.dataobjects.NaturalLanguage; import org.haikuos.haikudepotserver.dataobjects.PkgCategory; +import org.haikuos.haikudepotserver.dataobjects.UserRatingStability; import org.haikuos.haikudepotserver.feed.controller.FeedController; import org.haikuos.haikudepotserver.support.RuntimeInformationService; import org.junit.Test; @@ -36,6 +37,53 @@ @Resource RuntimeInformationService runtimeInformationService; + + @Test + public void testGetAllUserRatingStabilities() { + + // ------------------------------------+ GetAllUserRatingStabilitiesResult result = miscellaneousApi.getAllUserRatingStabilities(new GetAllUserRatingStabilitiesRequest());
+ // ------------------------------------ + + ObjectContext objectContext = serverRuntime.getContext(); ++ List<UserRatingStability> userRatingStabilities = UserRatingStability.getAll(objectContext);
++ Assertions.assertThat(userRatingStabilities.size()).isEqualTo(result.userRatingStabilities.size());
+ + for (int i = 0; i < userRatingStabilities.size(); i++) {+ UserRatingStability userRatingStability = userRatingStabilities.get(i); + GetAllUserRatingStabilitiesResult.UserRatingStability apiUserRatingStability = result.userRatingStabilities.get(i); + Assertions.assertThat(userRatingStability.getCode()).isEqualTo(apiUserRatingStability.code); + Assertions.assertThat(userRatingStability.getName()).isEqualTo(apiUserRatingStability.name);
+ } + } + + @Test + public void testGetAllUserRatingStabilities_de() { ++ GetAllUserRatingStabilitiesRequest request = new GetAllUserRatingStabilitiesRequest();
+ request.naturalLanguageCode = NaturalLanguage.CODE_GERMAN; + + // ------------------------------------+ GetAllUserRatingStabilitiesResult result = miscellaneousApi.getAllUserRatingStabilities(request);
+ // ------------------------------------ ++ Optional<GetAllUserRatingStabilitiesResult.UserRatingStability> userRatingStabilityOptional =
+ Iterables.tryFind( + result.userRatingStabilities,+ new Predicate<GetAllUserRatingStabilitiesResult.UserRatingStability>() {
+ @Override+ public boolean apply(GetAllUserRatingStabilitiesResult.UserRatingStability input) {
+ return input.code.equals("mostlystable"); + } + } + ); ++ Assertions.assertThat(userRatingStabilityOptional.isPresent()).isTrue(); + Assertions.assertThat(userRatingStabilityOptional.get().name).isEqualTo("Ziemlich stabil");
+ + } @Test public void testGetAllPkgCategories() { @@ -57,6 +105,37 @@Assertions.assertThat(pkgCategory.getCode()).isEqualTo(apiPkgCategory.code);
} } + + /**+ * <p>If the client asks for all of the categories with a natural language code then they will be returned + * the localized name of the category where possible. This tests this with German as German translations
+ * are known to be present.</p> + */ + + @Test + public void testGetAllPkgCategories_de() { ++ GetAllPkgCategoriesRequest request = new GetAllPkgCategoriesRequest();
+ request.naturalLanguageCode = NaturalLanguage.CODE_GERMAN; + + // ------------------------------------+ GetAllPkgCategoriesResult result = miscellaneousApi.getAllPkgCategories(request);
+ // ------------------------------------ ++ Optional<GetAllPkgCategoriesResult.PkgCategory> pkgCategoryOptional =
+ Iterables.tryFind( + result.pkgCategories,+ new Predicate<GetAllPkgCategoriesResult.PkgCategory>() {
+ @Override+ public boolean apply(GetAllPkgCategoriesResult.PkgCategory input) {
+ return input.code.equals("EDUCATION"); + } + } + ); + + Assertions.assertThat(pkgCategoryOptional.isPresent()).isTrue();+ Assertions.assertThat(pkgCategoryOptional.get().name).isEqualTo("Ausbildung");
+ } @Test public void testGetAllNaturalLanguages() { @@ -78,6 +157,37 @@Assertions.assertThat(naturalLanguage.getCode()).isEqualTo(apiNaturalLanguage.code);
} } + + /**+ * <p>It is possible to request the natural languages with a natural language code. In this case, the + * results will localize the names as opposed to using those onces directly from the database.</p>
+ */ + + @Test + public void testGetAllNaturalLanguages_de() { ++ GetAllNaturalLanguagesRequest request = new GetAllNaturalLanguagesRequest();
+ request.naturalLanguageCode = NaturalLanguage.CODE_GERMAN; + + // ------------------------------------+ GetAllNaturalLanguagesResult result = miscellaneousApi.getAllNaturalLanguages(request);
+ // ------------------------------------ ++ Optional<GetAllNaturalLanguagesResult.NaturalLanguage> naturalLanguageOptional =
+ Iterables.tryFind( + result.naturalLanguages,+ new Predicate<GetAllNaturalLanguagesResult.NaturalLanguage>() {
+ @Override+ public boolean apply(GetAllNaturalLanguagesResult.NaturalLanguage input) {
+ return input.code.equalsIgnoreCase("es"); + } + } + ); ++ Assertions.assertThat(naturalLanguageOptional.isPresent()).isTrue(); + Assertions.assertThat(naturalLanguageOptional.get().name).isEqualTo("Espa\u00F1ol");
+ + } @Test public void getRuntimeInformation_asUnauthenticated() { =======================================--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/multipage/model/PaginationTest.java Sun Aug 31 09:07:52 2014 UTC +++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/multipage/model/PaginationTest.java Sun Aug 31 10:18:17 2014 UTC
@@ -35,13 +35,13 @@ @Test public void testGenerateSuggestedPages_fanLeft() { Pagination p = new Pagination(500,475,10);- Assertions.assertThat(p.generateSuggestedPages(10)).isEqualTo(new int[] {0,13,23,31,38,43,45,46,48,49}); + Assertions.assertThat(p.generateSuggestedPages(10)).isEqualTo(new int[] {0,15,26,35,41,45,46,47,48,49});
} @Test public void testGenerateSuggestedPages_fanLeftAndRight() { Pagination p = new Pagination(500,250,10);- Assertions.assertThat(p.generateSuggestedPages(10)).isEqualTo(new int[] {0,9,16,21,23,24,27,31,38,49}); + Assertions.assertThat(p.generateSuggestedPages(10)).isEqualTo(new int[] {0,11,18,23,24,25,26,28,36,49});
} @Test ============================================================================== Revision: 6dcf4d936436 Author: Andrew Lindesay <apl@xxxxxxxxxxxxxx> Date: Sun Aug 31 12:05:02 2014 UTC Log: updates to integration tests for lower case pkg category namesupdate integration tests to show that bulk pkg get will return 'any' architecture packages
https://code.google.com/p/haiku-depot-web-app/source/detail?r=6dcf4d936436 Modified:/haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/PkgVersion.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/IntegrationTestSupportService.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/api1/MiscelaneousApiIT.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/api1/PkgApiIT.java
=======================================--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/PkgVersion.java Sat Aug 30 10:29:45 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/PkgVersion.java Sun Aug 31 12:05:02 2014 UTC
@@ -183,6 +183,10 @@ if(!pkgVersionLocalizationOptional.isPresent()) {pkgVersionLocalizationOptional = getPkgVersionLocalization(NaturalLanguage.CODE_ENGLISH);
} + + if(!pkgVersionLocalizationOptional.isPresent()) {+ throw new IllegalStateException("unable to find the fallback localization for " + toString());
+ } return pkgVersionLocalizationOptional.get(); } =======================================--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/IntegrationTestSupportService.java Wed Aug 6 09:46:44 2014 UTC +++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/IntegrationTestSupportService.java Sun Aug 31 12:05:02 2014 UTC
@@ -111,6 +111,14 @@ addPngPkgIcon(objectContext, pkg, 32); addHvifPkgIcon(objectContext, pkg); } ++ public void addDummyLocalization(ObjectContext context, PkgVersion pkgVersion) { + PkgVersionLocalization pkgVersionLocalization = context.newObject(PkgVersionLocalization.class); + pkgVersionLocalization.setNaturalLanguage(NaturalLanguage.getByCode(context, NaturalLanguage.CODE_ENGLISH).get());
+ pkgVersionLocalization.setDescription("sample description"); + pkgVersionLocalization.setSummary("sample summary");+ pkgVersion.addToManyTarget(PkgVersion.PKG_VERSION_LOCALIZATIONS_PROPERTY, pkgVersionLocalization, true);
+ } public StandardTestData createStandardTestData() { @@ -123,6 +131,7 @@ Architecture x86 = Architecture.getByCode(context, "x86").get();Architecture x86_gcc2 = Architecture.getByCode(context, "x86_gcc2").get();
+ Architecture any = Architecture.getByCode(context, "any").get(); result.repository = context.newObject(Repository.class); result.repository.setActive(Boolean.TRUE); @@ -140,7 +149,7 @@ {PkgPkgCategory pkgPkgCategory = context.newObject(PkgPkgCategory.class); result.pkg1.addToManyTarget(Pkg.PKG_PKG_CATEGORIES_PROPERTY, pkgPkgCategory, true); - pkgPkgCategory.setPkgCategory(PkgCategory.getByCode(context, "GRAPHICS").get()); + pkgPkgCategory.setPkgCategory(PkgCategory.getByCode(context, "graphics").get());
} addPkgScreenshot(context,result.pkg1); @@ -157,6 +166,7 @@ result.pkg1Version1x86.setIsLatest(false); result.pkg1Version1x86.setPkg(result.pkg1); result.pkg1Version1x86.setRepository(result.repository); + addDummyLocalization(context, result.pkg1Version1x86); result.pkg1Version2x86 = context.newObject(PkgVersion.class); result.pkg1Version2x86.setActive(Boolean.TRUE); @@ -218,6 +228,7 @@ result.pkg2Version1.setIsLatest(true); result.pkg2Version1.setPkg(result.pkg2); result.pkg2Version1.setRepository(result.repository); + addDummyLocalization(context, result.pkg2Version1); result.pkg3 = context.newObject(Pkg.class); result.pkg3.setActive(true); @@ -233,6 +244,23 @@ result.pkg3Version1.setIsLatest(true); result.pkg3Version1.setPkg(result.pkg3); result.pkg3Version1.setRepository(result.repository); + addDummyLocalization(context, result.pkg3Version1); + + result.pkgAny = context.newObject(Pkg.class); + result.pkgAny.setActive(true); + result.pkgAny.setName("pkgany"); + result.pkgAny.setProminence(prominence); + + result.pkgAnyVersion1 = context.newObject(PkgVersion.class); + result.pkgAnyVersion1.setActive(Boolean.TRUE); + result.pkgAnyVersion1.setArchitecture(any); + result.pkgAnyVersion1.setMajor("123"); + result.pkgAnyVersion1.setMicro("123"); + result.pkgAnyVersion1.setRevision(3); + result.pkgAnyVersion1.setIsLatest(true); + result.pkgAnyVersion1.setPkg(result.pkgAny); + result.pkgAnyVersion1.setRepository(result.repository); + addDummyLocalization(context, result.pkgAnyVersion1); context.commitChanges(); @@ -330,6 +358,10 @@ public Pkg pkg3; public PkgVersion pkg3Version1; + + public Pkg pkgAny; + public PkgVersion pkgAnyVersion1; + } } =======================================--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/api1/MiscelaneousApiIT.java Sun Aug 31 10:18:17 2014 UTC +++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/api1/MiscelaneousApiIT.java Sun Aug 31 12:05:02 2014 UTC
@@ -128,7 +128,7 @@new Predicate<GetAllPkgCategoriesResult.PkgCategory>() {
@Overridepublic boolean apply(GetAllPkgCategoriesResult.PkgCategory input) {
- return input.code.equals("EDUCATION"); + return input.code.equals("education"); } } ); =======================================--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/api1/PkgApiIT.java Wed Aug 6 09:46:44 2014 UTC +++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/api1/PkgApiIT.java Sun Aug 31 12:05:02 2014 UTC
@@ -33,6 +33,7 @@ import javax.annotation.Resource; import java.util.Collections; import java.util.List; +import java.util.Set; public class PkgApiIT extends AbstractIntegrationTest { @@ -59,13 +60,13 @@ {PkgPkgCategory pkgPkgCategory = context.newObject(PkgPkgCategory.class); - pkgPkgCategory.setPkgCategory(PkgCategory.getByCode(context, "GAMES").get()); + pkgPkgCategory.setPkgCategory(PkgCategory.getByCode(context, "games").get()); pkg.addToManyTarget(Pkg.PKG_PKG_CATEGORIES_PROPERTY, pkgPkgCategory, true);
} {PkgPkgCategory pkgPkgCategory = context.newObject(PkgPkgCategory.class); - pkgPkgCategory.setPkgCategory(PkgCategory.getByCode(context, "BUSINESS").get()); + pkgPkgCategory.setPkgCategory(PkgCategory.getByCode(context, "business").get()); pkg.addToManyTarget(Pkg.PKG_PKG_CATEGORIES_PROPERTY, pkgPkgCategory, true);
} @@ -74,7 +75,7 @@UpdatePkgCategoriesRequest request = new UpdatePkgCategoriesRequest();
request.pkgName = data.pkg1.getName();- request.pkgCategoryCodes = ImmutableList.of("BUSINESS", "DEVELOPMENT"); + request.pkgCategoryCodes = ImmutableList.of("business", "development");
// ------------------------------------ pkgApi.updatePkgCategories(request); @@ -87,16 +88,16 @@ ObjectContext context = serverRuntime.getContext(); Pkg pkg = Pkg.getByName(context, data.pkg1.getName()).get();- Assertions.assertThat(ImmutableSet.of("BUSINESS", "DEVELOPMENT")).isEqualTo(
- ImmutableSet.copyOf(Iterables.transform( - pkg.getPkgPkgCategories(), - new Function<PkgPkgCategory, String>() { - @Override - public String apply(PkgPkgCategory input) { - return input.getPkgCategory().getCode();+ Assertions.assertThat(ImmutableSet.of("business", "development")).isEqualTo(
+ ImmutableSet.copyOf(Iterables.transform( + pkg.getPkgPkgCategories(), + new Function<PkgPkgCategory, String>() { + @Override + public String apply(PkgPkgCategory input) {+ return input.getPkgCategory().getCode();
+ } } - } - )) + )) ); } @@ -118,7 +119,7 @@ SearchPkgsResult result = pkgApi.searchPkgs(request); // ------------------------------------ - Assertions.assertThat(result.total).isEqualTo(3);+ Assertions.assertThat(result.total).isEqualTo(4); // note includes the "any" package
Assertions.assertThat(result.items.size()).isEqualTo(2); Assertions.assertThat(result.items.get(0).name).isEqualTo("pkg1"); Assertions.assertThat(result.items.get(1).name).isEqualTo("pkg2"); @@ -690,13 +691,27 @@ request.versionType = PkgVersionType.LATEST; request.architectureCode = "x86"; request.naturalLanguageCode = "en"; - request.pkgNames = ImmutableList.of("pkg1","pkg2","pkg3");+ request.pkgNames = ImmutableList.of("pkg1","pkg2","pkg3","pkg4","pkgany"); // pkg4 does not exist
// ------------------------------------ GetBulkPkgResult result = pkgApi.getBulkPkg(request); // ------------------------------------ - Assertions.assertThat(result.pkgs.size()).isEqualTo(3);+ Assertions.assertThat(result.pkgs.size()).isEqualTo(4); // includes the any package
+ + // check they are all there. + + Set<String> packageNames = ImmutableSet.copyOf(Lists.transform( + result.pkgs, + new Function<GetBulkPkgResult.Pkg, String>() { + @Override + public String apply(GetBulkPkgResult.Pkg input) { + return input.name; + } + } + )); ++ Assertions.assertThat(packageNames).containsOnly("pkg1","pkg2","pkg3","pkgany");
// now check pkg1 because it has some in-depth data on it. @@ -711,7 +726,7 @@ Assertions.assertThat(pkg1.modifyTimestamp).isNotNull(); Assertions.assertThat(pkg1.pkgCategoryCodes.size()).isEqualTo(1);- Assertions.assertThat(pkg1.pkgCategoryCodes.get(0)).isEqualTo("GRAPHICS"); + Assertions.assertThat(pkg1.pkgCategoryCodes.get(0)).isEqualTo("graphics");
Assertions.assertThat(pkg1.derivedRating).isNotNull();Assertions.assertThat(pkg1.derivedRating).isGreaterThanOrEqualTo(0.0f);
@@ -728,11 +743,11 @@ Assertions.assertThat(pkg1.pkgIcons.size()).isEqualTo(3); Assertions.assertThat(Iterables.tryFind(pkg1.pkgIcons, new Predicate<org.haikuos.haikudepotserver.api1.model.pkg.PkgIcon>() {
- @Override- public boolean apply(org.haikuos.haikudepotserver.api1.model.pkg.PkgIcon input) { - return input.mediaTypeCode.equals(org.haikuos.haikudepotserver.dataobjects.MediaType.MEDIATYPE_HAIKUVECTORICONFILE);
- } - }).isPresent()).isTrue(); + @Override+ public boolean apply(org.haikuos.haikudepotserver.api1.model.pkg.PkgIcon input) { + return input.mediaTypeCode.equals(org.haikuos.haikudepotserver.dataobjects.MediaType.MEDIATYPE_HAIKUVECTORICONFILE);
+ } + }).isPresent()).isTrue(); Assertions.assertThat(pkg1.versions.size()).isEqualTo(1);Assertions.assertThat(pkg1.versions.get(0).naturalLanguageCode).isEqualTo("en");