{"action":"created","comment":{"url":"https://api.github.com/repos/owncloud/core/pulls/comments/31839112","id":31839112,"diff_hunk":"@@ -355,93 +410,243 @@ public function deleteAllPreviews() {\n \t}\n \n \t/**\n-\t * Checks if thumbnail or bigger version of thumbnail of file is already cached\n+\t * Checks if a preview matching the asked dimensions or a bigger version is already cached\n \t *\n-\t * @param int $fileId fileId of the original image\n-\t * @return string|false path to thumbnail if it exists or false\n+\t *    * We first retrieve the size of the max preview since this is what we be used to create\n+\t * all our preview. If it doesn't exist we return false, so that it can be generated\n+\t *    * Using the dimensions of the max preview, we calculate what the size of the new\n+\t * thumbnail should be\n+\t *    * And finally, we look for a suitable candidate in the cache\n+\t *\n+\t * @param int $fileId fileId of the original file we need a preview of\n+\t *\n+\t * @return string|false path to the cached preview if it exists or false\n \t */\n \tpublic function isCached($fileId) {\n \t\tif (is_null($fileId)) {\n \t\t\treturn false;\n \t\t}\n \n-\t\t// This gives us a calculated path to a preview of asked dimensions\n-\t\t// thumbnailFolder/fileId/my_image-<maxX>-<maxY>.png\n-\t\t$preview = $this->buildCachePath($fileId);\n+\t\t/**\n+\t\t * Phase 1: Looking for the max preview\n+\t\t */\n+\t\t$previewPath = $this->getPreviewPath($fileId);\n+\t\t// We currently can't look for a single file due to bugs related to #16478\n+\t\t$allThumbnails = $this->userView->getDirectoryContent($previewPath);\n+\t\tlist($maxPreviewWidth, $maxPreviewHeight) = $this->getMaxPreviewSize($allThumbnails);\n+\n+\t\t// Only use the cache if we have a max preview\n+\t\tif (!is_null($maxPreviewWidth) && !is_null($maxPreviewHeight)) {\n+\n+\t\t\t/**\n+\t\t\t * Phase 2: Calculating the size of the preview we need to send back\n+\t\t\t */\n+\t\t\t$this->maxPreviewWidth = $maxPreviewWidth;\n+\t\t\t$this->maxPreviewHeight = $maxPreviewHeight;\n+\n+\t\t\tlist($previewWidth, $previewHeight) = $this->simulatePreviewDimensions();\n+\t\t\tif (empty($previewWidth) || empty($previewHeight)) {\n+\t\t\t\treturn false;\n+\t\t\t}\n+\n+\t\t\t$this->previewWidth = $previewWidth;\n+\t\t\t$this->previewHeight = $previewHeight;\n+\n+\t\t\t/**\n+\t\t\t * Phase 3: We look for a preview of the exact size\n+\t\t\t */\n+\t\t\t// This gives us a calculated path to a preview of asked dimensions\n+\t\t\t// thumbnailFolder/fileId/<maxX>-<maxY>(-max|-with-aspect).png\n+\t\t\t$preview = $this->buildCachePath($fileId, $previewWidth, $previewHeight);\n+\n+\t\t\t// FIXME DEBUG CODE\n+\t\t\t\\OCP\\Util::writeLog(\n+\t\t\t\t'core', 'Preview to find in the cache ' . $preview, \\OCP\\Util::DEBUG\n+\t\t\t);\n+\n+\t\t\t// This checks if we have a preview of those exact dimensions in the cache\n+\t\t\tif ($this->userView->file_exists($preview)) {\n+\t\t\t\treturn $preview;\n+\t\t\t}\n+\n+\t\t\t/**\n+\t\t\t * Phase 4: We look for a larger preview, matching the aspect ratio\n+\t\t\t */\n+\t\t\tif (($this->getMaxX() >= $maxPreviewWidth)\n+\t\t\t\t&& ($this->getMaxY() >= $maxPreviewHeight)\n+\t\t\t) {\n+\n+\t\t\t\t// FIXME DEBUG CODE\n+\t\t\t\t\\OCP\\Util::writeLog(\n+\t\t\t\t\t'core',\n+\t\t\t\t\t\"Exact preview not found and it's larger than the max preview, so returning that\",\n+\t\t\t\t\t\\OCP\\Util::DEBUG\n+\t\t\t\t);\n+\n+\t\t\t\t// The preview we-re looking for is the exact size or larger than the max preview,\n+\t\t\t\t// so return that\n+\t\t\t\treturn $this->buildCachePath($fileId, $maxPreviewWidth, $maxPreviewHeight);\n+\t\t\t} else {\n+\t\t\t\t// The last resort is to look for something bigger than what we've calculated,\n+\t\t\t\t// but still smaller than the max preview\n+\t\t\t\treturn $this->isCachedBigger($fileId, $allThumbnails);\n+\t\t\t}\n+\t\t}\n+\n+\t\treturn false;\n+\t}\n+\n+\t/**\n+\t * Returns the dimensions of the max preview\n+\t *\n+\t * @param FileInfo[] $allThumbnails the list of all our cached thumbnails\n+\t *\n+\t * @return int[]\n+\t */\n+\tprivate function getMaxPreviewSize($allThumbnails) {\n+\t\t$maxPreviewX = null;\n+\t\t$maxPreviewY = null;\n+\n+\t\tforeach ($allThumbnails as $thumbnail) {\n+\t\t\t$name = $thumbnail['name'];\n+\t\t\tif (strpos($name, 'max')) {\n+\t\t\t\tlist($maxPreviewX, $maxPreviewY) = $this->getDimensionsFromFilename($name);\n+\n+\t\t\t\t// FIXME DEBUG CODE\n+\t\t\t\t\\OCP\\Util::writeLog('core', 'maxPreview ' . $name, \\OCP\\Util::DEBUG);\n \n-\t\t// This checks if a preview exists at that location\n-\t\tif ($this->userView->file_exists($preview)) {\n-\t\t\treturn $preview;\n+\t\t\t\tbreak;\n+\t\t\t}\n \t\t}\n \n-\t\treturn $this->isCachedBigger($fileId);\n+\t\treturn [$maxPreviewX, $maxPreviewY];\n \t}\n \n \t/**\n-\t * Checks if a bigger version of a file preview is cached and if not\n-\t * return the preview of max allowed dimensions\n+\t * Determines the size of the preview we should be looking for in the cache\n \t *\n-\t * @param int $fileId fileId of the original image\n+\t * @return int[]\n+\t */\n+\tprivate function simulatePreviewDimensions() {\n+\t\t$askedWidth = $this->getMaxX();\n+\t\t$askedHeight = $this->getMaxY();\n+\n+\t\tif ($this->keepAspect) {\n+\n+\t\t\t// FIXME DEBUG CODE\n+\t\t\t$originalRatio = $this->maxPreviewWidth / $this->maxPreviewHeight;\n+\t\t\t\\OCP\\Util::writeLog(\n+\t\t\t\t'core',\n+\t\t\t\t\"BEFORE AR $askedWidth x $askedHeight Ratio $originalRatio\",\n+\t\t\t\t\\OCP\\Util::DEBUG\n+\t\t\t);\n+\n+\t\t\tlist($newPreviewWidth, $newPreviewHeight) =\n+\t\t\t\t$this->applyAspectRatio($askedWidth, $askedHeight);\n+\n+\t\t\t// FIXME DEBUG CODE\n+\t\t\t\\OCP\\Util::writeLog(\n+\t\t\t\t'core', 'AFTER AR ' . $newPreviewWidth . 'x' . $newPreviewHeight, \\OCP\\Util::DEBUG\n+\t\t\t);\n+\t\t} else {\n+\t\t\tlist($newPreviewWidth, $newPreviewHeight) = $this->fixSize($askedWidth, $askedHeight);\n+\t\t}\n+\n+\t\treturn [(int)$newPreviewWidth, (int)$newPreviewHeight];\n+\t}\n+\n+\t/**\n+\t * Resizes the boundaries to match the aspect ratio\n \t *\n-\t * @return string|false path to bigger thumbnail if it exists or false\n+\t * @param int $askedWidth\n+\t * @param int $askedHeight\n+\t *\n+\t * @return \\int[]\n \t */\n-\tprivate function isCachedBigger($fileId) {\n+\tprivate function applyAspectRatio($askedWidth, $askedHeight) {\n+\t\t$originalRatio = $this->maxPreviewWidth / $this->maxPreviewHeight;\n+\t\t// Defines the box in which the preview has to fit\n+\t\t$scaleFactor = $this->scalingUp ? $this->maxScaleFactor : 1;\n+\t\t$askedWidth = min($askedWidth, $this->maxPreviewWidth * $scaleFactor);\n+\t\t$askedHeight = min($askedHeight, $this->maxPreviewHeight * $scaleFactor);\n+\n+\t\tif ($askedWidth / $originalRatio < $askedHeight) {\n+\t\t\t// width restricted\n+\t\t\t$askedHeight = round($askedWidth / $originalRatio);\n+\t\t} else {\n+\t\t\t$askedWidth = round($askedHeight * $originalRatio);\n+\t\t}\n \n-\t\tif (is_null($fileId)) {\n-\t\t\treturn false;\n+\t\treturn [(int)$askedWidth, (int)$askedHeight];","path":"lib/private/preview.php","position":547,"original_position":547,"commit_id":"69cab1e72f9528c2e219ad9e2f2975c21e61db39","original_commit_id":"69cab1e72f9528c2e219ad9e2f2975c21e61db39","user":{"login":"LukasReschke","id":878997,"avatar_url":"https://avatars.githubusercontent.com/u/878997?v=3","gravatar_id":"","url":"https://api.github.com/users/LukasReschke","html_url":"https://github.com/LukasReschke","followers_url":"https://api.github.com/users/LukasReschke/followers","following_url":"https://api.github.com/users/LukasReschke/following{/other_user}","gists_url":"https://api.github.com/users/LukasReschke/gists{/gist_id}","starred_url":"https://api.github.com/users/LukasReschke/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/LukasReschke/subscriptions","organizations_url":"https://api.github.com/users/LukasReschke/orgs","repos_url":"https://api.github.com/users/LukasReschke/repos","events_url":"https://api.github.com/users/LukasReschke/events{/privacy}","received_events_url":"https://api.github.com/users/LukasReschke/received_events","type":"User","site_admin":false},"body":"I really like how you cast stuff. Kudos for that :rocket: :beers","created_at":"2015-06-05T18:33:48Z","updated_at":"2015-06-05T18:33:48Z","html_url":"https://github.com/owncloud/core/pull/16382#discussion_r31839112","pull_request_url":"https://api.github.com/repos/owncloud/core/pulls/16382","_links":{"self":{"href":"https://api.github.com/repos/owncloud/core/pulls/comments/31839112"},"html":{"href":"https://github.com/owncloud/core/pull/16382#discussion_r31839112"},"pull_request":{"href":"https://api.github.com/repos/owncloud/core/pulls/16382"}}},"pull_request":{"url":"https://api.github.com/repos/owncloud/core/pulls/16382","id":35576277,"html_url":"https://github.com/owncloud/core/pull/16382","diff_url":"https://github.com/owncloud/core/pull/16382.diff","patch_url":"https://github.com/owncloud/core/pull/16382.patch","issue_url":"https://api.github.com/repos/owncloud/core/issues/16382","number":16382,"state":"open","locked":false,"title":"Fix max preview, some resizing and caching issues and force preview providers to resize their previews properly","user":{"login":"oparoz","id":323220,"avatar_url":"https://avatars.githubusercontent.com/u/323220?v=3","gravatar_id":"","url":"https://api.github.com/users/oparoz","html_url":"https://github.com/oparoz","followers_url":"https://api.github.com/users/oparoz/followers","following_url":"https://api.github.com/users/oparoz/following{/other_user}","gists_url":"https://api.github.com/users/oparoz/gists{/gist_id}","starred_url":"https://api.github.com/users/oparoz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/oparoz/subscriptions","organizations_url":"https://api.github.com/users/oparoz/orgs","repos_url":"https://api.github.com/users/oparoz/repos","events_url":"https://api.github.com/users/oparoz/events{/privacy}","received_events_url":"https://api.github.com/users/oparoz/received_events","type":"User","site_admin":false},"body":"## TL;DR\r\nThis PR:\r\n* [introduces a method in OC_Image](https://github.com/owncloud/core/pull/16382/files#diff-8590d19664dac0d5de93639c3e21373cR984) which doesn't stretch images when trying to make them fit in a box\r\n* adds the method to all key providers so that they can do their job, as expected by the Preview class\r\n* [improves the caching mechanism of Preview](https://github.com/oparoz/core/commit/d4483fd25c4c55ad8e188fab1b8b2c5866e4ee94) in order to reduce I/O and to avoid filling the available disk space\r\n* fixes some long standing issues\r\n* **contains mostly tests**\r\n\r\n## Description\r\nhttps://github.com/owncloud/core/issues/13607 describes what needed to be done **at the provider level** in order to support the max preview feature, but unfortunately, only the bitmap provider was fixed until now and this led to some max previews being cropped (https://github.com/owncloud/core/pull/16355).\r\n\r\nSince all the other providers end up with an \\OC_Image object, they can simply use one of the available methods to make sure the preview they create fits in the asked dimension. I decided to call that method from within each provider since they're responsible to provide a preview which matches the requirements.\r\n\r\nUnfortunately, since none of the available method does the job, a new one had to be introduced to make sure a smaller preview wouldn't be stretched to fit in the max dimensions.\r\n\r\nOne test per group of provider is provided.\r\n\r\nAlso, since when working on this I encountered many bugs, both introduced by the max preview and through the years, I've decided to **write tests (41 x 4 samples)** for the Preview class to try and detect most of the problems and to fix them.\r\n\r\nThis branch will now also fix\r\n- [x] Never cache previews larger than the max preview https://github.com/owncloud/core/issues/16545\r\n- [x] Don't store the exact same preview every time we check for its existence https://github.com/owncloud/core/issues/16544\r\n- [x]  Preview can't resize wide images properly #14712 \r\n\r\n**Enjoy and hopefully this will be working well for all of you!**\r\n\r\nI'm also open to discussing testing strategies as this is not easy to do properly and requires large data samples.\r\n\r\n## Test data\r\nI suggest you upload at least these 4 pictures from the tests/data folder to your test folder:\r\n* `testimage.eps` | standard eps file, converted by ImageMagick\r\n* `testimage.jpg` | normal jpg with some text in it\r\n* `testimage-wide.png` | panoramic picture\r\n* `testimageblah.gif` | small animated gif\r\n\r\nThey were chosen because they trigger different scenarios in the resizing mechanism. You're welcome to test with your libraries as well, just make sure you've deleted the thumbnail folder before proceeding.\r\n\r\nYour configuration should have the max preview dimensions set at 756x756 if you want to be able to really test the various restrictions the resizing can encouter.\r\nIf you want to test upscaling, then you should have a max_factor of above 1.0 in your configuration\r\n\r\n## Tests\r\n\r\n#### URLs\r\n**No aspect ratio**\r\n`/index.php/core/preview.png?file=testimage.jpg&x=756&y=756&forceIcon=1`\r\n**With ratio**\r\n`/index.php/core/preview.png?file=testimage.jpg&x=756&y=756&forceIcon=1&a`\r\n\r\nYou're encouraged to play with various sizes, to see the effect it has on the rendering\r\n\r\nIt's also possible to disable upscaling by adding this to the URL: `&scalingup=0`, but you will need to delete your thumbnails folder in order to be able to get real results, since there is no mechanism in place which detects if a preview has been upscaled or not. We currently only track with or without aspect ratio.\r\n\r\n#### Gallery\r\nI also invite you to install [Gallery 8.1](https://apps.owncloud.com/content/show.php?content=169939) to test this since it will generate thumbnails and large previews and would be a good quick test.\r\nMake sure you increase the the max preview dimensions and delete the thumbnails folder prior to testing or your large previews will look tiny.\r\n\r\n#### Mobile clients\r\nYou should be getting the same previews you were getting before, except maybe a few \"fixed\" ones.\r\n\r\n## Test results\r\n\r\n* Pictures should never be bigger than 756x756 (using the config described above)\r\n* Whenever a picture is smaller than what is asked, it will be sent as is if the aspect ratio is required and with a transparent background if no aspect ratio is required.\r\n\r\nI've left lots of debugging messages (to be cleanup later), so by looking at the logs, you should be getting an idea of what the class is trying to do with the request it's been given.\r\n\r\n","created_at":"2015-05-16T19:52:08Z","updated_at":"2015-06-05T18:33:48Z","closed_at":null,"merged_at":null,"merge_commit_sha":"dba02d6bda0ee95e437742d11f5a510bcf55a056","assignee":{"login":"oparoz","id":323220,"avatar_url":"https://avatars.githubusercontent.com/u/323220?v=3","gravatar_id":"","url":"https://api.github.com/users/oparoz","html_url":"https://github.com/oparoz","followers_url":"https://api.github.com/users/oparoz/followers","following_url":"https://api.github.com/users/oparoz/following{/other_user}","gists_url":"https://api.github.com/users/oparoz/gists{/gist_id}","starred_url":"https://api.github.com/users/oparoz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/oparoz/subscriptions","organizations_url":"https://api.github.com/users/oparoz/orgs","repos_url":"https://api.github.com/users/oparoz/repos","events_url":"https://api.github.com/users/oparoz/events{/privacy}","received_events_url":"https://api.github.com/users/oparoz/received_events","type":"User","site_admin":false},"milestone":{"url":"https://api.github.com/repos/owncloud/core/milestones/31","html_url":"https://github.com/owncloud/core/milestones/8.1-current","labels_url":"https://api.github.com/repos/owncloud/core/milestones/31/labels","id":912160,"number":31,"title":"8.1-current","description":"","creator":{"login":"MorrisJobke","id":245432,"avatar_url":"https://avatars.githubusercontent.com/u/245432?v=3","gravatar_id":"","url":"https://api.github.com/users/MorrisJobke","html_url":"https://github.com/MorrisJobke","followers_url":"https://api.github.com/users/MorrisJobke/followers","following_url":"https://api.github.com/users/MorrisJobke/following{/other_user}","gists_url":"https://api.github.com/users/MorrisJobke/gists{/gist_id}","starred_url":"https://api.github.com/users/MorrisJobke/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/MorrisJobke/subscriptions","organizations_url":"https://api.github.com/users/MorrisJobke/orgs","repos_url":"https://api.github.com/users/MorrisJobke/repos","events_url":"https://api.github.com/users/MorrisJobke/events{/privacy}","received_events_url":"https://api.github.com/users/MorrisJobke/received_events","type":"User","site_admin":false},"open_issues":78,"closed_issues":1346,"state":"open","created_at":"2014-12-22T12:04:01Z","updated_at":"2015-06-05T18:09:56Z","due_on":"2015-04-02T22:00:00Z","closed_at":null},"commits_url":"https://api.github.com/repos/owncloud/core/pulls/16382/commits","review_comments_url":"https://api.github.com/repos/owncloud/core/pulls/16382/comments","review_comment_url":"https://api.github.com/repos/owncloud/core/pulls/comments{/number}","comments_url":"https://api.github.com/repos/owncloud/core/issues/16382/comments","statuses_url":"https://api.github.com/repos/owncloud/core/statuses/69cab1e72f9528c2e219ad9e2f2975c21e61db39","head":{"label":"oparoz:providers-are-responsible-for-the-size-of-their-preview","ref":"providers-are-responsible-for-the-size-of-their-preview","sha":"69cab1e72f9528c2e219ad9e2f2975c21e61db39","user":{"login":"oparoz","id":323220,"avatar_url":"https://avatars.githubusercontent.com/u/323220?v=3","gravatar_id":"","url":"https://api.github.com/users/oparoz","html_url":"https://github.com/oparoz","followers_url":"https://api.github.com/users/oparoz/followers","following_url":"https://api.github.com/users/oparoz/following{/other_user}","gists_url":"https://api.github.com/users/oparoz/gists{/gist_id}","starred_url":"https://api.github.com/users/oparoz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/oparoz/subscriptions","organizations_url":"https://api.github.com/users/oparoz/orgs","repos_url":"https://api.github.com/users/oparoz/repos","events_url":"https://api.github.com/users/oparoz/events{/privacy}","received_events_url":"https://api.github.com/users/oparoz/received_events","type":"User","site_admin":false},"repo":{"id":23593901,"name":"core","full_name":"oparoz/core","owner":{"login":"oparoz","id":323220,"avatar_url":"https://avatars.githubusercontent.com/u/323220?v=3","gravatar_id":"","url":"https://api.github.com/users/oparoz","html_url":"https://github.com/oparoz","followers_url":"https://api.github.com/users/oparoz/followers","following_url":"https://api.github.com/users/oparoz/following{/other_user}","gists_url":"https://api.github.com/users/oparoz/gists{/gist_id}","starred_url":"https://api.github.com/users/oparoz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/oparoz/subscriptions","organizations_url":"https://api.github.com/users/oparoz/orgs","repos_url":"https://api.github.com/users/oparoz/repos","events_url":"https://api.github.com/users/oparoz/events{/privacy}","received_events_url":"https://api.github.com/users/oparoz/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/oparoz/core","description":"The ownCloud core","fork":true,"url":"https://api.github.com/repos/oparoz/core","forks_url":"https://api.github.com/repos/oparoz/core/forks","keys_url":"https://api.github.com/repos/oparoz/core/keys{/key_id}","collaborators_url":"https://api.github.com/repos/oparoz/core/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/oparoz/core/teams","hooks_url":"https://api.github.com/repos/oparoz/core/hooks","issue_events_url":"https://api.github.com/repos/oparoz/core/issues/events{/number}","events_url":"https://api.github.com/repos/oparoz/core/events","assignees_url":"https://api.github.com/repos/oparoz/core/assignees{/user}","branches_url":"https://api.github.com/repos/oparoz/core/branches{/branch}","tags_url":"https://api.github.com/repos/oparoz/core/tags","blobs_url":"https://api.github.com/repos/oparoz/core/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/oparoz/core/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/oparoz/core/git/refs{/sha}","trees_url":"https://api.github.com/repos/oparoz/core/git/trees{/sha}","statuses_url":"https://api.github.com/repos/oparoz/core/statuses/{sha}","languages_url":"https://api.github.com/repos/oparoz/core/languages","stargazers_url":"https://api.github.com/repos/oparoz/core/stargazers","contributors_url":"https://api.github.com/repos/oparoz/core/contributors","subscribers_url":"https://api.github.com/repos/oparoz/core/subscribers","subscription_url":"https://api.github.com/repos/oparoz/core/subscription","commits_url":"https://api.github.com/repos/oparoz/core/commits{/sha}","git_commits_url":"https://api.github.com/repos/oparoz/core/git/commits{/sha}","comments_url":"https://api.github.com/repos/oparoz/core/comments{/number}","issue_comment_url":"https://api.github.com/repos/oparoz/core/issues/comments{/number}","contents_url":"https://api.github.com/repos/oparoz/core/contents/{+path}","compare_url":"https://api.github.com/repos/oparoz/core/compare/{base}...{head}","merges_url":"https://api.github.com/repos/oparoz/core/merges","archive_url":"https://api.github.com/repos/oparoz/core/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/oparoz/core/downloads","issues_url":"https://api.github.com/repos/oparoz/core/issues{/number}","pulls_url":"https://api.github.com/repos/oparoz/core/pulls{/number}","milestones_url":"https://api.github.com/repos/oparoz/core/milestones{/number}","notifications_url":"https://api.github.com/repos/oparoz/core/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/oparoz/core/labels{/name}","releases_url":"https://api.github.com/repos/oparoz/core/releases{/id}","created_at":"2014-09-02T20:10:47Z","updated_at":"2015-04-27T19:10:42Z","pushed_at":"2015-05-31T12:42:20Z","git_url":"git://github.com/oparoz/core.git","ssh_url":"git@github.com:oparoz/core.git","clone_url":"https://github.com/oparoz/core.git","svn_url":"https://github.com/oparoz/core","homepage":null,"size":185861,"stargazers_count":0,"watchers_count":0,"language":"PHP","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"owncloud:master","ref":"master","sha":"744a9f2c78e2e311f19d3b44207f4adcded76be0","user":{"login":"owncloud","id":1645051,"avatar_url":"https://avatars.githubusercontent.com/u/1645051?v=3","gravatar_id":"","url":"https://api.github.com/users/owncloud","html_url":"https://github.com/owncloud","followers_url":"https://api.github.com/users/owncloud/followers","following_url":"https://api.github.com/users/owncloud/following{/other_user}","gists_url":"https://api.github.com/users/owncloud/gists{/gist_id}","starred_url":"https://api.github.com/users/owncloud/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/owncloud/subscriptions","organizations_url":"https://api.github.com/users/owncloud/orgs","repos_url":"https://api.github.com/users/owncloud/repos","events_url":"https://api.github.com/users/owncloud/events{/privacy}","received_events_url":"https://api.github.com/users/owncloud/received_events","type":"Organization","site_admin":false},"repo":{"id":5550552,"name":"core","full_name":"owncloud/core","owner":{"login":"owncloud","id":1645051,"avatar_url":"https://avatars.githubusercontent.com/u/1645051?v=3","gravatar_id":"","url":"https://api.github.com/users/owncloud","html_url":"https://github.com/owncloud","followers_url":"https://api.github.com/users/owncloud/followers","following_url":"https://api.github.com/users/owncloud/following{/other_user}","gists_url":"https://api.github.com/users/owncloud/gists{/gist_id}","starred_url":"https://api.github.com/users/owncloud/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/owncloud/subscriptions","organizations_url":"https://api.github.com/users/owncloud/orgs","repos_url":"https://api.github.com/users/owncloud/repos","events_url":"https://api.github.com/users/owncloud/events{/privacy}","received_events_url":"https://api.github.com/users/owncloud/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/owncloud/core","description":"The ownCloud core","fork":false,"url":"https://api.github.com/repos/owncloud/core","forks_url":"https://api.github.com/repos/owncloud/core/forks","keys_url":"https://api.github.com/repos/owncloud/core/keys{/key_id}","collaborators_url":"https://api.github.com/repos/owncloud/core/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/owncloud/core/teams","hooks_url":"https://api.github.com/repos/owncloud/core/hooks","issue_events_url":"https://api.github.com/repos/owncloud/core/issues/events{/number}","events_url":"https://api.github.com/repos/owncloud/core/events","assignees_url":"https://api.github.com/repos/owncloud/core/assignees{/user}","branches_url":"https://api.github.com/repos/owncloud/core/branches{/branch}","tags_url":"https://api.github.com/repos/owncloud/core/tags","blobs_url":"https://api.github.com/repos/owncloud/core/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/owncloud/core/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/owncloud/core/git/refs{/sha}","trees_url":"https://api.github.com/repos/owncloud/core/git/trees{/sha}","statuses_url":"https://api.github.com/repos/owncloud/core/statuses/{sha}","languages_url":"https://api.github.com/repos/owncloud/core/languages","stargazers_url":"https://api.github.com/repos/owncloud/core/stargazers","contributors_url":"https://api.github.com/repos/owncloud/core/contributors","subscribers_url":"https://api.github.com/repos/owncloud/core/subscribers","subscription_url":"https://api.github.com/repos/owncloud/core/subscription","commits_url":"https://api.github.com/repos/owncloud/core/commits{/sha}","git_commits_url":"https://api.github.com/repos/owncloud/core/git/commits{/sha}","comments_url":"https://api.github.com/repos/owncloud/core/comments{/number}","issue_comment_url":"https://api.github.com/repos/owncloud/core/issues/comments{/number}","contents_url":"https://api.github.com/repos/owncloud/core/contents/{+path}","compare_url":"https://api.github.com/repos/owncloud/core/compare/{base}...{head}","merges_url":"https://api.github.com/repos/owncloud/core/merges","archive_url":"https://api.github.com/repos/owncloud/core/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/owncloud/core/downloads","issues_url":"https://api.github.com/repos/owncloud/core/issues{/number}","pulls_url":"https://api.github.com/repos/owncloud/core/pulls{/number}","milestones_url":"https://api.github.com/repos/owncloud/core/milestones{/number}","notifications_url":"https://api.github.com/repos/owncloud/core/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/owncloud/core/labels{/name}","releases_url":"https://api.github.com/repos/owncloud/core/releases{/id}","created_at":"2012-08-25T10:32:48Z","updated_at":"2015-06-05T08:16:57Z","pushed_at":"2015-06-05T18:28:23Z","git_url":"git://github.com/owncloud/core.git","ssh_url":"git@github.com:owncloud/core.git","clone_url":"https://github.com/owncloud/core.git","svn_url":"https://github.com/owncloud/core","homepage":"https://owncloud.org/","size":1051020,"stargazers_count":3366,"watchers_count":3366,"language":"PHP","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1077,"mirror_url":null,"open_issues_count":1862,"forks":1077,"open_issues":1862,"watchers":3366,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/owncloud/core/pulls/16382"},"html":{"href":"https://github.com/owncloud/core/pull/16382"},"issue":{"href":"https://api.github.com/repos/owncloud/core/issues/16382"},"comments":{"href":"https://api.github.com/repos/owncloud/core/issues/16382/comments"},"review_comments":{"href":"https://api.github.com/repos/owncloud/core/pulls/16382/comments"},"review_comment":{"href":"https://api.github.com/repos/owncloud/core/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/owncloud/core/pulls/16382/commits"},"statuses":{"href":"https://api.github.com/repos/owncloud/core/statuses/69cab1e72f9528c2e219ad9e2f2975c21e61db39"}}}}