Jump to:

5450 Posts in 1672 Topics by 1197 members

Customising the CMS

SilverStripe Forums » Customising the CMS » Virtual Page with Custom Fields

Moderators: martimiz, Sean, biapar, Willr, Ingo, swaiba, simon_w

Page: 1
Go to End
Author Topic: 2795 Views
  • Lloydy
    Avatar
    Community Member
    3 Posts

    Virtual Page with Custom Fields Link to this post

    We've been struggling with getting Virtual Pages working on our SilverStripe install. We can successfully use the built in SilverStripe Virtual Page just fine. The challenge is that we are using a number of custom fields to build a right column menu and a bunch of other parts of the page. When we create the Virtual Pages though it only copies over the Content and Title fields, not all of our custom fields.

    So what I've done is I have copied the Sapphire code for Virtual Pages to make our own "Virtual Content Page" for handling this. I'm at the point where I have the "Virtual Content Page" working in exactly the same way as the default "Virtual Page" works. If I uncomment the code below it even shows the needed fields:

          /*
          $fields->addFieldToTab('Root.Content.Main', new TextField('BannerImage'), '');
          $fields->addFieldToTab('Root.Content.Main', new TextField('TopTip1'), '');
          $fields->addFieldToTab('Root.Content.Main', new TextField('TopTip2'), '');
          $fields->addFieldToTab('Root.Content.Main', new TextField('TopTip3'), '');
          $fields->addFieldToTab('Root.Content.Main', new TextField('TopTip4'), '');
          $fields->addFieldToTab('Root.Content.Main', new TextField('TopTip5'), '');
          $fields->addFieldToTab('Root.Content.Main', new TextField('BizToolkit'), '');
          $fields->addFieldToTab('Root.Content.Main', new TextField('Licenses'), '');
          $fields->addFieldToTab('Root.Content.Main', new TextField('Workshops'), '');
          $fields->addFieldToTab('Root.Content.Main', new TextField('Books'), '');
          */

    But I'm stumped as to how I would make those fields populate from the original page and be linked to the original page (In the same way as the content field is).

    I've done a heap of searching and the closest I've been able to find to my problem is this topic:
    http://ssorg.bigbird.silverstripe.com/archive/show/33497

    I'm sure this is within spitting distance of a solution and would be INCREDIBLY thankful to anyone that could help point me in the direction of a solution. I've included both the content of ContentPage.php (Where the extra fields are defined for normal pages) and the full contents of VirtualContentPage.php (A clone of the Sapphire version with a few of my own tweaks).

    Contents of /mysite/code/ContentPage.php

    <?php
    /**
    * Defines the ContentPage page type
    */
    class ContentPage extends Page {
    static $db = array(
    'BannerImage' => 'Text',
    'TopTip1' => 'HTMLText',
    'TopTip2' => 'HTMLText',
    'TopTip3' => 'HTMLText',
    'TopTip4' => 'HTMLText',
    'TopTip5' => 'HTMLText',
    'BizToolkit' => 'HTMLText',
    'Licenses' => 'HTMLText',
    'Workshops' => 'HTMLText',
    'Books' => 'HTMLText'
       );
    static $has_one = array(
       );

    function getCMSFields() {
    $fields = parent::getCMSFields();

    $fields->addFieldToTab('Root.Content.Main', new TextField('BannerImage'), '');
    $fields->addFieldToTab('Root.Content.Main', new TextField('TopTip1'), '');
    $fields->addFieldToTab('Root.Content.Main', new TextField('TopTip2'), '');
    $fields->addFieldToTab('Root.Content.Main', new TextField('TopTip3'), '');
    $fields->addFieldToTab('Root.Content.Main', new TextField('TopTip4'), '');
    $fields->addFieldToTab('Root.Content.Main', new TextField('TopTip5'), '');
    $fields->addFieldToTab('Root.Content.Main', new TextField('BizToolkit'), '');
    $fields->addFieldToTab('Root.Content.Main', new TextField('Licenses'), '');
    $fields->addFieldToTab('Root.Content.Main', new TextField('Workshops'), '');
    $fields->addFieldToTab('Root.Content.Main', new TextField('Books'), '');
       
    return $fields;
    }
    }

    class ContentPage_Controller extends Page_Controller {

    }

    ?>

    Contents of /mysite/code/VirtualContentPage.php

    <?php

    /**
    * @package cms
    */

    /**
    * Virtual Page creates an instance of a page, with the same fields that the original page had, but readonly.
    * This allows you can have a page in mulitple places in the site structure, with different children without duplicating the content
    * Note: This Only duplicates $db fields and not the $has_one etc..
    * @package cms
    */
    class VirtualContentPage extends Page {

       static $add_action = "Virtual content page (another page's content)";
       
       static $icon = array("cms/images/treeicons/page-shortcut-gold","file");
       
       public static $virtualFields;
       
       static $has_one = array(
          "CopyContentFrom" => "SiteTree",
          'BannerImage' => 'Text',
          'TopTip1' => 'HTMLText',
          'TopTip2' => 'HTMLText',
          'TopTip3' => 'HTMLText',
          'TopTip4' => 'HTMLText',
          'TopTip5' => 'HTMLText',
          'BizToolkit' => 'HTMLText',
          'Licenses' => 'HTMLText',
          'Workshops' => 'HTMLText',
           'Books' => 'HTMLText'
       );
       
       static $db = array(
          "VersionID" => "Int",
       );
       
       /**
        * Generates the array of fields required for the page type.
        */
       function getVirtualFields() {
          $nonVirtualFields = array(
             "SecurityTypeID",
             "OwnerID",
             "AssignedToID",
             "RequestedByID",
             "URLSegment",
             "Sort",
             "Status",
             'ShowInMenus',
             'ShowInSearch'
          );

          $allFields = $this->db();
          if($hasOne = $this->has_one()) foreach($hasOne as $link) $allFields[$link . 'ID'] = "Int";
          foreach($allFields as $field => $type) {
             if(!in_array($field, $nonVirtualFields)) $virtualFields[] = $field;
          }
          
          return $virtualFields;
       }

       function ContentSource() {
          return $this->CopyContentFrom();
       }
       
          
       /**
        * Generate the CMS fields from the fields from the original page.
        */
       function getCMSFields($cms = null) {
          $fields = parent::getCMSFields($cms);
          
          // Setup the linking to the original page.
          $copyContentFromField = new TreeDropdownField(
             "CopyContentFromID",
             _t('VirtualContentPage.CHOOSE', "Choose a page to link to"),
             "SiteTree"
          );
          $copyContentFromField->setFilterFunction(create_function('$item', 'return $item->ClassName != "VirtualContentPage";'));
          
          // Setup virtual fields
          if($virtualFields = $this->getVirtualFields()) {
             $roTransformation = new ReadonlyTransformation();
             foreach($virtualFields as $virtualField) {
                if($fields->dataFieldByName($virtualField))
                {
                   $fields->replaceField($virtualField, $fields->dataFieldByName($virtualField)->transform($roTransformation));
                   //$fields->addFieldToTab('Root.Content.Main', $fields->dataFieldByName($virtualField), '');
                }
             }
          }
          
          // Add fields to the tab
          $fields->addFieldToTab("Root.Content.Main",
             new HeaderField(_t('VirtualContentPage.HEADER', "This is a virtual content page")),
             "Title"
          );
          $fields->addFieldToTab("Root.Content.Main", $copyContentFromField, "Title");
          
          /*
          $fields->addFieldToTab('Root.Content.Main', new TextField('BannerImage'), '');
          $fields->addFieldToTab('Root.Content.Main', new TextField('TopTip1'), '');
          $fields->addFieldToTab('Root.Content.Main', new TextField('TopTip2'), '');
          $fields->addFieldToTab('Root.Content.Main', new TextField('TopTip3'), '');
          $fields->addFieldToTab('Root.Content.Main', new TextField('TopTip4'), '');
          $fields->addFieldToTab('Root.Content.Main', new TextField('TopTip5'), '');
          $fields->addFieldToTab('Root.Content.Main', new TextField('BizToolkit'), '');
          $fields->addFieldToTab('Root.Content.Main', new TextField('Licenses'), '');
          $fields->addFieldToTab('Root.Content.Main', new TextField('Workshops'), '');
          $fields->addFieldToTab('Root.Content.Main', new TextField('Books'), '');
          */
          
          // Create links back to the original object in the CMS
          if($this->CopyContentFromID) {
             $linkToContent = "<a class=\"cmsEditlink\" href=\"admin/show/$this->CopyContentFromID\">" .
                _t('VirtualContentPage.EDITCONTENT', 'click here to edit the content') . "</a>";
             $fields->addFieldToTab("Root.Content.Main", new LabelField($linkToContent, null, true), "Title");
          }
       
          return $fields;
       }
       
       /**
        * We have to change it to copy all the content from the original page first.
        */
       function onBeforeWrite() {
          // Don't do this stuff when we're publishing
          if(!$this->extension_instances['Versioned']->migratingVersion) {
              if(isset($this->changed['CopyContentFromID']) && $this->changed['CopyContentFromID']
                       && $this->CopyContentFromID != 0 && $this->class == 'VirtualContentPage' ) {
                $CopyContentFromID = $this->CopyContentFromID;
                $source = DataObject::get_one("SiteTree","`SiteTree`.`ID`='$CopyContentFromID'");
                $this->copyFrom($source);
                $this->URLSegment = $source->URLSegment . '-' . $this->ID;         
             }
          }
          
          parent::onBeforeWrite();
       }
       /**
        * Ensure we have an up-to-date version of everything.
        */
       function copyFrom($source) {
          if($source) {
             foreach($this->getVirtualFields() AS $virtualField)
                $this->$virtualField = $source->$virtualField;
          }
       }
    }

    /**
    * Controller for the virtual page.
    * @package cms
    */
    class VirtualContentPage_Controller extends Page_Controller {

       /**
        * Reloads the content if the version is different ;-)
        */
       function reloadContent() {
          $this->failover->copyFrom($this->failover->CopyContentFrom());
          $this->failover->write();
          return;
       }
       
       /**
        * When the VirtualContentPage is loaded, check to see if the versions are the same
        * if not, reload the content.
        * NOTE: Virtual page must have a container object of subclass of sitetree.
        * We can't load the content without an ID or record to copy it from.
        */
       function init(){
          if($this->record->ID){
             if($this->record->VersionID != $this->failover->CopyContentFrom()->Version){
                $this->reloadContent();
                $this->VersionID = $this->failover->CopyContentFrom()->VersionID;
             }
          }
          parent::init();
       }

       function loadcontentall() {
          $pages = DataObject::get("VirtualContentPage");
          foreach($pages as $page) {
             $page->copyFrom($page->CopyContentFrom());
             $page->write();
             $page->publish("Stage", "Live");
             echo "<li>Published $page->URLSegment";
          }
       }
    }

    ?>

    Thanks in advance!

  • Lloydy
    Avatar
    Community Member
    3 Posts

    Re: Virtual Page with Custom Fields Link to this post

    Bump? Still looking for help with this one. If anybody could suggest anything that would be awesome...

  • Lloydy
    Avatar
    Community Member
    3 Posts

    Re: Virtual Page with Custom Fields Link to this post

    Anybody?

  • Artyom
    Avatar
    Community Member
    22 Posts

    Re: Virtual Page with Custom Fields Link to this post

    This is a known problem w virtual pages... and the SS guys don't seem to want to fix it.

    The solutions I've found are essentially to return the *actual* childpage in a list, for example, which the other posts document.

    Personally, I think this is a really serious design flaw in the framework as it is. It should be able to somehow hand non-tree structures that allow this sort of thing.

    2795 Views
Page: 1
Go to Top

Want to know more about the company that brought you SilverStripe? Then check out SilverStripe.com

Comments on this website? Please give feedback.