public function augmentSQL(SQLQuery &$query, DataQuery &$dataQuery = null)
 {
     // Actives locales defined on a SiteConfig are there as a global setting
     if ($this->owner instanceof SiteConfig) {
         return;
     }
     // In admin, show everthing anyway
     if ($this->isAdminBackend()) {
         return;
     }
     // Find in set is only compatible with MySql
     $c = DB::getConn();
     if (!$c instanceof MySQLDatabase) {
         return;
     }
     $locale = $dataQuery->getQueryParam('Fluent.Locale') ?: Fluent::current_locale();
     $from = $query->getFrom();
     $where = $query->getWhere();
     $column = 'ActiveLocales';
     $table = null;
     // Check on which table is the ActiveLocales field
     foreach ($from as $fromTable => $conditions) {
         if ($table === null) {
             $table = $fromTable;
         }
         $db = DataObject::custom_database_fields($fromTable);
         if ($db && isset($db[$column])) {
             $table = $fromTable;
             break;
         }
     }
     $identifier = "\"{$table}\".\"{$column}\"";
     $where[] = "{$identifier} IS NULL OR FIND_IN_SET ('{$locale}', {$identifier}) > 0";
     $query->setWhere($where);
 }
 /**
  * Determines the fields to translate on the given class
  *
  * @return array List of field names and data types
  */
 public static function translated_fields_for($class)
 {
     if (isset(self::$translated_fields_for_cache[$class])) {
         return self::$translated_fields_for_cache[$class];
     }
     return self::$translated_fields_for_cache[$class] = self::without_fluent_fields(function () use($class) {
         $db = DataObject::custom_database_fields($class);
         $filter = Config::inst()->get($class, 'translate', Config::UNINHERITED);
         if ($filter === 'none') {
             return array();
         }
         // Data and field filters
         $fieldsInclude = Fluent::config()->field_include;
         $fieldsExclude = Fluent::config()->field_exclude;
         $dataInclude = Fluent::config()->data_include;
         $dataExclude = Fluent::config()->data_exclude;
         // filter out DB
         if ($db) {
             foreach ($db as $field => $type) {
                 if (!empty($filter)) {
                     // If given an explicit field name filter, then remove non-presented fields
                     if (!in_array($field, $filter)) {
                         unset($db[$field]);
                     }
                 } else {
                     // Without a name filter then check against each filter type
                     if ($fieldsInclude && !Fluent::any_match($field, $fieldsInclude) || $fieldsExclude && Fluent::any_match($field, $fieldsExclude) || $dataInclude && !Fluent::any_match($type, $dataInclude) || $dataExclude && Fluent::any_match($type, $dataExclude)) {
                         unset($db[$field]);
                     }
                 }
             }
         }
         return $db;
     });
 }
 /**
  * Returns a filtered list of fields which could contain shortcodes.
  *
  * @param String
  * @return Array Map of class names to an array of field names on these classes.
  */
 function getShortcodeFields($class)
 {
     $fields = array();
     $ancestry = array_values(ClassInfo::dataClassesFor($class));
     foreach ($ancestry as $ancestor) {
         if (ClassInfo::classImplements($ancestor, 'TestOnly')) {
             continue;
         }
         $ancFields = DataObject::custom_database_fields($ancestor);
         if ($ancFields) {
             foreach ($ancFields as $ancFieldName => $ancFieldSpec) {
                 if (preg_match($this->fieldSpecRegex, $ancFieldSpec)) {
                     if (!@$fields[$ancestor]) {
                         $fields[$ancestor] = array();
                     }
                     $fields[$ancestor][$ancFieldName] = $ancFieldSpec;
                 }
             }
         }
     }
     return $fields;
 }
