Merge pull request #9411 from tobiasd/20201011-de
authorMichael Vogel <icarus@dabo.de>
Sun, 11 Oct 2020 15:03:19 +0000 (17:03 +0200)
committerGitHub <noreply@github.com>
Sun, 11 Oct 2020 15:03:19 +0000 (17:03 +0200)
DE translation update THX  copiis

29 files changed:
composer.json
composer.lock
include/conversation.php
mod/parse_url.php
src/Content/Text/BBCode.php
src/Model/GServer.php
src/Model/Photo.php
src/Model/User.php
src/Network/CurlResult.php
src/Network/GuzzleResponse.php [new file with mode: 0644]
src/Network/HTTPRequest.php
src/Network/IHTTPRequest.php
src/Network/IHTTPResult.php [new file with mode: 0644]
src/Network/Probe.php
src/Object/Post.php
src/Protocol/DFRN.php
src/Protocol/OStatus.php
src/Protocol/Salmon.php
src/Util/Images.php
src/Util/ParseUrl.php
tests/datasets/curl/about.head.php [new file with mode: 0644]
tests/datasets/curl/about.redirect.php [new file with mode: 0644]
tests/src/Core/InstallerTest.php
tests/src/Network/CurlResultTest.php
view/templates/wall_thread.tpl
view/theme/frio/templates/wall_thread.tpl
view/theme/quattro/templates/wall_thread.tpl
view/theme/smoothly/templates/wall_thread.tpl
view/theme/vier/templates/wall_thread.tpl

index efd4bd0..0821615 100644 (file)
@@ -31,6 +31,7 @@
                "divineomega/password_exposed": "^2.8",
                "ezyang/htmlpurifier": "^4.7",
                "friendica/json-ld": "^1.0",
+               "guzzlehttp/guzzle": "^6.5",
                "league/html-to-markdown": "^4.8",
                "level-2/dice": "^4",
                "lightopenid/lightopenid": "dev-master",
