+++ /dev/null
-<?php
-
-/**
- * @file mod/photo.php
- */
-use Friendica\App;
-use Friendica\Database\DBA;
-use Friendica\Object\Image;
-use Friendica\Util\Security;
-
-function photo_init(App $a)
-{
- global $_SERVER;
-
- $prvcachecontrol = false;
- $file = "";
-
- switch ($a->argc) {
- case 4:
- $person = $a->argv[3];
- $customres = intval($a->argv[2]);
- $type = $a->argv[1];
- break;
- case 3:
- $person = $a->argv[2];
- $type = $a->argv[1];
- break;
- case 2:
- $photo = $a->argv[1];
- $file = $photo;
- break;
- case 1:
- default:
- killme();
- // NOTREACHED
- }
-
- if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
- header('HTTP/1.1 304 Not Modified');
- header("Last-Modified: " . gmdate("D, d M Y H:i:s", time()) . " GMT");
- if (!empty($_SERVER['HTTP_IF_NONE_MATCH'])) {
- header('Etag: ' . $_SERVER['HTTP_IF_NONE_MATCH']);
- }
- header("Expires: " . gmdate("D, d M Y H:i:s", time() + (31536000)) . " GMT");
- header("Cache-Control: max-age=31536000");
- if (function_exists('header_remove')) {
- header_remove('Last-Modified');
- header_remove('Expires');
- header_remove('Cache-Control');
- }
- exit;
- }
-
- $default = 'images/person-300.jpg';
- $public = true;
-
- if (isset($type)) {
- // Profile photos
- switch ($type) {
- case 'profile':
- case 'custom':
- $resolution = 4;
- break;
- case 'micro':
- $resolution = 6;
- $default = 'images/person-48.jpg';
- break;
- case 'avatar':
- default:
- $resolution = 5;
- $default = 'images/person-80.jpg';
- break;
- }
-
- $uid = str_replace(['.jpg', '.png', '.gif'], ['', '', ''], $person);
-
- foreach (Image::supportedTypes() AS $m => $e) {
- $uid = str_replace('.' . $e, '', $uid);
- }
-
- $r = q("SELECT * FROM `photo` WHERE `scale` = %d AND `uid` = %d AND `profile` = 1 LIMIT 1",
- intval($resolution),
- intval($uid)
- );
- if (DBA::isResult($r)) {
- $data = $r[0]['data'];
- $mimetype = $r[0]['type'];
- }
- if (empty($data)) {
- $data = file_get_contents($default);
- $mimetype = 'image/jpeg';
- }
- } else {
- // Other photos
- $resolution = 0;
- $photo = str_replace(['.jpg', '.png', '.gif'], ['', '', ''], $photo);
-
- foreach (Image::supportedTypes() AS $m => $e) {
- $photo = str_replace('.' . $e, '', $photo);
- }
-
- if (substr($photo, -2, 1) == '-') {
- $resolution = intval(substr($photo, -1, 1));
- $photo = substr($photo, 0, -2);
- }
-
- // check if the photo exists and get the owner of the photo
- $r = q("SELECT `uid` FROM `photo` WHERE `resource-id` = '%s' LIMIT 1",
- DBA::escape($photo),
- intval($resolution)
- );
- if (DBA::isResult($r)) {
- $sql_extra = Security::getPermissionsSQLByUserId($r[0]['uid']);
-
- // Now we'll see if we can access the photo
- $r = q("SELECT * FROM `photo` WHERE `resource-id` = '%s' AND `scale` <= %d $sql_extra ORDER BY scale DESC LIMIT 1",
- DBA::escape($photo),
- intval($resolution)
- );
- if (DBA::isResult($r)) {
- $resolution = $r[0]['scale'];
- $data = $r[0]['data'];
- $mimetype = $r[0]['type'];
- $public = $r[0]['allow_cid'] == '' && $r[0]['allow_gid'] == '' && $r[0]['deny_cid'] == '' && $r[0]['deny_gid'] == '';
- } else {
- // The picure exists. We already checked with the first query.
- // obviously, this is not an authorized viev!
- $data = file_get_contents('images/nosign.jpg');
- $mimetype = 'image/jpeg';
- $prvcachecontrol = true;
- $public = false;
- }
- }
- }
-
- if (empty($data)) {
- if (isset($resolution)) {
- switch ($resolution) {
- case 4:
- $data = file_get_contents('images/person-300.jpg');
- $mimetype = 'image/jpeg';
- break;
- case 5:
- $data = file_get_contents('images/person-80.jpg');
- $mimetype = 'image/jpeg';
- break;
- case 6:
- $data = file_get_contents('images/person-48.jpg');
- $mimetype = 'image/jpeg';
- break;
- default:
- killme();
- // NOTREACHED
- break;
- }
- }
- }
-
- // Resize only if its not a GIF and it is supported by the library
- if ($mimetype != "image/gif" && in_array($mimetype, Image::supportedTypes())) {
- $Image = new Image($data, $mimetype);
- if ($Image->isValid()) {
- if (isset($customres) && $customres > 0 && $customres < 500) {
- $Image->scaleToSquare($customres);
- }
- $data = $Image->asString();
- $mimetype = $Image->getType();
- }
- }
-
- if (function_exists('header_remove')) {
- header_remove('Pragma');
- header_remove('pragma');
- }
-
- header("Content-type: " . $mimetype);
-
- if ($prvcachecontrol) {
- // it is a private photo that they have no permission to view.
- // tell the browser not to cache it, in case they authenticate
- // and subsequently have permission to see it
- header("Cache-Control: no-store, no-cache, must-revalidate");
- } else {
- header("Last-Modified: " . gmdate("D, d M Y H:i:s", time()) . " GMT");
- header('Etag: "' . md5($data) . '"');
- header("Expires: " . gmdate("D, d M Y H:i:s", time() + (31536000)) . " GMT");
- header("Cache-Control: max-age=31536000");
- }
- echo $data;
-
- // If the photo is public and there is an existing photo directory store the photo there
- if ($public and $file != '') {
- // If the photo path isn't there, try to create it
- $basepath = $a->getBasePath();
- if (!is_dir($basepath . "/photo")) {
- if (is_writable($basepath)) {
- mkdir($basepath . "/photo");
- }
- }
-
- if (is_dir($basepath . "/photo")) {
- file_put_contents($basepath . "/photo/" . $file, $data);
- }
- }
-
- killme();
- // NOTREACHED
-}
*/
namespace Friendica\Model;
+use Friendica\BaseObject;
use Friendica\Core\Cache;
use Friendica\Core\Config;
use Friendica\Core\L10n;
use Friendica\Core\System;
use Friendica\Database\DBA;
+use Friendica\Database\DBStructure;
use Friendica\Object\Image;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Network;
/**
* Class to handle photo dabatase table
*/
-class Photo
+class Photo extends BaseObject
{
/**
+ * @brief Select rows from the photo table
+ *
+ * @param array $fields Array of selected fields, empty for all
+ * @param array $condition Array of fields for condition
+ * @param array $params Array of several parameters
+ *
+ * @return boolean|array
+ *
+ * @see \Friendica\Database\DBA::select
+ */
+ public static function select(array $fields = [], array $condition = [], array $params = [])
+ {
+ if (empty($fields)) {
+ $selected = self::getFields();
+ }
+
+ $r = DBA::select("photo", $fields, $condition, $params);
+ return DBA::toArray($r);
+ }
+
+ /**
+ * @brief Retrieve a single record from the photo table
+ *
+ * @param array $fields Array of selected fields, empty for all
+ * @param array $condition Array of fields for condition
+ * @param array $params Array of several parameters
+ *
+ * @return bool|array
+ *
+ * @see \Friendica\Database\DBA::select
+ */
+ public static function selectFirst(array $fields = [], array $condition = [], array $params = [])
+ {
+ if (empty($fields)) {
+ $selected = self::getFields();
+ }
+
+ return DBA::selectFirst("photo", $fields, $condition, $params);
+ }
+
+ /**
+ * @brief Get a single photo given resource id and scale
+ *
+ * This method checks for permissions. Returns associative array
+ * on success, a generic "red sign" data if user has no permission,
+ * false if photo does not exists
+ *
+ * @param string $resourceid Rescource ID for the photo
+ * @param integer $scale Scale of the photo. Defaults to 0
+ *
+ * @return boolean|array
+ */
+ public static function getPhoto($resourceid, $scale = 0)
+ {
+ $r = self::selectFirst(["uid"], ["resource-id" => $resourceid]);
+ if ($r===false) return false;
+
+ $sql_acl = Security::getPermissionsSQLByUserId($r["uid"]);
+
+ $condition = [
+ "`resource-id` = ? AND `scale` <= ? " . $sql_acl,
+ $resourceid, $scale
+ ];
+
+ $params = [ "order" => ["scale" => true]];
+
+ $photo = self::selectFirst([], $condition, $params);
+ if ($photo === false) {
+ return false; ///TODO: Return info for red sign image
+ }
+ return $photo;
+ }
+
+ /**
+ * @brief Check if photo with given resource id exists
+ *
+ * @param string $resourceid Resource ID of the photo
+ *
+ * @return boolean
+ */
+ public static function exists($resourceid)
+ {
+ return DBA::count("photo", ["resource-id" => $resourceid]) > 0;
+ }
+
+ /**
+ * @brief Get Image object for given row id. null if row id does not exist
+ *
+ * @param integer $id Row id
+ *
+ * @return \Friendica\Object\Image
+ */
+ public static function getImageForPhotoId($id)
+ {
+ $i = self::selectFirst(["data", "type"],["id"=>$id]);
+ if ($i===false) {
+ return null;
+ }
+ return new Image($i["data"], $i["type"]);
+ }
+
+ /**
+ * @brief Return a list of fields that are associated with the photo table
+ *
+ * @return array field list
+ */
+ private static function getFields()
+ {
+ $allfields = DBStructure::definition(false);
+ $fields = array_keys($allfields["photo"]["fields"]);
+ array_splice($fields, array_search("data", $fields), 1);
+ return $fields;
+ }
+
+
+
+ /**
+ * @brief store photo metadata in db and binary in default backend
+ *
* @param Image $Image image
* @param integer $uid uid
* @param integer $cid cid
* @param string $deny_cid optional, default = ''
* @param string $deny_gid optional, default = ''
* @param string $desc optional, default = ''
- * @return object
+ *
+ * @return boolean True on success
*/
public static function store(Image $Image, $uid, $cid, $rid, $filename, $album, $scale, $profile = 0, $allow_cid = '', $allow_gid = '', $deny_cid = '', $deny_gid = '', $desc = '')
{
--- /dev/null
+<?php
+/**
+ * @file src/Module/Photo.php
+ */
+
+namespace Friendica\Module;
+
+use Friendica\BaseModule;
+use Friendica\Core\Logger;
+use Friendica\Model\Photo as MPhoto;
+use Friendica\Object\Image;
+use Friendica\Util\Security;
+use Friendica\Network\HTTPException\NotFoundException;
+use Friendica\Network\HTTPException\BadRequestException;
+use Friendica\Network\HTTPException\InternalServerErrorException;
+
+/**
+ * @brief Photo Module
+ */
+class Photo extends BaseModule
+{
+
+ /**
+ * @brief Module initializer
+ *
+ * Fetch a photo or an avatar, in optional size, check for permissions and
+ * return the image
+ */
+ public static function init()
+ {
+ $a = self::getApp();
+ if ($a->argc <= 1 || $a->argc > 4) {
+ throw new BadRequestException();
+ killme();
+ }
+
+ if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
+ header('HTTP/1.1 304 Not Modified');
+ header("Last-Modified: " . gmdate("D, d M Y H:i:s", time()) . " GMT");
+ if (!empty($_SERVER['HTTP_IF_NONE_MATCH'])) {
+ header('Etag: ' . $_SERVER['HTTP_IF_NONE_MATCH']);
+ }
+ header("Expires: " . gmdate("D, d M Y H:i:s", time() + (31536000)) . " GMT");
+ header("Cache-Control: max-age=31536000");
+ if (function_exists('header_remove')) {
+ header_remove('Last-Modified');
+ header_remove('Expires');
+ header_remove('Cache-Control');
+ }
+ exit;
+ }
+
+ $customsize = 0;
+ switch($a->argc) {
+ case 4:
+ $customsize = intval($a->argv[2]);
+ $uid = self::stripExtension($a->argv[3]);
+ $photo = self::getAvatar($uid, $a->argv[1]);
+ break;
+ case 3:
+ $uid = self::stripExtension($a->argv[2]);
+ $photo = self::getAvatar($uid, $a->argv[1]);
+ break;
+ case 2:
+ $photoid = self::stripExtension($a->argv[1]);
+ $scale = 0;
+ if (substr($photoid, -2, 1) == '-') {
+ $scale = intval(substr($photoid, -1, 1));
+ $photoid = substr($photoid, 0, -2);
+ }
+ $photo = MPhoto::getPhoto($photoid, $scale);
+ break;
+ }
+
+ if ($photo===false) {
+ header($_SERVER["SERVER_PROTOCOL"] . " 404 Not Found" , true, 404);
+ killme();
+ }
+
+ $cacheable = ($photo["allow_cid"].$photo["allow_gid"].$photo["deny_cid"].$photo["deny_gid"] === "") || defaults($photo, "cacheable", false);
+
+ $img = MPhoto::getImageForPhotoId($photo["id"]);
+
+ if (is_null($img) || !$img->isValid()) {
+ Logger::log("Invalid photo with id {$photo['id']}.");
+ throw new InternalServerErrorException();
+ }
+
+
+ // if customsize is set and image is not a gif, resize it
+ if ($img->getType() !== "image/gif" && $customsize > 0 && $customsize < 501) {
+ $img->scaleToSquare($customsize);
+ }
+
+
+ if (function_exists('header_remove')) {
+ header_remove('Pragma');
+ header_remove('pragma');
+ }
+
+ header("Content-type: " . $img->getType());
+
+ if (!$cacheable) {
+ // it is a private photo that they have no permission to view.
+ // tell the browser not to cache it, in case they authenticate
+ // and subsequently have permission to see it
+ header("Cache-Control: no-store, no-cache, must-revalidate");
+ } else {
+ header("Last-Modified: " . gmdate("D, d M Y H:i:s", time()) . " GMT");
+ header('Etag: "' . md5($img->asString()) . '"');
+ header("Expires: " . gmdate("D, d M Y H:i:s", time() + (31536000)) . " GMT");
+ header("Cache-Control: max-age=31536000");
+ }
+
+
+
+ echo $img->asString();
+
+
+ killme();
+ }
+
+ private static function stripExtension($name)
+ {
+ $name = str_replace([".jpg", ".png", ".gif"], ["", "", ""], $name);
+ foreach (Image::supportedTypes() AS $m => $e) {
+ $name = str_replace('.' . $e, '', $name);
+ }
+ return $name;
+ }
+
+ private static function getAvatar($uid, $type="avatar")
+ {
+
+ switch($type) {
+ case "profile":
+ case "custom":
+ $scale = 4;
+ $default = "images/person-300.jpg";
+ break;
+ case "micro":
+ $scale = 6;
+ $default = "images/person-48.jpg";
+ break;
+ case "avatar":
+ default:
+ $scale = 5;
+ $default = "images/person-80.jpg";
+ }
+
+ $photo = MPhoto::selectFirst([], ["scale" => $scale, "uid" => $uid, "profile" => 1]);
+ if ($photo===false) {
+ // todo default image info
+ }
+ return $photo;
+ }
+
+}
\ No newline at end of file