ZenCart data import - admin/custom menu.php

from HTYP, the free directory anyone can edit if they can prove to me that they're not a spambot
Revision as of 11:18, 16 March 2007 by Woozle (talk | contribs) (→‎Navigation: $zc prefix for consistency with parent page)
Jump to navigation Jump to search

Navigation

computing: software: ZenCart: importing data: $zc/admin/custom_menu.php

Overview

This file does the actual import. It contains a number of classes which might later be split off into separate PHP files, as they model particular aspects of the ZenCart data.

Source

<php><?php // // +----------------------------------------------------------------------+ // |zen-cart Open Source E-commerce | // +----------------------------------------------------------------------+ // | Copyright (c) 2003 The zen-cart developers | // | | // | http://www.zen-cart.com/index.php | // | | // | Portions Copyright (c) 2003 osCommerce | // +----------------------------------------------------------------------+ // | This source file is subject to version 2.0 of the GPL license, | // | that is bundled with this package in the file LICENSE, and is | // | available through the world-wide-web at the following url: | // | http://www.zen-cart.com/license/2_0.txt. | // | If you did not receive a copy of the zen-cart license and are unable | // | to obtain it through the world-wide-web, please send a note to | // | license@zen-cart.com so we can mail you a copy immediately. | // +----------------------------------------------------------------------+ // /*

2006-09-17 (wzl) handlers for "Custom" menu. Used modules.php as model.
SET values:

prod_entry (default): bulk product entry ACTION values: (none): show bulk product entry form check: data is being submitted for preview commit: data is being submitted for final commit (update database)

  • /
 require('includes/application_top.php');
 define('CUSTOM_MODULE_SET_PROD_ENTRY','prod_entry');
 define('CUSTOM_MODULE_SAVE_DATA','save');
 define('CUSTOM_MODULE_PROD_DATA_NAME','products');
 define('CUSTOM_MODULE_ACT_CHECK','check');
 define('CUSTOM_MODULE_ACT_COMMIT','commit');
 define('CUSTOM_MODULE_BTN_CHECK','Preview Changes');
 define('CUSTOM_MODULE_BTN_COMMIT','COMMIT');
 define('URL_ISBN_SEARCH','http://www.abebooks.com/servlet/SearchResults?isbn=<<1>>');
 define('CUSTOM_DEFAULT_SUPPLIER','Jubilee');  // when import data supplier field is empty
 define('CUSTOM_DEFAULT_CATEGORY','Unfiled');  // when imported category field is empty
 define('TABLE_CUSTOM_IMPORT_PROD',DB_PREFIX.'custom_import_prod');
 define('TABLE_CUSTOM_IMPORT_MFGRS',DB_PREFIX.'custom_import_mfgrs');
 // new item
 define('HTML_STATUS_NEW', 'NEW');
 // item is saved in list
 define('HTML_STATUS_NOTED', 'ok');
 // item found in database
 define('HTML_STATUS_EXISTS', 'found');


 $set = (isset($_GET['set']) ? $_GET['set'] : );
 $button = (isset($_POST['action']) ? $_POST['action'] : );
 switch ($button) {
   case CUSTOM_MODULE_BTN_CHECK: $action=CUSTOM_MODULE_ACT_CHECK; break;
   case CUSTOM_MODULE_BTN_COMMIT: $action=CUSTOM_MODULE_ACT_COMMIT; break;
   default: $action=;
 }

/*

After page has displayed, this handles the user's input
  • /
 switch ($action) {
   case CUSTOM_MODULE_SAVE_DATA:
     $doSave = true;

// new SQL code for updating products: $sqlUpdateProd = "update " . TABLE_PRODUCTS . " set configuration_value = '" . $value . "' where configuration_key = '" . $key ."'";

     break;
   case 'enter':	// showing data entry form, so this stage (data processing) does nothing
   default:
 }

?> <!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN"> > <?php echo TITLE; ?> This feature is currently being debugged; do not use yet. N. Staddon 2006-10-11
Previewing entered data:"; ProcessData(false); break; case CUSTOM_MODULE_ACT_COMMIT: echo "Saving entered data:"; $hide_form = 1; ProcessData(true); break; default: } /* DEFAULT: show bulk product entry form check: data is being submitted for preview commit: data is being submitted for final commit (update database) */ // always show data entry form: } if (!$hide_form) { $htmlSep = '<tab>'; echo zen_draw_form('prod_entry', CUSTOM_MENU_MODULE); echo '