Example #4
0
 public function forApi()
 {
     $fields = array_merge(array('ID', 'ClassName', 'LastEdited', 'Title', 'MenuTitle', 'ShowInMenus', 'ShowInSearch', 'Sort', 'ParentID', 'Content', 'Sections'), array_keys(DataObject::custom_database_fields($this->ClassName)));
     return $this->getFieldsForAPI($fields);
 }
 /**
  * Test that db fields for a translated objects is correctly extended
  */
 public function testTranslateFields()
 {
     $db = DataObject::custom_database_fields('FluentTest_TranslatedObject');
     ksort($db);
     $this->assertEquals(array('Description' => 'Text', 'Description_en_NZ' => 'Text', 'Description_en_US' => 'Text', 'Description_es_ES' => 'Text', 'Description_fr_CA' => 'Text', 'ImageID' => 'ForeignKey', 'ImageID_en_NZ' => 'Int', 'ImageID_en_US' => 'Int', 'ImageID_es_ES' => 'Int', 'ImageID_fr_CA' => 'Int', 'Title' => 'Varchar(255)', 'Title_en_NZ' => 'Varchar(255)', 'Title_en_US' => 'Varchar(255)', 'Title_es_ES' => 'Varchar(255)', 'Title_fr_CA' => 'Varchar(255)', 'URLKey' => 'Text'), $db);
 }
 /**
  * Getter similar to DataObject::get(); returns a SS_List of products filtered by the requirements in self::getRequiredAttributes();
  * If an product is free of charge, it can have no price. This is for giveaways and gifts.
  *
  * Expected format of $joins:
  * <pre>
  * array(
  *      array(
  *          'table' => 'JoinTableName_1',
  *          'on'    => 'JoinTableOnClause_1',
  *          'alias' => 'JoinTableAlias_1',
  *      ),
  *      array(
  *          'table' => 'JoinTableName_2',
  *          'on'    => 'JoinTableOnClause_2',
  *          'alias' => 'JoinTableAlias_2',
  *      ),
  *      ...
  * )
  * </pre>
  * 
  * @param string  $whereClause to be inserted into the sql where clause
  * @param string  $sort        string with sort clause
  * @param array   $joins       left join data as multi dimensional array
  * @param integer $limit       DataObject limit
  *
  * @return PaginatedList|ArrayList PaginatedList of products or empty ArrayList
  * 
  * @author Roland Lehmann <*****@*****.**>,
  *         Sebastian Diel <*****@*****.**>
  * @since 03.02.2015
  */
 public static function getProducts($whereClause = "", $sort = null, $joins = null, $limit = null)
 {
     $requiredAttributes = self::getRequiredAttributes();
     $pricetype = SilvercartConfig::Pricetype();
     $filter = "";
     if (!empty($requiredAttributes)) {
         foreach ($requiredAttributes as $requiredAttribute) {
             //find out if we are dealing with a real attribute or a multilingual field
             if (array_key_exists($requiredAttribute, DataObject::custom_database_fields('SilvercartProduct')) || $requiredAttribute == "Price") {
                 if ($requiredAttribute == "Price") {
                     // Gross price as default if not defined
                     if ($pricetype == "net") {
                         $filter .= sprintf("(PriceNetAmount != 0.0) AND ");
                     } else {
                         $filter .= sprintf("(PriceGrossAmount != 0.0) AND ");
                     }
                 } else {
                     $filter .= sprintf("%s != '' AND ", $requiredAttribute);
                 }
             } else {
                 // if its a multilingual attribute it comes from a relational class
                 $filter .= sprintf("SilvercartProductLanguage.%s != '' AND ", $requiredAttribute);
             }
         }
     }
     if ($whereClause != "") {
         $filter = $filter . $whereClause . ' AND ';
     }
     $filter .= 'isActive = 1 AND SilvercartProductGroupID > 0';
     if ($sort === null) {
         $sort = self::defaultSort();
     }
     $onclause = sprintf('"SPL"."SilvercartProductID" = "SilvercartProduct"."ID" AND "SPL"."Locale" = \'%s\'', Translatable::get_current_locale());
     $databaseFilteredProducts = SilvercartProduct::get()->leftJoin('SilvercartProductLanguage', $onclause, 'SPL')->where($filter)->sort($sort);
     if (!is_null($joins) && is_array($joins)) {
         foreach ($joins as $joinData) {
             $table = $alias = $joinData['table'];
             $onClause = $joinData['on'];
             if (array_key_exists('alias', $joinData)) {
                 $alias = $joinData['alias'];
             }
             $databaseFilteredProducts = $databaseFilteredProducts->leftJoin($table, $onClause, $alias);
         }
     }
     if (!is_null($limit)) {
         $offset = 0;
         if (strpos($limit, ',') !== false) {
             list($offset, $limit) = explode(',', $limit);
         }
         $databaseFilteredProducts = $databaseFilteredProducts->limit($limit, $offset);
     }
     if (Controller::curr()->hasMethod('getProductsPerPageSetting') && $databaseFilteredProducts) {
         $databaseFilteredProducts = new PaginatedList($databaseFilteredProducts, $_GET);
         $databaseFilteredProducts->setPageLength(Controller::curr()->getProductsPerPageSetting());
     }
     return $databaseFilteredProducts;
 }
 /**
  * used to find the imported DataObject, e.g not relations
  * @param $proxy
  * @param $fields
  * @return mixed
  */
 private function findDataObject(ProxyObject $proxy, $fields)
 {
     $filters = array();
     $uniqueFields = array();
     foreach ($this->uniqueFields as $field) {
         if (strpos($field, '.') === false) {
             $uniqueFields[] = $field;
         }
     }
     if (empty($uniqueFields)) {
         return null;
     }
     $class = $this->dataObjectClass;
     $dbFields = DataObject::custom_database_fields($class);
     foreach ($this->uniqueFields as $field) {
         if (isset($fields[$field]) && isset($dbFields[$field])) {
             $values = $this->getProxyFieldValues($proxy, $fields[$field]);
             $filters[$field] = empty($values) ? null : $values[0];
         } else {
             // throw error, field does not exist
         }
     }
     return $class::get()->filter($filters)->first();
 }
 /**
  * Returns the active products for this page.
  *
  * @return DataObject
  * 
  * @author Sebastian Diel <*****@*****.**>,
  *         Sascha Koehler <*****@*****.**>
  * @since 27.07.2015
  */
 public function ActiveSilvercartProducts()
 {
     if (!array_key_exists($this->ID, self::$activeSilvercartProducts)) {
         $requiredAttributes = SilvercartProduct::getRequiredAttributes();
         $activeProducts = array();
         $productGroupIDs = self::getFlatChildPageIDsForPage($this->ID);
         $translations = $this->getTranslations();
         if ($translations && $translations->count() > 0) {
             foreach ($translations as $translation) {
                 $productGroupIDs = array_merge($productGroupIDs, self::getFlatChildPageIDsForPage($translation->ID));
             }
         }
         $filter = array('');
         if (!empty($requiredAttributes)) {
             foreach ($requiredAttributes as $requiredAttribute) {
                 //find out if we are dealing with a real attribute or a multilingual field
                 if (array_key_exists($requiredAttribute, DataObject::custom_database_fields('SilvercartProduct')) || $requiredAttribute == "Price") {
                     if ($requiredAttribute == "Price") {
                         // Gross price as default if not defined
                         if (SilvercartConfig::Pricetype() == "net") {
                             $filter[] = sprintf('("PriceNetAmount" != 0.0)');
                         } else {
                             $filter[] = sprintf('("PriceGrossAmount" != 0.0)');
                         }
                     } else {
                         $filter[] = sprintf('"%s" != \'\'', $requiredAttribute);
                     }
                 } else {
                     // if its a multilingual attribute it comes from a relational class
                     $filter[] = sprintf("SilvercartProductLanguage.%s != ''", $requiredAttribute);
                 }
             }
         }
         if (count($filter) == 1) {
             $filter = array();
         }
         $filterString = sprintf("isActive = 1\n                     AND (SilvercartProductGroupID IN (%s)\n                         OR ID IN (\n                            SELECT\n                                SilvercartProductID\n                            FROM\n                                SilvercartProduct_SilvercartProductGroupMirrorPages\n                            WHERE\n                                SilvercartProductGroupPageID IN (%s)))\n                     %s", implode(',', $productGroupIDs), implode(',', $productGroupIDs), implode(' AND ', $filter));
         $this->extend('updateActiveSilvercartProductsFilter', $filterString);
         $records = DB::query(sprintf("SELECT\n                        ID\n                     FROM\n                        SilvercartProduct\n                     WHERE\n                        %s", $filterString));
         foreach ($records as $record) {
             $activeProducts[] = $record['ID'];
         }
         self::$activeSilvercartProducts[$this->ID] = $activeProducts;
     }
     $result = new DataObject();
     $result->ID = count(self::$activeSilvercartProducts[$this->ID]);
     $result->Count = count(self::$activeSilvercartProducts[$this->ID]);
     $result->IDs = self::$activeSilvercartProducts[$this->ID];
     return $result;
 }
