diff --git a/.upgrade.yml b/.upgrade.yml
index 189dffc..f8b12ee 100644
--- a/.upgrade.yml
+++ b/.upgrade.yml
@@ -1,6 +1,6 @@
mappings:
- DocumentConverterDecorator: SilverStripe\DocumentConverter\ConvertsDocumentsExtension
- DocumentImportField: SilverStripe\DocumentConverter\DocumentConversionField
- DocumentImportIFrameField_Importer: SilverStripe\DocumentConverter\DocumentConverter
- DocumentImportInnerField: SilverStripe\DocumentConverter\DocumentImporterField
+ DocumentConverterDecorator: SilverStripe\DocumentConverter\PageExtension
+ DocumentImportField: SilverStripe\DocumentConverter\SettingsField
+ DocumentImportIFrameField_Importer: SilverStripe\DocumentConverter\ServiceConnector
+ DocumentImportInnerField: SilverStripe\DocumentConverter\ImportField
DocumentConverterTest: SilverStripe\DocumentConverter\Tests\DocumentConverterTest
diff --git a/composer.json b/composer.json
index 8a43c26..530e9ad 100644
--- a/composer.json
+++ b/composer.json
@@ -5,9 +5,12 @@
"license": "BSD-3-Clause",
"keywords": ["silverstripe", "cwp"],
"require": {
- "silverstripe/cms": "^4"
+ "silverstripe/cms": "^4",
+ "silverstripe/asset-admin": "^1"
},
"require-dev": {
+ "ext-tidy": "*",
+ "ext-zip": "*",
"phpunit/phpunit": "^5.7",
"squizlabs/php_codesniffer": "^3.0"
},
diff --git a/lang/ar.yml b/lang/ar.yml
index 992c2b3..503092a 100644
--- a/lang/ar.yml
+++ b/lang/ar.yml
@@ -1,10 +1,10 @@
ar:
- SilverStripe\DocumentConverter\DocumentConverter:
+ SilverStripe\DocumentConverter\ServiceConnector:
PROCESSFAILED: 'لم يتم التمكن من معالجة الوثيقة, من فضلك قم بمراجعة أنك قمت برفع الملف بصيغة .دى أو سى أو صيغة .دى أو سى إكس.'
SERVERUNREACHABLE: 'لم يتم التواصل مع سيرفر تحويل الوثائق. من فضلك حاول مرة أخرى لاحقا أو اتصل بمشرف النظام الخاص بك.'
- SilverStripe\DocumentConverter\ConvertsDocumentsExtension:
+ SilverStripe\DocumentConverter\PageExtension:
ImportTab: استيراد
- SilverStripe\DocumentConverter\DocumentConversionField:
+ SilverStripe\DocumentConverter\SettingsField:
ChooseFolder: 'اختر مجلد لحفظ هذا الملف'
EachH1: 'لكل عنوان واحد'
EachH2: 'لكل عنوان اثنين'
@@ -14,7 +14,7 @@ ar:
KeepSource: 'احتفظ بالملف الأصلي. وقم بإضافة رابط له في جدول المحتويات، إذا أمكن ذلك.'
SplitHeader: 'تقسيم المستند إلى صفحات'
publishPages: 'نشر الصفحات المُعدلة (لا يُوصى به إلا إذا كنت متأكد من ناتج التحويل)'
- SilverStripe\DocumentConverter\DocumentImporterField:
+ SilverStripe\DocumentConverter\ImportField:
ATTACHONCESAVED2: 'يمكن إرفاق الملفات عند حفظ السجل للمرة الأولى.'
UploadField:
ATTACHFILE: 'إرفاق ملف'
diff --git a/lang/de.yml b/lang/de.yml
index c03d14d..4ce7cd7 100644
--- a/lang/de.yml
+++ b/lang/de.yml
@@ -1,7 +1,7 @@
de:
- SilverStripe\DocumentConverter\ConvertsDocumentsExtension:
+ SilverStripe\DocumentConverter\PageExtension:
ImportTab: Importieren
- SilverStripe\DocumentConverter\DocumentConversionField:
+ SilverStripe\DocumentConverter\SettingsField:
ChooseFolder: 'Wählen Sie einen Ordner zum Speichern dieser Datei aus'
EachH1: 'für jede Überschrift 1'
EachH2: 'für jede Überschrift 2'
diff --git a/lang/en.yml b/lang/en.yml
index a2b125c..e88fee9 100644
--- a/lang/en.yml
+++ b/lang/en.yml
@@ -1,10 +1,10 @@
en:
- SilverStripe\DocumentConverter\DocumentConverter:
+ SilverStripe\DocumentConverter\ServiceConnector:
PROCESSFAILED: 'Could not process document, please double-check you uploaded a .doc or .docx format.'
SERVERUNREACHABLE: 'Could not contact document conversion server. Please try again later or contact your system administrator.'
- SilverStripe\DocumentConverter\ConvertsDocumentsExtension:
+ SilverStripe\DocumentConverter\PageExtension:
ImportTab: Import
- SilverStripe\DocumentConverter\DocumentConversionField:
+ SilverStripe\DocumentConverter\SettingsField:
ChooseFolder: 'Choose a folder to save this file'
EachH1: 'for each heading 1'
EachH2: 'for each heading 2'
@@ -15,7 +15,7 @@ en:
'No': 'no'
SplitHeader: 'Split document into pages'
publishPages: 'Publish modified pages (not recommended unless you are sure about the conversion outcome)'
- SilverStripe\DocumentConverter\DocumentImporterField:
+ SilverStripe\DocumentConverter\ImportField:
ATTACHONCESAVED2: 'Files can be attached once you have saved the record for the first time.'
UploadField:
ATTACHFILE: 'Attach a file'
diff --git a/lang/eo.yml b/lang/eo.yml
index ff7961a..c37e151 100644
--- a/lang/eo.yml
+++ b/lang/eo.yml
@@ -1,10 +1,10 @@
eo:
- SilverStripe\DocumentConverter\DocumentConverter:
+ SilverStripe\DocumentConverter\ServiceConnector:
PROCESSFAILED: 'Ne povis trakti dokumenton. Bonvole kontrolu ke vi alŝutis en formato .doc aŭ .docx.'
SERVERUNREACHABLE: 'Ne povis kontakti servilon por konverti dokumenton. Bonvole reprovu postiome aŭ kontaktu vian administranton.'
- SilverStripe\DocumentConverter\ConvertsDocumentsExtension:
+ SilverStripe\DocumentConverter\PageExtension:
ImportTab: Importi
- SilverStripe\DocumentConverter\DocumentConversionField:
+ SilverStripe\DocumentConverter\SettingsField:
ChooseFolder: 'Elektu dosierujon en kiu konservi la dosieron'
EachH1: 'por ĉiu titolo 1'
EachH2: 'por ĉiu titolo 2'
@@ -15,7 +15,7 @@ eo:
'No': ne
SplitHeader: 'Dividi dokumenton en paĝojn'
publishPages: 'Publikigi ŝanĝitajn paĝojn (ne rekomendinde krom se vi certas pri la rezulto de konverto)'
- SilverStripe\DocumentConverter\DocumentImporterField:
+ SilverStripe\DocumentConverter\ImportField:
ATTACHONCESAVED2: 'Kiam vi unuafoje estos konservinta la rikordon vi povos alligi dosierojn.'
UploadField:
ATTACHFILE: 'Alligi dosieron'
diff --git a/lang/fa_IR.yml b/lang/fa_IR.yml
index a98ba83..d678140 100644
--- a/lang/fa_IR.yml
+++ b/lang/fa_IR.yml
@@ -1,7 +1,7 @@
fa_IR:
- SilverStripe\DocumentConverter\ConvertsDocumentsExtension:
+ SilverStripe\DocumentConverter\PageExtension:
ImportTab: وارد کردن
- SilverStripe\DocumentConverter\DocumentConversionField:
+ SilverStripe\DocumentConverter\SettingsField:
ChooseFolder: 'یک پوشه برای ذخیره این فایل را انتخاب کنید'
UploadField:
ATTACHFILE: 'یک فایل ضمیمه کنید'
diff --git a/lang/fi.yml b/lang/fi.yml
index 2432796..cda8648 100644
--- a/lang/fi.yml
+++ b/lang/fi.yml
@@ -1,10 +1,10 @@
fi:
- SilverStripe\DocumentConverter\DocumentConverter:
+ SilverStripe\DocumentConverter\ServiceConnector:
PROCESSFAILED: 'Dokumenttia ei voitu prosessoida, ole hyvä ja tarkista että siirtämäsi dokumentit olivat varmasti .doc tai .docx muodossa.'
SERVERUNREACHABLE: 'Muunnospalvelimeen ei saatu yhteyttä. Ole hyvä ja yritä myöhemmin uudelleen tai ota yhteyttä järjestelmän ylläpitäjään.'
- SilverStripe\DocumentConverter\ConvertsDocumentsExtension:
+ SilverStripe\DocumentConverter\PageExtension:
ImportTab: Tuo
- SilverStripe\DocumentConverter\DocumentConversionField:
+ SilverStripe\DocumentConverter\SettingsField:
ChooseFolder: 'Valitse kansio, jonne tiedosto tallennetaan'
EachH1: 'jokaiselle otsikko 1'
EachH2: 'jokaiselle otsikko 2'
@@ -15,7 +15,7 @@ fi:
'No': ei
SplitHeader: 'Jaa dokumentti sivuiksi'
publishPages: 'Julkaise muokatut sivut (ei suositella, jollet ole varma muunnoksen lopputulemasta)'
- SilverStripe\DocumentConverter\DocumentImporterField:
+ SilverStripe\DocumentConverter\ImportField:
ATTACHONCESAVED2: 'Tiedostot voidaan liittää, kun olet tallentanut ensin tietueen.'
UploadField:
ATTACHFILE: 'Liitä tiedosto'
diff --git a/lang/id.yml b/lang/id.yml
index f0683c6..97850e8 100644
--- a/lang/id.yml
+++ b/lang/id.yml
@@ -1,7 +1,7 @@
id:
- SilverStripe\DocumentConverter\ConvertsDocumentsExtension:
+ SilverStripe\DocumentConverter\PageExtension:
ImportTab: Impor
- SilverStripe\DocumentConverter\DocumentConversionField:
+ SilverStripe\DocumentConverter\SettingsField:
ChooseFolder: 'Pilih sebuah map untuk menyimpan berkas ini'
UploadField:
FROMCOMPUTER: 'Dari komputermu'
diff --git a/lang/mi.yml b/lang/mi.yml
index 67ed3fa..d4c05ae 100644
--- a/lang/mi.yml
+++ b/lang/mi.yml
@@ -1,10 +1,10 @@
mi:
- SilverStripe\DocumentConverter\DocumentConverter:
+ SilverStripe\DocumentConverter\ServiceConnector:
PROCESSFAILED: 'Kāore i taea te tukatuka te tuhinga, whakatūturu pūtia kua tukuna atu he hōputu .doc, .docx rānei.'
SERVERUNREACHABLE: 'Kāore i taea te whakapā ki te tūmau huri tuhinga. Me ngana anō ā muri ake, ka whakapā rānei ki tō kaiwhakahaere pūnaha.'
- DocumentCSilverStripe\DocumentConverter\ConvertsDocumentsExtension:
+ DocumentCSilverStripe\DocumentConverter\PageExtension:
ImportTab: Kawemai
- SilverStripe\DocumentConverter\DocumentImporterField:
+ SilverStripe\DocumentConverter\ImportField:
ATTACHONCESAVED2: 'Ka taea te āpiti kōnae ina oti te tiaki tuatahi o te pūkete.'
UploadField:
ATTACHFILE: 'Tāpiritia tētahi kōnae'
diff --git a/lang/ru.yml b/lang/ru.yml
index 5158ff1..aa93319 100644
--- a/lang/ru.yml
+++ b/lang/ru.yml
@@ -1,10 +1,10 @@
ru:
- SilverStripe\DocumentConverter\DocumentConverter:
+ SilverStripe\DocumentConverter\ServiceConnector:
PROCESSFAILED: 'Не могу обработать документ, пожалуйста проверьте что документ загружен в формате .doc или .docx.'
SERVERUNREACHABLE: 'Не могу подключиться к серверу конвертации документов. Пожалуйста попробуйте позднее или свяжитесь с вашим системным администратором.'
- DocumentConverterDecorator:
+ SilverStripe\DocumentConverter\PageExtension:
ImportTab: Импорт
- DocumentImportField:
+ SilverStripe\DocumentConverter\SettingsField:
ChooseFolder: 'Выберите папку для сохранения файла'
EachH1: 'для каждого заголовка 1'
EachH2: 'для каждого заголовка 2'
@@ -15,7 +15,7 @@ ru:
'No': нет
SplitHeader: 'Разделить документ на страницы'
publishPages: 'Опубликовать модифицированные страницы (не рекомендуется если вы не уверены в последствиях)'
- SilverStripe\DocumentConverter\DocumentImporterField:
+ SilverStripe\DocumentConverter\ImportField:
ATTACHONCESAVED2: 'Вы сможете прикрепитьфайлы после первого сохранения записи.'
UploadField:
ATTACHFILE: 'Прикрепить файл'
diff --git a/lang/zh.yml b/lang/zh.yml
index 4caab4c..70dcbba 100644
--- a/lang/zh.yml
+++ b/lang/zh.yml
@@ -1,10 +1,10 @@
zh:
- SilverStripe\DocumentConverter\DocumentConverter:
+ SilverStripe\DocumentConverter\ServiceConnector:
PROCESSFAILED: '无法处理文档,请复核您上传的是 .doc 或 .docx 格式。'
SERVERUNREACHABLE: '无法连到文档转换服务器。请稍后再试或联系系统管理员。'
- SilverStripe\DocumentConverter\ConvertsDocumentsExtension:
+ SilverStripe\DocumentConverter\PageExtension:
ImportTab: 导入
- SilverStripe\DocumentConverter\DocumentConversionField:
+ SilverStripe\DocumentConverter\SettingsField:
ChooseFolder: '选择文件夹保存此文件'
EachH1: '每个标题1'
EachH2: '每个标题2'
@@ -14,7 +14,7 @@ zh:
KeepSource: '保留原始文档。如果允许的话,在 TOC 上为其添加一个链接。'
SplitHeader: 拆分文件转换成网页
publishPages: '发布修改页面(除非你确认转换结果,否则不推荐)'
- SilverStripe\DocumentConverter\DocumentImporterField:
+ SilverStripe\DocumentConverter\ImportField:
ATTACHONCESAVED2: '只要您第一次保存记录,文件就可以被附加。'
UploadField:
ATTACHFILE: '附加一个文件'
diff --git a/src/ConvertsDocumentsExtension.php b/src/ConvertsDocumentsExtension.php
deleted file mode 100644
index 3c8df15..0000000
--- a/src/ConvertsDocumentsExtension.php
+++ /dev/null
@@ -1,31 +0,0 @@
- File::class
- );
-
- function updateCMSFields(FieldList $fields) {
- /*
- // Currently the ToggleCompositeField plays badly with TreeDropdownField formatting.
- // Could be switched back in the future, if this is fixed.
- $fields->addFieldToTab('Root.Main',
- ToggleCompositeField::create('Import', 'Import', array(
- new DocumentImportField()
- ))->setHeadingLevel(4)
- );
- */
- $fields->findOrMakeTab(
- 'Root.Import',
- _t(__CLASS__ . '.ImportTab', 'Import')
- );
- $fields->addFieldToTab('Root.Import', DocumentConversionField::create());
- }
-}
diff --git a/src/DocumentConversionField.php b/src/DocumentConversionField.php
deleted file mode 100644
index 843cbb5..0000000
--- a/src/DocumentConversionField.php
+++ /dev/null
@@ -1,101 +0,0 @@
-setName() if you want to change it.');
- if ($children) throw new InvalidArgumentException('DocumentConversionField::__construct provides its own fields and does not accept additional children.');
-
- // Add JS specific to this field.
- Requirements::javascript('silverstripe/documentconverter: javascript/DocumentConversionField.js');
-
- $fields = FieldList::create(array(
- HeaderField::create(
- 'FileWarningHeader',
- _t(
- __CLASS__ . '.FileWarningHeader',
- 'Warning: import will remove all content and subpages of this page.'
- ),
- 4
- ),
- $splitHeader = DropdownField::create(
- 'DocumentConversionField-SplitHeader',
- _t(
- __CLASS__ . '.SplitHeader',
- 'Split document into pages'
- ),
- array(
- 0 => _t(__CLASS__ . '.No','no'),
- 1 => _t(__CLASS__ . '.EachH1','for each heading 1'),
- 2 => _t(__CLASS__ . '.EachH2','for each heading 2')
- )
- ),
- $keepSource = CheckboxField::create(
- 'DocumentConversionField-KeepSource',
- _t(
- __CLASS__ . '.KeepSource',
- 'Keep the original document. Adds a link to it on TOC, if enabled.'
- )
- ),
- $chosenFolderID = TreeDropdownField::create(
- 'DocumentConversionField-ChosenFolderID',
- _t(__CLASS__ . '.ChooseFolder', 'Choose a folder to save this file'),
- Folder::class
- ),
- $includeTOC = CheckboxField::create(
- 'DocumentConversionField-IncludeTOC',
- _t(__CLASS__ . '.IncludeTOC', 'Replace this page with a Table of Contents.')
- ),
- $publishPages = CheckboxField::create(
- 'DocumentConversionField-PublishPages',
- _t(
- __CLASS__ . '.publishPages',
- 'Publish modified pages (not recommended unless you are sure about the conversion outcome)'
- )
- ),
- $this->innerField = DocumentImporterField::create(
- 'ImportedFromFile',
- _t(__CLASS__ . '.ImportedFromFile','Import content from a word document')
- ),
- ));
-
- // Prevent the warning popup that appears when navigating away from the page.
- $splitHeader->addExtraClass('no-change-track');
- $keepSource->addExtraClass('no-change-track');
- $chosenFolderID->addExtraClass('no-change-track');
- $includeTOC->addExtraClass('no-change-track');
- $publishPages->addExtraClass('no-change-track');
-
- return parent::__construct($fields);
- }
-
- public function getInnerField() {
- return $this->innerField;
- }
-}
diff --git a/src/DocumentConverter.php b/src/DocumentConverter.php
deleted file mode 100644
index 74fe6f3..0000000
--- a/src/DocumentConverter.php
+++ /dev/null
@@ -1,153 +0,0 @@
- '',
- 'password' => '',
- 'url' => ''
- ];
-
- /**
- * Associative array of:
- * - name: the full name of the file including the extension.
- * - path: the path to the file on the local filesystem.
- * - mimeType
- */
- protected $fileDescriptor;
-
- /**
- * @var int
- * ID of a SilverStripe\Assets\Folder
- */
- protected $chosenFolderID;
-
- /**
- * @var array instance specific connection details
- * initially filled with the config settings
- */
- protected $docvertDetails = [
- 'username' => '',
- 'password' => '',
- 'url' => ''
- ];
-
- public function __construct($fileDescriptor, $chosenFolderID = null) {
- $this->fileDescriptor = $fileDescriptor;
- $this->chosenFolderID = $chosenFolderID;
- array_merge($this->docvertDetails, (array)$this->config()->get('docvert_details'));
- }
-
- public function setDocvertUsername($username = null) {
- $this->docvertDetails['username'] = $username;
- }
-
- public function getDocvertUsername() {
- return $this->docvertDetails['username'];
- }
-
- public function setDocvertPassword($password = null) {
- $this->docvertDetails['password'] = $password;
- }
-
- public function getDocvertPassword() {
- return $this->docvertDetails['password'];
- }
-
- public function setDocvertUrl($url = null) {
- $this->docvertDetails['url'] = $url;
- }
-
- public function getDocvertUrl() {
- return $this->docvertDetails['url'];
- }
-
- public function import() {
- $ch = curl_init();
-
- // PHP 5.5+ introduced CURLFile which makes the '@/path/to/file' syntax deprecated.
- if(class_exists('CURLFile')) {
- $file = new CURLFile(
- $this->fileDescriptor['path'],
- $this->fileDescriptor['mimeType'],
- $this->fileDescriptor['name']
- );
- } else {
- $file = '@' . $this->fileDescriptor['path'];
- }
-
- curl_setopt_array($ch, array(
- CURLOPT_URL => $this->getDocvertUrl(),
- CURLOPT_USERPWD => sprintf('%s:%s', $this->getDocvertUsername(), $this->getDocvertPassword()),
- CURLOPT_POST => 1,
- CURLOPT_POSTFIELDS => array('file' => $file),
- CURLOPT_CONNECTTIMEOUT => 25,
- CURLOPT_TIMEOUT => 100,
- ));
-
- $chosenFolder = ($this->chosenFolderID) ? DataObject::get_by_id(Folder::class, $this->chosenFolderID) : null;
- $folderName = ($chosenFolder) ? '/' . $chosenFolder->Name : '';
- $outname = tempnam(ASSETS_PATH, 'convert');
- $outzip = $outname . '.zip';
-
- $out = fopen($outzip, 'w');
- curl_setopt($ch, CURLOPT_FILE, $out);
- $returnValue = curl_exec($ch);
- $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
- curl_close($ch);
- fclose($out);
- chmod($outzip, 0666);
-
- if (!$returnValue || ($status != 200)) {
- return array('error' => _t(
- __CLASS__ . '.SERVERUNREACHABLE',
- 'Could not contact document conversion server. Please try again later or contact your system administrator.',
- 'Document Converter process Word documents into HTML.'
- ));
- }
-
- // extract the converted document into assets
- // you need php zip, i.e. port install php5-zip
- $zip = new ZipArchive();
-
- if($zip->open($outzip)) {
- $zip->extractTo(ASSETS_PATH .$folderName);
- }
-
- // remove temporary files
- unlink($outname);
- unlink($outzip);
-
- if (!file_exists(ASSETS_PATH . $folderName . '/index.html')) {
- return array('error' => _t(
- __CLASS__ . '.PROCESSFAILED',
- 'Could not process document, please double-check you uploaded a .doc or .docx format.',
- 'Document Converter processes Word documents into HTML.'
- ));
- }
-
- $content = file_get_contents(ASSETS_PATH . $folderName . '/index.html');
-
- unlink(ASSETS_PATH . $folderName . '/index.html');
-
- return $content;
- }
-
-}
diff --git a/src/DocumentImporterField.php b/src/DocumentImporterField.php
deleted file mode 100644
index 67fdb51..0000000
--- a/src/DocumentImporterField.php
+++ /dev/null
@@ -1,434 +0,0 @@
-isDisabled() || $this->isReadonly()) return $this->httpError(403);
-
- // Protect against CSRF on destructive action
- $token = $this->getForm()->getSecurityToken();
- if(!$token->checkRequest($request)) return $this->httpError(400);
-
- $name = $this->getName();
- $tmpfile = $request->postVar($name);
-
- // Check if the file has been uploaded into the temporary storage.
- if (!$tmpfile) {
- $return = array('error' => _t('SilverStripe\\AssetAdmin\\Forms\\UploadField.FIELDNOTSET', 'File information not found'));
- } else {
- $return = array(
- 'name' => $tmpfile['name'],
- 'size' => $tmpfile['size'],
- 'type' => $tmpfile['type'],
- 'error' => $tmpfile['error']
- );
- }
-
- if (!$return['error']) {
- // Get options for this import.
- $splitHeader = (int)$request->postVar('SplitHeader');
- $keepSource = (bool)$request->postVar('KeepSource');
- $chosenFolderID = (int)$request->postVar('ChosenFolderID');
- $publishPages = (bool)$request->postVar('PublishPages');
- $includeTOC = (bool)$request->postVar('IncludeTOC');
-
- // Process the document and write the page.
- $preservedDocument = null;
- if ($keepSource) $preservedDocument = $this->preserveSourceDocument($tmpfile, $chosenFolderID);
-
- $importResult = $this->importFromPOST($tmpfile, $splitHeader, $publishPages, $chosenFolderID);
- if (is_array($importResult) && isset($importResult['error'])) {
- $return['error'] = $importResult['error'];
- } else if ($includeTOC) {
- $this->writeTOC($publishPages, $keepSource ? $preservedDocument : null);
- }
- }
-
- $response = HTTPResponse::create(Convert::raw2json(array($return)));
- $response->addHeader('Content-Type', 'text/plain');
- return $response;
- }
-
- /**
- * Preserves the source file by copying it to a specified folder.
- *
- * @param $tmpfile Temporary file data structure.
- * @param int $chosenFolderID Target folder.
- * @return File Stored file.
- */
- protected function preserveSourceDocument($tmpfile, $chosenFolderID = null) {
- $upload = Upload::create();
-
- $file = File::create();
- $upload->loadIntoFile($tmpfile, $file, $chosenFolderID);
-
- $page = $this->form->getRecord();
- $page->ImportedFromFileID = $file->ID;
- $page->write();
-
- return $file;
- }
-
- /**
- * Builds and writes the table of contents for the document.
- *
- * @param bool $publishPage Should the parent page be published.
- * @param File $preservedDocument Set if the link to the original document should be added.
- */
- protected function writeTOC($publishPages = false, $preservedDocument = null) {
- $page = $this->form->getRecord();
- $content = '
';
-
- if($page) {
- if($page->Children()->Count() > 0) {
- foreach($page->Children() as $child) {
- $content .= '- ' . $child->Title . '
';
- }
- $page->Content = $content . '
';
- } else {
- $doc = new DOMDocument();
- $doc->loadHTML($page->Content);
- $body = $doc->getElementsByTagName('body')->item(0);
- $node = $body->firstChild;
- $h1 = $h2 = 1;
- while($node) {
- if($node instanceof DOMElement && $node->tagName == 'h1') {
- $content .= ''. trim(preg_replace('/\n|\r/', '', Convert::html2raw($node->textContent))) . '';
- $node->setAttributeNode(new DOMAttr("id", "h1.".$h1));
- $h1++;
- } elseif($node instanceof DOMElement && $node->tagName == 'h2') {
- $content .= '';
- $node->setAttributeNode(new DOMAttr("id", "h2.".$h2));
- $h2++;
- }
- $node = $node->nextSibling;
- }
- $page->Content = $content . '' . $doc->saveHTML();
- }
-
- // Add in the link to the original document, if provided.
- if($preservedDocument) {
- $page->Content = 'download original document (' .
- $preservedDocument->getSize() . ')' . $page->Content;
- }
-
- // Store the result
- $page->write();
- if($publishPages) $page->doPublish();
- }
- }
-
- protected function getBodyText($doc, $node) {
- // Build a new doc
- $htmldoc = new DOMDocument();
- // Create the html element
- $html = $htmldoc->createElement('html');
- $htmldoc->appendChild($html);
- // Append the body node
- $html->appendChild($htmldoc->importNode($node, true));
-
- // Get the text as html, remove the entry and exit root tags and return
- $text = $htmldoc->saveHTML();
- $text = preg_replace('/^.*/', '', $text);
- $text = preg_replace('/<\/body>.*$/', '', $text);
-
- return $text;
- }
-
- /**
- * Used only when writing the document that has been split by headers.
- * Can write both to the chapter pages as well as the master page.
- *
- * @param string $subtitle Title of the chapter - if missing, it will write to the master page.
- * @param $subdoc
- * @param $subnode
- * @param int $sort Order of the chapter page.
- * @param $publishPages Whether to publish the resulting child/master pages.
- */
- protected function writeContent($subtitle, $subdoc, $subnode, $sort = null, $publishPages = false) {
- $record = $this->form->getRecord();
-
- if($subtitle) {
- // Write the chapter page to a subpage.
- $page = DataObject::get_one('Page', sprintf('"Title" = \'%s\' AND "ParentID" = %d', $subtitle, $record->ID));
- if(!$page) {
- $page = Page::create();
- $page->ParentID = $record->ID;
- $page->Title = $subtitle;
- }
-
- unset($this->unusedChildren[$page->ID]);
- file_put_contents(ASSETS_PATH . '/index-' . $sort . '.html', $this->getBodyText($subdoc, $subnode));
-
- if ($sort) $page->Sort = $sort;
- $page->Content = $this->getBodyText($subdoc, $subnode);
- $page->write();
- if($publishPages) $page->doPublish();
- } else {
- // Write to the master page.
- $record->Content = $this->getBodyText($subdoc, $subnode);
- $record->write();
-
- if($publishPages) $record->doPublish();
- }
-
- }
-
- /**
- * Imports a document at a certain path onto the current page and writes it.
- * CAUTION: Overwrites any existing content on the page!
- *
- * @param array $tmpFile Array as received from PHP's POST upload.
- * @param bool $splitHeader Heading level to split by.
- * @param bool $publishPages Whether the underlying pages should be published after import.
- * @param int $chosenFolderID ID of the working folder - here the converted file and images will be stored.
- */
- public function importFromPOST($tmpFile, $splitHeader = false, $publishPages = false, $chosenFolderID = null) {
-
- $fileDescriptor = array(
- 'name' => $tmpFile['name'],
- 'path' => $tmpFile['tmp_name'],
- 'mimeType' => $tmpFile['type']
- );
-
- $sourcePage = $this->form->getRecord();
- $importerClass = Config::inst()->get(__CLASS__, 'importer_class');
- $importer = Injector::inst()->create($importerClass, $fileDescriptor, $chosenFolderID);
- $content = $importer->import();
-
- if (is_array($content) && isset($content['error'])) {
- return $content;
- }
-
- // Clean up with tidy (requires tidy module)
- $tidy = new Tidy();
- $tidy->parseString($content, array('output-xhtml' => true), 'utf8');
- $tidy->cleanRepair();
-
- $fragment = [];
- foreach($tidy->body()->child as $child) {
- $fragment[] = $child->value;
- }
-
- $htmlValue = Injector::inst()->create('HTMLValue', implode("\n", $fragment));
-
- // Sanitise
- $santiser = Injector::inst()->create('HtmlEditorSanitiser', HtmlEditorConfig::get_active());
- $santiser->sanitise($htmlValue);
-
- // Load in the HTML
- $doc = $htmlValue->getDocument();
- $xpath = new DOMXPath($doc);
-
- // make sure any images are added as Image records with a relative link to assets
- $chosenFolder = ($this->chosenFolderID) ? DataObject::get_by_id(Folder::class, $this->chosenFolderID) : null;
- $folderName = ($chosenFolder) ? '/' . $chosenFolder->Name : '';
- $imgs = $xpath->query('//img');
- for($i = 0; $i < $imgs->length; $i++) {
- $img = $imgs->item($i);
- $originalPath = 'assets/' . $folderName . '/' . $img->getAttribute('src');
- $name = FileNameFilter::create()->filter(basename($originalPath));
-
- $image = Image::get()->filter(array('Name' => $name, 'ParentID' => (int) $chosenFolderID))->first();
- if(!($image && $image->exists())) {
- $image = Image::create();
- $image->ParentID = (int) $chosenFolderID;
- $image->Name = $name;
- $image->write();
- }
-
- // make sure it's put in place correctly so Image record knows where it is.
- // e.g. in the case of underscores being renamed to dashes.
- @rename(Director::getAbsFile($originalPath), Director::getAbsFile($image->getFilename()));
-
- $img->setAttribute('src', $image->getFilename());
- }
-
- $remove_rules = array(
- '//h1[.//font[not(@face)]]' => 'p', // Change any headers that contain font tags (other than font face tags) into p elements
- '//font' // Remove any font tags
- );
-
- foreach($remove_rules as $rule => $parenttag) {
- if(is_numeric($rule)) {
- $rule = $parenttag;
- $parenttag = null;
- }
-
- $nodes = array();
- foreach($xpath->query($rule) as $node) $nodes[] = $node;
-
- foreach($nodes as $node) {
- $parent = $node->parentNode;
-
- if($parenttag) {
- $parent = $doc->createElement($parenttag);
- $node->nextSibling ? $node->parentNode->insertBefore($parent, $node->nextSibling) : $node->parentNode->appendChild($parent);
- }
-
- while($node->firstChild) $parent->appendChild($node->firstChild);
- $node->parentNode->removeChild($node);
- }
- }
-
- // Strip style, class, lang attributes.
- $els = $doc->getElementsByTagName('*');
- for ($i = 0; $i < $els->length; $i++) {
- $el = $els->item($i);
- $el->removeAttribute('class');
- $el->removeAttribute('style');
- $el->removeAttribute('lang');
- }
-
- $els = $doc->getElementsByTagName('*');
-
- // Remove a bunch of unwanted elements
- $clean = array(
- '//p[not(descendant-or-self::text() | descendant-or-self::img)]', // Empty paragraphs
- '//*[self::h1 | self::h2 | self::h3 | self::h4 | self::h5 | self::h6][not(descendant-or-self::text() | descendant-or-self::img)]', // Empty headers
- '//a[not(@href)]', // Anchors
- '//br' // BR tags
- );
-
- foreach($clean as $query) {
- // First get all the nodes. Need to build array, as they'll disappear from the nodelist while we're deleteing them, causing the indexing
- // to screw up.
- $nodes = array();
- foreach($xpath->query($query) as $node) $nodes[] = $node;
-
- // Then remove them all
- foreach ($nodes as $node) { if ($node->parentNode) $node->parentNode->removeChild($node); }
- }
-
- // Now split the document into portions by H1
- $body = $doc->getElementsByTagName('body')->item(0);
-
- $this->unusedChildren = array();
- foreach($sourcePage->Children() as $child) {
- $this->unusedChildren[$child->ID] = $child;
- }
-
- $documentImporterFieldError;
-
- $documentImporterFieldErrorHandler = function ($errno, $errstr, $errfile, $errline) use ( $documentImporterFieldError ) {
- $documentImporterFieldError = _t(
- 'SilverStripe\\DocumentConverter\\DocumentConverter.PROCESSFAILED',
- 'Could not process document, please double-check you uploaded a .doc or .docx format.',
- 'Document Converter processes Word documents into HTML.'
- );
-
- // Do not cascade the error through other handlers
- return true;
- };
-
- set_error_handler($documentImporterFieldErrorHandler);
-
- $subtitle = null;
- $subdoc = new DOMDocument();
- $subnode = $subdoc->createElement('body');
- $node = $body->firstChild;
- $sort = 1;
- if($splitHeader == 1 || $splitHeader == 2) {
- while($node && !$documentImporterFieldError) {
- if($node instanceof DOMElement && $node->tagName == 'h' . $splitHeader) {
- if($subnode->hasChildNodes()) {
- $this->writeContent($subtitle, $subdoc, $subnode, $sort, $publishPages);
- $sort++;
- }
-
- $subdoc = new DOMDocument();
- $subnode = $subdoc->createElement('body');
- $subtitle = trim(preg_replace('/\n|\r/', '', Convert::html2raw($node->textContent)));
- } else {
- $subnode->appendChild($subdoc->importNode($node, true));
- }
-
- $node = $node->nextSibling;
- }
- } else {
- $this->writeContent($subtitle, $subdoc, $body, null, $publishPages);
- }
-
- if($subnode->hasChildNodes() && !$documentImporterFieldError) {
- $this->writeContent($subtitle, $subdoc, $subnode, null, $publishPages);
- }
-
- restore_error_handler();
- if ($documentImporterFieldError) {
- return array('error' => $documentImporterFieldError);
- }
-
- foreach($this->unusedChildren as $child) {
- $origStage = Versioned::current_stage();
-
- Versioned::reading_stage('Stage');
- $clone = clone $child;
- $clone->delete();
-
- Versioned::reading_stage('Live');
- $clone = clone $child;
- $clone->delete();
-
- Versioned::reading_stage($origStage);
- }
-
- $sourcePage->write();
- }
-}
diff --git a/src/ImportField.php b/src/ImportField.php
new file mode 100644
index 0000000..2620750
--- /dev/null
+++ b/src/ImportField.php
@@ -0,0 +1,504 @@
+isDisabled() || $this->isReadonly()) {
+ return $this->httpError(403);
+ }
+
+ // Protect against CSRF on destructive action
+ $token = $this->getForm()->getSecurityToken();
+ if (!$token->checkRequest($request)) {
+ return $this->httpError(400);
+ }
+
+ $tmpfile = $request->postVar('Upload');
+
+ // Check if the file has been uploaded into the temporary storage.
+ if (!$tmpfile) {
+ $return = [
+ 'error' => _t(
+ 'SilverStripe\\AssetAdmin\\Forms\\UploadField.FIELDNOTSET',
+ 'File information not found'
+ )
+ ];
+ } else {
+ $return = [
+ 'name' => $tmpfile['name'],
+ 'size' => $tmpfile['size'],
+ 'type' => $tmpfile['type'],
+ 'error' => $tmpfile['error']
+ ];
+ }
+
+ if (!$return['error']) {
+ // Get options for this import.
+ $splitHeader = (int)$request->postVar('SplitHeader');
+ $keepSource = (bool)$request->postVar('KeepSource');
+ $chosenFolderID = (int)$request->postVar('ChosenFolderID');
+ $publishPages = (bool)$request->postVar('PublishPages');
+ $includeTOC = (bool)$request->postVar('IncludeTOC');
+
+ // Process the document and write the page.
+ $preservedDocument = null;
+ if ($keepSource) {
+ $preservedDocument = $this->preserveSourceDocument($tmpfile, $chosenFolderID);
+ }
+
+ $importResult = $this->importFromPOST($tmpfile, $splitHeader, $publishPages, $chosenFolderID);
+ if (is_array($importResult) && isset($importResult['error'])) {
+ $return['error'] = $importResult['error'];
+ } elseif ($includeTOC) {
+ $this->writeTOC($publishPages, $keepSource ? $preservedDocument : null);
+ }
+ }
+
+ $response = HTTPResponse::create(Convert::raw2json([$return]));
+ $response->addHeader('Content-Type', 'application/json');
+ return $response;
+ }
+
+ /**
+ * Preserves the source file by copying it to a specified folder.
+ *
+ * @param $tmpfile Temporary file data structure.
+ * @param int $chosenFolderID Target folder.
+ * @return File Stored file.
+ */
+ protected function preserveSourceDocument($tmpfile, $chosenFolderID = null)
+ {
+ $upload = Upload::create();
+
+ $file = File::create();
+ $upload->loadIntoFile($tmpfile, $file, $chosenFolderID);
+
+ $page = $this->form->getRecord();
+ $page->ImportedFromFileID = $file->ID;
+ $page->write();
+
+ return $file;
+ }
+
+ /**
+ * Builds and writes the table of contents for the document.
+ *
+ * @param bool $publishPage Should the parent page be published.
+ * @param File $preservedDocument Set if the link to the original document should be added.
+ */
+ protected function writeTOC($publishPages = false, $preservedDocument = null)
+ {
+ $page = $this->form->getRecord();
+ $content = '';
+
+ if ($page) {
+ if ($page->Children()->Count() > 0) {
+ foreach ($page->Children() as $child) {
+ $content .= '- ' . $child->Title . '
';
+ }
+ $page->Content = $content . '
';
+ } else {
+ $doc = new DOMDocument();
+ $doc->loadHTML($page->Content);
+ $body = $doc->getElementsByTagName('body')->item(0);
+ $node = $body->firstChild;
+ $h1 = $h2 = 1;
+ while ($node) {
+ if ($node instanceof DOMElement && $node->tagName == 'h1') {
+ $content .= '' .
+ trim(preg_replace('/\n|\r/', '', Convert::html2raw($node->textContent))) .
+ '';
+ $node->setAttributeNode(new DOMAttr("id", "h1.".$h1));
+ $h1++;
+ } elseif ($node instanceof DOMElement && $node->tagName == 'h2') {
+ $content .= '';
+ $node->setAttributeNode(new DOMAttr("id", "h2.".$h2));
+ $h2++;
+ }
+ $node = $node->nextSibling;
+ }
+ $page->Content = $content . '' . $doc->saveHTML();
+ }
+
+ // Add in the link to the original document, if provided.
+ if ($preservedDocument) {
+ $page->Content = 'download original document (' .
+ $preservedDocument->getSize() .
+ ')' .
+ $page->Content;
+ }
+
+ // Store the result
+ $page->write();
+ if ($publishPages) {
+ $page->doPublish();
+ }
+ }
+ }
+
+ protected function getBodyText($doc, $node)
+ {
+ // Build a new doc
+ $htmldoc = new DOMDocument();
+ // Create the html element
+ $html = $htmldoc->createElement('html');
+ $htmldoc->appendChild($html);
+ // Append the body node
+ $html->appendChild($htmldoc->importNode($node, true));
+
+ // Get the text as html, remove the entry and exit root tags and return
+ $text = $htmldoc->saveHTML();
+ $text = preg_replace('/^.*/', '', $text);
+ $text = preg_replace('/<\/body>.*$/', '', $text);
+
+ return $text;
+ }
+
+ /**
+ * Used only when writing the document that has been split by headers.
+ * Can write both to the chapter pages as well as the master page.
+ *
+ * @param string $subtitle Title of the chapter - if missing, it will write to the master page.
+ * @param $subdoc
+ * @param $subnode
+ * @param int $sort Order of the chapter page.
+ * @param $publishPages Whether to publish the resulting child/master pages.
+ */
+ protected function writeContent($subtitle, $subdoc, $subnode, $sort = null, $publishPages = false)
+ {
+ $record = $this->form->getRecord();
+
+ if ($subtitle) {
+ // Write the chapter page to a subpage.
+ $page = DataObject::get_one(
+ 'Page',
+ sprintf('"Title" = \'%s\' AND "ParentID" = %d', $subtitle, $record->ID)
+ );
+ if (!$page) {
+ $page = Page::create();
+ $page->ParentID = $record->ID;
+ $page->Title = $subtitle;
+ }
+
+ unset($this->unusedChildren[$page->ID]);
+ file_put_contents(ASSETS_PATH . '/index-' . $sort . '.html', $this->getBodyText($subdoc, $subnode));
+
+ if ($sort) {
+ $page->Sort = $sort;
+ }
+ $page->Content = $this->getBodyText($subdoc, $subnode);
+ $page->write();
+ if ($publishPages) {
+ $page->doPublish();
+ }
+ } else {
+ // Write to the master page.
+ $record->Content = $this->getBodyText($subdoc, $subnode);
+ $record->write();
+
+ if ($publishPages) {
+ $record->doPublish();
+ }
+ }
+ }
+
+ /**
+ * Imports a document at a certain path onto the current page and writes it.
+ * CAUTION: Overwrites any existing content on the page!
+ *
+ * @param array $tmpFile Array as received from PHP's POST upload.
+ * @param bool $splitHeader Heading level to split by.
+ * @param bool $publishPages Whether the underlying pages should be published after import.
+ * @param int $chosenFolderID ID of the working folder - here the converted file and images will be stored.
+ */
+ public function importFromPOST($tmpFile, $splitHeader = false, $publishPages = false, $chosenFolderID = null)
+ {
+
+ $fileDescriptor = [
+ 'name' => $tmpFile['name'],
+ 'path' => $tmpFile['tmp_name'],
+ 'mimeType' => $tmpFile['type']
+ ];
+
+ $sourcePage = $this->form->getRecord();
+ $importerClass = $this->config()->get('importer_class');
+ $importer = Injector::inst()->create($importerClass, $fileDescriptor, $chosenFolderID);
+ $content = $importer->import();
+
+ if (is_array($content) && isset($content['error'])) {
+ return $content;
+ }
+
+ // Clean up with tidy (requires tidy module)
+ $tidy = new Tidy();
+ $tidy->parseString($content, ['output-xhtml' => true], 'utf8');
+ $tidy->cleanRepair();
+
+ $fragment = [];
+ foreach ($tidy->body()->child as $child) {
+ $fragment[] = $child->value;
+ }
+
+ $htmlValue = Injector::inst()->create(HTMLValue::class, implode("\n", $fragment));
+
+ // Sanitise
+ $santiser = Injector::inst()->create(HTMLEditorSanitiser::class, HTMLEditorConfig::get_active());
+ $santiser->sanitise($htmlValue);
+
+ // Load in the HTML
+ $doc = $htmlValue->getDocument();
+ $xpath = new DOMXPath($doc);
+
+ // make sure any images are added as Image records with a relative link to assets
+ $chosenFolder = ($this->chosenFolderID) ? DataObject::get_by_id(Folder::class, $this->chosenFolderID) : null;
+ $folderName = ($chosenFolder) ? '/' . $chosenFolder->Name : '';
+ $imgs = $xpath->query('//img');
+ for ($i = 0; $i < $imgs->length; $i++) {
+ $img = $imgs->item($i);
+ $originalPath = 'assets/' . $folderName . '/' . $img->getAttribute('src');
+ $name = FileNameFilter::create()->filter(basename($originalPath));
+
+ $image = Image::get()->filter([
+ 'Name' => $name,
+ 'ParentID' => (int)$chosenFolderID
+ ])->first();
+ if (!($image && $image->exists())) {
+ $image = Image::create();
+ $image->ParentID = (int)$chosenFolderID;
+ $image->Name = $name;
+ $image->write();
+ }
+
+ // make sure it's put in place correctly so Image record knows where it is.
+ // e.g. in the case of underscores being renamed to dashes.
+ @rename(Director::getAbsFile($originalPath), Director::getAbsFile($image->getFilename()));
+
+ $img->setAttribute('src', $image->getFilename());
+ }
+
+ $remove_rules = [
+ // Change any headers that contain font tags (other than font face tags) into p elements
+ '//h1[.//font[not(@face)]]' => 'p',
+ // Remove any font tags
+ '//font'
+ ];
+
+ foreach ($remove_rules as $rule => $parenttag) {
+ if (is_numeric($rule)) {
+ $rule = $parenttag;
+ $parenttag = null;
+ }
+
+ $nodes = [];
+ foreach ($xpath->query($rule) as $node) {
+ $nodes[] = $node;
+ }
+
+ foreach ($nodes as $node) {
+ $parent = $node->parentNode;
+
+ if ($parenttag) {
+ $parent = $doc->createElement($parenttag);
+ $node->nextSibling ?
+ $node->parentNode->insertBefore($parent, $node->nextSibling) :
+ $node->parentNode->appendChild($parent);
+ }
+
+ while ($node->firstChild) {
+ $parent->appendChild($node->firstChild);
+ }
+ $node->parentNode->removeChild($node);
+ }
+ }
+
+ // Strip style, class, lang attributes.
+ $els = $doc->getElementsByTagName('*');
+ for ($i = 0; $i < $els->length; $i++) {
+ $el = $els->item($i);
+ $el->removeAttribute('class');
+ $el->removeAttribute('style');
+ $el->removeAttribute('lang');
+ }
+
+ $els = $doc->getElementsByTagName('*');
+
+ $headingXPath = [
+ 'self::h1',
+ 'self::h2',
+ 'self::h3',
+ 'self::h4',
+ 'self::h5',
+ 'self::h6',
+ ];
+ // Remove a bunch of unwanted elements
+ $clean = [
+ // Empty paragraphs
+ '//p[not(descendant-or-self::text() | descendant-or-self::img)]',
+ // Empty headers
+ '//*[' . implode(' | ', $headingXPath) . '][not(descendant-or-self::text() | descendant-or-self::img)]',
+ // Anchors
+ '//a[not(@href)]',
+ // BR tags
+ '//br'
+ ];
+
+ foreach ($clean as $query) {
+ // First get all the nodes. Need to build array, as they'll disappear from the
+ // nodelist while we're deleteing them, causing the indexing to screw up.
+ $nodes = [];
+ foreach ($xpath->query($query) as $node) {
+ $nodes[] = $node;
+ }
+
+ // Then remove them all
+ foreach ($nodes as $node) {
+ if ($node->parentNode) {
+ $node->parentNode->removeChild($node);
+ }
+ }
+ }
+
+ // Now split the document into portions by H1
+ $body = $doc->getElementsByTagName('body')->item(0);
+
+ $this->unusedChildren = [];
+ foreach ($sourcePage->Children() as $child) {
+ $this->unusedChildren[$child->ID] = $child;
+ }
+
+ $documentImporterFieldError = false;
+
+ $documentImporterFieldErrorHandler = function (
+ $errno,
+ $errstr,
+ $errfile,
+ $errline
+ ) use ($documentImporterFieldError) {
+ $documentImporterFieldError = _t(
+ 'SilverStripe\\DocumentConverter\\ServiceConnector.PROCESSFAILED',
+ 'Could not process document, please double-check you uploaded a .doc or .docx format.',
+ 'Document Converter processes Word documents into HTML.'
+ );
+
+ // Do not cascade the error through other handlers
+ return true;
+ };
+
+ set_error_handler($documentImporterFieldErrorHandler);
+
+ $subtitle = null;
+ $subdoc = new DOMDocument();
+ $subnode = $subdoc->createElement('body');
+ $node = $body->firstChild;
+ $sort = 1;
+ if ($splitHeader == 1 || $splitHeader == 2) {
+ while ($node && !$documentImporterFieldError) {
+ if ($node instanceof DOMElement && $node->tagName == 'h' . $splitHeader) {
+ if ($subnode->hasChildNodes()) {
+ $this->writeContent($subtitle, $subdoc, $subnode, $sort, $publishPages);
+ $sort++;
+ }
+
+ $subdoc = new DOMDocument();
+ $subnode = $subdoc->createElement('body');
+ $subtitle = trim(preg_replace('/\n|\r/', '', Convert::html2raw($node->textContent)));
+ } else {
+ $subnode->appendChild($subdoc->importNode($node, true));
+ }
+
+ $node = $node->nextSibling;
+ }
+ } else {
+ $this->writeContent($subtitle, $subdoc, $body, null, $publishPages);
+ }
+
+ if ($subnode->hasChildNodes() && !$documentImporterFieldError) {
+ $this->writeContent($subtitle, $subdoc, $subnode, null, $publishPages);
+ }
+
+ restore_error_handler();
+ if ($documentImporterFieldError) {
+ return ['error' => $documentImporterFieldError];
+ }
+
+ foreach ($this->unusedChildren as $child) {
+ $origStage = Versioned::current_stage();
+
+ Versioned::reading_stage('Stage');
+ $draft = clone $child;
+ $draft->delete();
+
+ Versioned::reading_stage('Live');
+ $published = clone $child;
+ $published->delete();
+
+ Versioned::reading_stage($origStage);
+ }
+
+ $sourcePage->write();
+ }
+}
diff --git a/src/PageExtension.php b/src/PageExtension.php
new file mode 100644
index 0000000..1e780d4
--- /dev/null
+++ b/src/PageExtension.php
@@ -0,0 +1,33 @@
+ File::class
+ ];
+
+ public function updateCMSFields(FieldList $fields)
+ {
+ /*
+ // Currently the ToggleCompositeField plays badly with TreeDropdownField formatting.
+ // Could be switched back in the future, if this is fixed.
+ $fields->addFieldToTab('Root.Main',
+ ToggleCompositeField::create('Import', 'Import', [
+ SettingsField::create()
+ ])->setHeadingLevel(4)
+ );
+ */
+ $fields->findOrMakeTab(
+ 'Root.Import',
+ _t(__CLASS__ . '.ImportTab', 'Import')
+ );
+ $fields->addFieldToTab('Root.Import', SettingsField::create());
+ }
+}
diff --git a/src/ServiceConnector.php b/src/ServiceConnector.php
new file mode 100644
index 0000000..e1aa102
--- /dev/null
+++ b/src/ServiceConnector.php
@@ -0,0 +1,207 @@
+ null,
+ 'password' => null,
+ 'url' => null
+ ];
+
+ public function __construct($fileDescriptor, $chosenFolderID = null)
+ {
+ $this->fileDescriptor = $fileDescriptor;
+ $this->chosenFolderID = $chosenFolderID;
+ }
+
+ public function setUsername($username = null)
+ {
+ $this->docvertDetails['username'] = $username;
+ }
+
+ public function getUsername()
+ {
+ $username = $this->docvertDetails['username'];
+ if ($username) {
+ return $username;
+ }
+ $username = $this->config()->get('username');
+ if ($username) {
+ return $username;
+ }
+ $username = Environment::getEnv('DOCVERT_USERNAME');
+ if ($username) {
+ return $username;
+ }
+ return null;
+ }
+
+ public function setPassword($password = null)
+ {
+ $this->docvertDetails['password'] = $password;
+ }
+
+ public function getPassword()
+ {
+ $username = $this->docvertDetails['password'];
+ if ($username) {
+ return $username;
+ }
+ $username = $this->config()->get('password');
+ if ($username) {
+ return $username;
+ }
+ $username = Environment::getEnv('DOCVERT_PASSWORD');
+ if ($username) {
+ return $username;
+ }
+ return null;
+ }
+
+ public function setUrl($url = null)
+ {
+ $this->docvertDetails['url'] = $url;
+ }
+
+ public function getUrl()
+ {
+ $username = $this->docvertDetails['url'];
+ if ($username) {
+ return $username;
+ }
+ $username = $this->config()->get('url');
+ if ($username) {
+ return $username;
+ }
+ $username = Environment::getEnv('DOCVERT_URL');
+ if ($username) {
+ return $username;
+ }
+ return null;
+ }
+
+ public function import()
+ {
+ $ch = curl_init();
+
+ // PHP 5.5+ introduced CURLFile which makes the '@/path/to/file' syntax deprecated.
+ if (class_exists('CURLFile')) {
+ $file = new CURLFile(
+ $this->fileDescriptor['path'],
+ $this->fileDescriptor['mimeType'],
+ $this->fileDescriptor['name']
+ );
+ } else {
+ $file = '@' . $this->fileDescriptor['path'];
+ }
+
+ curl_setopt_array($ch, [
+ CURLOPT_URL => $this->getUrl(),
+ CURLOPT_USERPWD => sprintf('%s:%s', $this->getUsername(), $this->getPassword()),
+ CURLOPT_POST => 1,
+ CURLOPT_POSTFIELDS => ['file' => $file],
+ CURLOPT_CONNECTTIMEOUT => 25,
+ CURLOPT_TIMEOUT => 100,
+ ]);
+
+ $chosenFolder = ($this->chosenFolderID) ? DataObject::get_by_id(Folder::class, $this->chosenFolderID) : null;
+ $folderName = ($chosenFolder) ? '/' . $chosenFolder->Name : '';
+ $outname = tempnam(ASSETS_PATH, 'convert');
+ $outzip = $outname . '.zip';
+ $out = fopen($outzip, 'w');
+ curl_setopt($ch, CURLOPT_FILE, $out);
+ $returnValue = curl_exec($ch);
+ $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+ curl_close($ch);
+ fclose($out);
+ chmod($outzip, 0666);
+
+ if (!$returnValue || ($status != 200)) {
+ return ['error' => _t(
+ __CLASS__ . '.SERVERUNREACHABLE',
+ 'Could not contact document conversion server. Please try again later ' .
+ 'or contact your system administrator.',
+ 'Document Converter process Word documents into HTML.'
+ )];
+ }
+
+ // extract the converted document into assets
+ // you need php zip, e.g. apt-get install php-zip
+ $zip = new ZipArchive();
+
+ if ($zip->open($outzip)) {
+ $zip->extractTo(ASSETS_PATH .$folderName);
+ $zip->close();
+ }
+
+ // remove temporary files
+ unlink($outname);
+ unlink($outzip);
+
+ if (!file_exists(ASSETS_PATH . $folderName . '/index.html')) {
+ return ['error' => _t(
+ __CLASS__ . '.PROCESSFAILED',
+ 'Could not process document, please double-check you uploaded a .doc or .docx format.',
+ 'Document Converter processes Word documents into HTML.'
+ )];
+ }
+
+ $content = file_get_contents(ASSETS_PATH . $folderName . '/index.html');
+
+ unlink(ASSETS_PATH . $folderName . '/index.html');
+
+ return $content;
+ }
+}
diff --git a/src/SettingsField.php b/src/SettingsField.php
new file mode 100644
index 0000000..9405798
--- /dev/null
+++ b/src/SettingsField.php
@@ -0,0 +1,108 @@
+ _t(__CLASS__ . '.No', 'no'),
+ 1 => _t(__CLASS__ . '.EachH1', 'for each heading 1'),
+ 2 => _t(__CLASS__ . '.EachH2', 'for each heading 2')
+ ]
+ ),
+ $keepSource = CheckboxField::create(
+ 'DocumentConversionSettings-KeepSource',
+ _t(
+ __CLASS__ . '.KeepSource',
+ 'Keep the original document. Adds a link to it on TOC, if enabled.'
+ )
+ ),
+ $chosenFolderID = TreeDropdownField::create(
+ 'DocumentConversionSettings-ChosenFolderID',
+ _t(__CLASS__ . '.ChooseFolder', 'Choose a folder to save this file'),
+ Folder::class
+ ),
+ $includeTOC = CheckboxField::create(
+ 'DocumentConversionSettings-IncludeTOC',
+ _t(__CLASS__ . '.IncludeTOC', 'Replace this page with a Table of Contents.')
+ ),
+ $publishPages = CheckboxField::create(
+ 'DocumentConversionSettings-PublishPages',
+ _t(
+ __CLASS__ . '.publishPages',
+ 'Publish modified pages (not recommended unless you are sure about the conversion outcome)'
+ )
+ ),
+ $this->innerField = ImportField::create(
+ 'ImportedFromFile',
+ _t(__CLASS__ . '.ImportedFromFile', 'Import content from a word document')
+ )
+ ]);
+
+ // Prevent the warning popup that appears when navigating away from the page.
+ $splitHeader->addExtraClass('no-change-track');
+ $keepSource->addExtraClass('no-change-track');
+ $chosenFolderID->addExtraClass('no-change-track');
+ $includeTOC->addExtraClass('no-change-track');
+ $publishPages->addExtraClass('no-change-track');
+
+ return parent::__construct($fields);
+ }
+
+ public function getInnerField()
+ {
+ return $this->innerField;
+ }
+}
diff --git a/tests/DocumentConverterDecoratorTest.php b/tests/DocumentConverterDecoratorTest.php
deleted file mode 100644
index 37d0828..0000000
--- a/tests/DocumentConverterDecoratorTest.php
+++ /dev/null
@@ -1,19 +0,0 @@
- array(
- 'DocumentConverterDecorator',
- ),
- );
-
- public function testFieldListHasDocumentImportField()
- {
- $fields = (new SiteTree)->getCMSFields();
- $this->assertInstanceOf(
- 'DocumentImportField',
- $fields->fieldByName('Root.Import')->Fields()->First()
- );
- }
-}
diff --git a/tests/DocumentConverterTest.php b/tests/DocumentConverterTest.php
deleted file mode 100644
index dc422fe..0000000
--- a/tests/DocumentConverterTest.php
+++ /dev/null
@@ -1,17 +0,0 @@
-markTestIncomplete();
- }
-}
\ No newline at end of file
diff --git a/tests/DocumentImportFieldTest.php b/tests/DocumentImportFieldTest.php
deleted file mode 100644
index 0564a4d..0000000
--- a/tests/DocumentImportFieldTest.php
+++ /dev/null
@@ -1,51 +0,0 @@
-clear();
-
- new DocumentImportField();
- $javascript = Requirements::backend()->get_javascript();
- $this->assertNotEmpty($javascript);
- }
-
- public function testFieldListGeneration()
- {
- $importField = new DocumentImportField();
-
- $fields = $importField->getChildren();
- $this->assertInstanceOf('FieldList', $fields);
-
- // We don't need to check that all of the fields are there, but just check a couple
- $this->assertInstanceOf('HeaderField', $fields->fieldByName('FileWarningHeader'));
- $innerField = $fields->fieldByName('ImportedFromFile');
- $this->assertInstanceOf('DocumentImportInnerField', $innerField);
-
- // Check the getter works
- $this->assertSame($innerField, $importField->getInnerField());
-
- // Check the fields have been given has the change tracker disabled
- $splitHeader = $fields->fieldByName('DocumentImportField-SplitHeader');
- $this->assertInstanceOf('DropdownField', $splitHeader);
- $this->assertContains('no-change-track', $splitHeader->extraClass());
- }
-}
diff --git a/tests/PageExtensionTest.php b/tests/PageExtensionTest.php
new file mode 100644
index 0000000..bac5c60
--- /dev/null
+++ b/tests/PageExtensionTest.php
@@ -0,0 +1,25 @@
+ [PageExtension::class]
+ ];
+
+ public function testFieldListHasDocumentImportField()
+ {
+ $siteTree = new SiteTree;
+ $fields = $siteTree->getCMSFields();
+ $this->assertInstanceOf(
+ SettingsField::class,
+ $fields->fieldByName('Root.Import')->Fields()->First()
+ );
+ }
+}
diff --git a/tests/SettingsFieldTest.php b/tests/SettingsFieldTest.php
new file mode 100644
index 0000000..1498af2
--- /dev/null
+++ b/tests/SettingsFieldTest.php
@@ -0,0 +1,74 @@
+clear();
+
+ new SettingsField();
+ $javascript = Requirements::backend()->getJavascript();
+ $this->assertNotEmpty($javascript);
+ }
+
+ public function testFieldListGeneration()
+ {
+ $importField = new SettingsField();
+
+ $fields = $importField->getChildren();
+ $this->assertInstanceOf(FieldList::class, $fields);
+
+ // We don't need to check that all of the fields are there, but just check a couple
+ $this->assertInstanceOf(HeaderField::class, $fields->fieldByName('FileWarningHeader'));
+ $innerField = $fields->fieldByName('ImportedFromFile');
+ $this->assertInstanceOf(ImportField::class, $innerField);
+
+ // Check the getter works
+ $this->assertSame($innerField, $importField->getInnerField());
+
+ // Check the fields have been given has the change tracker disabled
+ $settingsFields = [
+ 'SplitHeader' => DropdownField::class,
+ 'KeepSource' => CheckboxField::class,
+ 'ChosenFolderID' => TreeDropdownField::class,
+ 'IncludeTOC' => CheckboxField::class,
+ 'PublishPages' => CheckboxField::class
+ ];
+ foreach ($settingsFields as $fieldName => $className) {
+ $field = $fields->fieldByName(
+ 'DocumentConversionSettings-' . $fieldName
+ );
+ $this->assertInstanceOf($className, $field);
+ $this->assertContains('no-change-track', $field->extraClass());
+ }
+ }
+}
diff --git a/tests/Stubs/MockDocvertService.php b/tests/Stubs/MockDocvertService.php
new file mode 100644
index 0000000..cc3540a
--- /dev/null
+++ b/tests/Stubs/MockDocvertService.php
@@ -0,0 +1,14 @@
+Fake documentFor testing purposes.
';
+ }
+}
diff --git a/tests/Stubs/TestPage.php b/tests/Stubs/TestPage.php
new file mode 100644
index 0000000..0b73610
--- /dev/null
+++ b/tests/Stubs/TestPage.php
@@ -0,0 +1,15 @@
+ 'Default TestPage
With pre-import content.
'
+ ];
+}