commit 1e925b4b53a42a62bd9a11424b8e6ff5c6ba785b Author: SVarlamov Date: Mon Jun 20 21:36:09 2016 +0300 Добавлен Модуль Импорт Документов diff --git a/README.md b/README.md new file mode 100644 index 0000000..85e17e3 --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +## import + +# Модуль импорта v1.2.2 + + +## Данный модуль предназначен для импорта документов в определенную рубрику. + + * Первая строка в файле - название полей. Со второй строки - значения для импорта. Столбец - это значение поля. Строка - это отдельная позиция, документ. + для примера:
+ Здесь -> и
+ Здесь - > + +## Перед копированием модуля в папку modules, удалите файл README.md, копируйте только корневую папку import со всем ее содержимым внутри! + +## Changelog: + +03.07.2015 - версия 1.2.2 \ No newline at end of file diff --git a/import/class.import.php b/import/class.import.php new file mode 100644 index 0000000..f850c4b --- /dev/null +++ b/import/class.import.php @@ -0,0 +1,641 @@ +Query(" + UPDATE " . PREFIX . "_documents + SET + document_status = '0', + document_deleted = '1' + WHERE Id = " . $id + ); + } + + /** + * Удаление документа + * + * @param int $id идентификатор документа + */ + function DeleteDoc($id) + { + global $AVE_DB; + $AVE_DB->Query(" + UPDATE " . PREFIX . "_documents + SET + document_status = '0', + document_deleted = '1' + WHERE Id=". $id + ); + } + + /** + * Изменение документа + * @param int $id идентификатор документа + * @param array $array набор данных документа + */ + function UpdateDoc($rub, $id, $array) + { + require_once(BASE_DIR . '/class/class.docs.php'); + + $AVE_Document = new AVE_Document(); + + $d = $array['header']; + $d['doc_title'] = $d['document_title']; + $d['document_expire'] = date("d.m.Y H:i",strtotime('+20 year')); + $d['document_status'] = 1; + $d['feld'] = $array['body']; + + return $AVE_Document->documentSave($rub,$id,$d); + } + + /** + * Добавление документа + * + * @param int $rub идентификатор рубрики + * @param array $array набор данных документа + */ + function InsertDoc($rub, $array) + { + require_once(BASE_DIR.'/class/class.docs.php'); + + $AVE_Document=new AVE_Document(); + $d = $array['header']; + + $d['document_expire'] = date("d.m.Y H:i",strtotime('+20 year')); + $d['document_status'] = 1; + $d['doc_title'] = $d['document_title']; + $d['feld'] = $array['body']; + + return $AVE_Document->documentSave((int)$rub, null, $d); + + } + + /** + * Проверка наличия документа по ключевым полям + * + * @param array $array - массив ID_поля_в_рублике=>Ключевое_значение + * @param int $rub - id рубрики + * + * @return int/false - возвращает Id документа или false + */ + function Doc_Exists($key_fields,$rub) + { + global $AVE_DB; + + $sql_tables=''; + $sql_header=''; + $sql_body=''; + + $header = array(); + foreach($key_fields['header'] as $k => $v) + { + $header[] = 'a.' . $k . " = '" . $v . "'"; + } + if ($header) $sql_header = ' AND ' . implode(' AND ', $header); + + $tables = array(); + $body = array(); + $x = 0; + + foreach($key_fields['body'] as $k => $v) + { + $tables[] = PREFIX . "_document_fields AS t" . $x; + $body[] = "(a.Id=t" . $x . ".document_id AND(t" . $x . ".rubric_field_id=" . $k . " AND t" . $x . ".field_value='" . addslashes($v) . "'))"; + $x++; + } + + if ($tables) $sql_tables = ', ' . implode(', ', $tables); + + if ($body) $sql_body = ' AND ' . implode(' AND ', $body); + + $sql = " + SELECT a.Id FROM " . PREFIX . "_documents + AS a " . $sql_tables . " + WHERE + a.rubric_id=" . $rub . $sql_header . $sql_body; + + $doc_id = $AVE_DB->Query($sql)->GetCell(); + + return $doc_id; + } + + static function object_to_array($Class) + { + # Typecast to (array) automatically converts stdClass -> array. + $Class = (array)$Class; + $emptyarr=array(); + if($emptyarr===$Class) return ''; + # Iterate through the former properties looking for any stdClass properties. + # Recursively apply (array). + foreach($Class as $key => &$value) + { + if((is_object($value)||is_array($value))) + { + $Class[$key] = import::object_to_array($value); + } + //$value=addslashes($value); + } + return $Class; + } + + /** + * read a csv file and return an indexed array. + * @param string $cvsfile path to csv file + * @param array $fldnames array of fields names. Leave this to null to use the first row values as fields names. + * @param string $sep string used as a field separator (default ';') + * @param string $protect char used to protect field (generally single or double quote) + * @param array $filters array of regular expression that row must match to be in the returned result. + * ie: array('fldname'=>'/pcre_regexp/') + * @return array + */ + function CSV2Array($csvfile,$fldnames=null,$sep=',',$protect='"',$filters=null) + { + if(! $csv = file($csvfile) ) + return FALSE; + + # use the first line as fields names + if( is_null($fldnames) ){ + $fldnames = array_shift($csv); + $fldnames = explode($sep,$fldnames); + $fldnames = array_map('trim',$fldnames); + if($protect){ + foreach($fldnames as $k=>$v) + $fldnames[$k] = preg_replace(array("/(?$fldval) + $res[$i][(isset($fldnames[$fldnb])?$fldnames[$fldnb]:$fldnb)] = $fldval; + + if( is_array($filters) ){ + foreach($filters as $k=>$exp){ + if(! preg_match($exp,$res[$i][$k]) ) + unset($res[$i]); + } + } + $i++; + } + unset($csv); + return $res; + } + + function Excel2Array($fname) + { + require_once(dirname(__FILE__).'/excel_reader.php'); + + $Excel = new Spreadsheet_Excel_Reader(); // создаем объект + $Excel->setOutputEncoding('UTF-8'); // устанавливаем кодировку + $Excel->read($fname); // открываем файл + + $rowscount = $Excel->sheets[0]['numRows']; // узнаем количество строк в 1 листе $xml = import::object_to_array($xml); + + $res = array(); + + for($rowNum=2;$rowNum<=$rowscount;$rowNum++) + { + $collscount=count($Excel->sheets[0]['cells'][$rowNum]); + + for($cell=1;$cell<($collscount+1);$cell++) + { + //Чойто с екселя приходит 160 символ если пусто в ячейке + $val=(trim($Excel->val($rowNum,$cell))); + $res[$rowNum][$Excel->sheets[0]['cells'][1][$cell]]=($val===chr(160) ? '' : $val); + } + } + + return $res; + } + + function XML2Array($fname) + { + $xml = (simplexml_load_file($fname)); + $xml = import::object_to_array($xml); + // Убираем роот элемент из Массива чтобы добраться до самих записей - может есть варианты полегче... + $a = array_values($xml); + unset($xml); + return ($a[0]); + } + + // рекурсивно создаёт массив с заменами + function _replace_array($mixed,$key='',$new=true) + { + static $arr = array(); + if($new) $arr = array(); + + if (!is_array($mixed)) return $arr['[row'.$key.']'] = $mixed; + $res = $key; + foreach ($mixed as $k=>$v) + { + if(is_array($v)) + { + $arr['[row:'.$k.']'] = serialize($v); + } + $this->_replace_array($v,$res.':'.$k,false); + } + return $arr; + } + + // Заменяет в массиве доков, используя массив замен, выполняя код php + function _replace(&$item, &$key, $repl_array) + { + $code = stripslashes(strtr($item,$repl_array)); + $item = eval2var('?>' . $code . 'Query("SELECT * FROM " . PREFIX . "_module_import"); + + while ($result = $sql->FetchRow()) + { + array_push($imports, $result); + } + + $rubs = array(); + + $sql = $AVE_DB->Query("SELECT * FROM " . PREFIX . "_rubrics"); + + while ($result = $sql->FetchRow()) + { + array_push($rubs, $result); + } + + $AVE_Template->assign('imports', $imports); + $AVE_Template->assign('rubs', $rubs); + $AVE_Template->assign('content', $AVE_Template->fetch($tpl_dir . 'admin_list.tpl')); + } + + /** + * Редактирование импорта + * + * @param int $import_id идентификатор системного блока + * @param string $tpl_dir - путь к папке с шаблонами модуля + * + * @todo сделать отдельно методы добавления и редактирования + */ + function importEdit($import_id, $tpl_dir) + { + global $AVE_DB, $AVE_Template; + + if (is_numeric($import_id)) + { + $row = $AVE_DB->Query(" + SELECT * + FROM " . PREFIX . "_module_import + WHERE id = '" . $import_id . "' + ")->FetchAssocArray(); + + $row['import_text'] = unserialize($row['import_text']); + } + else + { + $row['import_name'] = ''; + $row['import_text'] = array(); + $row['import_delete_docs'] = 0; + $row['import_docs_create'] = 0; + } + + // рубрики + $sql = $AVE_DB->Query("SELECT * FROM " . PREFIX . "_rubrics"); + while ($result = $sql->FetchRow()) + { + $rubs[] = $result; + } + + // основные поля + $fields = array(); + foreach ($this->header_fields as $field) + { + $val = @$row['import_text']['fields']['header'][$field]; + $fields['header'][$field] = array(0 => $val, 1 => $AVE_Template->get_config_vars('IMPORT_' . $field)); + } + + // поля + $s = $AVE_DB->Query(" + SELECT * FROM " . PREFIX . "_rubric_fields + WHERE rubric_id = " . intval(@$row['import_rub']) + ); + while ($r = $s->FetchAssocArray()) + { + $val = $row['import_text']['fields']['body'][$r['Id']]; + $fields['body'][$r['Id']] = array(0 => $val, 1 => $r['rubric_field_title']); + } + + $row['import_text']['fields'] = $fields; + + // передаём данные в смарти + $AVE_Template->assign('rubs',$rubs); + $AVE_Template->assign('parses', $this->parsers); + $AVE_Template->assign('data', $row['import_text']); + + unset($row['import_text']); + + $AVE_Template->assign($row); + $AVE_Template->assign('content', $AVE_Template->fetch($tpl_dir . 'admin_edit.tpl')); + } + + /** + * Сохранение импорта + * + * @param int $import_id идентификатор импорта + */ + function importSave($import_id = null) + { + global $AVE_DB; + + if (is_numeric($import_id)) + { + $AVE_DB->Query(" + UPDATE " . PREFIX . "_module_import + SET + `import_name` = '" . addslashes($_POST['import_name']) . "', + ".($_POST['import_rub']> '' ? "`import_rub` = '" . addslashes($_POST['import_rub']) . "'," : "" )." + `import_parser` = '" . addslashes($_POST['import_parser']) . "', + `import_delete_docs` = '" . ($_POST['import_delete_docs'] ? 1 : 0) . "', + `import_docs_create` = '" . ($_POST['import_docs_create'] ? 1 : 0) . "', + `import_default_file` = '" . addslashes($_POST['import_default_file']) . "', + `import_monitor_file` = '" . addslashes($_POST['import_monitor_file'] ? '1' : '0') . "', + `import_last_update` = '" . addslashes($_POST['import_last_update']) . "', + `import_text` = '" . addslashes(serialize($_POST['document'])) . "' + WHERE + id = '" . $import_id . "' + "); + + header('Location:index.php?do=modules&action=modedit&mod=import&moduleaction=1&cp=' . SESSION); + exit(); + } + else + { + $AVE_DB->Query(" + INSERT + INTO " . PREFIX . "_module_import + SET + `import_name` = '" . addslashes($_POST['import_name']) . "', + `import_rub` = '" . addslashes($_POST['import_rub']) . "', + `import_parser` = '" . addslashes($_POST['import_parser']) . "', + `import_delete_docs` = '" . ($_POST['import_delete_docs'] ? 1 : 0) . "', + `import_docs_create` = '" . ($_POST['import_docs_create'] ? 1 : 0) . "', + `import_default_file` = '" . addslashes($_POST['import_default_file']) . "', + `import_monitor_file` = '" . addslashes($_POST['import_monitor_file'] ? '1' : '0') . "', + `import_last_update` = '" . addslashes($_POST['import_last_update']) . "', + `import_text` = '" . addslashes(serialize($_POST['document'])) . "' + "); + + $import_id = $AVE_DB->Query("SELECT LAST_INSERT_ID(id) FROM " . PREFIX . "_module_import ORDER BY id DESC LIMIT 1")->GetCell(); + + header('Location:index.php?do=modules&action=modedit&mod=import&moduleaction=edit&id=' . $import_id . '&cp=' . SESSION); + exit(); + } + } + + /** + * Удаление импорта + * + * @param int $import_id идентификатор системного блока + */ + function importDelete($import_id) + { + global $AVE_DB; + + if (is_numeric($import_id)) + { + $AVE_DB->Query(" + DELETE + FROM " . PREFIX . "_module_import + WHERE id = '" . $import_id . "' + "); + } + header('Location:index.php?do=modules&action=modedit&mod=import&moduleaction=1&cp=' . SESSION); + } + + /** + * Импорт + * + * @param int $import_id идентификатор системного блока + */ + function DoImport($import_id, $tags_only = false, $location = true) + { + global $AVE_DB; + + $_REQUEST['import'] = 'import'; + + $import = $AVE_DB->Query(" + SELECT * + FROM " . PREFIX . "_module_import + WHERE id = '" . $import_id . "' + ")->FetchAssocArray(); + + $import['import_text'] = @unserialize($import['import_text']); + + if($tags_only) $import['import_text']['tags'] = array(); + + //Создаем массив ключевых полей + if(is_array(@$import['import_text']['key']['header'])){ + foreach($import['import_text']['key']['header'] as $k=>$v) + { + $import_key_fields['header'][$k] = $import['import_text']['fields']['header'][$k]; + } + } + + if(is_array(@$import['import_text']['key']['body'])){ + foreach($import['import_text']['key']['body'] as $k=>$v) + { + $import_key_fields['body'][$k] = $import['import_text']['fields']['body'][$k]; + } + } + + // Получаем массив из файла импорта + $func = $import['import_parser']; + $rows = $this->$func(BASE_DIR . $import['import_default_file']); + + // Помечаем документы как удалённые, если нужно + if($import['import_delete_docs'] && !$tags_only) + { + $AVE_DB->Query(" + UPDATE " . PREFIX . "_documents + SET + document_status = '0' + WHERE rubric_id=" . $import['import_rub'] + ); + } + + // Обрабатываем по очереди каждый объект + foreach($rows as $row) + { + // создаем массив замен + $replace_array = array(); + $replace_array = $this->_replace_array($row); + $replace_array['[Y-m-d]'] = date('d.m.Y H:i'); + + // если нужно только обновить теги + if($tags_only) + { + $import['import_text']['tags'] = array_unique(array_merge($import['import_text']['tags'],array_keys($replace_array))); + } + else + { + // дополняем массив замен отсутствующими тегами + foreach($import['import_text']['tags'] as $v) + { + if(! $replace_array[$v]) $replace_array[$v] = ''; + } + + $key_fields = $import_key_fields; + $doc_fields = array(); + $doc_fields['header'] = $import['import_text']['fields']['header']; + $doc_fields['body'] = $import['import_text']['fields']['body']; + + // гуляем по шаблонам - заменяем теги на значения + array_walk_recursive($key_fields, array($this, '_replace'), $replace_array); + array_walk_recursive($doc_fields, array($this, '_replace'), $replace_array); + + //проверяем значения по критическим полям + $critical = false; + + if(isset($import['import_text']['critical']['header'])&&is_array($import['import_text']['critical']['header'])) + foreach(@$import['import_text']['critical']['header'] as $k => $v) + if(trim($doc_fields['header'][$k])=='') $critical=true; + + if(isset($import['import_text']['critical']['body'])&&is_array($import['import_text']['critical']['body'])) + foreach(@$import['import_text']['critical']['body'] as $k => $v) + if(trim($doc_fields['body'][$k])=='') $critical=true; + + if(! $critical) + { + //Если прошли проверку импортируем + // проверяем наличие документа по ключевому полю + $id = $this->Doc_Exists($key_fields, $import['import_rub']); + + if ($id) + { + // удаляем из массива поля, которые не надо импортировать + foreach($import['import_text']['active']['header'] as $k => $v) + { + if(! $v) + { + $doc_fields['header'][$k] = $AVE_DB->Query(" + SELECT " . $k . " FROM " . PREFIX . "_documents + WHERE Id = " . $id + )->GetCell(); + } + } + + foreach($import['import_text']['active']['body'] as $k => $v) + { + if(! $v) + unset($doc_fields['body'][$k]); + } + + unset($doc_fields['header']['Id']); + + $this->UpdateDoc($import['import_rub'], $id, $doc_fields); + } + else + { + if ($import['import_docs_create']) + $this->InsertDoc($import['import_rub'], $doc_fields); + } + } + } + } + + if (! $tags_only) + { + $AVE_DB->Query(" + UPDATE " . PREFIX . "_module_import + SET + import_last_update = '" . time() . "', + import_text = '" . addslashes(serialize($import['import_text'])) . "' + WHERE id = " . $import_id + ); + header('Location:index.php?do=modules&action=modedit&mod=import&moduleaction=1&cp=' . SESSION); + } + else + { + $AVE_DB->Query(" + UPDATE " . PREFIX . "_module_import + SET + import_text = '" . addslashes(serialize($import['import_text'])) . "' + WHERE id = " . $import_id + ); + if ($location) + { + header('Location:index.php?do=modules&action=modedit&mod=import&moduleaction=edit&id=' . $import_id . '&cp=' . SESSION); + } + } + + } +} +?> \ No newline at end of file diff --git a/import/excel_reader.php b/import/excel_reader.php new file mode 100644 index 0000000..15e6128 --- /dev/null +++ b/import/excel_reader.php @@ -0,0 +1,1737 @@ + + * Maintained at http://code.google.com/p/php-excel-reader/ + * + * Format parsing and MUCH more contributed by: + * Matt Roxburgh < http://www.roxburgh.me.uk > + * + * DOCUMENTATION + * ============= + * http://code.google.com/p/php-excel-reader/wiki/Documentation + * + * CHANGE LOG + * ========== + * http://code.google.com/p/php-excel-reader/wiki/ChangeHistory + * + * DISCUSSION/SUPPORT + * ================== + * http://groups.google.com/group/php-excel-reader-discuss/topics + * + * -------------------------------------------------------------------------- + * + * Originally developed by Vadim Tkachenko under the name PHPExcelReader. + * (http://sourceforge.net/projects/phpexcelreader) + * Based on the Java version by Andy Khan (http://www.andykhan.com). Now + * maintained by David Sanders. Reads only Biff 7 and Biff 8 formats. + * + * PHP versions 4 and 5 + * + * LICENSE: This source file is subject to version 3.0 of the PHP license + * that is available through the world-wide-web at the following URI: + * http://www.php.net/license/3_0.txt. If you did not receive a copy of + * the PHP License and are unable to obtain it through the web, please + * send a note to license@php.net so we can mail you a copy immediately. + * + * @category Spreadsheet + * @package Spreadsheet_Excel_Reader + * @author Vadim Tkachenko + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: reader.php 19 2007-03-13 12:42:41Z shangxiao $ + * @link http://pear.php.net/package/Spreadsheet_Excel_Reader + * @see OLE, Spreadsheet_Excel_Writer + * -------------------------------------------------------------------------- + */ + +define('NUM_BIG_BLOCK_DEPOT_BLOCKS_POS', 0x2c); +define('SMALL_BLOCK_DEPOT_BLOCK_POS', 0x3c); +define('ROOT_START_BLOCK_POS', 0x30); +define('BIG_BLOCK_SIZE', 0x200); +define('SMALL_BLOCK_SIZE', 0x40); +define('EXTENSION_BLOCK_POS', 0x44); +define('NUM_EXTENSION_BLOCK_POS', 0x48); +define('PROPERTY_STORAGE_BLOCK_SIZE', 0x80); +define('BIG_BLOCK_DEPOT_BLOCKS_POS', 0x4c); +define('SMALL_BLOCK_THRESHOLD', 0x1000); +// property storage offsets +define('SIZE_OF_NAME_POS', 0x40); +define('TYPE_POS', 0x42); +define('START_BLOCK_POS', 0x74); +define('SIZE_POS', 0x78); +define('IDENTIFIER_OLE', pack("CCCCCCCC",0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1)); + + +function GetInt4d($data, $pos) { + $value = ord($data[$pos]) | (ord($data[$pos+1]) << 8) | (ord($data[$pos+2]) << 16) | (ord($data[$pos+3]) << 24); + if ($value>=4294967294) { + $value=-2; + } + return $value; +} + +// http://uk.php.net/manual/en/function.getdate.php +function gmgetdate($ts = null){ + $k = array('seconds','minutes','hours','mday','wday','mon','year','yday','weekday','month',0); + return(array_comb($k,split(":",gmdate('s:i:G:j:w:n:Y:z:l:F:U',is_null($ts)?time():$ts)))); + } + +// Added for PHP4 compatibility +function array_comb($array1, $array2) { + $out = array(); + foreach ($array1 as $key => $value) { + $out[$value] = $array2[$key]; + } + return $out; +} + +function v($data,$pos) { + return ord($data[$pos]) | ord($data[$pos+1])<<8; +} + +class OLERead { + var $data = ''; + function OLERead(){ } + + function read($sFileName){ + // check if file exist and is readable (Darko Miljanovic) + if(!is_readable($sFileName)) { + $this->error = 1; + return false; + } + $this->data = @file_get_contents($sFileName); + if (!$this->data) { + $this->error = 1; + return false; + } + if (substr($this->data, 0, 8) != IDENTIFIER_OLE) { + $this->error = 1; + return false; + } + $this->numBigBlockDepotBlocks = GetInt4d($this->data, NUM_BIG_BLOCK_DEPOT_BLOCKS_POS); + $this->sbdStartBlock = GetInt4d($this->data, SMALL_BLOCK_DEPOT_BLOCK_POS); + $this->rootStartBlock = GetInt4d($this->data, ROOT_START_BLOCK_POS); + $this->extensionBlock = GetInt4d($this->data, EXTENSION_BLOCK_POS); + $this->numExtensionBlocks = GetInt4d($this->data, NUM_EXTENSION_BLOCK_POS); + + $bigBlockDepotBlocks = array(); + $pos = BIG_BLOCK_DEPOT_BLOCKS_POS; + $bbdBlocks = $this->numBigBlockDepotBlocks; + if ($this->numExtensionBlocks != 0) { + $bbdBlocks = (BIG_BLOCK_SIZE - BIG_BLOCK_DEPOT_BLOCKS_POS)/4; + } + + for ($i = 0; $i < $bbdBlocks; $i++) { + $bigBlockDepotBlocks[$i] = GetInt4d($this->data, $pos); + $pos += 4; + } + + + for ($j = 0; $j < $this->numExtensionBlocks; $j++) { + $pos = ($this->extensionBlock + 1) * BIG_BLOCK_SIZE; + $blocksToRead = min($this->numBigBlockDepotBlocks - $bbdBlocks, BIG_BLOCK_SIZE / 4 - 1); + + for ($i = $bbdBlocks; $i < $bbdBlocks + $blocksToRead; $i++) { + $bigBlockDepotBlocks[$i] = GetInt4d($this->data, $pos); + $pos += 4; + } + + $bbdBlocks += $blocksToRead; + if ($bbdBlocks < $this->numBigBlockDepotBlocks) { + $this->extensionBlock = GetInt4d($this->data, $pos); + } + } + + // readBigBlockDepot + $pos = 0; + $index = 0; + $this->bigBlockChain = array(); + + for ($i = 0; $i < $this->numBigBlockDepotBlocks; $i++) { + $pos = ($bigBlockDepotBlocks[$i] + 1) * BIG_BLOCK_SIZE; + //echo "pos = $pos"; + for ($j = 0 ; $j < BIG_BLOCK_SIZE / 4; $j++) { + $this->bigBlockChain[$index] = GetInt4d($this->data, $pos); + $pos += 4 ; + $index++; + } + } + + // readSmallBlockDepot(); + $pos = 0; + $index = 0; + $sbdBlock = $this->sbdStartBlock; + $this->smallBlockChain = array(); + + while ($sbdBlock != -2) { + $pos = ($sbdBlock + 1) * BIG_BLOCK_SIZE; + for ($j = 0; $j < BIG_BLOCK_SIZE / 4; $j++) { + $this->smallBlockChain[$index] = GetInt4d($this->data, $pos); + $pos += 4; + $index++; + } + $sbdBlock = $this->bigBlockChain[$sbdBlock]; + } + + + // readData(rootStartBlock) + $block = $this->rootStartBlock; + $pos = 0; + $this->entry = $this->__readData($block); + $this->__readPropertySets(); + } + + function __readData($bl) { + $block = $bl; + $pos = 0; + $data = ''; + while ($block != -2) { + $pos = ($block + 1) * BIG_BLOCK_SIZE; + $data = $data.substr($this->data, $pos, BIG_BLOCK_SIZE); + $block = $this->bigBlockChain[$block]; + } + return $data; + } + + function __readPropertySets(){ + $offset = 0; + while ($offset < strlen($this->entry)) { + $d = substr($this->entry, $offset, PROPERTY_STORAGE_BLOCK_SIZE); + $nameSize = ord($d[SIZE_OF_NAME_POS]) | (ord($d[SIZE_OF_NAME_POS+1]) << 8); + $type = ord($d[TYPE_POS]); + $startBlock = GetInt4d($d, START_BLOCK_POS); + $size = GetInt4d($d, SIZE_POS); + $name = ''; + for ($i = 0; $i < $nameSize ; $i++) { + $name .= $d[$i]; + } + $name = str_replace("\x00", "", $name); + $this->props[] = array ( + 'name' => $name, + 'type' => $type, + 'startBlock' => $startBlock, + 'size' => $size); + if ((strtolower($name) == "workbook") || ( strtolower($name) == "book")) { + $this->wrkbook = count($this->props) - 1; + } + if ($name == "Root Entry") { + $this->rootentry = count($this->props) - 1; + } + $offset += PROPERTY_STORAGE_BLOCK_SIZE; + } + + } + + + function getWorkBook(){ + if ($this->props[$this->wrkbook]['size'] < SMALL_BLOCK_THRESHOLD){ + $rootdata = $this->__readData($this->props[$this->rootentry]['startBlock']); + $streamData = ''; + $block = $this->props[$this->wrkbook]['startBlock']; + $pos = 0; + while ($block != -2) { + $pos = $block * SMALL_BLOCK_SIZE; + $streamData .= substr($rootdata, $pos, SMALL_BLOCK_SIZE); + $block = $this->smallBlockChain[$block]; + } + return $streamData; + }else{ + $numBlocks = $this->props[$this->wrkbook]['size'] / BIG_BLOCK_SIZE; + if ($this->props[$this->wrkbook]['size'] % BIG_BLOCK_SIZE != 0) { + $numBlocks++; + } + + if ($numBlocks == 0) return ''; + $streamData = ''; + $block = $this->props[$this->wrkbook]['startBlock']; + $pos = 0; + while ($block != -2) { + $pos = ($block + 1) * BIG_BLOCK_SIZE; + $streamData .= substr($this->data, $pos, BIG_BLOCK_SIZE); + $block = $this->bigBlockChain[$block]; + } + return $streamData; + } + } + +} + +define('SPREADSHEET_EXCEL_READER_BIFF8', 0x600); +define('SPREADSHEET_EXCEL_READER_BIFF7', 0x500); +define('SPREADSHEET_EXCEL_READER_WORKBOOKGLOBALS', 0x5); +define('SPREADSHEET_EXCEL_READER_WORKSHEET', 0x10); +define('SPREADSHEET_EXCEL_READER_TYPE_BOF', 0x809); +define('SPREADSHEET_EXCEL_READER_TYPE_EOF', 0x0a); +define('SPREADSHEET_EXCEL_READER_TYPE_BOUNDSHEET', 0x85); +define('SPREADSHEET_EXCEL_READER_TYPE_DIMENSION', 0x200); +define('SPREADSHEET_EXCEL_READER_TYPE_ROW', 0x208); +define('SPREADSHEET_EXCEL_READER_TYPE_DBCELL', 0xd7); +define('SPREADSHEET_EXCEL_READER_TYPE_FILEPASS', 0x2f); +define('SPREADSHEET_EXCEL_READER_TYPE_NOTE', 0x1c); +define('SPREADSHEET_EXCEL_READER_TYPE_TXO', 0x1b6); +define('SPREADSHEET_EXCEL_READER_TYPE_RK', 0x7e); +define('SPREADSHEET_EXCEL_READER_TYPE_RK2', 0x27e); +define('SPREADSHEET_EXCEL_READER_TYPE_MULRK', 0xbd); +define('SPREADSHEET_EXCEL_READER_TYPE_MULBLANK', 0xbe); +define('SPREADSHEET_EXCEL_READER_TYPE_INDEX', 0x20b); +define('SPREADSHEET_EXCEL_READER_TYPE_SST', 0xfc); +define('SPREADSHEET_EXCEL_READER_TYPE_EXTSST', 0xff); +define('SPREADSHEET_EXCEL_READER_TYPE_CONTINUE', 0x3c); +define('SPREADSHEET_EXCEL_READER_TYPE_LABEL', 0x204); +define('SPREADSHEET_EXCEL_READER_TYPE_LABELSST', 0xfd); +define('SPREADSHEET_EXCEL_READER_TYPE_NUMBER', 0x203); +define('SPREADSHEET_EXCEL_READER_TYPE_NAME', 0x18); +define('SPREADSHEET_EXCEL_READER_TYPE_ARRAY', 0x221); +define('SPREADSHEET_EXCEL_READER_TYPE_STRING', 0x207); +define('SPREADSHEET_EXCEL_READER_TYPE_FORMULA', 0x406); +define('SPREADSHEET_EXCEL_READER_TYPE_FORMULA2', 0x6); +define('SPREADSHEET_EXCEL_READER_TYPE_FORMAT', 0x41e); +define('SPREADSHEET_EXCEL_READER_TYPE_XF', 0xe0); +define('SPREADSHEET_EXCEL_READER_TYPE_BOOLERR', 0x205); +define('SPREADSHEET_EXCEL_READER_TYPE_FONT', 0x0031); +define('SPREADSHEET_EXCEL_READER_TYPE_PALETTE', 0x0092); +define('SPREADSHEET_EXCEL_READER_TYPE_UNKNOWN', 0xffff); +define('SPREADSHEET_EXCEL_READER_TYPE_NINETEENFOUR', 0x22); +define('SPREADSHEET_EXCEL_READER_TYPE_MERGEDCELLS', 0xE5); +define('SPREADSHEET_EXCEL_READER_UTCOFFSETDAYS' , 25569); +define('SPREADSHEET_EXCEL_READER_UTCOFFSETDAYS1904', 24107); +define('SPREADSHEET_EXCEL_READER_MSINADAY', 86400); +define('SPREADSHEET_EXCEL_READER_TYPE_HYPER', 0x01b8); +define('SPREADSHEET_EXCEL_READER_TYPE_COLINFO', 0x7d); +define('SPREADSHEET_EXCEL_READER_TYPE_DEFCOLWIDTH', 0x55); +define('SPREADSHEET_EXCEL_READER_TYPE_STANDARDWIDTH', 0x99); +define('SPREADSHEET_EXCEL_READER_DEF_NUM_FORMAT', "%s"); + + +/* +* Main Class +*/ +class Spreadsheet_Excel_Reader { + + // MK: Added to make data retrieval easier + var $colnames = array(); + var $colindexes = array(); + var $standardColWidth = 0; + var $defaultColWidth = 0; + + function myHex($d) { + if ($d < 16) return "0" . dechex($d); + return dechex($d); + } + + function dumpHexData($data, $pos, $length) { + $info = ""; + for ($i = 0; $i <= $length; $i++) { + $info .= ($i==0?"":" ") . $this->myHex(ord($data[$pos + $i])) . (ord($data[$pos + $i])>31? "[" . $data[$pos + $i] . "]":''); + } + return $info; + } + + function getCol($col) { + if (is_string($col)) { + $col = strtolower($col); + if (array_key_exists($col,$this->colnames)) { + $col = $this->colnames[$col]; + } + } + return $col; + } + + // PUBLIC API FUNCTIONS + // -------------------- + + function val($row,$col,$sheet=0) { + $col = $this->getCol($col); + if (array_key_exists($row,$this->sheets[$sheet]['cells']) && array_key_exists($col,$this->sheets[$sheet]['cells'][$row])) { + return $this->sheets[$sheet]['cells'][$row][$col]; + } + return ""; + } + function value($row,$col,$sheet=0) { + return $this->val($row,$col,$sheet); + } + function info($row,$col,$type='',$sheet=0) { + $col = $this->getCol($col); + if (array_key_exists('cellsInfo',$this->sheets[$sheet]) + && array_key_exists($row,$this->sheets[$sheet]['cellsInfo']) + && array_key_exists($col,$this->sheets[$sheet]['cellsInfo'][$row]) + && array_key_exists($type,$this->sheets[$sheet]['cellsInfo'][$row][$col])) { + return $this->sheets[$sheet]['cellsInfo'][$row][$col][$type]; + } + return ""; + } + function type($row,$col,$sheet=0) { + return $this->info($row,$col,'type',$sheet); + } + function raw($row,$col,$sheet=0) { + return $this->info($row,$col,'raw',$sheet); + } + function rowspan($row,$col,$sheet=0) { + $val = $this->info($row,$col,'rowspan',$sheet); + if ($val=="") { return 1; } + return $val; + } + function colspan($row,$col,$sheet=0) { + $val = $this->info($row,$col,'colspan',$sheet); + if ($val=="") { return 1; } + return $val; + } + function hyperlink($row,$col,$sheet=0) { + $link = $this->sheets[$sheet]['cellsInfo'][$row][$col]['hyperlink']; + if ($link) { + return $link['link']; + } + return ''; + } + function rowcount($sheet=0) { + return $this->sheets[$sheet]['numRows']; + } + function colcount($sheet=0) { + return $this->sheets[$sheet]['numCols']; + } + function colwidth($col,$sheet=0) { + // Col width is actually the width of the number 0. So we have to estimate and come close + return $this->colInfo[$sheet][$col]['width']/9142*200; + } + function colhidden($col,$sheet=0) { + return !!$this->colInfo[$sheet][$col]['hidden']; + } + function rowheight($row,$sheet=0) { + return $this->rowInfo[$sheet][$row]['height']; + } + function rowhidden($row,$sheet=0) { + return !!$this->rowInfo[$sheet][$row]['hidden']; + } + + // GET THE CSS FOR FORMATTING + // ========================== + function style($row,$col,$sheet=0,$properties='') { + $css = ""; + $font=$this->font($row,$col,$sheet); + if ($font!="") { + $css .= "font-family:$font;"; + } + $align=$this->align($row,$col,$sheet); + if ($align!="") { + $css .= "text-align:$align;"; + } + $height=$this->height($row,$col,$sheet); + if ($height!="") { + $css .= "font-size:$height"."px;"; + } + $bgcolor=$this->bgColor($row,$col,$sheet); + if ($bgcolor!="") { + $bgcolor = $this->colors[$bgcolor]; + $css .= "background-color:$bgcolor;"; + } + $color=$this->color($row,$col,$sheet); + if ($color!="") { + $css .= "color:$color;"; + } + $bold=$this->bold($row,$col,$sheet); + if ($bold) { + $css .= "font-weight:bold;"; + } + $italic=$this->italic($row,$col,$sheet); + if ($italic) { + $css .= "font-style:italic;"; + } + $underline=$this->underline($row,$col,$sheet); + if ($underline) { + $css .= "text-decoration:underline;"; + } + // Borders + $bLeft = $this->borderLeft($row,$col,$sheet); + $bRight = $this->borderRight($row,$col,$sheet); + $bTop = $this->borderTop($row,$col,$sheet); + $bBottom = $this->borderBottom($row,$col,$sheet); + $bLeftCol = $this->borderLeftColor($row,$col,$sheet); + $bRightCol = $this->borderRightColor($row,$col,$sheet); + $bTopCol = $this->borderTopColor($row,$col,$sheet); + $bBottomCol = $this->borderBottomColor($row,$col,$sheet); + // Try to output the minimal required style + if ($bLeft!="" && $bLeft==$bRight && $bRight==$bTop && $bTop==$bBottom) { + $css .= "border:" . $this->lineStylesCss[$bLeft] .";"; + } + else { + if ($bLeft!="") { $css .= "border-left:" . $this->lineStylesCss[$bLeft] .";"; } + if ($bRight!="") { $css .= "border-right:" . $this->lineStylesCss[$bRight] .";"; } + if ($bTop!="") { $css .= "border-top:" . $this->lineStylesCss[$bTop] .";"; } + if ($bBottom!="") { $css .= "border-bottom:" . $this->lineStylesCss[$bBottom] .";"; } + } + // Only output border colors if there is an actual border specified + if ($bLeft!="" && $bLeftCol!="") { $css .= "border-left-color:" . $bLeftCol .";"; } + if ($bRight!="" && $bRightCol!="") { $css .= "border-right-color:" . $bRightCol .";"; } + if ($bTop!="" && $bTopCol!="") { $css .= "border-top-color:" . $bTopCol . ";"; } + if ($bBottom!="" && $bBottomCol!="") { $css .= "border-bottom-color:" . $bBottomCol .";"; } + + return $css; + } + + // FORMAT PROPERTIES + // ================= + function format($row,$col,$sheet=0) { + return $this->info($row,$col,'format',$sheet); + } + function formatIndex($row,$col,$sheet=0) { + return $this->info($row,$col,'formatIndex',$sheet); + } + function formatColor($row,$col,$sheet=0) { + return $this->info($row,$col,'formatColor',$sheet); + } + + // CELL (XF) PROPERTIES + // ==================== + function xfRecord($row,$col,$sheet=0) { + $xfIndex = $this->info($row,$col,'xfIndex',$sheet); + if ($xfIndex!="") { + return $this->xfRecords[$xfIndex]; + } + return null; + } + function xfProperty($row,$col,$sheet,$prop) { + $xfRecord = $this->xfRecord($row,$col,$sheet); + if ($xfRecord!=null) { + return $xfRecord[$prop]; + } + return ""; + } + function align($row,$col,$sheet=0) { + return $this->xfProperty($row,$col,$sheet,'align'); + } + function bgColor($row,$col,$sheet=0) { + return $this->xfProperty($row,$col,$sheet,'bgColor'); + } + function borderLeft($row,$col,$sheet=0) { + return $this->xfProperty($row,$col,$sheet,'borderLeft'); + } + function borderRight($row,$col,$sheet=0) { + return $this->xfProperty($row,$col,$sheet,'borderRight'); + } + function borderTop($row,$col,$sheet=0) { + return $this->xfProperty($row,$col,$sheet,'borderTop'); + } + function borderBottom($row,$col,$sheet=0) { + return $this->xfProperty($row,$col,$sheet,'borderBottom'); + } + function borderLeftColor($row,$col,$sheet=0) { + return $this->colors[$this->xfProperty($row,$col,$sheet,'borderLeftColor')]; + } + function borderRightColor($row,$col,$sheet=0) { + return $this->colors[$this->xfProperty($row,$col,$sheet,'borderRightColor')]; + } + function borderTopColor($row,$col,$sheet=0) { + return $this->colors[$this->xfProperty($row,$col,$sheet,'borderTopColor')]; + } + function borderBottomColor($row,$col,$sheet=0) { + return $this->colors[$this->xfProperty($row,$col,$sheet,'borderBottomColor')]; + } + + // FONT PROPERTIES + // =============== + function fontRecord($row,$col,$sheet=0) { + $xfRecord = $this->xfRecord($row,$col,$sheet); + if ($xfRecord!=null) { + $font = $xfRecord['fontIndex']; + if ($font!=null) { + return $this->fontRecords[$font]; + } + } + return null; + } + function fontProperty($row,$col,$sheet=0,$prop) { + $font = $this->fontRecord($row,$col,$sheet); + if ($font!=null) { + return $font[$prop]; + } + return false; + } + function fontIndex($row,$col,$sheet=0) { + return $this->xfProperty($row,$col,$sheet,'fontIndex'); + } + function color($row,$col,$sheet=0) { + $formatColor = $this->formatColor($row,$col,$sheet); + if ($formatColor!="") { + return $formatColor; + } + $ci = $this->fontProperty($row,$col,$sheet,'color'); + return $this->rawColor($ci); + } + function rawColor($ci) { + if (($ci <> 0x7FFF) && ($ci <> '')) { + return $this->colors[$ci]; + } + return ""; + } + function bold($row,$col,$sheet=0) { + return $this->fontProperty($row,$col,$sheet,'bold'); + } + function italic($row,$col,$sheet=0) { + return $this->fontProperty($row,$col,$sheet,'italic'); + } + function underline($row,$col,$sheet=0) { + return $this->fontProperty($row,$col,$sheet,'under'); + } + function height($row,$col,$sheet=0) { + return $this->fontProperty($row,$col,$sheet,'height'); + } + function font($row,$col,$sheet=0) { + return $this->fontProperty($row,$col,$sheet,'font'); + } + + // DUMP AN HTML TABLE OF THE ENTIRE XLS DATA + // ========================================= + function dump($row_numbers=false,$col_letters=false,$sheet=0,$table_class='excel') { + $out = ""; + if ($col_letters) { + $out .= "\n\t"; + if ($row_numbers) { + $out .= "\n\t\t"; + } + for($i=1;$i<=$this->colcount($sheet);$i++) { + $style = "width:" . ($this->colwidth($i,$sheet)*1) . "px;"; + if ($this->colhidden($i,$sheet)) { + $style .= "display:none;"; + } + $out .= "\n\t\t"; + } + $out .= "\n"; + } + + $out .= "\n"; + for($row=1;$row<=$this->rowcount($sheet);$row++) { + $rowheight = $this->rowheight($row,$sheet); + $style = "height:" . ($rowheight*(4/3)) . "px;"; + if ($this->rowhidden($row,$sheet)) { + $style .= "display:none;"; + } + $out .= "\n\t"; + if ($row_numbers) { + $out .= "\n\t\t"; + } + for($col=1;$col<=$this->colcount($sheet);$col++) { + // Account for Rowspans/Colspans + $rowspan = $this->rowspan($row,$col,$sheet); + $colspan = $this->colspan($row,$col,$sheet); + for($i=0;$i<$rowspan;$i++) { + for($j=0;$j<$colspan;$j++) { + if ($i>0 || $j>0) { + $this->sheets[$sheet]['cellsInfo'][$row+$i][$col+$j]['dontprint']=1; + } + } + } + if(!$this->sheets[$sheet]['cellsInfo'][$row][$col]['dontprint']) { + $style = $this->style($row,$col,$sheet); + if ($this->colhidden($col,$sheet)) { + $style .= "display:none;"; + } + $out .= "\n\t\t"; + } + } + $out .= "\n"; + } + $out .= "
 " . strtoupper($this->colindexes[$i]) . "