'; echo 'Enter product data here:
'; echo 'Format is: ' .'ID string'.$htmlSep .'(ignored)'.$htmlSep .'Title'.$htmlSep .'(ignored)'.$htmlSep .'Supplier'.$htmlSep .'Price
'; echo '
'; echo zen_draw_textarea_field(CUSTOM_MODULE_PROD_DATA_NAME, '', '100', '20'); // later on this can use Zen functions for more slickness, but for now this should work: echo '
'; echo ''; echo ''; echo ''; echo ''; echo '

'; echo ''; } ?>
<?php require(DIR_WS_INCLUDES . 'application_bottom.php');

function Pluralize($iQty, $iSing=, $iPlur='s') {

 if ($iQty == 1) {
   return $iSing;
 } else {
   return $iPlur;
 }

}

function ProcessData($iCommit) {

 $data_raw = $_POST[CUSTOM_MODULE_PROD_DATA_NAME];
 $data_lines = explode("\n",$data_raw);

echo '

'; echo ''; echo '';
 $ctrTitlesNew = 0;
 $ctrTitlesOld = 0;
 foreach ($data_lines as $data_line) {

// create title object for each import line. Title object should do all the lookups and printing and stuff.

   if ($data_line) {
     $objTitle = clsTitles::Import($data_line);
     $ctrSuppFnd = clsSuppliers::Count(); // # of suppliers found
     $ctrSuppNew = clsSuppliers::CountNew(); // # of *new* suppliers
     $ctrCatgFnd = clsCategories::Count(); // # of new categories
     $ctrCatgNew = clsCategories::CountNew(); // # of new categories
echo ''.$objTitle->TableRowCells().''; } } if ($ctrSuppNew) { $htmlSuppList = ': '.clsSuppliers::DoListNew('','',', '); $htmlSuppList .= ' OLD: '.clsSuppliers::DoListOld('','',', '); } if ($ctrCatgNew) { $htmlCatgList = ': '.clsCategories::DoListNew('','',', '); $htmlCatgList .= ' OLD: '.clsCategories::DoListOld('','',', '); } echo '
Data Entry Status
CatgIDTitleSupplierPriceSupplierTitleCategory

'; echo 'Processed '.$ctrLines.' lines:

    ' .'
  • '.clsTitles::Records()->CountNew().' new titles to add' .'
  • '.clsTitles::Records()->CountOld().' titles to update (ignored for now)' .'
  • '.$ctrSuppFnd.' supplier'.Pluralize($ctrSuppFnd).' in import, '.$ctrSuppNew.' new'.$htmlSuppList .'
  • '.$ctrCatgFnd.' categor'.Pluralize($ctrCatgFnd,'y','ies').' in import, '.$ctrCatgNew.' new'.$htmlCatgList .'

';

 echo 'Adding suppliers: '.clsSuppliers::Save($iCommit).'
'; echo 'Adding categories: '.clsCategories::Save($iCommit).'
'; echo 'Adding titles: '.clsTitles::Save($iCommit).'
';

} function MakeKey($iStr) {

 return preg_replace('/[ -\.,;]*/',,strtolower($iStr));

} /*

===== CLASSES =====
  • /

class clsRecords {

 public $List;		// actual unique records
 public $qtyNew;	// number of *new* records found during import
 public $qtyOld;
 public function Count() {
   return Count($this->List);
 }
 public function CountOld() {
   if ($this->qtyOld) {
     return $this->qtyOld;
   } else {
     return 0;
   }
 }
 public function CountNew() {
   if ($this->qtyNew) {
     return $this->qtyNew;
   } else {
     return 0;
   }
 }
 public function AddObject($iName,$iObj) {
   if (!$this->List[$iName]) {
     $this->List[$iName] = $iObj;
   }
   if ($iObj->isFound) {
     $this->qtyOld++;
   } else {
     $this->qtyNew++;
   }
 }
 public function Exists($iName) {
   if (is_array($this->List)) {
     $isSet = isset($this->List[$iName]);
     return $isSet;
   } else {
     return false;
   }
 }
 public function DoListNew($iPfx,$iSfx,$iSep) {
   foreach ($this->List as $obj) {
     if (!$obj->isFound) {
       if ($out) {
         $out .= $iSep;
       }
       $out .= $iPfx.$obj->Name.$iSfx;
     }
   }
   return $out;
 }
 public function DoListOld($iPfx,$iSfx,$iSep) {
   foreach ($this->List as $obj) {
     if ($obj->isFound) {
       if ($out) {
         $out .= $iSep;
       }
       $out .= $iPfx.$obj->Name.$iSfx;
     }
   }
   return $out;
 }

}

class clsTitles {

 private static $objRecords;
 public function Records() {
   self::InitRecords();
   return self::$objRecords;
 }
 public function Import($iLine) {
   $obj = new clsTitle;
   $obj->ImportLine($iLine);
   if (!$obj->isCatg) {
     self::Records()->AddObject($obj->ImportKey,$obj);
   }
   return $obj;
 }
 public function Save($iCommit) {
   global $db;

$result = '

    '; foreach (self::Records()->List as $obj) { $result .= $obj->Save($iCommit); } $result .= '

';

   return $result;
 }
 public function Count() {
   return self::Records()->Count();
 }
 public function CountNew() {
   return self::Records()->CountNew();
 }
 public function DoListNew($iPfx,$iSfx,$iSep) {
   return self::Records()->DoListNew($iPfx,$iSfx,$iSep);
 }
 protected function InitRecords() {
   if (!isset(self::$objRecords)) {
     self::$objRecords = new clsRecords;
   }
 }

}

class clsTitle {

 public $ImportKey;
 private $intID;
 public $Name;
 public $Price;
 public $Supplier;
 public $SuppImp;
 public $Category;
 public $CatgImp;
 public $isCatg;

// calculated fields

 public $ISBN;
 public $ISBN_URL;
 public $HtmlName;

// status fields

 public $Status;
 public $isFound;

// public static $lstTitles;

 public function ID() {
   if (!isset($this->intID)) {
     $this->UpdateName();
   }

//echo 'TITLE ID='.$this->intID;

   return $this->intID;
 }
 public function ImportLine($iLine) {
   global $db;
   list($ImportKeyRaw,$notUsed,$this->Name,$notUsed,$strSupplierName,$notUsed,$this->Price) = explode("\t",$iLine);

//echo 'NAME='.$Name.'
'; // Check to see if the title is actually a category record - price is 0, name is empty or contains "classification"

   $this->isCatg = false;
   if ($this->Price == 0.0) {
     if ((!MakeKey($this->Name)) || (eregi('classification',$this->Name))) {
       $this->isCatg = true;
     }
   }
   if ($this->isCatg) {

// for now, we're not really doing anything with category lines

     $this->HtmlName = ''.$this->Name.'';
   } else {

// ** Supplier

     if ($strSupplierName == ) {
       $strSupplierName = CUSTOM_DEFAULT_SUPPLIER;
     }
     $this->SuppImp = clsSuppliers::Import($strSupplierName);
     $this->Supplier = $this->SuppImp->Supplier;

// Parse out the category and title key

     $listTitleParts = array_reverse(split(':',$ImportKeyRaw));
     $this->ImportKey = MakeKey(array_shift($listTitleParts));
     $strCatg = array_shift($listTitleParts);   // for now, assume just one level of category
     if (!$strCatg) {
       $strCatg = CUSTOM_DEFAULT_CATEGORY;
     }
     $this->CatgImp = clsCategoryImports::Import($strCatg);
     $this->Category = $this->CatgImp->Category;

//echo 'CATG='.$strCatg.' stored='.$this->Category->Name.'
';

     $this->UpdateName();

// ** Title: cross-reference import key to title ID

     $sqlFind = 'SELECT * FROM '.TABLE_CUSTOM_IMPORT_PROD.' WHERE import_key="'.$this->ImportKey.'";';
     $qryTitle = $db->Execute($sqlFind);
     if ($qryTitle->RecordCount()) {
       $this->isFound = true;
       $this->Status = HTML_STATUS_EXISTS;
       $this->ID = $qryTitle->fields['prod_key'];
     } else {
       $this->isFound = false;

if ($lstTitles[$this->ImportKey]) { $this->Status = HTML_STATUS_NOTED;

       } else {
         $this->Status = HTML_STATUS_NEW;

// $lstTitles[$this->ImportKey] = $this->ID; // may eventually want to save an object, not just a string

       }
     }
   }
 }
 public function UpdateName() {
   $subject = $this->Name;
   $pattern = '/ISBN *[0-9\-]*/';
   preg_match($pattern, $subject, $matches, PREG_OFFSET_CAPTURE);

