mirror of
https://github.com/espocrm/espocrm.git
synced 2026-06-28 06:56:05 +00:00
stream image link inserting
This commit is contained in:
@@ -41,4 +41,18 @@ class Attachment extends \Espo\Core\Controllers\Record
|
||||
}
|
||||
return parent::actionList($params, $data, $request);
|
||||
}
|
||||
|
||||
public function postActionGetAttachmentFromImageUrl($params, $data)
|
||||
{
|
||||
if (empty($data->url)) throw new BadRequest();
|
||||
|
||||
return $this->getRecordService()->getAttachmentFromImageUrl($data->url)->getValueMap();
|
||||
}
|
||||
|
||||
public function postActionGetCopiedAttachment($params, $data)
|
||||
{
|
||||
if (empty($data->id)) throw new BadRequest();
|
||||
|
||||
return $this->getRecordService()->getCopiedAttachment($data->id)->getValueMap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ use \Espo\ORM\Entity;
|
||||
use \Espo\Core\Exceptions\BadRequest;
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
use \Espo\Core\Exceptions\NotFound;
|
||||
|
||||
class Attachment extends Record
|
||||
{
|
||||
@@ -166,5 +167,111 @@ class Attachment extends Record
|
||||
$entity->clear('storage');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getCopiedAttachment($id)
|
||||
{
|
||||
$attachment = $this->getEntity($id);
|
||||
if (!$attachment) throw new NotFound();
|
||||
|
||||
$copied = $this->getRepository()->getCopiedAttachment($attachment);
|
||||
|
||||
return $copied;
|
||||
}
|
||||
|
||||
public function getAttachmentFromImageUrl($url)
|
||||
{
|
||||
$attachment = $this->getEntity();
|
||||
|
||||
$data = $this->getImageDataByUrl($url);
|
||||
if (!$data) throw new Error('Attachment::getAttachmentFromImageUrl: Bad image data.');
|
||||
|
||||
$type = $data['type'];
|
||||
$contents = $data['contents'];
|
||||
|
||||
$attachment->set([
|
||||
'name' => $url,
|
||||
'type' => $type,
|
||||
'contents' => $contents,
|
||||
'role' => 'Attachment'
|
||||
]);
|
||||
|
||||
$this->getRepository()->save($attachment);
|
||||
|
||||
$attachment->clear('contents');
|
||||
|
||||
return $attachment;
|
||||
}
|
||||
|
||||
protected function getImageDataByUrl($url)
|
||||
{
|
||||
$type = null;
|
||||
|
||||
if (function_exists('curl_init')) {
|
||||
$opts = [];
|
||||
$httpHeaders = [];
|
||||
$httpHeaders[] = 'Expect:';
|
||||
$opts[\CURLOPT_URL] = $url;
|
||||
$opts[\CURLOPT_HTTPHEADER] = $httpHeaders;
|
||||
$opts[\CURLOPT_CONNECTTIMEOUT] = 10;
|
||||
$opts[\CURLOPT_TIMEOUT] = 10;
|
||||
$opts[\CURLOPT_HEADER] = true;
|
||||
$opts[\CURLOPT_BINARYTRANSFER] = true;
|
||||
$opts[\CURLOPT_VERBOSE] = true;
|
||||
$opts[\CURLOPT_SSL_VERIFYPEER] = false;
|
||||
$opts[\CURLOPT_SSL_VERIFYHOST] = 2;
|
||||
$opts[\CURLOPT_RETURNTRANSFER] = true;
|
||||
$opts[\CURLOPT_FOLLOWLOCATION] = true;
|
||||
$opts[\CURLOPT_MAXREDIRS] = 2;
|
||||
$opts[\CURLOPT_IPRESOLVE] = \CURL_IPRESOLVE_V4;
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt_array($ch, $opts);
|
||||
$response = curl_exec($ch);
|
||||
|
||||
$headerSize = curl_getinfo($ch, \CURLINFO_HEADER_SIZE);
|
||||
|
||||
$header = substr($response, 0, $headerSize);
|
||||
$body = substr($response, $headerSize);
|
||||
|
||||
$headLineList = explode("\n", $header);
|
||||
foreach ($headLineList as $i => $line) {
|
||||
if ($i === 0) continue;
|
||||
if (strpos(strtolower($line), strtolower('Content-Type:')) === 0) {
|
||||
$part = trim(substr($line, 13));
|
||||
if ($part) {
|
||||
$type = trim(explode(";", $part)[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$type) {
|
||||
$extTypeMap = [
|
||||
'png' => 'image/png',
|
||||
'jpg' => 'image/jpeg',
|
||||
'jpeg' => 'image/jpeg',
|
||||
'gif' => 'image/gif'
|
||||
];
|
||||
|
||||
$extension = preg_replace('#\?.*#', '', pathinfo($url, \PATHINFO_EXTENSION));
|
||||
|
||||
if (isset($extTypeMap[$extension])) {
|
||||
$type = $extTypeMap[$extension];
|
||||
}
|
||||
}
|
||||
|
||||
if (!$type) return;
|
||||
|
||||
if (!in_array($type, ['image/png', 'image/jpeg', 'image/gif'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
return [
|
||||
'type' => $type,
|
||||
'contents' => $body
|
||||
];
|
||||
|
||||
curl_close($ch);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div class="form-group post-container{{#if postDisabled}} hidden{{/if}}">
|
||||
<textarea class="note form-control" rows="1" cols="10" placeholder="{{placeholderText}}"></textarea>
|
||||
<div class="textarea-container">{{{postField}}}</div>
|
||||
<div class="buttons-panel margin hide floated-row clearfix">
|
||||
<div>
|
||||
<button class="btn btn-primary post">{{translate 'Post'}}</button>
|
||||
|
||||
@@ -38,10 +38,20 @@ Espo.define('views/note/fields/post', ['views/fields/text', 'lib!Textcomplete'],
|
||||
'input textarea': function (e) {
|
||||
this.controlTextareaHeight();
|
||||
},
|
||||
'paste textarea': function (e) {
|
||||
if (!e.originalEvent.clipboardData) return;
|
||||
var text = e.originalEvent.clipboardData.getData('text/plain');
|
||||
if (!text) return;
|
||||
text = text.trim();
|
||||
if (!text) return;
|
||||
this.handlePastedText(text);
|
||||
}
|
||||
}, Dep.prototype.events),
|
||||
|
||||
setup: function () {
|
||||
Dep.prototype.setup.call(this);
|
||||
|
||||
this.insertedImagesData = {};
|
||||
},
|
||||
|
||||
controlTextareaHeight: function (lastHeight) {
|
||||
@@ -62,7 +72,8 @@ Espo.define('views/note/fields/post', ['views/fields/text', 'lib!Textcomplete'],
|
||||
|
||||
afterRender: function () {
|
||||
Dep.prototype.afterRender.call(this);
|
||||
this.$element.attr('placeholder', this.translate('writeMessage', 'messages', 'Note'));
|
||||
var placeholderText = this.options.placeholderText || this.translate('writeMessage', 'messages', 'Note');
|
||||
this.$element.attr('placeholder', placeholderText);
|
||||
|
||||
this.$textarea = this.$element;
|
||||
var $textarea = this.$textarea;
|
||||
@@ -143,6 +154,81 @@ Espo.define('views/note/fields/post', ['views/fields/text', 'lib!Textcomplete'],
|
||||
return Dep.prototype.validateRequired.call(this);
|
||||
},
|
||||
|
||||
handlePastedText: function (text) {
|
||||
if (/^http(s){0,1}\:\/\//.test(text)) {
|
||||
var imageExtensionList = ['jpg', 'jpeg', 'png', 'gif'];
|
||||
var regExpString = '.+\\.(' + imageExtensionList.join('|') + ')(/?.*){0,1}$';
|
||||
var regExp = new RegExp(regExpString, 'i');
|
||||
var url = text;
|
||||
var siteUrl = this.getConfig().get('siteUrl').replace(/\/$/, '');
|
||||
|
||||
var attachmentIdList = this.model.get('attachmentsIds') || [];
|
||||
|
||||
if (regExp.test(text)) {
|
||||
var insertedId = this.insertedImagesData[url];
|
||||
if (insertedId) {
|
||||
if (~attachmentIdList.indexOf(insertedId)) return;
|
||||
}
|
||||
|
||||
this.ajaxPostRequest('Attachment/action/getAttachmentFromImageUrl', {
|
||||
url: url
|
||||
}).then(function (attachment) {
|
||||
var attachmentIdList = Espo.Utils.clone(this.model.get('attachmentsIds') || []);
|
||||
var attachmentNames = Espo.Utils.clone(this.model.get('attachmentsNames') || {});
|
||||
var attachmentTypes = Espo.Utils.clone(this.model.get('attachmentsTypes') || {});
|
||||
|
||||
attachmentIdList.push(attachment.id);
|
||||
attachmentNames[attachment.id] = attachment.name;
|
||||
attachmentTypes[attachment.id] = attachment.type;
|
||||
|
||||
this.insertedImagesData[url] = attachment.id;
|
||||
|
||||
this.model.set({
|
||||
attachmentsIds: attachmentIdList,
|
||||
attachmentsNames: attachmentNames,
|
||||
attachmentsTypes: attachmentTypes
|
||||
});
|
||||
}.bind(this)).fail(function (xhr) {
|
||||
xhr.errorIsHandled = true;
|
||||
});
|
||||
|
||||
} else if (/\?entryPoint\=image\&/.test(text) && text.indexOf(siteUrl) === 0) {
|
||||
url = text.replace(/[\&]{0,1}size\=[a-z\-]*/, '');
|
||||
|
||||
var match = /\&{0,1}id\=([a-z0-9A-Z]*)/g.exec(text)
|
||||
if (match.length === 2) {
|
||||
var id = match[1];
|
||||
if (~attachmentIdList.indexOf(id)) return;
|
||||
var insertedId = this.insertedImagesData[id];
|
||||
if (insertedId) {
|
||||
if (~attachmentIdList.indexOf(insertedId)) return;
|
||||
}
|
||||
|
||||
this.ajaxPostRequest('Attachment/action/getCopiedAttachment', {
|
||||
id: id
|
||||
}).then(function (attachment) {
|
||||
var attachmentIdList = Espo.Utils.clone(this.model.get('attachmentsIds') || []);
|
||||
var attachmentNames = Espo.Utils.clone(this.model.get('attachmentsNames') || {});
|
||||
var attachmentTypes = Espo.Utils.clone(this.model.get('attachmentsTypes') || {});
|
||||
|
||||
attachmentIdList.push(attachment.id);
|
||||
attachmentNames[attachment.id] = attachment.name;
|
||||
attachmentTypes[attachment.id] = attachment.type;
|
||||
|
||||
this.insertedImagesData[id] = attachment.id;
|
||||
|
||||
this.model.set({
|
||||
attachmentsIds: attachmentIdList,
|
||||
attachmentsNames: attachmentNames,
|
||||
attachmentsTypes: attachmentTypes
|
||||
});
|
||||
}.bind(this)).fail(function (xhr) {
|
||||
xhr.errorIsHandled = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ Espo.define('views/stream/panel', ['views/record/panels/relationship', 'lib!Text
|
||||
postDisabled: false,
|
||||
|
||||
events: _.extend({
|
||||
'focus textarea.note': function (e) {
|
||||
'focus textarea[name="post"]': function (e) {
|
||||
this.enablePostingMode();
|
||||
},
|
||||
'click button.post': function () {
|
||||
@@ -55,7 +55,7 @@ Espo.define('views/stream/panel', ['views/record/panels/relationship', 'lib!Text
|
||||
}
|
||||
|
||||
},
|
||||
'keypress textarea.note': function (e) {
|
||||
'keypress textarea[name="post"]': function (e) {
|
||||
if ((e.keyCode == 10 || e.keyCode == 13) && e.ctrlKey) {
|
||||
this.post();
|
||||
} else if (e.keyCode == 9) {
|
||||
@@ -64,10 +64,7 @@ Espo.define('views/stream/panel', ['views/record/panels/relationship', 'lib!Text
|
||||
this.disablePostingMode();
|
||||
}
|
||||
}
|
||||
},
|
||||
'input textarea.note': function (e) {
|
||||
this.controlTextareaHeight();
|
||||
},
|
||||
}
|
||||
}, Dep.prototype.events),
|
||||
|
||||
data: function () {
|
||||
@@ -78,26 +75,12 @@ Espo.define('views/stream/panel', ['views/record/panels/relationship', 'lib!Text
|
||||
return data;
|
||||
},
|
||||
|
||||
controlTextareaHeight: function (lastHeight) {
|
||||
var scrollHeight = this.$textarea.prop('scrollHeight');
|
||||
var clientHeight = this.$textarea.prop('clientHeight');
|
||||
|
||||
if (clientHeight === lastHeight) return;
|
||||
if (scrollHeight > clientHeight + 1) {
|
||||
this.$textarea.attr('rows', this.$textarea.prop('rows') + 1);
|
||||
this.controlTextareaHeight(clientHeight);
|
||||
}
|
||||
if (this.$textarea.val().length === 0) {
|
||||
this.$textarea.attr('rows', 1);
|
||||
}
|
||||
},
|
||||
|
||||
enablePostingMode: function () {
|
||||
this.$el.find('.buttons-panel').removeClass('hide');
|
||||
|
||||
if (!this.postingMode) {
|
||||
if (this.$textarea.val() && this.$textarea.val().length) {
|
||||
this.controlTextareaHeight();
|
||||
this.getView('postField').controlTextareaHeight();
|
||||
}
|
||||
$('body').on('click.stream-panel', function (e) {
|
||||
var $target = $(e.target);
|
||||
@@ -169,6 +152,18 @@ Espo.define('views/stream/panel', ['views/record/panels/relationship', 'lib!Text
|
||||
attachmentsNames: storedAttachments.names
|
||||
});
|
||||
}
|
||||
|
||||
this.createView('postField', 'views/note/fields/post', {
|
||||
el: this.getSelector() + ' .textarea-container',
|
||||
name: 'post',
|
||||
mode: 'edit',
|
||||
params: {
|
||||
required: true,
|
||||
rows: 1
|
||||
},
|
||||
model: this.seed,
|
||||
placeholderText: this.placeholderText
|
||||
});
|
||||
this.createCollection(function () {
|
||||
this.wait(false);
|
||||
}, this);
|
||||
@@ -213,7 +208,7 @@ Espo.define('views/stream/panel', ['views/record/panels/relationship', 'lib!Text
|
||||
},
|
||||
|
||||
afterRender: function () {
|
||||
this.$textarea = this.$el.find('textarea.note');
|
||||
this.$textarea = this.$el.find('textarea[name="post"]');
|
||||
this.$attachments = this.$el.find('div.attachments');
|
||||
this.$postContainer = this.$el.find('.post-container');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user