/**
  * Copys a Layout
  * @param <int> $oldLayoutId
  * @param <string> $newLayoutName
  * @param <int> $userId
  * @param <bool> $copyMedia Make copies of this layouts media
  * @return <int> 
  */
 public function Copy($oldLayoutId, $newLayoutName, $newDescription, $userId, $copyMedia = false)
 {
     try {
         $dbh = PDOConnect::init();
         $currentdate = date("Y-m-d H:i:s");
         $campaign = new Campaign($this->db);
         // Include to media data class?
         if ($copyMedia) {
             $mediaObject = new Media($this->db);
             $mediaSecurity = new MediaGroupSecurity($this->db);
         }
         // We need the old campaignid
         $oldCampaignId = $campaign->GetCampaignId($oldLayoutId);
         // The Layout ID is the old layout
         $SQL = "";
         $SQL .= " INSERT INTO layout (layout, xml, userID, description, retired, duration, backgroundImageId, createdDT, modifiedDT, status) ";
         $SQL .= " SELECT :layout, xml, :userid, :description, retired, duration, backgroundImageId, :createddt, :modifieddt, status ";
         $SQL .= "  FROM layout ";
         $SQL .= " WHERE layoutid = :layoutid";
         $sth = $dbh->prepare($SQL);
         $sth->execute(array('layout' => $newLayoutName, 'description' => $newDescription, 'userid' => $userId, 'createddt' => $currentdate, 'modifieddt' => $currentdate, 'layoutid' => $oldLayoutId));
         $newLayoutId = $dbh->lastInsertId();
         // Create a campaign
         $newCampaignId = $campaign->Add($newLayoutName, 1, $userId);
         // Link them
         $campaign->Link($newCampaignId, $newLayoutId, 0);
         // Open the layout XML and parse for media nodes
         if (!$this->SetDomXml($newLayoutId)) {
             $this->ThrowError(25000, __('Unable to copy layout'));
         }
         // Handle the Background
         $sth = $dbh->prepare('SELECT mediaId FROM lklayoutmedia WHERE layoutId = :layoutId AND regionId = :regionId');
         $sth->execute(array('layoutId' => $oldLayoutId, 'regionId' => 'background'));
         if ($row = $sth->fetch()) {
             // This layout does have a background image
             // Link it to the new one
             if (!($newLkId = $this->AddLk($newLayoutId, 'background', $row['mediaId']))) {
                 throw new Exception(__('Unable to link background'));
             }
         }
         // Get all media nodes
         $xpath = new DOMXpath($this->DomXml);
         // Create an XPath to get all media nodes
         $mediaNodes = $xpath->query("//media");
         Debug::LogEntry('audit', 'About to loop through media nodes', 'layout', 'Copy');
         $copiesMade = array();
         // On each media node, take the existing LKID and MediaID and create a new LK record in the database
         $sth = $dbh->prepare('SELECT StoredAs FROM media WHERE MediaID = :mediaid');
         foreach ($mediaNodes as $mediaNode) {
             $mediaId = $mediaNode->getAttribute('id');
             $type = $mediaNode->getAttribute('type');
             // Store the old media id
             $oldMediaId = $mediaId;
             Debug::LogEntry('audit', sprintf('Media %s node found with id %d', $type, $mediaId), 'layout', 'Copy');
             // If this is a non region specific type, then move on
             if ($this->IsRegionSpecific($type)) {
                 // Generate a new media id
                 $newMediaId = md5(Kit::uniqueId());
                 $mediaNode->setAttribute('id', $newMediaId);
                 // Copy media security
                 $security = new LayoutMediaGroupSecurity($this->db);
                 $security->CopyAllForMedia($oldLayoutId, $newLayoutId, $mediaId, $newMediaId);
                 continue;
             }
             // Library media assigned to the layout, it will have a lkid
             $lkId = $mediaNode->getAttribute('lkid');
             // Get the regionId
             $regionNode = $mediaNode->parentNode;
             $regionId = $regionNode->getAttribute('id');
             // Do we need to copy this media record?
             if ($copyMedia) {
                 // Take this media item and make a hard copy of it.
                 if (!($mediaId = $mediaObject->Copy($mediaId, $newLayoutName))) {
                     throw new Exception("Error Processing Request", 1);
                 }
                 // Update the permissions for the new media record
                 $mediaSecurity->Copy($oldMediaId, $mediaId);
                 // Copied the media node, so set the ID
                 $mediaNode->setAttribute('id', $mediaId);
                 // Also need to set the options node
                 // Get the stored as value of the new node
                 $sth->execute(array('mediaid' => $mediaId));
                 if (!($row = $sth->fetch())) {
                     $this->ThrowError(25000, __('Unable to find stored value of newly copied media'));
                 }
                 $fileName = Kit::ValidateParam($row['StoredAs'], _STRING);
                 $newNode = $this->DomXml->createElement('uri', $fileName);
                 // Find the old node
                 $uriNodes = $mediaNode->getElementsByTagName('uri');
                 $uriNode = $uriNodes->item(0);
                 // Replace it
                 $uriNode->parentNode->replaceChild($newNode, $uriNode);
                 // Update the permissions for this media on this layout
                 $security = new LayoutMediaGroupSecurity($this->db);
                 $security->CopyAllForMedia($oldLayoutId, $newLayoutId, $oldMediaId, $mediaId);
             } else {
                 // We haven't copied the media file, therefore we only want to copy permissions once per region
                 // this is due to https://github.com/xibosignage/xibo/issues/487
                 if (!isset($copiesMade[$regionId]) || !in_array($mediaId, $copiesMade[$regionId])) {
                     // Update the permissions for this media on this layout
                     $security = new LayoutMediaGroupSecurity($this->db);
                     $security->CopyAllForMedia($oldLayoutId, $newLayoutId, $oldMediaId, $mediaId);
                     $copiesMade[$regionId][] = $mediaId;
                 }
             }
             // Add the database link for this media record
             if (!($newLkId = $this->AddLk($newLayoutId, $regionId, $mediaId))) {
                 throw new Exception("Error Processing Request", 1);
             }
             // Set this LKID on the media node
             $mediaNode->setAttribute('lkid', $newLkId);
         }
         Debug::LogEntry('audit', 'Finished looping through media nodes', 'layout', 'Copy');
         // Set the XML
         $this->SetLayoutXml($newLayoutId, $this->DomXml->saveXML());
         // Layout permissions
         $security = new CampaignSecurity($this->db);
         $security->CopyAll($oldCampaignId, $newCampaignId);
         $security = new LayoutRegionGroupSecurity($this->db);
         $security->CopyAll($oldLayoutId, $newLayoutId);
         // Return the new layout id
         return $newLayoutId;
     } catch (Exception $e) {
         Debug::LogEntry('error', $e->getMessage());
         if (!$this->IsError()) {
             $this->SetError(25000, __('Unable to Copy this Layout'));
         }
         return false;
     }
 }