getAcl()->checkScope('Attachment', 'create')) { throw new Forbidden(); } $arr = explode(',', $fileData); if (count($arr) > 1) { list($prefix, $contents) = $arr; $contents = base64_decode($contents); } else { $contents = ''; } $attachment = $this->getEntityManager()->getEntity('Attachment'); $attachment->set('contents', $contents); $this->getEntityManager()->saveEntity($attachment); return $attachment; } public function createEntity($data) { if (!empty($data->file)) { $arr = explode(',', $data->file); $contents = ''; if (count($arr) > 1) { $contents = $arr[1]; } $contents = base64_decode($contents); $data->contents = $contents; $relatedEntityType = null; $field = null; $role = 'Attachment'; if (isset($data->parentType)) { $relatedEntityType = $data->parentType; } else if (isset($data->relatedType)) { $relatedEntityType = $data->relatedType; } if (isset($data->field)) { $field = $data->field; } if (isset($data->role)) { $role = $data->role; } if (!$relatedEntityType || !$field) { throw new BadRequest("Params 'field' and 'parentType' not passed along with 'file'."); } $fieldType = $this->getMetadata()->get(['entityDefs', $relatedEntityType, 'fields', $field, 'type']); $this->checkAttachmentField($relatedEntityType, $field); $size = mb_strlen($contents, '8bit'); if ($role === 'Attachment') { if (!in_array($fieldType, $this->attachmentFieldTypeList)) { throw new Error("Field type '{$fieldType}' is not allowed for attachment."); } $maxSize = $this->getMetadata()->get(['entityDefs', $relatedEntityType, 'fields', $field, 'maxFileSize']); if (!$maxSize) { $maxSize = $this->getConfig()->get('attachmentUploadMaxSize'); } if ($maxSize) { if ($size > $maxSize * 1024 * 1024) { throw new Error("File size should not exceed {$maxSize}Mb."); } } } else if ($role === 'Inline Attachment') { if (!in_array($fieldType, $this->inlineAttachmentFieldTypeList)) { throw new Error("Field '{$field}' is not allowed to have inline attachment."); } $inlineAttachmentUploadMaxSize = $this->getConfig()->get('inlineAttachmentUploadMaxSize'); if ($inlineAttachmentUploadMaxSize) { if ($size > $inlineAttachmentUploadMaxSize * 1024 * 1024) { throw new Error("File size should not exceed {$inlineAttachmentUploadMaxSize}Mb."); } } } else { throw new BadRequest("Not supported attachment role."); } } $entity = parent::createEntity($data); if (!empty($data->file)) { $entity->clear('contents'); } return $entity; } protected function beforeCreateEntity(Entity $entity, $data) { $storage = $entity->get('storage'); if ($storage && !$this->getMetadata()->get(['app', 'fileStorage', 'implementationClassNameMap', $storage])) { $entity->clear('storage'); } } protected function beforeUpdateEntity(Entity $entity, $data) { $storage = $entity->get('storage'); if ($storage && !$this->getMetadata()->get(['app', 'fileStorage', 'implementationClassNameMap', $storage])) { $entity->clear('storage'); } } protected function checkAttachmentField($relatedEntityType, $field) { $fieldType = $this->getMetadata()->get(['entityDefs', $relatedEntityType, 'fields', $field, 'type']); if (!$fieldType) { throw new Error("Field '{$field}' does not exist."); } if (!in_array($fieldType, $this->attachmentFieldTypeList)) { throw new Error("Field type '{$fieldType}' is not allowed for attachment."); } if ( !$this->getAcl()->checkScope($relatedEntityType, 'create') && !$this->getAcl()->checkScope($relatedEntityType, 'edit') ) { throw new Forbidden("No access to " . $relatedEntityType . "."); } if (in_array($field, $this->getAcl()->getScopeForbiddenFieldList($relatedEntityType, 'edit'))) { throw new Forbidden("No access to field '" . $field . "'."); } } public function getCopiedAttachment($data) { if (empty($data->id)) throw new BadRequest(); if (empty($data->field)) throw new BadRequest(); if (isset($data->parentType)) { $relatedEntityType = $data->parentType; } else if (isset($data->relatedType)) { $relatedEntityType = $data->relatedType; } else { throw new BadRequest(); } $field = $data->field; $this->checkAttachmentField($relatedEntityType, $field); $attachment = $this->getEntity($data->id); if (!$attachment) throw new NotFound(); $copied = $this->getRepository()->getCopiedAttachment($attachment); $attachment = $copied; if (isset($data->parentType)) { $attachment->set('parentType', $data->parentType); } if (isset($data->relatedType)) { $attachment->set('relatedType', $data->relatedType); } $attachment->set('field', $field); $attachment->set('role', 'Attachment'); $this->getRepository()->save($attachment); return $copied; } public function getAttachmentFromImageUrl($data) { $attachment = $this->getEntity(); if (empty($data->url)) throw new BadRequest(); if (empty($data->field)) throw new BadRequest(); if (isset($data->parentType)) { $relatedEntityType = $data->parentType; } else if (isset($data->relatedType)) { $relatedEntityType = $data->relatedType; } else { throw new BadRequest(); } $url = $data->url; $field = $data->field; $this->checkAttachmentField($relatedEntityType, $field); $data = $this->getImageDataByUrl($url); if (!$data) throw new Error('Attachment::getAttachmentFromImageUrl: Bad image data.'); $type = $data['type']; $contents = $data['contents']; $size = mb_strlen($contents, '8bit'); $maxSize = $this->getMetadata()->get(['entityDefs', $relatedEntityType, 'fields', $field, 'maxFileSize']); if (!$maxSize) { $maxSize = $this->getConfig()->get('attachmentUploadMaxSize'); } if ($maxSize) { if ($size > $maxSize * 1024 * 1024) { throw new Error("File size should not exceed {$maxSize}Mb."); } } $attachment->set([ 'name' => $url, 'type' => $type, 'contents' => $contents, 'role' => 'Attachment' ]); if (isset($data->parentType)) { $attachment->set('parentType', $data->parentType); } if (isset($data->relatedType)) { $attachment->set('relatedType', $data->relatedType); } $attachment->set('field', $field); $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; } }