From: Christian Weiske Date: Tue, 20 Sep 2016 08:22:12 +0000 (+0200) Subject: add support for media endpoints X-Git-Tag: v0.2.0~6 X-Git-Url: https://git.cweiske.de/shpub.git/commitdiff_plain/96196413bef5ffb4ec3e09770b4e6847090dd71e add support for media endpoints --- diff --git a/src/shpub/Command/AbstractProps.php b/src/shpub/Command/AbstractProps.php index b581f3c..d939aa7 100644 --- a/src/shpub/Command/AbstractProps.php +++ b/src/shpub/Command/AbstractProps.php @@ -40,6 +40,15 @@ class Command_AbstractProps 'default' => [], ) ); + $cmd->addOption( + 'direct_upload', + array( + 'long_name' => '--direct-upload', + 'description' => 'Ignore media endpoint at file upload', + 'action' => 'StoreTrue', + 'default' => false, + ) + ); $cmd->addOption( 'name', array( @@ -170,6 +179,7 @@ class Command_AbstractProps ); } + $req->setDirectUpload($cmdRes->options['direct_upload']); $this->handleFiles($cmdRes, $req); if (count($cmdRes->options['x'])) { @@ -232,24 +242,15 @@ class Command_AbstractProps if ($type == 'image') { $type = 'photo'; } - if (count($urls) == 1) { - $req->req->addPostParameter($type, reset($urls)); - } else if (count($urls) > 1) { - $n = 0; - foreach ($urls as $url) { - $req->req->addPostParameter( - $type . '[' . $n++ . ']', $url - ); - } + if (count($urls) > 0) { + $req->addProperty($type, $urls); } } foreach ($fileList as $type => $filePaths) { if ($type == 'image') { $type = 'photo'; } - if (count($filePaths) == 1) { - $req->addUpload($type, reset($filePaths)); - } else if (count($filePaths) > 0) { + if (count($filePaths) > 0) { $req->addUpload($type, $filePaths); } } diff --git a/src/shpub/Config/Endpoints.php b/src/shpub/Config/Endpoints.php index bc0510e..c343550 100644 --- a/src/shpub/Config/Endpoints.php +++ b/src/shpub/Config/Endpoints.php @@ -86,6 +86,44 @@ class Config_Endpoints ); } + public function discoverMedia($accessToken) + { + $configUrl = $this->micropub; + if (strpos($configUrl, '?') === false) { + $configUrl .= '?q=config'; + } else { + $configUrl .= '&q=config'; + } + + $ctx = stream_context_create( + [ + 'http' => [ + 'header' => [ + 'Accept: application/json', + 'Authorization: Bearer ' . $accessToken + ], + ] + ] + ); + $json = @file_get_contents($configUrl, false, $ctx); + if ($json === false) { + //does not exist + return; + } + $configData = json_decode($json); + if ($configData === null) { + //json could not be decoded + Log::err('micropub configuration is invalid'); + return; + } + if (isset($configData->{'media-endpoint'})) { + $configUrlObj = new \Net_URL2($configUrl); + $this->media = (string) $configUrlObj->resolve( + $configData->{'media-endpoint'} + ); + } + } + public function load($server) { $file = $this->getCacheFilePath($server, false); diff --git a/src/shpub/Config/Host.php b/src/shpub/Config/Host.php index 51d0ff5..74dd523 100644 --- a/src/shpub/Config/Host.php +++ b/src/shpub/Config/Host.php @@ -49,7 +49,10 @@ class Config_Host $this->endpoints->load($this->server); if ($this->endpoints->incomplete()) { $this->endpoints->discover($this->server); - $this->endpoints->save($this->server); + if ($this->token) { + $this->endpoints->discoverMedia($this->token); + } + //$this->endpoints->save($this->server); } } } diff --git a/src/shpub/CurlPrinter.php b/src/shpub/CurlPrinter.php new file mode 100644 index 0000000..cd2ccaa --- /dev/null +++ b/src/shpub/CurlPrinter.php @@ -0,0 +1,59 @@ +getMethod() != 'GET') { + $command .= ' -X ' . $httpReq->getMethod(); + } + foreach ($httpReq->getHeaders() as $key => $val) { + $caseKey = implode('-', array_map('ucfirst', explode('-', $key))); + $command .= ' -H ' . escapeshellarg($caseKey . ': ' . $val); + } + + $postParams = $httpReq->getPostParams(); + + if (count($uploadsInfo) == 0) { + foreach ($postParams as $k => $v) { + if (!is_array($v)) { + $command .= ' -d ' . escapeshellarg($k . '=' . $v); + } else { + foreach ($v as $ak => $av) { + $command .= ' -d ' . escapeshellarg( + $k . '[' . $ak . ']=' . $av + ); + } + } + } + } else { + foreach ($postParams as $k => $v) { + $command .= ' -F ' . escapeshellarg($k . '=' . $v); + } + foreach ($uploadsInfo as $fieldName => $fileName) { + if (!is_array($fileName)) { + $command .= ' -F ' . escapeshellarg( + $fieldName . '=@' . $fileName + ); + } else { + foreach ($fileName as $k => $realFilename) { + $command .= ' -F ' . escapeshellarg( + $fieldName . '[' . $k . ']=@' . $realFilename + ); + } + } + } + } + + if ($dedicatedBody) { + $command .= ' --data ' . escapeshellarg($httpReq->getBody()); + } + + $command .= ' ' . escapeshellarg((string) $httpReq->getUrl()); + + Log::msg($command); + } +} +?> diff --git a/src/shpub/Request.php b/src/shpub/Request.php index 544962b..3a4512e 100644 --- a/src/shpub/Request.php +++ b/src/shpub/Request.php @@ -5,8 +5,10 @@ class Request { public $req; public $cfg; + public $host; protected $sendAsJson = false; + protected $directUpload = false; protected $uploadsInfo = []; protected $dedicatedBody = false; @@ -17,16 +19,25 @@ class Request public function __construct($host, $cfg) { - $this->cfg = $cfg; - $this->req = new MyHttpRequest2($host->endpoints->micropub, 'POST'); - $this->req->setHeader('User-Agent: shpub'); + $this->cfg = $cfg; + $this->host = $host; + $this->req = $this->getHttpRequest( + $this->host->endpoints->micropub, $this->host->token + ); + } + + protected function getHttpRequest($url, $accessToken) + { + $req = new MyHttpRequest2($url, 'POST'); + $req->setHeader('User-Agent: shpub'); if (version_compare(PHP_VERSION, '5.6.0', '<')) { //correct ssl validation on php 5.5 is a pain, so disable - $this->req->setConfig('ssl_verify_host', false); - $this->req->setConfig('ssl_verify_peer', false); + $req->setConfig('ssl_verify_host', false); + $req->setConfig('ssl_verify_peer', false); } - $this->req->setHeader('Content-type', 'application/x-www-form-urlencoded'); - $this->req->setHeader('authorization', 'Bearer ' . $host->token); + $req->setHeader('Content-type', 'application/x-www-form-urlencoded'); + $req->setHeader('authorization', 'Bearer ' . $accessToken); + return $req; } public function send($body = null) @@ -79,7 +90,8 @@ class Request $this->req->setBody($body); } if ($this->cfg->debug) { - $this->printCurl(); + $cp = new CurlPrinter(); + $cp->show($this->req, $this->uploadsInfo, $this->dedicatedBody); } $res = $this->req->send(); @@ -109,17 +121,68 @@ class Request } /** - * @param string $fieldName name of file-upload field - * @param string|resource|array $filename full name of local file, - * pointer to open file or an array of files + * @param string $fieldName name of file-upload field + * @param array $fileNames list of local file paths */ - public function addUpload($fieldName, $filename) + public function addUpload($fieldName, $fileNames) { - if ($this->sendAsJson) { - throw new \Exception('File uploads do not work with JSON'); + if ($this->host->endpoints->media === null + || $this->directUpload + ) { + if ($this->sendAsJson) { + throw new \Exception( + 'No media endpoint available, which is required for JSON' + ); + } + if (count($fileNames) == 1) { + $fileNames = reset($fileNames); + } + $this->uploadsInfo[$fieldName] = $fileNames; + return $this->req->addUpload($fieldName, $fileNames); } - $this->uploadsInfo[$fieldName] = $filename; - return $this->req->addUpload($fieldName, $filename); + + $urls = []; + foreach ($fileNames as $fileName) { + $urls[] = $this->uploadToMediaEndpoint($fileName); + } + if (count($urls) == 1) { + $urls = reset($urls); + } + $this->addProperty($fieldName, $urls); + } + + /** + * @return string URL at media endpoint + */ + protected function uploadToMediaEndpoint($fileName) + { + $httpReq = $this->getHttpRequest( + $this->host->endpoints->media, $this->host->token + ); + $httpReq->addUpload('file', $fileName); + + if ($this->cfg->debug) { + $cp = new CurlPrinter(); + $cp->show($httpReq, ['file' => $fileName]); + } + $res = $httpReq->send(); + if (intval($res->getStatus() / 100) != 2) { + Log::err( + 'Media endpoint returned an error status code ' + . $res->getStatus() + ); + Log::err($res->getBody()); + exit(11); + } + + $location = $res->getHeader('location'); + if ($location === null) { + Log::err('Media endpoint did not return a URL'); + exit(11); + } + + $base = new \Net_URL2($this->host->endpoints->media); + return (string) $base->resolve($location); } public function addContent($text, $isHtml) @@ -145,61 +208,13 @@ class Request $this->properties[$key] = (array) $values; } - protected function printCurl() + public function setSendAsJson($json) { - $command = 'curl'; - if ($this->req->getMethod() != 'GET') { - $command .= ' -X ' . $this->req->getMethod(); - } - foreach ($this->req->getHeaders() as $key => $val) { - $caseKey = implode('-', array_map('ucfirst', explode('-', $key))); - $command .= ' -H ' . escapeshellarg($caseKey . ': ' . $val); - } - - $postParams = $this->req->getPostParams(); - - if (count($this->uploadsInfo) == 0) { - foreach ($postParams as $k => $v) { - if (!is_array($v)) { - $command .= ' -d ' . escapeshellarg($k . '=' . $v); - } else { - foreach ($v as $ak => $av) { - $command .= ' -d ' . escapeshellarg( - $k . '[' . $ak . ']=' . $av - ); - } - } - } - } else { - foreach ($postParams as $k => $v) { - $command .= ' -F ' . escapeshellarg($k . '=' . $v); - } - foreach ($this->uploadsInfo as $fieldName => $filename) { - if (!is_array($filename)) { - $command .= ' -F ' . escapeshellarg( - $fieldName . '=@' . $filename - ); - } else { - foreach ($filename as $k => $realFilename) { - $command .= ' -F ' . escapeshellarg( - $fieldName . '[' . $k . ']=@' . $realFilename - ); - } - } - } - } - - if ($this->dedicatedBody) { - $command .= ' --data ' . escapeshellarg($this->req->getBody()); - } - - $command .= ' ' . escapeshellarg((string) $this->req->getUrl()); - - Log::msg($command); + $this->sendAsJson = $json; } - public function setSendAsJson($json) + public function setDirectUpload($directUpload) { - $this->sendAsJson = $json; + $this->directUpload = $directUpload; } } \ No newline at end of file