Bei einigen meiner Projekte habe ich pro Seite ein Hauptbild (für die Kategorienlistung) sowie eine Bildergalerie, z.B. für Produktseiten oder Mitgliedsbetriebe einer Vereinigung. Damit später nicht die Bilder aller Unterseiten in einem Ordner liegen und ein riesiges Durcheinander bilden, macht es Sinn, die Bilder pro Seite in einen eigenen Unterordner zu legen. Silverstripe bietet mit onAfterWrite() einen prima Hook, um diese Ordner automatisch anzulegen.
In meiner abeleiteten Klasse von Page habe ich diesen Ordner sowie das Hauptbild mittels hasOne definiert:
public static $has_one = array(
'Picture' => 'Image',
'RootFolder' => 'Folder'
);
Beim ersten Abspeichern sowie beim Ändern des Titels bzw. des URLSegments wird der Name des Ordners aktualisiert. Da die Bilder verknüpfte Bildergalerie per hasMany verknüpft ist (dazu später mehr), sollte diese Namensänderung nichts machen. Das ganze passiert im Hook onAfterWrite(). Alle Ordner werden in diesem Beispiel als Unterordner von assets/mitglieder/ definiert:
function onAfterWrite()
{
if( $this->ID ) $this->checkFolder();
parent::onAfterWrite();
}
function checkFolder() {
if( ! $this->RootFolderID ) {
$galleries = Folder::findOrMake('mitglieder');
$galleries->Title = 'Mitglieder';
$galleries->write();
$folder = Folder::findOrMake('mitglieder/' . $this->URLSegment);
$folder->Title = $this->Title;
$folder->setName($this->Title);
$folder->write();
$this->RootFolderID = $folder->ID;
$this->write();
}
else {
if ($this->URLSegment) {
$this->RootFolder()->Title = $this->Title;
$this->RootFolder()->setName($this->URLSegment);
$this->RootFolder()->write();
}
}
}
Wichtig ist es, nach Änderungen des Ordnernamens $folder->write() aufzurufen, da sich seit 2.4.1 diese Änderung nicht mehr sofort im Dateisystem wiederfindet.
Das Hauptbild wird im richtigen Ordner abgespeichert, wenn man dem ImageField den Ordnernamen mitgibt (ohne assets/...):
$picfolderName = $this->RootFolder()->FileName;
$picfolderName = str_replace('assets/','',$picfolderName);
$fields->addFieldToTab('Root.Content.Main', new ImageField('Picture','Bild hochladen',null,null,null,$picfolderName));
Da ich bei mehreren Seitentypen eine Bildergalerie benötige, habe ich diese als Dekoratorklasse geschrieben. Sie benutzt den DataObjectManager sowie SWFUpload, um die Bilder hochzuladen:
class PageGallery extends DataObjectDecorator {
function extraStatics() {
return array(
'has_many' => array(
'Gallery' => 'GalleryPic'
)
);
}
function updateCMSFields(&$fields) {
$manager = new FileDataObjectManager(
$this->owner, // Controller
'GalleryPics', // Source name
'GalleryPic', // Source class
'Attachment', // File name on DataObject
array(
'Title' => 'Titel',
'Description' => 'Beschreibung',
'Copyright' => 'Copyright / Quelle'
), // Headings
'getCMSFields_forPopup' // Detail fields (function name or FieldSet object)
// Filter clause
// Sort clause
// Join clause
);
// If undefined, all types are allowed. Pass with or without a leading "."
$manager->setAllowedFileTypes(array('jpg'));
// Label for the upload button in the popup
$manager->setBrowseButtonText("Nur .jpg Bilder sind erlaubt");
// In grid view, what field will appear underneath the icon. If left out, it defaults to the file title.
$manager->setGridLabelField('Name');
// Plural form of the objects being managed. Used on the "Add" button.
// If left out, this defaults to [MyObjectName]s
$manager->setPluralTitle('Bildergalerie');
if ($this->owner->RootFolder()) {
$folderName = $this->owner->RootFolder()->FileName;
$folderName = str_replace('assets/','',$folderName);
$manager->setUploadFolder($folderName);
}
$fields->addFieldToTab("Root.Content.Bilder", $manager);
}
}
Wenn die zu erweiternde Klasse einen "RootFolder" hat, wird dieser automatisch als Ablageplatz für die Galeriebilder verwendet.
Die Erweiterung wird in mysite/_config.php mittels
Dataobject::add_extension('MyPage','PageGallery');
aktiviert.
Als nächstes werde ich die RootFolder Logik auch in eine Dekoratorklasse auslagern.