]> 91.132.146.200 Git - insipid.git/commitdiff
importing the exported xml file with validation and stuff. Not complete yet
authorBanana <mail@bananas-playground.net>
Mon, 10 Feb 2020 16:58:46 +0000 (17:58 +0100)
committerBanana <mail@bananas-playground.net>
Mon, 10 Feb 2020 16:58:46 +0000 (17:58 +0100)
ChangeLog
TODO
documentation/requirements.txt
webroot/lib/import-export.class.php
webroot/lib/link.class.php
webroot/lib/management.class.php
webroot/lib/summoner.class.php
webroot/lib/xmlimport.xsd [new file with mode: 0644]
webroot/view/stats.inc.php
webroot/view/stats.php

index cc014f35e77f9a59be1cd3eff7d45a2910a29f15..421a4ea749903a891c2606aeba9b5ffce41c81a2 100755 (executable)
--- a/ChangeLog
+++ b/ChangeLog
@@ -5,6 +5,8 @@ version x.x - Seven Portals (tba)
     + #2 Protection if the email-import.php file if it needs to be
         in a web accessible folder
     + Fixed the search for words. See update instructions how to correct your data
+    + Removed JS and replaced it with plain old working JS
+    + Dropped IE support. Edge still working.
 
 version 2.3 - Guardian of Steel (2019-12-30)
 
diff --git a/TODO b/TODO
index 2fcf6c42cc41376d9d15bdd2e9cae94886b18d35..682152c86c80d00430b72c3923be67b1773bb7ce 100755 (executable)
--- a/TODO
+++ b/TODO
@@ -2,6 +2,8 @@ TODO / Feature list
 + Export and import of your data
 + snapshots
 + bookmark js snippet
++ translation support
++ stats cleanup. Management functions should be standalone
 + theme support
 + more "secure" user authentication
 ++ multiple user accounts and stuff
index 1b08bc87990689d8efd41f890c4707258e714268..d8702adcd1780b7c24bb0f864d8d616f403f52bb 100644 (file)
@@ -1,4 +1,12 @@
 Apache (2.4 and up) with PHP extension enabled
-PHP (7 and up) with MySQL extension -> mysql & mysqli; curl; pdo; (+imap +ssl if you us the email importer)
+PHP (7 and up)
+- mysql & mysqli
+- curl
+- pdo
+- imap +ssl if you us the email importer
+- xmlread
+- xmlwriter
 MySQL server or access to database 5.6.x and up
 - DB user rights has to include create, alter a view