$row 1?" colspan=$colspan":"") . ($rowspan > 1?" rowspan=$rowspan":"") . ">"; + $val = $this->val($row,$col,$sheet); + if ($val=='') { $val=" "; } + else { + $val = htmlentities($val); + $link = $this->hyperlink($row,$col,$sheet); + if ($link!='') { + $val = "$val"; + } + } + $out .= "".nl2br($val).""; + $out .= "
"; + return $out; + } + + // -------------- + // END PUBLIC API + + + var $boundsheets = array(); + var $formatRecords = array(); + var $fontRecords = array(); + var $xfRecords = array(); + var $colInfo = array(); + var $rowInfo = array(); + + var $sst = array(); + var $sheets = array(); + + var $data; + var $_ole; + var $_defaultEncoding = "UTF-8"; + var $_defaultFormat = SPREADSHEET_EXCEL_READER_DEF_NUM_FORMAT; + var $_columnsFormat = array(); + var $_rowoffset = 1; + var $_coloffset = 1; + + /** + * List of default date formats used by Excel + */ + var $dateFormats = array ( + 0xe => "m/d/Y", + 0xf => "M-d-Y", + 0x10 => "d-M", + 0x11 => "M-Y", + 0x12 => "h:i a", + 0x13 => "h:i:s a", + 0x14 => "H:i", + 0x15 => "H:i:s", + 0x16 => "d/m/Y H:i", + 0x2d => "i:s", + 0x2e => "H:i:s", + 0x2f => "i:s.S" + ); + + /** + * Default number formats used by Excel + */ + var $numberFormats = array( + 0x1 => "0", + 0x2 => "0.00", + 0x3 => "#,##0", + 0x4 => "#,##0.00", + 0x5 => "\$#,##0;(\$#,##0)", + 0x6 => "\$#,##0;[Red](\$#,##0)", + 0x7 => "\$#,##0.00;(\$#,##0.00)", + 0x8 => "\$#,##0.00;[Red](\$#,##0.00)", + 0x9 => "0%", + 0xa => "0.00%", + 0xb => "0.00E+00", + 0x25 => "#,##0;(#,##0)", + 0x26 => "#,##0;[Red](#,##0)", + 0x27 => "#,##0.00;(#,##0.00)", + 0x28 => "#,##0.00;[Red](#,##0.00)", + 0x29 => "#,##0;(#,##0)", // Not exactly + 0x2a => "\$#,##0;(\$#,##0)", // Not exactly + 0x2b => "#,##0.00;(#,##0.00)", // Not exactly + 0x2c => "\$#,##0.00;(\$#,##0.00)", // Not exactly + 0x30 => "##0.0E+0" + ); + + var $colors = Array( + 0x00 => "#000000", + 0x01 => "#FFFFFF", + 0x02 => "#FF0000", + 0x03 => "#00FF00", + 0x04 => "#0000FF", + 0x05 => "#FFFF00", + 0x06 => "#FF00FF", + 0x07 => "#00FFFF", + 0x08 => "#000000", + 0x09 => "#FFFFFF", + 0x0A => "#FF0000", + 0x0B => "#00FF00", + 0x0C => "#0000FF", + 0x0D => "#FFFF00", + 0x0E => "#FF00FF", + 0x0F => "#00FFFF", + 0x10 => "#800000", + 0x11 => "#008000", + 0x12 => "#000080", + 0x13 => "#808000", + 0x14 => "#800080", + 0x15 => "#008080", + 0x16 => "#C0C0C0", + 0x17 => "#808080", + 0x18 => "#9999FF", + 0x19 => "#993366", + 0x1A => "#FFFFCC", + 0x1B => "#CCFFFF", + 0x1C => "#660066", + 0x1D => "#FF8080", + 0x1E => "#0066CC", + 0x1F => "#CCCCFF", + 0x20 => "#000080", + 0x21 => "#FF00FF", + 0x22 => "#FFFF00", + 0x23 => "#00FFFF", + 0x24 => "#800080", + 0x25 => "#800000", + 0x26 => "#008080", + 0x27 => "#0000FF", + 0x28 => "#00CCFF", + 0x29 => "#CCFFFF", + 0x2A => "#CCFFCC", + 0x2B => "#FFFF99", + 0x2C => "#99CCFF", + 0x2D => "#FF99CC", + 0x2E => "#CC99FF", + 0x2F => "#FFCC99", + 0x30 => "#3366FF", + 0x31 => "#33CCCC", + 0x32 => "#99CC00", + 0x33 => "#FFCC00", + 0x34 => "#FF9900", + 0x35 => "#FF6600", + 0x36 => "#666699", + 0x37 => "#969696", + 0x38 => "#003366", + 0x39 => "#339966", + 0x3A => "#003300", + 0x3B => "#333300", + 0x3C => "#993300", + 0x3D => "#993366", + 0x3E => "#333399", + 0x3F => "#333333", + 0x40 => "#000000", + 0x41 => "#FFFFFF", + + 0x43 => "#000000", + 0x4D => "#000000", + 0x4E => "#FFFFFF", + 0x4F => "#000000", + 0x50 => "#FFFFFF", + 0x51 => "#000000", + + 0x7FFF => "#000000" + ); + + var $lineStyles = array( + 0x00 => "", + 0x01 => "Thin", + 0x02 => "Medium", + 0x03 => "Dashed", + 0x04 => "Dotted", + 0x05 => "Thick", + 0x06 => "Double", + 0x07 => "Hair", + 0x08 => "Medium dashed", + 0x09 => "Thin dash-dotted", + 0x0A => "Medium dash-dotted", + 0x0B => "Thin dash-dot-dotted", + 0x0C => "Medium dash-dot-dotted", + 0x0D => "Slanted medium dash-dotted" + ); + + var $lineStylesCss = array( + "Thin" => "1px solid", + "Medium" => "2px solid", + "Dashed" => "1px dashed", + "Dotted" => "1px dotted", + "Thick" => "3px solid", + "Double" => "double", + "Hair" => "1px solid", + "Medium dashed" => "2px dashed", + "Thin dash-dotted" => "1px dashed", + "Medium dash-dotted" => "2px dashed", + "Thin dash-dot-dotted" => "1px dashed", + "Medium dash-dot-dotted" => "2px dashed", + "Slanted medium dash-dotte" => "2px dashed" + ); + + function read16bitstring($data, $start) { + $len = 0; + while (ord($data[$start + $len]) + ord($data[$start + $len + 1]) > 0) $len++; + return substr($data, $start, $len); + } + + // ADDED by Matt Kruse for better formatting + function _format_value($format,$num,$f) { + // 49==TEXT format + // http://code.google.com/p/php-excel-reader/issues/detail?id=7 + if ( (!$f && $format=="%s") || ($f==49) || ($format=="GENERAL") ) { + return array('string'=>$num, 'formatColor'=>null); + } + + // Custom pattern can be POSITIVE;NEGATIVE;ZERO + // The "text" option as 4th parameter is not handled + $parts = split(";",$format); + $pattern = $parts[0]; + // Negative pattern + if (count($parts)>2 && $num==0) { + $pattern = $parts[2]; + } + // Zero pattern + if (count($parts)>1 && $num<0) { + $pattern = $parts[1]; + $num = abs($num); + } + + $color = ""; + $matches = array(); + $color_regex = "/^\[(BLACK|BLUE|CYAN|GREEN|MAGENTA|RED|WHITE|YELLOW)\]/i"; + if (preg_match($color_regex,$pattern,$matches)) { + $color = strtolower($matches[1]); + $pattern = preg_replace($color_regex,"",$pattern); + } + + // In Excel formats, "_" is used to add spacing, which we can't do in HTML + $pattern = preg_replace("/_./","",$pattern); + + // Some non-number characters are escaped with \, which we don't need + $pattern = preg_replace("/\\\/","",$pattern); + + // Some non-number strings are quoted, so we'll get rid of the quotes + $pattern = preg_replace("/\"/","",$pattern); + + // TEMPORARY - Convert # to 0 + $pattern = preg_replace("/\#/","0",$pattern); + + // Find out if we need comma formatting + $has_commas = preg_match("/,/",$pattern); + if ($has_commas) { + $pattern = preg_replace("/,/","",$pattern); + } + + // Handle Percentages + if (preg_match("/\d(\%)([^\%]|$)/",$pattern,$matches)) { + $num = $num * 100; + $pattern = preg_replace("/(\d)(\%)([^\%]|$)/","$1%$3",$pattern); + } + + // Handle the number itself + $number_regex = "/(\d+)(\.?)(\d*)/"; + if (preg_match($number_regex,$pattern,$matches)) { + $left = $matches[1]; + $dec = $matches[2]; + $right = $matches[3]; + if ($has_commas) { + $formatted = number_format($num,strlen($right)); + } + else { + $sprintf_pattern = "%1.".strlen($right)."f"; + $formatted = sprintf($sprintf_pattern, $num); + } + $pattern = preg_replace($number_regex, $formatted, $pattern); + } + + return array( + 'string'=>$pattern, + 'formatColor'=>$color + ); + } + + /** + * Constructor + * + * Some basic initialisation + */ + function Spreadsheet_Excel_Reader($file='',$store_extended_info=true,$outputEncoding='') { + $this->_ole =& new OLERead(); + $this->setUTFEncoder('iconv'); + if ($outputEncoding != '') { + $this->setOutputEncoding($outputEncoding); + } + for ($i=1; $i<245; $i++) { + $name = strtolower(( (($i-1)/26>=1)?chr(($i-1)/26+64):'') . chr(($i-1)%26+65)); + $this->colnames[$name] = $i; + $this->colindexes[$i] = $name; + } + $this->store_extended_info = $store_extended_info; + if ($file!="") { + $this->read($file); + } + } + + /** + * Set the encoding method + */ + function setOutputEncoding($encoding) { + $this->_defaultEncoding = $encoding; + } + + /** + * $encoder = 'iconv' or 'mb' + * set iconv if you would like use 'iconv' for encode UTF-16LE to your encoding + * set mb if you would like use 'mb_convert_encoding' for encode UTF-16LE to your encoding + */ + function setUTFEncoder($encoder = 'iconv') { + $this->_encoderFunction = ''; + if ($encoder == 'iconv') { + $this->_encoderFunction = function_exists('iconv') ? 'iconv' : ''; + } elseif ($encoder == 'mb') { + $this->_encoderFunction = function_exists('mb_convert_encoding') ? 'mb_convert_encoding' : ''; + } + } + + function setRowColOffset($iOffset) { + $this->_rowoffset = $iOffset; + $this->_coloffset = $iOffset; + } + + /** + * Set the default number format + */ + function setDefaultFormat($sFormat) { + $this->_defaultFormat = $sFormat; + } + + /** + * Force a column to use a certain format + */ + function setColumnFormat($column, $sFormat) { + $this->_columnsFormat[$column] = $sFormat; + } + + /** + * Read the spreadsheet file using OLE, then parse + */ + function read($sFileName) { + $res = $this->_ole->read($sFileName); + + // oops, something goes wrong (Darko Miljanovic) + if($res === false) { + // check error code + if($this->_ole->error == 1) { + // bad file + die('The filename ' . $sFileName . ' is not readable'); + } + // check other error codes here (eg bad fileformat, etc...) + } + $this->data = $this->_ole->getWorkBook(); + $this->_parse(); + } + + /** + * Parse a workbook + * + * @access private + * @return bool + */ + function _parse() { + $pos = 0; + $data = $this->data; + + $code = v($data,$pos); + $length = v($data,$pos+2); + $version = v($data,$pos+4); + $substreamType = v($data,$pos+6); + + $this->version = $version; + + if (($version != SPREADSHEET_EXCEL_READER_BIFF8) && + ($version != SPREADSHEET_EXCEL_READER_BIFF7)) { + return false; + } + + if ($substreamType != SPREADSHEET_EXCEL_READER_WORKBOOKGLOBALS){ + return false; + } + + $pos += $length + 4; + + $code = v($data,$pos); + $length = v($data,$pos+2); + + while ($code != SPREADSHEET_EXCEL_READER_TYPE_EOF) { + switch ($code) { + case SPREADSHEET_EXCEL_READER_TYPE_SST: + $spos = $pos + 4; + $limitpos = $spos + $length; + $uniqueStrings = $this->_GetInt4d($data, $spos+4); + $spos += 8; + for ($i = 0; $i < $uniqueStrings; $i++) { + // Read in the number of characters + if ($spos == $limitpos) { + $opcode = v($data,$spos); + $conlength = v($data,$spos+2); + if ($opcode != 0x3c) { + return -1; + } + $spos += 4; + $limitpos = $spos + $conlength; + } + $numChars = ord($data[$spos]) | (ord($data[$spos+1]) << 8); + $spos += 2; + $optionFlags = ord($data[$spos]); + $spos++; + $asciiEncoding = (($optionFlags & 0x01) == 0) ; + $extendedString = ( ($optionFlags & 0x04) != 0); + + // See if string contains formatting information + $richString = ( ($optionFlags & 0x08) != 0); + + if ($richString) { + // Read in the crun + $formattingRuns = v($data,$spos); + $spos += 2; + } + + if ($extendedString) { + // Read in cchExtRst + $extendedRunLength = $this->_GetInt4d($data, $spos); + $spos += 4; + } + + $len = ($asciiEncoding)? $numChars : $numChars*2; + if ($spos + $len < $limitpos) { + $retstr = substr($data, $spos, $len); + $spos += $len; + } + else{ + // found countinue + $retstr = substr($data, $spos, $limitpos - $spos); + $bytesRead = $limitpos - $spos; + $charsLeft = $numChars - (($asciiEncoding) ? $bytesRead : ($bytesRead / 2)); + $spos = $limitpos; + + while ($charsLeft > 0){ + $opcode = v($data,$spos); + $conlength = v($data,$spos+2); + if ($opcode != 0x3c) { + return -1; + } + $spos += 4; + $limitpos = $spos + $conlength; + $option = ord($data[$spos]); + $spos += 1; + if ($asciiEncoding && ($option == 0)) { + $len = min($charsLeft, $limitpos - $spos); // min($charsLeft, $conlength); + $retstr .= substr($data, $spos, $len); + $charsLeft -= $len; + $asciiEncoding = true; + } + elseif (!$asciiEncoding && ($option != 0)) { + $len = min($charsLeft * 2, $limitpos - $spos); // min($charsLeft, $conlength); + $retstr .= substr($data, $spos, $len); + $charsLeft -= $len/2; + $asciiEncoding = false; + } + elseif (!$asciiEncoding && ($option == 0)) { + // Bummer - the string starts off as Unicode, but after the + // continuation it is in straightforward ASCII encoding + $len = min($charsLeft, $limitpos - $spos); // min($charsLeft, $conlength); + for ($j = 0; $j < $len; $j++) { + $retstr .= $data[$spos + $j].chr(0); + } + $charsLeft -= $len; + $asciiEncoding = false; + } + else{ + $newstr = ''; + for ($j = 0; $j < strlen($retstr); $j++) { + $newstr = $retstr[$j].chr(0); + } + $retstr = $newstr; + $len = min($charsLeft * 2, $limitpos - $spos); // min($charsLeft, $conlength); + $retstr .= substr($data, $spos, $len); + $charsLeft -= $len/2; + $asciiEncoding = false; + } + $spos += $len; + } + } + $retstr = ($asciiEncoding) ? $retstr : $this->_encodeUTF16($retstr); + + if ($richString){ + $spos += 4 * $formattingRuns; + } + + // For extended strings, skip over the extended string data + if ($extendedString) { + $spos += $extendedRunLength; + } + $this->sst[]=$retstr; + } + break; + case SPREADSHEET_EXCEL_READER_TYPE_FILEPASS: + return false; + break; + case SPREADSHEET_EXCEL_READER_TYPE_NAME: + break; + case SPREADSHEET_EXCEL_READER_TYPE_FORMAT: + $indexCode = v($data,$pos+4); + if ($version == SPREADSHEET_EXCEL_READER_BIFF8) { + $numchars = v($data,$pos+6); + if (ord($data[$pos+8]) == 0){ + $formatString = substr($data, $pos+9, $numchars); + } else { + $formatString = substr($data, $pos+9, $numchars*2); + } + } else { + $numchars = ord($data[$pos+6]); + $formatString = substr($data, $pos+7, $numchars*2); + } + $this->formatRecords[$indexCode] = $formatString; + break; + case SPREADSHEET_EXCEL_READER_TYPE_FONT: + $height = v($data,$pos+4); + $option = v($data,$pos+6); + $color = v($data,$pos+8); + $weight = v($data,$pos+10); + $under = ord($data[$pos+14]); + $font = ""; + // Font name + $numchars = ord($data[$pos+18]); + if ((ord($data[$pos+19]) & 1) == 0){ + $font = substr($data, $pos+20, $numchars); + } else { + $font = substr($data, $pos+20, $numchars*2); + $font = $this->_encodeUTF16($font); + } + $this->fontRecords[] = array( + 'height' => $height / 20, + 'italic' => !!($option & 2), + 'color' => $color, + 'under' => !($under==0), + 'bold' => ($weight==700), + 'font' => $font, + 'raw' => $this->dumpHexData($data, $pos+3, $length) + ); + break; + + case SPREADSHEET_EXCEL_READER_TYPE_PALETTE: + $colors = ord($data[$pos+4]) | ord($data[$pos+5]) << 8; + for ($coli = 0; $coli < $colors; $coli++) { + $colOff = $pos + 2 + ($coli * 4); + $colr = ord($data[$colOff]); + $colg = ord($data[$colOff+1]); + $colb = ord($data[$colOff+2]); + $this->colors[0x07 + $coli] = '#' . $this->myhex($colr) . $this->myhex($colg) . $this->myhex($colb); + } + break; + + case SPREADSHEET_EXCEL_READER_TYPE_XF: + $fontIndexCode = (ord($data[$pos+4]) | ord($data[$pos+5]) << 8) - 1; + $fontIndexCode = max(0,$fontIndexCode); + $indexCode = ord($data[$pos+6]) | ord($data[$pos+7]) << 8; + $alignbit = ord($data[$pos+10]) & 3; + $bgi = (ord($data[$pos+22]) | ord($data[$pos+23]) << 8) & 0x3FFF; + $bgcolor = ($bgi & 0x7F); +// $bgcolor = ($bgi & 0x3f80) >> 7; + $align = ""; + if ($alignbit==3) { $align="right"; } + if ($alignbit==2) { $align="center"; } + + $fillPattern = (ord($data[$pos+21]) & 0xFC) >> 2; + if ($fillPattern == 0) { + $bgcolor = ""; + } + + $xf = array(); + $xf['formatIndex'] = $indexCode; + $xf['align'] = $align; + $xf['fontIndex'] = $fontIndexCode; + $xf['bgColor'] = $bgcolor; + $xf['fillPattern'] = $fillPattern; + + $border = ord($data[$pos+14]) | (ord($data[$pos+15]) << 8) | (ord($data[$pos+16]) << 16) | (ord($data[$pos+17]) << 24); + $xf['borderLeft'] = $this->lineStyles[($border & 0xF)]; + $xf['borderRight'] = $this->lineStyles[($border & 0xF0) >> 4]; + $xf['borderTop'] = $this->lineStyles[($border & 0xF00) >> 8]; + $xf['borderBottom'] = $this->lineStyles[($border & 0xF000) >> 12]; + + $xf['borderLeftColor'] = ($border & 0x7F0000) >> 16; + $xf['borderRightColor'] = ($border & 0x3F800000) >> 23; + $border = (ord($data[$pos+18]) | ord($data[$pos+19]) << 8); + + $xf['borderTopColor'] = ($border & 0x7F); + $xf['borderBottomColor'] = ($border & 0x3F80) >> 7; + + if (array_key_exists($indexCode, $this->dateFormats)) { + $xf['type'] = 'date'; + $xf['format'] = $this->dateFormats[$indexCode]; + if ($align=='') { $xf['align'] = 'right'; } + }elseif (array_key_exists($indexCode, $this->numberFormats)) { + $xf['type'] = 'number'; + $xf['format'] = $this->numberFormats[$indexCode]; + if ($align=='') { $xf['align'] = 'right'; } + }else{ + $isdate = FALSE; + $formatstr = ''; + if ($indexCode > 0){ + if (isset($this->formatRecords[$indexCode])) + $formatstr = $this->formatRecords[$indexCode]; + if ($formatstr!="") { + $tmp = preg_replace("/\;.*/","",$formatstr); + $tmp = preg_replace("/^\[[^\]]*\]/","",$tmp); + if (preg_match("/[^hmsday\/\-:\s\\\,AMP]/i", $tmp) == 0) { // found day and time format + $isdate = TRUE; + $formatstr = $tmp; + $formatstr = str_replace(array('AM/PM','mmmm','mmm'), array('a','F','M'), $formatstr); + // m/mm are used for both minutes and months - oh SNAP! + // This mess tries to fix for that. + // 'm' == minutes only if following h/hh or preceding s/ss + $formatstr = preg_replace("/(h:?)mm?/","$1i", $formatstr); + $formatstr = preg_replace("/mm?(:?s)/","i$1", $formatstr); + // A single 'm' = n in PHP + $formatstr = preg_replace("/(^|[^m])m([^m]|$)/", '$1n$2', $formatstr); + $formatstr = preg_replace("/(^|[^m])m([^m]|$)/", '$1n$2', $formatstr); + // else it's months + $formatstr = str_replace('mm', 'm', $formatstr); + // Convert single 'd' to 'j' + $formatstr = preg_replace("/(^|[^d])d([^d]|$)/", '$1j$2', $formatstr); + $formatstr = str_replace(array('dddd','ddd','dd','yyyy','yy','hh','h'), array('l','D','d','Y','y','H','g'), $formatstr); + $formatstr = preg_replace("/ss?/", 's', $formatstr); + } + } + } + if ($isdate){ + $xf['type'] = 'date'; + $xf['format'] = $formatstr; + if ($align=='') { $xf['align'] = 'right'; } + }else{ + // If the format string has a 0 or # in it, we'll assume it's a number + if (preg_match("/[0#]/", $formatstr)) { + $xf['type'] = 'number'; + if ($align=='') { $xf['align']='right'; } + } + else { + $xf['type'] = 'other'; + } + $xf['format'] = $formatstr; + $xf['code'] = $indexCode; + } + } + $this->xfRecords[] = $xf; + break; + case SPREADSHEET_EXCEL_READER_TYPE_NINETEENFOUR: + $this->nineteenFour = (ord($data[$pos+4]) == 1); + break; + case SPREADSHEET_EXCEL_READER_TYPE_BOUNDSHEET: + $rec_offset = $this->_GetInt4d($data, $pos+4); + $rec_typeFlag = ord($data[$pos+8]); + $rec_visibilityFlag = ord($data[$pos+9]); + $rec_length = ord($data[$pos+10]); + + if ($version == SPREADSHEET_EXCEL_READER_BIFF8){ + $chartype = ord($data[$pos+11]); + if ($chartype == 0){ + $rec_name = substr($data, $pos+12, $rec_length); + } else { + $rec_name = $this->_encodeUTF16(substr($data, $pos+12, $rec_length*2)); + } + }elseif ($version == SPREADSHEET_EXCEL_READER_BIFF7){ + $rec_name = substr($data, $pos+11, $rec_length); + } + $this->boundsheets[] = array('name'=>$rec_name,'offset'=>$rec_offset); + break; + + } + + $pos += $length + 4; + $code = ord($data[$pos]) | ord($data[$pos+1])<<8; + $length = ord($data[$pos+2]) | ord($data[$pos+3])<<8; + } + + foreach ($this->boundsheets as $key=>$val){ + $this->sn = $key; + $this->_parsesheet($val['offset']); + } + return true; + } + + /** + * Parse a worksheet + */ + function _parsesheet($spos) { + $cont = true; + $data = $this->data; + // read BOF + $code = ord($data[$spos]) | ord($data[$spos+1])<<8; + $length = ord($data[$spos+2]) | ord($data[$spos+3])<<8; + + $version = ord($data[$spos + 4]) | ord($data[$spos + 5])<<8; + $substreamType = ord($data[$spos + 6]) | ord($data[$spos + 7])<<8; + + if (($version != SPREADSHEET_EXCEL_READER_BIFF8) && ($version != SPREADSHEET_EXCEL_READER_BIFF7)) { + return -1; + } + + if ($substreamType != SPREADSHEET_EXCEL_READER_WORKSHEET){ + return -2; + } + $spos += $length + 4; + while($cont) { + $lowcode = ord($data[$spos]); + if ($lowcode == SPREADSHEET_EXCEL_READER_TYPE_EOF) break; + $code = $lowcode | ord($data[$spos+1])<<8; + $length = ord($data[$spos+2]) | ord($data[$spos+3])<<8; + $spos += 4; + $this->sheets[$this->sn]['maxrow'] = $this->_rowoffset - 1; + $this->sheets[$this->sn]['maxcol'] = $this->_coloffset - 1; + unset($this->rectype); + switch ($code) { + case SPREADSHEET_EXCEL_READER_TYPE_DIMENSION: + if (!isset($this->numRows)) { + if (($length == 10) || ($version == SPREADSHEET_EXCEL_READER_BIFF7)){ + $this->sheets[$this->sn]['numRows'] = ord($data[$spos+2]) | ord($data[$spos+3]) << 8; + $this->sheets[$this->sn]['numCols'] = ord($data[$spos+6]) | ord($data[$spos+7]) << 8; + } else { + $this->sheets[$this->sn]['numRows'] = ord($data[$spos+4]) | ord($data[$spos+5]) << 8; + $this->sheets[$this->sn]['numCols'] = ord($data[$spos+10]) | ord($data[$spos+11]) << 8; + } + } + break; + case SPREADSHEET_EXCEL_READER_TYPE_MERGEDCELLS: + $cellRanges = ord($data[$spos]) | ord($data[$spos+1])<<8; + for ($i = 0; $i < $cellRanges; $i++) { + $fr = ord($data[$spos + 8*$i + 2]) | ord($data[$spos + 8*$i + 3])<<8; + $lr = ord($data[$spos + 8*$i + 4]) | ord($data[$spos + 8*$i + 5])<<8; + $fc = ord($data[$spos + 8*$i + 6]) | ord($data[$spos + 8*$i + 7])<<8; + $lc = ord($data[$spos + 8*$i + 8]) | ord($data[$spos + 8*$i + 9])<<8; + if ($lr - $fr > 0) { + $this->sheets[$this->sn]['cellsInfo'][$fr+1][$fc+1]['rowspan'] = $lr - $fr + 1; + } + if ($lc - $fc > 0) { + $this->sheets[$this->sn]['cellsInfo'][$fr+1][$fc+1]['colspan'] = $lc - $fc + 1; + } + } + break; + case SPREADSHEET_EXCEL_READER_TYPE_RK: + case SPREADSHEET_EXCEL_READER_TYPE_RK2: + $row = ord($data[$spos]) | ord($data[$spos+1])<<8; + $column = ord($data[$spos+2]) | ord($data[$spos+3])<<8; + $rknum = $this->_GetInt4d($data, $spos + 6); + $numValue = $this->_GetIEEE754($rknum); + $info = $this->_getCellDetails($spos,$numValue,$column); + $this->addcell($row, $column, $info['string'],$info); + break; + case SPREADSHEET_EXCEL_READER_TYPE_LABELSST: + $row = ord($data[$spos]) | ord($data[$spos+1])<<8; + $column = ord($data[$spos+2]) | ord($data[$spos+3])<<8; + $xfindex = ord($data[$spos+4]) | ord($data[$spos+5])<<8; + $index = $this->_GetInt4d($data, $spos + 6); + $this->addcell($row, $column, $this->sst[$index], array('xfIndex'=>$xfindex) ); + break; + case SPREADSHEET_EXCEL_READER_TYPE_MULRK: + $row = ord($data[$spos]) | ord($data[$spos+1])<<8; + $colFirst = ord($data[$spos+2]) | ord($data[$spos+3])<<8; + $colLast = ord($data[$spos + $length - 2]) | ord($data[$spos + $length - 1])<<8; + $columns = $colLast - $colFirst + 1; + $tmppos = $spos+4; + for ($i = 0; $i < $columns; $i++) { + $numValue = $this->_GetIEEE754($this->_GetInt4d($data, $tmppos + 2)); + $info = $this->_getCellDetails($tmppos-4,$numValue,$colFirst + $i + 1); + $tmppos += 6; + $this->addcell($row, $colFirst + $i, $info['string'], $info); + } + break; + case SPREADSHEET_EXCEL_READER_TYPE_NUMBER: + $row = ord($data[$spos]) | ord($data[$spos+1])<<8; + $column = ord($data[$spos+2]) | ord($data[$spos+3])<<8; + $tmp = unpack("ddouble", substr($data, $spos + 6, 8)); // It machine machine dependent + if ($this->isDate($spos)) { + $numValue = $tmp['double']; + } + else { + $numValue = $this->createNumber($spos); + } + $info = $this->_getCellDetails($spos,$numValue,$column); + $this->addcell($row, $column, $info['string'], $info); + break; + + case SPREADSHEET_EXCEL_READER_TYPE_FORMULA: + case SPREADSHEET_EXCEL_READER_TYPE_FORMULA2: + $row = ord($data[$spos]) | ord($data[$spos+1])<<8; + $column = ord($data[$spos+2]) | ord($data[$spos+3])<<8; + if ((ord($data[$spos+6])==0) && (ord($data[$spos+12])==255) && (ord($data[$spos+13])==255)) { + //String formula. Result follows in a STRING record + // This row/col are stored to be referenced in that record + // http://code.google.com/p/php-excel-reader/issues/detail?id=4 + $previousRow = $row; + $previousCol = $column; + } elseif ((ord($data[$spos+6])==1) && (ord($data[$spos+12])==255) && (ord($data[$spos+13])==255)) { + //Boolean formula. Result is in +2; 0=false,1=true + // http://code.google.com/p/php-excel-reader/issues/detail?id=4 + if (ord($this->data[$spos+8])==1) { + $this->addcell($row, $column, "TRUE"); + } else { + $this->addcell($row, $column, "FALSE"); + } + } elseif ((ord($data[$spos+6])==2) && (ord($data[$spos+12])==255) && (ord($data[$spos+13])==255)) { + //Error formula. Error code is in +2; + } elseif ((ord($data[$spos+6])==3) && (ord($data[$spos+12])==255) && (ord($data[$spos+13])==255)) { + //Formula result is a null string. + $this->addcell($row, $column, ''); + } else { + // result is a number, so first 14 bytes are just like a _NUMBER record + $tmp = unpack("ddouble", substr($data, $spos + 6, 8)); // It machine machine dependent + if ($this->isDate($spos)) { + $numValue = $tmp['double']; + } + else { + $numValue = $this->createNumber($spos); + } + $info = $this->_getCellDetails($spos,$numValue,$column); + $this->addcell($row, $column, $info['string'], $info); + } + break; + case SPREADSHEET_EXCEL_READER_TYPE_BOOLERR: + $row = ord($data[$spos]) | ord($data[$spos+1])<<8; + $column = ord($data[$spos+2]) | ord($data[$spos+3])<<8; + $string = ord($data[$spos+6]); + $this->addcell($row, $column, $string); + break; + case SPREADSHEET_EXCEL_READER_TYPE_STRING: + // http://code.google.com/p/php-excel-reader/issues/detail?id=4 + if ($version == SPREADSHEET_EXCEL_READER_BIFF8){ + // Unicode 16 string, like an SST record + $xpos = $spos; + $numChars =ord($data[$xpos]) | (ord($data[$xpos+1]) << 8); + $xpos += 2; + $optionFlags =ord($data[$xpos]); + $xpos++; + $asciiEncoding = (($optionFlags &0x01) == 0) ; + $extendedString = (($optionFlags & 0x04) != 0); + // See if string contains formatting information + $richString = (($optionFlags & 0x08) != 0); + if ($richString) { + // Read in the crun + $formattingRuns =ord($data[$xpos]) | (ord($data[$xpos+1]) << 8); + $xpos += 2; + } + if ($extendedString) { + // Read in cchExtRst + $extendedRunLength =$this->_GetInt4d($this->data, $xpos); + $xpos += 4; + } + $len = ($asciiEncoding)?$numChars : $numChars*2; + $retstr =substr($data, $xpos, $len); + $xpos += $len; + $retstr = ($asciiEncoding)? $retstr : $this->_encodeUTF16($retstr); + } + elseif ($version == SPREADSHEET_EXCEL_READER_BIFF7){ + // Simple byte string + $xpos = $spos; + $numChars =ord($data[$xpos]) | (ord($data[$xpos+1]) << 8); + $xpos += 2; + $retstr =substr($data, $xpos, $numChars); + } + $this->addcell($previousRow, $previousCol, $retstr); + break; + case SPREADSHEET_EXCEL_READER_TYPE_ROW: + $row = ord($data[$spos]) | ord($data[$spos+1])<<8; + $rowInfo = ord($data[$spos + 6]) | ((ord($data[$spos+7]) << 8) & 0x7FFF); + if (($rowInfo & 0x8000) > 0) { + $rowHeight = -1; + } else { + $rowHeight = $rowInfo & 0x7FFF; + } + $rowHidden = (ord($data[$spos + 12]) & 0x20) >> 5; + $this->rowInfo[$this->sn][$row+1] = Array('height' => $rowHeight / 20, 'hidden'=>$rowHidden ); + break; + case SPREADSHEET_EXCEL_READER_TYPE_DBCELL: + break; + case SPREADSHEET_EXCEL_READER_TYPE_MULBLANK: + $row = ord($data[$spos]) | ord($data[$spos+1])<<8; + $column = ord($data[$spos+2]) | ord($data[$spos+3])<<8; + $cols = ($length / 2) - 3; + for ($c = 0; $c < $cols; $c++) { + $xfindex = ord($data[$spos + 4 + ($c * 2)]) | ord($data[$spos + 5 + ($c * 2)])<<8; + $this->addcell($row, $column + $c, "", array('xfIndex'=>$xfindex)); + } + break; + case SPREADSHEET_EXCEL_READER_TYPE_LABEL: + $row = ord($data[$spos]) | ord($data[$spos+1])<<8; + $column = ord($data[$spos+2]) | ord($data[$spos+3])<<8; + $this->addcell($row, $column, substr($data, $spos + 8, ord($data[$spos + 6]) | ord($data[$spos + 7])<<8)); + break; + case SPREADSHEET_EXCEL_READER_TYPE_EOF: + $cont = false; + break; + case SPREADSHEET_EXCEL_READER_TYPE_HYPER: + // Only handle hyperlinks to a URL + $row = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8; + $row2 = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8; + $column = ord($this->data[$spos+4]) | ord($this->data[$spos+5])<<8; + $column2 = ord($this->data[$spos+6]) | ord($this->data[$spos+7])<<8; + $linkdata = Array(); + $flags = ord($this->data[$spos + 28]); + $udesc = ""; + $ulink = ""; + $uloc = 32; + $linkdata['flags'] = $flags; + if (($flags & 1) > 0 ) { // is a type we understand + // is there a description ? + if (($flags & 0x14) == 0x14 ) { // has a description + $uloc += 4; + $descLen = ord($this->data[$spos + 32]) | ord($this->data[$spos + 33]) << 8; + $udesc = substr($this->data, $spos + $uloc, $descLen * 2); + $uloc += 2 * $descLen; + } + $ulink = $this->read16bitstring($this->data, $spos + $uloc + 20); + if ($udesc == "") { + $udesc = $ulink; + } + } + $linkdata['desc'] = $udesc; + $linkdata['link'] = $this->_encodeUTF16($ulink); + for ($r=$row; $r<=$row2; $r++) { + for ($c=$column; $c<=$column2; $c++) { + $this->sheets[$this->sn]['cellsInfo'][$r+1][$c+1]['hyperlink'] = $linkdata; + } + } + break; + case SPREADSHEET_EXCEL_READER_TYPE_DEFCOLWIDTH: + $this->defaultColWidth = ord($data[$spos+4]) | ord($data[$spos+5]) << 8; + break; + case SPREADSHEET_EXCEL_READER_TYPE_STANDARDWIDTH: + $this->standardColWidth = ord($data[$spos+4]) | ord($data[$spos+5]) << 8; + break; + case SPREADSHEET_EXCEL_READER_TYPE_COLINFO: + $colfrom = ord($data[$spos+0]) | ord($data[$spos+1]) << 8; + $colto = ord($data[$spos+2]) | ord($data[$spos+3]) << 8; + $cw = ord($data[$spos+4]) | ord($data[$spos+5]) << 8; + $cxf = ord($data[$spos+6]) | ord($data[$spos+7]) << 8; + $co = ord($data[$spos+8]); + for ($coli = $colfrom; $coli <= $colto; $coli++) { + $this->colInfo[$this->sn][$coli+1] = Array('width' => $cw, 'xf' => $cxf, 'hidden' => ($co & 0x01), 'collapsed' => ($co & 0x1000) >> 12); + } + break; + + default: + break; + } + $spos += $length; + } + + if (!isset($this->sheets[$this->sn]['numRows'])) + $this->sheets[$this->sn]['numRows'] = $this->sheets[$this->sn]['maxrow']; + if (!isset($this->sheets[$this->sn]['numCols'])) + $this->sheets[$this->sn]['numCols'] = $this->sheets[$this->sn]['maxcol']; + } + + function isDate($spos) { + $xfindex = ord($this->data[$spos+4]) | ord($this->data[$spos+5]) << 8; + return ($this->xfRecords[$xfindex]['type'] == 'date'); + } + + // Get the details for a particular cell + function _getCellDetails($spos,$numValue,$column) { + $xfindex = ord($this->data[$spos+4]) | ord($this->data[$spos+5]) << 8; + $xfrecord = $this->xfRecords[$xfindex]; + $type = $xfrecord['type']; + + $format = $xfrecord['format']; + $formatIndex = $xfrecord['formatIndex']; + $fontIndex = $xfrecord['fontIndex']; + $formatColor = ""; + $rectype = ''; + $string = ''; + $raw = ''; + + if (isset($this->_columnsFormat[$column + 1])){ + $format = $this->_columnsFormat[$column + 1]; + } + + if ($type == 'date') { + // See http://groups.google.com/group/php-excel-reader-discuss/browse_frm/thread/9c3f9790d12d8e10/f2045c2369ac79de + $rectype = 'date'; + // Convert numeric value into a date + $utcDays = floor($numValue - ($this->nineteenFour ? SPREADSHEET_EXCEL_READER_UTCOFFSETDAYS1904 : SPREADSHEET_EXCEL_READER_UTCOFFSETDAYS)); + $utcValue = ($utcDays) * SPREADSHEET_EXCEL_READER_MSINADAY; + $dateinfo = gmgetdate($utcValue); + + $raw = $numValue; + $fractionalDay = $numValue - floor($numValue) + .0000001; // The .0000001 is to fix for php/excel fractional diffs + + $totalseconds = floor(SPREADSHEET_EXCEL_READER_MSINADAY * $fractionalDay); + $secs = $totalseconds % 60; + $totalseconds -= $secs; + $hours = floor($totalseconds / (60 * 60)); + $mins = floor($totalseconds / 60) % 60; + $string = date ($format, mktime($hours, $mins, $secs, $dateinfo["mon"], $dateinfo["mday"], $dateinfo["year"])); + } else if ($type == 'number') { + $rectype = 'number'; + $formatted = $this->_format_value($format, $numValue, $formatIndex); + $string = $formatted['string']; + $formatColor = $formatted['formatColor']; + $raw = $numValue; + } else{ + if ($format=="") { + $format = $this->_defaultFormat; + } + $rectype = 'unknown'; + $formatted = $this->_format_value($format, $numValue, $formatIndex); + $string = $formatted['string']; + $formatColor = $formatted['formatColor']; + $raw = $numValue; + } + + return array( + 'string'=>$string, + 'raw'=>$raw, + 'rectype'=>$rectype, + 'format'=>$format, + 'formatIndex'=>$formatIndex, + 'fontIndex'=>$fontIndex, + 'formatColor'=>$formatColor, + 'xfIndex'=>$xfindex + ); + + } + + + function createNumber($spos) { + $rknumhigh = $this->_GetInt4d($this->data, $spos + 10); + $rknumlow = $this->_GetInt4d($this->data, $spos + 6); + $sign = ($rknumhigh & 0x80000000) >> 31; + $exp = ($rknumhigh & 0x7ff00000) >> 20; + $mantissa = (0x100000 | ($rknumhigh & 0x000fffff)); + $mantissalow1 = ($rknumlow & 0x80000000) >> 31; + $mantissalow2 = ($rknumlow & 0x7fffffff); + $value = $mantissa / pow( 2 , (20- ($exp - 1023))); + if ($mantissalow1 != 0) $value += 1 / pow (2 , (21 - ($exp - 1023))); + $value += $mantissalow2 / pow (2 , (52 - ($exp - 1023))); + if ($sign) {$value = -1 * $value;} + return $value; + } + + function addcell($row, $col, $string, $info=null) { + $this->sheets[$this->sn]['maxrow'] = max($this->sheets[$this->sn]['maxrow'], $row + $this->_rowoffset); + $this->sheets[$this->sn]['maxcol'] = max($this->sheets[$this->sn]['maxcol'], $col + $this->_coloffset); + $this->sheets[$this->sn]['cells'][$row + $this->_rowoffset][$col + $this->_coloffset] = $string; + if ($this->store_extended_info && $info) { + foreach ($info as $key=>$val) { + $this->sheets[$this->sn]['cellsInfo'][$row + $this->_rowoffset][$col + $this->_coloffset][$key] = $val; + } + } + } + + + function _GetIEEE754($rknum) { + if (($rknum & 0x02) != 0) { + $value = $rknum >> 2; + } else { + //mmp + // I got my info on IEEE754 encoding from + // http://research.microsoft.com/~hollasch/cgindex/coding/ieeefloat.html + // The RK format calls for using only the most significant 30 bits of the + // 64 bit floating point value. The other 34 bits are assumed to be 0 + // So, we use the upper 30 bits of $rknum as follows... + $sign = ($rknum & 0x80000000) >> 31; + $exp = ($rknum & 0x7ff00000) >> 20; + $mantissa = (0x100000 | ($rknum & 0x000ffffc)); + $value = $mantissa / pow( 2 , (20- ($exp - 1023))); + if ($sign) { + $value = -1 * $value; + } + //end of changes by mmp + } + if (($rknum & 0x01) != 0) { + $value /= 100; + } + return $value; + } + + function _encodeUTF16($string) { + $result = $string; + if ($this->_defaultEncoding){ + switch ($this->_encoderFunction){ + case 'iconv' : $result = iconv('UTF-16LE', $this->_defaultEncoding, $string); + break; + case 'mb_convert_encoding' : $result = mb_convert_encoding($string, $this->_defaultEncoding, 'UTF-16LE' ); + break; + } + } + return $result; + } + + function _GetInt4d($data, $pos) { + $value = ord($data[$pos]) | (ord($data[$pos+1]) << 8) | (ord($data[$pos+2]) << 16) | (ord($data[$pos+3]) << 24); + if ($value>=4294967294) { + $value=-2; + } + return $value; + } + +} + +?> diff --git a/import/index.php b/import/index.php new file mode 100644 index 0000000..4ca25aa --- /dev/null +++ b/import/index.php @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/import/lang/index.php b/import/lang/index.php new file mode 100644 index 0000000..4ca25aa --- /dev/null +++ b/import/lang/index.php @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/import/lang/ru.txt b/import/lang/ru.txt new file mode 100644 index 0000000..d30e0e6 --- /dev/null +++ b/import/lang/ru.txt @@ -0,0 +1,63 @@ +IMPORT_MODULE_NAME = "Импорт" +IMPORT_MODULE = "Управление модулем" +IMPORT_EDIT = "Редактирование модуля  > Импорт" +IMPORT_EDIT_TIP = "В данном разделе предоставлены все импорты." +IMPORT_ID = "Id" +IMPORT_NAME = "Имя импорта" +IMPORT_LAST_UPDATE = "Последнее изменение" +IMPORT_ACTIONS = "Действия" +IMPORT_DO_HINT = "Запустить импорт" +IMPORT_NO_ITEMS = "В настоящий момент нет данных" +IMPORT_BUTTON_SAVE = "Сохранить изменения" +IMPORT_CTRLS = "Сохранить и продолжить (CTRL+S)" +IMPORT_BUTTON_ADD = "Добавить импорт" +IMPORT_H_ADD = "Добавление нового импорта" +IMPORT_H_EDIT = "Редактирование импорта" +IMPORT_MAIN_FIELDS = "Основные поля документа" +IMPORT_RUB_FIELDS = "Поля рубрики" +IMPORT_F = "Поле" +IMPORT_TPL = "Шаблон импорта из файла" +IMPORT_TAGS = "Теги:" +IMPORT_KEYS_INFO = "Ключевое поле. Например, Вы сделали импорт и после изменения файла хотите повторить операцию. Если у какого-либо документа ключевое поле совпало, то он будет изменён, в противном случае - создан заново." +IMPORT_ACTIVE_INFO = "Импортировать поле?" +IMPORT_CRITICAL_INFO = "Обязательное поле - если не будет значения запись не импортируется" +IMPORT_INNAME = "Введите название импорт" +IMPORT_ENTER_NAME = "Пожалуйста, укажите название импорта" +IMPORT_INFO_ADD = "Здесь вы можете добавить новый импорт" +IMPORT_INFO_EDIT = "Здесь вы можете изменить выбранный вами импорт" +IMPORT_SAVE = "Сохранить" +IMPORT_CREATE = "Создать и продолжить" +IMPORT_INTEXT = "Системный блок" +IMPORT_ADD = "Добавить новый импорт" +IMPORT_EDIT_HINT = "Редактировать импорт" +IMPORT_DELETE_HINT = "Удалить импорт" +IMPORT_DEL_HINT = "Вы уверены, что хотите удалить импорт ?" +IMPORT_LIST_LINK = "Список импортов" +IMPORT_FILE = "Файл импорта по умолчанию" +IMPORT_DELETE_DOCS = "Перед импортом пометить все документы рубрики как неактивные" +IMPORT_DOCS_CREATE = "Создавать новый документ, если не удаётся найти совпадение по ключевому полю" +IMPORT_CHECK_FILE = "Отслеживать изменения файла и автоматически делать импорт" +IMPORT_PARSER = "Используемый парсер" +IMPORT_RUBRICS = "Рубрика" + +IMPORT_Id = "Id документа" +IMPORT_document_title = "Название документа" +IMPORT_document_alias = "Псевдоним документа (alias)" +IMPORT_document_meta_keywords = "Ключевые слова (meta keywords)" +IMPORT_document_meta_description = "Описание страницы (meta description)" +IMPORT_document_meta_robots = "Тип индексирования страницы (meta robots)" +IMPORT_document_published = "Начало публикации" +IMPORT_document_in_search = "Разрешить поиск по документу (0 или 1)" +IMPORT_document_status = "Статус документа (неактивен = 0 или
активен = 1)" +IMPORT_document_linked_navi_id = "Связать с пунктом меню (navi_id)" +IMPORT_document_breadcrum_title = "Название ссылки для хлебных крошек" +IMPORT_document_parent = "Связать с документом (doc_id)" +IMPORT_document_count_view = "Количество просмотров" + +IMPORT_TAG_DATE = "Тег текущей даты. Формат вывода: yyyy-mm-dd" +IMPORT_TAG_1 = "Значение из элемента массива, где XXX - имя ключа" +IMPORT_TAG_2 = "Значение из многомерного массива; уровень вложенности через :" +IMPORT_SAVED = "Импорт успешно сохранён" +IMPORT_FAILED = "Запрос не выполнен" +IMPORT_TAGS_TITLE = "Теги файла:" +IMPORT_REFRESH_TAGS = "Обновить список тегов" \ No newline at end of file diff --git a/import/module.php b/import/module.php new file mode 100644 index 0000000..4a01d84 --- /dev/null +++ b/import/module.php @@ -0,0 +1,110 @@ +Query($sql); + $rows = array(); + + while ($row = $res->FetchAssocArray()) + { + $rows[] = $row; + + if(file_exists(BASE_DIR . $row["import_default_file"])) + { + if(filemtime(BASE_DIR . $row["import_default_file"]) > $row['import_last_update']) + { + if (!(is_file(BASE_DIR . '/modules/import/class.import.php') && + include_once(BASE_DIR . '/modules/import/class.import.php'))) module_error(); + + $import = new import; + $import -> DoImport($row['Id']); + } + } + } +} + +if (defined('ACP') && $_REQUEST['moduleaction']) +{ + if (! (is_file(BASE_DIR . '/modules/import/class.import.php'))) + module_error(); + + include_once(BASE_DIR . '/modules/import/class.import.php'); + + $import = new import; + + $tpl_dir = BASE_DIR . '/modules/import/templates/'; + $lang_file = BASE_DIR . '/modules/import/lang/' . $_SESSION['user_language'] . '.txt'; + + $AVE_Template->config_load($lang_file); + + switch ($_REQUEST['moduleaction']) + { + case '1': + $import->importList($tpl_dir); + break; + + case 'del': + $import->importDelete($_REQUEST['id']); + break; + + case 'edit': + $import->importEdit(isset($_REQUEST['id']) ? $_REQUEST['id'] : null, $tpl_dir); + break; + + case 'saveedit': + $import->importSave(isset($_REQUEST['id']) ? $_REQUEST['id'] : null); + break; + + case 'do': + $import->DoImport($_REQUEST['id'], false); + break; + + case 'tags': + $import->DoImport(isset($_REQUEST['id']) ? $_REQUEST['id'] : null, true); + break; + } +} +?> \ No newline at end of file diff --git a/import/sql.php b/import/sql.php new file mode 100644 index 0000000..5019484 --- /dev/null +++ b/import/sql.php @@ -0,0 +1,47 @@ + \ No newline at end of file diff --git a/import/templates/admin_edit.tpl b/import/templates/admin_edit.tpl new file mode 100644 index 0000000..3357836 --- /dev/null +++ b/import/templates/admin_edit.tpl @@ -0,0 +1,274 @@ +
+
{if $smarty.request.id == ''}{#IMPORT_H_ADD#}{else}{#IMPORT_H_EDIT#}{/if}
+
+ +
+
+ {#IMPORT_INFO_EDIT#} +
+
+ + + +
+
+
+
{#IMPORT_H_EDIT#}
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{#IMPORT_NAME#} +
+ +
+
{#IMPORT_RUBRICS#} + +
{#IMPORT_PARSER#} + +
{#IMPORT_DELETE_DOCS#}
{#IMPORT_DOCS_CREATE#}
{#IMPORT_FILE#}
+ +
{#IMPORT_CHECK_FILE#}
+ + {if $smarty.request.id != ''} + + + + + {/if} +
+
+ + {if $smarty.request.id != ''} +
+
+
+
{#IMPORT_MAIN_FIELDS#}
+
+ + + + + + + + + + + + {foreach from=$data.fields.header key=k item=v} + + + + + + + + {/foreach} + +
+
+ +
+
+
+ +
+
+
+ +
+
{#IMPORT_F#}{#IMPORT_TPL#}
+ + + {$v[1]}
+
+
+
+
{#IMPORT_RUB_FIELDS#}
+
+ + + + + + + + + + + + {foreach from=$data.fields.body key=k item=v} + + + + + + + + {/foreach} + + + + +
+
+ +
+
+
+ +
+
+
+ +
+
{#IMPORT_F#}{#IMPORT_TPL#}
+ + + {$v[1]}
+ + + + + +
+
+
+
+
+
{#IMPORT_TAGS#}
+
+ + + + + {if $data.tags} + + + + + {foreach from=$data.tags key=k item=v} + + + + {/foreach} + {/if} +
+
+ + + +
+
+ {#IMPORT_TAGS_TITLE#} +
+
+
+
+ {/if} +
+ + \ No newline at end of file diff --git a/import/templates/admin_list.tpl b/import/templates/admin_list.tpl new file mode 100644 index 0000000..640f469 --- /dev/null +++ b/import/templates/admin_list.tpl @@ -0,0 +1,95 @@ + + +
+
{#IMPORT_MODULE_NAME#}
+
+
+
+ {#IMPORT_EDIT_TIP#} +
+
+ +
+
+
{#IMPORT_MODULE_NAME#}
+ +
+ + + + + + + + + + + + + {foreach from=$imports item=import} + + + + + + + + + + + {/foreach} + {if ! $imports} + + + + {/if} + +
{#IMPORT_ID#}{#IMPORT_NAME#}{#IMPORT_RUBRICS#}{#IMPORT_PARSER#}{#IMPORT_LAST_UPDATE#}{#IMPORT_ACTIONS#}
{$import->id} {$import->import_name|escape} + {foreach from=$rubs item=rub} + {if $rub->Id==$import->import_rub} + {$rub->rubric_title} + {/if} + {/foreach} + {$import->import_parser}{$import->import_last_update|date_format:"%Y-%m-%d %H:%M"}
    +
  • {#IMPORT_NO_ITEMS#}
  • +
+
+{if $page_nav} + +{/if} \ No newline at end of file diff --git a/import/templates/index.php b/import/templates/index.php new file mode 100644 index 0000000..4ca25aa --- /dev/null +++ b/import/templates/index.php @@ -0,0 +1,4 @@ + \ No newline at end of file