// This operation is unnecessarily complicated, but it is what seems to work:

   $this->ISBN = trim(preg_replace('/ISBN/',,$matches[0][0]));
   if ($this->ISBN) {
     $this->ISBN_URL = preg_replace('/<<1>>/',$this->ISBN,URL_ISBN_SEARCH);
     $this->HtmlName = '<a href="'.$this->ISBN_URL.'">'.$this->Name.'</a>';
   } else {
     $this->HtmlName = $this->Name;
     $this->ISBN_URL = ;
   }
 }
 public function Save($iCommit) {
   global $db;
   if ($this->isFound) {

// TO DO: write update routine

   } else {

// Check for missing manufacturer:

    if (!$this->Supplier->ID()) {
      echo 'MISSING SUPPLIER for title "'.$this->Name.'"
'; } if (!$this->Category->ID()) { echo 'MISSING CATEGORY for title "'.$this->Name.'"
'; }

// only insert items with a numerically valid price

     if (is_numeric(trim($this->Price))) {

// Theory: MySQL defaults to using the first index as an auto-increment key. We'll see if this works. // Update main PRODUCTS table:

       $sqlAdd = 'INSERT INTO '.TABLE_PRODUCTS.' (products_status,products_price,products_date_added,manufacturers_id,master_categories_id)'

.'VALUES(' .'1,' // products_status (1 = active) .$this->Price.',' // products_price .'NOW(),' // products_date_added .$this->Supplier->ID().',' // manufacturers_id .$this->Category->ID().')'; // master_categories_id

$result .= '

  • SQL: '.$sqlAdd; if ($iCommit) { $db->Execute($sqlAdd); $result .= '
  • main products table status: '.mysql_info(); $this->intID = mysql_insert_id(); $result .= '
  • ID: '.$this->intID; } else { $this->intID = 0; } // Update product import table: $sqlAdd = 'INSERT INTO '.TABLE_CUSTOM_IMPORT_PROD.' (import_key,prod_key) VALUES("'.mysql_real_escape_string($this->ImportKey).'",'.$this->ID().')'; $result .= '
  • SQL: '.$sqlAdd; if ($iCommit) { $db->Execute($sqlAdd); $result .= '
  • products import table status: '.mysql_info(); } // Update PRODUCTS_DESCRIPTION: $sqlAdd = 'INSERT INTO '.TABLE_PRODUCTS_DESCRIPTION.' (products_id,products_name) VALUES('.$this->ID().',"'.mysql_real_escape_string($this->Name).'")'; $result .= '
  • SQL: '.$sqlAdd; if ($iCommit) { $db->Execute($sqlAdd); $result .= '
  • products description status: '.mysql_info(); } // Update PRODUCT CATEGORIES: $sqlAdd = 'INSERT INTO '.TABLE_PRODUCTS_TO_CATEGORIES.' (products_id,categories_id) VALUES('.$this->ID().','.$this->Category->ID().')'; $result .= '
  • SQL: '.$sqlAdd; if ($iCommit) { $db->Execute($sqlAdd); $result .= '
  • products description status: '.mysql_info(); } } else { $result = '
  • Warning: product ['.$this->Name.'] has no price set ('.$this->Price.'); skipping.'; } return $result; } } public function TableRowCells() { $out = ''.$this->Category->Name.''.$this->ImportKey.''.$this->HtmlName.'';
       if ($this->isCatg) {
    
    $out .= 'category (ignored)';
       } else {
    
    $out .= '('.$this->Supplier->ID().') '.$this->Supplier->Name.''.$this->Price.''.$this->SuppImp->Status.''.$this->Status.''.$this->CatgImp->Status.'';
       }
       return $out;
     }
    

    }

    class clsSuppliers {

     private static $Records;
    
     public function Import($iName) {
       self::InitRecords();
       $objSuppImp = new clsSupplierImport;
       if (self::$Records->List[$iName]) {
    

    // supplier has already been seen this session

         $objSupp = self::$Records->List[$iName];  // retrieve existing supplier record
         $objSuppImp->ImportOld($objSupp);  // attach it to supplier import record for this import
       } else {
    

    // new supplier for this session

         $objSuppImp->ImportNew($iName);
         self::$Records->AddObject($iName,$objSuppImp->Supplier);
    

    // self::$Records->List[$iName] = $objSuppImp->Supplier;

    //     if (!$objSuppImp->Supplier->isFound) {
     //     $this->Records->qtyNew++;
       //  }
       }
       return $objSuppImp;
     }
    

    // save list of (new) suppliers to database

     public function Save($iCommit) {
       global $db;
    
    $result = '
      '; foreach (self::$Records->List as $obj) { if (!$obj->isFound) { if ($strValues) { $strValues .= ','; } $strValues .= '("'.mysql_real_escape_string($obj->Name).'",NOW())'; } } if ($strValues) { $sqlAdd = 'INSERT INTO '.TABLE_MANUFACTURERS.' (manufacturers_name, date_added) VALUES'.$strValues; $result .= '
    • SQL: '.$sqlAdd; if ($iCommit) { $db->Execute($sqlAdd); $result .= '
    • status: '.mysql_info(); } } $result .= '
    ';
       return $result;
     }
     public function Count() {
       self::InitRecords();
       return self::$Records->Count();
     }
     public function CountNew() {
       self::InitRecords();
       return self::$Records->CountNew();
     }
     public function DoListNew($iPfx,$iSfx,$iSep) {
       self::InitRecords();
       return self::$Records->DoListNew($iPfx,$iSfx,$iSep);
     }
     public function DoListOld($iPfx,$iSfx,$iSep) {
       self::InitRecords();
       return self::$Records->DoListOld($iPfx,$iSfx,$iSep);
     }
     protected function InitRecords() {
       if (!isset(self::$Records)) {
         self::$Records = new clsRecords;
       }
     }
    

    }

    class clsSupplier {

     private $intID;
     public $Name;
     public $Key;
     public $isFound;
    
     public function Init($iName) {
       global $db;
    
       $this->Name = $iName;
       $this->UpdateName();
     }
     public function Init_fromID($iID) {
       global $db;
    
       $this->intID = $iID;
       $sqlFind = 'SELECT * FROM '.TABLE_MANUFACTURERS.' WHERE manufacturers_id="'.$iID.'";';
       $qryFind = $db->Execute($sqlFind);
       $this->isFound = $qryFind->RecordCount();
       if ($this->isFound) {
         $this->Name = $qryFind->fields['manufacturers_name'];
       }
     }
     private function UpdateName() {
       global $db;
    
       $sqlFind = 'SELECT * FROM '.TABLE_MANUFACTURERS.' WHERE manufacturers_name="'.$this->Name.'";';
       $qryFind = $db->Execute($sqlFind);
       $this->isFound = $qryFind->RecordCount();
       if ($this->isFound) {
         $this->intID = $qryFind->fields['manufacturers_id'];
       } else {
    

    // not found in main list, so check aliases

         $sqlFind = 'SELECT * FROM '.TABLE_CUSTOM_IMPORT_MFGRS.' WHERE LCASE(import_alias)="'.mysql_real_escape_string(strtolower($this->Name)).'";';
         $qryFind = $db->Execute($sqlFind);
         $this->isFound = $qryFind->RecordCount();
         if ($this->isFound) {
           $this->Init_fromID($qryFind->fields['id_mfgr']);
         }
       }
     }
     public function ID() {
       if ($this->intID) {
         $this->UpdateName();
       }
       return $this->intID;
     }
    

    }

    class clsSupplierImport {

     public $Supplier;	// points to supplier data
     public $Status;	// indicates status of this particular import of that data
    

    // Supplier record has already been created, so use it:

     public function ImportOld($iObj) {
       $this->Supplier = $iObj;
       $this->Status = HTML_STATUS_NOTED;
     }
    

    // Supplier record needs to be created:

     public function ImportNew($iName) {
       global $db;
    
       $this->Supplier = new clsSupplier;
       $this->Supplier->Init($iName);
       if ($this->Supplier->isFound) {
         $this->Status = HTML_STATUS_EXISTS;
       } else {
         $this->Status = HTML_STATUS_NEW;
       }
     }
    

    }

    /*

    ===== CATEGORIES =====
    
    • /

    class clsCategories {

     private static $Records;
    
     public function Create($iName) {
    

    // This function is only called if category has not been seen before in this run

       global $db;
    
       $objNew = new clsCategory;
       self::InitRecords();
       
       $objNew->Init($iName);
       self::$Records->AddObject($iName,$objNew);
    

    /* self::$Records->List[$iName] = $objNew;

       if (!$objNew->isFound) {
         self::$Records->qtyNew++;
       }
    
    • /
       return $objNew;
     }
    

    // add any new categories to database

     public function Save($iCommit) {
       foreach (self::$Records->List as $obj) {
         if (!$obj->isFound) {
           $sqlOut .= $obj->Save($iCommit);
         }
       }
       return $sqlOut;
     }
     public function GetObject($ID) {
       self::InitRecords();
       return self::$Records->List[$ID];
     }
     public function Count() {
       self::InitRecords();
       return self::$Records->Count();
     }
     public function CountNew() {
       self::InitRecords();
       return self::$Records->CountNew();
     }
     public function Exists($iName) {
       self::InitRecords();
       return self::$Records->Exists($iName);
     }
     public function DoListNew($iPfx,$iSfx,$iSep) {
       self::InitRecords();
       return self::$Records->DoListNew($iPfx,$iSfx,$iSep);
     }
     public function DoListOld($iPfx,$iSfx,$iSep) {
       self::InitRecords();
       return self::$Records->DoListOld($iPfx,$iSfx,$iSep);
     }
     protected function InitRecords() {
       if (!isset(self::$Records)) {
         self::$Records = new clsRecords;
       }
     }
    

    } class clsCategory {

     private $intID;
     public $Name;
     public $ImportKey;
    

    // status

     public $isFound;
    
     public function Init($iName) {
       global $db;
    

    //echo 'storing 2:'.$iName.' ';

       $this->Name = $iName;
       $this->ImportKey = MakeKey($iName);
       $this->UpdateName();
     }
     private function UpdateName() {
       global $db;
    
       $sqlFind = 'SELECT * FROM '.TABLE_CATEGORIES_DESCRIPTION.' WHERE categories_name="'.$this->Name.'"';
       $qryFind = $db->Execute($sqlFind);
       $this->isFound = $qryFind->RecordCount();
       $this->intID = $qryFind->fields['categories_id'];
     }
     public function ID() {
       if (!$this->intID) {
         $this->UpdateName();
       }
       return $this->intID;
     }
     public function Save($iCommit) {
       global $db;
    
    $result = '
      '; $sqlAdd = 'INSERT INTO '.TABLE_CATEGORIES.' (date_added) VALUES(NOW())'; $result .= '
    • SQL: '.$sqlAdd; if ($iCommit) { $db->Execute($sqlAdd); $result .= '
    • status: '.mysql_info(); $idNew = mysql_insert_id(); $result .= '
    • ID: '.$idNew; } else { $idNew = '?'; } $sqlAdd = 'INSERT INTO '.TABLE_CATEGORIES_DESCRIPTION.' (categories_id,categories_name) VALUES(' .$idNew.',"'.mysql_real_escape_string($this->Name).'");'; $result .= '
    • SQL: '.$sqlAdd; if ($iCommit) { $db->Execute($sqlAdd); $result .= '
    • status: '.mysql_info(); } $result .= '
    ';
       return $result;
     }
    

    }

    class clsCategoryImports {

     public function Import($iName) {
       $objImport = new clsCategoryImport;
       $objImport->Import($iName);
       return $objImport;
     }
    

    }

    class clsCategoryImport {

     public $Category;
     public $Status;
    
     public function Import($iName) {
    

    // echo 'storing 1:'.$iName.' ';

       if (clsCategories::Exists($iName)) {
    

    //echo 'EXISTS ';

         $this->Category = clsCategories::GetObject($iName);
         $this->Status = HTML_STATUS_NOTED;
       } else {
    

    //echo '*NEW* ';

         $this->Category = clsCategories::Create($iName);
         if ($this->Category->isFound) {
           $this->Status = HTML_STATUS_EXISTS;
         } else {
           $this->Status = HTML_STATUS_NEW;
         }
       }
     }
    

    }

    ?></php>