From 732cb11e0cd987574e597d9b04def0ff02c3bf03 Mon Sep 17 00:00:00 2001 From: Mark Baker Date: Sun, 17 Nov 2013 00:11:45 +0000 Subject: [PATCH] Feature: (LWol) Work Item GH-252 - Adding support for macros, Ribbon in Excel 2007 --- Classes/PHPExcel.php | 253 +++++++++++++++++- Classes/PHPExcel/Reader/Excel2007.php | 71 ++++- Classes/PHPExcel/Worksheet.php | 102 +++++++ Classes/PHPExcel/Writer/Excel2007.php | 31 +++ .../Writer/Excel2007/ContentTypes.php | 34 ++- Classes/PHPExcel/Writer/Excel2007/Rels.php | 20 ++ .../PHPExcel/Writer/Excel2007/RelsRibbon.php | 77 ++++++ Classes/PHPExcel/Writer/Excel2007/RelsVBA.php | 72 +++++ .../PHPExcel/Writer/Excel2007/Worksheet.php | 6 + changelog.txt | 1 + 10 files changed, 652 insertions(+), 15 deletions(-) create mode 100644 Classes/PHPExcel/Writer/Excel2007/RelsRibbon.php create mode 100644 Classes/PHPExcel/Writer/Excel2007/RelsVBA.php diff --git a/Classes/PHPExcel.php b/Classes/PHPExcel.php index 7bd02434..c71ee5f6 100644 --- a/Classes/PHPExcel.php +++ b/Classes/PHPExcel.php @@ -112,18 +112,257 @@ class PHPExcel */ private $_cellStyleXfCollection = array(); - /** - * Create a new PHPExcel with one Worksheet + /** + * _hasMacros : this workbook have macros ? + * + * @var bool + */ + private $_hasMacros = FALSE; + + /** + * _macrosCode : all macros code (the vbaProject.bin file, this include form, code, etc.), NULL if no macro + * + * @var binary + */ + private $_macrosCode=NULL; + /** + * _macrosCertificate : if macros are signed, contains vbaProjectSignature.bin file, NULL if not signed + * + * @var binary + */ + private $_macrosCertificate=NULL; + + /** + * _ribbonXMLData : NULL if workbook is'nt Excel 2007 or not contain a customized UI + * + * @var NULL|string + */ + private $_ribbonXMLData=NULL; + + /** + * _ribbonBinObjects : NULL if workbook is'nt Excel 2007 or not contain embedded objects (picture(s)) for Ribbon Elements + * ignored if $_ribbonXMLData is null + * + * @var NULL|array + */ + private $_ribbonBinObjects=NULL; + + /** + * The workbook has macros ? + * + * @return true if workbook has macros, false if not + */ + public function hasMacros(){ + return $this->_hasMacros; + } + + /** + * Define if a workbook has macros + * + * @param true|false + */ + public function setHasMacros($hasMacros=false){ + $this->_hasMacros=(bool)$hasMacros; + } + + /** + * Set the macros code + * + * @param binary string|null + */ + public function setMacrosCode($MacrosCode){ + $this->_macrosCode=$MacrosCode; + $this->setHasMacros(!is_null($MacrosCode)); + } + + /** + * Return the macros code + * + * @return binary|null + */ + public function getMacrosCode(){ + return $this->_macrosCode; + } + + /** + * Set the macros certificate + * + * @param binary|null + */ + public function setMacrosCertificate($Certificate=NULL){ + $this->_macrosCertificate=$Certificate; + } + + /** + * Is the project signed ? + * + * @return true|false + */ + public function hasMacrosCertificate(){ + return !is_null($this->_macrosCertificate); + } + + /** + * Return the macros certificate + * + * @return binary|null + */ + public function getMacrosCertificate(){ + return $this->_macrosCertificate; + } + + /** + * Remove all macros, certificate from spreadsheet + * + * @param none + * @return void + */ + public function discardMacros(){ + $this->_hasMacros=false; + $this->_macrosCode=NULL; + $this->_macrosCertificate=NULL; + } + + /** + * set ribbon XML data + * + */ + public function setRibbonXMLData($Target=NULL, $XMLData=NULL){ + if(!is_null($Target) && !is_null($XMLData)){ + $this->_ribbonXMLData=array('target'=>$Target, 'data'=>$XMLData); + }else{ + $this->_ribbonXMLData=NULL; + } + } + + /** + * retrieve ribbon XML Data + * + * return string|null|array + */ + public function getRibbonXMLData($What='all'){//we need some constants here... + $ReturnData=NULL; + $What=strtolower($What); + switch($What){ + case 'all': + $ReturnData=$this->_ribbonXMLData; + break; + case 'target': + case 'data': + if(is_array($this->_ribbonXMLData) && array_key_exists($What,$this->_ribbonXMLData)){ + $ReturnData=$this->_ribbonXMLData[$What]; + }//else $ReturnData stay at null + break; + }//default: $ReturnData at null + return $ReturnData; + } + + /** + * store binaries ribbon objects (pictures) + * + */ + public function setRibbonBinObjects($BinObjectsNames=NULL, $BinObjectsData=NULL){ + if(!is_null($BinObjectsNames) && !is_null($BinObjectsData)){ + $this->_ribbonBinObjects=array('names'=>$BinObjectsNames, 'data'=>$BinObjectsData); + }else{ + $this->_ribbonBinObjects=NULL; + } + } + /** + * return the extension of a filename. Internal use for a array_map callback (php<5.3 don't like lambda function) + * + */ + private function _getExtensionOnly($ThePath){ + return pathinfo($ThePath, PATHINFO_EXTENSION); + } + + /** + * retrieve Binaries Ribbon Objects + * + */ + public function getRibbonBinObjects($What='all'){ + $ReturnData=NULL; + $What=strtolower($What); + switch($What){ + case 'all': + return $this->_ribbonBinObjects; + break; + case 'names': + case 'data': + if(is_array($this->_ribbonBinObjects) && array_key_exists($What, $this->_ribbonBinObjects)){ + $ReturnData=$this->_ribbonBinObjects[$What]; + } + break; + case 'types': + if(is_array($this->_ribbonBinObjects) && array_key_exists('data', $this->_ribbonBinObjects) && is_array($this->_ribbonBinObjects['data'])){ + $tmpTypes=array_keys($this->_ribbonBinObjects['data']); + $ReturnData=array_unique(array_map(array($this,'_getExtensionOnly'), $tmpTypes)); + }else + $ReturnData=array();//the caller want an array... not null if empty + break; + } + return $ReturnData; + } + + /** + * This workbook have a custom UI ? + * + * @return true|false + */ + public function hasRibbon(){ + return !is_null($this->_ribbonXMLData); + } + + /** + * This workbook have additionnal object for the ribbon ? + * + * @return true|false + */ + public function hasRibbonBinObjects(){ + return !is_null($this->_ribbonBinObjects); + } + + /** + * Check if a sheet with a specified code name already exists + * + * @param string $pSheetCodeName Name of the worksheet to check + * @return boolean */ - public function __construct() + public function sheetCodeNameExists($pSheetCodeName) { + return ($this->getSheetByCodeName($pSheetCodeName) !== NULL); + } + + /** + * Get sheet by code name. Warning : sheet don't have always a code name ! + * + * @param string $pName Sheet name + * @return PHPExcel_Worksheet + */ + public function getSheetByCodeName($pName = '') + { + $worksheetCount = count($this->_workSheetCollection); + for ($i = 0; $i < $worksheetCount; ++$i) { + if ($this->_workSheetCollection[$i]->getCodeName() == $pName) { + return $this->_workSheetCollection[$i]; + } + } + + return null; + } + + /** + * Create a new PHPExcel with one Worksheet + */ + public function __construct() + { $this->_uniqueID = uniqid(); $this->_calculationEngine = PHPExcel_Calculation::getInstance($this); - // Initialise worksheet collection and add one worksheet - $this->_workSheetCollection = array(); - $this->_workSheetCollection[] = new PHPExcel_Worksheet($this); - $this->_activeSheetIndex = 0; + // Initialise worksheet collection and add one worksheet + $this->_workSheetCollection = array(); + $this->_workSheetCollection[] = new PHPExcel_Worksheet($this); + $this->_activeSheetIndex = 0; // Create document properties $this->_properties = new PHPExcel_DocumentProperties(); diff --git a/Classes/PHPExcel/Reader/Excel2007.php b/Classes/PHPExcel/Reader/Excel2007.php index 7ae0a992..8f58279b 100644 --- a/Classes/PHPExcel/Reader/Excel2007.php +++ b/Classes/PHPExcel/Reader/Excel2007.php @@ -440,7 +440,13 @@ class PHPExcel_Reader_Excel2007 extends PHPExcel_Reader_Abstract implements PHPE } } break; - + //Ribbon + case "http://schemas.microsoft.com/office/2006/relationships/ui/extensibility": + $customUI = $rel['Target']; + if(!is_null($customUI)){ + $this->_readRibbon($excel, $customUI, $zip); + } + break; case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument": $dir = dirname($rel["Target"]); $relsWorkbook = simplexml_load_string($this->_getFromZipArchive($zip, "$dir/_rels/" . basename($rel["Target"]) . ".rels")); //~ http://schemas.openxmlformats.org/package/2006/relationships"); @@ -460,12 +466,30 @@ class PHPExcel_Reader_Excel2007 extends PHPExcel_Reader_Abstract implements PHPE } $worksheets = array(); + $macros = $customUI = NULL; foreach ($relsWorkbook->Relationship as $ele) { - if ($ele["Type"] == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet") { + switch($ele['Type']){ + case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet": $worksheets[(string) $ele["Id"]] = $ele["Target"]; + break; + // a vbaProject ? (: some macros) + case "http://schemas.microsoft.com/office/2006/relationships/vbaProject": + $macros = $ele["Target"]; + break; } } + if(!is_null($macros)){ + $macrosCode = $this->_getFromZipArchive($zip, 'xl/vbaProject.bin');//vbaProject.bin always in 'xl' dir and always named vbaProject.bin + if($macrosCode !== false){ + $excel->setMacrosCode($macrosCode); + $excel->setHasMacros(true); + //short-circuit : not reading vbaProject.bin.rel to get Signature =>allways vbaProjectSignature.bin in 'xl' dir + $Certificate = $this->_getFromZipArchive($zip, 'xl/vbaProjectSignature.bin'); + if($Certificate !== false) + $excel->setMacrosCertificate($Certificate); + } + } $styles = array(); $cellStyles = array(); $xpath = self::array_item($relsWorkbook->xpath("rel:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles']")); @@ -684,7 +708,9 @@ class PHPExcel_Reader_Excel2007 extends PHPExcel_Reader_Abstract implements PHPE $docSheet->getTabColor()->setARGB( (string)$xmlSheet->sheetPr->tabColor['rgb'] ); } } - + if (isset($xmlSheet->sheetPr) && isset($xmlSheet->sheetPr['codeName'])) { + $docSheet->setCodeName((string) $xmlSheet->sheetPr['codeName']); + } if (isset($xmlSheet->sheetPr) && isset($xmlSheet->sheetPr->outlinePr)) { if (isset($xmlSheet->sheetPr->outlinePr['summaryRight']) && !self::boolean((string) $xmlSheet->sheetPr->outlinePr['summaryRight'])) { @@ -1942,7 +1968,44 @@ class PHPExcel_Reader_Excel2007 extends PHPExcel_Reader_Abstract implements PHPE return $value; } - + private function _readRibbon($excel, $customUITarget, $zip) + { + $baseDir = dirname($customUITarget); + $nameCustomUI = basename($customUITarget); + // get the xml file (ribbon) + $localRibbon = $this->_getFromZipArchive($zip, $customUITarget); + $customUIImagesNames = array(); + $customUIImagesBinaries = array(); + // something like customUI/_rels/customUI.xml.rels + $pathRels = $baseDir . '/_rels/' . $nameCustomUI . '.rels'; + $dataRels = $this->_getFromZipArchive($zip, $pathRels); + if ($dataRels) { + // exists and not empty if the ribbon have some pictures (other than internal MSO) + $UIRels = simplexml_load_string($dataRels); + if ($UIRels) { + // we need to save id and target to avoid parsing customUI.xml and "guess" if it's a pseudo callback who load the image + foreach ($UIRels->Relationship as $ele) { + if ($ele["Type"] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image') { + // an image ? + $customUIImagesNames[(string) $ele['Id']] = (string)$ele['Target']; + $customUIImagesBinaries[(string)$ele['Target']] = $this->_getFromZipArchive($zip, $baseDir . '/' . (string) $ele['Target']); + } + } + } + } + if ($localRibbon) { + $excel->setRibbonXMLData($customUITarget, $localRibbon); + if (count($customUIImagesNames) > 0 && count($customUIImagesBinaries) > 0) { + $excel->setRibbonBinObjects($customUIImagesNames, $customUIImagesBinaries); + } else { + $excel->setRibbonBinObjects(NULL); + } + } else { + $excel->setRibbonXMLData(NULL); + $excel->setRibbonBinObjects(NULL); + } + } + private static function array_item($array, $key = 0) { return (isset($array[$key]) ? $array[$key] : null); } diff --git a/Classes/PHPExcel/Worksheet.php b/Classes/PHPExcel/Worksheet.php index 9d7a5007..3fecc164 100644 --- a/Classes/PHPExcel/Worksheet.php +++ b/Classes/PHPExcel/Worksheet.php @@ -326,6 +326,13 @@ class PHPExcel_Worksheet implements PHPExcel_IComparable private $_hash = null; /** + * CodeName + * + * @var string + */ + private $_codeName = null; + + /** * Create a new worksheet * * @param PHPExcel $pParent @@ -336,6 +343,8 @@ class PHPExcel_Worksheet implements PHPExcel_IComparable // Set parent and title $this->_parent = $pParent; $this->setTitle($pTitle, FALSE); + // setTitle can change $pTitle + $this->setCodeName($this->getTitle()); $this->setSheetState(PHPExcel_Worksheet::SHEETSTATE_VISIBLE); $this->_cellCollection = PHPExcel_CachedObjectStorageFactory::getInstance($this); @@ -417,6 +426,34 @@ class PHPExcel_Worksheet implements PHPExcel_IComparable } /** + * Check sheet code name for valid Excel syntax + * + * @param string $pValue The string to check + * @return string The valid string + * @throws Exception + */ + private static function _checkSheetCodeName($pValue) + { + $CharCount = PHPExcel_Shared_String::CountCharacters($pValue); + if ($CharCount == 0) { + throw new PHPExcel_Exception('Sheet code name cannot be empty.'); + } + // Some of the printable ASCII characters are invalid: * : / \ ? [ ] and first and last characters cannot be a "'" + if ((str_replace(self::$_invalidCharacters, '', $pValue) !== $pValue) || + (PHPExcel_Shared_String::Substring($pValue,-1,1)=='\'') || + (PHPExcel_Shared_String::Substring($pValue,0,1)=='\'')) { + throw new PHPExcel_Exception('Invalid character found in sheet code name'); + } + + // Maximum 31 characters allowed for sheet title + if ($CharCount > 31) { + throw new PHPExcel_Exception('Maximum 31 characters allowed in sheet code name.'); + } + + return $pValue; + } + + /** * Check sheet title for valid Excel syntax * * @param string $pValue The string to check @@ -2802,4 +2839,69 @@ class PHPExcel_Worksheet implements PHPExcel_IComparable } } } +/** + * Define the code name of the sheet + * + * @param null|string Same rule as Title minus space not allowed (but, like Excel, change silently space to underscore) + * @return objWorksheet + * @throws PHPExcel_Exception + */ + public function setCodeName($pValue=null){ + // Is this a 'rename' or not? + if ($this->getCodeName() == $pValue) { + return $this; + } + $pValue = str_replace(' ', '_', $pValue);//Excel does this automatically without flinching, we are doing the same + // Syntax check + // throw an exception if not valid + self::_checkSheetCodeName($pValue); + + // We use the same code that setTitle to find a valid codeName else not using a space (Excel don't like) but a '_' + + if ($this->getParent()) { + // Is there already such sheet name? + if ($this->getParent()->sheetCodeNameExists($pValue)) { + // Use name, but append with lowest possible integer + + if (PHPExcel_Shared_String::CountCharacters($pValue) > 29) { + $pValue = PHPExcel_Shared_String::Substring($pValue,0,29); + } + $i = 1; + while ($this->getParent()->sheetCodeNameExists($pValue . '_' . $i)) { + ++$i; + if ($i == 10) { + if (PHPExcel_Shared_String::CountCharacters($pValue) > 28) { + $pValue = PHPExcel_Shared_String::Substring($pValue,0,28); + } + } elseif ($i == 100) { + if (PHPExcel_Shared_String::CountCharacters($pValue) > 27) { + $pValue = PHPExcel_Shared_String::Substring($pValue,0,27); + } + } + } + + $pValue = $pValue . '_' . $i;// ok, we have a valid name + //codeName is'nt used in formula : no need to call for an update + //return $this->setTitle($altTitle,$updateFormulaCellReferences); + } + } + + $this->_codeName=$pValue; + return $this; + } + /** + * Return the code name of the sheet + * + * @return null|string + */ + public function getCodeName(){ + return $this->_codeName; + } + /** + * Sheet has a code name ? + * @return boolean + */ + public function hasCodeName(){ + return !(is_null($this->_codeName)); + } } diff --git a/Classes/PHPExcel/Writer/Excel2007.php b/Classes/PHPExcel/Writer/Excel2007.php index 4e93201a..5ff98f2f 100644 --- a/Classes/PHPExcel/Writer/Excel2007.php +++ b/Classes/PHPExcel/Writer/Excel2007.php @@ -144,6 +144,8 @@ class PHPExcel_Writer_Excel2007 extends PHPExcel_Writer_Abstract implements PHPE 'drawing' => 'PHPExcel_Writer_Excel2007_Drawing', 'comments' => 'PHPExcel_Writer_Excel2007_Comments', 'chart' => 'PHPExcel_Writer_Excel2007_Chart', + 'relsvba' => 'PHPExcel_Writer_Excel2007_RelsVBA', + 'relsribbonobjects' => 'PHPExcel_Writer_Excel2007_RelsRibbon' ); // Initialise writer parts @@ -243,6 +245,35 @@ class PHPExcel_Writer_Excel2007 extends PHPExcel_Writer_Abstract implements PHPE // Add [Content_Types].xml to ZIP file $objZip->addFromString('[Content_Types].xml', $this->getWriterPart('ContentTypes')->writeContentTypes($this->_spreadSheet, $this->_includeCharts)); + //if hasMacros, add the vbaProject.bin file, Certificate file(if exists) + if($this->_spreadSheet->hasMacros()){ + $macrosCode=$this->_spreadSheet->getMacrosCode(); + if(!is_null($macrosCode)){// we have the code ? + $objZip->addFromString('xl/vbaProject.bin', $macrosCode);//allways in 'xl', allways named vbaProject.bin + if($this->_spreadSheet->hasMacrosCertificate()){//signed macros ? + // Yes : add the certificate file and the related rels file + $objZip->addFromString('xl/vbaProjectSignature.bin', $this->_spreadSheet->getMacrosCertificate()); + $objZip->addFromString('xl/_rels/vbaProject.bin.rels', + $this->getWriterPart('RelsVBA')->writeVBARelationships($this->_spreadSheet)); + } + } + } + //a custom UI in this workbook ? add it ("base" xml and additional objects (pictures) and rels) + if($this->_spreadSheet->hasRibbon()){ + $tmpRibbonTarget=$this->_spreadSheet->getRibbonXMLData('target'); + $objZip->addFromString($tmpRibbonTarget, $this->_spreadSheet->getRibbonXMLData('data')); + if($this->_spreadSheet->hasRibbonBinObjects()){ + $tmpRootPath=dirname($tmpRibbonTarget).'/'; + $ribbonBinObjects=$this->_spreadSheet->getRibbonBinObjects('data');//the files to write + foreach($ribbonBinObjects as $aPath=>$aContent){ + $objZip->addFromString($tmpRootPath.$aPath, $aContent); + } + //the rels for files + $objZip->addFromString($tmpRootPath.'_rels/'.basename($tmpRibbonTarget).'.rels', + $this->getWriterPart('RelsRibbonObjects')->writeRibbonRelationships($this->_spreadSheet)); + } + } + // Add relationships to ZIP file $objZip->addFromString('_rels/.rels', $this->getWriterPart('Rels')->writeRelationships($this->_spreadSheet)); $objZip->addFromString('xl/_rels/workbook.xml.rels', $this->getWriterPart('Rels')->writeWorkbookRelationships($this->_spreadSheet)); diff --git a/Classes/PHPExcel/Writer/Excel2007/ContentTypes.php b/Classes/PHPExcel/Writer/Excel2007/ContentTypes.php index 9fdc9dd5..b19b4cd9 100644 --- a/Classes/PHPExcel/Writer/Excel2007/ContentTypes.php +++ b/Classes/PHPExcel/Writer/Excel2007/ContentTypes.php @@ -86,9 +86,26 @@ class PHPExcel_Writer_Excel2007_ContentTypes extends PHPExcel_Writer_Excel2007_W ); // Workbook - $this->_writeOverrideContentType( - $objWriter, '/xl/workbook.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml' - ); + if($pPHPExcel->hasMacros()){ //Macros in workbook ? + // Yes : not standard content but "macroEnabled" + $this->_writeOverrideContentType( + $objWriter, '/xl/workbook.xml', 'application/vnd.ms-excel.sheet.macroEnabled.main+xml' + ); + //... and define a new type for the VBA project + $this->_writeDefaultContentType( + $objWriter, 'bin', 'application/vnd.ms-office.vbaProject' + ); + if($pPHPExcel->hasMacrosCertificate()){// signed macros ? + // Yes : add needed information + $this->_writeOverrideContentType( + $objWriter, '/xl/vbaProjectSignature.bin', 'application/vnd.ms-office.vbaProjectSignature' + ); + } + }else{// no macros in workbook, so standard type + $this->_writeOverrideContentType( + $objWriter, '/xl/workbook.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml' + ); + } // DocProps $this->_writeOverrideContentType( @@ -178,7 +195,16 @@ class PHPExcel_Writer_Excel2007_ContentTypes extends PHPExcel_Writer_Excel2007_W ); } } - + if($pPHPExcel->hasRibbonBinObjects()){//Some additional objects in the ribbon ? + //we need to write "Extension" but not already write for media content + $tabRibbonTypes=array_diff($pPHPExcel->getRibbonBinObjects('types'), array_keys($aMediaContentTypes)); + foreach($tabRibbonTypes as $aRibbonType){ + $mimeType='image/.'.$aRibbonType;//we wrote $mimeType like customUI Editor + $this->_writeDefaultContentType( + $objWriter, $aRibbonType, $mimeType + ); + } + } $sheetCount = $pPHPExcel->getSheetCount(); for ($i = 0; $i < $sheetCount; ++$i) { if (count($pPHPExcel->getSheet()->getHeaderFooter()->getImages()) > 0) { diff --git a/Classes/PHPExcel/Writer/Excel2007/Rels.php b/Classes/PHPExcel/Writer/Excel2007/Rels.php index fa225eec..87403b62 100644 --- a/Classes/PHPExcel/Writer/Excel2007/Rels.php +++ b/Classes/PHPExcel/Writer/Excel2007/Rels.php @@ -94,6 +94,15 @@ class PHPExcel_Writer_Excel2007_Rels extends PHPExcel_Writer_Excel2007_WriterPar 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument', 'xl/workbook.xml' ); + // a custom UI in workbook ? + if($pPHPExcel->hasRibbon()){ + $this->_writeRelationShip( + $objWriter, + 5, + 'http://schemas.microsoft.com/office/2006/relationships/ui/extensibility', + $pPHPExcel->getRibbonXMLData('target') + ); + } $objWriter->endElement(); @@ -159,6 +168,17 @@ class PHPExcel_Writer_Excel2007_Rels extends PHPExcel_Writer_Excel2007_WriterPar 'worksheets/sheet' . ($i + 1) . '.xml' ); } + // Relationships for vbaProject if needed + // id : just after the last sheet + if($pPHPExcel->hasMacros()){ + $this->_writeRelationShip( + $objWriter, + ($i + 1 + 3), + 'http://schemas.microsoft.com/office/2006/relationships/vbaProject', + 'vbaProject.bin' + ); + ++$i;//increment i if needed for an another relation + } $objWriter->endElement(); diff --git a/Classes/PHPExcel/Writer/Excel2007/RelsRibbon.php b/Classes/PHPExcel/Writer/Excel2007/RelsRibbon.php new file mode 100644 index 00000000..47b75a31 --- /dev/null +++ b/Classes/PHPExcel/Writer/Excel2007/RelsRibbon.php @@ -0,0 +1,77 @@ +getParentWriter()->getUseDiskCaching()) { + $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory()); + } else { + $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_MEMORY); + } + + // XML header + $objWriter->startDocument('1.0','UTF-8','yes'); + + // Relationships + $objWriter->startElement('Relationships'); + $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships'); + $localRels=$pPHPExcel->getRibbonBinObjects('names'); + if(is_array($localRels)){ + foreach($localRels as $aId=>$aTarget){ + $objWriter->startElement('Relationship'); + $objWriter->writeAttribute('Id', $aId); + $objWriter->writeAttribute('Type', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image'); + $objWriter->writeAttribute('Target', $aTarget); + $objWriter->endElement();//Relationship + } + } + $objWriter->endElement();//Relationships + + // Return + return $objWriter->getData(); + + } + +} diff --git a/Classes/PHPExcel/Writer/Excel2007/RelsVBA.php b/Classes/PHPExcel/Writer/Excel2007/RelsVBA.php new file mode 100644 index 00000000..60833e10 --- /dev/null +++ b/Classes/PHPExcel/Writer/Excel2007/RelsVBA.php @@ -0,0 +1,72 @@ +getParentWriter()->getUseDiskCaching()) { + $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory()); + } else { + $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_MEMORY); + } + + // XML header + $objWriter->startDocument('1.0','UTF-8','yes'); + + // Relationships + $objWriter->startElement('Relationships'); + $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships'); + $objWriter->startElement('Relationship'); + $objWriter->writeAttribute('Id', 'rId1'); + $objWriter->writeAttribute('Type', 'http://schemas.microsoft.com/office/2006/relationships/vbaProjectSignature'); + $objWriter->writeAttribute('Target', 'vbaProjectSignature.bin'); + $objWriter->endElement();//Relationship + $objWriter->endElement();//Relationships + + // Return + return $objWriter->getData(); + + } + +} diff --git a/Classes/PHPExcel/Writer/Excel2007/Worksheet.php b/Classes/PHPExcel/Writer/Excel2007/Worksheet.php index f70d4fe7..8ca33aa4 100644 --- a/Classes/PHPExcel/Writer/Excel2007/Worksheet.php +++ b/Classes/PHPExcel/Writer/Excel2007/Worksheet.php @@ -148,6 +148,12 @@ class PHPExcel_Writer_Excel2007_Worksheet extends PHPExcel_Writer_Excel2007_Writ // sheetPr $objWriter->startElement('sheetPr'); //$objWriter->writeAttribute('codeName', $pSheet->getTitle()); + if($pSheet->getParent()->hasMacros()){//if the workbook have macros, we need to have codeName for the sheet + if($pSheet->hasCodeName()==false){ + $pSheet->setCodeName($pSheet->getTitle()); + } + $objWriter->writeAttribute('codeName', $pSheet->getCodeName()); + } $autoFilterRange = $pSheet->getAutoFilter()->getRange(); if (!empty($autoFilterRange)) { $objWriter->writeAttribute('filterMode', 1); diff --git a/changelog.txt b/changelog.txt index 2f5bb06b..deeb03b3 100644 --- a/changelog.txt +++ b/changelog.txt @@ -38,6 +38,7 @@ Fixed in develop branch for release v1.8.0: - Feature: (MBaker) - Added "Quote Prefix" to style settings (Excel2007 Reader and Writer only) - Feature: (MBaker) - Added Horizontal FILL alignment for Excel5 and Excel2007 Readers/Writers, and Horizontal DISTRIBUTED alignment for Excel2007 Reader/Writer - Feature: (trvrnrth) - Add support for reading protected (RC4 encrypted) .xls files (64-bit Linux only) +- Feature: (LWol) Work Item GH-252 - Adding support for macros, Ribbon in Excel 2007 - General: (cdhutch) Work item 20055 - Remove array_shift in ReferenceHelper::insertNewBefore improves column or row delete speed - General: (MBaker) - Improve stock chart handling and rendering, with help from Swashata Ghosh - General: (MBaker) - Fix to calculation properties for Excel2007 so that the opening application will only recalculate on load if it's actually required