master moved from 79eb8a4e32f8 to 4ee4560ddc66 5 new revisions: Revision: fb41a5edfbe3 Author: Andrew Lindesay <apl@xxxxxxxxxxxxxx> Date: Tue Feb 18 00:02:17 2014 UTC Log: + fix for hpkr chunk reading where reads cross over chunks... http://code.google.com/p/haiku-depot-web-app/source/detail?r=fb41a5edfbe3 Revision: d1c7e6ee2e39 Author: Andrew Lindesay <apl@xxxxxxxxxxxxxx> Date: Tue Feb 18 00:17:42 2014 UTC Log: + modify routes to handle re-navigation to the view pkg page http://code.google.com/p/haiku-depot-web-app/source/detail?r=d1c7e6ee2e39 Revision: 2ba6b572106c Author: Andrew Lindesay <apl@xxxxxxxxxxxxxx> Date: Tue Feb 18 09:54:04 2014 UTC Log: + additional data for screenshots... http://code.google.com/p/haiku-depot-web-app/source/detail?r=2ba6b572106c Revision: 99da10a910a9 Author: Andrew Lindesay <apl@xxxxxxxxxxxxxx> Date: Tue Feb 18 10:09:54 2014 UTC Log: + documentation update... http://code.google.com/p/haiku-depot-web-app/source/detail?r=99da10a910a9 Revision: 4ee4560ddc66 Author: Andrew Lindesay <apl@xxxxxxxxxxxxxx> Date: Tue Feb 18 10:16:48 2014 UTC Log: + cache control on icons and screenshots http://code.google.com/p/haiku-depot-web-app/source/detail?r=4ee4560ddc66 ============================================================================== Revision: fb41a5edfbe3 Author: Andrew Lindesay <apl@xxxxxxxxxxxxxx> Date: Tue Feb 18 00:02:17 2014 UTC Log: + fix for hpkr chunk reading where reads cross over chunks + implemented basic screenshot handling functions http://code.google.com/p/haiku-depot-web-app/source/detail?r=fb41a5edfbe3 Added: /haikudepotserver-webapp/src/main/webapp/css/editpkgscreenshots.css/haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgscreenshots.html /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgscreenshotscontroller.js /haikudepotserver-webapp/src/main/webapp/js/app/service/pkgscreenshotservice.js
Modified:/haikudepotserver-packagefile/src/main/java/org/haikuos/pkg/HpkStringTable.java /haikudepotserver-packagefile/src/main/java/org/haikuos/pkg/heap/HpkHeapReader.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/PkgApiImpl.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/PkgService.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/controller/PkgScreenshotController.java
/haikudepotserver-webapp/src/main/resources/messages.properties /haikudepotserver-webapp/src/main/resources/spring/webresourcegroup.xml /haikudepotserver-webapp/src/main/webapp/css/haikudepotserver.css /haikudepotserver-webapp/src/main/webapp/css/viewpkg.css/haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgiconcontroller.js /haikudepotserver-webapp/src/main/webapp/js/app/controller/homecontroller.js
/haikudepotserver-webapp/src/main/webapp/js/app/controller/viewpkg.html/haikudepotserver-webapp/src/main/webapp/js/app/controller/viewpkgcontroller.js /haikudepotserver-webapp/src/main/webapp/js/app/directive/breadcrumbsdirective.js /haikudepotserver-webapp/src/main/webapp/js/app/directive/validpassworddirective.js
/haikudepotserver-webapp/src/main/webapp/js/app/routes.js/haikudepotserver-webapp/src/main/webapp/js/app/service/breadcrumbsservice.js
/haikudepotserver-webapp/src/main/webapp/js/app/service/pkgiconservice.js /haikudepotserver-webapp/src/main/webapp/js/app/service/userstateservice.js ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/webapp/css/editpkgscreenshots.css Tue Feb 18 00:02:17 2014 UTC
@@ -0,0 +1,24 @@ +#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 > img { + float: left; + border: 1px solid black; +} + +#pkg-screenshots-list div.pkg-screenshot-controls { + margin-left: 190px; + padding-top: 4px; + padding-bottom: 4px; +} + + + ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgscreenshots.html Tue Feb 18 00:02:17 2014 UTC
@@ -0,0 +1,39 @@ +<breadcrumbs items="breadcrumbItems"></breadcrumbs> + +<div class="content-container edit-pkg-screenshots"> + + <div class="onelineform-container"> + <form name="addPkgScreenshotForm" novalidate="novalidate"> + PNG Image File :+ <input name="file" type="file" ng-model="addPkgScreenshot.file" file-supply required></input>
+ <button + ng-disabled="addPkgScreenshotForm.$invalid" + ng-click="goAddPkgScreenshot()" + type="submit" + class="main-action">Add</button> ++ <error-messages key-prefix="addPkgScreenshot.file" error="addPkgScreenshotForm.file.$error" ng-show="!addPkgScreenshotForm.$pristine"></error-messages>
+ + </form> + </div> + + <div id="pkg-screenshots-list"> + <div ng-repeat="pkgScreenshot in pkgScreenshots"> + <img ng-src="{{pkgScreenshot.imageThumbnailUrl}}"></img> + <div class="pkg-screenshot-controls"> + <div><strong>#{{$index+1}}</strong></div> + + <ul>+ <li><a href="" ng-click="goRemovePkgScreenshot(pkgScreenshot)">Remove</a></li> + <li><a href="{{pkgScreenshot.imageDownloadUrl}}">Download</a></li>
+ </ul> + </div> + <div style="clear: both;"></div> + </div> + </div> + +</div> + +<div class="footer"></div> +<spinner spin="shouldSpin()"></spinner> + ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgscreenshotscontroller.js Tue Feb 18 00:02:17 2014 UTC
@@ -0,0 +1,180 @@ +/* + * Copyright 2014, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +angular.module('haikudepotserver').controller( + 'EditPkgScreenshotsController', + [ + '$scope','$log','$location','$routeParams', + 'jsonRpc','constants','pkgScreenshot','errorHandling', + 'breadcrumbs', + function( + $scope,$log,$location,$routeParams, + jsonRpc,constants,pkgScreenshot,errorHandling, + breadcrumbs) { ++ // the upload size must be less than this or it is too big for the
+ // far end to process. + + var FILESIZEMAX = 2 * 1024 * 1024; + + var THUMBNAIL_TARGETWIDTH = 180; + var THUMBNAIL_TARGETHEIGHT = 90; + + $scope.breadcrumbItems = undefined; + $scope.pkg = undefined; + $scope.pkgScreenshots = undefined; + $scope.amCommunicating = false; + $scope.addPkgScreenshot = { + file : undefined + }; + + $scope.shouldSpin = function() {+ return undefined == $scope.pkg || undefined == $scope.pkgScreenshots || $scope.amCommunicating;
+ } + + $scope.deriveFormControlsContainerClasses = function(name) {+ return $scope.addPkgScreenshotForm[name].$invalid ? ['form-control-group-error'] : [];
+ } ++ // pulls back the list of package screenshots so that they can be displayed
+ // in a list. + + function refetchPkgScreenshots() { + jsonRpc.call( + constants.ENDPOINT_API_V1_PKG, + "getPkgScreenshots", + [{ pkgName: $routeParams.name }] + ).then( + function(result) {+ $scope.pkgScreenshots = _.map(result.items, function(item) {
+ return { + code : item.code,+ imageThumbnailUrl : pkgScreenshot.url($scope.pkg,item.code,THUMBNAIL_TARGETWIDTH,THUMBNAIL_TARGETHEIGHT), + imageDownloadUrl : pkgScreenshot.rawUrl($scope.pkg,item.code)
+ }; + }) ++ $log.info('found '+result.items.length+' screenshots for pkg '+result.name);
+ }, + function(err) { + errorHandling.handleJsonRpcError(err); + } + ); + } ++ // pulls the pkg data back from the server so that it can be used to
+ // display the form. + + function refetchPkg() { + jsonRpc.call( + constants.ENDPOINT_API_V1_PKG, + "getPkg", + [{ + name: $routeParams.name, + versionType: 'NONE',+ architectureCode: undefined // not required if we don't need the version
+ }] + ).then( + function(result) { + $scope.pkg = result; + $log.info('found '+result.name+' pkg'); + refreshBreadcrumbItems(); + refetchPkgScreenshots(); + }, + function(err) { + errorHandling.handleJsonRpcError(err); + } + ); + } + + refetchPkg(); + + function refreshBreadcrumbItems() { + $scope.breadcrumbItems = [+ breadcrumbs.createViewPkg($scope.pkg,'latest',$location.search()['arch']),
+ breadcrumbs.createEditPkgScreenshots($scope.pkg) + ]; + } + + // ------------------------- + // ADDING A SCREENSHOT ++ // This function will check to make sure that the file is not too large or too small to be a valid PNG. + // The 'model' is an instance of ngModel from in the form onto which the invalidity can be applied. + // This will also reset any validation problems that relate to the bad format of the PNG payload from + // the server because the user will not know if an updated file is also bad until the server has seen it; + // ie: the validation is happening server-side rather than client-side.
+ + $scope.$watch('addPkgScreenshot.file', function(newValue) { + var file = $scope.addPkgScreenshot.file; + var model = $scope.addPkgScreenshotForm['file'] + model.$setValidity('badformatorsize',true);+ model.$setValidity('badsize',undefined==file || (file.size
24 && file.size < FILESIZEMAX));
+ }); ++ // This function will take the data from the form and will create the user from this data.
+ + $scope.goAddPkgScreenshot = function() { + + if($scope.addPkgScreenshotForm.$invalid) {+ throw 'expected the editing of package screenshots only to be possible if the form is valid';
+ } + + $scope.amCommunicating = true; ++ // two PUT requests are made to the server in order to convey the PNG data.
++ pkgScreenshot.addScreenshot($scope.pkg, $scope.addPkgScreenshot.file).then(
+ function(code) {+ $log.info('have added a screenshot for the pkg '+$scope.pkg.name);
+ $scope.addPkgScreenshot.file = undefined; + $scope.addPkgScreenshotForm.$setPristine(); + $scope.pkgScreenshots.push({ + code : code,+ imageThumbnailUrl : pkgScreenshot.url($scope.pkg,code,THUMBNAIL_TARGETWIDTH,THUMBNAIL_TARGETHEIGHT), + imageDownloadUrl : pkgScreenshot.rawUrl($scope.pkg,code)
+ }) + }, + function(e) {+ if(e==pkgScreenshot.errorCodes.BADFORMATORSIZEERROR) { + $scope.addPkgScreenshotForm['file'].$setValidity('badformatorsize',false);
+ } + else {+ $log.error('unable to add the screenshot for; '+$scope.pkg.name);
+ $location.path('/error').search({}); + } + } + )['finally']( + function() { + $scope.amCommunicating = false; + + } + ) + } + + // ------------------------- + // REMOVE A SCREENSHOT + + $scope.goRemovePkgScreenshot = function(pkgScreenshot) { + jsonRpc.call( + constants.ENDPOINT_API_V1_PKG, + "removePkgScreenshot", + [{ code: pkgScreenshot.code }] + ).then( + function() {+ $log.info('did remove screenshot '+pkgScreenshot.code); + $scope.pkgScreenshots = _.reject($scope.pkgScreenshots, function(item) {
+ return item.code == pkgScreenshot.code; + }) + }, + function(err) { + errorHandling.handleJsonRpcError(err); + } + ); + } + + } + ] +); ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/webapp/js/app/service/pkgscreenshotservice.js Tue Feb 18 00:02:17 2014 UTC
@@ -0,0 +1,172 @@ +/* + * Copyright 2014, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +/**+ * <p>This service provides functionality for accessing and updating the screenshots for a package.</p>
+ */ + +angular.module('haikudepotserver').factory('pkgScreenshot', + [ + '$log','$q','$http', + function($log,$q,$http) { + + var PkgScreenshot = { ++ // these are errors that may be returned to the caller below. They match to the HTTP status codes
+ // used, but this should not be relied upon. + + errorCodes : { + BADFORMATORSIZEERROR : 415, + NOTFOUND : 404, + BADREQUEST : 400, + UNKNOWN : -1 + }, + + /**+ * <p>This is a map of HTTP headers that is sent on each request into the server.</p>
+ */ + + headers : {}, + + /**+ * <p>This method will set the HTTP header that is sent on each request. This is handy,
+ * for example for authentication.</p> + */ + + setHeader : function(name, value) { + + if(!name || 0==''+name.length) { + throw 'the name of the http header is required'; + } + + if(!value || 0==''+value.length) { + delete PkgScreenshot.headers[name]; + } + else { + PkgScreenshot.headers[name] = value; + } + + }, ++ // this function will add a screenshot for the package nominated.
+ + addScreenshot : function(pkg, screenshotFile) { + + if(!pkg) {+ throw 'the pkg must be supplied to add a pkg screenshot';
+ } + + if(!screenshotFile) {+ throw 'to add a screenshot for '+pkg.name+' the image file must be provided';
+ } + + var deferred = $q.defer(); + + $http({ + cache: false, + method: 'PUT', + url: '/pkgscreenshot/'+pkg.name+'.png', + headers: _.extend( + { 'Content-Type' : 'image/png' }, + PkgScreenshot.headers), + data: screenshotFile + }) + .success(function(data,status,header,config) {+ var code = header('X-HaikuDepotServer-ScreenshotCode');
+ + if(!code || !code.length) {+ throw 'the screenshot code should have been supplied back from creating a new screenshot';
+ } + + deferred.resolve(code); + }) + .error(function(data,status,header,config) { + switch(status) { + case 200: + deferred.resolve(); + break; + + case 415: // unsupported media type+ deferred.reject(PkgScreenshot.errorCodes.BADFORMATORSIZEERROR);
+ break; + + case 400: // bad request+ deferred.reject(PkgScreenshot.errorCodes.BADREQUEST);
+ break; + + case 404: // not found+ deferred.reject(PkgScreenshot.errorCodes.NOTFOUND);
+ break; + + default:+ deferred.reject(PkgScreenshot.errorCodes.UNKNOWN);
+ break; + + } + }); + + return deferred.promise; + }, + + /**+ * <p>This function will provide a raw-data download for the screenshot.</p>
+ */ + + rawUrl : function(pkg, code) { + if(!pkg) {+ throw 'the pkg must be supplied to get a package screenshot url';
+ } + + if(!code || !code.length) {+ throw 'the code must be supplied to derive a url for the screenshot image';
+ } + + return '/pkgscreenshot/raw/' + code; + }, + + /**+ * <p>This function will provide a URL to the packages' screenshot. The package object is supplied and + * a code is supplied to identify the actual screenshot. The target width and height provide the + * size o the image that the screenshot will be scaled to.</p>
+ */ + + url : function(pkg, code, targetWidth, targetHeight) { + if(!pkg) {+ throw 'the pkg must be supplied to get a package screenshot url';
+ } + + if(!code || !code.length) {+ throw 'the code must be supplied to derive a url for the screenshot image';
+ } + + var u = '/pkgscreenshot/' + code + '.png'; + var q = []; + + if(pkg.modifyTimestamp) { + q.push('m=' + pkg.modifyTimestamp); + } + + if(targetWidth) { + q.push('tw=' + targetWidth); + } + + if(targetHeight) { + q.push('th=' + targetHeight); + } + + if(q.length) { + u += '?' + q.join('&'); + } + + return u; + } + + }; + + return PkgScreenshot; + + } + ] +); =======================================--- /haikudepotserver-packagefile/src/main/java/org/haikuos/pkg/HpkStringTable.java Fri Nov 15 08:51:45 2013 UTC +++ /haikudepotserver-packagefile/src/main/java/org/haikuos/pkg/HpkStringTable.java Tue Feb 18 00:02:17 2014 UTC
@@ -1,5 +1,5 @@ /* - * Copyright 2013, Andrew Lindesay + * Copyright 2013-2014, Andrew Lindesay * Distributed under the terms of the MIT License. */ @@ -47,7 +47,7 @@ }- // TODO; could avoid the big read into a buffer by reading the heap byte by byte. + // TODO; could avoid the big read into a buffer by reading the heap byte by byte or with a buffer.
private String[] readStrings() throws HpkException { String[] result = new String[(int) expectedCount]; byte[] stringsDataBuffer = new byte[(int) heapLength]; @@ -58,6 +58,9 @@ new HeapCoordinates( heapOffset, heapLength)); + +// try { Files.write(stringsDataBuffer, new File("/tmp/dat")); } +// catch(IOException ioe) {} // now work through the data and load them into the strings. @@ -73,10 +76,14 @@ return result; } + + if(stringIndex >= expectedCount) {+ throw new HpkException("have already read all of the strings from the string table, but have not exhausted the string table data");
+ } int start = offset;- while(0!=stringsDataBuffer[offset] && offset < stringsDataBuffer.length) { + while(offset < stringsDataBuffer.length && 0!=stringsDataBuffer[offset]) {
offset++; } @@ -88,7 +95,7 @@ }- throw new HpkException("expected to find the null-terminator for the list of strings, but was not able to find one."); + throw new HpkException("expected to find the null-terminator for the list of strings, but was not able to find one; did read "+stringIndex+" of "+expectedCount);
} private String[] getStrings() throws HpkException { =======================================--- /haikudepotserver-packagefile/src/main/java/org/haikuos/pkg/heap/HpkHeapReader.java Fri Nov 15 08:51:45 2013 UTC +++ /haikudepotserver-packagefile/src/main/java/org/haikuos/pkg/heap/HpkHeapReader.java Tue Feb 18 00:02:17 2014 UTC
@@ -1,5 +1,5 @@ /* - * Copyright 2013, Andrew Lindesay + * Copyright 2013-2014, Andrew Lindesay * Distributed under the terms of the MIT License. */ @@ -338,7 +338,7 @@ buffer, bufferOffset + chunkLength, new HeapCoordinates( - heapOffset + chunkLength, + coordinates.getOffset() + chunkLength, coordinates.getLength() - chunkLength)); } =======================================--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/PkgApiImpl.java Wed Feb 12 10:07:08 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/PkgApiImpl.java Tue Feb 18 00:02:17 2014 UTC
@@ -284,7 +284,7 @@ @Overridepublic GetPkgScreenshotsResult getPkgScreenshots(GetPkgScreenshotsRequest getPkgScreenshotsRequest) throws ObjectNotFoundException {
Preconditions.checkNotNull(getPkgScreenshotsRequest); - Preconditions.checkNotNull(getPkgScreenshotsRequest.pkgName);+ Preconditions.checkState(!Strings.isNullOrEmpty(getPkgScreenshotsRequest.pkgName));
final ObjectContext context = serverRuntime.getContext();Optional<Pkg> pkgOptional = Pkg.getByName(context, getPkgScreenshotsRequest.pkgName);
=======================================--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/PkgService.java Fri Feb 14 08:42:33 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/PkgService.java Tue Feb 18 00:02:17 2014 UTC
@@ -248,6 +248,11 @@ byte[] pngData = ByteStreams.toByteArray(input); ImageHelper.Size size = imageHelper.derivePngSize(pngData); + if(null==size) {+ logger.warn("attempt to set the package icon for package {}, but the data does not look like png",pkg.getName());
+ throw new BadPkgIconException(); + } +// check that the file roughly looks like PNG and that the size can be
// parsed and that the size fits the requirements for the icon. @@ -322,7 +327,7 @@ByteArrayInputStream imageInputStream = new ByteArrayInputStream(data);
BufferedImage bufferedImage = ImageIO.read(imageInputStream);BufferedImage scaledBufferedImage = Scalr.resize(bufferedImage, targetWidth, targetHeight);
- ImageIO.write(scaledBufferedImage, "PNG", output); + ImageIO.write(scaledBufferedImage, "png", output); } else { output.write(data); @@ -347,11 +352,17 @@ byte[] pngData = ByteStreams.toByteArray(input); ImageHelper.Size size = imageHelper.derivePngSize(pngData); + if(null==size) {+ logger.warn("attempt to store a screenshot image that is not a png");
+ throw new BadPkgScreenshotException(); + } +// check that the file roughly looks like PNG and the size is something
// reasonable.if(size.height > SCREENSHOT_SIDE_LIMIT || size.width > SCREENSHOT_SIDE_LIMIT) { logger.warn("attempt to store a screenshot image that is too large; "+size.toString());
+ throw new BadPkgScreenshotException(); }MediaType png = MediaType.getByCode(context, com.google.common.net.MediaType.PNG.toString()).get();
=======================================--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/controller/PkgScreenshotController.java Wed Feb 12 10:07:08 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/controller/PkgScreenshotController.java Tue Feb 18 00:02:17 2014 UTC
@@ -62,8 +62,8 @@ public void fetchHead( HttpServletRequest request, HttpServletResponse response, - @RequestParam(value = KEY_TARGETWIDTH) Integer targetWidth, - @RequestParam(value = KEY_TARGETHEIGHT) Integer targetHeight,+ @RequestParam(value = KEY_TARGETWIDTH, required=true) Integer targetWidth, + @RequestParam(value = KEY_TARGETHEIGHT, required=true) Integer targetHeight,
@PathVariable(value = KEY_FORMAT) String format,@PathVariable(value = KEY_SCREENSHOTCODE) String screenshotCode)
throws IOException { @@ -149,9 +149,6 @@ throw new ScreenshotNotFound(); }- ByteCounterOutputStream byteCounter = new ByteCounterOutputStream(new NoOpOutputStream());
-- response.setHeader(HttpHeaders.CONTENT_LENGTH, Long.toString(byteCounter.getCounter()));
response.setContentType(MediaType.PNG.toString()); response.setDateHeader( HttpHeaders.LAST_MODIFIED, @@ -164,6 +161,55 @@ targetWidth, targetHeight); } + + /**+ * <p>This one downloads the raw data that is stored for a screenshot.</p>
+ */ ++ @RequestMapping(value = "/raw/{"+KEY_SCREENSHOTCODE+"}", method = RequestMethod.GET)
+ public void fetchRawGet( + HttpServletRequest request, + HttpServletResponse response,+ @PathVariable(value = KEY_SCREENSHOTCODE) String screenshotCode)
+ throws IOException { + + if(Strings.isNullOrEmpty(screenshotCode)) { + throw new MissingScreenshotCode(); + } + + ObjectContext context = serverRuntime.getContext();+ Optional<PkgScreenshot> screenshotOptional = PkgScreenshot.getByCode(context, screenshotCode);
+ + if(!screenshotOptional.isPresent()) { + throw new ScreenshotNotFound(); + } ++ byte[] data = screenshotOptional.get().getPkgScreenshotImage().get().getData(); + org.haikuos.haikudepotserver.dataobjects.MediaType mediaType = screenshotOptional.get().getPkgScreenshotImage().get().getMediaType();
+ + // TODO - find a better way to do this. + String extension = null; + + if(mediaType.getCode().equals(MediaType.PNG.toString())) { + extension = "png"; + } + + if(null==extension) {+ throw new IllegalStateException("the media type for the screenshot is not able to be converted into a file extension");
+ } + + response.setContentLength(data.length); + response.setContentType(mediaType.getCode()); + response.setHeader( + HttpHeaders.CONTENT_DISPOSITION, + String.format( + "attachment; filename=\"%s__%s.%s\"", + screenshotOptional.get().getPkg().getName(), + screenshotOptional.get().getCode(), + extension)); + + response.getOutputStream().write(data); + } /*** <p>This handler will take-up an HTTP PUT that provides a new screenshot for the package.</p>
=======================================--- /haikudepotserver-webapp/src/main/resources/messages.properties Sun Feb 9 10:38:52 2014 UTC +++ /haikudepotserver-webapp/src/main/resources/messages.properties Tue Feb 18 00:02:17 2014 UTC
@@ -17,6 +17,7 @@changePassword.newPasswordClearRepeated.repeat=The password has not been repeated correctly. changePassword.captchaResponse.required=The result of this simple question must be supplied to prove that the change password is from a human operator. changePassword.oldPasswordClear.mismatched=Authentication problem; try to enter the existing password again. +changePassword.captchaResponse.badresponse=The response supplied does not match the question in the image or the question has expired; a new image has been provided.
authenticateUser.nickname.required=The nickname is required to login. authenticateUser.passwordClear.required=The password is required to login. @@ -45,6 +46,9 @@ addEditRepository.url.required=The url to the .hpkr data must be supplied. addEditRepository.url.malformed=The url must be well-formed.+addPkgScreenshot.file.badformatorsize=The file must be a PNG image no larger than 1500x1500 and smaller than 2 megabytes in length.
+addPkgScreenshot.file.required=A data file is required to add a screenshot.+addPkgScreenshot.file.badsize=The file must be a PNG image no larger than 1500x1500 and smaller than 2 megabytes in length.
# Test case test.it=Test line for integration testing =======================================--- /haikudepotserver-webapp/src/main/resources/spring/webresourcegroup.xml Sun Feb 9 10:00:28 2014 UTC +++ /haikudepotserver-webapp/src/main/resources/spring/webresourcegroup.xml Tue Feb 18 00:02:17 2014 UTC
@@ -77,12 +77,14 @@<value>/js/app/controller/listrepositoriescontroller.js</value> <value>/js/app/controller/viewrepositorycontroller.js</value> <value>/js/app/controller/addeditrepositorycontroller.js</value> + <value>/js/app/controller/editpkgscreenshotscontroller.js</value>
<value>/js/app/service/jsonrpcservice.js</value> <value>/js/app/service/messagesourceservice.js</value> <value>/js/app/service/userstateservice.js</value> <value>/js/app/service/referencedataservice.js</value> <value>/js/app/service/pkgiconservice.js</value> + <value>/js/app/service/pkgscreenshotservice.js</value> <value>/js/app/service/breadcrumbsservice.js</value> <value>/js/app/service/errorhandlingservice.js</value>
@@ -109,6 +111,7 @@ <value>/css/viewpkg.css</value> <value>/css/banner.css</value> <value>/css/breadcrumbs.css</value> + <value>/css/editpkgscreenshots.css</value> </list> </property> </bean> =======================================--- /haikudepotserver-webapp/src/main/webapp/css/haikudepotserver.css Sat Feb 8 11:31:22 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/css/haikudepotserver.css Tue Feb 18 00:02:17 2014 UTC
@@ -64,7 +64,6 @@ ALERT BOXES FOR USE OUTSIDE OF FORMS */ - .alert-container { padding: 8px; border: 1px solid red; @@ -78,6 +77,17 @@ background-color: white; color: black; } + +.onelineform-container { + padding: 8px; + border: 1px solid black; + background-color: white; + color: black; +} + +.onelineform-container form { + margin-bottom: 0; +} /* ================================== =======================================--- /haikudepotserver-webapp/src/main/webapp/css/viewpkg.css Mon Jan 13 10:50:43 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/css/viewpkg.css Tue Feb 18 00:02:17 2014 UTC
@@ -23,7 +23,7 @@ float: right; width: 480px; border: 1px solid black; - min-height: 320px; + min-height: 96px; position: relative; background-color: #EEE; } @@ -32,6 +32,11 @@ background-color: #444; color: white; padding: 4px; + border-bottom: 1px solid black; +} + +#pkg-screenshot-title.muted { + background-color: rgba(1,1,1,0.5); } /* @@ -39,8 +44,8 @@ for this package. */ -#pkg-screenshot-none { - margin-top: 32px; +#pkg-screenshot-status { + margin-top: 20px; margin-bottom: 0px; width: 240px; margin-left: auto; =======================================--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgiconcontroller.js Sat Feb 8 09:19:06 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgiconcontroller.js Tue Feb 18 00:02:17 2014 UTC
@@ -8,9 +8,11 @@ [ '$scope','$log','$location','$routeParams', 'jsonRpc','constants','pkgIcon','errorHandling', + 'breadcrumbs', function( $scope,$log,$location,$routeParams, - jsonRpc,constants,pkgIcon,errorHandling) { + jsonRpc,constants,pkgIcon,errorHandling, + breadcrumbs) { $scope.breadcrumbItems = undefined; $scope.pkg = undefined; @@ -56,14 +58,9 @@ function refreshBreadcrumbItems() { $scope.breadcrumbItems = [ - { - title : $scope.pkg.name, - path : '/viewpkg/'+$scope.pkg.name - }, - { - title : 'Edit Icon', // TODO - localize - path : '/editpkgicon/'+$scope.pkg.name - }];+ breadcrumbs.createViewPkg($scope.pkg,'latest',$location.search()['arch']),
+ breadcrumbs.createEditPkgIcon($scope.pkg) + ]; }// This function will check to make sure that the file is not too large or too small to be a valid PNG.
@@ -93,12 +90,7 @@ icon16FileDidChange(); }); - - $scope.nicknameDidChange = function() {- $scope.createUserForm.nickname.$setValidity('notunique',true);
- } -- // This function will take the data from the form and will create the user from this data. + // This function will take the data from the form and load in the new pkg icons
$scope.goStorePkgIcons = function() { =======================================--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/homecontroller.js Sun Feb 2 09:15:35 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/homecontroller.js Tue Feb 18 00:02:17 2014 UTC
@@ -139,14 +139,6 @@ return false; } - - $scope.classPreviousPage = function() { - return $scope.offset > 0 ? [] : ['disabled']; - } - - $scope.classNextPage = function() { - return $scope.hasMore ? [] : ['disabled']; - } // ---- VIEW PKG + VERSION =======================================--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/viewpkg.html Sat Feb 8 11:31:22 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/viewpkg.html Tue Feb 18 00:02:17 2014 UTC
@@ -26,16 +26,28 @@ <div id="pkg-screenshot-container"> <div id="pkg-screenshot-pagination-container">- <pagination-arrow active="false" direction="left"></pagination-arrow> - <pagination-arrow active="false" direction="right"></pagination-arrow>
+ <pagination-arrow + page-click="goPreviousScreenshot()"+ active="pkgScreenshots.length && pkgScreenshotOffset > 0"
+ direction="left"></pagination-arrow> + <pagination-arrow + page-click="goNextScreenshot()"+ active="pkgScreenshots.length && pkgScreenshotOffset < (pkgScreenshots.length-1)"
+ direction="right"></pagination-arrow> </div> <div id="pkg-screenshot-title"> - Screenshots + <span ng-show="!pkgScreenshots.length">Screenshots</span> + <span ng-show="pkgScreenshots.length">+ Screenshot {{pkgScreenshotOffset+1}} <span class="muted">of {{pkgScreenshots.length}}</span>
+ </span> </div> - <div id="pkg-screenshot-none"> - No screenshots available+ <img ng-show="pkgScreenshots.length" ng-src="{{currentPkgScreenshot().imageThumbnailUrl}}">
++ <div id="pkg-screenshot-status" ng-show="!pkgScreenshots.length">
+ <span ng-show="undefined==pkgScreenshots">Loading...</span>+ <span ng-show="pkgScreenshots && !pkgScreenshots.length">No screenshots available</span>
</div> </div> @@ -51,6 +63,9 @@<li ng-show="canEditIcon()" pkg="pkg" show-if-pkg-permission="'PKG_EDITICON'">
<a href="" ng-click="goEditIcon()">Edit icons</a> </li>+ <li ng-show="canEditScreenshots()" pkg="pkg" show-if-pkg-permission="'PKG_EDITSCREENSHOT'"> + <a href="" ng-click="goEditScreenshots()">Edit screenshots</a>
+ </li> </ul> </div> =======================================--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/viewpkgcontroller.js Wed Feb 12 10:07:08 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/viewpkgcontroller.js Tue Feb 18 00:02:17 2014 UTC
@@ -8,12 +8,19 @@ [ '$scope','$log','$location','$routeParams', 'jsonRpc','constants','userState','errorHandling', + 'pkgScreenshot', function( $scope,$log,$location,$routeParams, - jsonRpc,constants,userState,errorHandling) { + jsonRpc,constants,userState,errorHandling, + pkgScreenshot) { + + var SCREENSHOT_THUMBNAIL_TARGETWIDTH = 480; + var SCREENSHOT_THUMBNAIL_TARGETHEIGHT = 320; $scope.breadcrumbItems = undefined; $scope.pkg = undefined; + $scope.pkgScreenshots = undefined; + $scope.pkgScreenshotOffset = 0; refetchPkg(); @@ -28,6 +35,10 @@ $scope.canEditIcon = function() { return $scope.pkg; } + + $scope.canEditScreenshots = function() { + return $scope.pkg; + } $scope.homePageLink = function() { var u = undefined; @@ -67,16 +78,81 @@ $scope.pkg = result; $log.info('found '+result.name+' pkg'); refreshBreadcrumbItems(); + refetchPkgScreenshots(); }, function(err) { errorHandling.handleJsonRpcError(err); } ); } + + // ------------------------ + // SCREENSHOTS + + $scope.goPreviousScreenshot = function() {+ if($scope.pkgScreenshots && $scope.pkgScreenshotOffset > 0) {
+ $scope.pkgScreenshotOffset--; + } + return false; + } + + $scope.goNextScreenshot = function() {+ if($scope.pkgScreenshots && $scope.pkgScreenshotOffset < ($scope.pkgScreenshots.length-1)) {
+ $scope.pkgScreenshotOffset++; + } + return false; + } + + $scope.currentPkgScreenshot = function() {+ return $scope.pkgScreenshots ? $scope.pkgScreenshots[$scope.pkgScreenshotOffset] : null;
+ } + + function refetchPkgScreenshots() { + + $scope.pkgScreenshots = undefined; + + jsonRpc.call( + constants.ENDPOINT_API_V1_PKG, + "getPkgScreenshots", + [{ pkgName: $scope.pkg.name }] + ).then( + function(result) {+ $scope.pkgScreenshots = _.map(result.items, function(item) {
+ return { + code : item.code, + imageThumbnailUrl : pkgScreenshot.url( + $scope.pkg, + item.code, + SCREENSHOT_THUMBNAIL_TARGETWIDTH, + SCREENSHOT_THUMBNAIL_TARGETHEIGHT), + imageDownloadUrl : pkgScreenshot.rawUrl( + $scope.pkg, + item.code) + }; + }); + + $scope.pkgScreenshotOffset = 0; ++ $log.info('found '+result.items.length+' screenshots for pkg '+$routeParams.name);
+ refreshBreadcrumbItems(); + }, + function(err) { + errorHandling.handleJsonRpcError(err); + } + ); + + } + + // --------------------- + // ACTIONS FOR PACKAGE $scope.goEditIcon = function() {$location.path("/editpkgicon/"+$scope.pkg.name).search({'arch':$routeParams.architectureCode});
} + + $scope.goEditScreenshots = function() {+ $location.path("/editpkgscreenshots/"+$scope.pkg.name).search({'arch':$routeParams.architectureCode});
+ } $scope.goRemoveIcon = function() { jsonRpc.call( =======================================--- /haikudepotserver-webapp/src/main/webapp/js/app/directive/breadcrumbsdirective.js Fri Nov 15 08:51:45 2013 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/directive/breadcrumbsdirective.js Tue Feb 18 00:02:17 2014 UTC
@@ -1,5 +1,5 @@ /* - * Copyright 2013, Andrew Lindesay + * Copyright 2013-2014, Andrew Lindesay * Distributed under the terms of the MIT License. */ @@ -49,7 +49,12 @@ }); $scope.goItem = function(item) { - $location.path(item.path).search({}); + $location.path(item.path); + + if(item.search) { + $location.search(item.search); + } + return false; } =======================================--- /haikudepotserver-webapp/src/main/webapp/js/app/directive/validpassworddirective.js Thu Jan 16 08:37:44 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/directive/validpassworddirective.js Tue Feb 18 00:02:17 2014 UTC
@@ -16,7 +16,7 @@ ctrl.$parsers.unshift(function(value) { - var valid = (value.length >= 8) + var valid = value && (value.length >= 8) && value.replace(/[^0-9]/g,'').length >= 2 && value.replace(/[^A-Z]/g,'').length >= 1; =======================================--- /haikudepotserver-webapp/src/main/webapp/js/app/routes.js Sun Feb 9 10:00:28 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/routes.js Tue Feb 18 00:02:17 2014 UTC
@@ -40,6 +40,7 @@.when('/viewuser/:nickname',{controller:'ViewUserController', templateUrl:'/js/app/controller/viewuser.html'}) .when('/viewpkg/:name/:version/:architectureCode',{controller:'ViewPkgController', templateUrl:'/js/app/controller/viewpkg.html'}) .when('/editpkgicon/:name',{controller:'EditPkgIconController', templateUrl:'/js/app/controller/editpkgicon.html'}) + .when('/editpkgscreenshots/:name',{controller:'EditPkgScreenshotsController', templateUrl:'/js/app/controller/editpkgscreenshots.html'}) .when('/error',{controller:'ErrorController', templateUrl:'/js/app/controller/error.html'})
.when('/',{ controller:'HomeController', =======================================--- /haikudepotserver-webapp/src/main/webapp/js/app/service/breadcrumbsservice.js Sun Feb 9 10:00:28 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/service/breadcrumbsservice.js Tue Feb 18 00:02:17 2014 UTC
@@ -55,6 +55,27 @@ }; },+ createViewPkg : function(pkg,versionType,architectureCode) {
+ return { + title : pkg.name,+ path : '/viewpkg/' + pkg.name + '/' + versionType + '/' + architectureCode
+ }; + }, + + createEditPkgIcon : function(pkg) { + return { + title : 'Edit Icon', + path : '/editpkgicon/' + pkg.name + }; + }, + + createEditPkgScreenshots : function(pkg) { + return { + title : 'Screenshots', // TODO - localize + path : '/editpkgscreenshots/'+pkg.name + }; + }, + createViewUser : function(user) { return { title : user.nickname, =======================================--- /haikudepotserver-webapp/src/main/webapp/js/app/service/pkgiconservice.js Thu Dec 5 09:23:22 2013 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/service/pkgiconservice.js Tue Feb 18 00:02:17 2014 UTC
@@ -1,5 +1,5 @@ /* - * Copyright 2013, Andrew Lindesay + * Copyright 2013-2014, Andrew Lindesay * Distributed under the terms of the MIT License. */ @@ -20,7 +20,7 @@ errorCodes : { BADFORMATORSIZEERROR : 415, NOTFOUND : 404, - BADREQUEST : 404, + BADREQUEST : 400, UNKNOWN : -1 }, =======================================--- /haikudepotserver-webapp/src/main/webapp/js/app/service/userstateservice.js Sat Feb 8 09:19:06 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/service/userstateservice.js Tue Feb 18 00:02:17 2014 UTC
@@ -13,8 +13,11 @@ angular.module('haikudepotserver').factory('userState', [ - '$log','$q','$rootScope','jsonRpc','pkgIcon','errorHandling','constants',- function($log,$q,$rootScope,jsonRpc,pkgIcon,errorHandling,constants) {
+ '$log','$q','$rootScope','jsonRpc', + 'pkgIcon','pkgScreenshot','errorHandling','constants', + function( + $log,$q,$rootScope,jsonRpc, + pkgIcon,pkgScreenshot,errorHandling,constants) { const SIZE_CHECKED_PERMISSION_CACHE = 25; @@ -208,6 +211,7 @@// remove the Authorization header for HTTP transport
jsonRpc.setHeader('Authorization'); pkgIcon.setHeader('Authorization'); + pkgScreenshot.setHeader('Authorization'); } else { @@ -219,13 +223,11 @@throw 'the password clear is required when setting a user';
} - jsonRpc.setHeader( - 'Authorization', - 'Basic '+window.btoa(''+value.nickname+':'+value.passwordClear));+ var basic = 'Basic '+window.btoa(''+value.nickname+':'+value.passwordClear);
- pkgIcon.setHeader( - 'Authorization', - 'Basic '+window.btoa(''+value.nickname+':'+value.passwordClear)); + jsonRpc.setHeader('Authorization',basic); + pkgIcon.setHeader('Authorization',basic); + pkgScreenshot.setHeader('Authorization',basic); user = value; ============================================================================== Revision: d1c7e6ee2e39 Author: Andrew Lindesay <apl@xxxxxxxxxxxxxx> Date: Tue Feb 18 00:17:42 2014 UTC Log: + modify routes to handle re-navigation to the view pkg page http://code.google.com/p/haiku-depot-web-app/source/detail?r=d1c7e6ee2e39 Modified:/haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgiconcontroller.js /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgscreenshotscontroller.js /haikudepotserver-webapp/src/main/webapp/js/app/controller/viewpkgcontroller.js
/haikudepotserver-webapp/src/main/webapp/js/app/routes.js/haikudepotserver-webapp/src/main/webapp/js/app/service/breadcrumbsservice.js
=======================================--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgiconcontroller.js Tue Feb 18 00:02:17 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgiconcontroller.js Tue Feb 18 00:17:42 2014 UTC
@@ -58,7 +58,10 @@ function refreshBreadcrumbItems() { $scope.breadcrumbItems = [- breadcrumbs.createViewPkg($scope.pkg,'latest',$location.search()['arch']),
+ breadcrumbs.createViewPkg( + $scope.pkg, + $routeParams.version, + $routeParams.architectureCode), breadcrumbs.createEditPkgIcon($scope.pkg) ]; } @@ -110,15 +113,7 @@ function() { $scope.amSaving = false;$log.info('have set the 32px icon for the pkg '+$scope.pkg.name);
- var arch = $location.search()['arch']; - - if(arch) {- $location.path('/viewpkg/'+$scope.pkg.name+'/latest/'+arch).search({});
- } - else {- $log.info('unable to navigate back to the pkg as no \'arch\' was supplied in the search parameters --> will go home');
- $location.path('/').search({}); - }+ $location.path('/viewpkg/'+$routeParams.name+'/'+$routeParams.version+'/'+$routeParams.architectureCode).search({});
}, function(e) { $scope.amSaving = false; =======================================--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgscreenshotscontroller.js Tue Feb 18 00:02:17 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgscreenshotscontroller.js Tue Feb 18 00:17:42 2014 UTC
@@ -93,7 +93,10 @@ function refreshBreadcrumbItems() { $scope.breadcrumbItems = [- breadcrumbs.createViewPkg($scope.pkg,'latest',$location.search()['arch']),
+ breadcrumbs.createViewPkg( + $scope.pkg, + $routeParams.version, + $routeParams.architectureCode), breadcrumbs.createEditPkgScreenshots($scope.pkg) ]; } =======================================--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/viewpkgcontroller.js Tue Feb 18 00:02:17 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/viewpkgcontroller.js Tue Feb 18 00:17:42 2014 UTC
@@ -147,11 +147,11 @@ // ACTIONS FOR PACKAGE $scope.goEditIcon = function() {- $location.path("/editpkgicon/"+$scope.pkg.name).search({'arch':$routeParams.architectureCode});
+ $location.path($location.path() + '/editicon').search({}); } $scope.goEditScreenshots = function() {- $location.path("/editpkgscreenshots/"+$scope.pkg.name).search({'arch':$routeParams.architectureCode}); + $location.path($location.path() + '/editscreenshots').search({});
} $scope.goRemoveIcon = function() { =======================================--- /haikudepotserver-webapp/src/main/webapp/js/app/routes.js Tue Feb 18 00:02:17 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/routes.js Tue Feb 18 00:17:42 2014 UTC
@@ -39,8 +39,8 @@.when('/changepassword/:nickname',{controller:'ChangePasswordController', templateUrl:'/js/app/controller/changepassword.html'}) .when('/viewuser/:nickname',{controller:'ViewUserController', templateUrl:'/js/app/controller/viewuser.html'}) .when('/viewpkg/:name/:version/:architectureCode',{controller:'ViewPkgController', templateUrl:'/js/app/controller/viewpkg.html'}) - .when('/editpkgicon/:name',{controller:'EditPkgIconController', templateUrl:'/js/app/controller/editpkgicon.html'}) - .when('/editpkgscreenshots/:name',{controller:'EditPkgScreenshotsController', templateUrl:'/js/app/controller/editpkgscreenshots.html'}) + .when('/viewpkg/:name/:version/:architectureCode/editicon',{controller:'EditPkgIconController', templateUrl:'/js/app/controller/editpkgicon.html'}) + .when('/viewpkg/:name/:version/:architectureCode/editscreenshots',{controller:'EditPkgScreenshotsController', templateUrl:'/js/app/controller/editpkgscreenshots.html'}) .when('/error',{controller:'ErrorController', templateUrl:'/js/app/controller/error.html'})
.when('/',{ controller:'HomeController', =======================================--- /haikudepotserver-webapp/src/main/webapp/js/app/service/breadcrumbsservice.js Tue Feb 18 00:02:17 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/service/breadcrumbsservice.js Tue Feb 18 00:17:42 2014 UTC
@@ -56,6 +56,19 @@ },createViewPkg : function(pkg,versionType,architectureCode) {
+ + if(!pkg||!pkg.name||!pkg.name.length) {+ throw 'the package must be supplied to create a view pkg and must have a name';
+ } + + if(!versionType||!versionType.length) {+ throw 'the versionType must be supplied to create a view pkg';
+ } + + if(!architectureCode||!architectureCode.length) {+ throw 'the architectureCode must be supplied to create a view pkg';
+ } + return { title : pkg.name,path : '/viewpkg/' + pkg.name + '/' + versionType + '/' + architectureCode
============================================================================== Revision: 2ba6b572106c Author: Andrew Lindesay <apl@xxxxxxxxxxxxxx> Date: Tue Feb 18 09:54:04 2014 UTC Log: + additional data for screenshots+ avoid the possibility of a rogue client uploading too much data for an icon or screenshot
http://code.google.com/p/haiku-depot-web-app/source/detail?r=2ba6b572106c Added:/haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgScreenshotRequest.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgScreenshotResult.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/model/SizeLimitReachedException.java /haikudepotserver-webapp/src/main/resources/db/haikudepot/migration/V1.2__ScreenshotAdditions.sql
/haikudepotserver-webapp/src/main/webapp/js/app/filter/datalengthfilter.js Modified:/haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/PkgApi.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgScreenshotsResult.java /haikudepotserver-packagefile/src/main/java/org/haikuos/pkg/HpkStringTable.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/PkgApiImpl.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/PkgScreenshot.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/auto/_Pkg.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/auto/_PkgScreenshot.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/PkgService.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/controller/PkgIconController.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/controller/PkgScreenshotController.java
/haikudepotserver-webapp/src/main/resources/HaikuDepot.map.xml /haikudepotserver-webapp/src/main/resources/spring/webresourcegroup.xml/haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgiconcontroller.js /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgscreenshots.html /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgscreenshotscontroller.js
======================================= --- /dev/null+++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgScreenshotRequest.java Tue Feb 18 09:54:04 2014 UTC
@@ -0,0 +1,12 @@ +/* + * Copyright 2014, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +package org.haikuos.haikudepotserver.api1.model.pkg; + +public class GetPkgScreenshotRequest { + + public String code; + +} ======================================= --- /dev/null+++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgScreenshotResult.java Tue Feb 18 09:54:04 2014 UTC
@@ -0,0 +1,15 @@ +/* + * Copyright 2014, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +package org.haikuos.haikudepotserver.api1.model.pkg; + +public class GetPkgScreenshotResult { + + public String code; + public Integer length; + public Integer height; + public Integer width; + +} ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/model/SizeLimitReachedException.java Tue Feb 18 09:54:04 2014 UTC
@@ -0,0 +1,19 @@ +/* + * Copyright 2014, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +package org.haikuos.haikudepotserver.pkg.model; + +import java.io.IOException; + +/**+ * <p>Thrown when more bytes are read from an input than were anticipated.</p>
+ */ + +public class SizeLimitReachedException extends IOException { + + public SizeLimitReachedException() { + } + +} ======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/resources/db/haikudepot/migration/V1.2__ScreenshotAdditions.sql Tue Feb 18 09:54:04 2014 UTC
@@ -0,0 +1,9 @@ +-- ------------------------------------------------------ +-- ADDITIONAL MATERIAL FOR SCREENSHOTS +-- ------------------------------------------------------ + +ALTER TABLE haikudepot.pkg_screenshot ADD COLUMN length INTEGER NOT NULL; +ALTER TABLE haikudepot.pkg_screenshot ADD COLUMN width INTEGER NOT NULL; +ALTER TABLE haikudepot.pkg_screenshot ADD COLUMN height INTEGER NOT NULL;+ALTER TABLE haikudepot.pkg_screenshot ADD COLUMN create_timestamp TIMESTAMP NOT NULL; +ALTER TABLE haikudepot.pkg_screenshot ADD COLUMN modify_timestamp TIMESTAMP NOT NULL;
======================================= --- /dev/null+++ /haikudepotserver-webapp/src/main/webapp/js/app/filter/datalengthfilter.js Tue Feb 18 09:54:04 2014 UTC
@@ -0,0 +1,35 @@ +/* + * Copyright 2014, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +/**+ * <p>This filter will render a human readable string for a data size. It expects a value in bytes and it will
+ * decide if it makes sense to render in MB or GB or...</p> + */ + +angular.module('haikudepotserver').filter('dataLength', function() { + return function(input) { + + if(undefined==input||null==input) { + return ''; + } + + var val = !_.isNumber(input) ? input : parseInt(''+input,10); + + if(val < 1024) { + return val + ' bytes'; + } + + if(val < 1024*1024) { + return (val/1024).toFixed(1) + ' KB'; + } + + if(val < 1024*1024*1024) { + return (val/(1024*1024)).toFixed(1) + ' MB'; + } + + return (val/(1024*1024*1024)).toFixed(1) + ' GB'; + } + } +); =======================================--- /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/PkgApi.java Wed Feb 12 10:07:08 2014 UTC +++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/PkgApi.java Tue Feb 18 09:54:04 2014 UTC
@@ -36,6 +36,12 @@RemovePkgIconResult removePkgIcon(RemovePkgIconRequest request) throws ObjectNotFoundException;
+ /** + * <p>This method will get the details of a screenshot image.</p> + */ ++ GetPkgScreenshotResult getPkgScreenshot(GetPkgScreenshotRequest request) throws ObjectNotFoundException;
+ /*** <p>This method will return an ordered list of the screenshots that are available for this package. It will * throw an {@link org.haikuos.haikudepotserver.api1.support.ObjectNotFoundException} in the case where the
=======================================--- /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgScreenshotsResult.java Wed Feb 12 10:07:08 2014 UTC +++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgScreenshotsResult.java Tue Feb 18 09:54:04 2014 UTC
@@ -13,6 +13,9 @@ public static class PkgScreenshot { public String code; + public Integer length; + public Integer height; + public Integer width; } } =======================================--- /haikudepotserver-packagefile/src/main/java/org/haikuos/pkg/HpkStringTable.java Tue Feb 18 00:02:17 2014 UTC +++ /haikudepotserver-packagefile/src/main/java/org/haikuos/pkg/HpkStringTable.java Tue Feb 18 09:54:04 2014 UTC
@@ -58,9 +58,6 @@ new HeapCoordinates( heapOffset, heapLength)); - -// try { Files.write(stringsDataBuffer, new File("/tmp/dat")); } -// catch(IOException ioe) {} // now work through the data and load them into the strings. =======================================--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/PkgApiImpl.java Tue Feb 18 00:02:17 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/PkgApiImpl.java Tue Feb 18 09:54:04 2014 UTC
@@ -280,6 +280,26 @@ return new RemovePkgIconResult(); } + + @Override+ public GetPkgScreenshotResult getPkgScreenshot(GetPkgScreenshotRequest request) throws ObjectNotFoundException {
+ Preconditions.checkNotNull(request); + Preconditions.checkNotNull(request.code); + + final ObjectContext context = serverRuntime.getContext();+ Optional<PkgScreenshot> pkgScreenshotOptional = PkgScreenshot.getByCode(context, request.code);
+ + if(!pkgScreenshotOptional.isPresent()) {+ throw new ObjectNotFoundException(PkgScreenshot.class.getSimpleName(), request.code);
+ } + + GetPkgScreenshotResult result = new GetPkgScreenshotResult(); + result.code = pkgScreenshotOptional.get().getCode(); + result.height = pkgScreenshotOptional.get().getHeight(); + result.width = pkgScreenshotOptional.get().getWidth(); + result.length = pkgScreenshotOptional.get().getLength(); + return result; + } @Overridepublic GetPkgScreenshotsResult getPkgScreenshots(GetPkgScreenshotsRequest getPkgScreenshotsRequest) throws ObjectNotFoundException {
@@ -301,6 +321,9 @@public GetPkgScreenshotsResult.PkgScreenshot apply(PkgScreenshot pkgScreenshot) { GetPkgScreenshotsResult.PkgScreenshot rs = new GetPkgScreenshotsResult.PkgScreenshot();
rs.code = pkgScreenshot.getCode(); + rs.height = pkgScreenshot.getHeight(); + rs.width = pkgScreenshot.getWidth(); + rs.length = pkgScreenshot.getLength(); return rs; } } =======================================--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/PkgScreenshot.java Wed Feb 12 10:07:08 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/PkgScreenshot.java Tue Feb 18 09:54:04 2014 UTC
@@ -12,11 +12,14 @@ import org.apache.cayenne.ObjectContext; import org.apache.cayenne.exp.ExpressionFactory; import org.apache.cayenne.query.SelectQuery; +import org.apache.cayenne.validation.BeanValidationFailure; +import org.apache.cayenne.validation.ValidationResult; import org.haikuos.haikudepotserver.dataobjects.auto._PkgScreenshot;+import org.haikuos.haikudepotserver.dataobjects.support.CreateAndModifyTimestamped;
import java.util.List;-public class PkgScreenshot extends _PkgScreenshot implements Comparable<PkgScreenshot> { +public class PkgScreenshot extends _PkgScreenshot implements Comparable<PkgScreenshot>, CreateAndModifyTimestamped {
public static Optional<PkgScreenshot> getByCode(ObjectContext context, String code) {
Preconditions.checkNotNull(context); @@ -48,4 +51,22 @@ public int compareTo(PkgScreenshot o) { return getOrdering().compareTo(o.getOrdering()); } + + @Override + protected void validateForSave(ValidationResult validationResult) { + super.validateForSave(validationResult); + + if(getHeight() <= 0) {+ validationResult.addFailure(new BeanValidationFailure(this,HEIGHT_PROPERTY,"range"));
+ } + + if(getWidth() <= 0) {+ validationResult.addFailure(new BeanValidationFailure(this,WIDTH_PROPERTY,"range"));
+ } + + if(getLength() <= 0) {+ validationResult.addFailure(new BeanValidationFailure(this,LENGTH_PROPERTY,"range"));
+ } + } + } =======================================--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/auto/_Pkg.java Wed Dec 11 08:25:33 2013 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/auto/_Pkg.java Tue Feb 18 09:54:04 2014 UTC
@@ -60,7 +60,6 @@ public void removeFromPkgIcons(PkgIcon obj) { removeToManyTarget(PKG_ICONS_PROPERTY, obj, true); } - @SuppressWarnings("unchecked") public List<PkgIcon> getPkgIcons() { return (List<PkgIcon>)readProperty(PKG_ICONS_PROPERTY); =======================================--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/auto/_PkgScreenshot.java Wed Dec 11 08:25:33 2013 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/auto/_PkgScreenshot.java Tue Feb 18 09:54:04 2014 UTC
@@ -1,5 +1,6 @@ package org.haikuos.haikudepotserver.dataobjects.auto; +import java.util.Date; import java.util.List; import org.haikuos.haikudepotserver.dataobjects.Pkg; @@ -15,7 +16,12 @@ public abstract class _PkgScreenshot extends AbstractDataObject { public static final String CODE_PROPERTY = "code";+ public static final String CREATE_TIMESTAMP_PROPERTY = "createTimestamp";
+ public static final String HEIGHT_PROPERTY = "height"; + public static final String LENGTH_PROPERTY = "length";+ public static final String MODIFY_TIMESTAMP_PROPERTY = "modifyTimestamp";
public static final String ORDERING_PROPERTY = "ordering"; + public static final String WIDTH_PROPERTY = "width"; public static final String PKG_PROPERTY = "pkg";public static final String PKG_SCREENSHOT_IMAGES_PROPERTY = "pkgScreenshotImages";
@@ -27,6 +33,34 @@ public String getCode() { return (String)readProperty(CODE_PROPERTY); } + + public void setCreateTimestamp(Date createTimestamp) { + writeProperty(CREATE_TIMESTAMP_PROPERTY, createTimestamp); + } + public Date getCreateTimestamp() { + return (Date)readProperty(CREATE_TIMESTAMP_PROPERTY); + } + + public void setHeight(Integer height) { + writeProperty(HEIGHT_PROPERTY, height); + } + public Integer getHeight() { + return (Integer)readProperty(HEIGHT_PROPERTY); + } + + public void setLength(Integer length) { + writeProperty(LENGTH_PROPERTY, length); + } + public Integer getLength() { + return (Integer)readProperty(LENGTH_PROPERTY); + } + + public void setModifyTimestamp(Date modifyTimestamp) { + writeProperty(MODIFY_TIMESTAMP_PROPERTY, modifyTimestamp); + } + public Date getModifyTimestamp() { + return (Date)readProperty(MODIFY_TIMESTAMP_PROPERTY); + } public void setOrdering(Integer ordering) { writeProperty(ORDERING_PROPERTY, ordering); @@ -34,6 +68,13 @@ public Integer getOrdering() { return (Integer)readProperty(ORDERING_PROPERTY); } + + public void setWidth(Integer width) { + writeProperty(WIDTH_PROPERTY, width); + } + public Integer getWidth() { + return (Integer)readProperty(WIDTH_PROPERTY); + } public void setPkg(Pkg pkg) { setToOneTarget(PKG_PROPERTY, pkg, true); =======================================--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/PkgService.java Tue Feb 18 00:02:17 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/PkgService.java Tue Feb 18 09:54:04 2014 UTC
@@ -23,6 +23,7 @@ import org.haikuos.haikudepotserver.pkg.model.BadPkgIconException; import org.haikuos.haikudepotserver.pkg.model.BadPkgScreenshotException; import org.haikuos.haikudepotserver.pkg.model.PkgSearchSpecification; +import org.haikuos.haikudepotserver.pkg.model.SizeLimitReachedException; import org.haikuos.haikudepotserver.support.Closeables; import org.haikuos.haikudepotserver.support.ImageHelper; import org.haikuos.haikudepotserver.support.cayenne.LikeHelper; @@ -50,7 +51,41 @@ protected static int SCREENSHOT_SIDE_LIMIT = 1500;+ // these seem like reasonable limits for the size of image data to have to
+ // handle in-memory. + + protected static int SCREENSHOT_SIZE_LIMIT = 2 * 1024 * 1024; // 2MB + protected static int ICON_SIZE_LIMIT = 100 * 1024; // 100k + private ImageHelper imageHelper = new ImageHelper(); + + // ------------------------------ + // HELP + + /**+ * <p>This method will read in the quantity of bytes from the input stream upto the limit. If the limit is + * reached, the method will throw {@link org.haikuos.haikudepotserver.pkg.model.SizeLimitReachedException}.</p>
+ */ ++ public static byte[] toByteArray(InputStream inputStream, int sizeLimit) throws IOException {
+ Preconditions.checkNotNull(inputStream); + Preconditions.checkState(sizeLimit > 0); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buffer = new byte[8*1024]; + int read; + + while(-1 != (read = inputStream.read(buffer,0,buffer.length))) { + + if(read + baos.size() > sizeLimit) { + throw new SizeLimitReachedException(); + } + + baos.write(buffer,0,read); + } + + return baos.toByteArray(); + } // ------------------------------ // SEARCH @@ -245,7 +280,7 @@ Preconditions.checkNotNull(pkg); Preconditions.checkState(16==expectedSize||32==expectedSize); - byte[] pngData = ByteStreams.toByteArray(input); + byte[] pngData = toByteArray(input, ICON_SIZE_LIMIT); ImageHelper.Size size = imageHelper.derivePngSize(pngData); if(null==size) { @@ -349,7 +384,7 @@ Preconditions.checkNotNull(context); Preconditions.checkNotNull(pkg); - byte[] pngData = ByteStreams.toByteArray(input); + byte[] pngData = toByteArray(input, SCREENSHOT_SIZE_LIMIT); ImageHelper.Size size = imageHelper.derivePngSize(pngData); if(null==size) { @@ -380,6 +415,9 @@ PkgScreenshot screenshot = context.newObject(PkgScreenshot.class); screenshot.setCode(UUID.randomUUID().toString()); screenshot.setOrdering(new Integer(ordering)); + screenshot.setHeight(size.height); + screenshot.setWidth(size.width); + screenshot.setLength(pngData.length);pkg.addToManyTarget(Pkg.PKG_SCREENSHOTS_PROPERTY, screenshot, true);
PkgScreenshotImage screenshotImage = context.newObject(PkgScreenshotImage.class);
=======================================--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/controller/PkgIconController.java Wed Feb 12 10:07:08 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/controller/PkgIconController.java Tue Feb 18 09:54:04 2014 UTC
@@ -11,6 +11,7 @@ import com.google.common.net.MediaType; import org.apache.cayenne.ObjectContext; import org.apache.cayenne.configuration.server.ServerRuntime; +import org.haikuos.haikudepotserver.pkg.model.SizeLimitReachedException; import org.haikuos.haikudepotserver.security.model.Permission; import org.haikuos.haikudepotserver.security.AuthorizationService; import org.haikuos.haikudepotserver.support.ByteCounterOutputStream; @@ -192,6 +193,10 @@ context, pkg.get()); } + catch(SizeLimitReachedException sizeLimit) {+ logger.warn("attempt to load in an icon file that is larger than that allowed");
+ throw new MissingOrBadFormat(); + } catch(BadPkgIconException badIcon) { throw new MissingOrBadFormat(); } =======================================--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/controller/PkgScreenshotController.java Tue Feb 18 00:02:17 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/controller/PkgScreenshotController.java Tue Feb 18 09:54:04 2014 UTC
@@ -16,6 +16,7 @@ import org.haikuos.haikudepotserver.dataobjects.User; import org.haikuos.haikudepotserver.pkg.PkgService; import org.haikuos.haikudepotserver.pkg.model.BadPkgScreenshotException; +import org.haikuos.haikudepotserver.pkg.model.SizeLimitReachedException; import org.haikuos.haikudepotserver.security.AuthorizationService; import org.haikuos.haikudepotserver.security.model.Permission; import org.haikuos.haikudepotserver.support.ByteCounterOutputStream; @@ -62,8 +63,8 @@ public void fetchHead( HttpServletRequest request, HttpServletResponse response,- @RequestParam(value = KEY_TARGETWIDTH, required=true) Integer targetWidth, - @RequestParam(value = KEY_TARGETHEIGHT, required=true) Integer targetHeight,
+ @RequestParam(value = KEY_TARGETWIDTH) Integer targetWidth, + @RequestParam(value = KEY_TARGETHEIGHT) Integer targetHeight, @PathVariable(value = KEY_FORMAT) String format,@PathVariable(value = KEY_SCREENSHOTCODE) String screenshotCode)
throws IOException { @@ -255,6 +256,10 @@ context, pkg.get()).getCode(); } + catch(SizeLimitReachedException sizeLimit) {+ logger.warn("attempt to load in a screenshot larger than the size limit");
+ throw new MissingOrBadFormat(); + } catch(BadPkgScreenshotException badIcon) { throw new MissingOrBadFormat(); } =======================================--- /haikudepotserver-webapp/src/main/resources/HaikuDepot.map.xml Wed Feb 12 10:07:08 2014 UTC +++ /haikudepotserver-webapp/src/main/resources/HaikuDepot.map.xml Tue Feb 18 09:54:04 2014 UTC
@@ -61,9 +61,14 @@ </db-entity> <db-entity name="pkg_screenshot" schema="haikudepot"> <db-attribute name="code" type="CHAR" isMandatory="true" length="36"/>+ <db-attribute name="create_timestamp" type="TIMESTAMP" isMandatory="true"/>
+ <db-attribute name="height" type="INTEGER" isMandatory="true"/><db-attribute name="id" type="BIGINT" isPrimaryKey="true" isMandatory="true"/>
+ <db-attribute name="length" type="INTEGER" isMandatory="true"/>+ <db-attribute name="modify_timestamp" type="TIMESTAMP" isMandatory="true"/>
<db-attribute name="ordering" type="INTEGER" isMandatory="true"/> <db-attribute name="pkg_id" type="BIGINT" isMandatory="true"/> + <db-attribute name="width" type="INTEGER" isMandatory="true"/> <db-key-generator> <db-generator-type>ORACLE</db-generator-type> <db-generator-name>haikudepot.pkg_screenshot_seq</db-generator-name> @@ -218,7 +223,16 @@ </obj-entity><obj-entity name="PkgScreenshot" className="org.haikuos.haikudepotserver.dataobjects.PkgScreenshot" lock-type="optimistic" dbEntityName="pkg_screenshot" superClassName="org.haikuos.haikudepotserver.dataobjects.support.AbstractDataObject"> <obj-attribute name="code" type="java.lang.String" db-attribute-path="code"/> + <obj-attribute name="createTimestamp" type="java.util.Date" db-attribute-path="create_timestamp"/> + <obj-attribute name="height" type="java.lang.Integer" db-attribute-path="height"/> + <obj-attribute name="length" type="java.lang.Integer" db-attribute-path="length"/> + <obj-attribute name="modifyTimestamp" type="java.util.Date" db-attribute-path="modify_timestamp"/> <obj-attribute name="ordering" type="java.lang.Integer" db-attribute-path="ordering"/> + <obj-attribute name="width" type="java.lang.Integer" db-attribute-path="width"/> + <entity-listener class="org.haikuos.haikudepotserver.support.cayenne.PostAddCreateAndModifyTimestampListener">
+ <post-add method-name="onPostAdd"/> + <pre-update method-name="onPreUpdate"/> + </entity-listener> </obj-entity><obj-entity name="PkgScreenshotImage" className="org.haikuos.haikudepotserver.dataobjects.PkgScreenshotImage" dbEntityName="pkg_screenshot_image" superClassName="org.haikuos.haikudepotserver.dataobjects.support.AbstractDataObject">
<obj-attribute name="data" type="byte[]" db-attribute-path="data"/> =======================================--- /haikudepotserver-webapp/src/main/resources/spring/webresourcegroup.xml Tue Feb 18 00:02:17 2014 UTC +++ /haikudepotserver-webapp/src/main/resources/spring/webresourcegroup.xml Tue Feb 18 09:54:04 2014 UTC
@@ -89,6 +89,7 @@<value>/js/app/service/errorhandlingservice.js</value>
<value>/js/app/filter/timestampfilter.js</value> + <value>/js/app/filter/datalengthfilter.js</value>
</list> </property> =======================================--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgiconcontroller.js Tue Feb 18 00:17:42 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgiconcontroller.js Tue Feb 18 09:54:04 2014 UTC
@@ -14,6 +14,8 @@ jsonRpc,constants,pkgIcon,errorHandling, breadcrumbs) { + var ICON_SIZE_LIMIT = 100 * 1024; // 100k + $scope.breadcrumbItems = undefined; $scope.pkg = undefined; $scope.amSaving = false; @@ -74,7 +76,7 @@ function validateIconFile(file, model) { model.$setValidity('badformatorsize',true);- model.$setValidity('badsize',undefined==file || (file.size
+ model.$setValidity('badsize',undefined==file || (file.size24 && file.size < 32*1024));
24 && file.size < ICON_SIZE_LIMIT));
} function icon32FileDidChange() { =======================================--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgscreenshots.html Tue Feb 18 00:02:17 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgscreenshots.html Tue Feb 18 09:54:04 2014 UTC
@@ -21,7 +21,7 @@ <div ng-repeat="pkgScreenshot in pkgScreenshots"> <img ng-src="{{pkgScreenshot.imageThumbnailUrl}}"></img> <div class="pkg-screenshot-controls"> - <div><strong>#{{$index+1}}</strong></div>+ <div><strong>#{{$index+1}}</strong> - ({{pkgScreenshot.width}} x {{pkgScreenshot.height}}), {{pkgScreenshot.length|dataLength}}</div>
<ul><li><a href="" ng-click="goRemovePkgScreenshot(pkgScreenshot)">Remove</a></li>
=======================================--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgscreenshotscontroller.js Tue Feb 18 00:17:42 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgscreenshotscontroller.js Tue Feb 18 09:54:04 2014 UTC
@@ -17,7 +17,7 @@// the upload size must be less than this or it is too big for the
// far end to process. - var FILESIZEMAX = 2 * 1024 * 1024; + var SCREENSHOT_SIZE_LIMIT = 2 * 1024 * 1024; // 2MB var THUMBNAIL_TARGETWIDTH = 180; var THUMBNAIL_TARGETHEIGHT = 90; @@ -51,6 +51,9 @@$scope.pkgScreenshots = _.map(result.items, function(item) {
return { code : item.code, + width : item.width, + height : item.height, + length : item.length,imageThumbnailUrl : pkgScreenshot.url($scope.pkg,item.code,THUMBNAIL_TARGETWIDTH,THUMBNAIL_TARGETHEIGHT), imageDownloadUrl : pkgScreenshot.rawUrl($scope.pkg,item.code)
}; @@ -114,7 +117,7 @@ var file = $scope.addPkgScreenshot.file; var model = $scope.addPkgScreenshotForm['file'] model.$setValidity('badformatorsize',true);- model.$setValidity('badsize',undefined==file || (file.size
+ model.$setValidity('badsize',undefined==file || (file.size24 && file.size < FILESIZEMAX));
24 && file.size < SCREENSHOT_SIZE_LIMIT));
});// This function will take the data from the form and will create the user from this data.
@@ -134,11 +137,28 @@$log.info('have added a screenshot for the pkg '+$scope.pkg.name);
$scope.addPkgScreenshot.file = undefined; $scope.addPkgScreenshotForm.$setPristine(); - $scope.pkgScreenshots.push({ - code : code,- imageThumbnailUrl : pkgScreenshot.url($scope.pkg,code,THUMBNAIL_TARGETWIDTH,THUMBNAIL_TARGETHEIGHT), - imageDownloadUrl : pkgScreenshot.rawUrl($scope.pkg,code)
- }) + + jsonRpc.call( + constants.ENDPOINT_API_V1_PKG, + "getPkgScreenshot", + [{ code : code }] + ).then( + function(result) { + $scope.pkgScreenshots.push({ + code : code, + height : result.height, + width : result.width, + length : result.length,+ imageThumbnailUrl : pkgScreenshot.url($scope.pkg,code,THUMBNAIL_TARGETWIDTH,THUMBNAIL_TARGETHEIGHT), + imageDownloadUrl : pkgScreenshot.rawUrl($scope.pkg,code)
+ }); + + $scope.amCommunicating = false; + }, + function(err) { + errorHandling.handleJsonRpcError(err); + } + ); }, function(e) {if(e==pkgScreenshot.errorCodes.BADFORMATORSIZEERROR) {
@@ -148,13 +168,11 @@$log.error('unable to add the screenshot for; '+$scope.pkg.name);
$location.path('/error').search({}); } - } - )['finally']( - function() { + $scope.amCommunicating = false; } - ) + ); } // ------------------------- ============================================================================== Revision: 99da10a910a9 Author: Andrew Lindesay <apl@xxxxxxxxxxxxxx> Date: Tue Feb 18 10:09:54 2014 UTC Log: + documentation update + additional integration test coverage http://code.google.com/p/haiku-depot-web-app/source/detail?r=99da10a910a9 Modified:/haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgScreenshotRequest.java
/haikudepotserver-docs/src/main/latex/docs/part-api.tex/haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/PkgApiIT.java
=======================================--- /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgScreenshotRequest.java Tue Feb 18 09:54:04 2014 UTC +++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/pkg/GetPkgScreenshotRequest.java Tue Feb 18 10:09:54 2014 UTC
@@ -9,4 +9,11 @@ public String code; + public GetPkgScreenshotRequest() { + } + + public GetPkgScreenshotRequest(String code) { + this.code = code; + } + } =======================================--- /haikudepotserver-docs/src/main/latex/docs/part-api.tex Wed Feb 12 10:07:08 2014 UTC +++ /haikudepotserver-docs/src/main/latex/docs/part-api.tex Tue Feb 18 10:09:54 2014 UTC
@@ -216,8 +216,8 @@ \end{itemize} \item Expected HTTP Status Codes \begin{itemize} - \item {\bf 200} : The icon is provided in the response (for GET)- \item {\bf 415} : The path did not include ".png" or the target width or height is invalid
+ \item {\bf 200} : The image data is provided in the response (for GET)+ \item {\bf 415} : The path did not include ".png" or the target width or height is invalid or the length of the data is too large
\item {\bf 400} : The screenshot code was not supplied \item {\bf 404} : The screenshot was not found \end{itemize} @@ -226,6 +226,25 @@ An example URL is;\framebox{\tt http://localhost:8080/pkgscreenshot/a78hw20fh2p20fh122jd92.png?tw=640\&th=480}
+ +\subsubsection{Get Raw Screenshot Image} + +This API is able to provide the {\it raw} screenshot data. + +\begin{itemize} +\item HTTP Method : GET +\item Path : /pkgscreenshot/raw/$<$screenshotcode$>$ +\item Response Content-Type : {\it As per the stored data} +\item Expected HTTP Status Codes + \begin{itemize} + \item {\bf 200} : The image data is provided in the response (for GET) + \item {\bf 404} : The screenshot was not found + \end{itemize} +\end{itemize} + +An example URL is; ++\framebox{\tt http://localhost:8080/pkgscreenshot/raw/a78hw20fh2p20fh122jd92}
\subsubsection{Put Screenshot Image} @@ -237,7 +256,7 @@ \item Expected HTTP Status Codes \begin{itemize} \item {\bf 200} : The screenshot image was stored- \item {\bf 415} : The path did not include ".png" or the size of the image is invalid or the payload is not PNG image data. + \item {\bf 415} : The path did not include ".png" or the size (pixels or data) of the image is invalid or the payload is not PNG image data. \item {\bf 404} : The package identified in the path was not able to be found
\item {\bf 400} : The package name was not supplied \end{itemize} =======================================--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/PkgApiIT.java Fri Feb 14 08:42:33 2014 UTC +++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotsever/api1/PkgApiIT.java Tue Feb 18 10:09:54 2014 UTC
@@ -154,8 +154,30 @@ PkgScreenshot pkgScreenshot = sortedScreenshots.get(i);GetPkgScreenshotsResult.PkgScreenshot apiPkgScreenshot = result.items.get(i); Assertions.assertThat(pkgScreenshot.getCode()).isEqualTo(apiPkgScreenshot.code);
+ Assertions.assertThat(pkgScreenshot.getWidth()).isEqualTo(320);+ Assertions.assertThat(pkgScreenshot.getHeight()).isEqualTo(240); + Assertions.assertThat(pkgScreenshot.getLength()).isEqualTo(41296);
} } + + /**+ * <p>This test depends on the sample package pkg1 having some screenshots associated with it.</p>
+ */ + + @Test + public void testGetPkgScreenshot() throws ObjectNotFoundException {+ IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
+ String code = data.pkg1.getSortedPkgScreenshots().get(0).getCode(); + + // ------------------------------------+ GetPkgScreenshotResult result = pkgApi.getPkgScreenshot(new GetPkgScreenshotRequest(code));
+ // ------------------------------------ + + Assertions.assertThat(result.code).isEqualTo(code); + Assertions.assertThat(result.width).isEqualTo(320); + Assertions.assertThat(result.height).isEqualTo(240); + Assertions.assertThat(result.length).isEqualTo(41296); + } /*** <p>This test depends on the sample package pkg1 having some screenshots associated with it.</p>
============================================================================== Revision: 4ee4560ddc66 Author: Andrew Lindesay <apl@xxxxxxxxxxxxxx> Date: Tue Feb 18 10:16:48 2014 UTC Log: + cache control on icons and screenshots http://code.google.com/p/haiku-depot-web-app/source/detail?r=4ee4560ddc66 Modified:/haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/controller/PkgIconController.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/controller/PkgScreenshotController.java
=======================================--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/controller/PkgIconController.java Tue Feb 18 09:54:04 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/controller/PkgIconController.java Tue Feb 18 10:16:48 2014 UTC
@@ -140,6 +140,7 @@ } response.setContentType(MediaType.PNG.toString()); + response.setHeader(HttpHeaders.CACHE_CONTROL, "max-age=3600");response.setDateHeader(HttpHeaders.LAST_MODIFIED, pkg.get().getModifyTimestampSecondAccuracy().getTime());
pkgService.writePkgIconImage( =======================================--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/controller/PkgScreenshotController.java Tue Feb 18 09:54:04 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/controller/PkgScreenshotController.java Tue Feb 18 10:16:48 2014 UTC
@@ -151,6 +151,7 @@ } response.setContentType(MediaType.PNG.toString()); + response.setHeader(HttpHeaders.CACHE_CONTROL, "max-age=3600"); response.setDateHeader( HttpHeaders.LAST_MODIFIED,screenshotOptional.get().getPkg().getModifyTimestampSecondAccuracy().getTime());