mirror of https://github.com/pixelfed/pixelfed
				
				
				
			
						commit
						02426d8c55
					
				@ -0,0 +1,9 @@
 | 
			
		||||
root = true
 | 
			
		||||
 | 
			
		||||
[*]
 | 
			
		||||
indent_style = space
 | 
			
		||||
indent_size = 4
 | 
			
		||||
end_of_line = lf
 | 
			
		||||
charset = utf-8
 | 
			
		||||
trim_trailing_whitespace = true
 | 
			
		||||
insert_final_newline = true
 | 
			
		||||
@ -0,0 +1,72 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Jobs\MentionPipeline;
 | 
			
		||||
 | 
			
		||||
use Cache, Log, Redis;
 | 
			
		||||
use App\{Mention, Notification, Profile, Status};
 | 
			
		||||
use Illuminate\Bus\Queueable;
 | 
			
		||||
use Illuminate\Queue\SerializesModels;
 | 
			
		||||
use Illuminate\Queue\InteractsWithQueue;
 | 
			
		||||
use Illuminate\Contracts\Queue\ShouldQueue;
 | 
			
		||||
use Illuminate\Foundation\Bus\Dispatchable;
 | 
			
		||||
 | 
			
		||||
class MentionPipeline implements ShouldQueue
 | 
			
		||||
{
 | 
			
		||||
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
			
		||||
 | 
			
		||||
    protected $status;
 | 
			
		||||
    protected $mention;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create a new job instance.
 | 
			
		||||
     *
 | 
			
		||||
     * @return void
 | 
			
		||||
     */
 | 
			
		||||
    public function __construct(Status $status, Mention $mention)
 | 
			
		||||
    {
 | 
			
		||||
        $this->status = $status;
 | 
			
		||||
        $this->mention = $mention;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Execute the job.
 | 
			
		||||
     *
 | 
			
		||||
     * @return void
 | 
			
		||||
     */
 | 
			
		||||
    public function handle()
 | 
			
		||||
    {
 | 
			
		||||
        
 | 
			
		||||
        $status = $this->status;
 | 
			
		||||
        $mention = $this->mention;
 | 
			
		||||
        $actor = $this->status->profile;
 | 
			
		||||
        $target = $this->mention->profile_id;
 | 
			
		||||
 | 
			
		||||
        $exists = Notification::whereProfileId($target)
 | 
			
		||||
                  ->whereActorId($actor->id)
 | 
			
		||||
                  ->whereAction('mention')
 | 
			
		||||
                  ->whereItemId($status->id)
 | 
			
		||||
                  ->whereItemType('App\Status')
 | 
			
		||||
                  ->count();
 | 
			
		||||
 | 
			
		||||
        if($actor->id === $target || $exists !== 0) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
 | 
			
		||||
            $notification = new Notification;
 | 
			
		||||
            $notification->profile_id = $target;
 | 
			
		||||
            $notification->actor_id = $actor->id;
 | 
			
		||||
            $notification->action = 'mention';
 | 
			
		||||
            $notification->message = $mention->toText();
 | 
			
		||||
            $notification->rendered = $mention->toHtml();
 | 
			
		||||
            $notification->item_id = $status->id;
 | 
			
		||||
            $notification->item_type = "App\Status";
 | 
			
		||||
            $notification->save();
 | 
			
		||||
 | 
			
		||||
        } catch (Exception $e) {
 | 
			
		||||
            
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,33 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App;
 | 
			
		||||
 | 
			
		||||
use Illuminate\Database\Eloquent\Model;
 | 
			
		||||
 | 
			
		||||
class Mention extends Model
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    public function profile()
 | 
			
		||||
    {
 | 
			
		||||
      return $this->belongsTo(Profile::class, 'profile_id', 'id');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function status()
 | 
			
		||||
    {
 | 
			
		||||
      return $this->belongsTo(Status::class, 'status_id', 'id');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function toText()
 | 
			
		||||
    {
 | 
			
		||||
      $actorName = $this->status->profile->username;
 | 
			
		||||
      return "{$actorName} " . __('notification.mentionedYou');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function toHtml()
 | 
			
		||||
    {
 | 
			
		||||
      $actorName = $this->status->profile->username;
 | 
			
		||||
      $actorUrl = $this->status->profile->url();
 | 
			
		||||
      return "<a href='{$actorUrl}' class='profile-link'>{$actorName}</a> " .
 | 
			
		||||
          __('notification.mentionedYou');
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,771 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author     Mike Cochrane <mikec@mikenz.geek.nz>
 | 
			
		||||
 * @author     Nick Pope <nick@nickpope.me.uk>
 | 
			
		||||
 * @copyright  Copyright © 2010, Mike Cochrane, Nick Pope
 | 
			
		||||
 * @license    http://www.apache.org/licenses/LICENSE-2.0  Apache License v2.0
 | 
			
		||||
 * @package    Twitter.Text
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace App\Util\Lexer;
 | 
			
		||||
 | 
			
		||||
use App\Util\Lexer\Regex;
 | 
			
		||||
use App\Util\Lexer\Extractor;
 | 
			
		||||
use App\Util\Lexer\StringUtils;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Twitter Autolink Class
 | 
			
		||||
 *
 | 
			
		||||
 * Parses tweets and generates HTML anchor tags around URLs, usernames,
 | 
			
		||||
 * username/list pairs and hashtags.
 | 
			
		||||
 *
 | 
			
		||||
 * Originally written by {@link http://github.com/mikenz Mike Cochrane}, this
 | 
			
		||||
 * is based on code by {@link http://github.com/mzsanford Matt Sanford} and
 | 
			
		||||
 * heavily modified by {@link http://github.com/ngnpope Nick Pope}.
 | 
			
		||||
 *
 | 
			
		||||
 * @author     Mike Cochrane <mikec@mikenz.geek.nz>
 | 
			
		||||
 * @author     Nick Pope <nick@nickpope.me.uk>
 | 
			
		||||
 * @copyright  Copyright © 2010, Mike Cochrane, Nick Pope
 | 
			
		||||
 * @license    http://www.apache.org/licenses/LICENSE-2.0  Apache License v2.0
 | 
			
		||||
 * @package    Twitter.Text
 | 
			
		||||
 */
 | 
			
		||||
class Autolink extends Regex
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * CSS class for auto-linked URLs.
 | 
			
		||||
     *
 | 
			
		||||
     * @var  string
 | 
			
		||||
     */
 | 
			
		||||
    protected $class_url = '';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * CSS class for auto-linked username URLs.
 | 
			
		||||
     *
 | 
			
		||||
     * @var  string
 | 
			
		||||
     */
 | 
			
		||||
    protected $class_user = 'u-url mention';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * CSS class for auto-linked list URLs.
 | 
			
		||||
     *
 | 
			
		||||
     * @var  string
 | 
			
		||||
     */
 | 
			
		||||
    protected $class_list = 'u-url list-slug';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * CSS class for auto-linked hashtag URLs.
 | 
			
		||||
     *
 | 
			
		||||
     * @var  string
 | 
			
		||||
     */
 | 
			
		||||
    protected $class_hash = 'u-url hashtag';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * CSS class for auto-linked cashtag URLs.
 | 
			
		||||
     *
 | 
			
		||||
     * @var  string
 | 
			
		||||
     */
 | 
			
		||||
    protected $class_cash = 'u-url cashtag';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * URL base for username links (the username without the @ will be appended).
 | 
			
		||||
     *
 | 
			
		||||
     * @var  string
 | 
			
		||||
     */
 | 
			
		||||
    protected $url_base_user = null;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * URL base for list links (the username/list without the @ will be appended).
 | 
			
		||||
     *
 | 
			
		||||
     * @var  string
 | 
			
		||||
     */
 | 
			
		||||
    protected $url_base_list = null;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * URL base for hashtag links (the hashtag without the # will be appended).
 | 
			
		||||
     *
 | 
			
		||||
     * @var  string
 | 
			
		||||
     */
 | 
			
		||||
    protected $url_base_hash = null;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * URL base for cashtag links (the hashtag without the $ will be appended).
 | 
			
		||||
     *
 | 
			
		||||
     * @var  string
 | 
			
		||||
     */
 | 
			
		||||
    protected $url_base_cash = null;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether to include the value 'nofollow' in the 'rel' attribute.
 | 
			
		||||
     *
 | 
			
		||||
     * @var  bool
 | 
			
		||||
     */
 | 
			
		||||
    protected $nofollow = true;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether to include the value 'noopener' in the 'rel' attribute.
 | 
			
		||||
     *
 | 
			
		||||
     * @var  bool
 | 
			
		||||
     */
 | 
			
		||||
    protected $noopener = true;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether to include the value 'external' in the 'rel' attribute.
 | 
			
		||||
     *
 | 
			
		||||
     * Often this is used to be matched on in JavaScript for dynamically adding
 | 
			
		||||
     * the 'target' attribute which is deprecated in HTML 4.01.  In HTML 5 it has
 | 
			
		||||
     * been undeprecated and thus the 'target' attribute can be used.  If this is
 | 
			
		||||
     * set to false then the 'target' attribute will be output.
 | 
			
		||||
     *
 | 
			
		||||
     * @var  bool
 | 
			
		||||
     */
 | 
			
		||||
    protected $external = true; 
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The scope to open the link in.
 | 
			
		||||
     *
 | 
			
		||||
     * Support for the 'target' attribute was deprecated in HTML 4.01 but has
 | 
			
		||||
     * since been reinstated in HTML 5.  To output the 'target' attribute you
 | 
			
		||||
     * must disable the adding of the string 'external' to the 'rel' attribute.
 | 
			
		||||
     *
 | 
			
		||||
     * @var  string
 | 
			
		||||
     */
 | 
			
		||||
    protected $target = '_blank';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * attribute for invisible span tag
 | 
			
		||||
     *
 | 
			
		||||
     * @var string
 | 
			
		||||
     */
 | 
			
		||||
    protected $invisibleTagAttrs = "style='position:absolute;left:-9999px;'";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     *
 | 
			
		||||
     * @var Extractor
 | 
			
		||||
     */
 | 
			
		||||
    protected $extractor = null;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Provides fluent method chaining.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  string  $tweet        The tweet to be converted.
 | 
			
		||||
     * @param  bool    $full_encode  Whether to encode all special characters.
 | 
			
		||||
     *
 | 
			
		||||
     * @see  __construct()
 | 
			
		||||
     *
 | 
			
		||||
     * @return  Autolink
 | 
			
		||||
     */
 | 
			
		||||
    public static function create($tweet = null, $full_encode = false)
 | 
			
		||||
    {
 | 
			
		||||
        return new static($tweet, $full_encode);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Reads in a tweet to be parsed and converted to contain links.
 | 
			
		||||
     *
 | 
			
		||||
     * As the intent is to produce links and output the modified tweet to the
 | 
			
		||||
     * user, we take this opportunity to ensure that we escape user input.
 | 
			
		||||
     *
 | 
			
		||||
     * @see  htmlspecialchars()
 | 
			
		||||
     *
 | 
			
		||||
     * @param  string  $tweet        The tweet to be converted.
 | 
			
		||||
     * @param  bool    $escape       Whether to escape the tweet (default: true).
 | 
			
		||||
     * @param  bool    $full_encode  Whether to encode all special characters.
 | 
			
		||||
     */
 | 
			
		||||
    public function __construct($tweet = null, $escape = true, $full_encode = false)
 | 
			
		||||
    {
 | 
			
		||||
        if ($escape && !empty($tweet)) {
 | 
			
		||||
            if ($full_encode) {
 | 
			
		||||
                parent::__construct(htmlentities($tweet, ENT_QUOTES, 'UTF-8', false));
 | 
			
		||||
            } else {
 | 
			
		||||
                parent::__construct(htmlspecialchars($tweet, ENT_QUOTES, 'UTF-8', false));
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            parent::__construct($tweet);
 | 
			
		||||
        }
 | 
			
		||||
        $this->extractor = Extractor::create();
 | 
			
		||||
        $this->url_base_user = config('app.url') . '/';
 | 
			
		||||
        $this->url_base_list = config('app.url') . '/';
 | 
			
		||||
        $this->url_base_hash = config('app.url') . "/discover/tags/";
 | 
			
		||||
        $this->url_base_cash = config('app.url') . '/search?q=%24';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * CSS class for auto-linked URLs.
 | 
			
		||||
     *
 | 
			
		||||
     * @return  string  CSS class for URL links.
 | 
			
		||||
     */
 | 
			
		||||
    public function getURLClass()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->class_url;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * CSS class for auto-linked URLs.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  string  $v  CSS class for URL links.
 | 
			
		||||
     *
 | 
			
		||||
     * @return  Autolink  Fluid method chaining.
 | 
			
		||||
     */
 | 
			
		||||
    public function setURLClass($v)
 | 
			
		||||
    {
 | 
			
		||||
        $this->class_url = trim($v);
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * CSS class for auto-linked username URLs.
 | 
			
		||||
     *
 | 
			
		||||
     * @return  string  CSS class for username links.
 | 
			
		||||
     */
 | 
			
		||||
    public function getUsernameClass()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->class_user;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * CSS class for auto-linked username URLs.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  string  $v  CSS class for username links.
 | 
			
		||||
     *
 | 
			
		||||
     * @return  Autolink  Fluid method chaining.
 | 
			
		||||
     */
 | 
			
		||||
    public function setUsernameClass($v)
 | 
			
		||||
    {
 | 
			
		||||
        $this->class_user = trim($v);
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * CSS class for auto-linked username/list URLs.
 | 
			
		||||
     *
 | 
			
		||||
     * @return  string  CSS class for username/list links.
 | 
			
		||||
     */
 | 
			
		||||
    public function getListClass()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->class_list;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * CSS class for auto-linked username/list URLs.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  string  $v  CSS class for username/list links.
 | 
			
		||||
     *
 | 
			
		||||
     * @return  Autolink  Fluid method chaining.
 | 
			
		||||
     */
 | 
			
		||||
    public function setListClass($v)
 | 
			
		||||
    {
 | 
			
		||||
        $this->class_list = trim($v);
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * CSS class for auto-linked hashtag URLs.
 | 
			
		||||
     *
 | 
			
		||||
     * @return  string  CSS class for hashtag links.
 | 
			
		||||
     */
 | 
			
		||||
    public function getHashtagClass()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->class_hash;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * CSS class for auto-linked hashtag URLs.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  string  $v  CSS class for hashtag links.
 | 
			
		||||
     *
 | 
			
		||||
     * @return  Autolink  Fluid method chaining.
 | 
			
		||||
     */
 | 
			
		||||
    public function setHashtagClass($v)
 | 
			
		||||
    {
 | 
			
		||||
        $this->class_hash = trim($v);
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * CSS class for auto-linked cashtag URLs.
 | 
			
		||||
     *
 | 
			
		||||
     * @return  string  CSS class for cashtag links.
 | 
			
		||||
     */
 | 
			
		||||
    public function getCashtagClass()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->class_cash;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * CSS class for auto-linked cashtag URLs.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  string  $v  CSS class for cashtag links.
 | 
			
		||||
     *
 | 
			
		||||
     * @return  Autolink  Fluid method chaining.
 | 
			
		||||
     */
 | 
			
		||||
    public function setCashtagClass($v)
 | 
			
		||||
    {
 | 
			
		||||
        $this->class_cash = trim($v);
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether to include the value 'nofollow' in the 'rel' attribute.
 | 
			
		||||
     *
 | 
			
		||||
     * @return  bool  Whether to add 'nofollow' to the 'rel' attribute.
 | 
			
		||||
     */
 | 
			
		||||
    public function getNoFollow()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->nofollow;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether to include the value 'nofollow' in the 'rel' attribute.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  bool  $v  The value to add to the 'target' attribute.
 | 
			
		||||
     *
 | 
			
		||||
     * @return  Autolink  Fluid method chaining.
 | 
			
		||||
     */
 | 
			
		||||
    public function setNoFollow($v)
 | 
			
		||||
    {
 | 
			
		||||
        $this->nofollow = $v;
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether to include the value 'external' in the 'rel' attribute.
 | 
			
		||||
     *
 | 
			
		||||
     * Often this is used to be matched on in JavaScript for dynamically adding
 | 
			
		||||
     * the 'target' attribute which is deprecated in HTML 4.01.  In HTML 5 it has
 | 
			
		||||
     * been undeprecated and thus the 'target' attribute can be used.  If this is
 | 
			
		||||
     * set to false then the 'target' attribute will be output.
 | 
			
		||||
     *
 | 
			
		||||
     * @return  bool  Whether to add 'external' to the 'rel' attribute.
 | 
			
		||||
     */
 | 
			
		||||
    public function getExternal()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->external;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether to include the value 'external' in the 'rel' attribute.
 | 
			
		||||
     *
 | 
			
		||||
     * Often this is used to be matched on in JavaScript for dynamically adding
 | 
			
		||||
     * the 'target' attribute which is deprecated in HTML 4.01.  In HTML 5 it has
 | 
			
		||||
     * been undeprecated and thus the 'target' attribute can be used.  If this is
 | 
			
		||||
     * set to false then the 'target' attribute will be output.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  bool  $v  The value to add to the 'target' attribute.
 | 
			
		||||
     *
 | 
			
		||||
     * @return  Autolink  Fluid method chaining.
 | 
			
		||||
     */
 | 
			
		||||
    public function setExternal($v)
 | 
			
		||||
    {
 | 
			
		||||
        $this->external = $v;
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The scope to open the link in.
 | 
			
		||||
     *
 | 
			
		||||
     * Support for the 'target' attribute was deprecated in HTML 4.01 but has
 | 
			
		||||
     * since been reinstated in HTML 5.  To output the 'target' attribute you
 | 
			
		||||
     * must disable the adding of the string 'external' to the 'rel' attribute.
 | 
			
		||||
     *
 | 
			
		||||
     * @return  string  The value to add to the 'target' attribute.
 | 
			
		||||
     */
 | 
			
		||||
    public function getTarget()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->target;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The scope to open the link in.
 | 
			
		||||
     *
 | 
			
		||||
     * Support for the 'target' attribute was deprecated in HTML 4.01 but has
 | 
			
		||||
     * since been reinstated in HTML 5.  To output the 'target' attribute you
 | 
			
		||||
     * must disable the adding of the string 'external' to the 'rel' attribute.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  string  $v  The value to add to the 'target' attribute.
 | 
			
		||||
     *
 | 
			
		||||
     * @return  Autolink  Fluid method chaining.
 | 
			
		||||
     */
 | 
			
		||||
    public function setTarget($v)
 | 
			
		||||
    {
 | 
			
		||||
        $this->target = trim($v);
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Autolink with entities
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $tweet
 | 
			
		||||
     * @param array $entities
 | 
			
		||||
     * @return string
 | 
			
		||||
     * @since 1.1.0
 | 
			
		||||
     */
 | 
			
		||||
    public function autoLinkEntities($tweet = null, $entities = null)
 | 
			
		||||
    {
 | 
			
		||||
        if (is_null($tweet)) {
 | 
			
		||||
            $tweet = $this->tweet;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $text = '';
 | 
			
		||||
        $beginIndex = 0;
 | 
			
		||||
        foreach ($entities as $entity) {
 | 
			
		||||
            if (isset($entity['screen_name'])) {
 | 
			
		||||
                $text .= StringUtils::substr($tweet, $beginIndex, $entity['indices'][0] - $beginIndex + 1);
 | 
			
		||||
            } else {
 | 
			
		||||
                $text .= StringUtils::substr($tweet, $beginIndex, $entity['indices'][0] - $beginIndex);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (isset($entity['url'])) {
 | 
			
		||||
                $text .= $this->linkToUrl($entity);
 | 
			
		||||
            } elseif (isset($entity['hashtag'])) {
 | 
			
		||||
                $text .= $this->linkToHashtag($entity, $tweet);
 | 
			
		||||
            } elseif (isset($entity['screen_name'])) {
 | 
			
		||||
                $text .= $this->linkToMentionAndList($entity);
 | 
			
		||||
            } elseif (isset($entity['cashtag'])) {
 | 
			
		||||
                $text .= $this->linkToCashtag($entity, $tweet);
 | 
			
		||||
            }
 | 
			
		||||
            $beginIndex = $entity['indices'][1];
 | 
			
		||||
        }
 | 
			
		||||
        $text .= StringUtils::substr($tweet, $beginIndex, StringUtils::strlen($tweet));
 | 
			
		||||
        return $text;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Auto-link hashtags, URLs, usernames and lists, with JSON entities.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  string The tweet to be converted
 | 
			
		||||
     * @param  mixed  The entities info
 | 
			
		||||
     * @return string that auto-link HTML added
 | 
			
		||||
     * @since 1.1.0
 | 
			
		||||
     */
 | 
			
		||||
    public function autoLinkWithJson($tweet = null, $json = null)
 | 
			
		||||
    {
 | 
			
		||||
        // concatenate entities
 | 
			
		||||
        $entities = array();
 | 
			
		||||
        if (is_object($json)) {
 | 
			
		||||
            $json = $this->object2array($json);
 | 
			
		||||
        }
 | 
			
		||||
        if (is_array($json)) {
 | 
			
		||||
            foreach ($json as $key => $vals) {
 | 
			
		||||
                $entities = array_merge($entities, $json[$key]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // map JSON entity to twitter-text entity
 | 
			
		||||
        foreach ($entities as $idx => $entity) {
 | 
			
		||||
            if (!empty($entity['text'])) {
 | 
			
		||||
                $entities[$idx]['hashtag'] = $entity['text'];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $entities = $this->extractor->removeOverlappingEntities($entities);
 | 
			
		||||
        return $this->autoLinkEntities($tweet, $entities);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * convert Object to Array
 | 
			
		||||
     *
 | 
			
		||||
     * @param mixed $obj
 | 
			
		||||
     * @return array
 | 
			
		||||
     */
 | 
			
		||||
    protected function object2array($obj)
 | 
			
		||||
    {
 | 
			
		||||
        $array = (array) $obj;
 | 
			
		||||
        foreach ($array as $key => $var) {
 | 
			
		||||
            if (is_object($var) || is_array($var)) {
 | 
			
		||||
                $array[$key] = $this->object2array($var);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return $array;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Auto-link hashtags, URLs, usernames and lists.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  string The tweet to be converted
 | 
			
		||||
     * @return string that auto-link HTML added
 | 
			
		||||
     * @since 1.1.0
 | 
			
		||||
     */
 | 
			
		||||
    public function autoLink($tweet = null)
 | 
			
		||||
    {
 | 
			
		||||
        if (is_null($tweet)) {
 | 
			
		||||
            $tweet = $this->tweet;
 | 
			
		||||
        }
 | 
			
		||||
        $entities = $this->extractor->extractURLWithoutProtocol(false)->extractEntitiesWithIndices($tweet);
 | 
			
		||||
        return $this->autoLinkEntities($tweet, $entities);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Auto-link the @username and @username/list references in the provided text. Links to @username references will
 | 
			
		||||
     * have the usernameClass CSS classes added. Links to @username/list references will have the listClass CSS class
 | 
			
		||||
     * added.
 | 
			
		||||
     *
 | 
			
		||||
     * @return string that auto-link HTML added
 | 
			
		||||
     * @since 1.1.0
 | 
			
		||||
     */
 | 
			
		||||
    public function autoLinkUsernamesAndLists($tweet = null)
 | 
			
		||||
    {
 | 
			
		||||
        if (is_null($tweet)) {
 | 
			
		||||
            $tweet = $this->tweet;
 | 
			
		||||
        }
 | 
			
		||||
        $entities = $this->extractor->extractMentionsOrListsWithIndices($tweet);
 | 
			
		||||
        return $this->autoLinkEntities($tweet, $entities);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Auto-link #hashtag references in the provided Tweet text. The #hashtag links will have the hashtagClass CSS class
 | 
			
		||||
     * added.
 | 
			
		||||
     *
 | 
			
		||||
     * @return string that auto-link HTML added
 | 
			
		||||
     * @since 1.1.0
 | 
			
		||||
     */
 | 
			
		||||
    public function autoLinkHashtags($tweet = null)
 | 
			
		||||
    {
 | 
			
		||||
        if (is_null($tweet)) {
 | 
			
		||||
            $tweet = $this->tweet;
 | 
			
		||||
        }
 | 
			
		||||
        $entities = $this->extractor->extractHashtagsWithIndices($tweet);
 | 
			
		||||
        return $this->autoLinkEntities($tweet, $entities);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Auto-link URLs in the Tweet text provided.
 | 
			
		||||
     * <p/>
 | 
			
		||||
     * This only auto-links URLs with protocol.
 | 
			
		||||
     *
 | 
			
		||||
     * @return string that auto-link HTML added
 | 
			
		||||
     * @since 1.1.0
 | 
			
		||||
     */
 | 
			
		||||
    public function autoLinkURLs($tweet = null)
 | 
			
		||||
    {
 | 
			
		||||
        if (is_null($tweet)) {
 | 
			
		||||
            $tweet = $this->tweet;
 | 
			
		||||
        }
 | 
			
		||||
        $entities = $this->extractor->extractURLWithoutProtocol(false)->extractURLsWithIndices($tweet);
 | 
			
		||||
        return $this->autoLinkEntities($tweet, $entities);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Auto-link $cashtag references in the provided Tweet text. The $cashtag links will have the cashtagClass CSS class
 | 
			
		||||
     * added.
 | 
			
		||||
     *
 | 
			
		||||
     * @return string that auto-link HTML added
 | 
			
		||||
     * @since 1.1.0
 | 
			
		||||
     */
 | 
			
		||||
    public function autoLinkCashtags($tweet = null)
 | 
			
		||||
    {
 | 
			
		||||
        if (is_null($tweet)) {
 | 
			
		||||
            $tweet = $this->tweet;
 | 
			
		||||
        }
 | 
			
		||||
        $entities = $this->extractor->extractCashtagsWithIndices($tweet);
 | 
			
		||||
        return $this->autoLinkEntities($tweet, $entities);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function linkToUrl($entity)
 | 
			
		||||
    {
 | 
			
		||||
        if (!empty($this->class_url)) {
 | 
			
		||||
            $attributes['class'] = $this->class_url;
 | 
			
		||||
        }
 | 
			
		||||
        $attributes['href'] = $entity['url'];
 | 
			
		||||
        $linkText = $this->escapeHTML($entity['url']);
 | 
			
		||||
 | 
			
		||||
        if (!empty($entity['display_url']) && !empty($entity['expanded_url'])) {
 | 
			
		||||
            // Goal: If a user copies and pastes a tweet containing t.co'ed link, the resulting paste
 | 
			
		||||
            // should contain the full original URL (expanded_url), not the display URL.
 | 
			
		||||
            //
 | 
			
		||||
            // Method: Whenever possible, we actually emit HTML that contains expanded_url, and use
 | 
			
		||||
            // font-size:0 to hide those parts that should not be displayed (because they are not part of display_url).
 | 
			
		||||
            // Elements with font-size:0 get copied even though they are not visible.
 | 
			
		||||
            // Note that display:none doesn't work here. Elements with display:none don't get copied.
 | 
			
		||||
            //
 | 
			
		||||
            // Additionally, we want to *display* ellipses, but we don't want them copied.  To make this happen we
 | 
			
		||||
            // wrap the ellipses in a tco-ellipsis class and provide an onCopy handler that sets display:none on
 | 
			
		||||
            // everything with the tco-ellipsis class.
 | 
			
		||||
            //
 | 
			
		||||
            // As an example: The user tweets "hi http://longdomainname.com/foo"
 | 
			
		||||
            // This gets shortened to "hi http://t.co/xyzabc", with display_url = "…nname.com/foo"
 | 
			
		||||
            // This will get rendered as:
 | 
			
		||||
            // <span class='tco-ellipsis'> <!-- This stuff should get displayed but not copied -->
 | 
			
		||||
            //   …
 | 
			
		||||
            //   <!-- There's a chance the onCopy event handler might not fire. In case that happens,
 | 
			
		||||
            //        we include an   here so that the … doesn't bump up against the URL and ruin it.
 | 
			
		||||
            //        The   is inside the tco-ellipsis span so that when the onCopy handler *does*
 | 
			
		||||
            //        fire, it doesn't get copied.  Otherwise the copied text would have two spaces in a row,
 | 
			
		||||
            //        e.g. "hi  http://longdomainname.com/foo".
 | 
			
		||||
            //   <span style='font-size:0'> </span>
 | 
			
		||||
            // </span>
 | 
			
		||||
            // <span style='font-size:0'>  <!-- This stuff should get copied but not displayed -->
 | 
			
		||||
            //   http://longdomai
 | 
			
		||||
            // </span>
 | 
			
		||||
            // <span class='js-display-url'> <!-- This stuff should get displayed *and* copied -->
 | 
			
		||||
            //   nname.com/foo
 | 
			
		||||
            // </span>
 | 
			
		||||
            // <span class='tco-ellipsis'> <!-- This stuff should get displayed but not copied -->
 | 
			
		||||
            //   <span style='font-size:0'> </span>
 | 
			
		||||
            //   …
 | 
			
		||||
            // </span>
 | 
			
		||||
            //
 | 
			
		||||
            // Exception: pic.socialhub.dev images, for which expandedUrl = "https://socialhub.dev/#!/username/status/1234/photo/1
 | 
			
		||||
            // For those URLs, display_url is not a substring of expanded_url, so we don't do anything special to render the elided parts.
 | 
			
		||||
            // For a pic.socialhub.dev URL, the only elided part will be the "https://", so this is fine.
 | 
			
		||||
            $displayURL = $entity['display_url'];
 | 
			
		||||
            $expandedURL = $entity['expanded_url'];
 | 
			
		||||
            $displayURLSansEllipses = preg_replace('/…/u', '', $displayURL);
 | 
			
		||||
            $diplayURLIndexInExpandedURL = mb_strpos($expandedURL, $displayURLSansEllipses);
 | 
			
		||||
 | 
			
		||||
            if ($diplayURLIndexInExpandedURL !== false) {
 | 
			
		||||
                $beforeDisplayURL = mb_substr($expandedURL, 0, $diplayURLIndexInExpandedURL);
 | 
			
		||||
                $afterDisplayURL = mb_substr($expandedURL, $diplayURLIndexInExpandedURL + mb_strlen($displayURLSansEllipses));
 | 
			
		||||
                $precedingEllipsis = (preg_match('/\A…/u', $displayURL)) ? '…' : '';
 | 
			
		||||
                $followingEllipsis = (preg_match('/…\z/u', $displayURL)) ? '…' : '';
 | 
			
		||||
 | 
			
		||||
                $invisibleSpan = "<span {$this->invisibleTagAttrs}>";
 | 
			
		||||
 | 
			
		||||
                $linkText = "<span class='tco-ellipsis'>{$precedingEllipsis}{$invisibleSpan} </span></span>";
 | 
			
		||||
                $linkText .= "{$invisibleSpan}{$this->escapeHTML($beforeDisplayURL)}</span>";
 | 
			
		||||
                $linkText .= "<span class='js-display-url'>{$this->escapeHTML($displayURLSansEllipses)}</span>";
 | 
			
		||||
                $linkText .= "{$invisibleSpan}{$this->escapeHTML($afterDisplayURL)}</span>";
 | 
			
		||||
                $linkText .= "<span class='tco-ellipsis'>{$invisibleSpan} </span>{$followingEllipsis}</span>";
 | 
			
		||||
            } else {
 | 
			
		||||
                $linkText = $entity['display_url'];
 | 
			
		||||
            }
 | 
			
		||||
            $attributes['title'] = $entity['expanded_url'];
 | 
			
		||||
        } elseif (!empty($entity['display_url'])) {
 | 
			
		||||
            $linkText = $entity['display_url'];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this->linkToText($entity, $linkText, $attributes);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     *
 | 
			
		||||
     * @param array  $entity
 | 
			
		||||
     * @param string $tweet
 | 
			
		||||
     * @return string
 | 
			
		||||
     * @since 1.1.0
 | 
			
		||||
     */
 | 
			
		||||
    public function linkToHashtag($entity, $tweet = null)
 | 
			
		||||
    {
 | 
			
		||||
        if (is_null($tweet)) {
 | 
			
		||||
            $tweet = $this->tweet;
 | 
			
		||||
        }
 | 
			
		||||
        $this->target = false;
 | 
			
		||||
        $attributes = array();
 | 
			
		||||
        $class = array();
 | 
			
		||||
        $hash = StringUtils::substr($tweet, $entity['indices'][0], 1);
 | 
			
		||||
        $linkText = $hash . $entity['hashtag'];
 | 
			
		||||
 | 
			
		||||
        $attributes['href'] = $this->url_base_hash . $entity['hashtag'] . '?src=hash';
 | 
			
		||||
        $attributes['title'] = '#' . $entity['hashtag'];
 | 
			
		||||
        if (!empty($this->class_hash)) {
 | 
			
		||||
            $class[] = $this->class_hash;
 | 
			
		||||
        }
 | 
			
		||||
        if (preg_match(self::$patterns['rtl_chars'], $linkText)) {
 | 
			
		||||
            $class[] = 'rtl';
 | 
			
		||||
        }
 | 
			
		||||
        if (!empty($class)) {
 | 
			
		||||
            $attributes['class'] = join(' ', $class);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this->linkToText($entity, $linkText, $attributes);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     *
 | 
			
		||||
     * @param array  $entity
 | 
			
		||||
     * @return string
 | 
			
		||||
     * @since 1.1.0
 | 
			
		||||
     */
 | 
			
		||||
    public function linkToMentionAndList($entity)
 | 
			
		||||
    {
 | 
			
		||||
        $attributes = array();
 | 
			
		||||
 | 
			
		||||
        if (!empty($entity['list_slug'])) {
 | 
			
		||||
            # Replace the list and username
 | 
			
		||||
            $linkText = $entity['screen_name'] . $entity['list_slug'];
 | 
			
		||||
            $class = $this->class_list;
 | 
			
		||||
            $url = $this->url_base_list . $linkText;
 | 
			
		||||
        } else {
 | 
			
		||||
            # Replace the username
 | 
			
		||||
            $linkText = $entity['screen_name'];
 | 
			
		||||
            $class = $this->class_user;
 | 
			
		||||
            $url = $this->url_base_user . $linkText;
 | 
			
		||||
        }
 | 
			
		||||
        if (!empty($class)) {
 | 
			
		||||
            $attributes['class'] = $class;
 | 
			
		||||
        }
 | 
			
		||||
        $attributes['href'] = $url;
 | 
			
		||||
 | 
			
		||||
        return $this->linkToText($entity, $linkText, $attributes);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     *
 | 
			
		||||
     * @param array  $entity
 | 
			
		||||
     * @param string $tweet
 | 
			
		||||
     * @return string
 | 
			
		||||
     * @since 1.1.0
 | 
			
		||||
     */
 | 
			
		||||
    public function linkToCashtag($entity, $tweet = null)
 | 
			
		||||
    {
 | 
			
		||||
        if (is_null($tweet)) {
 | 
			
		||||
            $tweet = $this->tweet;
 | 
			
		||||
        }
 | 
			
		||||
        $attributes = array();
 | 
			
		||||
        $doller = StringUtils::substr($tweet, $entity['indices'][0], 1);
 | 
			
		||||
        $linkText = $doller . $entity['cashtag'];
 | 
			
		||||
        $attributes['href'] = $this->url_base_cash . $entity['cashtag'];
 | 
			
		||||
        $attributes['title'] = $linkText;
 | 
			
		||||
        if (!empty($this->class_cash)) {
 | 
			
		||||
            $attributes['class'] = $this->class_cash;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this->linkToText($entity, $linkText, $attributes);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     *
 | 
			
		||||
     * @param array $entity
 | 
			
		||||
     * @param string $text
 | 
			
		||||
     * @param array $attributes
 | 
			
		||||
     * @return string
 | 
			
		||||
     * @since 1.1.0
 | 
			
		||||
     */
 | 
			
		||||
    public function linkToText(array $entity, $text, $attributes = array())
 | 
			
		||||
    {
 | 
			
		||||
        $rel = array();
 | 
			
		||||
        if ($this->external) {
 | 
			
		||||
            $rel[] = 'external';
 | 
			
		||||
        }
 | 
			
		||||
        if ($this->nofollow) {
 | 
			
		||||
            $rel[] = 'nofollow';
 | 
			
		||||
        }
 | 
			
		||||
        if ($this->noopener) {
 | 
			
		||||
            $rel[] = 'noopener';
 | 
			
		||||
        }
 | 
			
		||||
        if (!empty($rel)) {
 | 
			
		||||
            $attributes['rel'] = join(' ', $rel);
 | 
			
		||||
        }
 | 
			
		||||
        if ($this->target) {
 | 
			
		||||
            $attributes['target'] = $this->target;
 | 
			
		||||
        }
 | 
			
		||||
        $link = '<a';
 | 
			
		||||
        foreach ($attributes as $key => $val) {
 | 
			
		||||
            $link .= ' ' . $key . '="' . $this->escapeHTML($val) . '"';
 | 
			
		||||
        }
 | 
			
		||||
        $link .= '>' . $text . '</a>';
 | 
			
		||||
        return $link;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * html escape
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $text
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
    protected function escapeHTML($text)
 | 
			
		||||
    {
 | 
			
		||||
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8', false);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,202 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author     Nick Pope <nick@nickpope.me.uk>
 | 
			
		||||
 * @copyright  Copyright © 2010, Nick Pope
 | 
			
		||||
 * @license    http://www.apache.org/licenses/LICENSE-2.0  Apache License v2.0
 | 
			
		||||
 * @package    Twitter.Text
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace App\Util\Lexer;
 | 
			
		||||
 | 
			
		||||
use App\Util\Lexer\Regex;
 | 
			
		||||
use App\Util\Lexer\StringUtils;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Twitter HitHighlighter Class
 | 
			
		||||
 *
 | 
			
		||||
 * Performs "hit highlighting" on tweets that have been auto-linked already.
 | 
			
		||||
 * Useful with the results returned from the search API.
 | 
			
		||||
 *
 | 
			
		||||
 * Originally written by {@link http://github.com/mikenz Mike Cochrane}, this
 | 
			
		||||
 * is based on code by {@link http://github.com/mzsanford Matt Sanford} and
 | 
			
		||||
 * heavily modified by {@link http://github.com/ngnpope Nick Pope}.
 | 
			
		||||
 *
 | 
			
		||||
 * @author     Nick Pope <nick@nickpope.me.uk>
 | 
			
		||||
 * @copyright  Copyright © 2010, Nick Pope
 | 
			
		||||
 * @license    http://www.apache.org/licenses/LICENSE-2.0  Apache License v2.0
 | 
			
		||||
 * @package    Twitter.Text
 | 
			
		||||
 */
 | 
			
		||||
class HitHighlighter extends Regex
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The tag to surround hits with.
 | 
			
		||||
     *
 | 
			
		||||
     * @var  string
 | 
			
		||||
     */
 | 
			
		||||
    protected $tag = 'em';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Provides fluent method chaining.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  string  $tweet        The tweet to be hit highlighted.
 | 
			
		||||
     * @param  bool    $full_encode  Whether to encode all special characters.
 | 
			
		||||
     *
 | 
			
		||||
     * @see  __construct()
 | 
			
		||||
     *
 | 
			
		||||
     * @return  HitHighlighter
 | 
			
		||||
     */
 | 
			
		||||
    public static function create($tweet = null, $full_encode = false)
 | 
			
		||||
    {
 | 
			
		||||
        return new self($tweet, $full_encode);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Reads in a tweet to be parsed and hit highlighted.
 | 
			
		||||
     *
 | 
			
		||||
     * We take this opportunity to ensure that we escape user input.
 | 
			
		||||
     *
 | 
			
		||||
     * @see  htmlspecialchars()
 | 
			
		||||
     *
 | 
			
		||||
     * @param  string  $tweet        The tweet to be hit highlighted.
 | 
			
		||||
     * @param  bool    $escape       Whether to escape the tweet (default: true).
 | 
			
		||||
     * @param  bool    $full_encode  Whether to encode all special characters.
 | 
			
		||||
     */
 | 
			
		||||
    public function __construct($tweet = null, $escape = true, $full_encode = false)
 | 
			
		||||
    {
 | 
			
		||||
        if (!empty($tweet) && $escape) {
 | 
			
		||||
            if ($full_encode) {
 | 
			
		||||
                parent::__construct(htmlentities($tweet, ENT_QUOTES, 'UTF-8', false));
 | 
			
		||||
            } else {
 | 
			
		||||
                parent::__construct(htmlspecialchars($tweet, ENT_QUOTES, 'UTF-8', false));
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            parent::__construct($tweet);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set the highlighting tag to surround hits with.  The default tag is 'em'.
 | 
			
		||||
     *
 | 
			
		||||
     * @return  string  The tag name.
 | 
			
		||||
     */
 | 
			
		||||
    public function getTag()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->tag;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set the highlighting tag to surround hits with.  The default tag is 'em'.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  string  $v  The tag name.
 | 
			
		||||
     *
 | 
			
		||||
     * @return  HitHighlighter  Fluid method chaining.
 | 
			
		||||
     */
 | 
			
		||||
    public function setTag($v)
 | 
			
		||||
    {
 | 
			
		||||
        $this->tag = $v;
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Hit highlights the tweet.
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $tweet The tweet to be hit highlighted.
 | 
			
		||||
     * @param array  $hits  An array containing the start and end index pairs
 | 
			
		||||
     *                        for the highlighting.
 | 
			
		||||
     * @param bool   $escape      Whether to escape the tweet (default: true).
 | 
			
		||||
     * @param bool   $full_encode  Whether to encode all special characters.
 | 
			
		||||
     *
 | 
			
		||||
     * @return  string  The hit highlighted tweet.
 | 
			
		||||
     */
 | 
			
		||||
    public function highlight($tweet = null, array $hits = null)
 | 
			
		||||
    {
 | 
			
		||||
        if (is_null($tweet)) {
 | 
			
		||||
            $tweet = $this->tweet;
 | 
			
		||||
        }
 | 
			
		||||
        if (empty($hits)) {
 | 
			
		||||
            return $tweet;
 | 
			
		||||
        }
 | 
			
		||||
        $highlightTweet = '';
 | 
			
		||||
        $tags = array('<' . $this->tag . '>', '</' . $this->tag . '>');
 | 
			
		||||
        # Check whether we can simply replace or whether we need to chunk...
 | 
			
		||||
        if (strpos($tweet, '<') === false) {
 | 
			
		||||
            $ti = 0; // tag increment (for added tags)
 | 
			
		||||
            $highlightTweet = $tweet;
 | 
			
		||||
            foreach ($hits as $hit) {
 | 
			
		||||
                $highlightTweet = StringUtils::substrReplace($highlightTweet, $tags[0], $hit[0] + $ti, 0);
 | 
			
		||||
                $ti += StringUtils::strlen($tags[0]);
 | 
			
		||||
                $highlightTweet = StringUtils::substrReplace($highlightTweet, $tags[1], $hit[1] + $ti, 0);
 | 
			
		||||
                $ti += StringUtils::strlen($tags[1]);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            $chunks = preg_split('/[<>]/iu', $tweet);
 | 
			
		||||
            $chunk = $chunks[0];
 | 
			
		||||
            $chunk_index = 0;
 | 
			
		||||
            $chunk_cursor = 0;
 | 
			
		||||
            $offset = 0;
 | 
			
		||||
            $start_in_chunk = false;
 | 
			
		||||
            # Flatten the multidimensional hits array:
 | 
			
		||||
            $hits_flat = array();
 | 
			
		||||
            foreach ($hits as $hit) {
 | 
			
		||||
                $hits_flat = array_merge($hits_flat, $hit);
 | 
			
		||||
            }
 | 
			
		||||
            # Loop over the hit indices:
 | 
			
		||||
            for ($index = 0; $index < count($hits_flat); $index++) {
 | 
			
		||||
                $hit = $hits_flat[$index];
 | 
			
		||||
                $tag = $tags[$index % 2];
 | 
			
		||||
                $placed = false;
 | 
			
		||||
                while ($chunk !== null && $hit >= ($i = $offset + StringUtils::strlen($chunk))) {
 | 
			
		||||
                    $highlightTweet .= StringUtils::substr($chunk, $chunk_cursor);
 | 
			
		||||
                    if ($start_in_chunk && $hit === $i) {
 | 
			
		||||
                        $highlightTweet .= $tag;
 | 
			
		||||
                        $placed = true;
 | 
			
		||||
                    }
 | 
			
		||||
                    if (isset($chunks[$chunk_index + 1])) {
 | 
			
		||||
                        $highlightTweet .= '<' . $chunks[$chunk_index + 1] . '>';
 | 
			
		||||
                    }
 | 
			
		||||
                    $offset += StringUtils::strlen($chunk);
 | 
			
		||||
                    $chunk_cursor = 0;
 | 
			
		||||
                    $chunk_index += 2;
 | 
			
		||||
                    $chunk = (isset($chunks[$chunk_index]) ? $chunks[$chunk_index] : null);
 | 
			
		||||
                    $start_in_chunk = false;
 | 
			
		||||
                }
 | 
			
		||||
                if (!$placed && $chunk !== null) {
 | 
			
		||||
                    $hit_spot = $hit - $offset;
 | 
			
		||||
                    $highlightTweet .= StringUtils::substr($chunk, $chunk_cursor, $hit_spot - $chunk_cursor) . $tag;
 | 
			
		||||
                    $chunk_cursor = $hit_spot;
 | 
			
		||||
                    $start_in_chunk = ($index % 2 === 0);
 | 
			
		||||
                    $placed = true;
 | 
			
		||||
                }
 | 
			
		||||
                # Ultimate fallback - hits that run off the end get a closing tag:
 | 
			
		||||
                if (!$placed) {
 | 
			
		||||
                    $highlightTweet .= $tag;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if ($chunk !== null) {
 | 
			
		||||
                if ($chunk_cursor < StringUtils::strlen($chunk)) {
 | 
			
		||||
                    $highlightTweet .= StringUtils::substr($chunk, $chunk_cursor);
 | 
			
		||||
                }
 | 
			
		||||
                for ($index = $chunk_index + 1; $index < count($chunks); $index++) {
 | 
			
		||||
                    $highlightTweet .= ($index % 2 === 0 ? $chunks[$index] : '<' . $chunks[$index] . '>');
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return $highlightTweet;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Hit highlights the tweet.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  array  $hits  An array containing the start and end index pairs
 | 
			
		||||
     *                       for the highlighting.
 | 
			
		||||
     *
 | 
			
		||||
     * @return  string  The hit highlighted tweet.
 | 
			
		||||
     * @deprecated since version 1.1.0
 | 
			
		||||
     */
 | 
			
		||||
    public function addHitHighlighting(array $hits)
 | 
			
		||||
    {
 | 
			
		||||
        return $this->highlight($this->tweet, $hits);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
											
												
													File diff suppressed because one or more lines are too long
												
											
										
									
								@ -0,0 +1,104 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author     Takashi Nojima
 | 
			
		||||
 * @copyright  Copyright 2014, Takashi Nojima
 | 
			
		||||
 * @license    http://www.apache.org/licenses/LICENSE-2.0  Apache License v2.0
 | 
			
		||||
 * @package    Twitter.Text
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace App\Util\Lexer;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * String utility
 | 
			
		||||
 *
 | 
			
		||||
 * @author     Takashi Nojima
 | 
			
		||||
 * @copyright  Copyright 2014, Takashi Nojima
 | 
			
		||||
 * @license    http://www.apache.org/licenses/LICENSE-2.0  Apache License v2.0
 | 
			
		||||
 * @package    Twitter
 | 
			
		||||
 */
 | 
			
		||||
class StringUtils
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * alias of mb_substr
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $str
 | 
			
		||||
     * @param integer $start
 | 
			
		||||
     * @param integer $length
 | 
			
		||||
     * @param string $encoding
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
    public static function substr($str, $start, $length = null, $encoding = 'UTF-8')
 | 
			
		||||
    {
 | 
			
		||||
        if (is_null($length)) {
 | 
			
		||||
            // for PHP <= 5.4.7
 | 
			
		||||
            $length = mb_strlen($str, $encoding);
 | 
			
		||||
        }
 | 
			
		||||
        return mb_substr($str, $start, $length, $encoding);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * alias of mb_strlen
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $str
 | 
			
		||||
     * @param string $encoding
 | 
			
		||||
     * @return integer
 | 
			
		||||
     */
 | 
			
		||||
    public static function strlen($str, $encoding = 'UTF-8')
 | 
			
		||||
    {
 | 
			
		||||
        return mb_strlen($str, $encoding);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * alias of mb_strpos
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $haystack
 | 
			
		||||
     * @param string $needle
 | 
			
		||||
     * @param integer $offset
 | 
			
		||||
     * @param string $encoding
 | 
			
		||||
     * @return integer
 | 
			
		||||
     */
 | 
			
		||||
    public static function strpos($haystack, $needle, $offset = 0, $encoding = 'UTF-8')
 | 
			
		||||
    {
 | 
			
		||||
        return mb_strpos($haystack, $needle, $offset, $encoding);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A multibyte-aware substring replacement function.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  string  $string       The string to modify.
 | 
			
		||||
     * @param  string  $replacement  The replacement string.
 | 
			
		||||
     * @param  int     $start        The start of the replacement.
 | 
			
		||||
     * @param  int     $length       The number of characters to replace.
 | 
			
		||||
     * @param  string  $encoding     The encoding of the string.
 | 
			
		||||
     *
 | 
			
		||||
     * @return  string  The modified string.
 | 
			
		||||
     *
 | 
			
		||||
     * @see http://www.php.net/manual/en/function.substr-replace.php#90146
 | 
			
		||||
     */
 | 
			
		||||
    public static function substrReplace($string, $replacement, $start, $length = null, $encoding = 'UTF-8')
 | 
			
		||||
    {
 | 
			
		||||
        if (extension_loaded('mbstring') === true) {
 | 
			
		||||
            $string_length = static::strlen($string, $encoding);
 | 
			
		||||
            if ($start < 0) {
 | 
			
		||||
                $start = max(0, $string_length + $start);
 | 
			
		||||
            } elseif ($start > $string_length) {
 | 
			
		||||
                $start = $string_length;
 | 
			
		||||
            }
 | 
			
		||||
            if ($length < 0) {
 | 
			
		||||
                $length = max(0, $string_length - $start + $length);
 | 
			
		||||
            } elseif ((is_null($length) === true) || ($length > $string_length)) {
 | 
			
		||||
                $length = $string_length;
 | 
			
		||||
            }
 | 
			
		||||
            if (($start + $length) > $string_length) {
 | 
			
		||||
                $length = $string_length - $start;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $suffixOffset = $start + $length;
 | 
			
		||||
            $suffixLength = $string_length - $start - $length;
 | 
			
		||||
            return static::substr($string, 0, $start, $encoding) . $replacement . static::substr($string, $suffixOffset, $suffixLength, $encoding);
 | 
			
		||||
        }
 | 
			
		||||
        return (is_null($length) === true) ? substr_replace($string, $replacement, $start) : substr_replace($string, $replacement, $start, $length);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,388 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author     Nick Pope <nick@nickpope.me.uk>
 | 
			
		||||
 * @copyright  Copyright © 2010, Nick Pope
 | 
			
		||||
 * @license    http://www.apache.org/licenses/LICENSE-2.0  Apache License v2.0
 | 
			
		||||
 * @package    Twitter.Text
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace App\Util\Lexer;
 | 
			
		||||
 | 
			
		||||
use App\Util\Lexer\Regex;
 | 
			
		||||
use App\Util\Lexer\Extractor;
 | 
			
		||||
use App\Util\Lexer\StringUtils;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Twitter Validator Class
 | 
			
		||||
 *
 | 
			
		||||
 * Performs "validation" on tweets.
 | 
			
		||||
 *
 | 
			
		||||
 * Originally written by {@link http://github.com/mikenz Mike Cochrane}, this
 | 
			
		||||
 * is based on code by {@link http://github.com/mzsanford Matt Sanford} and
 | 
			
		||||
 * heavily modified by {@link http://github.com/ngnpope Nick Pope}.
 | 
			
		||||
 *
 | 
			
		||||
 * @author     Nick Pope <nick@nickpope.me.uk>
 | 
			
		||||
 * @copyright  Copyright © 2010, Nick Pope
 | 
			
		||||
 * @license    http://www.apache.org/licenses/LICENSE-2.0  Apache License v2.0
 | 
			
		||||
 * @package    Twitter.Text
 | 
			
		||||
 */
 | 
			
		||||
class Validator extends Regex
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The maximum length of a tweet.
 | 
			
		||||
     *
 | 
			
		||||
     * @var  int
 | 
			
		||||
     */
 | 
			
		||||
    const MAX_LENGTH = 140;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The length of a short URL beginning with http:
 | 
			
		||||
     *
 | 
			
		||||
     * @var  int
 | 
			
		||||
     */
 | 
			
		||||
    protected $short_url_length = 23;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The length of a short URL beginning with http:
 | 
			
		||||
     *
 | 
			
		||||
     * @var  int
 | 
			
		||||
     */
 | 
			
		||||
    protected $short_url_length_https = 23;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     *
 | 
			
		||||
     * @var Extractor
 | 
			
		||||
     */
 | 
			
		||||
    protected $extractor = null;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Provides fluent method chaining.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  string  $tweet  The tweet to be validated.
 | 
			
		||||
     * @param  mixed   $config Setup short URL length from Twitter API /help/configuration response.
 | 
			
		||||
     *
 | 
			
		||||
     * @see  __construct()
 | 
			
		||||
     *
 | 
			
		||||
     * @return  Validator
 | 
			
		||||
     */
 | 
			
		||||
    public static function create($tweet = null, $config = null)
 | 
			
		||||
    {
 | 
			
		||||
        return new self($tweet, $config);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Reads in a tweet to be parsed and validates it.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  string  $tweet  The tweet to validate.
 | 
			
		||||
     */
 | 
			
		||||
    public function __construct($tweet = null, $config = null)
 | 
			
		||||
    {
 | 
			
		||||
        parent::__construct($tweet);
 | 
			
		||||
        if (!empty($config)) {
 | 
			
		||||
            $this->setConfiguration($config);
 | 
			
		||||
        }
 | 
			
		||||
        $this->extractor = Extractor::create();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Setup short URL length from Twitter API /help/configuration response
 | 
			
		||||
     *
 | 
			
		||||
     * @param mixed $config
 | 
			
		||||
     * @return Validator
 | 
			
		||||
     * @link https://dev.twitter.com/docs/api/1/get/help/configuration
 | 
			
		||||
     */
 | 
			
		||||
    public function setConfiguration($config)
 | 
			
		||||
    {
 | 
			
		||||
        if (is_array($config)) {
 | 
			
		||||
            // setup from array
 | 
			
		||||
            if (isset($config['short_url_length'])) {
 | 
			
		||||
                $this->setShortUrlLength($config['short_url_length']);
 | 
			
		||||
            }
 | 
			
		||||
            if (isset($config['short_url_length_https'])) {
 | 
			
		||||
                $this->setShortUrlLengthHttps($config['short_url_length_https']);
 | 
			
		||||
            }
 | 
			
		||||
        } elseif (is_object($config)) {
 | 
			
		||||
            // setup from object
 | 
			
		||||
            if (isset($config->short_url_length)) {
 | 
			
		||||
                $this->setShortUrlLength($config->short_url_length);
 | 
			
		||||
            }
 | 
			
		||||
            if (isset($config->short_url_length_https)) {
 | 
			
		||||
                $this->setShortUrlLengthHttps($config->short_url_length_https);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set the length of a short URL beginning with http:
 | 
			
		||||
     *
 | 
			
		||||
     * @param mixed $length
 | 
			
		||||
     * @return Validator
 | 
			
		||||
     */
 | 
			
		||||
    public function setShortUrlLength($length)
 | 
			
		||||
    {
 | 
			
		||||
        $this->short_url_length = intval($length);
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the length of a short URL beginning with http:
 | 
			
		||||
     *
 | 
			
		||||
     * @return int
 | 
			
		||||
     */
 | 
			
		||||
    public function getShortUrlLength()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->short_url_length;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set the length of a short URL beginning with https:
 | 
			
		||||
     *
 | 
			
		||||
     * @param mixed $length
 | 
			
		||||
     * @return Validator
 | 
			
		||||
     */
 | 
			
		||||
    public function setShortUrlLengthHttps($length)
 | 
			
		||||
    {
 | 
			
		||||
        $this->short_url_length_https = intval($length);
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the length of a short URL beginning with https:
 | 
			
		||||
     *
 | 
			
		||||
     * @return int
 | 
			
		||||
     */
 | 
			
		||||
    public function getShortUrlLengthHttps()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->short_url_length_https;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check whether a tweet is valid.
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $tweet The tweet to validate.
 | 
			
		||||
     * @return  boolean  Whether the tweet is valid.
 | 
			
		||||
     */
 | 
			
		||||
    public function isValidTweetText($tweet = null)
 | 
			
		||||
    {
 | 
			
		||||
        if (is_null($tweet)) {
 | 
			
		||||
            $tweet = $this->tweet;
 | 
			
		||||
        }
 | 
			
		||||
        $length = $this->getTweetLength($tweet);
 | 
			
		||||
        if (!$tweet || !$length) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        if ($length > self::MAX_LENGTH) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        if (preg_match(self::$patterns['invalid_characters'], $tweet)) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check whether a tweet is valid.
 | 
			
		||||
     *
 | 
			
		||||
     * @return  boolean  Whether the tweet is valid.
 | 
			
		||||
     * @deprecated since version 1.1.0
 | 
			
		||||
     */
 | 
			
		||||
    public function validateTweet()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->isValidTweetText();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check whether a username is valid.
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $username The username to validate.
 | 
			
		||||
     * @return  boolean  Whether the username is valid.
 | 
			
		||||
     */
 | 
			
		||||
    public function isValidUsername($username = null)
 | 
			
		||||
    {
 | 
			
		||||
        if (is_null($username)) {
 | 
			
		||||
            $username = $this->tweet;
 | 
			
		||||
        }
 | 
			
		||||
        $length = StringUtils::strlen($username);
 | 
			
		||||
        if (empty($username) || !$length) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        $extracted = $this->extractor->extractMentionedScreennames($username);
 | 
			
		||||
        return count($extracted) === 1 && $extracted[0] === substr($username, 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check whether a username is valid.
 | 
			
		||||
     *
 | 
			
		||||
     * @return  boolean  Whether the username is valid.
 | 
			
		||||
     * @deprecated since version 1.1.0
 | 
			
		||||
     */
 | 
			
		||||
    public function validateUsername()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->isValidUsername();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check whether a list is valid.
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $list The list name to validate.
 | 
			
		||||
     * @return  boolean  Whether the list is valid.
 | 
			
		||||
     */
 | 
			
		||||
    public function isValidList($list = null)
 | 
			
		||||
    {
 | 
			
		||||
        if (is_null($list)) {
 | 
			
		||||
            $list = $this->tweet;
 | 
			
		||||
        }
 | 
			
		||||
        $length = StringUtils::strlen($list);
 | 
			
		||||
        if (empty($list) || !$length) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        preg_match(self::$patterns['valid_mentions_or_lists'], $list, $matches);
 | 
			
		||||
        $matches = array_pad($matches, 5, '');
 | 
			
		||||
        return isset($matches) && $matches[1] === '' && $matches[4] && !empty($matches[4]) && $matches[5] === '';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check whether a list is valid.
 | 
			
		||||
     *
 | 
			
		||||
     * @return  boolean  Whether the list is valid.
 | 
			
		||||
     * @deprecated since version 1.1.0
 | 
			
		||||
     */
 | 
			
		||||
    public function validateList()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->isValidList();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check whether a hashtag is valid.
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $hashtag The hashtag to validate.
 | 
			
		||||
     * @return  boolean  Whether the hashtag is valid.
 | 
			
		||||
     */
 | 
			
		||||
    public function isValidHashtag($hashtag = null)
 | 
			
		||||
    {
 | 
			
		||||
        if (is_null($hashtag)) {
 | 
			
		||||
            $hashtag = $this->tweet;
 | 
			
		||||
        }
 | 
			
		||||
        $length = StringUtils::strlen($hashtag);
 | 
			
		||||
        if (empty($hashtag) || !$length) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        $extracted = $this->extractor->extractHashtags($hashtag);
 | 
			
		||||
        return count($extracted) === 1 && $extracted[0] === substr($hashtag, 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check whether a hashtag is valid.
 | 
			
		||||
     *
 | 
			
		||||
     * @return  boolean  Whether the hashtag is valid.
 | 
			
		||||
     * @deprecated since version 1.1.0
 | 
			
		||||
     */
 | 
			
		||||
    public function validateHashtag()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->isValidHashtag();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check whether a URL is valid.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  string   $url               The url to validate.
 | 
			
		||||
     * @param  boolean  $unicode_domains   Consider the domain to be unicode.
 | 
			
		||||
     * @param  boolean  $require_protocol  Require a protocol for valid domain?
 | 
			
		||||
     *
 | 
			
		||||
     * @return  boolean  Whether the URL is valid.
 | 
			
		||||
     */
 | 
			
		||||
    public function isValidURL($url = null, $unicode_domains = true, $require_protocol = true)
 | 
			
		||||
    {
 | 
			
		||||
        if (is_null($url)) {
 | 
			
		||||
            $url = $this->tweet;
 | 
			
		||||
        }
 | 
			
		||||
        $length = StringUtils::strlen($url);
 | 
			
		||||
        if (empty($url) || !$length) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        preg_match(self::$patterns['validate_url_unencoded'], $url, $matches);
 | 
			
		||||
        $match = array_shift($matches);
 | 
			
		||||
        if (!$matches || $match !== $url) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        list($scheme, $authority, $path, $query, $fragment) = array_pad($matches, 5, '');
 | 
			
		||||
        # Check scheme, path, query, fragment:
 | 
			
		||||
        if (($require_protocol && !(
 | 
			
		||||
            self::isValidMatch($scheme, self::$patterns['validate_url_scheme']) && preg_match('/^https?$/i', $scheme))
 | 
			
		||||
            ) || !self::isValidMatch($path, self::$patterns['validate_url_path']) || !self::isValidMatch($query, self::$patterns['validate_url_query'], true)
 | 
			
		||||
            || !self::isValidMatch($fragment, self::$patterns['validate_url_fragment'], true)) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        # Check authority:
 | 
			
		||||
        $authority_pattern = $unicode_domains ? 'validate_url_unicode_authority' : 'validate_url_authority';
 | 
			
		||||
        return self::isValidMatch($authority, self::$patterns[$authority_pattern]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check whether a URL is valid.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  boolean  $unicode_domains   Consider the domain to be unicode.
 | 
			
		||||
     * @param  boolean  $require_protocol  Require a protocol for valid domain?
 | 
			
		||||
     *
 | 
			
		||||
     * @return  boolean  Whether the URL is valid.
 | 
			
		||||
     * @deprecated since version 1.1.0
 | 
			
		||||
     */
 | 
			
		||||
    public function validateURL($unicode_domains = true, $require_protocol = true)
 | 
			
		||||
    {
 | 
			
		||||
        return $this->isValidURL(null, $unicode_domains, $require_protocol);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Determines the length of a tweet.  Takes shortening of URLs into account.
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $tweet The tweet to validate.
 | 
			
		||||
     * @return  int  the length of a tweet.
 | 
			
		||||
     */
 | 
			
		||||
    public function getTweetLength($tweet = null)
 | 
			
		||||
    {
 | 
			
		||||
        if (is_null($tweet)) {
 | 
			
		||||
            $tweet = $this->tweet;
 | 
			
		||||
        }
 | 
			
		||||
        $length = StringUtils::strlen($tweet);
 | 
			
		||||
        $urls_with_indices = $this->extractor->extractURLsWithIndices($tweet);
 | 
			
		||||
        foreach ($urls_with_indices as $x) {
 | 
			
		||||
            $length += $x['indices'][0] - $x['indices'][1];
 | 
			
		||||
            $length += stripos($x['url'], 'https://') === 0 ? $this->short_url_length_https : $this->short_url_length;
 | 
			
		||||
        }
 | 
			
		||||
        return $length;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Determines the length of a tweet.  Takes shortening of URLs into account.
 | 
			
		||||
     *
 | 
			
		||||
     * @return  int  the length of a tweet.
 | 
			
		||||
     * @deprecated since version 1.1.0
 | 
			
		||||
     */
 | 
			
		||||
    public function getLength()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->getTweetLength();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A helper function to check for a valid match.  Used in URL validation.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  string   $string    The subject string to test.
 | 
			
		||||
     * @param  string   $pattern   The pattern to match against.
 | 
			
		||||
     * @param  boolean  $optional  Whether a match is compulsory or not.
 | 
			
		||||
     *
 | 
			
		||||
     * @return  boolean  Whether an exact match was found.
 | 
			
		||||
     */
 | 
			
		||||
    protected static function isValidMatch($string, $pattern, $optional = false)
 | 
			
		||||
    {
 | 
			
		||||
        $found = preg_match($pattern, $string, $matches);
 | 
			
		||||
        if (!$optional) {
 | 
			
		||||
            return (($string || $string === '') && $found && $matches[0] === $string);
 | 
			
		||||
        } else {
 | 
			
		||||
            return !(($string || $string === '') && (!$found || $matches[0] !== $string));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,34 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
use Illuminate\Support\Facades\Schema;
 | 
			
		||||
use Illuminate\Database\Schema\Blueprint;
 | 
			
		||||
use Illuminate\Database\Migrations\Migration;
 | 
			
		||||
 | 
			
		||||
class CreateMentionsTable extends Migration
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Run the migrations.
 | 
			
		||||
     *
 | 
			
		||||
     * @return void
 | 
			
		||||
     */
 | 
			
		||||
    public function up()
 | 
			
		||||
    {
 | 
			
		||||
        Schema::create('mentions', function (Blueprint $table) {
 | 
			
		||||
            $table->bigIncrements('id');
 | 
			
		||||
            $table->bigInteger('status_id')->unsigned();
 | 
			
		||||
            $table->bigInteger('profile_id')->unsigned();
 | 
			
		||||
            $table->boolean('local')->default(true);
 | 
			
		||||
            $table->timestamps();
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Reverse the migrations.
 | 
			
		||||
     *
 | 
			
		||||
     * @return void
 | 
			
		||||
     */
 | 
			
		||||
    public function down()
 | 
			
		||||
    {
 | 
			
		||||
        Schema::dropIfExists('mentions');
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
											
												
													File diff suppressed because one or more lines are too long
												
											
										
									
								@ -0,0 +1 @@
 | 
			
		||||
!function(t){var n={};function e(o){if(n[o])return n[o].exports;var r=n[o]={i:o,l:!1,exports:{}};return t[o].call(r.exports,r,r.exports,e),r.l=!0,r.exports}e.m=t,e.c=n,e.d=function(t,n,o){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:o})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,n){return Object.prototype.hasOwnProperty.call(t,n)},e.p="/",e(e.s=1)}({1:function(t,n,e){t.exports=e("nr3X")},nr3X:function(t,n){$(document).ready(function(){$(".pagination").hide();var t=document.querySelector(".notification-page .list-group");new InfiniteScroll(t,{path:".pagination__next",append:".notification-page .list-group",status:".page-load-status",history:!0})})}});
 | 
			
		||||
@ -1 +1 @@
 | 
			
		||||
!function(e){var n={};function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},t.p="/",t(t.s=1)}({1:function(e,n,t){e.exports=t("uOOV")},uOOV:function(e,n){$(document).ready(function(){$(".pagination").hide();var e=document.querySelector(".timeline-feed");new InfiniteScroll(e,{path:".pagination__next",append:".timeline-feed",status:".page-load-status",history:!0}).on("append",function(e,n,t){pixelfed.hydrateLikes()})})}});
 | 
			
		||||
!function(e){var n={};function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},t.p="/",t(t.s=2)}({2:function(e,n,t){e.exports=t("uOOV")},uOOV:function(e,n){$(document).ready(function(){$(".pagination").hide();var e=document.querySelector(".timeline-feed");new InfiniteScroll(e,{path:".pagination__next",append:".timeline-feed",status:".page-load-status",history:!1}).on("append",function(e,n,t){pixelfed.hydrateLikes()})})}});
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
    "/js/app.js": "/js/app.js?id=3cc7b07981e8b77e0db0",
 | 
			
		||||
    "/css/app.css": "/css/app.css?id=134b6c4c418ca85001f6",
 | 
			
		||||
    "/js/timeline.js": "/js/timeline.js?id=fd75ed1fd763eb407607"
 | 
			
		||||
    "/css/app.css": "/css/app.css?id=d7fd6ff1b9b190f3a2eb",
 | 
			
		||||
    "/js/timeline.js": "/js/timeline.js?id=d9a3145c0cd21ca09172",
 | 
			
		||||
    "/js/activity.js": "/js/activity.js?id=723dfb98bbbc96a9d39f"
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,10 @@
 | 
			
		||||
$(document).ready(function() {
 | 
			
		||||
  $('.pagination').hide();
 | 
			
		||||
  let elem = document.querySelector('.notification-page .list-group');
 | 
			
		||||
  let infScroll = new InfiniteScroll( elem, {
 | 
			
		||||
    path: '.pagination__next',
 | 
			
		||||
    append: '.notification-page .list-group',
 | 
			
		||||
    status: '.page-load-status',
 | 
			
		||||
    history: true,
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
@ -0,0 +1,152 @@
 | 
			
		||||
$switch-height: calc(#{$input-height} * .8) !default;
 | 
			
		||||
$switch-height-sm: calc(#{$input-height-sm} * .8) !default;
 | 
			
		||||
$switch-height-lg: calc(#{$input-height-lg} * .8) !default;
 | 
			
		||||
$switch-border-radius: $switch-height !default;
 | 
			
		||||
$switch-bg: $custom-control-indicator-bg !default;
 | 
			
		||||
$switch-checked-bg: map-get($theme-colors, 'danger') !default;
 | 
			
		||||
$switch-disabled-bg: $custom-control-indicator-disabled-bg !default;
 | 
			
		||||
$switch-disabled-color: $custom-control-description-disabled-color !default;
 | 
			
		||||
$switch-thumb-bg: $white !default;
 | 
			
		||||
$switch-thumb-border-radius: 50% !default;
 | 
			
		||||
$switch-thumb-padding: 2px !default;
 | 
			
		||||
$switch-focus-box-shadow: 0 0 0 $input-btn-focus-width rgba(map-get($theme-colors, 'primary'), .25);
 | 
			
		||||
$switch-transition: .2s all !default;
 | 
			
		||||
 | 
			
		||||
.switch {
 | 
			
		||||
  font-size: $font-size-base;
 | 
			
		||||
  position: relative;
 | 
			
		||||
 | 
			
		||||
  input {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    height: 1px;
 | 
			
		||||
    width: 1px;
 | 
			
		||||
    background: none;
 | 
			
		||||
    border: 0;
 | 
			
		||||
    clip: rect(0 0 0 0);
 | 
			
		||||
    clip-path: inset(50%);
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
    padding: 0;
 | 
			
		||||
 | 
			
		||||
    + label {
 | 
			
		||||
      position: relative;
 | 
			
		||||
      min-width: calc(#{$switch-height} * 2);
 | 
			
		||||
      border-radius: $switch-border-radius;
 | 
			
		||||
      height: $switch-height;
 | 
			
		||||
      line-height: $switch-height;
 | 
			
		||||
      display: inline-block;
 | 
			
		||||
      cursor: pointer;
 | 
			
		||||
      outline: none;
 | 
			
		||||
      user-select: none;
 | 
			
		||||
      vertical-align: middle;
 | 
			
		||||
      text-indent: calc(calc(#{$switch-height} * 2) + .5rem);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    + label::before,
 | 
			
		||||
    + label::after {
 | 
			
		||||
      content: '';
 | 
			
		||||
      position: absolute;
 | 
			
		||||
      top: 0;
 | 
			
		||||
      left: 0;
 | 
			
		||||
      width: calc(#{$switch-height} * 2);
 | 
			
		||||
      bottom: 0;
 | 
			
		||||
      display: block;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    + label::before {
 | 
			
		||||
      right: 0;
 | 
			
		||||
      background-color: $switch-bg;
 | 
			
		||||
      border-radius: $switch-border-radius;
 | 
			
		||||
      transition: $switch-transition;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    + label::after {
 | 
			
		||||
      top: $switch-thumb-padding;
 | 
			
		||||
      left: $switch-thumb-padding;
 | 
			
		||||
      width: calc(#{$switch-height} - calc(#{$switch-thumb-padding} * 2));
 | 
			
		||||
      height: calc(#{$switch-height} - calc(#{$switch-thumb-padding} * 2));
 | 
			
		||||
      border-radius: $switch-thumb-border-radius;
 | 
			
		||||
      background-color: $switch-thumb-bg;
 | 
			
		||||
      transition: $switch-transition;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &:checked + label::before {
 | 
			
		||||
      background-color: $switch-checked-bg;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &:checked + label::after {
 | 
			
		||||
      margin-left: $switch-height;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &:focus + label::before {
 | 
			
		||||
      outline: none;
 | 
			
		||||
      box-shadow: $switch-focus-box-shadow;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &:disabled + label {
 | 
			
		||||
      color: $switch-disabled-color;
 | 
			
		||||
      cursor: not-allowed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &:disabled + label::before {
 | 
			
		||||
      background-color: $switch-disabled-bg;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Small variation
 | 
			
		||||
  &.switch-sm {
 | 
			
		||||
    font-size: $font-size-sm;
 | 
			
		||||
 | 
			
		||||
    input {
 | 
			
		||||
      + label {
 | 
			
		||||
        min-width: calc(#{$switch-height-sm} * 2);
 | 
			
		||||
        height: $switch-height-sm;
 | 
			
		||||
        line-height: $switch-height-sm;
 | 
			
		||||
        text-indent: calc(calc(#{$switch-height-sm} * 2) + .5rem);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      + label::before {
 | 
			
		||||
        width: calc(#{$switch-height-sm} * 2);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      + label::after {
 | 
			
		||||
        width: calc(#{$switch-height-sm} - calc(#{$switch-thumb-padding} * 2));
 | 
			
		||||
        height: calc(#{$switch-height-sm} - calc(#{$switch-thumb-padding} * 2));
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      &:checked + label::after {
 | 
			
		||||
        margin-left: $switch-height-sm;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Large variation
 | 
			
		||||
  &.switch-lg {
 | 
			
		||||
    font-size: $font-size-lg;
 | 
			
		||||
 | 
			
		||||
    input {
 | 
			
		||||
      + label {
 | 
			
		||||
        min-width: calc(#{$switch-height-lg} * 2);
 | 
			
		||||
        height: $switch-height-lg;
 | 
			
		||||
        line-height: $switch-height-lg;
 | 
			
		||||
        text-indent: calc(calc(#{$switch-height-lg} * 2) + .5rem);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      + label::before {
 | 
			
		||||
        width: calc(#{$switch-height-lg} * 2);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      + label::after {
 | 
			
		||||
        width: calc(#{$switch-height-lg} - calc(#{$switch-thumb-padding} * 2));
 | 
			
		||||
        height: calc(#{$switch-height-lg} - calc(#{$switch-thumb-padding} * 2));
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      &:checked + label::after {
 | 
			
		||||
        margin-left: $switch-height-lg;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  + .switch {
 | 
			
		||||
    margin-left: 1rem;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,13 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
return [
 | 
			
		||||
 | 
			
		||||
    'viewMyProfile' => 'View my profile',
 | 
			
		||||
    'myTimeline' => 'My Timeline',
 | 
			
		||||
    'publicTimeline' => 'Public Timeline',
 | 
			
		||||
    'remoteFollow' => 'Remote Follow',
 | 
			
		||||
    'settings' => 'Settings',
 | 
			
		||||
    'admin' => 'Admin',
 | 
			
		||||
    'logout' => 'Logout',
 | 
			
		||||
 | 
			
		||||
];
 | 
			
		||||
@ -1,17 +1,19 @@
 | 
			
		||||
  <footer>
 | 
			
		||||
    <div class="container py-3">
 | 
			
		||||
    <div class="container py-5">
 | 
			
		||||
        <p class="mb-0 text-uppercase font-weight-bold small">
 | 
			
		||||
          <a href="{{route('site.about')}}" class="text-primary pr-2">About Us</a>
 | 
			
		||||
          <a href="{{route('site.help')}}" class="text-primary pr-2">Support</a>
 | 
			
		||||
          <a href="" class="text-primary pr-2">API</a>
 | 
			
		||||
          <a href="{{route('site.opensource')}}" class="text-primary pr-2">Open Source</a>
 | 
			
		||||
          <a href="{{route('site.language')}}" class="text-primary pr-2">Language</a>
 | 
			
		||||
          <span class="px-2"></span>
 | 
			
		||||
          <a href="{{route('site.terms')}}" class="text-primary pr-2 pl-2">Terms</a>
 | 
			
		||||
          <a href="{{route('site.privacy')}}" class="text-primary pr-2">Privacy</a>
 | 
			
		||||
          <a href="{{route('site.terms')}}" class="text-primary pr-2">Terms</a>
 | 
			
		||||
          <a href="#" class="text-primary pr-2">Directory</a>
 | 
			
		||||
          <a href="{{route('site.platform')}}" class="text-primary pr-2">API</a>
 | 
			
		||||
          <span class="px-2"></span>
 | 
			
		||||
          <a href="#" class="text-primary pr-2 pl-2">Directory</a>
 | 
			
		||||
          <a href="#" class="text-primary pr-2">Profiles</a>
 | 
			
		||||
          <a href="#" class="text-primary pr-2">Hashtags</a>
 | 
			
		||||
          <a href="{{route('site.language')}}" class="text-primary">Language</a>
 | 
			
		||||
          <a href="#" class="text-dark float-right">© {{date('Y')}} PixelFed.org</a>
 | 
			
		||||
          <a href="#" class="text-primary">Hashtags</a>
 | 
			
		||||
          <a href="http://pixelfed.org" class="text-muted float-right" rel="noopener">Powered by PixelFed</a>
 | 
			
		||||
        </p>
 | 
			
		||||
    </div>
 | 
			
		||||
  </footer>
 | 
			
		||||
 | 
			
		||||
					Loading…
					
					
				
		Reference in New Issue