+ $photo_id = $_REQUEST['photo_id'];
+
+ // prepare json/xml output with data from database for the requested photo
+ $data = prepare_photo_data($type, $scale, $photo_id);
+
+ return api_format_data("photo_detail", $type, $data);
+ }
+
+
+ /**
+ * @brief updates the profile image for the user (either a specified profile or the default profile)
+ *
+ * @param string $type Known types are 'atom', 'rss', 'xml' and 'json'
+ * @return string
+ */
+ function api_account_update_profile_image($type) {
+ if (api_user() === false) {
+ throw new ForbiddenException();
+ }
+ // input params
+ $profileid = (x($_REQUEST, 'profile_id') ? $_REQUEST['profile_id'] : 0);
+
+ // error if image data is missing
+ if (!x($_FILES, 'image')) {
+ throw new BadRequestException("no media data submitted");
+ }
+
+ // check if specified profile id is valid
+ if ($profileid != 0) {
+ $r = q("SELECT `id` FROM `profile` WHERE `uid` = %d AND `id` = %d",
+ intval(api_user()),
+ intval($profileid));
+ // error message if specified profile id is not in database
+ if (!dbm::is_result($r)) {
+ throw new BadRequestException("profile_id not available");
+ }
+ $is_default_profile = $r['profile'];
+ } else {
+ $is_default_profile = 1;
+ }
+
+ // get mediadata from image or media (Twitter call api/account/update_profile_image provides image)
+ $media = null;
+ if (x($_FILES, 'image')) {
+ $media = $_FILES['image'];
+ } elseif (x($_FILES, 'media')) {
+ $media = $_FILES['media'];
+ }
+ // save new profile image
+ $data = save_media_to_database("profileimage", $media, $type, t('Profile Photos'), "", "", "", "", "", $is_default_profile);
+
+ // get filetype
+ if (is_array($media['type'])) {
+ $filetype = $media['type'][0];
+ } else {
+ $filetype = $media['type'];
+ }
+ if ($filetype == "image/jpeg") {
+ $fileext = "jpg";
+ } elseif ($filetype == "image/png") {
+ $fileext = "png";
+ }
+ // change specified profile or all profiles to the new resource-id
+ if ($is_default_profile) {
+ $r = q("UPDATE `photo` SET `profile` = 0 WHERE `profile` = 1 AND `resource-id` != '%s' AND `uid` = %d",
+ dbesc($data['photo']['id']),
+ intval(local_user())
+ );
+
+ $r = q("UPDATE `contact` SET `photo` = '%s', `thumb` = '%s', `micro` = '%s' WHERE `self` AND `uid` = %d",
+ dbesc(App::get_baseurl() . '/photo/' . $data['photo']['id'] . '-4.' . $fileext),
+ dbesc(App::get_baseurl() . '/photo/' . $data['photo']['id'] . '-5.' . $fileext),
+ dbesc(App::get_baseurl() . '/photo/' . $data['photo']['id'] . '-6.' . $fileext),
+ intval(local_user())
+ );
+ } else {
+ $r = q("UPDATE `profile` SET `photo` = '%s', `thumb` = '%s' WHERE `id` = %d AND `uid` = %d",
+ dbesc(App::get_baseurl() . '/photo/' . $data['photo']['id'] . '-4.' . $filetype),
+ dbesc(App::get_baseurl() . '/photo/' . $data['photo']['id'] . '-5.' . $filetype),
+ intval($_REQUEST['profile']),
+ intval(local_user())
+ );
+ }
+
+ // we'll set the updated profile-photo timestamp even if it isn't the default profile,
+ // so that browsers will do a cache update unconditionally
+
+ $r = q("UPDATE `contact` SET `avatar-date` = '%s' WHERE `self` = 1 AND `uid` = %d",
+ dbesc(datetime_convert()),
+ intval(local_user())
+ );
+
+ // Update global directory in background
+ //$user = api_get_user(get_app());
+ $url = App::get_baseurl() . '/profile/' . get_app()->user['nickname'];
+ if ($url && strlen(get_config('system', 'directory'))) {
+ proc_run(PRIORITY_LOW, "include/directory.php", $url);
+ }
+
+ proc_run(PRIORITY_LOW, 'include/profile_update.php', api_user());
+
+ // output for client
+ if ($data) {
+ return api_account_verify_credentials($type);
+ } else {
+ // SaveMediaToDatabase failed for some reason
+ throw new InternalServerErrorException("image upload failed");
+ }
+ }
+
+ // place api-register for photoalbum calls before 'api/friendica/photo', otherwise this function is never reached
+ api_register_func('api/friendica/photoalbum/delete', 'api_fr_photoalbum_delete', true, API_METHOD_DELETE);
+ api_register_func('api/friendica/photoalbum/update', 'api_fr_photoalbum_update', true, API_METHOD_POST);
+ api_register_func('api/friendica/photos/list', 'api_fr_photos_list', true);
+ api_register_func('api/friendica/photo/create', 'api_fr_photo_create_update', true, API_METHOD_POST);
+ api_register_func('api/friendica/photo/update', 'api_fr_photo_create_update', true, API_METHOD_POST);
+ api_register_func('api/friendica/photo/delete', 'api_fr_photo_delete', true, API_METHOD_DELETE);
+ api_register_func('api/friendica/photo', 'api_fr_photo_detail', true);
+ api_register_func('api/account/update_profile_image', 'api_account_update_profile_image', true, API_METHOD_POST);
+
+
+ function check_acl_input($acl_string) {
+ if ($acl_string == null || $acl_string == " ") {
+ return false;
+ }
+ $contact_not_found = false;
+
+ // split <x><y><z> into array of cid's
+ preg_match_all("/<[A-Za-z0-9]+>/", $acl_string, $array);
+
+ // check for each cid if it is available on server
+ $cid_array = $array[0];
+ foreach ($cid_array as $cid) {
+ $cid = str_replace("<", "", $cid);
+ $cid = str_replace(">", "", $cid);
+ $contact = q("SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d",
+ intval($cid),
+ intval(api_user()));
+ $contact_not_found |= !dbm::is_result($contact);
+ }
+ return $contact_not_found;
+ }
+
+ function save_media_to_database($mediatype, $media, $type, $album, $allow_cid, $deny_cid, $allow_gid, $deny_gid, $desc, $profile = 0, $visibility = false, $photo_id = null) {
+ $visitor = 0;
+ $src = "";
+ $filetype = "";
+ $filename = "";
+ $filesize = 0;
+
+ if (is_array($media)) {
+ if (is_array($media['tmp_name'])) {
+ $src = $media['tmp_name'][0];
+ } else {
+ $src = $media['tmp_name'];
+ }
+ if (is_array($media['name'])) {
+ $filename = basename($media['name'][0]);
+ } else {
+ $filename = basename($media['name']);
+ }
+ if (is_array($media['size'])) {
+ $filesize = intval($media['size'][0]);
+ } else {
+ $filesize = intval($media['size']);
+ }
+ if (is_array($media['type'])) {
+ $filetype = $media['type'][0];
+ } else {
+ $filetype = $media['type'];
+ }
+ }
+
+ if ($filetype == "") {
+ $filetype=guess_image_type($filename);
+ }
+ $imagedata = getimagesize($src);
+ if ($imagedata) {
+ $filetype = $imagedata['mime'];
+ }
+ logger("File upload src: " . $src . " - filename: " . $filename .
+ " - size: " . $filesize . " - type: " . $filetype, LOGGER_DEBUG);
+
+ // check if there was a php upload error
+ if ($filesize == 0 && $media['error'] == 1) {
+ throw new InternalServerErrorException("image size exceeds PHP config settings, file was rejected by server");
+ }
+ // check against max upload size within Friendica instance
+ $maximagesize = get_config('system', 'maximagesize');
+ if (($maximagesize) && ($filesize > $maximagesize)) {
+ $formattedBytes = formatBytes($maximagesize);
+ throw new InternalServerErrorException("image size exceeds Friendica config setting (uploaded size: $formattedBytes)");
+ }
+
+ // create Photo instance with the data of the image
+ $imagedata = @file_get_contents($src);
+ $ph = new Photo($imagedata, $filetype);
+ if (! $ph->is_valid()) {
+ throw new InternalServerErrorException("unable to process image data");
+ }
+
+ // check orientation of image
+ $ph->orient($src);
+ @unlink($src);
+
+ // check max length of images on server
+ $max_length = get_config('system', 'max_image_length');
+ if (! $max_length) {
+ $max_length = MAX_IMAGE_LENGTH;
+ }
+ if ($max_length > 0) {
+ $ph->scaleImage($max_length);
+ logger("File upload: Scaling picture to new size " . $max_length, LOGGER_DEBUG);
+ }
+ $width = $ph->getWidth();
+ $height = $ph->getHeight();
+
+ // create a new resource-id if not already provided
+ $hash = ($photo_id == null) ? photo_new_resource() : $photo_id;
+
+ if ($mediatype == "photo") {
+ // upload normal image (scales 0, 1, 2)
+ logger("photo upload: starting new photo upload", LOGGER_DEBUG);
+
+ $r =$ph->store(local_user(), $visitor, $hash, $filename, $album, 0, 0, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $desc);
+ if (! $r) {
+ logger("photo upload: image upload with scale 0 (original size) failed");
+ }
+ if($width > 640 || $height > 640) {
+ $ph->scaleImage(640);
+ $r = $ph->store(local_user(),$visitor, $hash, $filename, $album, 1, 0, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $desc);
+ if (! $r) {
+ logger("photo upload: image upload with scale 1 (640x640) failed");
+ }
+ }
+
+ if ($width > 320 || $height > 320) {
+ $ph->scaleImage(320);
+ $r = $ph->store(local_user(), $visitor, $hash, $filename, $album, 2, 0, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $desc);
+ if (! $r) {
+ logger("photo upload: image upload with scale 2 (320x320) failed");
+ }
+ }
+ logger("photo upload: new photo upload ended", LOGGER_DEBUG);
+ } elseif ($mediatype == "profileimage") {
+ // upload profile image (scales 4, 5, 6)
+ logger("photo upload: starting new profile image upload", LOGGER_DEBUG);
+
+ if ($width > 175 || $height > 175) {
+ $ph->scaleImage(175);
+ $r = $ph->store(local_user(),$visitor, $hash, $filename, $album, 4, $profile, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $desc);
+ if (! $r) {
+ logger("photo upload: profile image upload with scale 4 (175x175) failed");
+ }
+ }
+
+ if ($width > 80 || $height > 80) {
+ $ph->scaleImage(80);
+ $r = $ph->store(local_user(),$visitor, $hash, $filename, $album, 5, $profile, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $desc);
+ if (! $r) {
+ logger("photo upload: profile image upload with scale 5 (80x80) failed");
+ }
+ }
+
+ if ($width > 48 || $height > 48) {
+ $ph->scaleImage(48);
+ $r = $ph->store(local_user(), $visitor, $hash, $filename, $album, 6, $profile, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $desc);
+ if (! $r) {
+ logger("photo upload: profile image upload with scale 6 (48x48) failed");
+ }
+ }
+ $ph->__destruct();
+ logger("photo upload: new profile image upload ended", LOGGER_DEBUG);
+ }
+
+ if ($r) {
+ // create entry in 'item'-table on new uploads to enable users to comment/like/dislike the photo
+ if ($photo_id == null && $mediatype == "photo") {
+ post_photo_item($hash, $allow_cid, $deny_cid, $allow_gid, $deny_gid, $filetype, $visibility);
+ }
+ // on success return image data in json/xml format (like /api/friendica/photo does when no scale is given)
+ return prepare_photo_data($type, false, $hash);
+ } else {
+ throw new InternalServerErrorException("image upload failed");
+ }
+ }
+
+ function post_photo_item($hash, $allow_cid, $deny_cid, $allow_gid, $deny_gid, $filetype, $visibility = false) {
+ // get data about the api authenticated user
+ $uri = item_new_uri(get_app()->get_hostname(), intval(api_user()));
+ $owner_record = q("SELECT * FROM `contact` WHERE `uid`= %d AND `self` LIMIT 1", intval(api_user()));
+
+ $arr = array();
+ $arr['guid'] = get_guid(32);
+ $arr['uid'] = intval(api_user());
+ $arr['uri'] = $uri;
+ $arr['parent-uri'] = $uri;
+ $arr['type'] = 'photo';
+ $arr['wall'] = 1;
+ $arr['resource-id'] = $hash;
+ $arr['contact-id'] = $owner_record[0]['id'];
+ $arr['owner-name'] = $owner_record[0]['name'];
+ $arr['owner-link'] = $owner_record[0]['url'];
+ $arr['owner-avatar'] = $owner_record[0]['thumb'];
+ $arr['author-name'] = $owner_record[0]['name'];
+ $arr['author-link'] = $owner_record[0]['url'];
+ $arr['author-avatar'] = $owner_record[0]['thumb'];
+ $arr['title'] = "";
+ $arr['allow_cid'] = $allow_cid;
+ $arr['allow_gid'] = $allow_gid;
+ $arr['deny_cid'] = $deny_cid;
+ $arr['deny_gid'] = $deny_gid;
+ $arr['last-child'] = 1;
+ $arr['visible'] = $visibility;
+ $arr['origin'] = 1;
+
+ $typetoext = array(
+ 'image/jpeg' => 'jpg',
+ 'image/png' => 'png',
+ 'image/gif' => 'gif'
+ );
+
+ // adds link to the thumbnail scale photo
+ $arr['body'] = '[url=' . App::get_baseurl() . '/photos/' . $owner_record[0]['name'] . '/image/' . $hash . ']'
+ . '[img]' . App::get_baseurl() . '/photo/' . $hash . '-' . "2" . '.'. $typetoext[$filetype] . '[/img]'
+ . '[/url]';
+
+ // do the magic for storing the item in the database and trigger the federation to other contacts
+ item_store($arr);
+ }
+
+ function prepare_photo_data($type, $scale, $photo_id) {
+ $scale_sql = ($scale === false ? "" : sprintf("AND scale=%d", intval($scale)));