mirror of
https://github.com/the-djmaze/snappymail.git
synced 2024-12-25 00:21:29 +08:00
New Message View (with attachments thumbnails)
This commit is contained in:
parent
1c7f1928c7
commit
06e6f1a2da
22 changed files with 3046 additions and 162 deletions
|
@ -57,6 +57,15 @@
|
|||
return this.sServer + '/Raw/' + this.sSubQuery + this.sSpecSuffix + '/View/' + this.sSubSubQuery + sDownload;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} sDownload
|
||||
* @return {string}
|
||||
*/
|
||||
Links.prototype.attachmentThumbnailPreview = function (sDownload)
|
||||
{
|
||||
return this.sServer + '/Raw/' + this.sSubQuery + this.sSpecSuffix + '/ViewThumbnail/' + this.sSubSubQuery + sDownload;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} sDownload
|
||||
* @return {string}
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
this.friendlySize = '';
|
||||
this.isInline = false;
|
||||
this.isLinked = false;
|
||||
this.isThumbnail = false;
|
||||
this.cid = '';
|
||||
this.cidWithOutTags = '';
|
||||
this.contentLocation = '';
|
||||
|
@ -56,6 +57,7 @@
|
|||
AttachmentModel.prototype.friendlySize = '';
|
||||
AttachmentModel.prototype.isInline = false;
|
||||
AttachmentModel.prototype.isLinked = false;
|
||||
AttachmentModel.prototype.isThumbnail = false;
|
||||
AttachmentModel.prototype.cid = '';
|
||||
AttachmentModel.prototype.cidWithOutTags = '';
|
||||
AttachmentModel.prototype.contentLocation = '';
|
||||
|
@ -78,6 +80,7 @@
|
|||
this.estimatedSize = Utils.pInt(oJsonAttachment.EstimatedSize);
|
||||
this.isInline = !!oJsonAttachment.IsInline;
|
||||
this.isLinked = !!oJsonAttachment.IsLinked;
|
||||
this.isThumbnail = !!oJsonAttachment.IsThumbnail;
|
||||
this.cid = oJsonAttachment.CID;
|
||||
this.contentLocation = oJsonAttachment.ContentLocation;
|
||||
this.download = oJsonAttachment.Download;
|
||||
|
@ -106,6 +109,14 @@
|
|||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {boolean}
|
||||
*/
|
||||
AttachmentModel.prototype.hasThumbnail = function ()
|
||||
{
|
||||
return this.isThumbnail;
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {boolean}
|
||||
*/
|
||||
|
@ -132,6 +143,14 @@
|
|||
!this.isPdf() && !this.isText() && !this.isImage();
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {boolean}
|
||||
*/
|
||||
AttachmentModel.prototype.hasPreview = function ()
|
||||
{
|
||||
return this.isImage() || this.isPdf() || this.isText() || this.isFramed();
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
|
@ -148,6 +167,15 @@
|
|||
return Links.attachmentPreview(this.download);
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
AttachmentModel.prototype.linkThumbnailPreviewStyle = function ()
|
||||
{
|
||||
return !this.hasThumbnail() ? '' :
|
||||
'background:url(' + Links.attachmentThumbnailPreview(this.download) + ')';
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
|
|
|
@ -52,7 +52,7 @@ html.rl-no-preview-pane {
|
|||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
|
||||
.b-message-list-wrapper {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
|
@ -275,7 +275,7 @@ html.rl-no-preview-pane {
|
|||
.flagParent {
|
||||
display: inline-block;
|
||||
float: right;
|
||||
padding: 0 8px 0 5px;
|
||||
padding: 0 10px 0 5px;
|
||||
}
|
||||
|
||||
&.e-single-line .flagParent {
|
||||
|
@ -299,7 +299,7 @@ html.rl-no-preview-pane {
|
|||
display: inline-block;
|
||||
float: right;
|
||||
position: relative;
|
||||
margin: 2px 8px 0 5px;
|
||||
margin: 2px 10px 0 5px;
|
||||
}
|
||||
|
||||
&.e-single-line .attachmentParent {
|
||||
|
@ -374,7 +374,7 @@ html.rl-no-preview-pane {
|
|||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
|
||||
.subject-prefix {
|
||||
color: #888;
|
||||
/*font-style: italic;*/
|
||||
|
@ -449,11 +449,11 @@ html.rl-no-preview-pane {
|
|||
.sidebarParent {
|
||||
background-color: #bdc3c7;
|
||||
}
|
||||
|
||||
|
||||
&.focused .sidebarParent {
|
||||
background-color: darken(#bdc3c7, 10%) !important;
|
||||
}
|
||||
|
||||
|
||||
&.unseen {
|
||||
background-color: darken(#ecf0f1, 5%);
|
||||
|
||||
|
@ -489,13 +489,13 @@ html.rl-no-preview-pane {
|
|||
background-color: #398CF2;
|
||||
.opacity(20);
|
||||
}
|
||||
|
||||
|
||||
+ .messageListItem .delimiter {
|
||||
background-color: #398CF2;
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
&.hasFlaggedSubMessage {
|
||||
.flagOff, .flagOn {
|
||||
display: none;
|
||||
|
|
|
@ -70,6 +70,13 @@ html.rl-no-preview-pane {
|
|||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.messageItemDropdown {
|
||||
z-index: 100;
|
||||
position: fixed;
|
||||
top: 70px;
|
||||
right: 25px;
|
||||
}
|
||||
|
||||
.messageItem {
|
||||
|
||||
position: absolute;
|
||||
|
@ -94,8 +101,8 @@ html.rl-no-preview-pane {
|
|||
.buttonUp, .buttonUnFull, .buttonFull {
|
||||
display: inline-block;
|
||||
position: fixed;
|
||||
right: 30px;
|
||||
top: 90px;
|
||||
right: 25px;
|
||||
bottom: 25px;
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
text-align: center;
|
||||
|
@ -119,11 +126,11 @@ html.rl-no-preview-pane {
|
|||
}
|
||||
|
||||
.buttonUp {
|
||||
right: 70px;
|
||||
right: 65px;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.buttonUnFull {
|
||||
.buttonUp, .buttonUnFull {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
@ -230,8 +237,7 @@ html.rl-no-preview-pane {
|
|||
|
||||
display: inline-block;
|
||||
margin: 5px;
|
||||
padding: 5px;
|
||||
max-width: 170px;
|
||||
max-width: 200px;
|
||||
min-width: 60px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
|
@ -246,19 +252,83 @@ html.rl-no-preview-pane {
|
|||
|
||||
border-radius: 2px;
|
||||
|
||||
.attachmentIcon {
|
||||
font-size: 23px;
|
||||
width: 23px;
|
||||
height: 23px;
|
||||
color: #666;
|
||||
.attachmentIconParent {
|
||||
height: 56px;
|
||||
width: 60px;
|
||||
|
||||
background: none;
|
||||
}
|
||||
|
||||
.attachmentPreview {
|
||||
color: #999;
|
||||
margin: 0px 5px;
|
||||
}
|
||||
.attachmentPreview:hover {
|
||||
.attachmentNameParent {
|
||||
margin-left: 60px;
|
||||
padding: 4px;
|
||||
padding-left: 6px;
|
||||
min-width: 90px;
|
||||
|
||||
color: #fff;
|
||||
background: #eee;
|
||||
|
||||
color: #333;
|
||||
background: #fafafa;
|
||||
border-left: 1px solid #ddd;
|
||||
}
|
||||
|
||||
&.hasThumbnail .attachmentNameParent {
|
||||
background: rgba(0, 0, 0, .6);
|
||||
|
||||
color: #fff;
|
||||
text-shadow: 0 1px 0 #000;
|
||||
border-left: 0px;
|
||||
}
|
||||
|
||||
.attachmentIcon {
|
||||
margin: 6px 0 0 13px;
|
||||
font-size: 36px;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.attachmentIconParent.hasPreview {
|
||||
.attachmentNonePreview {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
a.attachmentPreview {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.attachmentIconParent.hasPreview:hover {
|
||||
|
||||
background: #555 !important;
|
||||
background-image: none !important;
|
||||
background: rgba(0, 0, 0, .8) !important;
|
||||
|
||||
color: #fff;
|
||||
|
||||
.attachmentIcon {
|
||||
color: #fff;
|
||||
text-shadow: 0 1px 0 #000;
|
||||
}
|
||||
}
|
||||
|
||||
.attachmentIconParent.hasPreview .show-hover {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.attachmentIconParent.hasPreview:hover .show-hover {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.attachmentIconParent.hasPreview:hover .hide-hover {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.hasThumbnail .attachmentMainIcon {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -382,6 +452,7 @@ html.rl-no-preview-pane .messageView {
|
|||
.toolbar {
|
||||
padding-left: @rlMainBorderSize;
|
||||
}
|
||||
|
||||
.b-content {
|
||||
top: 50px;
|
||||
bottom: @rlBottomMargin;
|
||||
|
@ -389,10 +460,6 @@ html.rl-no-preview-pane .messageView {
|
|||
border: @rlMainBorderSize solid @rlMainDarkColor;
|
||||
box-shadow: @rlMainShadow;
|
||||
border-radius: @rlMainBorderRadius;
|
||||
|
||||
.buttonUp, .buttonUnFull, .buttonFull {
|
||||
top: 70px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -423,9 +490,12 @@ html.rl-message-fullscreen {
|
|||
border: @rlLowBorderSize solid @rlMainDarkColor;
|
||||
border-radius: @rlLowBorderRadius;
|
||||
|
||||
.buttonUp, .buttonUnFull {
|
||||
.messageItemDropdown {
|
||||
top: 20px;
|
||||
}
|
||||
|
||||
.buttonUnFull {
|
||||
display: inline-block;
|
||||
top: 36px;
|
||||
}
|
||||
|
||||
.buttonFull {
|
||||
|
@ -433,3 +503,7 @@ html.rl-message-fullscreen {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nano-scrolllimit-top .buttonUp {
|
||||
display: inline-block !important;
|
||||
}
|
||||
|
|
|
@ -81,7 +81,10 @@
|
|||
}
|
||||
}, this);
|
||||
|
||||
this.canBeRepliedOrForwarded = this.messageVisibility;
|
||||
this.canBeRepliedOrForwarded = ko.computed(function () {
|
||||
var bV = this.messageVisibility();
|
||||
return !this.isDraftFolder() && bV;
|
||||
}, this);
|
||||
|
||||
// commands
|
||||
this.closeMessage = Utils.createCommand(this, function () {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"name": "RainLoop",
|
||||
"title": "RainLoop Webmail",
|
||||
"version": "1.7.0",
|
||||
"release": "211",
|
||||
"version": "1.7.1",
|
||||
"release": "215",
|
||||
"description": "Simple, modern & fast web-based email client",
|
||||
"homepage": "http://rainloop.net",
|
||||
"main": "gulpfile.js",
|
||||
|
|
|
@ -35,6 +35,14 @@ if (!\defined('RAINLOOP_APP_LIBRARIES_PATH'))
|
|||
{
|
||||
return include RAINLOOP_APP_LIBRARIES_PATH.'GuzzleHttp/'.\str_replace('\\', '/', \substr($sClassName, 11)).'.php';
|
||||
}
|
||||
else if (0 === \strpos($sClassName, 'Symfony') && false !== \strpos($sClassName, '\\'))
|
||||
{
|
||||
return include RAINLOOP_APP_LIBRARIES_PATH.'Symfony/'.\str_replace('\\', '/', \substr($sClassName, 8)).'.php';
|
||||
}
|
||||
else if (0 === \strpos($sClassName, 'PHPThumb') && false !== \strpos($sClassName, '\\'))
|
||||
{
|
||||
return include RAINLOOP_APP_LIBRARIES_PATH.'PHPThumb/'.\str_replace('\\', '/', \substr($sClassName, 9)).'.php';
|
||||
}
|
||||
else if (0 === \strpos($sClassName, 'Sabre') && false !== \strpos($sClassName, '\\'))
|
||||
{
|
||||
if (!RAINLOOP_MB_SUPPORTED && !defined('RL_MB_FIXED'))
|
||||
|
@ -67,7 +75,7 @@ if (\class_exists('RainLoop\Service'))
|
|||
if (!\defined('APP_API_STARTED'))
|
||||
{
|
||||
\define('APP_API_STARTED', true);
|
||||
|
||||
|
||||
\RainLoop\Api::Handle();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of MailSo.
|
||||
*
|
||||
* (c) 2014 Usenko Timur
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace MailSo\Base\StreamWrappers;
|
||||
|
||||
/**
|
||||
* @category MailSo
|
||||
* @package Base
|
||||
* @subpackage StreamWrappers
|
||||
*/
|
||||
class TempFile
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
const STREAM_NAME = 'mailsotempfile';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $aStreams = array();
|
||||
|
||||
/**
|
||||
* @var resource
|
||||
*/
|
||||
private $rSream;
|
||||
|
||||
public static function Reg()
|
||||
{
|
||||
if (!in_array(self::STREAM_NAME, stream_get_wrappers()))
|
||||
{
|
||||
stream_wrapper_register(self::STREAM_NAME, '\MailSo\Base\StreamWrappers\TempFile');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sHash
|
||||
*
|
||||
* @return resource|bool
|
||||
*/
|
||||
public static function CreateStream($sHash)
|
||||
{
|
||||
self::Reg();
|
||||
|
||||
return fopen(self::STREAM_NAME.'://'.$sHash, 'r+b');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sPath
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function stream_open($sPath)
|
||||
{
|
||||
$bResult = false;
|
||||
$aPath = parse_url($sPath);
|
||||
|
||||
if (isset($aPath['host']) && isset($aPath['scheme']) &&
|
||||
0 < strlen($aPath['host']) && 0 < strlen($aPath['scheme']) &&
|
||||
self::STREAM_NAME === $aPath['scheme'])
|
||||
{
|
||||
$sHashName = $aPath['host'];
|
||||
if (isset(self::$aStreams[$sHashName]) &&
|
||||
is_resource(self::$aStreams[$sHashName]))
|
||||
{
|
||||
$this->rSream = self::$aStreams[$sHashName];
|
||||
$bResult = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->rSream = fopen('php://memory', 'r+b');
|
||||
self::$aStreams[$sHashName] = $this->rSream;
|
||||
|
||||
$bResult = true;
|
||||
|
||||
\MailSo\Base\Loader::IncStatistic('CreateStream/TempFile');
|
||||
}
|
||||
}
|
||||
|
||||
return $bResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function stream_close()
|
||||
{
|
||||
return -1 !== fseek($this->rSream, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function stream_flush()
|
||||
{
|
||||
return fflush($this->rSream);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $iLen
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function stream_read($iLen)
|
||||
{
|
||||
return fread($this->rSream, $iLen);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sInputString
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function stream_write($sInputString)
|
||||
{
|
||||
return fwrite($this->rSream, $sInputString);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function stream_tell()
|
||||
{
|
||||
return ftell($this->rSream);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function stream_eof()
|
||||
{
|
||||
return feof($this->rSream);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function stream_stat()
|
||||
{
|
||||
return fstat($this->rSream);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $iOffset
|
||||
* @param int $iWhence = SEEK_SET
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function stream_seek($iOffset, $iWhence = SEEK_SET)
|
||||
{
|
||||
return fseek($this->rSream, $iOffset, $iWhence);
|
||||
}
|
||||
}
|
|
@ -1215,7 +1215,7 @@ class Utils
|
|||
*/
|
||||
public static function ClearFileName($sFileName)
|
||||
{
|
||||
return \MailSo\Base\Utils::ClearNullBite(\preg_replace('/[\s]+/', ' ',
|
||||
return \MailSo\Base\Utils::ClearNullBite(\preg_replace('/[\s]+/u', ' ',
|
||||
\str_replace(array('"', '/', '\\', '*', '?', '<', '>', '|', ':'), ' ', $sFileName)));
|
||||
}
|
||||
|
||||
|
|
1417
rainloop/v/0.0.0/app/libraries/PHPThumb/GD.php
Normal file
1417
rainloop/v/0.0.0/app/libraries/PHPThumb/GD.php
Normal file
File diff suppressed because it is too large
Load diff
149
rainloop/v/0.0.0/app/libraries/PHPThumb/PHPThumb.php
Normal file
149
rainloop/v/0.0.0/app/libraries/PHPThumb/PHPThumb.php
Normal file
|
@ -0,0 +1,149 @@
|
|||
<?php
|
||||
|
||||
namespace PHPThumb;
|
||||
|
||||
/**
|
||||
* PhpThumb : PHP Thumb Library <http://phpthumb.gxdlabs.com>
|
||||
* Copyright (c) 2009, Ian Selby/Gen X Design
|
||||
*
|
||||
* Author(s): Ian Selby <ian@gen-x-design.com>
|
||||
*
|
||||
* Licensed under the MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author Ian Selby <ian@gen-x-design.com>
|
||||
* @copyright Copyright (c) 2009 Gen X Design
|
||||
* @link http://phpthumb.gxdlabs.com
|
||||
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
|
||||
*/
|
||||
|
||||
abstract class PHPThumb
|
||||
{
|
||||
/**
|
||||
* The name of the file we're manipulating
|
||||
* This must include the path to the file (absolute paths recommended)
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fileName;
|
||||
|
||||
/**
|
||||
* @var \Symfony\Component\Filesystem\Filesystem
|
||||
*/
|
||||
protected $filesystem;
|
||||
|
||||
/**
|
||||
* What the file format is (mime-type)
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $format;
|
||||
|
||||
/**
|
||||
* Whether or not the image is hosted remotely
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $remoteImage;
|
||||
|
||||
/**
|
||||
* An array of attached plugins to execute in order.
|
||||
* @var array
|
||||
*/
|
||||
protected $plugins;
|
||||
|
||||
/**
|
||||
* @param $fileName
|
||||
* @param array $options
|
||||
* @param array $plugins
|
||||
*/
|
||||
public function __construct($fileName, array $options = array(), array $plugins = array())
|
||||
{
|
||||
$this->filesystem = new \Symfony\Component\Filesystem\Filesystem();
|
||||
$this->fileName = $fileName;
|
||||
$this->remoteImage = false;
|
||||
|
||||
if(!$this->validateRequestedResource($fileName)) {
|
||||
throw new \InvalidArgumentException("Image file not found: {$fileName}");
|
||||
}
|
||||
|
||||
$this->setOptions($options);
|
||||
|
||||
$this->plugins = $plugins;
|
||||
}
|
||||
|
||||
abstract public function setOptions(array $options = array());
|
||||
|
||||
/**
|
||||
* Check the provided filename/url. If it is a url, validate that it is properly
|
||||
* formatted. If it is a file, check to make sure that it actually exists on
|
||||
* the filesystem.
|
||||
*
|
||||
* @param $filename
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateRequestedResource($filename)
|
||||
{
|
||||
if(false !== filter_var($filename, FILTER_VALIDATE_URL)) {
|
||||
$this->remoteImage = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if($this->filesystem->exists($filename)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the filename.
|
||||
* @return string
|
||||
*/
|
||||
public function getFileName()
|
||||
{
|
||||
return $this->fileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the filename.
|
||||
* @param $fileName
|
||||
* @return PHPThumb
|
||||
*/
|
||||
public function setFileName($fileName)
|
||||
{
|
||||
$this->fileName = $fileName;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the format.
|
||||
* @return string
|
||||
*/
|
||||
public function getFormat()
|
||||
{
|
||||
return $this->format;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the format.
|
||||
* @param $format
|
||||
* @return PHPThumb
|
||||
*/
|
||||
public function setFormat($format)
|
||||
{
|
||||
$this->format = $format;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the image exists remotely, i.e. it was loaded via a URL.
|
||||
* @return bool
|
||||
*/
|
||||
public function getIsRemoteImage()
|
||||
{
|
||||
return $this->remoteImage;
|
||||
}
|
||||
}
|
12
rainloop/v/0.0.0/app/libraries/PHPThumb/PluginInterface.php
Normal file
12
rainloop/v/0.0.0/app/libraries/PHPThumb/PluginInterface.php
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
namespace PHPThumb;
|
||||
|
||||
interface PluginInterface
|
||||
{
|
||||
/**
|
||||
* @param PHPThumb $phpthumb
|
||||
* @return PHPThumb
|
||||
*/
|
||||
public function execute($phpthumb);
|
||||
}
|
261
rainloop/v/0.0.0/app/libraries/PHPThumb/Plugins/Reflection.php
Normal file
261
rainloop/v/0.0.0/app/libraries/PHPThumb/Plugins/Reflection.php
Normal file
|
@ -0,0 +1,261 @@
|
|||
<?php
|
||||
|
||||
namespace PHPThumb\Plugins;
|
||||
|
||||
/**
|
||||
* GD Reflection Lib Plugin Definition File
|
||||
*
|
||||
* This file contains the plugin definition for the GD Reflection Lib for PHP Thumb
|
||||
*
|
||||
* PHP Version 5.3 with GD 2.0+
|
||||
* PhpThumb : PHP Thumb Library <http://phpthumb.gxdlabs.com>
|
||||
* Copyright (c) 2009, Ian Selby/Gen X Design
|
||||
*
|
||||
* Author(s): Ian Selby <ian@gen-x-design.com>
|
||||
*
|
||||
* Licensed under the MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author Ian Selby <ian@gen-x-design.com>
|
||||
* @copyright Copyright (c) 2009 Gen X Design
|
||||
* @link http://phpthumb.gxdlabs.com
|
||||
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
|
||||
* @version 3.0
|
||||
* @package PhpThumb
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
/**
|
||||
* GD Reflection Lib Plugin
|
||||
*
|
||||
* This plugin allows you to create those fun Apple(tm)-style reflections in your images
|
||||
*
|
||||
* @package PhpThumb
|
||||
* @subpackage Plugins
|
||||
*/
|
||||
class Reflection implements \PHPThumb\PluginInterface
|
||||
{
|
||||
protected $currentDimensions;
|
||||
protected $workingImage;
|
||||
protected $newImage;
|
||||
protected $options;
|
||||
|
||||
protected $percent;
|
||||
protected $reflection;
|
||||
protected $white;
|
||||
protected $border;
|
||||
protected $borderColor;
|
||||
|
||||
public function __construct($percent, $reflection, $white, $border, $borderColor)
|
||||
{
|
||||
$this->percent = $percent;
|
||||
$this->reflection = $reflection;
|
||||
$this->white = $white;
|
||||
$this->border = $border;
|
||||
$this->borderColor = $borderColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \PHPThumb\PHPThumb $phpthumb
|
||||
* @return \PHPThumb\PHPThumb
|
||||
*/
|
||||
public function execute($phpthumb)
|
||||
{
|
||||
$this->currentDimensions = $phpthumb->getCurrentDimensions();
|
||||
$this->workingImage = $phpthumb->getWorkingImage();
|
||||
$this->newImage = $phpthumb->getOldImage();
|
||||
$this->options = $phpthumb->getOptions();
|
||||
|
||||
$width = $this->currentDimensions['width'];
|
||||
$height = $this->currentDimensions['height'];
|
||||
$this->reflectionHeight = intval($height * ($this->reflection / 100));
|
||||
$newHeight = $height + $this->reflectionHeight;
|
||||
$reflectedPart = $height * ($this->percent / 100);
|
||||
|
||||
$this->workingImage = imagecreatetruecolor($width, $newHeight);
|
||||
|
||||
imagealphablending($this->workingImage, true);
|
||||
|
||||
$colorToPaint = imagecolorallocatealpha(
|
||||
$this->workingImage,
|
||||
255,
|
||||
255,
|
||||
255,
|
||||
0
|
||||
);
|
||||
|
||||
imagefilledrectangle(
|
||||
$this->workingImage,
|
||||
0,
|
||||
0,
|
||||
$width,
|
||||
$newHeight,
|
||||
$colorToPaint
|
||||
);
|
||||
|
||||
imagecopyresampled(
|
||||
$this->workingImage,
|
||||
$this->newImage,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
$reflectedPart,
|
||||
$width,
|
||||
$this->reflectionHeight,
|
||||
$width,
|
||||
($height - $reflectedPart)
|
||||
);
|
||||
|
||||
$this->imageFlipVertical();
|
||||
|
||||
imagecopy(
|
||||
$this->workingImage,
|
||||
$this->newImage,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
$width,
|
||||
$height
|
||||
);
|
||||
|
||||
imagealphablending($this->workingImage, true);
|
||||
|
||||
for ($i = 0; $i < $this->reflectionHeight; $i++) {
|
||||
$colorToPaint = imagecolorallocatealpha(
|
||||
$this->workingImage,
|
||||
255,
|
||||
255,
|
||||
255,
|
||||
($i / $this->reflectionHeight * -1 + 1) * $this->white
|
||||
);
|
||||
|
||||
imagefilledrectangle(
|
||||
$this->workingImage,
|
||||
0,
|
||||
$height + $i,
|
||||
$width,
|
||||
$height + $i,
|
||||
$colorToPaint
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->border == true) {
|
||||
$rgb = $this->hex2rgb($this->borderColor, false);
|
||||
$colorToPaint = imagecolorallocate($this->workingImage, $rgb[0], $rgb[1], $rgb[2]);
|
||||
|
||||
//top line
|
||||
imageline(
|
||||
$this->workingImage,
|
||||
0,
|
||||
0,
|
||||
$width,
|
||||
0,
|
||||
$colorToPaint
|
||||
);
|
||||
|
||||
//bottom line
|
||||
imageline(
|
||||
$this->workingImage,
|
||||
0,
|
||||
$height,
|
||||
$width,
|
||||
$height,
|
||||
$colorToPaint
|
||||
);
|
||||
|
||||
//left line
|
||||
imageline(
|
||||
$this->workingImage,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
$height,
|
||||
$colorToPaint
|
||||
);
|
||||
|
||||
//right line
|
||||
imageline(
|
||||
$this->workingImage,
|
||||
$width - 1,
|
||||
0,
|
||||
$width - 1,
|
||||
$height,
|
||||
$colorToPaint
|
||||
);
|
||||
}
|
||||
|
||||
if ($phpthumb->getFormat() == 'PNG') {
|
||||
$colorTransparent = imagecolorallocatealpha(
|
||||
$this->workingImage,
|
||||
$this->options['alphaMaskColor'][0],
|
||||
$this->options['alphaMaskColor'][1],
|
||||
$this->options['alphaMaskColor'][2],
|
||||
0
|
||||
);
|
||||
|
||||
imagefill($this->workingImage, 0, 0, $colorTransparent);
|
||||
imagesavealpha($this->workingImage, true);
|
||||
}
|
||||
|
||||
$phpthumb->setOldImage($this->workingImage);
|
||||
$this->currentDimensions['width'] = $width;
|
||||
$this->currentDimensions['height'] = $newHeight;
|
||||
$phpthumb->setCurrentDimensions($this->currentDimensions);
|
||||
|
||||
return $phpthumb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flips the image vertically
|
||||
*
|
||||
*/
|
||||
protected function imageFlipVertical ()
|
||||
{
|
||||
$x_i = imagesx($this->workingImage);
|
||||
$y_i = imagesy($this->workingImage);
|
||||
|
||||
for ($x = 0; $x < $x_i; $x++) {
|
||||
for ($y = 0; $y < $y_i; $y++) {
|
||||
imagecopy(
|
||||
$this->workingImage,
|
||||
$this->workingImage,
|
||||
$x,
|
||||
$y_i - $y - 1,
|
||||
$x,
|
||||
$y,
|
||||
1,
|
||||
1
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a hex color to rgb tuples
|
||||
*
|
||||
* @return mixed
|
||||
* @param string $hex
|
||||
* @param bool $asString
|
||||
*/
|
||||
protected function hex2rgb ($hex, $asString = false)
|
||||
{
|
||||
// strip off any leading #
|
||||
if (0 === strpos($hex, '#')) {
|
||||
$hex = substr($hex, 1);
|
||||
} elseif (0 === strpos($hex, '&H')) {
|
||||
$hex = substr($hex, 2);
|
||||
}
|
||||
|
||||
// break into hex 3-tuple
|
||||
$cutpoint = ceil(strlen($hex) / 2)-1;
|
||||
$rgb = explode(':', wordwrap($hex, $cutpoint, ':', $cutpoint), 3);
|
||||
|
||||
// convert each tuple to decimal
|
||||
$rgb[0] = (isset($rgb[0]) ? hexdec($rgb[0]) : 0);
|
||||
$rgb[1] = (isset($rgb[1]) ? hexdec($rgb[1]) : 0);
|
||||
$rgb[2] = (isset($rgb[2]) ? hexdec($rgb[2]) : 0);
|
||||
|
||||
return ($asString ? "{$rgb[0]} {$rgb[1]} {$rgb[2]}" : $rgb);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Filesystem\Exception;
|
||||
|
||||
/**
|
||||
* Exception interface for all exceptions thrown by the component.
|
||||
*
|
||||
* @author Romain Neutron <imprec@gmail.com>
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
interface ExceptionInterface
|
||||
{
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Filesystem\Exception;
|
||||
|
||||
/**
|
||||
* Exception class thrown when a file couldn't be found
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Christian Gärtner <christiangaertner.film@googlemail.com>
|
||||
*/
|
||||
class FileNotFoundException extends IOException
|
||||
{
|
||||
public function __construct($message = null, $code = 0, \Exception $previous = null, $path = null)
|
||||
{
|
||||
if (null === $message) {
|
||||
if (null === $path) {
|
||||
$message = 'File could not be found.';
|
||||
} else {
|
||||
$message = sprintf('File "%s" could not be found.', $path);
|
||||
}
|
||||
}
|
||||
|
||||
parent::__construct($message, $code, $previous, $path);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Filesystem\Exception;
|
||||
|
||||
/**
|
||||
* Exception class thrown when a filesystem operation failure happens
|
||||
*
|
||||
* @author Romain Neutron <imprec@gmail.com>
|
||||
* @author Christian Gärtner <christiangaertner.film@googlemail.com>
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
class IOException extends \RuntimeException implements IOExceptionInterface
|
||||
{
|
||||
private $path;
|
||||
|
||||
public function __construct($message, $code = 0, \Exception $previous = null, $path = null)
|
||||
{
|
||||
$this->path = $path;
|
||||
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPath()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Filesystem\Exception;
|
||||
|
||||
/**
|
||||
* IOException interface for file and input/output stream releated exceptions thrown by the component.
|
||||
*
|
||||
* @author Christian Gärtner <christiangaertner.film@googlemail.com>
|
||||
*/
|
||||
interface IOExceptionInterface extends ExceptionInterface
|
||||
{
|
||||
/**
|
||||
* Returns the associated path for the exception
|
||||
*
|
||||
* @return string The path.
|
||||
*/
|
||||
public function getPath();
|
||||
}
|
|
@ -0,0 +1,474 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Filesystem;
|
||||
|
||||
use Symfony\Component\Filesystem\Exception\IOException;
|
||||
use Symfony\Component\Filesystem\Exception\FileNotFoundException;
|
||||
|
||||
/**
|
||||
* Provides basic utility to manipulate the file system.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Filesystem
|
||||
{
|
||||
/**
|
||||
* Copies a file.
|
||||
*
|
||||
* This method only copies the file if the origin file is newer than the target file.
|
||||
*
|
||||
* By default, if the target already exists, it is not overridden.
|
||||
*
|
||||
* @param string $originFile The original filename
|
||||
* @param string $targetFile The target filename
|
||||
* @param boolean $override Whether to override an existing file or not
|
||||
*
|
||||
* @throws FileNotFoundException When orginFile doesn't exist
|
||||
* @throws IOException When copy fails
|
||||
*/
|
||||
public function copy($originFile, $targetFile, $override = false)
|
||||
{
|
||||
if (stream_is_local($originFile) && !is_file($originFile)) {
|
||||
throw new FileNotFoundException(sprintf('Failed to copy "%s" because file does not exist.', $originFile), 0, null, $originFile);
|
||||
}
|
||||
|
||||
$this->mkdir(dirname($targetFile));
|
||||
|
||||
if (!$override && is_file($targetFile)) {
|
||||
$doCopy = filemtime($originFile) > filemtime($targetFile);
|
||||
} else {
|
||||
$doCopy = true;
|
||||
}
|
||||
|
||||
if ($doCopy) {
|
||||
// https://bugs.php.net/bug.php?id=64634
|
||||
$source = fopen($originFile, 'r');
|
||||
$target = fopen($targetFile, 'w+');
|
||||
stream_copy_to_stream($source, $target);
|
||||
fclose($source);
|
||||
fclose($target);
|
||||
unset($source, $target);
|
||||
|
||||
if (!is_file($targetFile)) {
|
||||
throw new IOException(sprintf('Failed to copy "%s" to "%s".', $originFile, $targetFile), 0, null, $originFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a directory recursively.
|
||||
*
|
||||
* @param string|array|\Traversable $dirs The directory path
|
||||
* @param integer $mode The directory mode
|
||||
*
|
||||
* @throws IOException On any directory creation failure
|
||||
*/
|
||||
public function mkdir($dirs, $mode = 0777)
|
||||
{
|
||||
foreach ($this->toIterator($dirs) as $dir) {
|
||||
if (is_dir($dir)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (true !== @mkdir($dir, $mode, true)) {
|
||||
throw new IOException(sprintf('Failed to create "%s".', $dir), 0, null, $dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the existence of files or directories.
|
||||
*
|
||||
* @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to check
|
||||
*
|
||||
* @return Boolean true if the file exists, false otherwise
|
||||
*/
|
||||
public function exists($files)
|
||||
{
|
||||
foreach ($this->toIterator($files) as $file) {
|
||||
if (!file_exists($file)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets access and modification time of file.
|
||||
*
|
||||
* @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to create
|
||||
* @param integer $time The touch time as a unix timestamp
|
||||
* @param integer $atime The access time as a unix timestamp
|
||||
*
|
||||
* @throws IOException When touch fails
|
||||
*/
|
||||
public function touch($files, $time = null, $atime = null)
|
||||
{
|
||||
foreach ($this->toIterator($files) as $file) {
|
||||
$touch = $time ? @touch($file, $time, $atime) : @touch($file);
|
||||
if (true !== $touch) {
|
||||
throw new IOException(sprintf('Failed to touch "%s".', $file), 0, null, $file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes files or directories.
|
||||
*
|
||||
* @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to remove
|
||||
*
|
||||
* @throws IOException When removal fails
|
||||
*/
|
||||
public function remove($files)
|
||||
{
|
||||
$files = iterator_to_array($this->toIterator($files));
|
||||
$files = array_reverse($files);
|
||||
foreach ($files as $file) {
|
||||
if (!file_exists($file) && !is_link($file)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_dir($file) && !is_link($file)) {
|
||||
$this->remove(new \FilesystemIterator($file));
|
||||
|
||||
if (true !== @rmdir($file)) {
|
||||
throw new IOException(sprintf('Failed to remove directory "%s".', $file), 0, null, $file);
|
||||
}
|
||||
} else {
|
||||
// https://bugs.php.net/bug.php?id=52176
|
||||
if (defined('PHP_WINDOWS_VERSION_MAJOR') && is_dir($file)) {
|
||||
if (true !== @rmdir($file)) {
|
||||
throw new IOException(sprintf('Failed to remove file "%s".', $file), 0, null, $file);
|
||||
}
|
||||
} else {
|
||||
if (true !== @unlink($file)) {
|
||||
throw new IOException(sprintf('Failed to remove file "%s".', $file), 0, null, $file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change mode for an array of files or directories.
|
||||
*
|
||||
* @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change mode
|
||||
* @param integer $mode The new mode (octal)
|
||||
* @param integer $umask The mode mask (octal)
|
||||
* @param Boolean $recursive Whether change the mod recursively or not
|
||||
*
|
||||
* @throws IOException When the change fail
|
||||
*/
|
||||
public function chmod($files, $mode, $umask = 0000, $recursive = false)
|
||||
{
|
||||
foreach ($this->toIterator($files) as $file) {
|
||||
if ($recursive && is_dir($file) && !is_link($file)) {
|
||||
$this->chmod(new \FilesystemIterator($file), $mode, $umask, true);
|
||||
}
|
||||
if (true !== @chmod($file, $mode & ~$umask)) {
|
||||
throw new IOException(sprintf('Failed to chmod file "%s".', $file), 0, null, $file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the owner of an array of files or directories
|
||||
*
|
||||
* @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change owner
|
||||
* @param string $user The new owner user name
|
||||
* @param Boolean $recursive Whether change the owner recursively or not
|
||||
*
|
||||
* @throws IOException When the change fail
|
||||
*/
|
||||
public function chown($files, $user, $recursive = false)
|
||||
{
|
||||
foreach ($this->toIterator($files) as $file) {
|
||||
if ($recursive && is_dir($file) && !is_link($file)) {
|
||||
$this->chown(new \FilesystemIterator($file), $user, true);
|
||||
}
|
||||
if (is_link($file) && function_exists('lchown')) {
|
||||
if (true !== @lchown($file, $user)) {
|
||||
throw new IOException(sprintf('Failed to chown file "%s".', $file), 0, null, $file);
|
||||
}
|
||||
} else {
|
||||
if (true !== @chown($file, $user)) {
|
||||
throw new IOException(sprintf('Failed to chown file "%s".', $file), 0, null, $file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the group of an array of files or directories
|
||||
*
|
||||
* @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change group
|
||||
* @param string $group The group name
|
||||
* @param Boolean $recursive Whether change the group recursively or not
|
||||
*
|
||||
* @throws IOException When the change fail
|
||||
*/
|
||||
public function chgrp($files, $group, $recursive = false)
|
||||
{
|
||||
foreach ($this->toIterator($files) as $file) {
|
||||
if ($recursive && is_dir($file) && !is_link($file)) {
|
||||
$this->chgrp(new \FilesystemIterator($file), $group, true);
|
||||
}
|
||||
if (is_link($file) && function_exists('lchgrp')) {
|
||||
if (true !== @lchgrp($file, $group)) {
|
||||
throw new IOException(sprintf('Failed to chgrp file "%s".', $file), 0, null, $file);
|
||||
}
|
||||
} else {
|
||||
if (true !== @chgrp($file, $group)) {
|
||||
throw new IOException(sprintf('Failed to chgrp file "%s".', $file), 0, null, $file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames a file or a directory.
|
||||
*
|
||||
* @param string $origin The origin filename or directory
|
||||
* @param string $target The new filename or directory
|
||||
* @param Boolean $overwrite Whether to overwrite the target if it already exists
|
||||
*
|
||||
* @throws IOException When target file or directory already exists
|
||||
* @throws IOException When origin cannot be renamed
|
||||
*/
|
||||
public function rename($origin, $target, $overwrite = false)
|
||||
{
|
||||
// we check that target does not exist
|
||||
if (!$overwrite && is_readable($target)) {
|
||||
throw new IOException(sprintf('Cannot rename because the target "%s" already exists.', $target), 0, null, $target);
|
||||
}
|
||||
|
||||
if (true !== @rename($origin, $target)) {
|
||||
throw new IOException(sprintf('Cannot rename "%s" to "%s".', $origin, $target), 0, null, $target);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a symbolic link or copy a directory.
|
||||
*
|
||||
* @param string $originDir The origin directory path
|
||||
* @param string $targetDir The symbolic link name
|
||||
* @param Boolean $copyOnWindows Whether to copy files if on Windows
|
||||
*
|
||||
* @throws IOException When symlink fails
|
||||
*/
|
||||
public function symlink($originDir, $targetDir, $copyOnWindows = false)
|
||||
{
|
||||
if (!function_exists('symlink') && $copyOnWindows) {
|
||||
$this->mirror($originDir, $targetDir);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->mkdir(dirname($targetDir));
|
||||
|
||||
$ok = false;
|
||||
if (is_link($targetDir)) {
|
||||
if (readlink($targetDir) != $originDir) {
|
||||
$this->remove($targetDir);
|
||||
} else {
|
||||
$ok = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$ok) {
|
||||
if (true !== @symlink($originDir, $targetDir)) {
|
||||
$report = error_get_last();
|
||||
if (is_array($report)) {
|
||||
if (defined('PHP_WINDOWS_VERSION_MAJOR') && false !== strpos($report['message'], 'error code(1314)')) {
|
||||
throw new IOException('Unable to create symlink due to error code 1314: \'A required privilege is not held by the client\'. Do you have the required Administrator-rights?');
|
||||
}
|
||||
}
|
||||
|
||||
throw new IOException(sprintf('Failed to create symbolic link from "%s" to "%s".', $originDir, $targetDir), 0, null, $targetDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an existing path, convert it to a path relative to a given starting path
|
||||
*
|
||||
* @param string $endPath Absolute path of target
|
||||
* @param string $startPath Absolute path where traversal begins
|
||||
*
|
||||
* @return string Path of target relative to starting path
|
||||
*/
|
||||
public function makePathRelative($endPath, $startPath)
|
||||
{
|
||||
// Normalize separators on windows
|
||||
if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
|
||||
$endPath = strtr($endPath, '\\', '/');
|
||||
$startPath = strtr($startPath, '\\', '/');
|
||||
}
|
||||
|
||||
// Split the paths into arrays
|
||||
$startPathArr = explode('/', trim($startPath, '/'));
|
||||
$endPathArr = explode('/', trim($endPath, '/'));
|
||||
|
||||
// Find for which directory the common path stops
|
||||
$index = 0;
|
||||
while (isset($startPathArr[$index]) && isset($endPathArr[$index]) && $startPathArr[$index] === $endPathArr[$index]) {
|
||||
$index++;
|
||||
}
|
||||
|
||||
// Determine how deep the start path is relative to the common path (ie, "web/bundles" = 2 levels)
|
||||
$depth = count($startPathArr) - $index;
|
||||
|
||||
// Repeated "../" for each level need to reach the common path
|
||||
$traverser = str_repeat('../', $depth);
|
||||
|
||||
$endPathRemainder = implode('/', array_slice($endPathArr, $index));
|
||||
|
||||
// Construct $endPath from traversing to the common path, then to the remaining $endPath
|
||||
$relativePath = $traverser.(strlen($endPathRemainder) > 0 ? $endPathRemainder.'/' : '');
|
||||
|
||||
return (strlen($relativePath) === 0) ? './' : $relativePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mirrors a directory to another.
|
||||
*
|
||||
* @param string $originDir The origin directory
|
||||
* @param string $targetDir The target directory
|
||||
* @param \Traversable $iterator A Traversable instance
|
||||
* @param array $options An array of boolean options
|
||||
* Valid options are:
|
||||
* - $options['override'] Whether to override an existing file on copy or not (see copy())
|
||||
* - $options['copy_on_windows'] Whether to copy files instead of links on Windows (see symlink())
|
||||
* - $options['delete'] Whether to delete files that are not in the source directory (defaults to false)
|
||||
*
|
||||
* @throws IOException When file type is unknown
|
||||
*/
|
||||
public function mirror($originDir, $targetDir, \Traversable $iterator = null, $options = array())
|
||||
{
|
||||
$targetDir = rtrim($targetDir, '/\\');
|
||||
$originDir = rtrim($originDir, '/\\');
|
||||
|
||||
// Iterate in destination folder to remove obsolete entries
|
||||
if ($this->exists($targetDir) && isset($options['delete']) && $options['delete']) {
|
||||
$deleteIterator = $iterator;
|
||||
if (null === $deleteIterator) {
|
||||
$flags = \FilesystemIterator::SKIP_DOTS;
|
||||
$deleteIterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($targetDir, $flags), \RecursiveIteratorIterator::CHILD_FIRST);
|
||||
}
|
||||
foreach ($deleteIterator as $file) {
|
||||
$origin = str_replace($targetDir, $originDir, $file->getPathname());
|
||||
if (!$this->exists($origin)) {
|
||||
$this->remove($file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$copyOnWindows = false;
|
||||
if (isset($options['copy_on_windows']) && !function_exists('symlink')) {
|
||||
$copyOnWindows = $options['copy_on_windows'];
|
||||
}
|
||||
|
||||
if (null === $iterator) {
|
||||
$flags = $copyOnWindows ? \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS : \FilesystemIterator::SKIP_DOTS;
|
||||
$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($originDir, $flags), \RecursiveIteratorIterator::SELF_FIRST);
|
||||
}
|
||||
|
||||
foreach ($iterator as $file) {
|
||||
$target = str_replace($originDir, $targetDir, $file->getPathname());
|
||||
|
||||
if ($copyOnWindows) {
|
||||
if (is_link($file) || is_file($file)) {
|
||||
$this->copy($file, $target, isset($options['override']) ? $options['override'] : false);
|
||||
} elseif (is_dir($file)) {
|
||||
$this->mkdir($target);
|
||||
} else {
|
||||
throw new IOException(sprintf('Unable to guess "%s" file type.', $file), 0, null, $file);
|
||||
}
|
||||
} else {
|
||||
if (is_link($file)) {
|
||||
$this->symlink($file, $target);
|
||||
} elseif (is_dir($file)) {
|
||||
$this->mkdir($target);
|
||||
} elseif (is_file($file)) {
|
||||
$this->copy($file, $target, isset($options['override']) ? $options['override'] : false);
|
||||
} else {
|
||||
throw new IOException(sprintf('Unable to guess "%s" file type.', $file), 0, null, $file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the file path is an absolute path.
|
||||
*
|
||||
* @param string $file A file path
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
public function isAbsolutePath($file)
|
||||
{
|
||||
if (strspn($file, '/\\', 0, 1)
|
||||
|| (strlen($file) > 3 && ctype_alpha($file[0])
|
||||
&& substr($file, 1, 1) === ':'
|
||||
&& (strspn($file, '/\\', 2, 1))
|
||||
)
|
||||
|| null !== parse_url($file, PHP_URL_SCHEME)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically dumps content into a file.
|
||||
*
|
||||
* @param string $filename The file to be written to.
|
||||
* @param string $content The data to write into the file.
|
||||
* @param integer $mode The file mode (octal).
|
||||
* @throws IOException If the file cannot be written to.
|
||||
*/
|
||||
public function dumpFile($filename, $content, $mode = 0666)
|
||||
{
|
||||
$dir = dirname($filename);
|
||||
|
||||
if (!is_dir($dir)) {
|
||||
$this->mkdir($dir);
|
||||
} elseif (!is_writable($dir)) {
|
||||
throw new IOException(sprintf('Unable to write to the "%s" directory.', $dir), 0, null, $dir);
|
||||
}
|
||||
|
||||
$tmpFile = tempnam($dir, basename($filename));
|
||||
|
||||
if (false === @file_put_contents($tmpFile, $content)) {
|
||||
throw new IOException(sprintf('Failed to write file "%s".', $filename), 0, null, $filename);
|
||||
}
|
||||
|
||||
$this->rename($tmpFile, $filename, true);
|
||||
$this->chmod($filename, $mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $files
|
||||
*
|
||||
* @return \Traversable
|
||||
*/
|
||||
private function toIterator($files)
|
||||
{
|
||||
if (!$files instanceof \Traversable) {
|
||||
$files = new \ArrayObject(is_array($files) ? $files : array($files));
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
}
|
|
@ -2050,8 +2050,24 @@ class Actions
|
|||
*/
|
||||
public function DoFiltersSave()
|
||||
{
|
||||
sleep(2);
|
||||
$aIncFilters = $this->GetActionParam('Filters', array());
|
||||
|
||||
$aFilters = array();
|
||||
foreach ($aIncFilters as $aFilter)
|
||||
{
|
||||
if ($aFilter)
|
||||
{
|
||||
$oFilter = new \RainLoop\Providers\Filters\Classes\Filter();
|
||||
if ($oFilter->FromJSON($aFilter))
|
||||
{
|
||||
$this->Logger()->WriteDump($oFilter);
|
||||
$aFilters[] = $oFilter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->TrueResponse(__FUNCTION__);
|
||||
return $this->DefaultResponse(__FUNCTION__, $this->FiltersProvider()->Save($aFilters));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -7001,10 +7017,11 @@ class Actions
|
|||
|
||||
/**
|
||||
* @param bool $bDownload
|
||||
* @param bool $bThumbnail = false
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function rawSmart($bDownload)
|
||||
private function rawSmart($bDownload, $bThumbnail = false)
|
||||
{
|
||||
$sRawKey = (string) $this->GetActionParam('RawKey', '');
|
||||
$aValues = $this->getDecodedRawKeyValue($sRawKey);
|
||||
|
@ -7021,11 +7038,11 @@ class Actions
|
|||
$this->verifyCacheByKey($sRawKey);
|
||||
}
|
||||
|
||||
$this->initMailClientConnection();
|
||||
$oAccount = $this->initMailClientConnection();
|
||||
|
||||
$self = $this;
|
||||
return $this->MailClient()->MessageMimeStream(
|
||||
function($rResource, $sContentType, $sFileName, $sMimeIndex = '') use ($self, $sRawKey, $sContentTypeIn, $sFileNameIn, $bDownload) {
|
||||
function($rResource, $sContentType, $sFileName, $sMimeIndex = '') use ($self, $oAccount, $sRawKey, $sContentTypeIn, $sFileNameIn, $bDownload, $bThumbnail) {
|
||||
if (\is_resource($rResource))
|
||||
{
|
||||
$sContentTypeOut = $sContentTypeIn;
|
||||
|
@ -7046,16 +7063,41 @@ class Actions
|
|||
|
||||
$sFileNameOut = $self->MainClearFileName($sFileNameOut, $sContentTypeOut, $sMimeIndex);
|
||||
|
||||
\header('Content-Type: '.$sContentTypeOut);
|
||||
\header('Content-Disposition: '.($bDownload ? 'attachment' : 'inline').'; '.
|
||||
\trim(\MailSo\Base\Utils::EncodeHeaderUtf8AttributeValue('filename', $sFileNameOut)), true);
|
||||
|
||||
\header('Accept-Ranges: none', true);
|
||||
\header('Content-Transfer-Encoding: binary');
|
||||
|
||||
$self->cacheByKey($sRawKey);
|
||||
|
||||
\MailSo\Base\Utils::FpassthruWithTimeLimitReset($rResource);
|
||||
$bDone = false;
|
||||
if ($bThumbnail && !$bDownload)
|
||||
{
|
||||
\MailSo\Base\StreamWrappers\TempFile::Reg();
|
||||
|
||||
$sFileName = 'mailsotempfile://'.\md5($sFileNameOut.\rand(1000, 9999));
|
||||
|
||||
$rTempResource = \fopen($sFileName, 'r+b');
|
||||
if (@\is_resource($rTempResource))
|
||||
{
|
||||
\MailSo\Base\Utils::MultipleStreamWriter($rResource, array($rTempResource));
|
||||
@\fclose($rTempResource);
|
||||
|
||||
$oThumb =@ new \PHPThumb\GD($sFileName);
|
||||
if ($oThumb)
|
||||
{
|
||||
$oThumb->adaptiveResize(220, 80)->show();
|
||||
$bDone = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$bDone)
|
||||
{
|
||||
\header('Content-Type: '.$sContentTypeOut);
|
||||
\header('Content-Disposition: '.($bDownload ? 'attachment' : 'inline').'; '.
|
||||
\trim(\MailSo\Base\Utils::EncodeHeaderUtf8AttributeValue('filename', $sFileNameOut)), true);
|
||||
|
||||
\header('Accept-Ranges: none', true);
|
||||
\header('Content-Transfer-Encoding: binary');
|
||||
|
||||
\MailSo\Base\Utils::FpassthruWithTimeLimitReset($rResource);
|
||||
}
|
||||
}
|
||||
}, $sFolder, $iUid, true, $sMimeIndex);
|
||||
}
|
||||
|
@ -7076,6 +7118,14 @@ class Actions
|
|||
return $this->rawSmart(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function RawViewThumbnail()
|
||||
{
|
||||
return $this->rawSmart(false, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
|
@ -7126,6 +7176,42 @@ class Actions
|
|||
return \in_array($sExt, array('doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sFileName
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isFileHasThumbnail($sFileName)
|
||||
{
|
||||
static $aCache = array();
|
||||
|
||||
$sExt = \MailSo\Base\Utils::GetFileExtension($sFileName);
|
||||
if (isset($aCache[$sExt]))
|
||||
{
|
||||
return $aCache[$sExt];
|
||||
}
|
||||
|
||||
$bResult = \function_exists('gd_info');
|
||||
if ($bResult)
|
||||
{
|
||||
$bResult = false;
|
||||
switch ($sExt)
|
||||
{
|
||||
case 'png':
|
||||
$bResult = \function_exists('imagecreatefrompng');
|
||||
break;
|
||||
case 'jpg':
|
||||
case 'jpeg':
|
||||
$bResult = \function_exists('imagecreatefromjpeg');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$aCache[$sExt] = $bResult;
|
||||
|
||||
return $bResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
|
@ -8185,12 +8271,18 @@ class Actions
|
|||
'CID' => $mResponse->Cid(),
|
||||
'ContentLocation' => $mResponse->ContentLocation(),
|
||||
'IsInline' => $mResponse->IsInline(),
|
||||
'IsThumbnail' => !!$this->Config()->Get('interface', 'show_attachment_thumbnail', true),
|
||||
'IsLinked' => ($mFoundedCIDs && \in_array(\trim(\trim($mResponse->Cid()), '<>'), $mFoundedCIDs)) ||
|
||||
($mFoundedContentLocationUrls && \in_array(\trim($mResponse->ContentLocation()), $mFoundedContentLocationUrls))
|
||||
));
|
||||
|
||||
$mResult['Framed'] = $this->isFileHasFramedPreview($mResult['FileName']);
|
||||
|
||||
if ($mResult['IsThumbnail'])
|
||||
{
|
||||
$mResult['IsThumbnail'] = $this->isFileHasThumbnail($mResult['FileName']);
|
||||
}
|
||||
|
||||
$mResult['Download'] = \RainLoop\Utils::EncodeKeyValues(array(
|
||||
'V' => APP_VERSION,
|
||||
'Account' => $oAccount ? \md5($oAccount->Hash()) : '',
|
||||
|
@ -8201,6 +8293,8 @@ class Actions
|
|||
'FileName' => $mResult['FileName'],
|
||||
'Framed' => $mResult['Framed']
|
||||
));
|
||||
|
||||
$this->Logger()->WriteDump($mResult);
|
||||
}
|
||||
else if ('MailSo\Mail\Folder' === $sClassName)
|
||||
{
|
||||
|
|
|
@ -80,6 +80,10 @@ class Application extends \RainLoop\Config\AbstractConfig
|
|||
0 for unlimited.')
|
||||
),
|
||||
|
||||
'interface' => array(
|
||||
'show_attachment_thumbnail' => array(true, '')
|
||||
),
|
||||
|
||||
'branding' => array(
|
||||
'login_logo' => array(''),
|
||||
'login_desc' => array(''),
|
||||
|
|
|
@ -4,6 +4,11 @@ namespace RainLoop\Providers\Filters\Classes;
|
|||
|
||||
class Filter
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $sID;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
|
@ -41,6 +46,7 @@ class Filter
|
|||
|
||||
public function Clear()
|
||||
{
|
||||
$this->sID = '';
|
||||
$this->sName = '';
|
||||
|
||||
$this->aConditions = array();
|
||||
|
@ -54,6 +60,14 @@ class Filter
|
|||
$this->bSkipOthers = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function ID()
|
||||
{
|
||||
return $this->sID;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
|
@ -129,6 +143,16 @@ class Filter
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $aFilter
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function FromJSON($aFilter)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $bAjax = false
|
||||
*
|
||||
|
@ -138,6 +162,7 @@ class Filter
|
|||
{
|
||||
$aConditions = $this->Conditions();
|
||||
return array(
|
||||
'ID' => $this->ID(),
|
||||
'Name' => $this->Name(),
|
||||
'Conditions' => $aConditions,
|
||||
'FilterRulesType' => $this->FilterRulesType(),
|
||||
|
|
|
@ -15,17 +15,6 @@
|
|||
<i class="icon-pencil icon-white"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="btn-group" data-bind="visible: !isDraftFolder()">
|
||||
<a class="btn btn-dark-disabled-border buttonReply" data-tooltip-placement="bottom" data-bind="command: replyCommand, tooltip: 'MESSAGE/BUTTON_REPLY'">
|
||||
<i class="icon-reply"></i>
|
||||
</a>
|
||||
<a class="btn btn-dark-disabled-border buttonReplyAll" data-tooltip-placement="bottom" data-bind="command: replyAllCommand, tooltip: 'MESSAGE/BUTTON_REPLY_ALL'">
|
||||
<i class="icon-reply-all"></i>
|
||||
</a>
|
||||
<a class="btn btn-dark-disabled-border buttonForward" data-tooltip-placement="bottom" data-bind=" command: forwardCommand, tooltip: 'MESSAGE/BUTTON_FORWARD'">
|
||||
<i class="icon-forward"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="btn-group" data-bind="visible: !usePreviewPane()"> </div>
|
||||
<div class="btn-group" data-bind="visible: !usePreviewPane()">
|
||||
<a class="btn btn-dark-disabled-border button-archive" data-tooltip-placement="bottom" data-bind="visible: !isDraftFolder() && !isArchiveFolder() && !isArchiveDisabled(), command: archiveCommand, tooltip: 'MESSAGE/BUTTON_ARCHIVE'">
|
||||
|
@ -42,87 +31,6 @@
|
|||
</a>
|
||||
</div>
|
||||
<div class="btn-group"> </div>
|
||||
<div class="btn-group dropdown colored-toggle" data-bind="registrateBootstrapDropdown: true, openDropdownTrigger: moreDropdownTrigger">
|
||||
<a id="more-view-dropdown-id" class="btn btn-dark-disabled-border dropdown-toggle buttonMore" href="#" tabindex="-1" data-toggle="dropdown" data-tooltip-placement="bottom" data-bind="command: messageVisibilityCommand, tooltip: 'MESSAGE/BUTTON_MORE'">
|
||||
<i class="icon-list"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu g-ui-menu" role="menu" aria-labelledby="more-view-dropdown-id">
|
||||
<li class="e-item" role="presentation" data-bind="visible: !isDraftFolder()">
|
||||
<a class="e-link menuitem" href="#" tabindex="-1" data-bind="command: forwardAsAttachmentCommand">
|
||||
<i class="icon-reply"></i>
|
||||
|
||||
<span class="i18n" data-i18n-text="MESSAGE/BUTTON_FORWARD_AS_ATTACHMENT"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation" data-bind="visible: !isDraftFolder()">
|
||||
<a class="e-link menuitem" href="#" tabindex="-1" data-bind="command: editAsNewCommand">
|
||||
<i class="icon-pencil"></i>
|
||||
|
||||
<span class="i18n" data-i18n-text="MESSAGE/BUTTON_EDIT_AS_NEW"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="divider" role="presentation" data-bind="visible: !isDraftFolder()"></li>
|
||||
<li class="e-item" role="presentation" data-bind="visible: usePreviewPane() && !isDraftFolder() && !isArchiveFolder() && !isArchiveDisabled()">
|
||||
<a target="_blank" class="e-link menuitem" href="#" tabindex="-1" data-bind="command: archiveCommand">
|
||||
<i class="icon-archive"></i>
|
||||
|
||||
<span class="i18n" data-i18n-text="MESSAGE/BUTTON_ARCHIVE"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation" data-bind="visible: usePreviewPane() && !isDraftFolder() && !isSentFolder() && !isSpamFolder() && !isSpamDisabled()">
|
||||
<a target="_blank" class="e-link menuitem" href="#" tabindex="-1" data-bind="command: spamCommand">
|
||||
<i class="icon-angry-smiley"></i>
|
||||
|
||||
<span class="i18n" data-i18n-text="MESSAGE/BUTTON_SPAM"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation" data-bind="visible: usePreviewPane() && !isDraftFolder() && !isSentFolder() && isSpamFolder() && !isSpamDisabled()">
|
||||
<a target="_blank" class="e-link menuitem" href="#" tabindex="-1" data-bind="command: notSpamCommand">
|
||||
<i class="icon-happy-smiley"></i>
|
||||
|
||||
<span class="i18n" data-i18n-text="MESSAGE/BUTTON_NOT_SPAM"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation" data-bind="visible: usePreviewPane()">
|
||||
<a target="_blank" class="e-link menuitem" href="#" tabindex="-1" data-bind="command: deleteCommand">
|
||||
<i class="icon-trash"></i>
|
||||
|
||||
<span class="i18n" data-i18n-text="MESSAGE/BUTTON_DELETE"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="divider" role="presentation" data-bind="visible: usePreviewPane()"></li>
|
||||
<li class="e-item" role="presentation">
|
||||
<a target="_blank" class="e-link menuitem" href="#" tabindex="-1" data-bind="click: function () { if (message()) { message().printMessage(); }} ">
|
||||
<i class="icon-print"></i>
|
||||
|
||||
<span class="i18n" data-i18n-text="MESSAGE/MENU_PRINT"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation">
|
||||
<a target="_blank" class="e-link menuitem" href="#" tabindex="-1" data-bind="click: function () { if (message()) { message().viewPopupMessage(); }}">
|
||||
<i class="icon-popup"></i>
|
||||
|
||||
<span class="i18n" data-i18n-text="MESSAGE/BUTTON_IN_NEW_WINDOW"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="divider" role="presentation"></li>
|
||||
<li class="e-item" role="presentation">
|
||||
<a target="_blank" class="e-link menuitem" href="#" tabindex="-1" data-bind="link: viewViewLink()">
|
||||
<i class="icon-file-code"></i>
|
||||
|
||||
<span class="i18n" data-i18n-text="MESSAGE/MENU_VIEW_ORIGINAL"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation">
|
||||
<a target="_blank" class="e-link menuitem" href="#" tabindex="-1" data-bind="link: viewDownloadLink()">
|
||||
<i class="icon-download"></i>
|
||||
|
||||
<span class="i18n" data-i18n-text="MESSAGE/MENU_DOWNLOAD_ORIGINAL"></span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="btn-group"> </div>
|
||||
<div class="btn-group" data-bind="visible: !usePreviewPane()">
|
||||
<a class="btn btn-dark-disabled-border buttonUp" data-bind="command: goUpCommand">
|
||||
<i class="icon-left-middle"></i>
|
||||
|
@ -146,7 +54,129 @@
|
|||
<div class="b-message-view-desc error" data-bind="visible: !message() && '' !== messageError()">
|
||||
<span class="text" data-bind="text: messageError()"></span>
|
||||
</div>
|
||||
|
||||
<div data-bind="visible: message">
|
||||
|
||||
<div class="btn-group colored-toggle pull-right messageItemDropdown">
|
||||
<a class="btn btn-dark-disabled-border buttonReply" data-tooltip-placement="bottom"
|
||||
data-bind="command: replyCommand, tooltip: 'MESSAGE/BUTTON_REPLY'">
|
||||
<i class="icon-reply"></i>
|
||||
</a>
|
||||
<!--
|
||||
<a class="btn btn-dark-disabled-border buttonReplyAll" data-tooltip-placement="bottom"
|
||||
data-bind="command: replyAllCommand, tooltip: 'MESSAGE/BUTTON_REPLY_ALL'">
|
||||
<i class="icon-reply-all"></i>
|
||||
</a>
|
||||
<a class="btn btn-dark-disabled-border buttonForward" data-tooltip-placement="bottom"
|
||||
data-bind="command: forwardCommand, tooltip: 'MESSAGE/BUTTON_FORWARD'">
|
||||
<i class="icon-reply-all"></i>
|
||||
</a>
|
||||
-->
|
||||
<a id="more-view-dropdown-id" class="btn btn-dark-disabled-border dropdown-toggle buttonMore"
|
||||
href="#" tabindex="-1" data-toggle="dropdown" data-tooltip-placement="bottom"
|
||||
data-bind="command: messageVisibilityCommand, tooltip: 'MESSAGE/BUTTON_MORE'">
|
||||
<span class="caret"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu g-ui-menu" role="menu" aria-labelledby="more-view-dropdown-id">
|
||||
<!--
|
||||
<li class="e-item" role="presentation" data-bind="visible: !isDraftFolder()">
|
||||
<a class="e-link menuitem" href="#" tabindex="-1" data-bind="command: replyCommand">
|
||||
<i class="icon-reply"></i>
|
||||
|
||||
<span class="i18n" data-i18n-text="MESSAGE/BUTTON_REPLY"></span>
|
||||
</a>
|
||||
</li>
|
||||
-->
|
||||
<li class="e-item" role="presentation" data-bind="visible: !isDraftFolder()">
|
||||
<a class="e-link menuitem" href="#" tabindex="-1" data-bind="command: replyAllCommand">
|
||||
<i class="icon-reply-all"></i>
|
||||
|
||||
<span class="i18n" data-i18n-text="MESSAGE/BUTTON_REPLY_ALL"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation" data-bind="visible: !isDraftFolder()">
|
||||
<a class="e-link menuitem" href="#" tabindex="-1" data-bind="command: forwardCommand">
|
||||
<i class="icon-forward"></i>
|
||||
|
||||
<span class="i18n" data-i18n-text="MESSAGE/BUTTON_FORWARD"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation" data-bind="visible: !isDraftFolder()">
|
||||
<a class="e-link menuitem" href="#" tabindex="-1" data-bind="command: editAsNewCommand">
|
||||
<i class="icon-pencil"></i>
|
||||
|
||||
<span class="i18n" data-i18n-text="MESSAGE/BUTTON_EDIT_AS_NEW"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation" data-bind="visible: !isDraftFolder()">
|
||||
<a class="e-link menuitem" href="#" tabindex="-1" data-bind="command: forwardAsAttachmentCommand">
|
||||
<i class="icon-forward"></i>
|
||||
|
||||
<span class="i18n" data-i18n-text="MESSAGE/BUTTON_FORWARD_AS_ATTACHMENT"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="divider" role="presentation" data-bind="visible: !isDraftFolder()"></li>
|
||||
<li class="e-item" role="presentation" data-bind="visible: usePreviewPane() && !isDraftFolder() && !isArchiveFolder() && !isArchiveDisabled()">
|
||||
<a target="_blank" class="e-link menuitem" href="#" tabindex="-1" data-bind="command: archiveCommand">
|
||||
<i class="icon-archive"></i>
|
||||
|
||||
<span class="i18n" data-i18n-text="MESSAGE/BUTTON_ARCHIVE"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation" data-bind="visible: usePreviewPane() && !isDraftFolder() && !isSentFolder() && !isSpamFolder() && !isSpamDisabled()">
|
||||
<a target="_blank" class="e-link menuitem" href="#" tabindex="-1" data-bind="command: spamCommand">
|
||||
<i class="icon-angry-smiley"></i>
|
||||
|
||||
<span class="i18n" data-i18n-text="MESSAGE/BUTTON_SPAM"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation" data-bind="visible: usePreviewPane() && !isDraftFolder() && !isSentFolder() && isSpamFolder() && !isSpamDisabled()">
|
||||
<a target="_blank" class="e-link menuitem" href="#" tabindex="-1" data-bind="command: notSpamCommand">
|
||||
<i class="icon-happy-smiley"></i>
|
||||
|
||||
<span class="i18n" data-i18n-text="MESSAGE/BUTTON_NOT_SPAM"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation" data-bind="visible: usePreviewPane()">
|
||||
<a target="_blank" class="e-link menuitem" href="#" tabindex="-1" data-bind="command: deleteCommand">
|
||||
<i class="icon-trash"></i>
|
||||
|
||||
<span class="i18n" data-i18n-text="MESSAGE/BUTTON_DELETE"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="divider" role="presentation" data-bind="visible: usePreviewPane()"></li>
|
||||
<li class="e-item" role="presentation">
|
||||
<a target="_blank" class="e-link menuitem" href="#" tabindex="-1" data-bind="click: function () { if (message()) { message().printMessage(); }} ">
|
||||
<i class="icon-print"></i>
|
||||
|
||||
<span class="i18n" data-i18n-text="MESSAGE/MENU_PRINT"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation">
|
||||
<a target="_blank" class="e-link menuitem" href="#" tabindex="-1" data-bind="click: function () { if (message()) { message().viewPopupMessage(); }}">
|
||||
<i class="icon-popup"></i>
|
||||
|
||||
<span class="i18n" data-i18n-text="MESSAGE/BUTTON_IN_NEW_WINDOW"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="divider" role="presentation"></li>
|
||||
<li class="e-item" role="presentation">
|
||||
<a target="_blank" class="e-link menuitem" href="#" tabindex="-1" data-bind="link: viewViewLink()">
|
||||
<i class="icon-file-code"></i>
|
||||
|
||||
<span class="i18n" data-i18n-text="MESSAGE/MENU_VIEW_ORIGINAL"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="e-item" role="presentation">
|
||||
<a target="_blank" class="e-link menuitem" href="#" tabindex="-1" data-bind="link: viewDownloadLink()">
|
||||
<i class="icon-download"></i>
|
||||
|
||||
<span class="i18n" data-i18n-text="MESSAGE/MENU_DOWNLOAD_ORIGINAL"></span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="messageItem" data-bind="css: viewLineAsCcc(), nano: true">
|
||||
<div class="content g-scrollbox" tabindex="0" data-bind="hasfocus: messageDomFocused">
|
||||
<div class="content-wrapper">
|
||||
|
@ -264,30 +294,40 @@
|
|||
</div>
|
||||
<div class="attachmentsPlace" data-bind="visible: message() && message().hasVisibleAttachments()">
|
||||
<ul class="attachmentList" data-bind="foreach: message() ? message().attachments() : []">
|
||||
<li class="attachmentItem" draggable="true" data-bind="visible: !isLinked, title: fileName, event: { 'dragstart': eventDragStart }">
|
||||
<div style="white-space: nowrap; text-overflow: ellipsis; overflow: hidden;">
|
||||
<i class="attachmentIcon" data-bind="css: iconClass()"></i>
|
||||
|
||||
<span class="attachmentName" data-bind="text: fileName"></span>
|
||||
<li class="attachmentItem clearfix" draggable="true" data-bind="visible: !isLinked, title: fileName, event: { 'dragstart': eventDragStart },
|
||||
css: { 'hasThumbnail': hasThumbnail() }, attr: { 'style': linkThumbnailPreviewStyle() }">
|
||||
<div class="attachmentIconParent pull-left" data-bind="css: { 'hasPreview': hasPreview() }">
|
||||
<div class="attachmentNonePreview">
|
||||
<i class="attachmentIcon attachmentMainIcon" data-bind="css: iconClass()"></i>
|
||||
</div>
|
||||
<a class="attachmentPreview attachmentImagePreview magnificPopupImage"
|
||||
data-bind="visible: isImage(), attr: {href: linkPreview(), title: fileName, 'data-index': $index}" target="_blank">
|
||||
<i class="attachmentIcon icon-eye show-hover"></i>
|
||||
<i class="attachmentIcon attachmentMainIcon hide-hover" data-bind="css: iconClass()"></i>
|
||||
</a>
|
||||
<a class="attachmentPreview"
|
||||
data-bind="visible: isText(), attr: {href: linkPreviewAsPlain(), title: fileName}" target="_blank">
|
||||
<i class="attachmentIcon icon-eye show-hover"></i>
|
||||
<i class="attachmentIcon attachmentMainIcon hide-hover" data-bind="css: iconClass()"></i>
|
||||
</a>
|
||||
<a class="attachmentPreview"
|
||||
data-bind="visible: isPdf(), attr: {href: linkPreview(), title: fileName}" target="_blank">
|
||||
<i class="attachmentIcon icon-eye show-hover"></i>
|
||||
<i class="attachmentIcon attachmentMainIcon hide-hover" data-bind="css: iconClass()"></i>
|
||||
</a>
|
||||
<a class="attachmentPreview"
|
||||
data-bind="visible: isFramed(), attr: {href: linkFramed(), title: fileName}" target="_blank">
|
||||
<i class="attachmentIcon icon-eye show-hover"></i>
|
||||
<i class="attachmentIcon attachmentMainIcon hide-hover" data-bind="css: iconClass()"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a class="attachmentPreview attachmentImagePreview magnificPopupImage pull-left"
|
||||
data-bind="visible: isImage(), attr: {href: linkPreview(), title: fileName, 'data-index': $index}" target="_blank">
|
||||
<i class="icon-eye"></i>
|
||||
</a>
|
||||
<a class="attachmentPreview pull-left"
|
||||
data-bind="visible: isText(), attr: {href: linkPreviewAsPlain(), title: fileName}" target="_blank">
|
||||
<i class="icon-eye"></i>
|
||||
</a>
|
||||
<a class="attachmentPreview pull-left"
|
||||
data-bind="visible: isPdf(), attr: {href: linkPreview(), title: fileName}" target="_blank">
|
||||
<i class="icon-eye"></i>
|
||||
</a>
|
||||
<a class="attachmentPreview pull-left"
|
||||
data-bind="visible: isFramed(), attr: {href: linkFramed(), title: fileName}" target="_blank">
|
||||
<i class="icon-eye"></i>
|
||||
</a>
|
||||
<span class="attachmentSize pull-right" data-bind="text: friendlySize"></span>
|
||||
<div class="attachmentNameParent">
|
||||
<div style="white-space: nowrap; text-overflow: ellipsis; overflow: hidden;">
|
||||
<span class="attachmentName" data-bind="text: fileName"></span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="attachmentSize" data-bind="text: friendlySize"></span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
Loading…
Reference in a new issue