index 8ba31ec..02a3122 100644 (file)
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "fd22bd8c29dcea3d6b6eeb117d79af52",
+    "content-hash": "6c141820d2160b278dffecc3e89f86e3",
     "packages": [
         {
             "name": "asika/simple-console",
index c2226fa..a0d1177 100644 (file)
@@ -562,9 +562,10 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
                                ];
 
                                $likebuttons = [
-                                       'like'    => null,
-                                       'dislike' => null,
-                                       'share'   => null,
+                                       'like'     => null,
+                                       'dislike'  => null,
+                                       'share'    => null,
+                                       'announce' => null,
                                ];
 
                                if (DI::pConfig()->get(local_user(), 'system', 'hide_dislike')) {
index a1faab6..39aae4a 100644 (file)
@@ -90,12 +90,13 @@ function parse_url_content(App $a)
        if ($curlResponse->isSuccess()) {
                // Convert the header fields into an array
                $hdrs = [];
-               $h = explode("\n", $curlResponse->getHeader());
+               $h = $curlResponse->getHeaders();
                foreach ($h as $l) {
-                       $header = array_map('trim', explode(':', trim($l), 2));
-                       if (count($header) == 2) {
-                               list($k, $v) = $header;
-                               $hdrs[$k] = $v;
+                       foreach ($l as $k => $v) {
+                               if (empty($hdrs[$k])) {
+                                       $hdrs[$k] = $v;
+                               }
+                               $hdrs[$k] .= " " . $v;
                        }
                }
                $type = null;
index 594cd9a..5256c3c 100644 (file)
@@ -491,8 +491,8 @@ class BBCode
                                }
 
                                $i = $curlResult->getBody();
-                               $type = $curlResult->getContentType();
-                               $type = Images::getMimeTypeByData($i, $mtch[1], $type);
+                               $contType = $curlResult->getContentType();
+                               $type = Images::getMimeTypeByData($i, $mtch[1], $contType);
 
                                if ($i) {
                                        $Image = new Image($i, $type);
index 323a23f..ac86ef3 100644 (file)
@@ -30,7 +30,7 @@ use Friendica\Core\Worker;
 use Friendica\Database\DBA;
 use Friendica\DI;
 use Friendica\Module\Register;
-use Friendica\Network\CurlResult;
+use Friendica\Network\IHTTPResult;
 use Friendica\Protocol\Diaspora;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Network;
@@ -630,18 +630,19 @@ class GServer
        /**
         * Detect server type by using the nodeinfo data
         *
-        * @param string     $url        address of the server
-        * @param CurlResult $curlResult
+        * @param string      $url        address of the server
+        * @param IHTTPResult $httpResult
+        *
         * @return array Server data
         * @throws \Friendica\Network\HTTPException\InternalServerErrorException
         */
-       private static function fetchNodeinfo(string $url, CurlResult $curlResult)
+       private static function fetchNodeinfo(string $url, IHTTPResult $httpResult)
        {
-               if (!$curlResult->isSuccess()) {
+               if (!$httpResult->isSuccess()) {
                        return [];
                }
 
-               $nodeinfo = json_decode($curlResult->getBody(), true);
+               $nodeinfo = json_decode($httpResult->getBody(), true);
 
                if (!is_array($nodeinfo) || empty($nodeinfo['links'])) {
                        return [];
index 6380f42..5f8551f 100644 (file)
@@ -424,16 +424,17 @@ class Photo
                if (!empty($image_url)) {
                        $ret = DI::httpRequest()->get($image_url, true);
                        $img_str = $ret->getBody();
-                       $type = $ret->getContentType();
+                       $contType = $ret->getContentType();
                } else {
                        $img_str = '';
+                       $contType = '';
                }
 
                if ($quit_on_error && ($img_str == "")) {
                        return false;
                }
 
-               $type = Images::getMimeTypeByData($img_str, $image_url, $type);
+               $type = Images::getMimeTypeByData($img_str, $image_url, $contType);
 
                $Image = new Image($img_str, $type);
                if ($Image->isValid()) {
index 68c42e4..e6f20f7 100644 (file)
@@ -1005,13 +1005,13 @@ class User
                        $curlResult = DI::httpRequest()->get($photo, true);
                        if ($curlResult->isSuccess()) {
                                $img_str = $curlResult->getBody();
-                               $type = $curlResult->getContentType();
+                               $contType = $curlResult->getContentType();
                        } else {
                                $img_str = '';
-                               $type = '';
+                               $contType = '';
                        }
 
-                       $type = Images::getMimeTypeByData($img_str, $photo, $type);
+                       $type = Images::getMimeTypeByData($img_str, $photo, $contType);
 
                        $Image = new Image($img_str, $type);
                        if ($Image->isValid()) {
index 9f52edf..4ecdd68 100644 (file)
@@ -29,7 +29,7 @@ use Friendica\Util\Network;
 /**
  * A content class for Curl call results
  */
-class CurlResult
+class CurlResult implements IHTTPResult
 {
        /**
         * @var int HTTP return code or 0 if timeout or failure
@@ -101,7 +101,7 @@ class CurlResult
         *
         * @param string $url optional URL
         *
-        * @return CurlResult a CURL with error response
+        * @return IHTTPResult a CURL with error response
         * @throws InternalServerErrorException
         */
        public static function createErrorCurl($url = '')
@@ -229,57 +229,43 @@ class CurlResult
                }
        }
 
-       /**
-        * Gets the Curl Code
-        *
-        * @return string The Curl Code
-        */
+       /** {@inheritDoc} */
        public function getReturnCode()
        {
                return $this->returnCode;
        }
 
-       /**
-        * Returns the Curl Content Type
-        *
-        * @return string the Curl Content Type
-        */
+       /** {@inheritDoc} */
        public function getContentType()
        {
                return $this->contentType;
        }
 
-       /**
-        * Returns the Curl headers
-        *
-        * @param string $field optional header field. Return all fields if empty
-        *
-        * @return string the Curl headers or the specified content of the header variable
-        */
-       public function getHeader(string $field = '')
+       /** {@inheritDoc} */
+       public function getHeader($header)
        {
-               if (empty($field)) {
-                       return $this->header;
+               if (empty($header)) {
+                       return [];
                }
 
-               $field = strtolower(trim($field));
+               $header = strtolower(trim($header));
 
                $headers = $this->getHeaderArray();
 
-               if (isset($headers[$field])) {
-                       return $headers[$field];
+               if (isset($headers[$header])) {
+                       return $headers[$header];
                }
 
-               return '';
+               return [];
        }
 
-       /**
-        * Check if a specified header exists
-        *
-        * @param string $field header field
-        *
-        * @return boolean "true" if header exists
-        */
+       /** {@inheritDoc} */
+       public function getHeaders()
+       {
+               return $this->getHeaderArray();
+       }
+
+       /** {@inheritDoc} */
        public function inHeader(string $field)
        {
                $field = strtolower(trim($field));
@@ -289,11 +275,7 @@ class CurlResult
                return array_key_exists($field, $headers);
        }
 
-       /**
-        * Returns the Curl headers as an associated array
-        *
-        * @return array associated header array
-        */
+       /** {@inheritDoc} */
        public function getHeaderArray()
        {
                if (!empty($this->header_fields)) {
@@ -307,79 +289,59 @@ class CurlResult
                        $parts = explode(':', $line);
                        $headerfield = strtolower(trim(array_shift($parts)));
                        $headerdata = trim(implode(':', $parts));
-                       $this->header_fields[$headerfield] = $headerdata;
+                       if (empty($this->header_fields[$headerfield])) {
+                               $this->header_fields[$headerfield] = [$headerdata];
+                       } elseif (!in_array($headerdata, $this->header_fields[$headerfield])) {
+                               $this->header_fields[$headerfield][] = $headerdata;
+                       }
                }
 
                return $this->header_fields;
        }
 
-       /**
-        * @return bool
-        */
+       /** {@inheritDoc} */
        public function isSuccess()
        {
                return $this->isSuccess;
        }
 
-       /**
-        * @return string
-        */
+       /** {@inheritDoc} */
        public function getUrl()
        {
                return $this->url;
        }
 
-       /**
-        * @return string
-        */
+       /** {@inheritDoc} */
        public function getRedirectUrl()
        {
                return $this->redirectUrl;
        }
 
-       /**
-        * @return string
-        */
+       /** {@inheritDoc} */
        public function getBody()
        {
                return $this->body;
        }
 
-       /**
-        * @return array
-        */
-       public function getInfo()
-       {
-               return $this->info;
-       }
-
-       /**
-        * @return bool
-        */
+       /** {@inheritDoc} */
        public function isRedirectUrl()
        {
                return $this->isRedirectUrl;
        }
 
-       /**
-        * @return int
-        */
+       /** {@inheritDoc} */
        public function getErrorNumber()
        {
                return $this->errorNumber;
        }
 
-       /**
-        * @return string
-        */
+       /** {@inheritDoc} */
        public function getError()
        {
                return $this->error;
        }
 
-       /**
-        * @return bool
-        */
+       /** {@inheritDoc} */
        public function isTimeout()
        {
                return $this->isTimeout;
diff --git a/src/Network/GuzzleResponse.php b/src/Network/GuzzleResponse.php
new file mode 100644 (file)
index 0000000..b1bbaa9
--- /dev/null
@@ -0,0 +1,148 @@
+<?php
+/**
+ * @copyright Copyright (C) 2020, Friendica
+ *
+ * @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\Network;
+
+use Friendica\Core\Logger;
+use Friendica\Core\System;
+use Friendica\Network\HTTPException\NotImplementedException;
+use GuzzleHttp\Psr7\Response;
+use Psr\Http\Message\ResponseInterface;
+
+/**
+ * A content wrapper class for Guzzle call results
+ */
+class GuzzleResponse extends Response implements IHTTPResult, ResponseInterface
+{
+       /** @var string The URL */
+       private $url;
+       /** @var boolean */
+       private $isTimeout;
+       /** @var boolean */
+       private $isSuccess;
+       /**
+        * @var int the error number or 0 (zero) if no error
+        */
+       private $errorNumber;
+
+       /**
+        * @var string the error message or '' (the empty string) if no
+        */
+       private $error;
+
+       public function __construct(ResponseInterface $response, string $url, $errorNumber = 0, $error = '')
+       {
+               parent::__construct($response->getStatusCode(), $response->getHeaders(), $response->getBody(), $response->getProtocolVersion(), $response->getReasonPhrase());
+               $this->url         = $url;
+               $this->error       = $error;
+               $this->errorNumber = $errorNumber;
+
+               $this->checkSuccess();
+       }
+
+       private function checkSuccess()
+       {
+               $this->isSuccess = ($this->getStatusCode() >= 200 && $this->getStatusCode() <= 299) || $this->errorNumber == 0;
+
+               // Everything higher or equal 400 is not a success
+               if ($this->getReturnCode() >= 400) {
+                       $this->isSuccess = false;
+               }
+
+               if (!$this->isSuccess) {
+                       Logger::notice('http error', ['url' => $this->url, 'code' => $this->getReturnCode(), 'error'  => $this->error, 'callstack' => System::callstack(20)]);
+                       Logger::debug('debug', ['info' => $this->getHeaders()]);
+               }
+
+               if (!$this->isSuccess && $this->errorNumber == CURLE_OPERATION_TIMEDOUT) {
+                       $this->isTimeout = true;
+               } else {
+                       $this->isTimeout = false;
+               }
+       }
+
+       /** {@inheritDoc} */
+       public function getReturnCode()
+       {
+               return $this->getStatusCode();
+       }
+
+       /** {@inheritDoc} */
+       public function getContentType()
+       {
+               $contentTypes = $this->getHeader('Content-Type') ?? [];
+               return array_pop($contentTypes) ?? '';
+       }
+
+       /** {@inheritDoc} */
+       public function inHeader(string $field)
+       {
+               return $this->hasHeader($field);
+       }
+
+       /** {@inheritDoc} */
+       public function getHeaderArray()
+       {
+               return $this->getHeaders();
+       }
+
+       /** {@inheritDoc} */
+       public function isSuccess()
+       {
+               return $this->isSuccess;
+       }
+
+       /** {@inheritDoc} */
+       public function getUrl()
+       {
+               return $this->url;
+       }
+
+       /** {@inheritDoc} */
+       public function getRedirectUrl()
+       {
+               return $this->url;
+       }
+
+       /** {@inheritDoc} */
+       public function isRedirectUrl()
+       {
+               throw new NotImplementedException();
+       }
+
+       /** {@inheritDoc} */
+       public function getErrorNumber()
+       {
+               return $this->errorNumber;
+       }
+
+       /** {@inheritDoc} */
+       public function getError()
+       {
+               return $this->error;
+       }
+
+       /** {@inheritDoc} */
+       public function isTimeout()
+       {
+               return $this->isTimeout;
+       }
+}
index e4ff041..82eec80 100644 (file)
@@ -28,6 +28,12 @@ use Friendica\Core\Config\IConfig;
 use Friendica\Core\System;
 use Friendica\Util\Network;
 use Friendica\Util\Profiler;
+use GuzzleHttp\Client;
+use GuzzleHttp\Exception\RequestException;
+use GuzzleHttp\Exception\TransferException;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\UriInterface;
 use Psr\Log\LoggerInterface;
 
 /**
@@ -54,12 +60,8 @@ class HTTPRequest implements IHTTPRequest
 
        /**
         * {@inheritDoc}
-        *
-        * @param int $redirects The recursion counter for internal use - default 0
-        *
-        * @throws \Friendica\Network\HTTPException\InternalServerErrorException
         */
-       public function get(string $url, bool $binary = false, array $opts = [], int &$redirects = 0)
+       public function get(string $url, bool $binary = false, array $opts = [])
        {
                $stamp1 = microtime(true);
 
@@ -86,124 +88,130 @@ class HTTPRequest implements IHTTPRequest
                        return CurlResult::createErrorCurl($url);
                }
 
-               $ch = @curl_init($url);
-
-               if (($redirects > 8) || (!$ch)) {
-                       return CurlResult::createErrorCurl($url);
-               }
+               $curlOptions = [];
 
-               @curl_setopt($ch, CURLOPT_HEADER, true);
+               $curlOptions[CURLOPT_HEADER] = true;
 
                if (!empty($opts['cookiejar'])) {
-                       curl_setopt($ch, CURLOPT_COOKIEJAR, $opts["cookiejar"]);
-                       curl_setopt($ch, CURLOPT_COOKIEFILE, $opts["cookiejar"]);
+                       $curlOptions[CURLOPT_COOKIEJAR] = $opts["cookiejar"];
+                       $curlOptions[CURLOPT_COOKIEFILE] = $opts["cookiejar"];
                }
 
                // These settings aren't needed. We're following the location already.
-               //      @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
-               //      @curl_setopt($ch, CURLOPT_MAXREDIRS, 5);
+               //      $curlOptions[CURLOPT_FOLLOWLOCATION] =true;
+               //      $curlOptions[CURLOPT_MAXREDIRS] = 5;
 
                if (!empty($opts['accept_content'])) {
-                       curl_setopt(
-                               $ch,
-                               CURLOPT_HTTPHEADER,
-                               ['Accept: ' . $opts['accept_content']]
-                       );
+                       $curlOptions[CURLOPT_HTTPHEADER][] = ['Accept: ' . $opts['accept_content']];
                }
 
                if (!empty($opts['header'])) {
-                       curl_setopt($ch, CURLOPT_HTTPHEADER, $opts['header']);
+                       $curlOptions[CURLOPT_HTTPHEADER][] = $opts['header'];
                }
 
-               @curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
-               @curl_setopt($ch, CURLOPT_USERAGENT, $this->getUserAgent());
+               $curlOptions[CURLOPT_RETURNTRANSFER] = true;
+               $curlOptions[CURLOPT_USERAGENT] = $this->getUserAgent();
 
                $range = intval($this->config->get('system', 'curl_range_bytes', 0));
 
                if ($range > 0) {
-                       @curl_setopt($ch, CURLOPT_RANGE, '0-' . $range);
+                       $curlOptions[CURLOPT_RANGE] = '0-' . $range;
                }
 
                // Without this setting it seems as if some webservers send compressed content
                // This seems to confuse curl so that it shows this uncompressed.
                /// @todo  We could possibly set this value to "gzip" or something similar
-               curl_setopt($ch, CURLOPT_ENCODING, '');
+               $curlOptions[CURLOPT_ENCODING] = '';
 
                if (!empty($opts['headers'])) {
-                       @curl_setopt($ch, CURLOPT_HTTPHEADER, $opts['headers']);
+                       $curlOptions[CURLOPT_HTTPHEADER][] = $opts['headers'];
                }
 
                if (!empty($opts['nobody'])) {
-                       @curl_setopt($ch, CURLOPT_NOBODY, $opts['nobody']);
+                       $curlOptions[CURLOPT_NOBODY] = $opts['nobody'];
                }
 
-               @curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
+               $curlOptions[CURLOPT_CONNECTTIMEOUT] = 10;
 
                if (!empty($opts['timeout'])) {
-                       @curl_setopt($ch, CURLOPT_TIMEOUT, $opts['timeout']);
+                       $curlOptions[CURLOPT_TIMEOUT] = $opts['timeout'];
                } else {
                        $curl_time = $this->config->get('system', 'curl_timeout', 60);
-                       @curl_setopt($ch, CURLOPT_TIMEOUT, intval($curl_time));
+                       $curlOptions[CURLOPT_TIMEOUT] = intval($curl_time);
                }
 
                // by default we will allow self-signed certs
                // but you can override this
 
                $check_cert = $this->config->get('system', 'verifyssl');
-               @curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, (($check_cert) ? true : false));
+               $curlOptions[CURLOPT_SSL_VERIFYPEER] = ($check_cert) ? true : false;
 
                if ($check_cert) {
-                       @curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
+                       $curlOptions[CURLOPT_SSL_VERIFYHOST] = 2;
                }
 
                $proxy = $this->config->get('system', 'proxy');
 
                if (!empty($proxy)) {
-                       @curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1);
-                       @curl_setopt($ch, CURLOPT_PROXY, $proxy);
+                       $curlOptions[CURLOPT_HTTPPROXYTUNNEL] = 1;
+                       $curlOptions[CURLOPT_PROXY] = $proxy;
                        $proxyuser = $this->config->get('system', 'proxyuser');
 
                        if (!empty($proxyuser)) {
-                               @curl_setopt($ch, CURLOPT_PROXYUSERPWD, $proxyuser);
+                               $curlOptions[CURLOPT_PROXYUSERPWD] = $proxyuser;
                        }
                }
 
                if ($this->config->get('system', 'ipv4_resolve', false)) {
-                       curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
+                       $curlOptions[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V4;
                }
 
                if ($binary) {
-                       @curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1);
+                       $curlOptions[CURLOPT_BINARYTRANSFER] = 1;
                }
 
-               // don't let curl abort the entire application
-               // if it throws any errors.
-
-               $s         = @curl_exec($ch);
-               $curl_info = @curl_getinfo($ch);
-
-               // Special treatment for HTTP Code 416
-               // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/416
-               if (($curl_info['http_code'] == 416) && ($range > 0)) {
-                       @curl_setopt($ch, CURLOPT_RANGE, '');
-                       $s         = @curl_exec($ch);
-                       $curl_info = @curl_getinfo($ch);
-               }
+               $logger = $this->logger;
 
-               $curlResponse = new CurlResult($url, $s, $curl_info, curl_errno($ch), curl_error($ch));
+               $onRedirect = function(
+                       RequestInterface $request,
+                       ResponseInterface $response,
+                       UriInterface $uri
+               ) use ($logger) {
+                       $logger->notice('Curl redirect.', ['url' => $request->getUri(), 'to' => $uri]);
+               };
 
-               if (!Network::isRedirectBlocked($url) && $curlResponse->isRedirectUrl()) {
-                       $redirects++;
-                       $this->logger->notice('Curl redirect.', ['url' => $url, 'to' => $curlResponse->getRedirectUrl()]);
-                       @curl_close($ch);
-                       return $this->get($curlResponse->getRedirectUrl(), $binary, $opts, $redirects);
+               $onHeaders = function (ResponseInterface $response) use ($opts) {
+                       if (!empty($opts['content_length']) &&
+                               $response->getHeaderLine('Content-Length') > $opts['content_length']) {
+                               throw new TransferException('The file is too big!');
+                       }
+               };
+
+               $client = new Client([
+                       'allow_redirect' => [
+                               'max' => 8,
+                               'on_redirect' => $onRedirect,
+                               'on_headers' => $onHeaders,
+                               'track_redirect' => true,
+                               'strict' => true,
+                               'referer' => true,
+                       ],
+                       'curl' => $curlOptions
+               ]);
+
+               try {
+                       $response = $client->get($url);
+                       return new GuzzleResponse($response, $url);
+               } catch (TransferException $exception) {
+                       if ($exception instanceof RequestException &&
+                               $exception->hasResponse()) {
+                               return new GuzzleResponse($exception->getResponse(), $url, $exception->getCode(), '');
+                       } else {
+                               return new CurlResult($url, '', ['http_code' => $exception->getCode()], $exception->getCode(), '');
+                       }
+               } finally {
+                       $this->profiler->saveTimestamp($stamp1, 'network');
                }
-
-               @curl_close($ch);
-
-               $this->profiler->saveTimestamp($stamp1, 'network');
-
-               return $curlResponse;
        }
 
        /**
@@ -458,8 +466,7 @@ class HTTPRequest implements IHTTPRequest
                                'timeout'        => $timeout,
                                'accept_content' => $accept_content,
                                'cookiejar'      => $cookiejar
-                       ],
-                       $redirects
+                       ]
                );
        }
 
index 3ebcc5d..4999308 100644 (file)
@@ -57,7 +57,7 @@ interface IHTTPRequest
         * @param string $accept_content  supply Accept: header with 'accept_content' as the value
         * @param string $cookiejar       Path to cookie jar file
         *
-        * @return CurlResult With all relevant information, 'body' contains the actual fetched content.
+        * @return IHTTPResult With all relevant information, 'body' contains the actual fetched content.
         */
        public function fetchFull(string $url, bool $binary = false, int $timeout = 0, string $accept_content = '', string $cookiejar = '');
 
@@ -75,8 +75,9 @@ interface IHTTPRequest
         *                           'nobody' => only return the header
         *                           'cookiejar' => path to cookie jar file
         *                           'header' => header array
+        *                           'content_length' => int maximum File content length
         *
-        * @return CurlResult
+        * @return IHTTPResult
         */
        public function get(string $url, bool $binary = false, array $opts = []);
 
@@ -88,7 +89,7 @@ interface IHTTPRequest
         * @param array  $headers HTTP headers
         * @param int    $timeout The timeout in seconds, default system config value or 60 seconds
         *
-        * @return CurlResult The content
+        * @return IHTTPResult The content
         */
        public function post(string $url, $params, array $headers = [], int $timeout = 0);
 
diff --git a/src/Network/IHTTPResult.php b/src/Network/IHTTPResult.php
new file mode 100644 (file)
index 0000000..38a1176
--- /dev/null
@@ -0,0 +1,104 @@
+<?php
+
+namespace Friendica\Network;
+
+use Psr\Http\Message\MessageInterface;
+
+/**
+ * Temporary class to map Friendica used variables based on PSR-7 HTTPResponse
+ */
+interface IHTTPResult
+{
+       /**
+        * Gets the Return Code
+        *
+        * @return string The Return Code
+        */
+       public function getReturnCode();
+
+       /**
+        * Returns the Content Type
+        *
+        * @return string the Content Type
+        */
+       public function getContentType();
+
+       /**
+        * Returns the headers
+        * @see MessageInterface::getHeader()
+        *
+        * @param string $header optional header field. Return all fields if empty
+        *
+        * @return string[] the headers or the specified content of the header variable
+        */
+       public function getHeader($header);
+
+       /**
+        * Returns all headers
+        * @see MessageInterface::getHeaders()
+        *
+        * @return string[][]
+        */
+       public function getHeaders();
+
+       /**
+        * Check if a specified header exists
+        * @see MessageInterface::hasHeader()
+        *
+        * @param string $field header field
+        *
+        * @return boolean "true" if header exists
+        */
+       public function inHeader(string $field);
+
+       /**
+        * Returns the headers as an associated array
+        * @see MessageInterface::getHeaders()
+        * @deprecated
+        *
+        * @return string[][] associated header array
+        */
+       public function getHeaderArray();
+
+       /**
+        * @return bool
+        */
+       public function isSuccess();
+
+       /**
+        * @return string
+        */
+       public function getUrl();
+
+       /**
+        * @return string
+        */
+       public function getRedirectUrl();
+
+       /**
+        * @see MessageInterface::getBody()
+        *
+        * @return string
+        */
+       public function getBody();
+
+       /**
+        * @return boolean
+        */
+       public function isRedirectUrl();
+
+       /**
+        * @return integer
+        */
+       public function getErrorNumber();
+
+       /**
+        * @return string
+        */
+       public function getError();
+
+       /**
+        * @return boolean
+        */
+       public function isTimeout();
+}
index 3fe0352..cfd0368 100644 (file)
@@ -423,16 +423,11 @@ class Probe
         */
        private static function getHideStatus($url)
        {
-               $curlResult = DI::httpRequest()->get($url);
+               $curlResult = DI::httpRequest()->get($url, false, ['content_length' => 1000000]);
                if (!$curlResult->isSuccess()) {
                        return false;
                }
 
-               // If the file is too large then exit
-               if (($curlResult->getInfo()['download_content_length'] ?? 0) > 1000000) {
-                       return false;
-               }
-
                // If it isn't a HTML file then exit
                if (($curlResult->getContentType() != '') && !strstr(strtolower($curlResult->getContentType()), 'html')) {
                        return false;
index 894b75f..b89a54a 100644 (file)
@@ -151,9 +151,10 @@ class Post
                }
                $sparkle = '';
                $buttons = [
-                       'like'    => null,
-                       'dislike' => null,
-                       'share'   => null,
+                       'like'     => null,
+                       'dislike'  => null,
+                       'share'    => null,
+                       'announce' => null,
                ];
                $dropping = false;
                $pinned = '';
@@ -175,6 +176,7 @@ class Post
                        : false);
 
                $shareable = in_array($conv->getProfileOwner(), [0, local_user()]) && $item['private'] != Item::PRIVATE;
+               $announceable = $shareable && in_array($item['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::TWITTER]);
 
                $edpost = false;
 
@@ -344,7 +346,10 @@ class Post
                        $buttons['like']    = [DI::l10n()->t("I like this \x28toggle\x29")      , DI::l10n()->t("like")];
                        $buttons['dislike'] = [DI::l10n()->t("I don't like this \x28toggle\x29"), DI::l10n()->t("dislike")];
                        if ($shareable) {
-                               $buttons['share'] = [DI::l10n()->t('Share this'), DI::l10n()->t('share')];
+                               $buttons['share'] = [DI::l10n()->t('Share this'), DI::l10n()->t('Share')];
+                       }
+                       if ($announceable) {
+                               $buttons['announce'] = [DI::l10n()->t('Announce this'), DI::l10n()->t('Announce')];
                        }
                }
 
index d20864c..f718e0a 100644 (file)
@@ -1358,7 +1358,7 @@ class DFRN
                        return -9; // timed out
                }
 
-               if (($curl_stat == 503) && stristr($postResult->getHeader(), 'retry-after')) {
+               if (($curl_stat == 503) && $postResult->inHeader('retry-after')) {
                        return -10;
                }
 
@@ -1453,7 +1453,7 @@ class DFRN
                        return -9; // timed out
                }
 
-               if (($curl_stat == 503) && (stristr($postResult->getHeader(), 'retry-after'))) {
+               if (($curl_stat == 503) && $postResult->inHeader('retry-after')) {
                        return -10;
                }
 
index 0635be8..1d95a6f 100644 (file)
@@ -746,7 +746,8 @@ class OStatus
 
                $xml = '';
 
-               if (stristr($curlResult->getHeader(), 'Content-Type: application/atom+xml')) {
+               if ($curlResult->inHeader('Content-Type') &&
+                       in_array('application/atom+xml', $curlResult->getHeader('Content-Type'))) {
                        $xml = $curlResult->getBody();
                }
 
@@ -939,7 +940,8 @@ class OStatus
 
                $xml = '';
 
-               if (stristr($curlResult->getHeader(), 'Content-Type: application/atom+xml')) {
+               if ($curlResult->inHeader('Content-Type') &&
+                       in_array('application/atom+xml', $curlResult->getHeader('Content-Type'))) {
                        Logger::log('Directly fetched XML for URI ' . $related_uri, Logger::DEBUG);
                        $xml = $curlResult->getBody();
                }
index 169a4d0..8084625 100644 (file)
@@ -215,7 +215,7 @@ class Salmon
                        return -1;
                }
 
-               if (($return_code == 503) && (stristr($postResult->getHeader(), 'retry-after'))) {
+               if (($return_code == 503) && $postResult->inHeader('retry-after')) {
                        return -1;
                }
 
index f39b0db..1b52b91 100644 (file)
@@ -77,21 +77,21 @@ class Images
         *
         * @param string $image_data Image data
         * @param string $filename   File name (for guessing the type via the extension)
-        * @param string $mime       default mime type
+        * @param string $mimeType   possible mime type
         *
         * @return string
         * @throws \Exception
         */
-       public static function getMimeTypeByData(string $image_data, string $filename = '', string $mime = '')
+       public static function getMimeTypeByData(string $image_data, string $filename = '', string $mimeType = '')
        {
-               if (substr($mime, 0, 6) == 'image/') {
-                       Logger::info('Using default mime type', ['filename' => $filename, 'mime' => $mime]);
-                       return $mime;
+               if (substr($mimeType, 0, 6) == 'image/') {
+                       Logger::info('Using default mime type', ['filename' => $filename, 'mime' => $mimeType]);
+                       return $mimeType;
                }
 
                $image = @getimagesizefromstring($image_data);
                if (!empty($image['mime'])) {
-                       Logger::info('Mime type detected via data', ['filename' => $filename, 'default' => $mime, 'mime' => $image['mime']]);
+                       Logger::info('Mime type detected via data', ['filename' => $filename, 'default' => $mimeType, 'mime' => $image['mime']]);
                        return $image['mime'];
                }
 
index 1596e01..1a81a25 100644 (file)
@@ -160,22 +160,16 @@ class ParseUrl
                        return $siteinfo;
                }
 
-               $curlResult = DI::httpRequest()->get($url);
+               $curlResult = DI::httpRequest()->get($url, false, ['content_length' => 1000000]);
                if (!$curlResult->isSuccess()) {
                        return $siteinfo;
                }
 
-               // If the file is too large then exit
-               if (($curlResult->getInfo()['download_content_length'] ?? 0) > 1000000) {
-                       return $siteinfo;
-               }
-
                // If it isn't a HTML file then exit
                if (($curlResult->getContentType() != '') && !strstr(strtolower($curlResult->getContentType()), 'html')) {
                        return $siteinfo;
                }
 
-               $header = $curlResult->getHeader();
                $body = $curlResult->getBody();
 
                if ($do_oembed) {
@@ -204,7 +198,7 @@ class ParseUrl
                $charset = '';
                // Look for a charset, first in headers
                // Expected form: Content-Type: text/html; charset=ISO-8859-4
-               if (preg_match('/charset=([a-z0-9-_.\/]+)/i', $header, $matches)) {
+               if (preg_match('/charset=([a-z0-9-_.\/]+)/i', $curlResult->getContentType(), $matches)) {
                        $charset = trim(trim(trim(array_pop($matches)), ';,'));
                }
 
diff --git a/tests/datasets/curl/about.head.php b/tests/datasets/curl/about.head.php
new file mode 100644 (file)
index 0000000..b7773b8
--- /dev/null
@@ -0,0 +1,20 @@
+<?php
+
+return [
+       'http/2 200' => [''],
+    'date' => ['Thu, 11 Oct 2018 18:43:54 GMT'],
+    'content-type' => ['text/html; charset=utf-8'],
+    'vary' => ['Accept-Encoding'],
+    'server' => ['Mastodon'],
+    'x-frame-options' => ['DENY', 'SAMEORIGIN'],
+    'x-content-type-options' => ['nosniff'],
+    'x-xss-protection' => ['1; mode=block'],
+    'etag' => ['W/"706e6c48957e1d46ecf9d7597a7880af"'],
+    'cache-control' => ['max-age=0, private, must-revalidate'],
+    'set-cookie' => ['_mastodon_session=v3kcy%2FW3aZYBBvZUohuwksEKwzYIyEUlEuJ1KqTAfWPKvVQq%2F4UuJ39zp621VyfpQNlvY46TL%2FYutzXowSLYQBNFCJcrEiF04aU0TdtHls9zynMiyeHhoVgCijOXWXNt9%2FCmpQ49RkNEujkv9NaJ0cum32MCVZKjE9%2BMKmLM%2F8ZygZeLBGJ7sg%3D%3D--QGIiU0%2FpXc3Aym8F--he2iRRPePOdtEs3z%2BufSXg%3D%3D; path=/; secure; HttpOnly'],
+    'x-request-id' => ['a0c0b8e7-cd60-4efa-b79b-cf1b0d5a0784'],
+    'x-runtime' => ['0.049566'],
+    'strict-transport-security' => ['max-age=31536000; includeSubDomains; preload'],
+    'referrer-policy' => ['same-origin'],
+    'content-security-policy' => ["frame-ancestors 'none'; script-src 'self'; object-src 'self'; img-src * data: blob:; media-src 'self' data:; font-src 'self' data: https://fonts.gstatic.com/; connect-src 'self' blob: wss://mastodonten.de"],
+];
diff --git a/tests/datasets/curl/about.redirect.php b/tests/datasets/curl/about.redirect.php
new file mode 100644 (file)
index 0000000..f01689a
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+return [
+       'http/2 301' => [''],
+    'date' => ['Thu, 11 Oct 2018 18:43:54 GMT'],
+    'content-type' => ['text/html; charset=utf-8'],
+    'vary' => ['Accept-Encoding'],
+    'server' => ['Mastodon'],
+    'location' => ['https://test.other/some/'],
+    'x-frame-options' => ['DENY', 'SAMEORIGIN'],
+    'x-content-type-options' => ['nosniff'],
+    'x-xss-protection' => ['1; mode=block'],
+    'etag' => ['W/"706e6c48957e1d46ecf9d7597a7880af"'],
+    'cache-control' => ['max-age=0, private, must-revalidate'],
+    'set-cookie' => ['_mastodon_session=v3kcy%2FW3aZYBBvZUohuwksEKwzYIyEUlEuJ1KqTAfWPKvVQq%2F4UuJ39zp621VyfpQNlvY46TL%2FYutzXowSLYQBNFCJcrEiF04aU0TdtHls9zynMiyeHhoVgCijOXWXNt9%2FCmpQ49RkNEujkv9NaJ0cum32MCVZKjE9%2BMKmLM%2F8ZygZeLBGJ7sg%3D%3D--QGIiU0%2FpXc3Aym8F--he2iRRPePOdtEs3z%2BufSXg%3D%3D; path=/; secure; HttpOnly'],
+    'x-request-id' => ['a0c0b8e7-cd60-4efa-b79b-cf1b0d5a0784'],
+    'x-runtime' => ['0.049566'],
+    'strict-transport-security' => ['max-age=31536000; includeSubDomains; preload'],
+    'referrer-policy' => ['same-origin'],
+    'content-security-policy' => ["frame-ancestors 'none'; script-src 'self'; object-src 'self'; img-src * data: blob:; media-src 'self' data:; font-src 'self' data: https://fonts.gstatic.com/; connect-src 'self' blob: wss://mastodonten.de"],
+];
index 0087968..37a754b 100644 (file)
@@ -25,7 +25,7 @@ namespace Friendica\Core;
 use Dice\Dice;
 use Friendica\Core\Config\Cache;
 use Friendica\DI;
-use Friendica\Network\CurlResult;
+use Friendica\Network\IHTTPResult;
 use Friendica\Network\IHTTPRequest;
 use Friendica\Test\MockedTest;
 use Friendica\Test\Util\VFSTrait;
@@ -297,14 +297,14 @@ class InstallerTest extends MockedTest
                $this->l10nMock->shouldReceive('t')->andReturnUsing(function ($args) { return $args; });
 
                // Mocking the CURL Response
-               $curlResult = \Mockery::mock(CurlResult::class);
-               $curlResult
+               $IHTTPResult = \Mockery::mock(IHTTPResult::class);
+               $IHTTPResult
                        ->shouldReceive('getReturnCode')
                        ->andReturn('404');
-               $curlResult
+               $IHTTPResult
                        ->shouldReceive('getRedirectUrl')
                        ->andReturn('');
-               $curlResult
+               $IHTTPResult
                        ->shouldReceive('getError')
                        ->andReturn('test Error');
 
@@ -313,11 +313,11 @@ class InstallerTest extends MockedTest
                $networkMock
                        ->shouldReceive('fetchFull')
                        ->with('https://test/install/testrewrite')
-                       ->andReturn($curlResult);
+                       ->andReturn($IHTTPResult);
                $networkMock
                        ->shouldReceive('fetchFull')
                        ->with('http://test/install/testrewrite')
-                       ->andReturn($curlResult);
+                       ->andReturn($IHTTPResult);
 
                $this->dice->shouldReceive('create')
                     ->with(IHTTPRequest::class)
@@ -344,14 +344,14 @@ class InstallerTest extends MockedTest
                $this->l10nMock->shouldReceive('t')->andReturnUsing(function ($args) { return $args; });
 
                // Mocking the failed CURL Response
-               $curlResultF = \Mockery::mock(CurlResult::class);
-               $curlResultF
+               $IHTTPResultF = \Mockery::mock(IHTTPResult::class);
+               $IHTTPResultF
                        ->shouldReceive('getReturnCode')
                        ->andReturn('404');
 
                // Mocking the working CURL Response
-               $curlResultW = \Mockery::mock(CurlResult::class);
-               $curlResultW
+               $IHTTPResultW = \Mockery::mock(IHTTPResult::class);
+               $IHTTPResultW
                        ->shouldReceive('getReturnCode')
                        ->andReturn('204');
 
@@ -360,11 +360,11 @@ class InstallerTest extends MockedTest
                $networkMock
                        ->shouldReceive('fetchFull')
                        ->with('https://test/install/testrewrite')
-                       ->andReturn($curlResultF);
+                       ->andReturn($IHTTPResultF);
                $networkMock
                        ->shouldReceive('fetchFull')
                        ->with('http://test/install/testrewrite')
-                       ->andReturn($curlResultW);
+                       ->andReturn($IHTTPResultW);
 
                $this->dice->shouldReceive('create')
                           ->with(IHTTPRequest::class)
index e066fb8..03a5288 100644 (file)
@@ -53,6 +53,7 @@ class CurlResultTest extends TestCase
        public function testNormal()
        {
                $header = file_get_contents(__DIR__ . '/../../datasets/curl/about.head');
+               $headerArray = include(__DIR__ . '/../../datasets/curl/about.head.php');
                $body = file_get_contents(__DIR__ . '/../../datasets/curl/about.body');
 
 
@@ -65,7 +66,7 @@ class CurlResultTest extends TestCase
                $this->assertTrue($curlResult->isSuccess());
                $this->assertFalse($curlResult->isTimeout());
                $this->assertFalse($curlResult->isRedirectUrl());
-               $this->assertSame($header, $curlResult->getHeader());
+               $this->assertSame($headerArray, $curlResult->getHeaders());
                $this->assertSame($body, $curlResult->getBody());
                $this->assertSame('text/html; charset=utf-8', $curlResult->getContentType());
                $this->assertSame('https://test.local', $curlResult->getUrl());
@@ -80,6 +81,7 @@ class CurlResultTest extends TestCase
        public function testRedirect()
        {
                $header = file_get_contents(__DIR__ . '/../../datasets/curl/about.head');
+               $headerArray = include(__DIR__ . '/../../datasets/curl/about.head.php');
                $body = file_get_contents(__DIR__ . '/../../datasets/curl/about.body');
 
 
@@ -93,7 +95,7 @@ class CurlResultTest extends TestCase
                $this->assertTrue($curlResult->isSuccess());
                $this->assertFalse($curlResult->isTimeout());
                $this->assertTrue($curlResult->isRedirectUrl());
-               $this->assertSame($header, $curlResult->getHeader());
+               $this->assertSame($headerArray, $curlResult->getHeaders());
                $this->assertSame($body, $curlResult->getBody());
                $this->assertSame('text/html; charset=utf-8', $curlResult->getContentType());
                $this->assertSame('https://test.local/test/it', $curlResult->getUrl());
@@ -106,6 +108,7 @@ class CurlResultTest extends TestCase
        public function testTimeout()
        {
                $header = file_get_contents(__DIR__ . '/../../datasets/curl/about.head');
+               $headerArray = include(__DIR__ . '/../../datasets/curl/about.head.php');
                $body = file_get_contents(__DIR__ . '/../../datasets/curl/about.body');
 
 
@@ -119,7 +122,7 @@ class CurlResultTest extends TestCase
                $this->assertFalse($curlResult->isSuccess());
                $this->assertTrue($curlResult->isTimeout());
                $this->assertFalse($curlResult->isRedirectUrl());
-               $this->assertSame($header, $curlResult->getHeader());
+               $this->assertSame($headerArray, $curlResult->getHeaders());
                $this->assertSame($body, $curlResult->getBody());
                $this->assertSame('text/html; charset=utf-8', $curlResult->getContentType());
                $this->assertSame('https://test.local/test/it', $curlResult->getRedirectUrl());
@@ -134,6 +137,7 @@ class CurlResultTest extends TestCase
        public function testRedirectHeader()
        {
                $header = file_get_contents(__DIR__ . '/../../datasets/curl/about.redirect');
+               $headerArray = include(__DIR__ . '/../../datasets/curl/about.redirect.php');
                $body = file_get_contents(__DIR__ . '/../../datasets/curl/about.body');
 
 
@@ -146,7 +150,7 @@ class CurlResultTest extends TestCase
                $this->assertTrue($curlResult->isSuccess());
                $this->assertFalse($curlResult->isTimeout());
                $this->assertTrue($curlResult->isRedirectUrl());
-               $this->assertSame($header, $curlResult->getHeader());
+               $this->assertSame($headerArray, $curlResult->getHeaders());
                $this->assertSame($body, $curlResult->getBody());
                $this->assertSame('text/html; charset=utf-8', $curlResult->getContentType());
                $this->assertSame('https://test.local/test/it?key=value', $curlResult->getUrl());
@@ -204,7 +208,7 @@ class CurlResultTest extends TestCase
                        'url' => 'https://test.local'
                ]);
 
-               $this->assertNotEmpty($curlResult->getHeader());
+               $this->assertNotEmpty($curlResult->getHeaders());
                $this->assertEmpty($curlResult->getHeader('wrongHeader'));
        }
 }
index b869b99..1d1a766 100644 (file)
@@ -82,7 +82,8 @@
                        <div class="wall-item-like-buttons" id="wall-item-like-buttons-{{$item.id}}">
                                <a href="#" class="icon like{{if $item.responses.like.self}} active{{/if}}" title="{{$item.vote.like.0}}" onclick="dolike({{$item.id}}, 'like'{{if $item.responses.like.self}}, true{{/if}}); return false"></a>
                                {{if $item.vote.dislike}}<a href="#" class="icon dislike{{if $item.responses.dislike.self}} active{{/if}}" title="{{$item.vote.dislike.0}}" onclick="dolike({{$item.id}},'dislike'{{if $item.responses.dislike.self}}, true{{/if}}); return false"></a>{{/if}}
-                               {{if $item.vote.share}}<a href="#" class="icon recycle wall-item-share-buttons" title="{{$item.vote.share.0}}" onclick="jotShare({{$item.id}}); return false"></a>{{/if}}
+                               {{if $item.vote.announce}}<a href="#" class="icon recycle{{if $item.responses.announce.self}} active{{/if}}" title="{{$item.vote.announce.0}}" onclick="dolike({{$item.id}},'announce'{{if $item.responses.announce.self}}, true{{/if}}); return false"></a>{{/if}}
+                               {{if $item.vote.share}}<a href="#" class="icon share wall-item-share-buttons" title="{{$item.vote.share.0}}" onclick="jotShare({{$item.id}}); return false"></a>{{/if}}
                                <img id="like-rotator-{{$item.id}}" class="like-rotator" src="images/rotator.gif" alt="{{$item.wait}}" title="{{$item.wait}}" style="display: none;" />
                        </div>
                        {{/if}}
index 0375dc4..2e7a1fb 100644 (file)
@@ -311,11 +311,17 @@ as the value of $top_child_total (this is done at the end of this file)
 
                        {{* Button for sharing the item *}}
                        {{if $item.vote}}
-                               {{if $item.vote.share}}
+                               {{if $item.vote.share OR $item.vote.announce}}
                                        {{if $item.vote.like OR $item.vote.dislike OR $item.comment_html}}
                                <span role="presentation" class="separator"></span>
                                        {{/if}}
-                               <button type="button" class="btn-link button-votes" id="share-{{$item.id}}" title="{{$item.vote.share.0}}" onclick="jotShare({{$item.id}});"><i class="fa fa-retweet" aria-hidden="true"></i>&nbsp;{{$item.vote.share.1}}</button>
+                               {{/if}}
+                               {{if $item.vote.announce}}
+                               <button type="button" class="btn-link button-announces{{if $item.responses.announce.self}} active" aria-pressed="true{{/if}}" id="announce-{{$item.id}}" title="{{$item.vote.announce.0}}" onclick="doLikeAction({{$item.id}}, 'announce'{{if $item.responses.announce.self}}, true{{/if}});" data-toggle="button"><i class="fa fa-retweet" aria-hidden="true"></i>&nbsp;{{$item.vote.announce.1}}</button>
+                               <span role="presentation" class="separator"></span>
+                               {{/if}}
+                               {{if $item.vote.share}}
+                               <button type="button" class="btn-link button-votes" id="share-{{$item.id}}" title="{{$item.vote.share.0}}" onclick="jotShare({{$item.id}});"><i class="fa fa-share" aria-hidden="true"></i>&nbsp;{{$item.vote.share.1}}</button>
                                {{/if}}
                        {{/if}}
 
@@ -434,10 +440,18 @@ as the value of $top_child_total (this is done at the end of this file)
                                </div>
                        {{/if}}
 
+                       {{* Button for announcing the item *}}
+                       {{if $item.vote.announce}}
+<!--                           <div class="btn-group" role="group">
+                                       <button type="button" class="btn btn-sm button-announces{{if $item.responses.announce.self}} active" aria-pressed="true{{/if}}" id="announce-{{$item.id}}" title="{{$item.vote.announce.0}}" onclick="doLikeAction({{$item.id}}, 'announce'{{if $item.responses.announce.self}}, true{{/if}});" data-toggle="button"><i class="fa fa-retweet-up" aria-hidden="true"></i></button>
+                               </div>
+                               -->
+                       {{/if}}
+
                        {{* Button for sharing the item *}}
                        {{if $item.vote.share}}
                                <div class="btn-group" role="group">
-                                       <button type="button" class="btn btn-sm button-votes" id="share-{{$item.id}}" title="{{$item.vote.share.0}}" onclick="jotShare({{$item.id}});"><i class="fa fa-retweet" aria-hidden="true"></i></button>
+                                       <button type="button" class="btn btn-sm button-votes" id="share-{{$item.id}}" title="{{$item.vote.share.0}}" onclick="jotShare({{$item.id}});"><i class="fa fa-share" aria-hidden="true"></i></button>
                                </div>
                        {{/if}}
 
index a97a0cc..84f83e0 100644 (file)
                                {{if $item.vote.dislike}}
                                <a href="#" id="dislike-{{$item.id}}"{{if $item.responses.dislike.self}} class="active{{/if}}" title="{{$item.vote.dislike.0}}" onclick="dolike({{$item.id}}, 'dislike'{{if $item.responses.dislike.self}}, true{{/if}}); return false">{{$item.vote.dislike.1}}</a>
                                {{/if}}
+                               {{if $item.vote.announce}}
+                               <a href="#" id="announce-{{$item.id}}"{{if $item.responses.announce.self}} class="active{{/if}}" title="{{$item.vote.announce.0}}" onclick="dolike({{$item.id}}, 'announce'{{if $item.responses.announce.self}}, true{{/if}}); return false">{{$item.vote.announce.1}}</a>
+                               {{/if}}
                            {{if $item.vote.share}}
                                    <a href="#" id="share-{{$item.id}}" title="{{$item.vote.share.0}}" onclick="jotShare({{$item.id}}); return false">{{$item.vote.share.1}}</a>
                            {{/if}}
index 2e83a24..1554a2e 100644 (file)
                                {{if $item.vote.dislike}}
                                <a href="#" class="icon dislike{{if $item.responses.dislike.self}} self{{/if}}" title="{{$item.vote.dislike.0}}" onclick="dolike({{$item.id}}, 'dislike'{{if $item.responses.dislike.self}}, true{{/if}}); return false"></a>
                                {{/if}}
+                               {{if $item.vote.announce}}
+                               <a href="#" class="icon recycle{{if $item.responses.announce.self}} self{{/if}}" title="{{$item.vote.announce.0}}" onclick="dolike({{$item.id}}, 'announce'{{if $item.responses.announce.self}}, true{{/if}}); return false"></a>
+                               {{/if}}
                                {{if $item.vote.share}}
-                               <a href="#" class="icon recycle wall-item-share-buttons" title="{{$item.vote.share.0}}" onclick="jotShare({{$item.id}}); return false"></a>                             {{/if}}
+                               <a href="#" class="icon share wall-item-share-buttons" title="{{$item.vote.share.0}}" onclick="jotShare({{$item.id}}); return false"></a>                               {{/if}}
                                <img id="like-rotator-{{$item.id}}" class="like-rotator" src="images/rotator.gif" alt="{{$item.wait}}" title="{{$item.wait}}" style="display: none;" />
                        </div>
                        {{/if}}
index 944aee7..32b15de 100644 (file)
                                <a role="button" id="like-{{$item.id}}"{{if $item.responses.like.self}} class="active"{{/if}} title="{{$item.vote.like.0}}" onclick="dolike({{$item.id}}, 'like'{{if $item.responses.like.self}}, true{{/if}}); return false"><i class="icon-thumbs-up icon-large"><span class="sr-only">{{$item.vote.like.0}}</span></i></a>
                                {{/if}}{{if $item.vote.dislike}}
                                <a role="button" id="dislike-{{$item.id}}"{{if $item.responses.dislike.self}} class="active"{{/if}} title="{{$item.vote.dislike.0}}" onclick="dolike({{$item.id}}, 'dislike'{{if $item.responses.dislike.self}}, true{{/if}}); return false"><i class="icon-thumbs-down icon-large"><span class="sr-only">{{$item.vote.dislike.0}}</span></i></a>
+                               {{/if}}{{if $item.vote.announce}}
+                               <a role="button" id="announce-{{$item.id}}"{{if $item.responses.announce.self}} class="active"{{/if}} title="{{$item.vote.announce.0}}" onclick="dolike({{$item.id}}, 'announce'{{if $item.responses.announce.self}}, true{{/if}}); return false"><i class="icon-retweet icon-large"><span class="sr-only">{{$item.vote.dislike.0}}</span></i></a>
                                {{/if}}
                            {{if $item.vote.share}}
-                                   <a role="button" id="share-{{$item.id}}" title="{{$item.vote.share.0}}" onclick="jotShare({{$item.id}}); return false"><i class="icon-retweet icon-large"><span class="sr-only">{{$item.vote.share.0}}</span></i></a>
+                                   <a role="button" id="share-{{$item.id}}" title="{{$item.vote.share.0}}" onclick="jotShare({{$item.id}}); return false"><i class="icon-share icon-large"><span class="sr-only">{{$item.vote.share.0}}</span></i></a>
                            {{/if}}
                        {{/if}}