1 <?php
2 3 4 5 6 7
8 class VirtualPage extends Page {
9
10 static $icon = array("cms/images/treeicons/page-shortcut-gold","file");
11
12 public static $virtualFields;
13
14 static $has_one = array(
15 "CopyContentFrom" => "SiteTree",
16 );
17
18 static $db = array(
19 "VersionID" => "Int",
20 );
21
22 23 24
25 function getVirtualFields() {
26 $nonVirtualFields = array(
27 "SecurityTypeID",
28 "OwnerID",
29 "URLSegment",
30 "Sort",
31 "Status",
32 'ShowInMenus',
33
34 'ShowInSearch',
35 'Version',
36 "Embargo",
37 "Expiry",
38 );
39
40 $allFields = $this->db();
41 if($hasOne = $this->has_one()) foreach($hasOne as $link) $allFields[$link . 'ID'] = "Int";
42 foreach($allFields as $field => $type) {
43 if(!in_array($field, $nonVirtualFields)) $virtualFields[] = $field;
44 }
45
46 return $virtualFields;
47 }
48
49 function CopyContentFrom() {
50 if(empty($this->record['CopyContentFromID'])) return new SiteTree();
51
52 if(!isset($this->components['CopyContentFrom'])) {
53 $this->components['CopyContentFrom'] = DataObject::get_by_id("SiteTree",
54 $this->record['CopyContentFromID']);
55
56
57 if($this->components['CopyContentFrom'] instanceof VirtualPage) {
58 $this->components['CopyContentFrom'] = null;
59 }
60
61
62 if(!$this->components['CopyContentFrom']) {
63 $this->components['CopyContentFrom'] = new SiteTree();
64 }
65 }
66
67 return $this->components['CopyContentFrom'];
68 }
69 function setCopyContentFromID($val) {
70 if(DataObject::get_by_id('SiteTree', (int)$val) instanceof VirtualPage) $val = 0;
71 return $this->setField("CopyContentFromID", $val);
72 }
73
74 function ContentSource() {
75 return $this->CopyContentFrom();
76 }
77
78 function allowedChildren() {
79 if($this->CopyContentFrom()) {
80 return $this->CopyContentFrom()->allowedChildren();
81 }
82 }
83
84 public function syncLinkTracking() {
85 if($this->CopyContentFromID) {
86 $this->HasBrokenLink = !(bool) DataObject::get_by_id('SiteTree', $this->CopyContentFromID);
87 } else {
88 $this->HasBrokenLink = true;
89 }
90 }
91
92 93 94
95 public function canPublish($member = null) {
96 return $this->isPublishable() && parent::canPublish($member);
97 }
98
99 100 101 102
103 public function canDeleteFromLive($member = null) {
104 return parent::canPublish($member);
105 }
106
107 108 109 110 111 112
113 public function isPublishable() {
114
115 if(!$this->CopyContentFrom() || !$this->CopyContentFrom()->ID) {
116 return false;
117 }
118
119
120 if(!Versioned::get_versionnumber_by_stage('SiteTree', 'Live', $this->CopyContentFromID)) {
121 return false;
122 }
123
124
125 return true;
126 }
127
128 129 130
131 function getCMSFields($cms = null) {
132 $fields = parent::getCMSFields($cms);
133
134
135 $copyContentFromField = new TreeDropdownField(
136 "CopyContentFromID",
137 _t('VirtualPage.CHOOSE', "Choose a page to link to"),
138 "SiteTree"
139 );
140
141
142
143
144 if($virtualFields = $this->getVirtualFields()) {
145 $roTransformation = new ReadonlyTransformation();
146 foreach($virtualFields as $virtualField) {
147 if($fields->dataFieldByName($virtualField))
148 $fields->replaceField($virtualField, $fields->dataFieldByName($virtualField)->transform($roTransformation));
149 }
150 }
151
152
153 $fields->addFieldToTab("Root.Content.Main",
154 new HeaderField('VirtualPageHeader',_t('VirtualPage.HEADER', "This is a virtual page")),
155 "Title"
156 );
157 $fields->addFieldToTab("Root.Content.Main", $copyContentFromField, "Title");
158
159
160 if($this->CopyContentFrom()->ID) {
161 $linkToContent = "<a class=\"cmsEditlink\" href=\"admin/show/$this->CopyContentFromID\">" .
162 _t('VirtualPage.EDITCONTENT', 'click here to edit the content') . "</a>";
163 $fields->addFieldToTab("Root.Content.Main",
164 $linkToContentLabelField = new LabelField('VirtualPageContentLinkLabel', $linkToContent),
165 "Title"
166 );
167 $linkToContentLabelField->setAllowHTML(true);
168 }
169
170 return $fields;
171 }
172
173 174 175
176 function onBeforeWrite() {
177
178 if($this->extension_instances['Versioned']->migratingVersion
179 && Versioned::current_stage() == 'Live') {
180 if($this->CopyContentFromID) {
181 $performCopyFrom = true;
182
183 $stageSourceVersion = DB::query("SELECT \"Version\" FROM \"SiteTree\" WHERE \"ID\" = $this->CopyContentFromID")->value();
184 $liveSourceVersion = DB::query("SELECT \"Version\" FROM \"SiteTree_Live\" WHERE \"ID\" = $this->CopyContentFromID")->value();
185
186
187
188 if($stageSourceVersion != $liveSourceVersion) {
189 $this->extension_instances['Versioned']->migratingVersion = null;
190 }
191 }
192
193
194
195 } else {
196 $performCopyFrom = $this->isChanged('CopyContentFromID') && $this->CopyContentFromID != 0;
197 }
198
199
200 if($performCopyFrom && $this instanceof VirtualPage) {
201
202 singleton('SiteTree')->flushCache();
203 $source = DataObject::get_one("SiteTree",sprintf('"SiteTree"."ID" = %d', $this->CopyContentFromID));
204
205 $this->copyFrom($source, false);
206 $this->URLSegment = $source->URLSegment;
207 }
208
209 parent::onBeforeWrite();
210 }
211
212 function onAfterWrite() {
213 parent::onAfterWrite();
214
215
216 if(!$this->extension_instances['Versioned']->migratingVersion) {
217 if(
218 $this->isChanged('CopyContentFromID')
219 && $this->CopyContentFromID != 0
220 && $this instanceof VirtualPage
221 ) {
222 $this->updateImageTracking();
223 }
224 }
225
226 FormResponse::add("$('Form_EditForm').reloadIfSetTo($this->ID);", $this->ID."_VirtualPage_onAfterWrite");
227 }
228
229 230 231
232 function copyFrom($source, $updateImageTracking = true) {
233 if($source) {
234 foreach($this->getVirtualFields() as $virtualField) {
235 $this->$virtualField = $source->$virtualField;
236 }
237
238
239
240 if($this->isChanged('CopyContentFromID')) {
241 $this->ShowInMenus = $source->ShowInMenus;
242 }
243
244 if($updateImageTracking) $this->updateImageTracking();
245 }
246 }
247
248 function updateImageTracking() {
249
250 if(!$this->ID) return;
251
252
253 unset($this->components['CopyContentFrom']);
254
255
256 $ID = $this->CopyContentFrom()->ID;
257 $class = $this->CopyContentFrom()->ClassName;
258
259
260 if ($copyContentFromTracking = DataObjectFileTracking::get_track($ID, $class)) {
261 $trackingData = array();
262 foreach($copyContentFromTracking as $entry) {
263 if (isset($trackingData[$entry->FieldName])) {
264 $trackingData[$entry->FieldName][] = $entry->FileID;
265 } else {
266 $trackingData[$entry->FieldName] = array($entry->FileID);
267 }
268 }
269
270
271 $virtualFields = $this->getVirtualFields();
272 foreach($trackingData as $fieldName=>$IDs) {
273 if (in_array($fieldName, $virtualFields)) {
274 DataObjectFileTracking::update_track($this->ID, $this->ClassName, $fieldName, $IDs);
275 }
276 }
277 }
278 }
279
280 281 282 283 284 285 286
287 function __get($field) {
288 if(parent::hasMethod($funcName = "get$field")) {
289 return $this->$funcName();
290 } else if(parent::hasField($field)) {
291 return $this->getField($field);
292 } else {
293 return $this->copyContentFrom()->$field;
294 }
295 }
296
297 298 299 300 301 302
303 function __call($method, $args) {
304 if(parent::hasMethod($method)) {
305 return parent::__call($method, $args);
306 } else {
307 return call_user_func_array(array($this->copyContentFrom(), $method), $args);
308 }
309 }
310
311 public function hasField($field) {
312 return (
313 array_key_exists($field, $this->record)
314 || $this->hasDatabaseField($field)
315 || array_key_exists($field, $this->db())
316 || parent::hasMethod("get{$field}")
317 || $this->CopyContentFrom()->hasField($field)
318 );
319 }
320 321 322 323 324 325
326 function hasMethod($method) {
327 if(parent::hasMethod($method)) return true;
328 return $this->copyContentFrom()->hasMethod($method);
329 }
330 }
331
332 333 334 335
336 class VirtualPage_Controller extends Page_Controller {
337
338 static $allowed_actions = array(
339 'loadcontentall' => 'ADMIN',
340 );
341
342 343 344
345 function reloadContent() {
346 $this->failover->copyFrom($this->failover->CopyContentFrom());
347 $this->failover->write();
348 return;
349 }
350
351 function getViewer($action) {
352 $originalClass = get_class($this->CopyContentFrom());
353 if ($originalClass == 'SiteTree') $name = 'Page_Controller';
354 else $name = $originalClass."_Controller";
355 $controller = new $name();
356 return $controller->getViewer($action);
357 }
358
359 360 361 362 363 364
365 function init(){
366 if(isset($this->record) && $this->record->ID){
367 if($this->record->VersionID != $this->failover->CopyContentFrom()->Version){
368 $this->reloadContent();
369 $this->VersionID = $this->failover->CopyContentFrom()->VersionID;
370 }
371 }
372 parent::init();
373 }
374
375 function loadcontentall() {
376 $pages = DataObject::get("VirtualPage");
377 foreach($pages as $page) {
378 $page->copyFrom($page->CopyContentFrom());
379 $page->write();
380 $page->publish("Stage", "Live");
381 echo "<li>Published $page->URLSegment";
382 }
383 }
384
385 386 387 388 389 390
391 function hasMethod($method) {
392 $haveIt = parent::hasMethod($method);
393 if (!$haveIt) {
394 $originalClass = get_class($this->CopyContentFrom());
395 if ($originalClass == 'SiteTree') $name = 'ContentController';
396 else $name = $originalClass."_Controller";
397 $controller = new $name($this->dataRecord->copyContentFrom());
398 $haveIt = $controller->hasMethod($method);
399 }
400 return $haveIt;
401 }
402
403 404 405 406 407 408
409 function __call($method, $args) {
410 try {
411 return parent::__call($method, $args);
412 } catch (Exception $e) {
413
414
415 if ($e->getCode() !== 2175) throw $e;
416 $original = $this->copyContentFrom();
417 $originalClass = get_class($original);
418 if ($originalClass == 'SiteTree') $name = 'ContentController';
419 else $name = $originalClass."_Controller";
420 $controller = new $name($this->dataRecord->copyContentFrom());
421 return call_user_func_array(array($controller, $method), $args);
422 }
423 }
424 }
425
426 ?>
427
[Raise a SilverStripe Framework issue/bug](https://github.com/silverstripe/silverstripe-framework/issues/new)
- [Raise a SilverStripe CMS issue/bug](https://github.com/silverstripe/silverstripe-cms/issues/new)
- Please use the
Silverstripe Forums to ask development related questions.
-