+
+Latest browser for accessing the client. IE (not Edge) is not supported anymore.
\ No newline at end of file
index 848d684ac37ca9270d7d0528a85ec6c5e24b1d15..99bf51b834156153911ff993338186975a3b6024 100644 (file)
  */
 class ImportExport {
 
+       /**
+        * @var String The current memory xmlwriter
+        */
        private $_currentXW;
 
+       private $_xmlImportXSD = 'lib/xmlimport.xsd';
+
+
+       /**
+        * @var
+        */
+       private $_uploadedData;
+
        public function __construct() {
        }
 
@@ -49,6 +60,7 @@ class ImportExport {
                xmlwriter_set_indent($this->_currentXW, 1);
                xmlwriter_set_indent_string($this->_currentXW, ' ');
                xmlwriter_start_document($this->_currentXW, '1.0', 'UTF-8');
+               xmlwriter_start_element($this->_currentXW, 'root');
 
                xmlwriter_start_element($this->_currentXW, 'insipidlink');
 
@@ -80,6 +92,12 @@ class ImportExport {
                xmlwriter_end_cdata($this->_currentXW);
                xmlwriter_end_element($this->_currentXW);
 
+               xmlwriter_start_element($this->_currentXW, 'image');
+               xmlwriter_start_cdata($this->_currentXW);
+               xmlwriter_text($this->_currentXW, $data['image']);
+               xmlwriter_end_cdata($this->_currentXW);
+               xmlwriter_end_element($this->_currentXW);
+
                if(!empty($data['tags'])) {
                        xmlwriter_start_element($this->_currentXW, 'tags');
                        foreach($data['tags'] as $k=>$v) {
@@ -114,13 +132,113 @@ class ImportExport {
                xmlwriter_end_cdata($this->_currentXW);
                xmlwriter_end_element($this->_currentXW);
 
+               xmlwriter_start_element($this->_currentXW, 'status');
+               xmlwriter_start_cdata($this->_currentXW);
+               xmlwriter_text($this->_currentXW, $data['status']);
+               xmlwriter_end_cdata($this->_currentXW);
+               xmlwriter_end_element($this->_currentXW);
+
 
                xmlwriter_end_element($this->_currentXW); // insipidlink
+
+               xmlwriter_end_element($this->_currentXW); // root
                xmlwriter_end_document($this->_currentXW); // document
 
                return xmlwriter_output_memory($this->_currentXW);
        }
 
+       /**
+        * @param $file array $_FILES array. Just check if everything is there
+        * and put it into _uploadedData
+        * @throws Exception
+        */
+       public function loadImportFile($file) {
+
+               if(!isset($file['name'])
+                       || !isset($file['type'])
+                       || !isset($file['size'])
+                       || !isset($file['tmp_name'])
+                       || !isset($file['error'])
+               ) {
+                       throw new Exception('Invalid Upload');
+               }
+
+               $workWith = $file['tmp_name'];
+               if(!empty($workWith)) {
+                       $finfo = finfo_open(FILEINFO_MIME_TYPE);
+                       $mime = finfo_file($finfo, $workWith);
+                       finfo_close($finfo);
+                       if($mime != 'text/xml') {
+                               throw new Exception('Invalid mime type');
+                       }
+               } else {
+                       throw new Exception('Invalid file upload information');
+               }
+
+               // now validate the xml file
+               $this->_uploadedData = file_get_contents($file['tmp_name']);
+
+               if(!empty($this->_uploadedData)) {
+                       $_valid = $this->_validateXMLImport();
+                       if($_valid !== true) {
+                               $this->_uploadedData = '';
+                               throw new Exception('Invalid xml format: '.$_valid);
+                       }
+               }
+               else {
+                       $this->_uploadedData = '';
+                       throw new Exception('Empty upload file?');
+               }
+       }
+
+       /**
+        * parse the data from _uploadedData and create an array we can use
+        * @return array
+        * @throws Exception
+        */
+       public function parseImportFile() {
+               $ret = array();
+
+               if(!empty($this->_uploadedData)) {
+                       $xml = simplexml_load_string($this->_uploadedData, "SimpleXMLElement", LIBXML_NOCDATA);
+                       if(!empty($xml->insipidlink)) {
+                               foreach($xml->insipidlink as $linkEntry) {
+                                       $_id = (string)$linkEntry->attributes()->id;
+                                       $ret[$_id]['id'] = $_id;
+                                       $ret[$_id]['link'] = (string)$linkEntry->link;
+                                       $ret[$_id]['description'] = (string)$linkEntry->description;
+                                       $ret[$_id]['title'] = (string)$linkEntry->title;
+                                       $ret[$_id]['hash'] = (string)$linkEntry->hash;
+                                       $ret[$_id]['created'] = (string)$linkEntry->created;
+                                       $ret[$_id]['updated'] = (string)$linkEntry->updated;
+                                       $ret[$_id]['private'] = (string)$linkEntry->status;
+                                       $ret[$_id]['image'] = (string)$linkEntry->image;
+
+                                       if($linkEntry->categories->count() > 0) {
+                                               $ret[$_id]['category'] = '';
+                                               foreach ($linkEntry->categories->category as $cat) {
+                                                       $_cname = (string)$cat;
+                                                       $ret[$_id]['category'] .= $_cname.",";
+                                               }
+                                       }
+
+                                       if($linkEntry->tags->count() > 0) {
+                                               $ret[$_id]['tag'] = '';
+                                               foreach ($linkEntry->tags->tag as $tag) {
+                                                       $_tname = (string)$tag;
+                                                       $ret[$_id]['tag'] .= $_tname.",";
+                                               }
+                                       }
+                               }
+                       }
+               }
+               else {
+                       throw new Exception('Empty xml data. LoadImportFile needs to be called first.');
+               }
+
+               return $ret;
+       }
+
        /**
         * Create a single xml element for the current loaded xmlwriter
         * @param String $name
@@ -143,4 +261,47 @@ class ImportExport {
                        xmlwriter_end_element($this->_currentXW);
                }
        }
+
+       /**
+        * validate an import of a export xml with the
+        * saved xsd file _xmlImportXSD
+        * @return bool|string
+        */
+       private function _validateXMLImport() {
+               $ret = false;
+               $xmlReader = new XMLReader();
+               $xmlReader->XML($this->_uploadedData);
+               if(!empty($xmlReader)) {
+                       $xmlReader->setSchema($this->_xmlImportXSD);
+                       libxml_use_internal_errors(true);
+                       while($xmlReader->read()) {
+                               if (!$xmlReader->isValid()) {
+                                       $ret = $this->_xmlErrors();
+                                       break;
+                               } else {
+                                       $ret = true;
+                                       break;
+                               }
+                       }
+               }
+
+               return $ret;
+       }
+
+       /**
+        * Reads libxml_get_errors and creates a simple string with all
+        * the info we need.
+        * @return string
+        */
+       private function _xmlErrors() {
+               $errors = libxml_get_errors();
+               $result = array();
+               foreach ($errors as $error) {
+                       $errorString = "Error $error->code in $error->file (Line:{$error->line}):";
+                       $errorString .= trim($error->message);
+                       $result[] = $errorString;
+               }
+               libxml_clear_errors();
+               return implode("\n",$result);
+       }
 }
index df4709a81668bde0a63f378764826c43f5b48958..e29c9089aeb7439289744f99e3c716d888bce52e 100644 (file)
@@ -175,7 +175,7 @@ class Link {
 
                $ret = false;
 
-               if (isset($data['title']) && !empty($data['title'])) {
+               if (isset($data['title']) && !empty($data['title']) && !empty($this->_data)) {
 
                        # categories and tag stuff
                        $catArr = Summoner::prepareTagOrCategoryStr($data['category']);
@@ -203,7 +203,6 @@ class Link {
                                                        `image` = '" . $this->DB->real_escape_string($data['image']) . "',
                                                        `search` = '" . $this->DB->real_escape_string($search) . "'
                                                  WHERE `hash` = '" . $this->DB->real_escape_string($this->_data['hash']) . "'";
-
                        $query = $this->DB->query($queryStr);
 
                        if ($query !== false) {
index 71a800820f233aa8a4de5f9c7cc7f4edba241feb..effd485aab4df07656892423a49aa0fe88480150 100644 (file)
@@ -718,6 +718,51 @@ class Management {
                return $ret;
        }
 
+
+       public function processImportFile($file, $options) {
+               $ret = array(
+                       'status' => 'error',
+                       'message' => 'Processing error'
+               );
+
+               $links = array();
+               require_once 'lib/import-export.class.php';
+               $ImEx = new ImportExport();
+               try {
+                       $ImEx->loadImportFile($file);
+                       $links = $ImEx->parseImportFile();
+               }
+               catch (Exception $e) {
+                       $ret['message'] = $e->getMessage();
+               }
+
+               $_existing = 0;
+               $_new = 0;
+               if(!empty($links)) {
+                       $_amount = count($links);
+                       foreach($links as $linkToImport) {
+                               if($this->_linkExistsById($linkToImport['id'])) {
+                                       $linkObj = new Link($this->DB);
+                                       $linkObj->load($linkToImport['hash']);
+                                       $do = $linkObj->update($linkToImport);
+                                       $_existing++;
+                               }
+                               else {
+                                       $_new++;
+                                       var_dump('new one');
+                               }
+                               //var_dump($linkToImport);
+                       }
+                       $ret = array(
+                               'status' => 'success',
+                               'message' => "Found $_amount link(s) to import. $_existing existing and $_new new one(s)."
+                       );
+
+               }
+
+               return $ret;
+       }
+
        /**
         * Return the query string for the correct status type
         * @return string
@@ -736,5 +781,21 @@ class Management {
                }
                return $ret;
        }
+
+       private function _linkExistsById($id) {
+               $ret = false;
+
+               if(!empty($id)) {
+                       $queryStr = "SELECT `id` 
+                                                       FROM `" . DB_PREFIX . "_link` 
+                                                       WHERE `id` = '" . $this->DB->real_escape_string($id) . "'";
+                       $query = $this->DB->query($queryStr);
+                       if(!empty($query) && $query->num_rows > 0) {
+                               $ret = true;
+                       }
+               }
+
+               return $ret;
+       }
 }
 
index c988a36320e71a881f03b1770d908196c18fd19c..ece2021c605d6c8ffde9e0057ff4e5facb65136b 100644 (file)
@@ -589,4 +589,48 @@ class Summoner {
 
         return $ret;
     }
+
+       /**
+        * Simple helper to detect the $_FILES upload status
+        * Expects the error value from $_FILES['error']
+        * @param $error
+        * @return array
+        */
+       static function checkFileUploadStatus($error) {
+               $message = "Unknown upload error";
+               $status = false;
+
+               switch ($error) {
+                       case UPLOAD_ERR_OK:
+                               $message = "There is no error, the file uploaded with success.";
+                               $status = true;
+                               break;
+                       case UPLOAD_ERR_INI_SIZE:
+                               $message = "The uploaded file exceeds the upload_max_filesize directive in php.ini";
+                               break;
+                       case UPLOAD_ERR_FORM_SIZE:
+                               $message = "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form";
+                               break;
+                       case UPLOAD_ERR_PARTIAL:
+                               $message = "The uploaded file was only partially uploaded";
+                               break;
+                       case UPLOAD_ERR_NO_FILE:
+                               $message = "No file was uploaded";
+                               break;
+                       case UPLOAD_ERR_NO_TMP_DIR:
+                               $message = "Missing a temporary folder";
+                               break;
+                       case UPLOAD_ERR_CANT_WRITE:
+                               $message = "Failed to write file to disk";
+                               break;
+                       case UPLOAD_ERR_EXTENSION:
+                               $message = "File upload stopped by extension";
+                               break;
+               }
+
+               return array(
+                       'message' => $message,
+                       'status' => $status
+               );
+       }
 }
diff --git a/webroot/lib/xmlimport.xsd b/webroot/lib/xmlimport.xsd
new file mode 100644 (file)
index 0000000..e8ffb60
--- /dev/null
@@ -0,0 +1,54 @@
+<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
+    <xs:element name="root">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element name="insipidlink" maxOccurs="unbounded" minOccurs="1">
+                    <xs:complexType>
+                        <xs:sequence>
+                            <xs:element type="xs:string" name="link"/>
+                            <xs:element type="xs:string" name="description"/>
+                            <xs:element type="xs:string" name="title"/>
+                            <xs:element type="xs:string" name="hash"/>
+                            <xs:element type="xs:string" name="image"/>
+                            <xs:element name="tags">
+                                <xs:complexType>
+                                    <xs:sequence>
+                                        <xs:element name="tag" maxOccurs="unbounded" minOccurs="0">
+                                            <xs:complexType>
+                                                <xs:simpleContent>
+                                                    <xs:extension base="xs:string">
+                                                        <xs:attribute type="xs:byte" name="id" use="optional"/>
+                                                    </xs:extension>
+                                                </xs:simpleContent>
+                                            </xs:complexType>
+                                        </xs:element>
+                                    </xs:sequence>
+                                </xs:complexType>
+                            </xs:element>
+                            <xs:element name="categories">
+                                <xs:complexType>
+                                    <xs:sequence>
+                                        <xs:element name="category" maxOccurs="unbounded" minOccurs="0">
+                                            <xs:complexType>
+                                                <xs:simpleContent>
+                                                    <xs:extension base="xs:string">
+                                                        <xs:attribute type="xs:byte" name="id" use="optional"/>
+                                                    </xs:extension>
+                                                </xs:simpleContent>
+                                            </xs:complexType>
+                                        </xs:element>
+                                    </xs:sequence>
+                                </xs:complexType>
+                            </xs:element>
+                            <xs:element type="xs:string" name="created"/>
+                            <xs:element type="xs:string" name="updated"/>
+                            <xs:element type="xs:string" name="exportcreated"/>
+                            <xs:element type="xs:string" name="status"/>
+                        </xs:sequence>
+                        <xs:attribute type="xs:byte" name="id" use="optional"/>
+                    </xs:complexType>
+                </xs:element>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+</xs:schema>
\ No newline at end of file
index 27c83045880b62436f28540845340b23af7d8faa..3c9f6cfcf572f3741d542c04d673b983341b474f 100644 (file)
@@ -78,6 +78,26 @@ if(isset($_POST['statsCreateDBBackup'])) {
     exit();
 }
 
+if(isset($_POST['statsImportXML'])) {
+       $_options = array();
+
+       if(isset($_FILES['importxmlfile']) && !empty($_FILES['importxmlfile'])) {
+               $do = $Management->processImportFile($_FILES['importxmlfile'], $_options);
+               if(isset($do['status']) && $do['status'] === 'success') {
+                       $submitFeedback['status'] = 'success';
+                       $submitFeedback['message'] = $do['message'];
+               }
+               else {
+                       $submitFeedback['message'] = $do['message'];
+                       $submitFeedback['status'] = 'error';
+               }
+       }
+       else {
+               $submitFeedback['message'] = 'Please provide a import file';
+               $submitFeedback['status'] = 'error';
+       }
+}
+
 if(isset($_POST['statsUpdateSearchIndex'])) {
 
     if($Management->updateSearchIndex() === true) {
index 8aaf432dd8dddf0541440837035b3c5013dc7107..21719cb87e05306790bc0bfc074c59f5d135afee 100644 (file)
@@ -27,6 +27,9 @@
  */
 ?>
 <section class="section">
+
+       <?php require('_displaySubmitStatus.inc.php'); ?>
+
        <div class="columns">
                <div class="column">
                        <p class="has-text-right">
             <form method="post">
                 <input type="submit" class="button is-info is-small" value="Update index" name="statsUpdateSearchIndex">
             </form>
+        </div>
+        <div class="column is-one-quarter">
+            <h4 class="is-size-4">Import XML</h4>
+            <p>Single or multiple</p>
+            <form method="post" enctype="multipart/form-data">
+                <div class="file">
+                    <label class="file-label">
+                        <input class="file-input" type="file" name="importxmlfile">
+                        <span class="file-cta">
+                            <span class="file-icon">
+                                <i class="ion-md-cloud-upload"></i>
+                            </span>
+                            <span class="file-label">
+                                Choose a fileā€¦
+                            </span>
+                        </span>
+                    </label>
+                </div>
+                <div class="field">
+                    <label class="checkbox">
+                        <input type="checkbox" value="overwrite" name="importOverwrite">
+                        Overwrite existing
+                    </label>
+                </div>
+                <div class="field">
+                    <input type="submit" class="button is-info is-small" value="Import" name="statsImportXML">
+                </div>
+            </form>
         </div>
                <?php } ?>
        </div>