Example #9
0
 /**
  * Creates pages into the SiteTree and populates them with content
  *
  * @see "silversmith help"	 
  * @param The parameters, e.g. from the command line
  */
 public static function seed_content($params = array())
 {
     if (!isset($params[2])) {
         fail("Usage: silversmith seed-content <class name>");
     }
     $className = $params[2];
     if (!class_exists($className)) {
         fail("Class {$className} does not exist!");
     }
     $parentField = !isset($params['parent-field']) ? "ParentID" : $params['parent-field'];
     $parent = !isset($params['parent']) ? false : $params['parent'];
     $count = !isset($params['count']) ? 10 : (int) $params['count'];
     $seedingLevel = !isset($params['seeding-level']) ? 3 : $params['seeding-level'];
     $verbose = isset($params['verbose']);
     $site_tree = is_subclass_of($className, "SiteTree");
     if (!$site_tree && $seedingLevel < 2) {
         fail("For non SiteTree objects, a seeding level of at least 2 is required.");
     }
     if ($parent) {
         if (is_numeric($parent)) {
             $parentObj = DataList::create("SiteTree")->byId((int) $parent);
             if (!$parentObj) {
                 fail("Page #{$parent} could not be found.");
             }
         } else {
             $parentObj = SiteTree::get_by_link($parent);
             if (!$parentObj) {
                 $parentObj = DataList::create("SiteTree")->where("Title = '" . trim($parent) . "'")->first();
             }
             if (!$parentObj) {
                 fail("Page '{$parent}' could not be found.");
             }
         }
     }
     $sample = Folder::find_or_make("silversmith-samples");
     if (!$sample->hasChildren()) {
         $answer = ask("This project does not have sample assets installed, which can be useful for content seeding. Do you want to install them now? (y/n)");
         if (strtolower(trim($answer)) == "y") {
             SilverSmith::add_sample_assets();
         }
     }
     for ($i = 0; $i < $count; $i++) {
         $p = new $className();
         if ($site_tree) {
             $p->Title = SilverSmithUtil::get_lipsum_words(rand(2, 5));
             $p->Content = SilverSmithUtil::get_default_content($p->obj('Content'), $seedingLevel);
             state("New {$className} created...");
             $p->Status = "Published";
         }
         if ($parent) {
             $p->{$parentField} = $parentObj->ID;
         }
         state("Seeding...");
         $p->write();
         state("Adding content...");
         SilverSmithUtil::add_default_content($p, $seedingLevel);
         $p->write();
         if ($site_tree) {
             $p->publish("Stage", "Live");
         }
         state("Done.\n");
         if ($verbose) {
             say("Debug output:");
             $fields = array_keys(DataObject::custom_database_fields($className));
             foreach (array_merge($p->has_many(), $p->many_many()) as $relation => $class) {
                 $fields[] = $relation;
                 if ($p->{$relation}()->exists()) {
                     $p->{$relation} = implode(',', $p->{$relation}()->column('ID'));
                 } else {
                     $p->{$relation} = "(none)";
                 }
             }
             if ($site_tree) {
                 $fields = array_merge(array('Title'), $fields);
             }
             foreach ($fields as $field) {
                 say("{$field}: {$p->{$field}}");
             }
         }
     }
 }
 /**
  * @param $className
  * @param $options
  * @param null $key
  * @return Field
  * @throws Exception
  */
 private function createObjectField($className, $options, $key = null)
 {
     $field = new Field();
     $field->dataType = $className;
     if (!is_array($options)) {
         $options = $this->parseProviderOptions($options);
     }
     $field->key = $key ?: $className;
     $field->options = $options;
     $object = singleton($className);
     $ancestry = array();
     foreach ($object->getClassAncestry() as $className) {
         $classObject = singleton($className);
         $ancestry[] = $classObject;
     }
     $field->ancestry = $ancestry;
     $ignoreFields = $this->getIgnoreFields($field, $options);
     $ignoreLookup = array();
     foreach ($ignoreFields as $ignoreField) {
         $ignoreLookup[$ignoreField] = $ignoreField;
     }
     $properties = isset($options['fields']) ? $options['fields'] : array();
     $fields = array();
     $hasOneFields = array();
     $hasManyFields = array();
     $manyManyFields = array();
     foreach ($field->ancestry as $classObject) {
         foreach (\DataObject::custom_database_fields($classObject->ClassName) as $fieldName => $fieldType) {
             $ignored = isset($ignoreLookup[$fieldName]) && !isset($properties[$fieldName]);
             if ($fieldType !== 'ForeignKey' && !$ignored) {
                 $fields[$fieldName] = $fieldType;
             }
         }
         foreach ($classObject->has_one() as $fieldName => $className) {
             $ignored = isset($ignoreLookup[$fieldName]) && !isset($properties[$fieldName]);
             if (!$ignored && isset($options['fields']) && array_key_exists($fieldName, $options['fields'])) {
                 $hasOneFields[$fieldName] = $className;
             }
         }
         // limit to fields that specify use
         foreach ($classObject->has_many() as $fieldName => $className) {
             $ignored = isset($ignoreLookup[$fieldName]) && !isset($properties[$fieldName]);
             if (!$ignored && isset($options['fields']) && array_key_exists($fieldName, $options['fields'])) {
                 $hasManyFields[$fieldName] = $className;
             }
         }
         // limit to fields that specify use
         foreach ($classObject->many_many() as $fieldName => $className) {
             $ignored = isset($ignoreLookup[$fieldName]) && !isset($properties[$fieldName]);
             if (!$ignored && isset($options['fields']) && array_key_exists($fieldName, $options['fields'])) {
                 $manyManyFields[$fieldName] = $className;
             }
         }
     }
     $properties = array_merge($this->getDefaultProperties($field), $properties);
     foreach ($fields as $fieldName => $dataType) {
         $fieldOptions = isset($properties[$fieldName]) ? $properties[$fieldName] : array();
         $fieldObject = $this->createField($dataType, $fieldOptions);
         $fieldObject->fieldName = $fieldName;
         $fieldObject->name = $fieldName;
         $fieldObject->parent = $field;
         $field->fields[] = $fieldObject;
     }
     foreach ($hasOneFields as $fieldName => $className) {
         $fieldOptions = isset($properties[$fieldName]) ? $properties[$fieldName] : array();
         $fieldObject = $this->createObjectField($className, $fieldOptions, $field->key);
         $fieldObject->fieldType = Field::FT_HAS_ONE;
         $fieldObject->name = $fieldName;
         $fieldObject->fieldName = $fieldName . 'ID';
         $fieldObject->methodName = $fieldName;
         $fieldObject->parent = $field;
         $fieldObject->count = 1;
         $field->hasOne[] = $fieldObject;
     }
     foreach ($hasManyFields as $fieldName => $className) {
         $fieldOptions = isset($properties[$fieldName]) ? $properties[$fieldName] : array();
         $fieldObject = $this->createObjectField($className, $fieldOptions, $field->key);
         $fieldObject->fieldType = Field::FT_HAS_MANY;
         $fieldObject->name = $fieldName;
         $fieldObject->methodName = $fieldName;
         $fieldObject->parent = $field;
         if (isset($fieldOptions['count']) && is_int($fieldOptions['count'])) {
             $fieldObject->count = $fieldOptions['count'];
         }
         $field->hasMany[] = $fieldObject;
     }
     foreach ($manyManyFields as $fieldName => $className) {
         $fieldOptions = isset($properties[$fieldName]) ? $properties[$fieldName] : array();
         $fieldObject = $this->createObjectField($className, $fieldOptions, $field->key);
         $fieldObject->fieldType = Field::FT_MANY_MANY;
         $fieldObject->name = $fieldName;
         $fieldObject->methodName = $fieldName;
         $fieldObject->parent = $field;
         if (isset($fieldOptions['count']) && is_int($fieldOptions['count'])) {
             $fieldObject->count = $fieldOptions['count'];
         }
         $field->manyMany[] = $fieldObject;
     }
     $this->setProvider($field, $options);
     return $field;
 }
 public function __construct($record = null, $isSingleton = false, $model = null)
 {
     //check what the constructor was passed
     $dmsObject = null;
     if ($record && is_subclass_of($record, 'DMSDocumentInterface')) {
         $dmsObject = $record;
         $record = null;
         //cancel the record creation to just create an empty object
     }
     //create the object
     parent::__construct($record, $isSingleton, $model);
     //copy the DMSDocument object, if passed into the constructor
     if ($dmsObject) {
         foreach (array_keys(DataObject::custom_database_fields($dmsObject->ClassName)) as $key) {
             $this->{$key} = $dmsObject->{$key};
         }
     }
 }
 /**
  * Generates a GraphViz dot template
  *
  * @return String a dot compatible data format
  */
 public function dot()
 {
     $opt = array();
     $opt['location'] = $this->paramDefault('location', 'mysite');
     $opt['ancestry'] = $this->paramDefault('ancestry', 1, 'numeric');
     $opt['relations'] = $this->paramDefault('relations', 1, 'numeric');
     $opt['fields'] = $this->paramDefault('fields', 1, 'numeric');
     $opt['include_root'] = $this->paramDefault('include-root', 0, 'numeric');
     $opt['exclude'] = $this->paramDefault('exclude');
     $opt['group'] = $this->paramDefault('group', 0, 'numeric');
     $opt['rankdir'] = $this->paramDefault('rankdir');
     if (!in_array($opt['rankdir'], array('LR', 'TB', 'BT', 'RL'))) {
         $opt['rankdir'] = 'TB';
     }
     $renderClasses = array();
     //Get all DataObject subclasses
     $dataClasses = ClassInfo::subclassesFor('DataObject');
     //Remove DataObject itself
     array_shift($dataClasses);
     //Get all classes in a specific folder(s)
     $folders = explode(",", $opt['location']);
     $folderClasses = array();
     foreach ($folders as $folder) {
         if (!empty($folder)) {
             $folderClasses[$folder] = ClassInfo::classes_for_folder($folder);
         }
     }
     $excludeArray = explode(",", $opt['exclude']);
     //Get the intersection of the two - grouped by the folder
     foreach ($dataClasses as $key => $dataClass) {
         foreach ($folderClasses as $folder => $classList) {
             foreach ($classList as $folderClass) {
                 if (strtolower($dataClass) == strtolower($folderClass)) {
                     //Remove all excluded classes
                     if (!in_array($dataClass, $excludeArray)) {
                         $renderClasses[$folder][$dataClass] = $dataClass;
                     }
                 }
             }
         }
     }
     if (count($renderClasses) == 0) {
         user_error("No classes that extend DataObject found in location: " . Convert::raw2xml($opt['location']));
     }
     $folders = new ArrayList();
     foreach ($renderClasses as $folderName => $classList) {
         $folder = new DataObject();
         $folder->Name = $folderName;
         $folder->Group = $opt['group'] == 1;
         $classes = new ArrayList();
         foreach ($classList as $className) {
             //Create a singleton of the class, to use for has_one,etc  instance methods
             $singleton = singleton($className);
             //Create a blank DO to use for rendering on the template
             $class = new DataObject();
             $class->ClassName = $className;
             //Get all the data fields for the class
             //fields = 0 - No fields
             //fields = 1 - only uninherited fields
             //fields = 2 - inherited fields
             $fields = new ArrayList();
             if ($opt['fields'] > 0) {
                 if ($opt['fields'] > 1) {
                     $dataFields = $singleton->inheritedDatabaseFields();
                 } else {
                     $dataFields = DataObject::custom_database_fields($className);
                 }
                 $fields = self::formatDataFields($dataFields, $fields);
             }
             $class->FieldList = $fields;
             if ($opt['relations'] > 1) {
                 $config = Config::INHERITED;
             } else {
                 $config = Config::UNINHERITED;
             }
             $hasOneArray = Config::inst()->get($className, 'has_one', $config);
             $hasManyArray = Config::inst()->get($className, 'has_many', $config);
             $manyManyArray = Config::inst()->get($className, 'many_many', $config);
             //TODO - what's the difference between:
             /*
             $hasOneArray = Config::inst()->get($className, 'has_one');
             $hasManyArray = Config::inst()->get($className, 'has_many');
             $manyManyArray = Config::inst()->get($className, 'many_many');
             
             //and
             
             $hasOneArray = $singleton->has_one();
             $hasManyArray = $singleton->has_many();
             $manyManyArray = $singleton->many_many();
             //Note - has_() calls are verbose - they retrieve relations all the way down to base class
             // ?? eg; for SiteTree, BackLinkTracking is a belongs_many_many
             */
             //$belongsToArray = $singleton->belongs_to();
             //print_r(ClassInfo::ancestry($className));
             //print_r($singleton->getClassAncestry());
             //Add parent class to HasOne
             //Remove the default "Parent" because thats the final parent, rather than the immediate parent
             unset($hasOneArray["Parent"]);
             $classAncestry = ClassInfo::ancestry($className);
             //getClassAncestry returns an array ordered from root to called class - to get parent, reverse and remove top element (called class)
             $classAncestry = array_reverse($classAncestry);
             array_shift($classAncestry);
             $parentClass = reset($classAncestry);
             $hasOneArray["Parent"] = $parentClass;
             //Ensure DataObject is not shown if include-root = 0
             if ($opt['include_root'] == 0 && $parentClass == "DataObject") {
                 unset($hasOneArray["Parent"]);
             }
             //if ancestry = 0, remove the "Parent" relation in has_one
             if ($opt['ancestry'] == 0 && isset($hasOneArray["Parent"])) {
                 unset($hasOneArray["Parent"]);
             }
             //if relations = 0, remove all but the parent relation
             if ($opt['relations'] == 0) {
                 $parent = isset($hasOneArray["Parent"]) ? $hasOneArray["Parent"] : null;
                 if ($parent) {
                     $hasOneArray = array();
                     $hasOneArray["Parent"] = $parent;
                 } else {
                     $hasOneArray = null;
                 }
                 $hasManyArray = null;
                 $manyManyArray = null;
             }
             $class->HasOneList = self::relationObject($hasOneArray, $excludeArray);
             $class->HasManyList = self::relationObject($hasManyArray, $excludeArray);
             $class->ManyManyList = self::relationObject($manyManyArray, $excludeArray, $class->ClassName);
             $classes->push($class);
         }
         $folder->Classes = $classes;
         $folders->push($folder);
     }
     $this->customise(array("Rankdir" => $opt['rankdir'], "Folders" => $folders));
     // Defend against source_file_comments
     Config::nest();
     Config::inst()->update('SSViewer', 'source_file_comments', false);
     // Render the output
     $output = $this->renderWith("Silvergraph");
     // Restore the original configuration
     Config::unnest();
     //Set output as plain text, and strip excess empty lines
     $this->response->addHeader("Content-type", "text/plain");
     $output = preg_replace("/(^[\r\n]*|[\r\n]+)[\\s\t]*[\r\n]+/", "\n", $output);
     return $output;
 }