Merge pull request #11043 from annando/api-fixes
API: Added more functions, fixed function names
This commit is contained in:
		
				commit
				
					
						d329f6d6f7
					
				
			
		
					 29 changed files with 623 additions and 422 deletions
				
			
		| 
						 | 
				
			
			@ -220,7 +220,7 @@ Deprecated Twitter sent direct message list endpoint. Returns [Private Messages]
 | 
			
		|||
* `friendica_verbose`: "true" enables different error returns (default: "false")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### POST/PUT api/direct_messages/new
 | 
			
		||||
### POST api/direct_messages/new
 | 
			
		||||
 | 
			
		||||
Deprecated Twitter direct message submission endpoint.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -232,7 +232,7 @@ Deprecated Twitter direct message submission endpoint.
 | 
			
		|||
* `replyto`: ID of the replied direct message
 | 
			
		||||
* `title`: Title of the direct message
 | 
			
		||||
 | 
			
		||||
### POST/DELETE api/direct_messages/destroy
 | 
			
		||||
### POST api/direct_messages/destroy
 | 
			
		||||
 | 
			
		||||
Deprecated Twitter direct message deletion endpoint.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -313,7 +313,7 @@ Array of:
 | 
			
		|||
* `gid`: id of the group
 | 
			
		||||
* `user`: array of [Contacts](help/API-Entities#Contact)
 | 
			
		||||
 | 
			
		||||
### POST/PUT api/friendica/group_create
 | 
			
		||||
### POST api/friendica/group_create
 | 
			
		||||
 | 
			
		||||
Create the group with the posted array of contacts as members.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -366,7 +366,7 @@ Array of:
 | 
			
		|||
* `status`: "missing user" | "ok"
 | 
			
		||||
* `wrong users`: array of users, which were not available in the contact table
 | 
			
		||||
 | 
			
		||||
### POST/DELETE api/friendica/group_delete
 | 
			
		||||
### POST api/friendica/group_delete
 | 
			
		||||
 | 
			
		||||
Delete the specified group of contacts; API call need to include the correct gid AND name of the group to be deleted.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -565,7 +565,7 @@ On error:
 | 
			
		|||
			"unknown error - update photo entry in database failed",
 | 
			
		||||
			"unknown error - this error on uploading or updating a photo should never happen"
 | 
			
		||||
 | 
			
		||||
### DELETE api/friendica/photo/delete
 | 
			
		||||
### POST api/friendica/photo/delete
 | 
			
		||||
 | 
			
		||||
Deletes a single image with the specified id, is not reversible -> ensure that client is asking user for being sure to do this
 | 
			
		||||
Sets item table entries for this photo to deleted = 1.
 | 
			
		||||
| 
						 | 
				
			
			@ -595,7 +595,7 @@ On error:
 | 
			
		|||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
### POST/DELETE api/friendica/photoalbum/delete
 | 
			
		||||
### POST api/friendica/photoalbum/delete
 | 
			
		||||
 | 
			
		||||
Deletes all images with the specified album name, is not reversible -> ensure that client is asking user for being sure to do this.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -622,7 +622,7 @@ On error:
 | 
			
		|||
* 400 BADREQUEST: "no albumname specified", "album not available"
 | 
			
		||||
* 500 INTERNALSERVERERROR: "problem with deleting item occured", "unknown error - deleting from database failed"
 | 
			
		||||
 | 
			
		||||
### POST/PUT api/friendica/photoalbum/update
 | 
			
		||||
### POST api/friendica/photoalbum/update
 | 
			
		||||
 | 
			
		||||
Changes the album name to album_new for all photos in album.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										277
									
								
								include/api.php
									
										
									
									
									
								
							
							
						
						
									
										277
									
								
								include/api.php
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -44,7 +44,6 @@ use Friendica\Network\HTTPException\BadRequestException;
 | 
			
		|||
use Friendica\Network\HTTPException\ForbiddenException;
 | 
			
		||||
use Friendica\Network\HTTPException\InternalServerErrorException;
 | 
			
		||||
use Friendica\Network\HTTPException\NotFoundException;
 | 
			
		||||
use Friendica\Network\HTTPException\TooManyRequestsException;
 | 
			
		||||
use Friendica\Network\HTTPException\UnauthorizedException;
 | 
			
		||||
use Friendica\Object\Image;
 | 
			
		||||
use Friendica\Util\DateTimeFormat;
 | 
			
		||||
| 
						 | 
				
			
			@ -412,9 +411,9 @@ function post_photo_item($hash, $allow_cid, $deny_cid, $allow_gid, $deny_gid, $f
 | 
			
		|||
 | 
			
		||||
	$arr = [];
 | 
			
		||||
	$arr['guid']          = System::createUUID();
 | 
			
		||||
	$arr['uid']           = intval($uid);
 | 
			
		||||
	$arr['uid']           = $uid;
 | 
			
		||||
	$arr['uri']           = $uri;
 | 
			
		||||
	$arr['type']          = 'photo';
 | 
			
		||||
	$arr['post-type']     = Item::PT_IMAGE;
 | 
			
		||||
	$arr['wall']          = 1;
 | 
			
		||||
	$arr['resource-id']   = $hash;
 | 
			
		||||
	$arr['contact-id']    = $owner_record['id'];
 | 
			
		||||
| 
						 | 
				
			
			@ -424,7 +423,7 @@ function post_photo_item($hash, $allow_cid, $deny_cid, $allow_gid, $deny_gid, $f
 | 
			
		|||
	$arr['author-name']   = $owner_record['name'];
 | 
			
		||||
	$arr['author-link']   = $owner_record['url'];
 | 
			
		||||
	$arr['author-avatar'] = $owner_record['thumb'];
 | 
			
		||||
	$arr['title']         = "";
 | 
			
		||||
	$arr['title']         = '';
 | 
			
		||||
	$arr['allow_cid']     = $allow_cid;
 | 
			
		||||
	$arr['allow_gid']     = $allow_gid;
 | 
			
		||||
	$arr['deny_cid']      = $deny_cid;
 | 
			
		||||
| 
						 | 
				
			
			@ -432,11 +431,7 @@ function post_photo_item($hash, $allow_cid, $deny_cid, $allow_gid, $deny_gid, $f
 | 
			
		|||
	$arr['visible']       = $visibility;
 | 
			
		||||
	$arr['origin']        = 1;
 | 
			
		||||
 | 
			
		||||
	$typetoext = [
 | 
			
		||||
			'image/jpeg' => 'jpg',
 | 
			
		||||
			'image/png' => 'png',
 | 
			
		||||
			'image/gif' => 'gif'
 | 
			
		||||
			];
 | 
			
		||||
	$typetoext = Images::supportedTypes();
 | 
			
		||||
 | 
			
		||||
	// adds link to the thumbnail scale photo
 | 
			
		||||
	$arr['body'] = '[url=' . DI::baseUrl() . '/photos/' . $owner_record['nick'] . '/image/' . $hash . ']'
 | 
			
		||||
| 
						 | 
				
			
			@ -646,270 +641,6 @@ function group_create($name, $uid, $users = [])
 | 
			
		|||
 * TWITTER API
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Updates the user’s current status.
 | 
			
		||||
 *
 | 
			
		||||
 * @param string $type Return type (atom, rss, xml, json)
 | 
			
		||||
 *
 | 
			
		||||
 * @return array|string
 | 
			
		||||
 * @throws BadRequestException
 | 
			
		||||
 * @throws ForbiddenException
 | 
			
		||||
 * @throws ImagickException
 | 
			
		||||
 * @throws InternalServerErrorException
 | 
			
		||||
 * @throws TooManyRequestsException
 | 
			
		||||
 * @throws UnauthorizedException
 | 
			
		||||
 * @see https://developer.twitter.com/en/docs/tweets/post-and-engage/api-reference/post-statuses-update
 | 
			
		||||
 */
 | 
			
		||||
function api_statuses_update($type)
 | 
			
		||||
{
 | 
			
		||||
	BaseApi::checkAllowedScope(BaseApi::SCOPE_WRITE);
 | 
			
		||||
	$uid = BaseApi::getCurrentUserID();
 | 
			
		||||
 | 
			
		||||
	$a = DI::app();
 | 
			
		||||
 | 
			
		||||
	// convert $_POST array items to the form we use for web posts.
 | 
			
		||||
	if (!empty($_REQUEST['htmlstatus'])) {
 | 
			
		||||
		$txt = $_REQUEST['htmlstatus'];
 | 
			
		||||
		if ((strpos($txt, '<') !== false) || (strpos($txt, '>') !== false)) {
 | 
			
		||||
			$txt = HTML::toBBCodeVideo($txt);
 | 
			
		||||
 | 
			
		||||
			$config = HTMLPurifier_Config::createDefault();
 | 
			
		||||
			$config->set('Cache.DefinitionImpl', null);
 | 
			
		||||
 | 
			
		||||
			$purifier = new HTMLPurifier($config);
 | 
			
		||||
			$txt = $purifier->purify($txt);
 | 
			
		||||
 | 
			
		||||
			$_REQUEST['body'] = HTML::toBBCode($txt);
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		$_REQUEST['body'] = $_REQUEST['status'] ?? null;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	$_REQUEST['title'] = $_REQUEST['title'] ?? null;
 | 
			
		||||
 | 
			
		||||
	$parent = $_REQUEST['in_reply_to_status_id'] ?? null;
 | 
			
		||||
 | 
			
		||||
	// Twidere sends "-1" if it is no reply ...
 | 
			
		||||
	if ($parent == -1) {
 | 
			
		||||
		$parent = "";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (ctype_digit($parent)) {
 | 
			
		||||
		$_REQUEST['parent'] = $parent;
 | 
			
		||||
	} else {
 | 
			
		||||
		$_REQUEST['parent_uri'] = $parent;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!empty($_REQUEST['lat']) && !empty($_REQUEST['long'])) {
 | 
			
		||||
		$_REQUEST['coord'] = sprintf("%s %s", $_REQUEST['lat'], $_REQUEST['long']);
 | 
			
		||||
	}
 | 
			
		||||
	$_REQUEST['profile_uid'] = $uid;
 | 
			
		||||
 | 
			
		||||
	if (!$parent) {
 | 
			
		||||
		// Check for throttling (maximum posts per day, week and month)
 | 
			
		||||
		$throttle_day = DI::config()->get('system', 'throttle_limit_day');
 | 
			
		||||
		if ($throttle_day > 0) {
 | 
			
		||||
			$datefrom = date(DateTimeFormat::MYSQL, time() - 24*60*60);
 | 
			
		||||
 | 
			
		||||
			$condition = ["`gravity` = ? AND `uid` = ? AND `wall` AND `received` > ?", GRAVITY_PARENT, $uid, $datefrom];
 | 
			
		||||
			$posts_day = Post::count($condition);
 | 
			
		||||
 | 
			
		||||
			if ($posts_day > $throttle_day) {
 | 
			
		||||
				logger::info('Daily posting limit reached for user ' . $uid);
 | 
			
		||||
				// die(api_error($type, DI::l10n()->t("Daily posting limit of %d posts reached. The post was rejected.", $throttle_day));
 | 
			
		||||
				throw new TooManyRequestsException(DI::l10n()->tt("Daily posting limit of %d post reached. The post was rejected.", "Daily posting limit of %d posts reached. The post was rejected.", $throttle_day));
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		$throttle_week = DI::config()->get('system', 'throttle_limit_week');
 | 
			
		||||
		if ($throttle_week > 0) {
 | 
			
		||||
			$datefrom = date(DateTimeFormat::MYSQL, time() - 24*60*60*7);
 | 
			
		||||
 | 
			
		||||
			$condition = ["`gravity` = ? AND `uid` = ? AND `wall` AND `received` > ?", GRAVITY_PARENT, $uid, $datefrom];
 | 
			
		||||
			$posts_week = Post::count($condition);
 | 
			
		||||
 | 
			
		||||
			if ($posts_week > $throttle_week) {
 | 
			
		||||
				logger::info('Weekly posting limit reached for user ' . $uid);
 | 
			
		||||
				// die(api_error($type, DI::l10n()->t("Weekly posting limit of %d posts reached. The post was rejected.", $throttle_week)));
 | 
			
		||||
				throw new TooManyRequestsException(DI::l10n()->tt("Weekly posting limit of %d post reached. The post was rejected.", "Weekly posting limit of %d posts reached. The post was rejected.", $throttle_week));
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		$throttle_month = DI::config()->get('system', 'throttle_limit_month');
 | 
			
		||||
		if ($throttle_month > 0) {
 | 
			
		||||
			$datefrom = date(DateTimeFormat::MYSQL, time() - 24*60*60*30);
 | 
			
		||||
 | 
			
		||||
			$condition = ["`gravity` = ? AND `uid` = ? AND `wall` AND `received` > ?", GRAVITY_PARENT, $uid, $datefrom];
 | 
			
		||||
			$posts_month = Post::count($condition);
 | 
			
		||||
 | 
			
		||||
			if ($posts_month > $throttle_month) {
 | 
			
		||||
				logger::info('Monthly posting limit reached for user ' . $uid);
 | 
			
		||||
				// die(api_error($type, DI::l10n()->t("Monthly posting limit of %d posts reached. The post was rejected.", $throttle_month));
 | 
			
		||||
				throw new TooManyRequestsException(DI::l10n()->t("Monthly posting limit of %d post reached. The post was rejected.", "Monthly posting limit of %d posts reached. The post was rejected.", $throttle_month));
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!empty($_REQUEST['media_ids'])) {
 | 
			
		||||
		$ids = explode(',', $_REQUEST['media_ids']);
 | 
			
		||||
	} elseif (!empty($_FILES['media'])) {
 | 
			
		||||
		// upload the image if we have one
 | 
			
		||||
		$picture = Photo::upload($uid, $_FILES['media']);
 | 
			
		||||
		if (is_array($picture)) {
 | 
			
		||||
			$ids[] = $picture['id'];
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	$attachments = [];
 | 
			
		||||
	$ressources = [];
 | 
			
		||||
 | 
			
		||||
	if (!empty($ids)) {
 | 
			
		||||
		foreach ($ids as $id) {
 | 
			
		||||
			$media = DBA::toArray(DBA::p("SELECT `resource-id`, `scale`, `nickname`, `type`, `desc`, `filename`, `datasize`, `width`, `height` FROM `photo`
 | 
			
		||||
					INNER JOIN `user` ON `user`.`uid` = `photo`.`uid` WHERE `resource-id` IN
 | 
			
		||||
						(SELECT `resource-id` FROM `photo` WHERE `id` = ?) AND `photo`.`uid` = ?
 | 
			
		||||
					ORDER BY `photo`.`width` DESC LIMIT 2", $id, $uid));
 | 
			
		||||
 | 
			
		||||
			if (!empty($media)) {
 | 
			
		||||
				$ressources[] = $media[0]['resource-id'];
 | 
			
		||||
				$phototypes = Images::supportedTypes();
 | 
			
		||||
				$ext = $phototypes[$media[0]['type']];
 | 
			
		||||
 | 
			
		||||
				$attachment = ['type' => Post\Media::IMAGE, 'mimetype' => $media[0]['type'],
 | 
			
		||||
					'url' => DI::baseUrl() . '/photo/' . $media[0]['resource-id'] . '-' . $media[0]['scale'] . '.' . $ext,
 | 
			
		||||
					'size' => $media[0]['datasize'],
 | 
			
		||||
					'name' => $media[0]['filename'] ?: $media[0]['resource-id'],
 | 
			
		||||
					'description' => $media[0]['desc'] ?? '',
 | 
			
		||||
					'width' => $media[0]['width'],
 | 
			
		||||
					'height' => $media[0]['height']];
 | 
			
		||||
 | 
			
		||||
				if (count($media) > 1) {
 | 
			
		||||
					$attachment['preview'] = DI::baseUrl() . '/photo/' . $media[1]['resource-id'] . '-' . $media[1]['scale'] . '.' . $ext;
 | 
			
		||||
					$attachment['preview-width'] = $media[1]['width'];
 | 
			
		||||
					$attachment['preview-height'] = $media[1]['height'];
 | 
			
		||||
				}
 | 
			
		||||
				$attachments[] = $attachment;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// We have to avoid that the post is rejected because of an empty body
 | 
			
		||||
		if (empty($_REQUEST['body'])) {
 | 
			
		||||
			$_REQUEST['body'] = '[hr]';
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!empty($attachments)) {
 | 
			
		||||
		$_REQUEST['attachments'] = $attachments;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// set this so that the item_post() function is quiet and doesn't redirect or emit json
 | 
			
		||||
 | 
			
		||||
	$_REQUEST['api_source'] = true;
 | 
			
		||||
 | 
			
		||||
	if (empty($_REQUEST['source'])) {
 | 
			
		||||
		$_REQUEST['source'] = BaseApi::getCurrentApplication()['name'] ?: 'API';
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// call out normal post function
 | 
			
		||||
	$item_id = item_post($a);
 | 
			
		||||
 | 
			
		||||
	if (!empty($ressources) && !empty($item_id)) {
 | 
			
		||||
		$item = Post::selectFirst(['uri-id', 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid'], ['id' => $item_id]);
 | 
			
		||||
		foreach ($ressources as $ressource) {
 | 
			
		||||
			Photo::setPermissionForRessource($ressource, $uid, $item['allow_cid'], $item['allow_gid'], $item['deny_cid'], $item['deny_gid']);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	$include_entities = strtolower(($_REQUEST['include_entities'] ?? 'false') == 'true');
 | 
			
		||||
 | 
			
		||||
	// output the post that we just posted.
 | 
			
		||||
	$status_info = DI::twitterStatus()->createFromItemId($item_id, $uid, $include_entities)->toArray();
 | 
			
		||||
	return DI::apiResponse()->formatData('statuses', $type, ['status' => $status_info]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
api_register_func('api/statuses/update', 'api_statuses_update', true);
 | 
			
		||||
api_register_func('api/statuses/update_with_media', 'api_statuses_update', true);
 | 
			
		||||
api_register_func('api/statuses/mediap', 'api_statuses_update', true);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Repeats a status.
 | 
			
		||||
 *
 | 
			
		||||
 * @param string $type Return type (atom, rss, xml, json)
 | 
			
		||||
 *
 | 
			
		||||
 * @return array|string
 | 
			
		||||
 * @throws BadRequestException
 | 
			
		||||
 * @throws ForbiddenException
 | 
			
		||||
 * @throws ImagickException
 | 
			
		||||
 * @throws InternalServerErrorException
 | 
			
		||||
 * @throws UnauthorizedException
 | 
			
		||||
 * @see https://developer.twitter.com/en/docs/tweets/post-and-engage/api-reference/post-statuses-retweet-id
 | 
			
		||||
 */
 | 
			
		||||
function api_statuses_repeat($type)
 | 
			
		||||
{
 | 
			
		||||
	BaseApi::checkAllowedScope(BaseApi::SCOPE_WRITE);
 | 
			
		||||
	$uid = BaseApi::getCurrentUserID();
 | 
			
		||||
 | 
			
		||||
	// params
 | 
			
		||||
	$id = intval(DI::args()->getArgv()[3] ?? 0);
 | 
			
		||||
 | 
			
		||||
	if ($id == 0) {
 | 
			
		||||
		$id = intval($_REQUEST['id'] ?? 0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Hotot workaround
 | 
			
		||||
	if ($id == 0) {
 | 
			
		||||
		$id = intval(DI::args()->getArgv()[4] ?? 0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logger::notice('API: api_statuses_repeat: ' . $id);
 | 
			
		||||
 | 
			
		||||
	$fields = ['uri-id', 'network', 'body', 'title', 'author-name', 'author-link', 'author-avatar', 'guid', 'created', 'plink'];
 | 
			
		||||
	$item = Post::selectFirst($fields, ['id' => $id, 'private' => [Item::PUBLIC, Item::UNLISTED]]);
 | 
			
		||||
 | 
			
		||||
	if (DBA::isResult($item) && !empty($item['body'])) {
 | 
			
		||||
		if (in_array($item['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::TWITTER])) {
 | 
			
		||||
			if (!Item::performActivity($id, 'announce', $uid)) {
 | 
			
		||||
				throw new InternalServerErrorException();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			$item_id = $id;
 | 
			
		||||
		} else {
 | 
			
		||||
			if (strpos($item['body'], "[/share]") !== false) {
 | 
			
		||||
				$pos = strpos($item['body'], "[share");
 | 
			
		||||
				$post = substr($item['body'], $pos);
 | 
			
		||||
			} else {
 | 
			
		||||
				$post = BBCode::getShareOpeningTag($item['author-name'], $item['author-link'], $item['author-avatar'], $item['plink'], $item['created'], $item['guid']);
 | 
			
		||||
 | 
			
		||||
				if (!empty($item['title'])) {
 | 
			
		||||
					$post .= '[h3]' . $item['title'] . "[/h3]\n";
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				$post .= $item['body'];
 | 
			
		||||
				$post .= "[/share]";
 | 
			
		||||
			}
 | 
			
		||||
			$_REQUEST['body'] = $post;
 | 
			
		||||
			$_REQUEST['profile_uid'] = $uid;
 | 
			
		||||
			$_REQUEST['api_source'] = true;
 | 
			
		||||
 | 
			
		||||
			if (empty($_REQUEST['source'])) {
 | 
			
		||||
				$_REQUEST['source'] = BaseApi::getCurrentApplication()['name'] ?: 'API';
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			$item_id = item_post(DI::app());
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		throw new ForbiddenException();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	$include_entities = strtolower(($_REQUEST['include_entities'] ?? 'false') == 'true');
 | 
			
		||||
 | 
			
		||||
	// output the post that we just posted.
 | 
			
		||||
	$status_info = DI::twitterStatus()->createFromItemId($item_id, $uid, $include_entities)->toArray();
 | 
			
		||||
	return DI::apiResponse()->formatData('statuses', $type, ['status' => $status_info]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
api_register_func('api/statuses/retweet', 'api_statuses_repeat', true);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Returns all lists the user subscribes to.
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -40,7 +40,7 @@ use Friendica\Module\BaseApi;
 | 
			
		|||
 */
 | 
			
		||||
class Activity extends BaseApi
 | 
			
		||||
{
 | 
			
		||||
	protected function rawContent(array $request = [])
 | 
			
		||||
	protected function post(array $request = [])
 | 
			
		||||
	{
 | 
			
		||||
		self::checkAllowedScope(self::SCOPE_WRITE);
 | 
			
		||||
		$uid = self::getCurrentUserID();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,7 +30,7 @@ use Friendica\Module\BaseApi;
 | 
			
		|||
 */
 | 
			
		||||
class Setseen extends BaseApi
 | 
			
		||||
{
 | 
			
		||||
	protected function rawContent(array $request = [])
 | 
			
		||||
	protected function post(array $request = [])
 | 
			
		||||
	{
 | 
			
		||||
		self::checkAllowedScope(self::SCOPE_WRITE);
 | 
			
		||||
		$uid = self::getCurrentUserID();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,7 +32,7 @@ use Friendica\Network\HTTPException\BadRequestException;
 | 
			
		|||
 */
 | 
			
		||||
class Delete extends BaseApi
 | 
			
		||||
{
 | 
			
		||||
	protected function rawContent(array $request = [])
 | 
			
		||||
	protected function post(array $request = [])
 | 
			
		||||
	{
 | 
			
		||||
		self::checkAllowedScope(self::SCOPE_WRITE);
 | 
			
		||||
		$uid = self::getCurrentUserID();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,7 +32,7 @@ use Friendica\Network\HTTPException\BadRequestException;
 | 
			
		|||
 */
 | 
			
		||||
class Update extends BaseApi
 | 
			
		||||
{
 | 
			
		||||
	protected function rawContent(array $request = [])
 | 
			
		||||
	protected function post(array $request = [])
 | 
			
		||||
	{
 | 
			
		||||
		BaseApi::checkAllowedScope(BaseApi::SCOPE_WRITE);
 | 
			
		||||
		$uid = BaseApi::getCurrentUserID();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,7 +38,7 @@ use Friendica\Network\HTTPException\NotFoundException;
 | 
			
		|||
 */
 | 
			
		||||
class Seen extends BaseApi
 | 
			
		||||
{
 | 
			
		||||
	protected function rawContent(array $request = [])
 | 
			
		||||
	protected function post(array $request = [])
 | 
			
		||||
	{
 | 
			
		||||
		BaseApi::checkAllowedScope(BaseApi::SCOPE_WRITE);
 | 
			
		||||
		$uid = BaseApi::getCurrentUserID();
 | 
			
		||||
| 
						 | 
				
			
			@ -71,6 +71,7 @@ class Seen extends BaseApi
 | 
			
		|||
					$ret  = [DI::twitterStatus()->createFromUriId($item['uri-id'], $item['uid'], $include_entities)->toArray()];
 | 
			
		||||
					$data = ['status' => $ret];
 | 
			
		||||
					$this->response->exit('statuses', $data, $this->parameters['extension'] ?? null);
 | 
			
		||||
					return;
 | 
			
		||||
				}
 | 
			
		||||
				// the item can't be found, but we set the notification as seen, so we count this as a success
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,7 +32,7 @@ use Friendica\Network\HTTPException\InternalServerErrorException;
 | 
			
		|||
 */
 | 
			
		||||
class Delete extends BaseApi
 | 
			
		||||
{
 | 
			
		||||
	protected function rawContent(array $request = [])
 | 
			
		||||
	protected function post(array $request = [])
 | 
			
		||||
	{
 | 
			
		||||
		$uid = self::getCurrentUserID();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,7 +34,7 @@ use Friendica\Network\HTTPException\InternalServerErrorException;
 | 
			
		|||
 */
 | 
			
		||||
class Delete extends BaseApi
 | 
			
		||||
{
 | 
			
		||||
	protected function rawContent(array $request = [])
 | 
			
		||||
	protected function post(array $request = [])
 | 
			
		||||
	{
 | 
			
		||||
		self::checkAllowedScope(self::SCOPE_WRITE);
 | 
			
		||||
		$uid = self::getCurrentUserID();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,7 +32,7 @@ use Friendica\Network\HTTPException\InternalServerErrorException;
 | 
			
		|||
 */
 | 
			
		||||
class Update extends BaseApi
 | 
			
		||||
{
 | 
			
		||||
	protected function rawContent(array $request = [])
 | 
			
		||||
	protected function post(array $request = [])
 | 
			
		||||
	{
 | 
			
		||||
		self::checkAllowedScope(self::SCOPE_WRITE);
 | 
			
		||||
		$uid = self::getCurrentUserID();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,7 +32,7 @@ use Friendica\Model\Profile;
 | 
			
		|||
 */
 | 
			
		||||
class UpdateProfile extends BaseApi
 | 
			
		||||
{
 | 
			
		||||
	protected function rawContent(array $request = [])
 | 
			
		||||
	protected function post(array $request = [])
 | 
			
		||||
	{
 | 
			
		||||
		BaseApi::checkAllowedScope(BaseApi::SCOPE_WRITE);
 | 
			
		||||
		$uid = BaseApi::getCurrentUserID();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,7 +31,7 @@ use Friendica\Network\HTTPException\BadRequestException;
 | 
			
		|||
 */
 | 
			
		||||
class Create extends BaseApi
 | 
			
		||||
{
 | 
			
		||||
	protected function rawContent(array $request = [])
 | 
			
		||||
	protected function post(array $request = [])
 | 
			
		||||
	{
 | 
			
		||||
		self::checkAllowedScope(self::SCOPE_WRITE);
 | 
			
		||||
		$uid = self::getCurrentUserID();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,7 +31,7 @@ use Friendica\Network\HTTPException\BadRequestException;
 | 
			
		|||
 */
 | 
			
		||||
class Destroy extends BaseApi
 | 
			
		||||
{
 | 
			
		||||
	protected function rawContent(array $request = [])
 | 
			
		||||
	protected function post(array $request = [])
 | 
			
		||||
	{
 | 
			
		||||
		self::checkAllowedScope(self::SCOPE_WRITE);
 | 
			
		||||
		$uid = self::getCurrentUserID();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,7 +37,7 @@ use Friendica\Network\HTTPException;
 | 
			
		|||
 */
 | 
			
		||||
class Destroy extends ContactEndpoint
 | 
			
		||||
{
 | 
			
		||||
	protected function rawContent(array $request = [])
 | 
			
		||||
	protected function post(array $request = [])
 | 
			
		||||
	{
 | 
			
		||||
		BaseApi::checkAllowedScope(BaseApi::SCOPE_WRITE);
 | 
			
		||||
		$uid = BaseApi::getCurrentUserID();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										118
									
								
								src/Module/Api/Twitter/Friendships/Show.php
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								src/Module/Api/Twitter/Friendships/Show.php
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,118 @@
 | 
			
		|||
<?php
 | 
			
		||||
/**
 | 
			
		||||
 * @copyright Copyright (C) 2010-2021, the Friendica project
 | 
			
		||||
 *
 | 
			
		||||
 * @license GNU AGPL version 3 or any later version
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as
 | 
			
		||||
 * published by the Free Software Foundation, either version 3 of the
 | 
			
		||||
 * License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace Friendica\Module\Api\Twitter\Friendships;
 | 
			
		||||
 | 
			
		||||
use Friendica\Database\DBA;
 | 
			
		||||
use Friendica\DI;
 | 
			
		||||
use Friendica\Model\Contact;
 | 
			
		||||
use Friendica\Module\Api\Twitter\ContactEndpoint;
 | 
			
		||||
use Friendica\Module\BaseApi;
 | 
			
		||||
use Friendica\Network\HTTPException\NotFoundException;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @see https://developer.twitter.com/en/docs/twitter-api/v1/accounts-and-users/follow-search-get-users/api-reference/get-friendships-show
 | 
			
		||||
 */
 | 
			
		||||
class Show extends ContactEndpoint
 | 
			
		||||
{
 | 
			
		||||
	protected function rawContent(array $request = [])
 | 
			
		||||
	{
 | 
			
		||||
		self::checkAllowedScope(self::SCOPE_READ);
 | 
			
		||||
		$uid = BaseApi::getCurrentUserID();
 | 
			
		||||
 | 
			
		||||
		$source_cid = BaseApi::getContactIDForSearchterm($_REQUEST['source_screen_name'] ?? '', '', $_REQUEST['source_id'] ?? 0, $uid);
 | 
			
		||||
 | 
			
		||||
		$target_cid = BaseApi::getContactIDForSearchterm($_REQUEST['target_screen_name'] ?? '', '', $_REQUEST['target_id'] ?? 0, $uid);
 | 
			
		||||
 | 
			
		||||
		$source = Contact::getById($source_cid);
 | 
			
		||||
		if (empty($source)) {
 | 
			
		||||
			throw new NotFoundException('Source not found');
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		$target = Contact::getById($target_cid);
 | 
			
		||||
		if (empty($source)) {
 | 
			
		||||
			throw new NotFoundException('Target not found');
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		$follower  = false;
 | 
			
		||||
		$following = false;
 | 
			
		||||
 | 
			
		||||
		if ($source_cid == Contact::getPublicIdByUserId($uid)) {
 | 
			
		||||
			$cdata = Contact::getPublicAndUserContactID($target_cid, $uid);
 | 
			
		||||
			if (!empty($cdata['user'])) {
 | 
			
		||||
				$usercontact = Contact::getById($cdata['user'], ['rel']);
 | 
			
		||||
				switch ($usercontact['rel'] ?? Contact::NOTHING) {
 | 
			
		||||
					case Contact::FOLLOWER:
 | 
			
		||||
						$follower  = true;
 | 
			
		||||
						$following = false;
 | 
			
		||||
						break;
 | 
			
		||||
 | 
			
		||||
					case Contact::SHARING:
 | 
			
		||||
						$follower  = false;
 | 
			
		||||
						$following = true;
 | 
			
		||||
						break;
 | 
			
		||||
 | 
			
		||||
					case Contact::FRIEND:
 | 
			
		||||
						$follower  = true;
 | 
			
		||||
						$following = true;
 | 
			
		||||
						break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			$follower  = DBA::exists('contact-relation', ['cid' => $source_cid, 'relation-cid' => $target_cid, 'follows' => true]);
 | 
			
		||||
			$following = DBA::exists('contact-relation', ['relation-cid' => $source_cid, 'cid' => $target_cid, 'follows' => true]);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		$relationship = [
 | 
			
		||||
			'relationship' => [
 | 
			
		||||
				'source' => [
 | 
			
		||||
					'id'                    => $source['id'],
 | 
			
		||||
					'id_str'                => (string)$source['id'],
 | 
			
		||||
					'screen_name'           => $source['nick'],
 | 
			
		||||
					'following'             => $following,
 | 
			
		||||
					'followed_by'           => $follower,
 | 
			
		||||
					'live_following'        => false,
 | 
			
		||||
					'following_received'    => null,
 | 
			
		||||
					'following_requested'   => null,
 | 
			
		||||
					'notifications_enabled' => null,
 | 
			
		||||
					'can_dm'                => $following && $follower,
 | 
			
		||||
					'blocking'              => null,
 | 
			
		||||
					'blocked_by'            => null,
 | 
			
		||||
					'muting'                => null,
 | 
			
		||||
					'want_retweets'         => null,
 | 
			
		||||
					'all_replies'           => null,
 | 
			
		||||
					'marked_spam'           => null
 | 
			
		||||
				],
 | 
			
		||||
				'target' => [
 | 
			
		||||
					'id'                  => $target['id'],
 | 
			
		||||
					'id_str'              => (string)$target['id'],
 | 
			
		||||
					'screen_name'         => $target['nick'],
 | 
			
		||||
					'following'           => $follower,
 | 
			
		||||
					'followed_by'         => $following,
 | 
			
		||||
					'following_received'  => null,
 | 
			
		||||
					'following_requested' => null
 | 
			
		||||
				]
 | 
			
		||||
			]
 | 
			
		||||
		];
 | 
			
		||||
 | 
			
		||||
		DI::apiResponse()->exit('relationship', ['relationship' => $relationship], $this->parameters['extension'] ?? null);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -34,7 +34,7 @@ use Friendica\Util\Network;
 | 
			
		|||
 */
 | 
			
		||||
class Create extends BaseApi
 | 
			
		||||
{
 | 
			
		||||
	protected function rawContent(array $request = [])
 | 
			
		||||
	protected function post(array $request = [])
 | 
			
		||||
	{
 | 
			
		||||
		BaseApi::checkAllowedScope(BaseApi::SCOPE_WRITE);
 | 
			
		||||
		$uid = BaseApi::getCurrentUserID();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,7 +35,7 @@ use Friendica\Network\HTTPException\InternalServerErrorException;
 | 
			
		|||
 */
 | 
			
		||||
class Upload extends BaseApi
 | 
			
		||||
{
 | 
			
		||||
	protected function rawContent(array $request = [])
 | 
			
		||||
	protected function post(array $request = [])
 | 
			
		||||
	{
 | 
			
		||||
		BaseApi::checkAllowedScope(BaseApi::SCOPE_WRITE);
 | 
			
		||||
		$uid = BaseApi::getCurrentUserID();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,7 +34,7 @@ use Friendica\Model\Item;
 | 
			
		|||
 */
 | 
			
		||||
class Destroy extends BaseApi
 | 
			
		||||
{
 | 
			
		||||
	protected function rawContent(array $request = [])
 | 
			
		||||
	protected function post(array $request = [])
 | 
			
		||||
	{
 | 
			
		||||
		BaseApi::checkAllowedScope(BaseApi::SCOPE_READ);
 | 
			
		||||
		$uid = BaseApi::getCurrentUserID();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										113
									
								
								src/Module/Api/Twitter/Statuses/Retweet.php
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								src/Module/Api/Twitter/Statuses/Retweet.php
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,113 @@
 | 
			
		|||
<?php
 | 
			
		||||
/**
 | 
			
		||||
 * @copyright Copyright (C) 2010-2021, the Friendica project
 | 
			
		||||
 *
 | 
			
		||||
 * @license GNU AGPL version 3 or any later version
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as
 | 
			
		||||
 * published by the Free Software Foundation, either version 3 of the
 | 
			
		||||
 * License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace Friendica\Module\Api\Twitter\Statuses;
 | 
			
		||||
 | 
			
		||||
use Friendica\Content\Text\BBCode;
 | 
			
		||||
use Friendica\Core\Protocol;
 | 
			
		||||
use Friendica\Database\DBA;
 | 
			
		||||
use Friendica\DI;
 | 
			
		||||
use Friendica\Model\Item;
 | 
			
		||||
use Friendica\Model\Post;
 | 
			
		||||
use Friendica\Model\User;
 | 
			
		||||
use Friendica\Module\BaseApi;
 | 
			
		||||
use Friendica\Network\HTTPException\BadRequestException;
 | 
			
		||||
use Friendica\Network\HTTPException\ForbiddenException;
 | 
			
		||||
use Friendica\Network\HTTPException\InternalServerErrorException;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Repeats a status.
 | 
			
		||||
 *
 | 
			
		||||
 * @see https://developer.twitter.com/en/docs/tweets/post-and-engage/api-reference/post-statuses-retweet-id
 | 
			
		||||
 */
 | 
			
		||||
class Retweet extends BaseApi
 | 
			
		||||
{
 | 
			
		||||
	protected function post(array $request = [])
 | 
			
		||||
	{
 | 
			
		||||
		self::checkAllowedScope(self::SCOPE_WRITE);
 | 
			
		||||
		$uid = self::getCurrentUserID();
 | 
			
		||||
 | 
			
		||||
		$id = $request['id'] ?? 0;
 | 
			
		||||
 | 
			
		||||
		if (empty($id)) {
 | 
			
		||||
			throw new BadRequestException('Item id not specified');
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		$fields = ['uri-id', 'network', 'body', 'title', 'author-name', 'author-link', 'author-avatar', 'guid', 'created', 'plink'];
 | 
			
		||||
		$item   = Post::selectFirst($fields, ['id' => $id, 'private' => [Item::PUBLIC, Item::UNLISTED]]);
 | 
			
		||||
 | 
			
		||||
		if (DBA::isResult($item) && !empty($item['body'])) {
 | 
			
		||||
			if (in_array($item['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::TWITTER])) {
 | 
			
		||||
				if (!Item::performActivity($id, 'announce', $uid)) {
 | 
			
		||||
					throw new InternalServerErrorException();
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				$item_id = $id;
 | 
			
		||||
			} else {
 | 
			
		||||
				if (strpos($item['body'], "[/share]") !== false) {
 | 
			
		||||
					$pos  = strpos($item['body'], "[share");
 | 
			
		||||
					$post = substr($item['body'], $pos);
 | 
			
		||||
				} else {
 | 
			
		||||
					$post = BBCode::getShareOpeningTag($item['author-name'], $item['author-link'], $item['author-avatar'], $item['plink'], $item['created'], $item['guid']);
 | 
			
		||||
 | 
			
		||||
					if (!empty($item['title'])) {
 | 
			
		||||
						$post .= '[h3]' . $item['title'] . "[/h3]\n";
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					$post .= $item['body'];
 | 
			
		||||
					$post .= "[/share]";
 | 
			
		||||
				}
 | 
			
		||||
				$item = [
 | 
			
		||||
					'uid'  => $uid,
 | 
			
		||||
					'body' => $post,
 | 
			
		||||
					'app'  => $request['source'] ?? '',
 | 
			
		||||
				];
 | 
			
		||||
 | 
			
		||||
				$owner = User::getOwnerDataById($uid);
 | 
			
		||||
 | 
			
		||||
				$item['allow_cid'] = $owner['allow_cid'];
 | 
			
		||||
				$item['allow_gid'] = $owner['allow_gid'];
 | 
			
		||||
				$item['deny_cid']  = $owner['deny_cid'];
 | 
			
		||||
				$item['deny_gid']  = $owner['deny_gid'];
 | 
			
		||||
 | 
			
		||||
				if (!empty($item['allow_cid'] . $item['allow_gid'] . $item['deny_cid'] . $item['deny_gid'])) {
 | 
			
		||||
					$item['private'] = Item::PRIVATE;
 | 
			
		||||
				} elseif (DI::pConfig()->get($uid, 'system', 'unlisted')) {
 | 
			
		||||
					$item['private'] = Item::UNLISTED;
 | 
			
		||||
				} else {
 | 
			
		||||
					$item['private'] = Item::PUBLIC;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if (empty($item['app']) && !empty(self::getCurrentApplication()['name'])) {
 | 
			
		||||
					$item['app'] = self::getCurrentApplication()['name'];
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				$item_id = Item::insert($item, true);
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			throw new ForbiddenException();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		$status_info = DI::twitterStatus()->createFromItemId($item_id, $uid)->toArray();
 | 
			
		||||
 | 
			
		||||
		DI::apiResponse()->exit('status', ['status' => $status_info], $this->parameters['extension'] ?? null);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										192
									
								
								src/Module/Api/Twitter/Statuses/Update.php
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								src/Module/Api/Twitter/Statuses/Update.php
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,192 @@
 | 
			
		|||
<?php
 | 
			
		||||
/**
 | 
			
		||||
 * @copyright Copyright (C) 2010-2021, the Friendica project
 | 
			
		||||
 *
 | 
			
		||||
 * @license GNU AGPL version 3 or any later version
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as
 | 
			
		||||
 * published by the Free Software Foundation, either version 3 of the
 | 
			
		||||
 * License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace Friendica\Module\Api\Twitter\Statuses;
 | 
			
		||||
 | 
			
		||||
use Friendica\Content\Text\BBCode;
 | 
			
		||||
use Friendica\Content\Text\HTML;
 | 
			
		||||
use Friendica\Content\Text\Markdown;
 | 
			
		||||
use Friendica\Database\DBA;
 | 
			
		||||
use Friendica\DI;
 | 
			
		||||
use Friendica\Model\Contact;
 | 
			
		||||
use Friendica\Model\Item;
 | 
			
		||||
use Friendica\Model\Photo;
 | 
			
		||||
use Friendica\Model\Post;
 | 
			
		||||
use Friendica\Model\User;
 | 
			
		||||
use Friendica\Module\BaseApi;
 | 
			
		||||
use Friendica\Protocol\Activity;
 | 
			
		||||
use Friendica\Util\Images;
 | 
			
		||||
use HTMLPurifier;
 | 
			
		||||
use HTMLPurifier_Config;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Updates the user’s current status.
 | 
			
		||||
 *
 | 
			
		||||
 * @see https://developer.twitter.com/en/docs/tweets/post-and-engage/api-reference/post-statuses-update
 | 
			
		||||
 */
 | 
			
		||||
class Update extends BaseApi
 | 
			
		||||
{
 | 
			
		||||
	public function post(array $request = [], array $post = [])
 | 
			
		||||
	{
 | 
			
		||||
		self::checkAllowedScope(self::SCOPE_WRITE);
 | 
			
		||||
		$uid = self::getCurrentUserID();
 | 
			
		||||
 | 
			
		||||
		$request = self::getRequest([
 | 
			
		||||
			'htmlstatus'            => '',
 | 
			
		||||
			'status'                => '',
 | 
			
		||||
			'title'                 => '',
 | 
			
		||||
			'in_reply_to_status_id' => 0,
 | 
			
		||||
			'lat'                   => 0,
 | 
			
		||||
			'long'                  => 0,
 | 
			
		||||
			'media_ids'             => '',
 | 
			
		||||
			'source'                => '',
 | 
			
		||||
			'include_entities'      => false,
 | 
			
		||||
		], $request);
 | 
			
		||||
 | 
			
		||||
		$owner = User::getOwnerDataById($uid);
 | 
			
		||||
 | 
			
		||||
		if (!empty($request['htmlstatus'])) {
 | 
			
		||||
			$body = HTML::toBBCodeVideo($request['htmlstatus']);
 | 
			
		||||
 | 
			
		||||
			$config = HTMLPurifier_Config::createDefault();
 | 
			
		||||
			$config->set('Cache.DefinitionImpl', null);
 | 
			
		||||
 | 
			
		||||
			$purifier = new HTMLPurifier($config);
 | 
			
		||||
			$body     = $purifier->purify($body);
 | 
			
		||||
 | 
			
		||||
			$body = HTML::toBBCode($request['htmlstatus']);
 | 
			
		||||
		} else {
 | 
			
		||||
			// The imput is defined as text. So we can use Markdown for some enhancements
 | 
			
		||||
			$body = Markdown::toBBCode($request['status']);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Avoids potential double expansion of existing links
 | 
			
		||||
		$body = BBCode::performWithEscapedTags($body, ['url'], function ($body) {
 | 
			
		||||
			return BBCode::expandTags($body);
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		$item               = [];
 | 
			
		||||
		$item['uid']        = $uid;
 | 
			
		||||
		$item['verb']       = Activity::POST;
 | 
			
		||||
		$item['contact-id'] = $owner['id'];
 | 
			
		||||
		$item['author-id']  = Contact::getPublicIdByUserId($uid);
 | 
			
		||||
		$item['owner-id']   = $item['author-id'];
 | 
			
		||||
		$item['title']      = $request['title'];
 | 
			
		||||
		$item['body']       = $body;
 | 
			
		||||
		$item['app']        = $request['source'];
 | 
			
		||||
 | 
			
		||||
		if (empty($item['app']) && !empty(self::getCurrentApplication()['name'])) {
 | 
			
		||||
			$item['app'] = self::getCurrentApplication()['name'];
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!empty($request['lat']) && !empty($request['long'])) {
 | 
			
		||||
			$item['coord'] = sprintf("%s %s", $request['lat'], $request['long']);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		$item['allow_cid'] = $owner['allow_cid'];
 | 
			
		||||
		$item['allow_gid'] = $owner['allow_gid'];
 | 
			
		||||
		$item['deny_cid']  = $owner['deny_cid'];
 | 
			
		||||
		$item['deny_gid']  = $owner['deny_gid'];
 | 
			
		||||
 | 
			
		||||
		if (!empty($item['allow_cid'] . $item['allow_gid'] . $item['deny_cid'] . $item['deny_gid'])) {
 | 
			
		||||
			$item['private'] = Item::PRIVATE;
 | 
			
		||||
		} elseif (DI::pConfig()->get($uid, 'system', 'unlisted')) {
 | 
			
		||||
			$item['private'] = Item::UNLISTED;
 | 
			
		||||
		} else {
 | 
			
		||||
			$item['private'] = Item::PUBLIC;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if ($request['in_reply_to_status_id']) {
 | 
			
		||||
			$parent = Post::selectFirst(['uri'], ['id' => $request['in_reply_to_status_id'], 'uid' => [0, $uid]]);
 | 
			
		||||
 | 
			
		||||
			$item['thr-parent']  = $parent['uri'];
 | 
			
		||||
			$item['gravity']     = GRAVITY_COMMENT;
 | 
			
		||||
			$item['object-type'] = Activity\ObjectType::COMMENT;
 | 
			
		||||
		} else {
 | 
			
		||||
			self::checkThrottleLimit();
 | 
			
		||||
 | 
			
		||||
			$item['gravity']     = GRAVITY_PARENT;
 | 
			
		||||
			$item['object-type'] = Activity\ObjectType::NOTE;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!empty($_REQUEST['media_ids'])) {
 | 
			
		||||
			$ids = explode(',', $_REQUEST['media_ids']);
 | 
			
		||||
		} elseif (!empty($_FILES['media'])) {
 | 
			
		||||
			// upload the image if we have one
 | 
			
		||||
			$picture = Photo::upload($uid, $_FILES['media']);
 | 
			
		||||
			if (!empty($picture)) {
 | 
			
		||||
				$ids[] = $picture['id'];
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!empty($ids)) {
 | 
			
		||||
			$item['object-type'] = Activity\ObjectType::IMAGE;
 | 
			
		||||
			$item['post-type']   = Item::PT_IMAGE;
 | 
			
		||||
			$item['attachments'] = [];
 | 
			
		||||
 | 
			
		||||
			foreach ($ids as $id) {
 | 
			
		||||
				$media = DBA::toArray(DBA::p("SELECT `resource-id`, `scale`, `type`, `desc`, `filename`, `datasize`, `width`, `height` FROM `photo`
 | 
			
		||||
						WHERE `resource-id` IN (SELECT `resource-id` FROM `photo` WHERE `id` = ?) AND `photo`.`uid` = ?
 | 
			
		||||
						ORDER BY `photo`.`width` DESC LIMIT 2", $id, $uid));
 | 
			
		||||
 | 
			
		||||
				if (empty($media)) {
 | 
			
		||||
					continue;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				Photo::setPermissionForRessource($media[0]['resource-id'], $uid, $item['allow_cid'], $item['allow_gid'], $item['deny_cid'], $item['deny_gid']);
 | 
			
		||||
 | 
			
		||||
				$ressources[] = $media[0]['resource-id'];
 | 
			
		||||
				$phototypes   = Images::supportedTypes();
 | 
			
		||||
				$ext          = $phototypes[$media[0]['type']];
 | 
			
		||||
 | 
			
		||||
				$attachment = [
 | 
			
		||||
					'type'        => Post\Media::IMAGE,
 | 
			
		||||
					'mimetype'    => $media[0]['type'],
 | 
			
		||||
					'url'         => DI::baseUrl() . '/photo/' . $media[0]['resource-id'] . '-' . $media[0]['scale'] . '.' . $ext,
 | 
			
		||||
					'size'        => $media[0]['datasize'],
 | 
			
		||||
					'name'        => $media[0]['filename'] ?: $media[0]['resource-id'],
 | 
			
		||||
					'description' => $media[0]['desc'] ?? '',
 | 
			
		||||
					'width'       => $media[0]['width'],
 | 
			
		||||
					'height'      => $media[0]['height']
 | 
			
		||||
				];
 | 
			
		||||
 | 
			
		||||
				if (count($media) > 1) {
 | 
			
		||||
					$attachment['preview']        = DI::baseUrl() . '/photo/' . $media[1]['resource-id'] . '-' . $media[1]['scale'] . '.' . $ext;
 | 
			
		||||
					$attachment['preview-width']  = $media[1]['width'];
 | 
			
		||||
					$attachment['preview-height'] = $media[1]['height'];
 | 
			
		||||
				}
 | 
			
		||||
				$item['attachments'][] = $attachment;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		$id = Item::insert($item, true);
 | 
			
		||||
		if (!empty($id)) {
 | 
			
		||||
			$item = Post::selectFirst(['uri-id'], ['id' => $id]);
 | 
			
		||||
			if (!empty($item['uri-id'])) {
 | 
			
		||||
				// output the post that we just posted.
 | 
			
		||||
				$status_info = DI::twitterStatus()->createFromUriId($item['uri-id'], $uid, $request['include_entities'])->toArray();
 | 
			
		||||
				DI::apiResponse()->exit('status', ['status' => $status_info], $this->parameters['extension'] ?? null, Contact::getPublicIdByUserId($uid));
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		DI::mstdnError()->InternalError();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -70,6 +70,7 @@ $apiRoutes = [
 | 
			
		|||
	'/friends/list[.{extension:json|xml|rss|atom}]'                => [Module\Api\Twitter\Friends\Lists::class,            [R::GET         ]],
 | 
			
		||||
	'/friendships/destroy[.{extension:json|xml|rss|atom}]'         => [Module\Api\Twitter\Friendships\Destroy::class,      [        R::POST]],
 | 
			
		||||
	'/friendships/incoming[.{extension:json|xml|rss|atom}]'        => [Module\Api\Twitter\Friendships\Incoming::class,     [R::GET         ]],
 | 
			
		||||
	'/friendships/show[.{extension:json|xml|rss|atom}]'            => [Module\Api\Twitter\Friendships\Show::class,         [R::GET         ]],
 | 
			
		||||
 | 
			
		||||
	'/friendica' => [
 | 
			
		||||
		'/activity/{verb:attendmaybe|attendno|attendyes|dislike|like|unattendmaybe|unattendno|unattendyes|undislike|unlike}[.{extension:json|xml|rss|atom}]'
 | 
			
		||||
| 
						 | 
				
			
			@ -118,22 +119,22 @@ $apiRoutes = [
 | 
			
		|||
	'/statusnet/version[.{extension:json|xml|rss|atom}]'               => [Module\Api\GNUSocial\GNUSocial\Version::class,      [R::GET         ]],
 | 
			
		||||
 | 
			
		||||
	'/statuses' => [
 | 
			
		||||
		'/destroy[.{extension:json|xml|rss|atom}]'                 => [Module\Api\Friendica\Index::class,                        [        R::POST]],
 | 
			
		||||
		'/destroy[.{extension:json|xml|rss|atom}]'                 => [Module\Api\Twitter\Statuses\Destroy::class,               [        R::POST]],
 | 
			
		||||
		'/followers[.{extension:json|xml|rss|atom}]'               => [Module\Api\Twitter\Followers\Lists::class,                [R::GET         ]],
 | 
			
		||||
		'/friends[.{extension:json|xml|rss|atom}]'                 => [Module\Api\Twitter\Friends\Lists::class,                  [R::GET         ]],
 | 
			
		||||
		'/friends_timeline[.{extension:json|xml|rss|atom}]'        => [Module\Api\Twitter\Statuses\HomeTimeline::class,          [R::GET         ]],
 | 
			
		||||
		'/home_timeline[.{extension:json|xml|rss|atom}]'           => [Module\Api\Twitter\Statuses\HomeTimeline::class,          [R::GET         ]],
 | 
			
		||||
		'/mediap[.{extension:json|xml|rss|atom}]'                  => [Module\Api\Friendica\Index::class,                        [        R::POST]],
 | 
			
		||||
		'/mediap[.{extension:json|xml|rss|atom}]'                  => [Module\Api\Twitter\Statuses\Update::class,                [        R::POST]],
 | 
			
		||||
		'/mentions[.{extension:json|xml|rss|atom}]'                => [Module\Api\Twitter\Statuses\Mentions::class,              [R::GET         ]],
 | 
			
		||||
		'/mentions_timeline[.{extension:json|xml|rss|atom}]'       => [Module\Api\Twitter\Statuses\Mentions::class,              [R::GET         ]],
 | 
			
		||||
		'/networkpublic_timeline[.{extension:json|xml|rss|atom}]'  => [Module\Api\Twitter\Statuses\NetworkPublicTimeline::class, [R::GET         ]],
 | 
			
		||||
		'/public_timeline[.{extension:json|xml|rss|atom}]'         => [Module\Api\Twitter\Statuses\PublicTimeline::class,        [R::GET         ]],
 | 
			
		||||
		'/replies[.{extension:json|xml|rss|atom}]'                 => [Module\Api\Twitter\Statuses\Mentions::class,              [R::GET         ]],
 | 
			
		||||
		'/retweet[.{extension:json|xml|rss|atom}]'                 => [Module\Api\Friendica\Index::class,                        [        R::POST]],
 | 
			
		||||
		'/retweet[.{extension:json|xml|rss|atom}]'                 => [Module\Api\Twitter\Statuses\Retweet::class,               [        R::POST]],
 | 
			
		||||
		'/show[.{extension:json|xml|rss|atom}]'                    => [Module\Api\Twitter\Statuses\Show::class,                  [R::GET         ]],
 | 
			
		||||
		'/show/{id:\d+}[.{extension:json|xml|rss|atom}]'           => [Module\Api\Twitter\Statuses\Show::class,                  [R::GET         ]],
 | 
			
		||||
		'/update[.{extension:json|xml|rss|atom}]'                  => [Module\Api\Friendica\Index::class,                        [        R::POST]],
 | 
			
		||||
		'/update_with_media[.{extension:json|xml|rss|atom}]'       => [Module\Api\Friendica\Index::class,                        [        R::POST]],
 | 
			
		||||
		'/update[.{extension:json|xml|rss|atom}]'                  => [Module\Api\Twitter\Statuses\Update::class,                [        R::POST]],
 | 
			
		||||
		'/update_with_media[.{extension:json|xml|rss|atom}]'       => [Module\Api\Twitter\Statuses\Update::class,                [        R::POST]],
 | 
			
		||||
		'/user_timeline[.{extension:json|xml|rss|atom}]'           => [Module\Api\Twitter\Statuses\UserTimeline::class,          [R::GET         ]],
 | 
			
		||||
	],
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										11
									
								
								tests/Util/AuthTestConfig.php
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								tests/Util/AuthTestConfig.php
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
<?php
 | 
			
		||||
 | 
			
		||||
namespace Friendica\Test\Util;
 | 
			
		||||
 | 
			
		||||
class AuthTestConfig
 | 
			
		||||
{
 | 
			
		||||
	/** @var bool */
 | 
			
		||||
	public static $authenticated = true;
 | 
			
		||||
	/** @var int  */
 | 
			
		||||
	public static $user_id = 42;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -16,6 +16,6 @@ function authtest_install()
 | 
			
		|||
 | 
			
		||||
function authtest_authenticate($a,&$b)
 | 
			
		||||
{
 | 
			
		||||
	$b['authenticated'] = 1;
 | 
			
		||||
	$b['user_record']   = User::getById(42);
 | 
			
		||||
	$b['authenticated'] = \Friendica\Test\Util\AuthTestConfig::$authenticated;
 | 
			
		||||
	$b['user_record']   = User::getById(\Friendica\Test\Util\AuthTestConfig::$user_id);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,6 +6,7 @@
 | 
			
		|||
namespace Friendica\Test\legacy;
 | 
			
		||||
 | 
			
		||||
use Friendica\App;
 | 
			
		||||
use Friendica\App\Router;
 | 
			
		||||
use Friendica\Core\Config\Capability\IManageConfigValues;
 | 
			
		||||
use Friendica\Core\PConfig\Capability\IManagePersonalConfigValues;
 | 
			
		||||
use Friendica\Core\Protocol;
 | 
			
		||||
| 
						 | 
				
			
			@ -198,25 +199,6 @@ class ApiTest extends FixtureTest
 | 
			
		|||
		// We could probably do more checks here.
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Get the path to a temporary empty PNG image.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return string Path
 | 
			
		||||
	 */
 | 
			
		||||
	private function getTempImage()
 | 
			
		||||
	{
 | 
			
		||||
		$tmpFile = tempnam(sys_get_temp_dir(), 'tmp_file');
 | 
			
		||||
		file_put_contents(
 | 
			
		||||
			$tmpFile,
 | 
			
		||||
			base64_decode(
 | 
			
		||||
			// Empty 1x1 px PNG image
 | 
			
		||||
				'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg=='
 | 
			
		||||
			)
 | 
			
		||||
		);
 | 
			
		||||
 | 
			
		||||
		return $tmpFile;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Test the api_user() function.
 | 
			
		||||
	 *
 | 
			
		||||
| 
						 | 
				
			
			@ -916,6 +898,7 @@ class ApiTest extends FixtureTest
 | 
			
		|||
	 */
 | 
			
		||||
	public function testApiStatusesUpdate()
 | 
			
		||||
	{
 | 
			
		||||
		/*
 | 
			
		||||
		$_REQUEST['status']                = 'Status content #friendica';
 | 
			
		||||
		$_REQUEST['in_reply_to_status_id'] = -1;
 | 
			
		||||
		$_REQUEST['lat']                   = 48;
 | 
			
		||||
| 
						 | 
				
			
			@ -934,6 +917,7 @@ class ApiTest extends FixtureTest
 | 
			
		|||
 | 
			
		||||
		$result = api_statuses_update('json');
 | 
			
		||||
		self::assertStatus($result['status']);
 | 
			
		||||
		*/
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -943,10 +927,12 @@ class ApiTest extends FixtureTest
 | 
			
		|||
	 */
 | 
			
		||||
	public function testApiStatusesUpdateWithHtml()
 | 
			
		||||
	{
 | 
			
		||||
		/*
 | 
			
		||||
		$_REQUEST['htmlstatus'] = '<b>Status content</b>';
 | 
			
		||||
 | 
			
		||||
		$result = api_statuses_update('json');
 | 
			
		||||
		self::assertStatus($result['status']);
 | 
			
		||||
		*/
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -956,10 +942,12 @@ class ApiTest extends FixtureTest
 | 
			
		|||
	 */
 | 
			
		||||
	public function testApiStatusesUpdateWithoutAuthenticatedUser()
 | 
			
		||||
	{
 | 
			
		||||
		/*
 | 
			
		||||
		$this->expectException(\Friendica\Network\HTTPException\UnauthorizedException::class);
 | 
			
		||||
		BasicAuth::setCurrentUserID();
 | 
			
		||||
		$_SESSION['authenticated'] = false;
 | 
			
		||||
		api_statuses_update('json');
 | 
			
		||||
		*/
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -992,74 +980,7 @@ class ApiTest extends FixtureTest
 | 
			
		|||
		$this->markTestIncomplete();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Test the \Friendica\Module\Api\Twitter\Media\Upload module.
 | 
			
		||||
	 * @runInSeparateProcess
 | 
			
		||||
	 * @preserveGlobalState disabled
 | 
			
		||||
	 */
 | 
			
		||||
	public function testApiMediaUpload()
 | 
			
		||||
	{
 | 
			
		||||
		$this->expectException(\Friendica\Network\HTTPException\BadRequestException::class);
 | 
			
		||||
		(new Upload(DI::app(), DI::l10n(), DI::baseUrl(), DI::args(), DI::logger(), DI::profiler(), DI::apiResponse(), $_SERVER))->run();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Test the \Friendica\Module\Api\Twitter\Media\Upload module without an authenticated user.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return void
 | 
			
		||||
	 */
 | 
			
		||||
	public function testApiMediaUploadWithoutAuthenticatedUser()
 | 
			
		||||
	{
 | 
			
		||||
		$this->expectException(\Friendica\Network\HTTPException\UnauthorizedException::class);
 | 
			
		||||
		BasicAuth::setCurrentUserID();
 | 
			
		||||
		$_SESSION['authenticated'] = false;
 | 
			
		||||
		(new Upload(DI::app(), DI::l10n(), DI::baseUrl(), DI::args(), DI::logger(), DI::profiler(), DI::apiResponse(), $_SERVER))->run();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Test the \Friendica\Module\Api\Twitter\Media\Upload module with an invalid uploaded media.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return void
 | 
			
		||||
	 */
 | 
			
		||||
	public function testApiMediaUploadWithMedia()
 | 
			
		||||
	{
 | 
			
		||||
		$this->expectException(\Friendica\Network\HTTPException\InternalServerErrorException::class);
 | 
			
		||||
		$_FILES = [
 | 
			
		||||
			'media' => [
 | 
			
		||||
				'id'       => 666,
 | 
			
		||||
				'tmp_name' => 'tmp_name'
 | 
			
		||||
			]
 | 
			
		||||
		];
 | 
			
		||||
		(new Upload(DI::app(), DI::l10n(), DI::baseUrl(), DI::args(), DI::logger(), DI::profiler(), DI::apiResponse(), $_SERVER))->run();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Test the \Friendica\Module\Api\Twitter\Media\Upload module with an valid uploaded media.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return void
 | 
			
		||||
	 */
 | 
			
		||||
	public function testApiMediaUploadWithValidMedia()
 | 
			
		||||
	{
 | 
			
		||||
		$_FILES    = [
 | 
			
		||||
			'media' => [
 | 
			
		||||
				'id'       => 666,
 | 
			
		||||
				'size'     => 666,
 | 
			
		||||
				'width'    => 666,
 | 
			
		||||
				'height'   => 666,
 | 
			
		||||
				'tmp_name' => $this->getTempImage(),
 | 
			
		||||
				'name'     => 'spacer.png',
 | 
			
		||||
				'type'     => 'image/png'
 | 
			
		||||
			]
 | 
			
		||||
		];
 | 
			
		||||
 | 
			
		||||
		$response = (new Upload(DI::app(), DI::l10n(), DI::baseUrl(), DI::args(), DI::logger(), DI::profiler(), DI::apiResponse(), $_SERVER))->run();
 | 
			
		||||
		$media = json_decode($response->getBody(), true);
 | 
			
		||||
 | 
			
		||||
		self::assertEquals('image/png', $media['image']['image_type']);
 | 
			
		||||
		self::assertEquals(1, $media['image']['w']);
 | 
			
		||||
		self::assertEquals(1, $media['image']['h']);
 | 
			
		||||
		self::assertNotEmpty($media['image']['friendica_preview_url']);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Test the api_statuses_repeat() function.
 | 
			
		||||
| 
						 | 
				
			
			@ -1068,8 +989,8 @@ class ApiTest extends FixtureTest
 | 
			
		|||
	 */
 | 
			
		||||
	public function testApiStatusesRepeat()
 | 
			
		||||
	{
 | 
			
		||||
		$this->expectException(\Friendica\Network\HTTPException\ForbiddenException::class);
 | 
			
		||||
		api_statuses_repeat('json');
 | 
			
		||||
		// $this->expectException(\Friendica\Network\HTTPException\ForbiddenException::class);
 | 
			
		||||
		// api_statuses_repeat('json');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -1079,10 +1000,10 @@ class ApiTest extends FixtureTest
 | 
			
		|||
	 */
 | 
			
		||||
	public function testApiStatusesRepeatWithoutAuthenticatedUser()
 | 
			
		||||
	{
 | 
			
		||||
		$this->expectException(\Friendica\Network\HTTPException\UnauthorizedException::class);
 | 
			
		||||
		BasicAuth::setCurrentUserID();
 | 
			
		||||
		$_SESSION['authenticated'] = false;
 | 
			
		||||
		api_statuses_repeat('json');
 | 
			
		||||
		// $this->expectException(\Friendica\Network\HTTPException\UnauthorizedException::class);
 | 
			
		||||
		// BasicAuth::setCurrentUserID();
 | 
			
		||||
		// $_SESSION['authenticated'] = false;
 | 
			
		||||
		// api_statuses_repeat('json');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -1092,14 +1013,14 @@ class ApiTest extends FixtureTest
 | 
			
		|||
	 */
 | 
			
		||||
	public function testApiStatusesRepeatWithId()
 | 
			
		||||
	{
 | 
			
		||||
		DI::args()->setArgv(['', '', '', 1]);
 | 
			
		||||
		$result = api_statuses_repeat('json');
 | 
			
		||||
		self::assertStatus($result['status']);
 | 
			
		||||
		// DI::args()->setArgv(['', '', '', 1]);
 | 
			
		||||
		// $result = api_statuses_repeat('json');
 | 
			
		||||
		// self::assertStatus($result['status']);
 | 
			
		||||
 | 
			
		||||
		// Also test with a shared status
 | 
			
		||||
		DI::args()->setArgv(['', '', '', 5]);
 | 
			
		||||
		$result = api_statuses_repeat('json');
 | 
			
		||||
		self::assertStatus($result['status']);
 | 
			
		||||
		// DI::args()->setArgv(['', '', '', 5]);
 | 
			
		||||
		// $result = api_statuses_repeat('json');
 | 
			
		||||
		// self::assertStatus($result['status']);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,9 +27,11 @@ use Friendica\Core\Hook;
 | 
			
		|||
use Friendica\Database\Database;
 | 
			
		||||
use Friendica\DI;
 | 
			
		||||
use Friendica\Security\Authentication;
 | 
			
		||||
use Friendica\Security\BasicAuth;
 | 
			
		||||
use Friendica\Test\FixtureTest;
 | 
			
		||||
use Friendica\Test\Util\AppDouble;
 | 
			
		||||
use Friendica\Test\Util\AuthenticationDouble;
 | 
			
		||||
use Friendica\Test\Util\AuthTestConfig;
 | 
			
		||||
 | 
			
		||||
abstract class ApiTest extends FixtureTest
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -60,9 +62,19 @@ abstract class ApiTest extends FixtureTest
 | 
			
		|||
		// Manual override to bypass API authentication
 | 
			
		||||
		DI::app()->setIsLoggedIn(true);
 | 
			
		||||
 | 
			
		||||
		AuthTestConfig::$authenticated = true;
 | 
			
		||||
		AuthTestConfig::$user_id = 42;
 | 
			
		||||
 | 
			
		||||
		$this->installAuthTest();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	protected function tearDown(): void
 | 
			
		||||
	{
 | 
			
		||||
		BasicAuth::setCurrentUserID();
 | 
			
		||||
 | 
			
		||||
		parent::tearDown(); // TODO: Change the autogenerated stub
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * installs auththest.
 | 
			
		||||
	 *
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -67,7 +67,7 @@ class DeleteTest extends ApiTest
 | 
			
		|||
	{
 | 
			
		||||
		$this->loadFixture(__DIR__ . '/../../../../../datasets/photo/photo.fixture.php', DI::dba());
 | 
			
		||||
 | 
			
		||||
		$delete   = new Delete(DI::app(), DI::l10n(), DI::baseUrl(), DI::args(), DI::logger(), DI::profiler(), DI::apiResponse(), ['REQUEST_METHOD' => Router::DELETE]);
 | 
			
		||||
		$delete   = new Delete(DI::app(), DI::l10n(), DI::baseUrl(), DI::args(), DI::logger(), DI::profiler(), DI::apiResponse(), ['REQUEST_METHOD' => Router::POST]);
 | 
			
		||||
		$response = $delete->run(['photo_id' => '709057080661a283a6aa598501504178']);
 | 
			
		||||
 | 
			
		||||
		$responseText = (string)$response->getBody();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -46,7 +46,7 @@ class DeleteTest extends ApiTest
 | 
			
		|||
	{
 | 
			
		||||
		$this->loadFixture(__DIR__ . '/../../../../../datasets/photo/photo.fixture.php', DI::dba());
 | 
			
		||||
 | 
			
		||||
		$delete   = new Delete(DI::app(), DI::l10n(), DI::baseUrl(), DI::args(), DI::logger(), DI::profiler(), DI::apiResponse(), ['REQUEST_METHOD' => Router::DELETE]);
 | 
			
		||||
		$delete   = new Delete(DI::app(), DI::l10n(), DI::baseUrl(), DI::args(), DI::logger(), DI::profiler(), DI::apiResponse(), ['REQUEST_METHOD' => Router::POST]);
 | 
			
		||||
		$response = $delete->run(['album' => 'test_album']);
 | 
			
		||||
 | 
			
		||||
		$responseText = (string)$response->getBody();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										101
									
								
								tests/src/Module/Api/Twitter/Media/UploadTest.php
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								tests/src/Module/Api/Twitter/Media/UploadTest.php
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,101 @@
 | 
			
		|||
<?php
 | 
			
		||||
 | 
			
		||||
namespace Friendica\Test\src\Module\Api\Twitter\Media;
 | 
			
		||||
 | 
			
		||||
use Friendica\App\Router;
 | 
			
		||||
use Friendica\DI;
 | 
			
		||||
use Friendica\Module\Api\Twitter\Media\Upload;
 | 
			
		||||
use Friendica\Network\HTTPException\BadRequestException;
 | 
			
		||||
use Friendica\Network\HTTPException\InternalServerErrorException;
 | 
			
		||||
use Friendica\Network\HTTPException\UnauthorizedException;
 | 
			
		||||
use Friendica\Test\src\Module\Api\ApiTest;
 | 
			
		||||
use Friendica\Test\Util\AuthTestConfig;
 | 
			
		||||
 | 
			
		||||
class UploadTest extends ApiTest
 | 
			
		||||
{
 | 
			
		||||
	/**
 | 
			
		||||
	 * Test the \Friendica\Module\Api\Twitter\Media\Upload module.
 | 
			
		||||
	 */
 | 
			
		||||
	public function testApiMediaUpload()
 | 
			
		||||
	{
 | 
			
		||||
		$this->expectException(BadRequestException::class);
 | 
			
		||||
		$upload = new Upload(DI::app(), DI::l10n(), DI::baseUrl(), DI::args(), DI::logger(), DI::profiler(), DI::apiResponse(), ['REQUEST_METHOD' => Router::POST]);
 | 
			
		||||
		$upload->run();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Test the \Friendica\Module\Api\Twitter\Media\Upload module without an authenticated user.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return void
 | 
			
		||||
	 */
 | 
			
		||||
	public function testApiMediaUploadWithoutAuthenticatedUser()
 | 
			
		||||
	{
 | 
			
		||||
		$this->expectException(UnauthorizedException::class);
 | 
			
		||||
		AuthTestConfig::$authenticated = false;
 | 
			
		||||
		(new Upload(DI::app(), DI::l10n(), DI::baseUrl(), DI::args(), DI::logger(), DI::profiler(), DI::apiResponse(), ['REQUEST_METHOD' => Router::POST]))->run();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Test the \Friendica\Module\Api\Twitter\Media\Upload module with an invalid uploaded media.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return void
 | 
			
		||||
	 */
 | 
			
		||||
	public function testApiMediaUploadWithMedia()
 | 
			
		||||
	{
 | 
			
		||||
		$this->expectException(InternalServerErrorException::class);
 | 
			
		||||
		$_FILES = [
 | 
			
		||||
			'media' => [
 | 
			
		||||
				'id'       => 666,
 | 
			
		||||
				'tmp_name' => 'tmp_name'
 | 
			
		||||
			]
 | 
			
		||||
		];
 | 
			
		||||
		(new Upload(DI::app(), DI::l10n(), DI::baseUrl(), DI::args(), DI::logger(), DI::profiler(), DI::apiResponse(), ['REQUEST_METHOD' => Router::POST]))->run();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Test the \Friendica\Module\Api\Twitter\Media\Upload module with an valid uploaded media.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return void
 | 
			
		||||
	 */
 | 
			
		||||
	public function testApiMediaUploadWithValidMedia()
 | 
			
		||||
	{
 | 
			
		||||
		$_FILES = [
 | 
			
		||||
			'media' => [
 | 
			
		||||
				'id'       => 666,
 | 
			
		||||
				'size'     => 666,
 | 
			
		||||
				'width'    => 666,
 | 
			
		||||
				'height'   => 666,
 | 
			
		||||
				'tmp_name' => $this->getTempImage(),
 | 
			
		||||
				'name'     => 'spacer.png',
 | 
			
		||||
				'type'     => 'image/png'
 | 
			
		||||
			]
 | 
			
		||||
		];
 | 
			
		||||
 | 
			
		||||
		$response = (new Upload(DI::app(), DI::l10n(), DI::baseUrl(), DI::args(), DI::logger(), DI::profiler(), DI::apiResponse(), ['REQUEST_METHOD' => Router::POST]))->run();
 | 
			
		||||
		$media    = json_decode($response->getBody(), true);
 | 
			
		||||
 | 
			
		||||
		self::assertEquals('image/png', $media['image']['image_type']);
 | 
			
		||||
		self::assertEquals(1, $media['image']['w']);
 | 
			
		||||
		self::assertEquals(1, $media['image']['h']);
 | 
			
		||||
		self::assertNotEmpty($media['image']['friendica_preview_url']);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Get the path to a temporary empty PNG image.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return string Path
 | 
			
		||||
	 */
 | 
			
		||||
	private function getTempImage()
 | 
			
		||||
	{
 | 
			
		||||
		$tmpFile = tempnam(sys_get_temp_dir(), 'tmp_file');
 | 
			
		||||
		file_put_contents(
 | 
			
		||||
			$tmpFile,
 | 
			
		||||
			base64_decode(
 | 
			
		||||
			// Empty 1x1 px PNG image
 | 
			
		||||
				'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg=='
 | 
			
		||||
			)
 | 
			
		||||
		);
 | 
			
		||||
 | 
			
		||||
		return $tmpFile;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -8,7 +8,7 @@ msgid ""
 | 
			
		|||
msgstr ""
 | 
			
		||||
"Project-Id-Version: 2021.12-rc\n"
 | 
			
		||||
"Report-Msgid-Bugs-To: \n"
 | 
			
		||||
"POT-Creation-Date: 2021-11-29 06:06-0500\n"
 | 
			
		||||
"POT-Creation-Date: 2021-11-30 05:49+0000\n"
 | 
			
		||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 | 
			
		||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 | 
			
		||||
"Language-Team: LANGUAGE <LL@li.org>\n"
 | 
			
		||||
| 
						 | 
				
			
			@ -18,25 +18,6 @@ msgstr ""
 | 
			
		|||
"Content-Transfer-Encoding: 8bit\n"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#: include/api.php:720 src/Module/BaseApi.php:281
 | 
			
		||||
#, php-format
 | 
			
		||||
msgid "Daily posting limit of %d post reached. The post was rejected."
 | 
			
		||||
msgid_plural "Daily posting limit of %d posts reached. The post was rejected."
 | 
			
		||||
msgstr[0] ""
 | 
			
		||||
msgstr[1] ""
 | 
			
		||||
 | 
			
		||||
#: include/api.php:734 src/Module/BaseApi.php:297
 | 
			
		||||
#, php-format
 | 
			
		||||
msgid "Weekly posting limit of %d post reached. The post was rejected."
 | 
			
		||||
msgid_plural "Weekly posting limit of %d posts reached. The post was rejected."
 | 
			
		||||
msgstr[0] ""
 | 
			
		||||
msgstr[1] ""
 | 
			
		||||
 | 
			
		||||
#: include/api.php:748 src/Module/BaseApi.php:313
 | 
			
		||||
#, php-format
 | 
			
		||||
msgid "Monthly posting limit of %d post reached. The post was rejected."
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: mod/cal.php:44 mod/cal.php:48 mod/follow.php:39 mod/redir.php:34
 | 
			
		||||
#: mod/redir.php:175 src/Module/Conversation/Community.php:181
 | 
			
		||||
#: src/Module/Debug/ItemBody.php:37 src/Module/Diaspora/Receive.php:57
 | 
			
		||||
| 
						 | 
				
			
			@ -6957,6 +6938,25 @@ msgstr ""
 | 
			
		|||
msgid "Too Many Requests"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: src/Module/BaseApi.php:281
 | 
			
		||||
#, php-format
 | 
			
		||||
msgid "Daily posting limit of %d post reached. The post was rejected."
 | 
			
		||||
msgid_plural "Daily posting limit of %d posts reached. The post was rejected."
 | 
			
		||||
msgstr[0] ""
 | 
			
		||||
msgstr[1] ""
 | 
			
		||||
 | 
			
		||||
#: src/Module/BaseApi.php:297
 | 
			
		||||
#, php-format
 | 
			
		||||
msgid "Weekly posting limit of %d post reached. The post was rejected."
 | 
			
		||||
msgid_plural "Weekly posting limit of %d posts reached. The post was rejected."
 | 
			
		||||
msgstr[0] ""
 | 
			
		||||
msgstr[1] ""
 | 
			
		||||
 | 
			
		||||
#: src/Module/BaseApi.php:313
 | 
			
		||||
#, php-format
 | 
			
		||||
msgid "Monthly posting limit of %d post reached. The post was rejected."
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: src/Module/BaseProfile.php:51 src/Module/Contact.php:460
 | 
			
		||||
msgid "Profile Details"
 | 
			
		||||
msgstr ""
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue