/**
  * Retrieve a set of products, based on the given parameters.
  * Add Parent Group Pages to diplay within list.
  *
  * Note that you can hide the "top level"
  * @return DataObjectSet | Null
  */
 public function currentFinalProducts($alternativeSort = null)
 {
     $products = parent::currentFinalProducts($alternativeSort);
     if ($products) {
         foreach ($products as $product) {
             $product->ParentSegments = null;
             if ($this->NumberOfLevelsToHide < 20) {
                 $segmentArray = array();
                 $item = $product;
                 while ($item && $item->ParentID) {
                     $item = ProductGroup::get()->byID($item->ParentID);
                     if ($item) {
                         $segmentArray[] = array("URLSegment" => $item->URLSegment, "ID" => $item->ID, "ClassName" => $item->ClassName, "Title" => $item->Title, "Link" => $item->Link());
                     }
                 }
                 if (count($segmentArray)) {
                     $product->ParentSegments = new ArrayList();
                     $segmentArray = array_reverse($segmentArray);
                     foreach ($segmentArray as $key => $segment) {
                         if ($key > $this->NumberOfLevelsToHide) {
                             $product->ParentSegments->push(new ArrayData($segment));
                         }
                     }
                 }
             }
         }
     }
     return $products;
 }
 function run($request)
 {
     $myProductGroup = ProductGroup::get()->first();
     $myProduct = Product::get()->first();
     $html = "\r\n\t\tPlease use the links below:\r\n\t\t<ul>\r\n\t\t\t<li><a href=\"/shoppingcart/debug/\" target=\"_debug\">debug cart</a></li>\r\n\t\t\t<li><a href=\"/shoppingcart/ajaxtest/?ajax=1\" target=\"_debug\">view cart response</a></li>";
     if ($myProductGroup) {
         $html .= "\r\n\t\t\t<li><a href=\"" . $myProductGroup->Link("debug") . "\" target=\"_debug\">debug product group</a></li>";
     }
     if ($myProduct) {
         $html .= "\r\n\t\t\t<li><a href=\"" . $myProduct->Link("debug") . "\" target=\"_debug\">debug product</a></li>";
     }
     $html .= "\r\n\t\t</ul>";
     DB::alteration_message("{$html}");
 }
 protected function checkField($fieldName, $acceptableValuesArray, $resetValue)
 {
     $faultyProductGroups = ProductGroup::get()->exclude(array($fieldName => $acceptableValuesArray));
     if ($faultyProductGroups->count()) {
         foreach ($faultyProductGroups as $faultyProductGroup) {
             $faultyProductGroup->{$fieldName} = $resetValue;
             $faultyProductGroup->writeToStage('Stage');
             $faultyProductGroup->publish('Stage', 'Live');
             DB::alteration_message("Reset {$fieldName} for " . $faultyProductGroup->Title, 'created');
         }
     } else {
         DB::alteration_message("Could not find any faulty records for ProductGroup.{$fieldName}");
     }
 }
 function run($request)
 {
     if (Product::get()->Count() == 0) {
         if (ProductGroup::get()->Count() == 0) {
             $productGroup1 = new ProductGroup();
             $productGroup1->Title = 'Products';
             $productGroup1->Content = "\r\n\t\t\t\t\t<p>This is the top level products page, it uses the <em>product group</em> page type, and it allows you to show your products checked as 'featured' on it. It also allows you to nest <em>product group</em> pages inside it.</p>\r\n\t\t\t\t\t<p>For example, you have a product group called 'DVDs', and inside you have more product groups like 'sci-fi', 'horrors' or 'action'.</p>\r\n\t\t\t\t\t<p>In this example we have setup a main product group (this page), with a nested product group containing 2 example products.</p>\r\n\t\t\t\t";
             $productGroup1->URLSegment = 'products';
             $productGroup1->writeToStage('Stage');
             $productGroup1->publish('Stage', 'Live');
             DB::alteration_message('Product group page \'Products\' created', 'created');
         }
         $content = '<p>This is a <em>product</em>. It\'s description goes into the Content field as a standard SilverStripe page would have it\'s content. This is an ideal place to describe your product.</p>';
         $page1 = new Product();
         $page1->Title = 'Example product';
         $page1->Content = $content . '<p>You may also notice that we have checked it as a featured product and it will be displayed on the main Products page.</p>';
         $page1->URLSegment = 'example-product';
         $page1->ParentID = $productGroup1->ID;
         $page1->Price = '15.00';
         $page1->FeaturedProduct = true;
         $page1->writeToStage('Stage');
         $page1->publish('Stage', 'Live');
         DB::alteration_message('Product page \'Example product\' created', 'created');
         $page2 = new Product();
         $page2->Title = 'Example product 2';
         $page2->Content = $content;
         $page2->URLSegment = 'example-product-2';
         $page2->ParentID = $productGroup1->ID;
         $page2->Price = '25.00';
         $page2->writeToStage('Stage');
         $page2->publish('Stage', 'Live');
         DB::alteration_message('Product page \'Example product 2\' created', 'created');
     } else {
         DB::alteration_message('No products created as they already exist.');
     }
 }
コード例 #5
0
 function doProductSearchForm($data, $form)
 {
     if (!$this->maximumNumberOfResults) {
         $this->maximumNumberOfResults = EcommerceConfig::get("ProductGroup", "maximum_number_of_products_to_list");
     }
     if (isset($data["DebugSearch"])) {
         $this->debug = $data["DebugSearch"] ? true : false;
     }
     if ($this->debug) {
         $this->debugOutput("<hr /><hr /><hr /><h2>Debugging Search Results</h2>");
     }
     //what is the baseclass?
     $baseClassName = $this->baseClassForBuyables;
     if (!$baseClassName) {
         $baseClassName = EcommerceConfig::get("ProductGroup", "base_buyable_class");
     }
     if (!$baseClassName) {
         user_error("Can not find {$baseClassName} (baseClassName)");
     }
     //basic get
     $searchableFields = $baseClassName::create()->stat('searchable_fields');
     $baseList = $baseClassName::get()->filter(array("ShowInSearch" => 1));
     $ecomConfig = EcommerceDBConfig::current_ecommerce_db_config();
     if ($ecomConfig->OnlyShowProductsThatCanBePurchased) {
         $baseList->filter(array("AllowPurchase" => 1));
     }
     $limitToCurrentSection = false;
     if (isset($data["SearchOnlyFieldsInThisSection"]) && $data["SearchOnlyFieldsInThisSection"]) {
         $limitToCurrentSection = true;
         if ($this->productsToSearch instanceof DataList) {
             $this->productsToSearch = $this->productsToSearch->map("ID", "ID")->toArray();
         }
         $baseList = $baseList->filter(array("ID" => $this->productsToSearch));
     }
     if (isset($data["MinimumPrice"]) && $data["MinimumPrice"]) {
         $baseList = $baseList->filter(array("Price:GreaterThanOrEqual" => floatval($data["MinimumPrice"])));
     }
     if (isset($data["MaximumPrice"]) && $data["MaximumPrice"]) {
         $baseList = $baseList->filter(array("Price:LessThanOrEqual" => floatval($data["MaximumPrice"])));
     }
     //defining some variables
     $isKeywordSearch = false;
     if ($this->debug) {
         $this->debugOutput("<hr /><h3>BASE LIST</h3><pre>" . str_replace($this->sqlWords, array_flip($this->sqlWords), $baseList->sql()) . "</pre>");
     }
     //KEYWORD SEARCH - only bother if we have any keywords and results at all ...
     if (isset($data["ShortKeyword"]) && !isset($data["Keyword"])) {
         $data["Keyword"] = $data["ShortKeyword"];
     }
     if (isset($data["Keyword"]) && ($keywordPhrase = $data["Keyword"])) {
         if ($baseList->count()) {
             if (strlen($keywordPhrase) > 1) {
                 $isKeywordSearch = true;
                 $this->resultArrayPos = 0;
                 $this->resultArray = array();
                 $keywordPhrase = Convert::raw2sql($keywordPhrase);
                 $keywordPhrase = strtolower($keywordPhrase);
                 SearchHistory::add_entry($keywordPhrase);
                 // 1) Exact search by code
                 $count = 0;
                 if ($this->debug) {
                     $this->debugOutput("<hr /><h2>SEARCH BY CODE</h2>");
                 }
                 if ($code = intval($keywordPhrase)) {
                     $list1 = $baseList->filter(array("InternalItemID" => $code));
                     $count = $list1->count();
                     if ($count == 1) {
                         if (!$this->debug) {
                             return $this->controller->redirect($list1->First()->Link());
                         }
                     } elseif ($count > 1) {
                         if ($this->addToResults($list1)) {
                             break;
                         }
                     }
                 }
                 if ($this->debug) {
                     $this->debugOutput("<h3>SEARCH BY CODE RESULT: {$count}</h3>");
                 }
                 // 2) Search of the entire keyword phrase and its replacements
                 $count = 0;
                 if ($this->debug) {
                     $this->debugOutput("<hr /><h3>FULL KEYWORD SEARCH</h3>");
                 }
                 if ($this->resultArrayPos <= $this->maximumNumberOfResults) {
                     //now we are going to look for synonyms
                     $words = explode(' ', trim(preg_replace('!\\s+!', ' ', $keywordPhrase)));
                     foreach ($words as $wordKey => $word) {
                         if ($this->debug) {
                             $this->debugOutput("checking for aliases of {$word}");
                         }
                         $replacements = SearchReplacement::get()->where("\n\t\t\t\t\t\t\t\t\tLOWER(\"Search\") = '{$word}' OR\n\t\t\t\t\t\t\t\t\tLOWER(\"Search\") LIKE '%,{$word}' OR\n\t\t\t\t\t\t\t\t\tLOWER(\"Search\") LIKE '{$word},%' OR\n\t\t\t\t\t\t\t\t\tLOWER(\"Search\") LIKE '%,{$word},%'");
                         if ($replacements->count()) {
                             $replacementsArray = $replacements->map('ID', 'Replace')->toArray();
                             if ($this->debug) {
                                 $this->debugOutput("found alias for {$word}");
                             }
                             foreach ($replacementsArray as $replacementWord) {
                                 $keywordPhrase = str_replace($word, $replacementWord, $keywordPhrase);
                             }
                         }
                     }
                     if ($this->debug) {
                         $this->debugOutput("<pre>WORD ARRAY: " . print_r($keywordPhrase, 1) . "</pre>");
                     }
                     //work out searches
                     $singleton = $baseClassName::create();
                     foreach ($this->extraBuyableFieldsToSearchFullText as $tempClassName => $fieldArrayTemp) {
                         if ($singleton instanceof $tempClassName) {
                             $fieldArray = $fieldArrayTemp;
                             break;
                         }
                     }
                     if ($this->debug) {
                         $this->debugOutput("<pre>FIELD ARRAY: " . print_r($fieldArray, 1) . "</pre>");
                     }
                     $searches = $this->getSearchArrays($keywordPhrase, $fieldArray);
                     //if($this->debug) { $this->debugOutput("<pre>SEARCH ARRAY: ".print_r($searches, 1)."</pre>");}
                     //we search exact matches first then other matches ...
                     foreach ($searches as $search) {
                         $list2 = $baseList->where($search);
                         $count = $list2->count();
                         if ($this->debug) {
                             $this->debugOutput("<p>{$search}: {$count}</p>");
                         }
                         if ($count == 1) {
                             if (!$this->debug) {
                                 return $this->controller->redirect($list2->First()->Link());
                             }
                         } elseif ($count > 1) {
                             if ($this->addToResults($list2)) {
                                 break;
                             }
                         }
                         if ($this->resultArrayPos > $this->maximumNumberOfResults) {
                             break;
                         }
                     }
                 }
                 if ($this->debug) {
                     $this->debugOutput("<h3>FULL KEYWORD SEARCH: {$count}</h3>");
                 }
                 if ($this->debug) {
                     $this->debugOutput("<hr /><h3>PRODUCT GROUP SEARCH</h3>");
                 }
                 // 3) Do the same search for Product Group names
                 $count = 0;
                 if ($limitToCurrentSection) {
                     //cant search other sections in this case...
                 } else {
                     $searches = $this->getSearchArrays($keywordPhrase);
                     if ($this->debug) {
                         $this->debugOutput("<pre>SEARCH ARRAY: " . print_r($searches, 1) . "</pre>");
                     }
                     foreach ($searches as $search) {
                         $productGroups = ProductGroup::get()->where($search)->filter(array("ShowInSearch" => 1));
                         $count = $productGroups->count();
                         //redirect if we find exactly one match and we have no matches so far...
                         if ($count == 1 && !$this->resultArrayPos) {
                             if (!$this->debug) {
                                 return $this->controller->redirect($productGroups->First()->Link());
                             }
                         } elseif ($count) {
                             foreach ($productGroups as $productGroup) {
                                 //we add them like this because we like to keep them in order!
                                 if (!in_array($productGroup->ID, $this->productGroupIDs)) {
                                     $this->productGroupIDs[] = $productGroup->ID;
                                 }
                             }
                         }
                     }
                     if ($this->debug) {
                         $this->debugOutput("<h3>PRODUCT GROUP SEARCH: {$count}</h3>");
                     }
                 }
             }
         }
     }
     if (!$isKeywordSearch) {
         $this->addToResults($baseList);
     }
     $redirectToPage = null;
     //if no specific section is being searched then we redirect to search page:
     if (!$limitToCurrentSection) {
         $redirectToPage = ProductGroupSearchPage::get()->first();
     }
     if (!$redirectToPage) {
         // for section specific search,
         // redirect to the specific section (basically where we came from)
         $redirectToPage = $this->controller->dataRecord;
     }
     if ($this->debug) {
         $this->debugOutput("<hr />" . "<h3>Previous Search Products: " . $redirectToPage->SearchResultsSessionVariable(false) . "</h3><p>" . print_r(Session::get($redirectToPage->SearchResultsSessionVariable(false)), 1) . "</p>" . "<h3>Previous Search Groups: " . $redirectToPage->SearchResultsSessionVariable(true) . "</h3><p>" . print_r(Session::get($redirectToPage->SearchResultsSessionVariable(true)), 1) . "</p>");
     }
     Session::set($redirectToPage->SearchResultsSessionVariable(false), implode(",", $this->resultArray));
     Session::set($redirectToPage->SearchResultsSessionVariable(true), implode(",", $this->productGroupIDs));
     Session::save();
     if ($this->debug) {
         $this->debugOutput("<hr />" . "<h3>SAVING Products to session: " . $redirectToPage->SearchResultsSessionVariable(false) . "</h3><p>" . print_r(explode(",", Session::get($redirectToPage->SearchResultsSessionVariable(false))), 1) . "</p>" . "<h3>SAVING Groups to session: " . $redirectToPage->SearchResultsSessionVariable(true) . "</h3><p>" . print_r(explode(",", Session::get($redirectToPage->SearchResultsSessionVariable(true))), 1) . "</p>");
     }
     $link = $redirectToPage->Link($this->controllerSearchResultDisplayMethod);
     if ($this->additionalGetParameters) {
         $link .= "?" . $this->additionalGetParameters;
     }
     if ($this->debug) {
         die($link);
     }
     $this->controller->redirect($link);
 }
コード例 #6
0
 /**
  * After a search is conducted you may end up with a bunch
  * of recommended product groups. They will be returned here...
  * We sort the list in the order that it is provided.
  * @return DataList | Null (ProductGroups)
  */
 public function SearchResultsChildGroups()
 {
     $groupArray = explode(",", Session::get($this->SearchResultsSessionVariable($isForGroup = true)));
     if (is_array($groupArray) && count($groupArray)) {
         $sortStatement = $this->createSortStatementFromIDArray($groupArray, "ProductGroup");
         return ProductGroup::get()->filter(array("ID" => $groupArray, "ShowInSearch" => 1))->sort($sortStatement);
     }
     return null;
 }
コード例 #7
0
 public function debug()
 {
     $html = EcommerceTaskDebugCart::debug_object($this);
     $html .= "<ul>";
     $html .= "<li><hr />Links<hr /></li>";
     $html .= "<li><b>Link:</b> <a href=\"" . $this->Link() . "\">" . $this->Link() . "</a></li>";
     $html .= "<li><b>Ajax Link:</b> <a href=\"" . $this->AjaxLink() . "\">" . $this->AjaxLink() . "</a></li>";
     $html .= "<li><b>AddVariations Link:</b> <a href=\"" . $this->AddVariationsLink() . "\">" . $this->AddVariationsLink() . "</a></li>";
     $html .= "<li><b>Add to Cart Link:</b> <a href=\"" . $this->AddLink() . "\">" . $this->AddLink() . "</a></li>";
     $html .= "<li><b>Increment Link:</b> <a href=\"" . $this->IncrementLink() . "\">" . $this->IncrementLink() . "</a></li>";
     $html .= "<li><b>Decrement Link:</b> <a href=\"" . $this->DecrementLink() . "\">" . $this->DecrementLink() . "</a></li>";
     $html .= "<li><b>Remove Link:</b> <a href=\"" . $this->RemoveAllLink() . "\">" . $this->RemoveLink() . "</a></li>";
     $html .= "<li><b>Remove All Link:</b> <a href=\"" . $this->RemoveAllLink() . "\">" . $this->RemoveAllLink() . "</a></li>";
     $html .= "<li><b>Remove All and Edit Link:</b> <a href=\"" . $this->RemoveAllAndEditLink() . "\">" . $this->RemoveAllAndEditLink() . "</a></li>";
     $html .= "<li><b>Set Specific Quantity Item Link (e.g. 77):</b> <a href=\"" . $this->SetSpecificQuantityItemLink(77) . "\">" . $this->SetSpecificQuantityItemLink(77) . "</a></li>";
     $html .= "<li><hr />Cart<hr /></li>";
     $html .= "<li><b>Allow Purchase (DB Value):</b> " . $this->AllowPurchaseNice() . " </li>";
     $html .= "<li><b>Can Purchase (overal calculation):</b> " . ($this->canPurchase() ? "YES" : "NO") . " </li>";
     $html .= "<li><b>Shop Open:</b> " . ($this->EcomConfig() ? $this->EcomConfig()->ShopClosed ? "NO" : "YES" : "NO CONFIG") . " </li>";
     $html .= "<li><b>Extended Country Can Purchase:</b> " . ($this->extendedCan('canPurchaseByCountry', null) === null ? "no applicable" : ($this->extendedCan('canPurchaseByCountry', null) ? "CAN PURCHASE" : "CAN NOT PURCHASE")) . " </li>";
     $html .= "<li><b>Allow sales to this country (" . EcommerceCountry::get_country() . "):</b> " . (EcommerceCountry::allow_sales() ? "YES" : "NO") . " </li>";
     $html .= "<li><b>Class Name for OrderItem:</b> " . $this->classNameForOrderItem() . " </li>";
     $html .= "<li><b>Quantity Decimals:</b> " . $this->QuantityDecimals() . " </li>";
     $html .= "<li><b>Is In Cart:</b> " . ($this->IsInCart() ? "YES" : "NO") . " </li>";
     $html .= "<li><b>Has Been Sold:</b> " . ($this->HasBeenSold() ? "YES" : "NO") . " </li>";
     $html .= "<li><b>Calculated Price:</b> " . $this->CalculatedPrice() . " </li>";
     $html .= "<li><b>Calculated Price as Money:</b> " . $this->getCalculatedPriceAsMoney()->Nice() . " </li>";
     $html .= "<li><hr />Location<hr /></li>";
     $html .= "<li><b>Main Parent Group:</b> " . $this->MainParentGroup()->Title . "</li>";
     $html .= "<li><b>All Others Parent Groups:</b> " . ($this->AllParentGroups()->count() ? "<pre>" . print_r($this->AllParentGroups()->map()->toArray(), 1) . "</pre>" : "none") . "</li>";
     $html .= "<li><hr />Image<hr /></li>";
     $html .= "<li><b>Image:</b> " . ($this->BestAvailableImage() ? "<img src=" . $this->BestAvailableImage()->Link() . " />" : "no image") . " </li>";
     $productGroup = ProductGroup::get()->byID($this->ParentID);
     if ($productGroup) {
         $html .= "<li><hr />Product Example<hr /></li>";
         $html .= "<li><b>Product Group View:</b> <a href=\"" . $productGroup->Link() . "\">" . $productGroup->Title . "</a> </li>";
         $html .= "<li><b>Product Group Debug:</b> <a href=\"" . $productGroup->Link("debug") . "\">" . $productGroup->Title . "</a> </li>";
         $html .= "<li><b>Product Group Admin:</b> <a href=\"" . "/admin/pages/edit/show/" . $productGroup->ID . "\">" . $productGroup->Title . " Admin</a> </li>";
         $html .= "<li><b>Edit this Product:</b> <a href=\"" . "/admin/pages/edit/show/" . $this->ID . "\">" . $this->Title . " Admin</a> </li>";
     }
     $html .= "</ul>";
     return $html;
     $html .= "</ul>";
     return $html;
 }
    private function collateexamplepages()
    {
        $this->addExamplePages(0, "Checkout page", CheckoutPage::get()->First());
        $this->addExamplePages(0, "Order Confirmation page", OrderConfirmationPage::get()->First());
        $this->addExamplePages(0, "Cart page (review cart without checkout)", CartPage::get()->where("ClassName = 'CartPage'")->First());
        $this->addExamplePages(0, "Account page", AccountPage::get()->First());
        //$this->addExamplePages(1, "Donation page", AnyPriceProductPage::get()->First());
        $this->addExamplePages(1, "Products that can not be sold", Product::get()->where("\"AllowPurchase\" = 0 AND ClassName = 'Product'")->First());
        $this->addExamplePages(1, "Product group with short product display template", ProductGroup::get()->where("\"DisplayStyle\" = 'Short'")->First());
        $this->addExamplePages(1, "Product group with medium length product display template", ProductGroup::get()->where("\"DisplayStyle\" = ''")->First());
        $this->addExamplePages(1, "Product group with more detail product display template", ProductGroup::get()->where("\"DisplayStyle\" = 'MoreDetail'")->First());
        //$this->addExamplePages(1, "Quick Add page", AddToCartPage::get()->first());
        //$this->addExamplePages(1, "Shop by Tag page ", ProductGroupWithTags::get()->first());
        $this->addExamplePages(2, "Delivery options (add product to cart first)", CheckoutPage::get()->First());
        $this->addExamplePages(2, "Taxes (NZ based GST - add product to cart first)", CheckoutPage::get()->first());
        $this->addExamplePages(2, "Discount Coupon (try <i>AAA</i>)", CheckoutPage::get()->First());
        $this->addExamplePages(4, "Products with zero price", Product::get()->where("\"Price\" = 0 AND ClassName = 'Product'")->First());
        //$this->addExamplePages(5, "Corporate Account Order page", AddUpProductsToOrderPage::get()->First());
        $html = '
		<h2>Some Interesting Features</h2>
		<p>
			Below are some features of this e-commerce application that may be of interest to you:
		</p>
		<ul>
			<li>customised search for users with search history graphs for admins</li>
			<li>ability to check-out with or without adding a password (creating an account)</li>
			<li>easy to use CMS</li>
			<li>very fast product listings, making extensive use of caching</li>
			<li>many ways to display products, allowing the content editor to set things like <i>products per page</i>, <i>product selctions</i>, <i>sorting orders</i></li>
			<li>multi-currency options and currency conversions</li>
			<li>step-by-step system for completed orders leading them from being submitted to archived via very steps.  This allows the admin to review orders where needed, add extra information, such as tracking codes for delivery, etc...</li>
			<li>code that is very easy to customise and adjust for your needs</li>
			<li>a ton of additional modules are available - you can add them directly to your e-commece install or use these as examples for building your own extensions </li>
			<li>geo-coding for addresses</li>
			<li>extensive developer assistance through various tools and personalised help</li>
		</ul>
		<h2>examples shown on this demo site</h2>';
        foreach ($this->examplePages as $key => $exampleGroups) {
            $html .= "<h3>" . $exampleGroups["Title"] . "</h3><ul>";
            foreach ($exampleGroups["List"] as $examplePages) {
                $html .= '<li><span class="exampleTitle">' . $examplePages["Title"] . '</span>' . $examplePages["List"] . '</li>';
            }
            $html .= "</ul>";
        }
        $html .= '
		<h2>API Access</h2>
		<p>
			E-commerce allows you to access its model using the built-in Silverstripe API.
			This is great for communication with third party applications.
			Access examples are listed below:
		</p>
		<ul>
			<li><a href="/api/v1/Order/">view all orders</a></li>
			<li><a href="/api/v1/Order/1">view order with ID = 1</a></li>
		</ul>
		<p>
			For more information on the restful server API, you can visit the modules home: <a href="https://github.com/silverstripe/silverstripe-restfulserver">https://github.com/silverstripe/silverstripe-restfulserver</a> to find out more on this topic.
		</p>
		';
        $featuresPage = Page::get()->where("URLSegment = 'features'")->First();
        $featuresPage->Content .= $html;
        $featuresPage->write();
        $featuresPage->Publish('Stage', 'Live');
        $featuresPage->flushCache();
    }
 /**
  *
  *
  */
 protected function createProducts()
 {
     $this->alterationMessage("================================================ CREATING PRODUCTS ================================================", "show");
     $productsCompleted = array();
     foreach ($this->csv as $row) {
         if (!isset($productsCompleted[$row["ProductTitle"]])) {
             $filterArray = array("Title" => $row["ProductTitle"], "InternalItemID" => $row["ProductInternalItemID"]);
             $product = ProductPage::get()->filterAny($filterArray)->first();
             if ($product && $product->ParentID) {
                 $this->defaultProductParentID = $product->ParentID;
             } elseif (!$this->defaultProductParentID) {
                 $this->defaultProductParentID = ProductGroup::get()->first()->ID;
             }
             if (!$product) {
                 $product = ProductPage::create($filterArray);
                 $product->MenuTitle = $row["ProductTitle"];
                 $this->alterationMessage("Creating Product: " . $row["ProductTitle"], "created");
             } else {
                 $this->alterationMessage("Product: " . $row["ProductTitle"] . " already exists");
             }
             if (!$product->ParentID) {
                 $product->ParentID = $this->defaultProductParentID;
             }
             $product->Title = $row["ProductTitle"];
             $product->InternalItemID = $row["ProductInternalItemID"];
             if ($this->forreal) {
                 $this->addMoreProduct($product, $row);
                 $product->write("Stage");
                 if ($product->IsPublished()) {
                     $product->Publish('Stage', 'Live');
                 }
             }
             $productsCompleted[$row["ProductTitle"]] = $product->ID;
             $this->data[$product->ID] = array("Product" => $product, "VariationRows" => array());
         }
     }
     $this->alterationMessage("================================================", "show");
 }
 function __construct($controller, $name, $moduleProductID = 0)
 {
     $fields = new FieldList();
     $moduleProduct = null;
     if ($moduleProductID) {
         $fields->push(new HeaderField('AddEditModule', 'Edit ' . $controller->dataRecord->Title, 2));
         $fields->push(new HiddenField('ModuleProductID', $moduleProductID, $moduleProductID));
         $moduleProduct = ModuleProduct::get()->byID($moduleProductID);
     } else {
         $fields->push(new HeaderField('AddEditModule', $controller->dataRecord->Title, 2));
         $fields->push(new HiddenField('ModuleProductID', 0, 0));
     }
     $fields->push(new TextField('Code', 'Code (folder name)'));
     $moduleProductGroups = ModuleProductGroup::get()->filter(array("ParentID:GreaterThan" => 0));
     if (ModuleProductGroup::get()->count()) {
         $types = array("" => " --- please select ---");
         $types += ModuleProductGroup::get()->map($index = 'ID', $titleField = 'MenuTitle')->toArray();
     } else {
         $types = array();
     }
     //$fields->push(new DropdownField('ParentID','Type', $types, $controller->dataRecord->ID));
     $fields->push(new TextField('Title', 'Title'));
     $fields->push(new TextareaField('MetaDescription', 'Three sentence Introduction'));
     $fields->push(new HtmlEditorField('Content', 'Long Description'));
     $fields->push(new TextField('AdditionalTags', 'Additional Keyword(s), comma separated'));
     $fields->push(new HeaderField('LinkHeader', 'Links', 4));
     $fields->push(new TextField('MainURL', 'Home page'));
     $fields->push(new TextField('ReadMeURL', 'Read me file - e.g. http://www.mymodule.com/readme.md'));
     $fields->push(new TextField('DemoURL', 'Demo - e.g. http://demo.mymodule.com/'));
     $fields->push(new TextField('SvnURL', 'SVN repository - allowing you to checkout trunk or latest version - e.g. http://svn.mymodule.com/svn/trunk/'));
     $fields->push(new TextField('GitURL', 'GIT repository - e.g. https://github.com/my-github-username/silverstripe-my-module'));
     $fields->push(new TextField('OtherURL', 'Link to other repository or download URL - e.g. http://www.mymodule.com/downloads/'));
     $fields->push(new CheckboxSetField('EcommerceProductTags', 'Tags', EcommerceProductTag::get()->map()->toArray()));
     $member = Member::currentUser();
     if ($member->inGroup("ADMIN")) {
         $fields->push(new CheckboxSetField('Authors', 'Author(s)', Member::get()->exclude("Email", "")->map("ID", "Email")->toArray()));
         $fields->push(new DropdownField('ParentID', 'Move to', ProductGroup::get()->map()->toArray()));
         $fields->push(new CheckboxField('ShowInMenus', 'Show in menus (unticking both boxes here will hide the module)'));
         $fields->push(new CheckboxField('ShowInSearch', 'Show in search (unticking both boxes here will hide the module)'));
     } else {
         $fields->push(new HiddenField('ShowInMenus', '', 0));
         $fields->push(new HiddenField('ShowInSearch', '', 0));
         $fields->push(new HiddenField('ParentID', '', $controller->dataRecord->ID));
         if ($moduleProduct) {
             $moduleProduct->ParentID = $controller->dataRecord->ID;
             $moduleProduct->ShowInSearch = 0;
             $moduleProduct->ShowInMenus = 0;
         }
     }
     if ($moduleProduct && $moduleProduct->canEdit()) {
         if ($authors = $moduleProduct->Authors()) {
             $authorsIDArray = $authors->map("ID", "ID")->toArray();
             $authorsIDArray[0] = 0;
             $fields->push($this->ManyManyComplexTableFieldAuthorsField($controller, $authorsIDArray));
             //$controller, $name, $sourceClass, $fieldList = null, $detailFormFields = null, $sourceFilter = "", $sourceSort = "", $sourceJoin = ""
         }
     }
     $actions = new FieldList(new FormAction("submit", "submit"));
     $validator = new AddingModuleProduct_RequiredFields($moduleProductID, array('Code', 'Name', 'ParentID', 'MainURL'));
     parent::__construct($controller, $name, $fields, $actions, $validator);
     if ($moduleProduct) {
         $this->loadDataFrom($moduleProduct);
     }
     return $this;
 }