public function addTokenizer($tokenizer)
    {
        if (! $tokenizer instanceof \Org\Heigl\Hyphenator\Tokenizer\Tokenizer) {
            $tokenizer = '\\Org\\Heigl\Hyphenator\\Tokenizer\\' . ucfirst($tokenizer) . 'Tokenizer';
            $tokenizer = new $tokenizer();
        }
        $this->tokenizers->add($tokenizer);

        return $this;
    }

public function addTokenizer ( $ tokenizer ) { if ( ! $ tokenizer instanceof \ Org \ Heigl \ Hyphenator \ Tokenizer \ Tokenizer ) { $ tokenizer = '\\Org\\Heigl\Hyphenator\\Tokenizer\\' . ucfirst ( $ tokenizer ) . 'Tokenizer' ; $ tokenizer = new $ tokenizer ( ) ; } $ this -> tokenizers -> add ( $ tokenizer ) ; return $ this ; }

Add a tokenizer to the tokenizer-registry

@param Tokenizer\Tokenizer|string $tokenizer The tokenizer to add

@return Hyphenator

Add a tokenizer to the tokenizer - registry


public function getTokenizers()
    {
        if (0 == $this->tokenizers->count()) {
            foreach ($this->getOptions()->getTokenizers() as $tokenizer) {
                $this->addTokenizer($tokenizer);
            }
        }

        return $this->tokenizers;
    }

public function getTokenizers ( ) { if ( 0 == $ this -> tokenizers -> count ( ) ) { foreach ( $ this -> getOptions ( ) -> getTokenizers ( ) as $ tokenizer ) { $ this -> addTokenizer ( $ tokenizer ) ; } } return $ this -> tokenizers ; }

Get the tokenizers

@return Tokenizer\TokenizerRegistry


public function getDictionaries()
    {
        if (0 == $this->dicts->count()) {
            $this->addDictionary($this->getOptions()->getDefaultLocale());
        }

        return $this->dicts;
    }

public function getDictionaries ( ) { if ( 0 == $ this -> dicts -> count ( ) ) { $ this -> addDictionary ( $ this -> getOptions ( ) -> getDefaultLocale ( ) ) ; } return $ this -> dicts ; }

Get the dictionaries

@return Dictionaries\DictionaryRegistry


public function getFilters()
    {
        if (0 == $this->filters->count()) {
            foreach ($this->getOptions()->getFilters() as $filter) {
                $this->addFilter($filter);
            }
        }

        return $this->filters;
    }

public function getFilters ( ) { if ( 0 == $ this -> filters -> count ( ) ) { foreach ( $ this -> getOptions ( ) -> getFilters ( ) as $ filter ) { $ this -> addFilter ( $ filter ) ; } } return $ this -> filters ; }

Get the filters

@return Filter\FilterRegistry


public function hyphenate($string)
    {
        $tokens = $this->tokenizers->tokenize($string);
        $tokens = $this->getHyphenationPattern($tokens);
        $tokens = $this->filter($tokens);
        if (1 === count($tokens) && 1 === $this->getFilters()->count()) {
            $tokens->rewind();
            return $tokens->current()->getHyphenatedContent();
        }

        return $this->getFilters()->concatenate($tokens);
    }

public function hyphenate ( $ string ) { $ tokens = $ this -> tokenizers -> tokenize ( $ string ) ; $ tokens = $ this -> getHyphenationPattern ( $ tokens ) ; $ tokens = $ this -> filter ( $ tokens ) ; if ( 1 === count ( $ tokens ) && 1 === $ this -> getFilters ( ) -> count ( ) ) { $ tokens -> rewind ( ) ; return $ tokens -> current ( ) -> getHyphenatedContent ( ) ; } return $ this -> getFilters ( ) -> concatenate ( $ tokens ) ; }

This method does the actual hyphenation.

The given <var>$string</var> is splitted into chunks (i.e. Words) at
every blank.

After that every chunk is hyphenated and the array of chunks is merged
into a single string using blanks again.

This method does not take into account other word-delimiters than blanks
(eg. returns or tabstops) and it will fail with texts containing markup
in any way.

@param string $string The string to hyphenate

@return string The hyphenated string

This method does the actual hyphenation .


public function getHyphenationPattern(Tokenizer\TokenRegistry $registry)
    {
        $minWordLength = $this->getOptions()->getMinWordLength();
        foreach ($registry as $token) {
            if (! $token instanceof \Org\Heigl\Hyphenator\Tokenizer\WordToken) {
                continue;
            }
            if ($minWordLength > $token->length()) {
                continue;
            }
            $this->getPatternForToken($token);
        }

        return $registry;
    }

public function getHyphenationPattern ( Tokenizer \ TokenRegistry $ registry ) { $ minWordLength = $ this -> getOptions ( ) -> getMinWordLength ( ) ; foreach ( $ registry as $ token ) { if ( ! $ token instanceof \ Org \ Heigl \ Hyphenator \ Tokenizer \ WordToken ) { continue ; } if ( $ minWordLength > $ token -> length ( ) ) { continue ; } $ this -> getPatternForToken ( $ token ) ; } return $ registry ; }

Get the hyphenation pattern for the contained tokens

Use the dictionaties and options of the given Hyphenator-Object

@param Tokenizer\TokenRegistry $registry The Hyphenator object containing the
dictionaries and options

@return Tokenizer\TokenRegistry


public function getPatternForToken(Tokenizer\WordToken $token)
    {
        foreach ($this->getDictionaries() as $dictionary) {
            $token->addPattern($dictionary->getPatternsForWord($token->get()));
        }

        return $token;
    }

public function getPatternForToken ( Tokenizer \ WordToken $ token ) { foreach ( $ this -> getDictionaries ( ) as $ dictionary ) { $ token -> addPattern ( $ dictionary -> getPatternsForWord ( $ token -> get ( ) ) ) ; } return $ token ; }

Hyphenate a Token-Object

@param Tokenizer\Token $token The token to hyphenate

@return Tokenizer\Token


public static function setDefaultHomePath($homePath)
    {
        if (! file_exists($homePath)) {
            throw new Exception\PathNotFoundException($homePath . ' does not exist');
        }
        if (! is_Dir($homePath)) {
            throw new Exception\PathNotDirException($homePath . ' is not a directory');
        }

        self::$defaultHomePath = realpath($homePath);
    }

public
static
function
setDefaultHomePath
(
$
homePath
)
{
if
(
!
file_exists
(
$
homePath
)
)
{
throw
new
Exception
\
PathNotFoundException
(
$
homePath
.
' does not exist'
)
;
}
if
(
!
is_Dir
(
$
homePath
)
)
{
throw
new
Exception
\
PathNotDirException
(
$
homePath
.
' is not a directory'
)
;
}
self
::
$
defaultHomePath
=
realpath
(
$
homePath
)
;
}

Set the default home-Path

@param string $homePath The default Hyphenator Home-path.

@throws Exception\PathNotFoundException
@throws Exception\PathNotDirException
@return void


public static function getDefaultHomePath()
    {
        if (is_Dir(self::$defaultHomePath)) {
            return self::$defaultHomePath;
        }
        if (defined('HYPHENATOR_HOME') && is_Dir(HYPHENATOR_HOME)) {
            return realpath(HYPHENATOR_HOME);
        }
        if ($home = getenv('HYPHENATOR_HOME')) {
            if (is_Dir($home)) {
                return $home;
            }
        }

        return __DIR__ . '/share';
    }

public static function getDefaultHomePath ( ) { if ( is_Dir ( self :: $ defaultHomePath ) ) { return self :: $ defaultHomePath ; } if ( defined ( 'HYPHENATOR_HOME' ) && is_Dir ( HYPHENATOR_HOME ) ) { return realpath ( HYPHENATOR_HOME ) ; } if ( $ home = getenv ( 'HYPHENATOR_HOME' ) ) { if ( is_Dir ( $ home ) ) { return $ home ; } } return __DIR__ . '/share' ; }

Get the default Home-Path

@return string


public function setHomePath($homePath)
    {
        if (! file_exists($homePath)) {
            throw new Exception\PathNotFoundException($homePath . ' does not exist');
        }
        if (! is_Dir($homePath)) {
            throw new Exception\PathNotDirException($homePath . ' is not a directory');
        }

        $this->homePath = realpath($homePath);

        return $this;
    }

public
function
setHomePath
(
$
homePath
)
{
if
(
!
file_exists
(
$
homePath
)
)
{
throw
new
Exception
\
PathNotFoundException
(
$
homePath
.
' does not exist'
)
;
}
if
(
!
is_Dir
(
$
homePath
)
)
{
throw
new
Exception
\
PathNotDirException
(
$
homePath
.
' is not a directory'
)
;
}
$
this
->
homePath
=
realpath
(
$
homePath
)
;
return
$
this
;
}

Set the instance-home-Path

@param string $homePath This instances home-path.

@throws Exception\PathNotFoundException
@throws Exception\PathNotDirException
@return \Org\Heigl\Hyphenator\Hyphenator


public static function factory($path = null, $locale = null)
    {
        $hyphenator = new Hyphenator();
        if (null !== $path && file_Exists($path)) {
            $hyphenator->setHomePath($path);
        }
        if (null !== $locale) {
            $hyphenator->getOptions()->setDefaultLocale($locale);
        }

        return $hyphenator;
    }

public static function factory ( $ path = null , $ locale = null ) { $ hyphenator = new Hyphenator ( ) ; if ( null !== $ path && file_Exists ( $ path ) ) { $ hyphenator -> setHomePath ( $ path ) ; } if ( null !== $ locale ) { $ hyphenator -> getOptions ( ) -> setDefaultLocale ( $ locale ) ; } return $ hyphenator ; }

Create a new Hyphenator-Object for a certain locale

To determine the storage of the dictionaries we either use the set
default configuration-file or we take the provided file and set the
home-path from the information within that file.

@param string $path   The path to the configuration-file to use
@param string $locale The locale to be used

@return Hyphenator

Create a new Hyphenator - Object for a certain locale


public static function __autoload($className)
    {
        if (0 !== strpos($className, 'Org\\Heigl\\Hyphenator')) {
            return false;
        }
        $className = substr($className, strlen('Org\\Heigl\\Hyphenator\\'));
        $file = str_replace('\\', '/', $className) . '.php';
        $fileName = __DIR__ . DIRECTORY_SEPARATOR . $file;
        if (! file_exists(realpath($fileName))) {
            return false;
        }
        if (! @include_once $fileName) {
            return false;
        }

        return true;
    }

public static function __autoload ( $ className ) { if ( 0 !== strpos ( $ className , 'Org\\Heigl\\Hyphenator' ) ) { return false ; } $ className = substr ( $ className , strlen ( 'Org\\Heigl\\Hyphenator\\' ) ) ; $ file = str_replace ( '\\' , '/' , $ className ) . '.php' ; $ fileName = __DIR__ . DIRECTORY_SEPARATOR . $ file ; if ( ! file_exists ( realpath ( $ fileName ) ) ) { return false ; } if ( ! @ include_once $ fileName ) { return false ; } return true ; }

autoload classes.

@param string $className the name of the class to load

@return void


public function replace(Token $oldToken, array $newTokens)
    {
        // Get the current key of the element.
        $key = array_search($oldToken, $this->registry, true);
        if (false === $key) {
            return $this;
        }
        $replacement = array();

        // Check for any non-token-elements and remove them.
        foreach ($newTokens as $token) {
            if (! $token instanceof Token) {
                continue;
            }
            $replacement[] = $token;
        }

        // Replace the old element with the newly created array
        array_splice($this->registry, $key, 1, $replacement);

        return $this;
    }

Replace a given token with an array of new tokens

This will be used when a tokenizer has to replace a token that has been
divided into multiple tokens.

@param Token   $oldToken  The token to be replaced
@param Token[] $newTokens The array of tokens replacing the old one

@return TokenRegistry


public function getTokenWithKey($key)
    {
        if (array_key_exists($key, $this->registry)) {
            return $this->registry[$key];
        }

        return null;
    }

public function getTokenWithKey ( $ key ) { if ( array_key_exists ( $ key , $ this -> registry ) ) { return $ this -> registry [ $ key ] ; } return null ; }

Get a Token entry by it's key

@param mixed $key The key to get the token for

@return \Org\Heigl\Hyphenator\Tokenizer\Token


public function getMergedPattern($quality = \Org\Heigl\Hyphenator\Hyphenator::QUALITY_HIGHEST)
    {
        $content = $this->getHyphenateContent();
        $endPattern = str_repeat('0', mb_strlen($content)+1);
        foreach ($this->pattern as $string => $pattern) {
            $strStart = -1;
            while (false !== $strStart = @mb_strpos($content, $string, $strStart + 1)) {
                $strLen   = mb_strlen($string);
                for ($i=0; $i <= $strLen; $i++) {
                    $start = $i+$strStart;
                    $currentQuality = substr($endPattern, $start, 1);
                    $patternQuality = substr($pattern, $i, 1);
                    if ($currentQuality >= $patternQuality) {
                        continue;
                    }
                    if ($quality < $patternQuality) {
                        continue;
                    }
                    $endPattern = substr($endPattern, 0, $start) . $patternQuality . substr($endPattern, $start+1);
                }
            }
        }

        return substr($endPattern, 1, strlen($endPattern)-2);
    }

public function getMergedPattern ( $ quality = \ Org \ Heigl \ Hyphenator \ Hyphenator :: QUALITY_HIGHEST ) { $ content = $ this -> getHyphenateContent ( ) ; $ endPattern = str_repeat ( '0' , mb_strlen ( $ content ) + 1 ) ; foreach ( $ this -> pattern as $ string => $ pattern ) { $ strStart = - 1 ; while ( false !== $ strStart = @ mb_strpos ( $ content , $ string , $ strStart + 1 ) ) { $ strLen = mb_strlen ( $ string ) ; for ( $ i = 0 ; $ i <= $ strLen ; $ i ++ ) { $ start = $ i + $ strStart ; $ currentQuality = substr ( $ endPattern , $ start , 1 ) ; $ patternQuality = substr ( $ pattern , $ i , 1 ) ; if ( $ currentQuality >= $ patternQuality ) { continue ; } if ( $ quality < $ patternQuality ) { continue ; } $ endPattern = substr ( $ endPattern , 0 , $ start ) . $ patternQuality . substr ( $ endPattern , $ start + 1 ) ; } } } return substr ( $ endPattern , 1 , strlen ( $ endPattern ) - 2 ) ; }

Merge the given Hyphenation patterns to one pattern for the given token

This is done using the given quality value.

@param \int $quality The hyphenation quality to use

@return Token


public function getFilterWithKey($key)
    {
        if (array_key_exists($key, $this->registry)) {
            return $this->registry[$key];
        }

        return null;
    }

public function getFilterWithKey ( $ key ) { if ( array_key_exists ( $ key , $ this -> registry ) ) { return $ this -> registry [ $ key ] ; } return null ; }

Get a Filters entry by it's key

@param mixed $key The key to get the Filter for.

@return \Org\Heigl\Hyphenator\Filter\Filter


public function filter(\Org\Heigl\Hyphenator\Tokenizer\TokenRegistry $tokens)
    {
        foreach ($this as $filter) {
            $tokens = $filter->run($tokens);
        }

        return $tokens;
    }

public function filter ( \ Org \ Heigl \ Hyphenator \ Tokenizer \ TokenRegistry $ tokens ) { foreach ( $ this as $ filter ) { $ tokens = $ filter -> run ( $ tokens ) ; } return $ tokens ; }

Pass the given string through the given Filter

@param \Org\Heigl\Hyphenator\Tokenizer\TokenRegistry $tokens The
Registry to filter

@return \Org\Heigl\Hyphenator\Tokenizer\TokenizerRegistry


public function fetchToken($authorizationCode)
    {
        $additionalParams = [
            'code' => $authorizationCode,
            'redirect_uri' => $this->configuration->getRedirectUrl(),
        ];

        $response = parent::fetchAccessToken('authentication/v1/gettoken', 'authorization_code', $additionalParams);

        $this->saveRefreshToken($response);
    }

public function fetchToken ( $ authorizationCode ) { $ additionalParams = [ 'code' => $ authorizationCode , 'redirect_uri' => $ this -> configuration -> getRedirectUrl ( ) , ] ; $ response = parent :: fetchAccessToken ( 'authentication/v1/gettoken' , 'authorization_code' , $ additionalParams ) ; $ this -> saveRefreshToken ( $ response ) ; }

Returns application token
@param $authorizationCode
@throws RuntimeException


public function migrateOrders()
    {
        $start = $count = 0;
        $batch = Order::get()->sort('Created', 'ASC')->limit($start, self::$batch_size);
        while ($batch->exists()) {
            foreach ($batch as $order) {
                $this->migrate($order);
                echo '. ';
                $count++;
            }
            $start += self::$batch_size;
            $batch = $batch->limit($start, self::$batch_size);
        };
        echo "$count orders updated.\n<br/>";
    }

public
function
migrateOrders
(
)
{
$
start
=
$
count
=
0
;
$
batch
=
Order
::
get
(
)
->
sort
(
'Created'
,
'ASC'
)
->
limit
(
$
start
,
self
::
$
batch_size
)
;
while
(
$
batch
->
exists
(
)
)
{
foreach
(
$
batch
as
$
order
)
{
$
this
->
migrate
(
$
order
)
;
echo
'. '
;
$
count
++
;
}
$
start
+=
self
::
$
batch_size
;
$
batch
=
$
batch
->
limit
(
$
start
,
self
::
$
batch_size
)
;
}
;
echo
"$count orders updated.\n<br/>"
;
}


public function migrate(Order $order)
    {
        //TODO: set a from / to version to preform a migration with
        $this->migrateStatuses($order);
        $this->migrateMemberFields($order);
        $this->migrateShippingValues($order);
        $this->migrateOrderCalculation($order);
        $order->write();
    }

Perform migration scripts on a single order.

Perform migration scripts on a single order .


public function migrateShippingValues($order)
    {
        //TODO: see if this actually works..it probably needs to be writeen to a SQL query
        if ($order->hasShippingCost && abs($order->Shipping)) {
            $modifier1 = Base::create();
            $modifier1->Amount = $order->Shipping < 0 ? abs($order->Shipping) : $order->Shipping;
            $modifier1->Type = 'Chargable';
            $modifier1->OrderID = $order->ID;
            $modifier1->ShippingChargeType = 'Default';
            $modifier1->write();
            $order->hasShippingCost = null;
            $order->Shipping = null;
        }
        if ($order->AddedTax) {
            $modifier2 = \SilverShop\Model\Modifiers\Tax\Base::create();
            $modifier2->Amount = $order->AddedTax < 0 ? abs($order->AddedTax) : $order->AddedTax;
            $modifier2->Type = 'Chargable';
            $modifier2->OrderID = $order->ID;
            //$modifier2->Name = 'Undefined After Ecommerce Upgrade';
            $modifier2->TaxType = 'Exclusive';
            $modifier2->write();
            $order->AddedTax = null;
        }
    }

Convert shipping and tax columns into modifiers

Applies to pre 0.6 sites


public function migrateOrderCalculation($order)
    {
        if (!is_numeric($order->Total) || $order->Total <= 0) {
            $order->calculate();
            $order->write();
        }
    }

public function migrateOrderCalculation ( $ order ) { if ( ! is_numeric ( $ order -> Total ) || $ order -> Total <= 0 ) { $ order -> calculate ( ) ; $ order -> write ( ) ; } }

Performs calculation function on un-calculated orders.

Performs calculation function on un - calculated orders .


public function viaphp()
    {
        $ps = singleton(Product::class);
        $q = $ps->buildSQL('"SilverShop_Product"."AllowPurchase" = 1');
        $select = $q->select;
        $select['NewPopularity'] =
            self::config()->number_sold_calculation_type . '("SilverShop_OrderItem"."Quantity") AS "NewPopularity"';
        $q->select($select);
        $q->groupby('"Product"."ID"');
        $q->orderby('"NewPopularity" DESC');
        $q->leftJoin('SilverShop_Product_OrderItem', '"SilverShop_Product"."ID" = "SilverShop_Product_OrderItem"."ProductID"');
        $q->leftJoin('SilverShop_OrderItem', '"SilverShop_Product_OrderItem"."ID" = "SilverShop_OrderItem"."ID"');
        $records = $q->execute();
        $productssold = $ps->buildDataObjectSet($records, "DataObjectSet", $q, Product::class);
        //TODO: this could be done faster with an UPDATE query (SQLQuery doesn't support this yet @11/06/2010)
        foreach ($productssold as $product) {
            if ($product->NewPopularity != $product->Popularity) {
                $product->Popularity = $product->NewPopularity;
                $product->writeToStage('Stage');
                $product->publishSingle();
            }
        }
    }

legacy function  for working out popularity


public function requireDefaultRecords()
    {
        parent::requireDefaultRecords();
        if (!self::get()->exists() && $this->config()->create_default_pages) {
            $page = self::create()->update(
                [
                    'Title'       => 'Checkout',
                    'URLSegment'  => CheckoutPageController::config()->url_segment,
                    'ShowInMenus' => 0,
                ]
            );
            $page->write();
            $page->publishSingle();
            $page->flushCache();
            DB::alteration_message('Checkout page created', 'created');
        }
    }

public
function
requireDefaultRecords
(
)
{
parent
::
requireDefaultRecords
(
)
;
if
(
!
self
::
get
(
)
->
exists
(
)
&&
$
this
->
config
(
)
->
create_default_pages
)
{
$
page
=
self
::
create
(
)
->
update
(
[
'Title'
=>
'Checkout'
,
'URLSegment'
=>
CheckoutPageController
::
config
(
)
->
url_segment
,
'ShowInMenus'
=>
0
,
]
)
;
$
page
->
write
(
)
;
$
page
->
publishSingle
(
)
;
$
page
->
flushCache
(
)
;
DB
::
alteration_message
(
'Checkout page created'
,
'created'
)
;
}
}

This module always requires a page model.

This module always requires a page model .


public function makePayment($gateway, $gatewaydata = array(), $successUrl = null, $cancelUrl = null)
    {
        //create payment
        $payment = $this->createPayment($gateway);
        if (!$payment) {
            //errors have been stored.
            return null;
        }

        $payment->setSuccessUrl($successUrl ? $successUrl : $this->getReturnUrl());

        // Explicitly set the cancel URL
        if ($cancelUrl) {
            $payment->setFailureUrl($cancelUrl);
        }

        // Create a payment service, by using the Service Factory. This will automatically choose an
        // AuthorizeService or PurchaseService, depending on Gateway configuration.
        // Set the user-facing success URL for redirects
        /**
         * @var ServiceFactory $factory
         */
        $factory = ServiceFactory::create();
        $service = $factory->getService($payment, ServiceFactory::INTENT_PAYMENT);

        // Initiate payment, get the result back
        try {
            $serviceResponse = $service->initiate($this->getGatewayData($gatewaydata));
        } catch (\SilverStripe\Omnipay\Exception\Exception $ex) {
            // error out when an exception occurs
            $this->error($ex->getMessage());
            return null;
        }

        // Check if the service response itself contains an error
        if ($serviceResponse->isError()) {
            if ($opResponse = $serviceResponse->getOmnipayResponse()) {
                $this->error($opResponse->getMessage());
            } else {
                $this->error('An unspecified payment error occurred. Please check the payment messages.');
            }
        }

        // For an OFFSITE payment, serviceResponse will now contain a redirect
        // For an ONSITE payment, ShopPayment::onCaptured will have been called, which will have called completePayment

        return $serviceResponse;
    }

/**
         * @var ServiceFactory $factory
         */

Create a payment model, and provide link to redirect to external gateway,
or redirect to order link.

@param string $gateway the gateway to use
@param array $gatewaydata the data that should be passed to the gateway
@param string $successUrl (optional) return URL for successful payments.
If left blank, the default return URL will be
used @see getReturnUrl
@param string $cancelUrl (optional) return URL for cancelled/failed payments

@return ServiceResponse|null
@throws \SilverStripe\Omnipay\Exception\InvalidConfigurationException

Create a payment model and provide link to redirect to external gateway or redirect to order link .


protected function getGatewayData($customData)
    {
        $shipping = $this->order->getShippingAddress();
        $billing = $this->order->getBillingAddress();

        $numPayments = Payment::get()
            ->filter(array('OrderID' => $this->order->ID))
            ->count() - 1;

        $transactionId = $this->order->Reference . ($numPayments > 0 ? "-$numPayments" : '');

        return array_merge(
            $customData,
            array(
                'transactionId'    => $transactionId,
                'firstName'        => $this->order->FirstName,
                'lastName'         => $this->order->Surname,
                'email'            => $this->order->Email,
                'company'          => $this->order->Company,
                'billingAddress1'  => $billing->Address,
                'billingAddress2'  => $billing->AddressLine2,
                'billingCity'      => $billing->City,
                'billingPostcode'  => $billing->PostalCode,
                'billingState'     => $billing->State,
                'billingCountry'   => $billing->Country,
                'billingPhone'     => $billing->Phone,
                'shippingAddress1' => $shipping->Address,
                'shippingAddress2' => $shipping->AddressLine2,
                'shippingCity'     => $shipping->City,
                'shippingPostcode' => $shipping->PostalCode,
                'shippingState'    => $shipping->State,
                'shippingCountry'  => $shipping->Country,
                'shippingPhone'    => $shipping->Phone,
            )
        );
    }

protected function getGatewayData ( $ customData ) { $ shipping = $ this -> order -> getShippingAddress ( ) ; $ billing = $ this -> order -> getBillingAddress ( ) ; $ numPayments = Payment :: get ( ) -> filter ( array ( 'OrderID' => $ this -> order -> ID ) ) -> count ( ) - 1 ; $ transactionId = $ this -> order -> Reference . ( $ numPayments > 0 ? "-$numPayments" : '' ) ; return array_merge ( $ customData , array ( 'transactionId' => $ transactionId , 'firstName' => $ this -> order -> FirstName , 'lastName' => $ this -> order -> Surname , 'email' => $ this -> order -> Email , 'company' => $ this -> order -> Company , 'billingAddress1' => $ billing -> Address , 'billingAddress2' => $ billing -> AddressLine2 , 'billingCity' => $ billing -> City , 'billingPostcode' => $ billing -> PostalCode , 'billingState' => $ billing -> State , 'billingCountry' => $ billing -> Country , 'billingPhone' => $ billing -> Phone , 'shippingAddress1' => $ shipping -> Address , 'shippingAddress2' => $ shipping -> AddressLine2 , 'shippingCity' => $ shipping -> City , 'shippingPostcode' => $ shipping -> PostalCode , 'shippingState' => $ shipping -> State , 'shippingCountry' => $ shipping -> Country , 'shippingPhone' => $ shipping -> Phone , ) ) ; }

Map shop data to omnipay fields

@param array $customData Usually user submitted data.

@return array


public function createPayment($gateway)
    {
        if (!GatewayInfo::isSupported($gateway)) {
            $this->error(
                _t(
                    __CLASS__ . ".InvalidGateway",
                    "`{gateway}` isn't a valid payment gateway.",
                    'gateway is the name of the payment gateway',
                    array('gateway' => $gateway)
                )
            );
            return false;
        }
        if (!$this->order->canPay(Security::getCurrentUser())) {
            $this->error(_t(__CLASS__ . ".CantPay", "Order can't be paid for."));
            return false;
        }
        $payment = Payment::create()->init(
            $gateway,
            $this->order->TotalOutstanding(true),
            ShopConfigExtension::config()->base_currency
        );
        $this->order->Payments()->add($payment);
        return $payment;
    }


public function completePayment()
    {
        if (!$this->order->IsPaid()) {
            // recalculate order to be sure we have the correct total
            $this->order->calculate();

            $this->order->extend('onPayment'); //a payment has been made
            //place the order, if not already placed
            if ($this->canPlace($this->order)) {
                $this->placeOrder();
            } else {
                if ($this->order->Locale) {
                    ShopTools::install_locale($this->order->Locale);
                }
            }

            if (($this->order->GrandTotal() > 0 && $this->order->TotalOutstanding(false) <= 0)
                // Zero-dollar order (e.g. paid with loyalty points)
                || ($this->order->GrandTotal() == 0 && Order::config()->allow_zero_order_total)
            ) {
                //set order as paid
                $this->order->Status = 'Paid';
                $this->order->write();
            }
        }
    }

Complete payment processing
- send receipt
- update order status accordingling
- fire event hooks


public function canPlace(Order $order)
    {
        if (!$order) {
            $this->error(_t(__CLASS__ . ".NoOrder", "Order does not exist."));
            return false;
        }
        //order status is applicable
        if (!$order->IsCart()) {
            $this->error(_t(__CLASS__ . ".NotCart", "Order is not a cart."));
            return false;
        }
        //order has products
        if ($order->Items()->Count() <= 0) {
            $this->error(_t(__CLASS__ . ".NoItems", "Order has no items."));
            return false;
        }

        return true;
    }

public
function
canPlace
(
Order
$
order
)
{
if
(
!
$
order
)
{
$
this
->
error
(
_t
(
__CLASS__
.
".NoOrder"
,
"Order does not exist."
)
)
;
return
false
;
}
//order status is applicable
if
(
!
$
order
->
IsCart
(
)
)
{
$
this
->
error
(
_t
(
__CLASS__
.
".NotCart"
,
"Order is not a cart."
)
)
;
return
false
;
}
//order has products
if
(
$
order
->
Items
(
)
->
Count
(
)
<=
0
)
{
$
this
->
error
(
_t
(
__CLASS__
.
".NoItems"
,
"Order has no items."
)
)
;
return
false
;
}
return
true
;
}

Determine if an order can be placed.

@param boolean $order


public function Nice()
    {
        $val = ShopConfigExtension::countryCode2name($this->value);
        if (!$val) {
            $val = $this->value;
        }
        return _t(__CLASS__ . '.' . $this->value, $val);
    }

public function Nice ( ) { $ val = ShopConfigExtension :: countryCode2name ( $ this -> value ) ; if ( ! $ val ) { $ val = $ this -> value ; } return _t ( __CLASS__ . '.' . $ this -> value , $ val ) ; }

Convert ISO abbreviation to full, translated country name

Convert ISO abbreviation to full translated country name


public function setPaymentMethod($paymentmethod)
    {
        $methods = GatewayInfo::getSupportedGateways();
        if (!isset($methods[$paymentmethod])) {
            ShopTools::getSession()
                ->set('Checkout.PaymentMethod', null)
                ->clear('Checkout.PaymentMethod');
            return $this->error(_t(__CLASS__ . '.NoPaymentMethod', 'Payment method does not exist'));
        }
        ShopTools::getSession()->set('Checkout.PaymentMethod', $paymentmethod);
        return true;
    }

public
function
setPaymentMethod
(
$
paymentmethod
)
{
$
methods
=
GatewayInfo
::
getSupportedGateways
(
)
;
if
(
!
isset
(
$
methods
[
$
paymentmethod
]
)
)
{
ShopTools
::
getSession
(
)
->
set
(
'Checkout.PaymentMethod'
,
)
->
clear
(
'Checkout.PaymentMethod'
)
;
return
$
this
->
error
(
_t
(
__CLASS__
.
'.NoPaymentMethod'
,
'Payment method does not exist'
)
)
;
}
ShopTools
::
getSession
(
)
->
set
(
'Checkout.PaymentMethod'
,
$
paymentmethod
)
;
return
true
;
}

Set payment method

@throws \SilverStripe\Omnipay\Exception\InvalidConfigurationException


public function getSelectedPaymentMethod($nice = false)
    {
        $methods = GatewayInfo::getSupportedGateways();
        reset($methods);
        $method = count($methods) === 1 ? key($methods) : ShopTools::getSession()->get('Checkout.PaymentMethod');
        if ($nice && isset($methods[$method])) {
            $method = $methods[$method];
        }
        return $method;
    }

public function getSelectedPaymentMethod ( $ nice = false ) { $ methods = GatewayInfo :: getSupportedGateways ( ) ; reset ( $ methods ) ; $ method = count ( $ methods ) === 1 ? key ( $ methods ) : ShopTools :: getSession ( ) -> get ( 'Checkout.PaymentMethod' ) ; if ( $ nice && isset ( $ methods [ $ method ] ) ) { $ method = $ methods [ $ method ] ; } return $ method ; }

Gets the selected payment method from the session,
or the only available method, if there is only one.

@throws \SilverStripe\Omnipay\Exception\InvalidConfigurationException

Gets the selected payment method from the session or the only available method if there is only one .


public function sendConfirmation()
    {
        $subject = _t(
            'SilverShop\ShopEmail.ConfirmationSubject',
            'Order #{OrderNo} confirmation',
            '',
            array('OrderNo' => $this->order->Reference)
        );
        return $this->sendEmail(
            'SilverShop/Model/Order_ConfirmationEmail',
            $subject,
            self::config()->bcc_confirmation_to_admin
        );
    }

Send customer a confirmation that the order has been received

@return bool


public function sendAdminNotification()
    {
        $subject = _t(
            'SilverShop\ShopEmail.AdminNotificationSubject',
            'Order #{OrderNo} notification',
            '',
            array('OrderNo' => $this->order->Reference)
        );

        $email = $this->buildEmail('SilverShop/Model/Order_AdminNotificationEmail', $subject)
            ->setTo(Email::config()->admin_email);

        if ($this->debugMode) {
            return $this->debug($email);
        } else {
            return $email->send();
        }
    }

Notify store owner about new order.

@return bool|string


public function sendCancelNotification()
    {
        $email = Email::create()
            ->setSubject(_t(
                'SilverShop\ShopEmail.CancelSubject',
                'Order #{OrderNo} cancelled by member',
                '',
                ['OrderNo' => $this->order->Reference]
            ))
            ->setFrom(Email::config()->admin_email)
            ->setTo(Email::config()->admin_email)
            ->setBody($this->order->renderWith(Order::class));

        if ($this->debugMode) {
            return $this->debug($email);
        } else {
            return $email->send();
        }
    }

public
function
sendCancelNotification
(
)
{
$
email
=
Email
::
create
(
)
->
setSubject
(
_t
(
'SilverShop\ShopEmail.CancelSubject'
,
'Order #{OrderNo} cancelled by member'
,
''
,
[
'OrderNo'
=>
$
this
->
order
->
Reference
]
)
)
->
setFrom
(
Email
::
config
(
)
->
admin_email
)
->
setTo
(
Email
::
config
(
)
->
admin_email
)
->
setBody
(
$
this
->
order
->
renderWith
(
Order
::
class
)
)
;
if
(
$
this
->
debugMode
)
{
return
$
this
->
debug
(
$
email
)
;
}
else
{
return
$
email
->
send
(
)
;
}
}

Sends an email to the admin that an order has been cancelled


protected static function build_url($action, $buyable, $params = [])
    {
        if (!$action || !$buyable) {
            return false;
        }

        if (SecurityToken::is_enabled() && !self::config()->disable_security_token) {
            $params[SecurityToken::inst()->getName()] = SecurityToken::inst()->getValue();
        }

        $className = get_class($buyable);

        $link = Controller::join_links(
            [
            self::config()->url_segment,
            $action,
            ShopTools::sanitiseClassName($className),
            $buyable->ID
            ]
        );

        return empty($params) ? $link : $link . '?' . http_build_query($params);
    }

protected static function build_url ( $ action , $ buyable , $ params = [ ] ) { if ( ! $ action || ! $ buyable ) { return false ; } if ( SecurityToken :: is_enabled ( ) && ! self :: config ( ) -> disable_security_token ) { $ params [ SecurityToken :: inst ( ) -> getName ( ) ] = SecurityToken :: inst ( ) -> getValue ( ) ; } $ className = get_class ( $ buyable ) ; $ link = Controller :: join_links ( [ self :: config ( ) -> url_segment , $ action , ShopTools :: sanitiseClassName ( $ className ) , $ buyable -> ID ] ) ; return empty ( $ params ) ? $ link : $ link . '?' . http_build_query ( $ params ) ; }


public static function direct($status = true)
    {
        if (Director::is_ajax()) {
            return (string)$status;
        }
        if (self::config()->direct_to_cart_page && ($cartlink = CartPage::find_link())) {
            return Controller::curr()->redirect($cartlink);
        } else {
            return Controller::curr()->redirectBack();
        }
    }

public static function direct ( $ status = true ) { if ( Director :: is_ajax ( ) ) { return ( string ) $ status ; } if ( self :: config ( ) -> direct_to_cart_page && ( $ cartlink = CartPage :: find_link ( ) ) ) { return Controller :: curr ( ) -> redirect ( $ cartlink ) ; } else { return Controller :: curr ( ) -> redirectBack ( ) ; } }

This is used here and in VariationForm and AddProductForm

@param bool|string $status

@return string|HTTPResponse


public function index()
    {
        if ($cart = $this->Cart()) {
            return $this->redirect($cart->CartLink);
        } elseif ($response = ErrorPage::response_for(404)) {
            return $response;
        }
        return $this->httpError(404, _t('SilverShop\Cart\ShoppingCart.NoCartInitialised', 'no cart initialised'));
    }

Handle index requests

@throws \SilverStripe\Control\HTTPResponse_Exception


public function debug()
    {
        if (Director::isDev() || Permission::check('ADMIN')) {
            //TODO: allow specifying a particular id to debug
            Requirements::css('silvershop/core: client/dist/css/cartdebug.css');
            $order = ShoppingCart::curr();
            $content = ($order)
                ? Debug::text($order)
                : 'Cart has not been created yet. Add a product.';
            return ['Content' => $content];
        }
    }


public function getFilter()
    {
        if (!is_array($this->data)) {
            return null;
        }
        $allowed = array_keys(DataObject::getSchema()->databaseFields($this->className));
        $fields = array_flip(array_intersect($allowed, $this->required));
        $singleton = singleton($this->className);

        $new = [];
        foreach ($fields as $field => $value) {
            $field = Convert::raw2sql($field);
            if (in_array($field, $allowed)) {
                if (isset($this->data[$field])) {
                    // allow wildcard in filter
                    if ($this->data[$field] === '*') {
                        continue;
                    }

                    $dbfield = $singleton->dbObject($field);
                    $value = $dbfield->prepValueForDB($this->data[$field]);

                    $new[] = "\"$field\" = '$value'";
                } else {
                    $new[] = "\"$field\" IS NULL";
                }
            } else {
                if (isset($this->data[$field])) {
                    $value = Convert::raw2sql($this->data[$field]);
                    $new[] = "\"$field\" = '$value'";
                } else {
                    $new[] = "\"$field\" IS NULL";
                }
            }
        }
        return $new;
    }

public
function
getFilter
(
)
{
if
(
!
is_array
(
$
this
->
data
)
)
{
return
;
}
$
allowed
=
array_keys
(
DataObject
::
getSchema
(
)
->
databaseFields
(
$
this
->
className
)
)
;
$
fields
=
array_flip
(
array_intersect
(
$
allowed
,
$
this
->
required
)
)
;
$
singleton
=
singleton
(
$
this
->
className
)
;
$
new
=
[
]
;
foreach
(
$
fields
as
$
field
=>
$
value
)
{
$
field
=
Convert
::
raw2sql
(
$
field
)
;
if
(
in_array
(
$
field
,
$
allowed
)
)
{
if
(
isset
(
$
this
->
data
[
$
field
]
)
)
{
// allow wildcard in filter
if
(
$
this
->
data
[
$
field
]
===
'*'
)
{
continue
;
}
$
dbfield
=
$
singleton
->
dbObject
(
$
field
)
;
$
value
=
$
dbfield
->
prepValueForDB
(
$
this
->
data
[
$
field
]
)
;
$
new
[
]
=
"\"$field\" = '$value'"
;
}
else
{
$
new
[
]
=
"\"$field\" IS NULL"
;
}
}
else
{
if
(
isset
(
$
this
->
data
[
$
field
]
)
)
{
$
value
=
Convert
::
raw2sql
(
$
this
->
data
[
$
field
]
)
;
$
new
[
]
=
"\"$field\" = '$value'"
;
}
else
{
$
new
[
]
=
"\"$field\" IS NULL"
;
}
}
}
return
$
new
;
}

Create SQL where filter

@return array of filter statements


public function submit($data, $form, $request)
    {
        $member = Security::getCurrentUser();
        if (!$member) {
            return false;
        }

        $form->saveInto($member);
        $member->write();
        $form->sessionMessage(_t(__CLASS__ . '.DetailsSaved', 'Your details have been saved'), 'good');

        $this->extend('updateShopAccountFormResponse', $request, $form, $data, $response);

        return $response ?: $this->getController()->redirectBack();
    }

public
function
submit
(
$
data
,
$
form
,
$
request
)
{
$
member
=
Security
::
getCurrentUser
(
)
;
if
(
!
$
member
)
{
return
false
;
}
$
form
->
saveInto
(
$
member
)
;
$
member
->
write
(
)
;
$
form
->
sessionMessage
(
_t
(
__CLASS__
.
'.DetailsSaved'
,
'Your details have been saved'
)
,
'good'
)
;
$
this
->
extend
(
'updateShopAccountFormResponse'
,
$
request
,
$
form
,
$
data
,
$
response
)
;
return
$
response
:
$
this
->
getController
(
)
->
redirectBack
(
)
;
}

Save the changes to the form

@param array       $data
@param Form        $form
@param HTTPRequest $request

@return bool|HTTPResponse
@throws \SilverStripe\ORM\ValidationException


public function proceed($data, $form, $request)
    {
        $member = Security::getCurrentUser();
        if (!$member) {
            return false;
        }

        $form->saveInto($member);
        $member->write();
        $form->sessionMessage(_t(__CLASS__ . '.DetailsSaved', 'Your details have been saved'), 'good');

        $this->extend('updateShopAccountFormResponse', $request, $form, $data, $response);

        return $response ?: $this->getController()->redirect(CheckoutPage::find_link());
    }

public
function
proceed
(
$
data
,
$
form
,
$
request
)
{
$
member
=
Security
::
getCurrentUser
(
)
;
if
(
!
$
member
)
{
return
false
;
}
$
form
->
saveInto
(
$
member
)
;
$
member
->
write
(
)
;
$
form
->
sessionMessage
(
_t
(
__CLASS__
.
'.DetailsSaved'
,
'Your details have been saved'
)
,
'good'
)
;
$
this
->
extend
(
'updateShopAccountFormResponse'
,
$
request
,
$
form
,
$
data
,
$
response
)
;
return
$
response
:
$
this
->
getController
(
)
->
redirect
(
CheckoutPage
::
find_link
(
)
)
;
}

Save the changes to the form, and redirect to the checkout page

@param array       $data
@param Form        $form
@param HTTPRequest $request

@return bool|HTTPResponse
@throws \SilverStripe\ORM\ValidationException

Save the changes to the form and redirect to the checkout page


public function getModifier($className, $forcecreate = false)
    {
        if (!ClassInfo::exists($className)) {
            user_error("Modifier class \"$className\" does not exist.");
        }
        //search for existing
        $modifier = $className::get()
            ->filter("OrderID", $this->order->ID)
            ->first();
        if ($modifier) {
            //remove if no longer valid
            if (!$modifier->valid()) {
                //TODO: need to provide feedback message - why modifier was removed
                $modifier->delete();
                $modifier->destroy();
                return null;
            }
            return $modifier;
        }
        $modifier = new $className();
        if ($modifier->required() || $forcecreate) { //create any modifiers that are required for every order
            $modifier->OrderID = $this->order->ID;
            $modifier->write();
            $this->order->Modifiers()->add($modifier);

            return $modifier;
        }

        return null;
    }

Retrieve a modifier of a given class for the order.
Modifier will be retrieved from database if it already exists,
or created if it is always required.

@param string  $className
@param boolean $forcecreate - force the modifier to be created.

Retrieve a modifier of a given class for the order . Modifier will be retrieved from database if it already exists or created if it is always required .


public function UnitPrice()
    {
        if ($this->Order()->IsCart()) {
            $buyable = $this->Buyable();
            $unitprice = ($buyable) ? $buyable->sellingPrice() : $this->UnitPrice;
            $this->extend('updateUnitPrice', $unitprice);
            return $this->UnitPrice = $unitprice;
        }
        return $this->UnitPrice;
    }

public function UnitPrice ( ) { if ( $ this -> Order ( ) -> IsCart ( ) ) { $ buyable = $ this -> Buyable ( ) ; $ unitprice = ( $ buyable ) ? $ buyable -> sellingPrice ( ) : $ this -> UnitPrice ; $ this -> extend ( 'updateUnitPrice' , $ unitprice ) ; return $ this -> UnitPrice = $ unitprice ; } return $ this -> UnitPrice ; }

Get unit price for this item.
Fetches from db, or Buyable, based on order status.

Get unit price for this item . Fetches from db or Buyable based on order status .


protected function calculatetotal()
    {
        $total = $this->UnitPrice() * $this->Quantity;
        $this->extend('updateTotal', $total);
        $this->CalculatedTotal = $total;
        return $total;
    }

protected function calculatetotal ( ) { $ total = $ this -> UnitPrice ( ) * $ this -> Quantity ; $ this -> extend ( 'updateTotal' , $ total ) ; $ this -> CalculatedTotal = $ total ; return $ total ; }

Calculates the total for this item.
Generally called by onBeforeWrite

Calculates the total for this item . Generally called by onBeforeWrite


public function uniquedata()
    {
        $required = self::config()->required_fields; //TODO: also combine with all ancestors of this->class
        $unique = [];
        $hasOnes = $this->hasOne();
        //reduce record to only required fields
        if ($required) {
            foreach ($required as $field) {
                if ($hasOnes === $field || isset($hasOnes[$field])) {
                    $field = $field . 'ID'; //add ID to hasones
                }
                $unique[$field] = $this->$field;
            }
        }
        return $unique;
    }

Intersects this item's required_fields with the data record.
This is used for uniquely adding items to the cart.

Intersects this item s required_fields with the data record . This is used for uniquely adding items to the cart .


public function onBeforeWrite()
    {
        parent::onBeforeWrite();
        if ($this->OrderID && $this->Order() && $this->Order()->isCart()) {
            $this->calculatetotal();
        }
    }

public function onBeforeWrite ( ) { parent :: onBeforeWrite ( ) ; if ( $ this -> OrderID && $ this -> Order ( ) && $ this -> Order ( ) -> isCart ( ) ) { $ this -> calculatetotal ( ) ; } }

Recalculate total before saving to database.

Recalculate total before saving to database .


public function ProductVariation($forcecurrent = false)
    {
        if ($this->ProductVariationID && $this->ProductVariationVersion && !$forcecurrent) {
            return Versioned::get_version(
                Variation::class,
                $this->ProductVariationID,
                $this->ProductVariationVersion
            );
        } elseif ($this->ProductVariationID
            && $product = Variation::get()->byID($this->ProductVariationID)
        ) {
            return $product;
        }
        return null;
    }

public function ProductVariation ( $ forcecurrent = false ) { if ( $ this -> ProductVariationID && $ this -> ProductVariationVersion && ! $ forcecurrent ) { return Versioned :: get_version ( Variation :: class , $ this -> ProductVariationID , $ this -> ProductVariationVersion ) ; } elseif ( $ this -> ProductVariationID && $ product = Variation :: get ( ) -> byID ( $ this -> ProductVariationID ) ) { return $ product ; } return null ; }

Overloaded relationship, for getting versioned variations

@param  boolean $current
@return Variation

Overloaded relationship for getting versioned variations


public function toString($separator = ', ')
    {
        $fields = array(
            $this->Company,
            $this->getName(),
            $this->Address,
            $this->AddressLine2,
            $this->City,
            $this->State,
            $this->PostalCode,
            $this->Country
        );
        $this->extend('updateToString', $fields);
        return implode($separator, array_filter($fields));
    }

public
function
toString
(
$
separator
=
', '
)
{
$
fields
=
array
(
$
this
->
Company
,
$
this
->
getName
(
)
,
$
this
->
Address
,
$
this
->
AddressLine2
,
$
this
->
City
,
$
this
->
State
,
$
this
->
PostalCode
,
$
this
->
Country
)
;
$
this
->
extend
(
'updateToString'
,
$
fields
)
;
return
implode
(
$
separator
,
array_filter
(
$
fields
)
)
;
}


public function getAddress()
    {
        $address = null;
        if ($data = $this->getLocationData()) {
            $address = Address::create();
            $address->update($data);
            $address->ID = 0; //ensure not in db
        }

        return $address;
    }

public
function
getAddress
(
)
{
$
address
=
;
if
(
$
data
=
$
this
->
getLocationData
(
)
)
{
$
address
=
Address
::
create
(
)
;
$
address
->
update
(
$
data
)
;
$
address
->
ID
=
0
;
//ensure not in db
}
return
$
address
;
}

Get location of user

@param  Address $address location
@return null|Address


public function CartForm()
    {
        $cart = $this->Cart();
        if (!$cart) {
            return false;
        }
        $form = CartForm::create($this, 'CartForm', $cart);

        $this->extend('updateCartForm', $form);

        return $form;
    }

public function CartForm ( ) { $ cart = $ this -> Cart ( ) ; if ( ! $ cart ) { return false ; } $ form = CartForm :: create ( $ this , 'CartForm' , $ cart ) ; $ this -> extend ( 'updateCartForm' , $ form ) ; return $ form ; }


public function value($subtotal = 0)
    {
        $totalWeight = $this->Weight();
        if (!$totalWeight) {
            return $this->Amount = 0;
        }
        $amount = 0;

        $table = $this->config()->weight_cost;
        if (!empty($table) && is_array($table)) {
            // ensure table is sorted
            ksort($table, SORT_NUMERIC);
            // set the amount to the highest value. In case the weight is higher than the max value in
            // the table, use the highest shipping cost and not 0!
            $amount = end($table);
            reset($table);
            foreach ($table as $weight => $cost) {
                if ($totalWeight <= $weight) {
                    $amount = $cost;
                    break;
                }
            }
        }
        return $this->Amount = $amount;
    }

Calculates shipping cost based on Product Weight.

Calculates shipping cost based on Product Weight .


public function Weight()
    {
        if ($this->weight) {
            return $this->weight;
        }
        $weight = 0;
        $order = $this->Order();
        if ($order && $orderItems = $order->Items()) {
            foreach ($orderItems as $orderItem) {
                if ($product = $orderItem->Product()) {
                    $weight = $weight + ($product->Weight * $orderItem->Quantity);
                }
            }
        }
        return $this->weight = $weight;
    }

public function Weight ( ) { if ( $ this -> weight ) { return $ this -> weight ; } $ weight = 0 ; $ order = $ this -> Order ( ) ; if ( $ order && $ orderItems = $ order -> Items ( ) ) { foreach ( $ orderItems as $ orderItem ) { if ( $ product = $ orderItem -> Product ( ) ) { $ weight = $ weight + ( $ product -> Weight * $ orderItem -> Quantity ) ; } } } return $ this -> weight = $ weight ; }

Calculate the total weight of the order

@return number


public function Sum($field, $onproduct = false)
    {
        $total = 0;
        foreach ($this->getIterator() as $item) {
            $quantity = ($field === 'Quantity') ? 1 : $item->Quantity;
            if (!$onproduct) {
                $total += $item->$field * $quantity;
            } elseif ($item->hasMethod($field)) {
                $total += $item->$field() * $quantity;
            } elseif ($product = $item->Product()) {
                $total += $product->$field * $quantity;
            }
        }
        return $total;
    }

public function Sum ( $ field , $ onproduct = false ) { $ total = 0 ; foreach ( $ this -> getIterator ( ) as $ item ) { $ quantity = ( $ field === 'Quantity' ) ? 1 : $ item -> Quantity ; if ( ! $ onproduct ) { $ total += $ item -> $ field * $ quantity ; } elseif ( $ item -> hasMethod ( $ field ) ) { $ total += $ item -> $ field ( ) * $ quantity ; } elseif ( $ product = $ item -> Product ( ) ) { $ total += $ product -> $ field * $ quantity ; } } return $ total ; }

Sums up all of desired field for items, and multiply by quantity.
Optionally sum product field instead.

@param string  $field     - field to sum
@param boolean $onproduct - sum from product or not

@return number - sum total of field

Sums up all of desired field for items and multiply by quantity . Optionally sum product field instead .


public function SubTotal()
    {
        $result = 0;
        foreach ($this->getIterator() as $item) {
            $result += $item->Total();
        }

        return $result;
    }

public function SubTotal ( ) { $ result = 0 ; foreach ( $ this -> getIterator ( ) as $ item ) { $ result += $ item -> Total ( ) ; } return $ result ; }

Add up the totals of all the order items in this list.

Add up the totals of all the order items in this list .


public function onBeforeWrite()
    {
        parent::onBeforeWrite();

        if (isset($_POST['ProductAttributes']) && is_array($_POST['ProductAttributes'])) {
            $this->AttributeValues()->setByIDList(array_values($_POST['ProductAttributes']));
        }

        $img = $this->Image();

        if ($img && $img->exists()) {
            $img->doPublish();
        }
    }

public function onBeforeWrite ( ) { parent :: onBeforeWrite ( ) ; if ( isset ( $ _POST [ 'ProductAttributes' ] ) && is_array ( $ _POST [ 'ProductAttributes' ] ) ) { $ this -> AttributeValues ( ) -> setByIDList ( array_values ( $ _POST [ 'ProductAttributes' ] ) ) ; } $ img = $ this -> Image ( ) ; if ( $ img && $ img -> exists ( ) ) { $ img -> doPublish ( ) ; } }

Save selected attributes - somewhat of a hack.

Save selected attributes - somewhat of a hack .


public function getViewer($action)
    {
        if (CheckoutPage::config()->first_step && $action == 'index') {
            $action = CheckoutPage::config()->first_step;
        }
        return parent::getViewer($action);
    }

public function getViewer ( $ action ) { if ( CheckoutPage :: config ( ) -> first_step && $ action == 'index' ) { $ action = CheckoutPage :: config ( ) -> first_step ; } return parent :: getViewer ( $ action ) ; }

Override viewer to get correct template for first step

{@inheritDoc}
@see \SilverStripe\CMS\Controllers\ContentController::getViewer()


public function Product($forcecurrent = false)
    {
        //TODO: this might need some unit testing to make sure it compliles with comment description
        //ie use live if in cart (however I see no logic for checking cart status)
        if ($this->ProductID && $this->ProductVersion && !$forcecurrent) {
            return Versioned::get_version(Product::class, $this->ProductID, $this->ProductVersion);
        } elseif ($this->ProductID
            && $product = Versioned::get_one_by_stage(
                Product::class,
                'Live',
                '"SilverShop_Product"."ID"  = ' . $this->ProductID
            )
        ) {
            return $product;
        }
        return null;
    }

Get related product
- live version if in cart, or
- historical version if order is placed

@param boolean $forcecurrent - force getting latest version of the product.

@return Product

Get related product - live version if in cart or - historical version if order is placed


public function updateCMSFields(FieldList $fields)
    {
        $fields->addFieldsToTab('Root.Variations', [
            ListboxField::create(
                'VariationAttributeTypes',
                _t(__CLASS__ . '.Attributes', "Attributes"),
                AttributeType::get()->map('ID', 'Title')->toArray()
            )
                ->setDescription(_t(
                    __CLASS__ . '.AttributesDescription',
                    'These are fields to indicate the way(s) each variation varies. Once selected, they can be edited on each variation.'
                )),
            GridField::create(
                'Variations',
                _t(__CLASS__ . '.Variations', 'Variations'),
                $this->owner->Variations(),
                GridFieldConfig_RecordEditor::create()
            )
        ]);
        if ($this->owner->Variations()->exists()) {
            $fields->addFieldToTab(
                'Root.Pricing',
                LabelField::create(
                    'variationspriceinstructinos',
                    _t(
                        __CLASS__ . '.VariationsInfo',
                        'Price - Because you have one or more variations, the price can be set in the "Variations" tab.'
                    )
                )
            );
            $fields->removeFieldFromTab('Root.Pricing', 'BasePrice');
            $fields->removeFieldFromTab('Root.Main', 'InternalItemID');
        }
    }

Adds variations specific fields to the CMS.

Adds variations specific fields to the CMS .


public function getVariationByAttributes(array $attributes)
    {
        if (!is_array($attributes)) {
            return null;
        }

        $attrs = array_filter(array_values($attributes));
        $set = Variation::get()->filter('ProductID', $this->owner->ID);

        foreach ($attrs as $i => $valueid) {
            $alias = "A$i";
            $set = $set->innerJoin(
                'SilverShop_Variation_AttributeValues',
                "\"SilverShop_Variation\".\"ID\" = \"$alias\".\"SilverShop_VariationID\"",
                $alias
            )->where(["\"$alias\".\"SilverShop_AttributeValueID\" = ?" => $valueid]);
        }

        return $set->first();
    }

Pass an array of attribute ids to query for the appropriate variation.

@param array $attributes

@return Variation|null

Pass an array of attribute ids to query for the appropriate variation .


public function generateVariationsFromAttributes(AttributeType $attributetype, array $values)
    {
        //TODO: introduce transactions here, in case objects get half made etc
        //if product has variation attribute types
        if (!empty($values)) {
            //TODO: get values dataobject set
            $avalues = $attributetype->convertArrayToValues($values);
            $existingvariations = $this->owner->Variations();
            if ($existingvariations->exists()) {
                //delete old variation, and create new ones - to prevent modification of exising variations
                foreach ($existingvariations as $oldvariation) {
                    $oldvalues = $oldvariation->AttributeValues();
                    foreach ($avalues as $value) {
                        $newvariation = $oldvariation->duplicate();
                        $newvariation->InternalItemID = $this->owner->InternalItemID . '-' . $newvariation->ID;
                        $newvariation->AttributeValues()->addMany($oldvalues);
                        $newvariation->AttributeValues()->add($value);
                        $newvariation->write();
                        $existingvariations->add($newvariation);
                    }
                    $existingvariations->remove($oldvariation);
                    $oldvariation->AttributeValues()->removeAll();
                    $oldvariation->delete();
                    $oldvariation->destroy();
                    //TODO: check that old variations actually stick around, as they will be needed for past orders etc
                }
            } else {
                foreach ($avalues as $value) {
                    $variation = Variation::create();
                    $variation->ProductID = $this->owner->ID;
                    $variation->Price = $this->owner->BasePrice;
                    $variation->write();
                    $variation->InternalItemID = $this->owner->InternalItemID . '-' . $variation->ID;
                    $variation->AttributeValues()->add($value);
                    $variation->write();
                    $existingvariations->add($variation);
                }
            }
        }
    }

Generates variations based on selected attributes.

@param  AttributeType $attributetype
@param  array         $values
@throws \SilverStripe\ORM\ValidationException

Generates variations based on selected attributes .


public function onAfterDelete()
    {
        $remove = false;
        // if a record is staged or live, leave it's variations alone.
        if (!property_exists($this, 'owner')) {
            $remove = true;
        } else {
            $staged = Versioned::get_by_stage($this->owner->ClassName, 'Stage')
                ->byID($this->owner->ID);
            $live = Versioned::get_by_stage($this->owner->ClassName, 'Live')
                ->byID($this->owner->ID);
            if (!$staged && !$live) {
                $remove = true;
            }
        }
        if ($remove) {
            foreach ($this->owner->Variations() as $variation) {
                $variation->delete();
                $variation->destroy();
            }
        }
    }

Make sure variations are deleted with product.

Make sure variations are deleted with product .


public function getComponentByType($type)
    {
        foreach ($this->components as $component) {
            if ($this->namespaced) {
                if ($component->Proxy() instanceof $type) {
                    return $component->Proxy();
                }
            } else {
                if ($component instanceof $type) {
                    return $component;
                }
            }
        }
    }

public function getComponentByType ( $ type ) { foreach ( $ this -> components as $ component ) { if ( $ this -> namespaced ) { if ( $ component -> Proxy ( ) instanceof $ type ) { return $ component -> Proxy ( ) ; } } else { if ( $ component instanceof $ type ) { return $ component ; } } } }

Returns the first available component with the given class or interface.

@param String ClassName

@return CheckoutComponent

Returns the first available component with the given class or interface .


public function getFormFields()
    {
        $fields = FieldList::create();
        foreach ($this->getComponents() as $component) {
            if ($cfields = $component->getFormFields($this->order)) {
                $fields->merge($cfields);
            } else {
                user_error('getFields on  ' . get_class($component) . ' must return a FieldList');
            }
        }
        return $fields;
    }

public
function
getFormFields
(
)
{
$
fields
=
FieldList
::
create
(
)
;
foreach
(
$
this
->
getComponents
(
)
as
$
component
)
{
if
(
$
cfields
=
$
component
->
getFormFields
(
$
this
->
order
)
)
{
$
fields
->
merge
(
$
cfields
)
;
}
else
{
user_error
(
'getFields on  '
.
get_class
(
$
component
)
.
' must return a FieldList'
)
;
}
}
return
$
fields
;
}

Get combined form fields

@return FieldList namespaced fields


public function validateData($data)
    {
        $result = ValidationResult::create();
        foreach ($this->getComponents() as $component) {
            try {
                $component->validateData($this->order, $this->dependantData($component, $data));
            } catch (ValidationException $e) {
                //transfer messages into a single result
                foreach ($e->getResult()->getMessages() as $code => $message) {
                    if (is_numeric($code)) {
                        $code = null;
                    }
                    if ($this->namespaced) {
                        $code = $component->namespaceFieldName($code);
                    }
                    $result->addError($message['message'], $code);
                }
            }
        }

        $this->order->extend('onValidateDataOnCheckout', $result);

        if (!$result->isValid()) {
            throw new ValidationException($result);
        }

        return true;
    }

Validate every component against given data.

@param array $data data to validate

@return boolean validation result
@throws ValidationException

Validate every component against given data .


public function getData()
    {
        $data = array();

        foreach ($this->getComponents() as $component) {
            $orderdata = $component->getData($this->order);

            if (is_array($orderdata)) {
                $data = array_merge($data, $orderdata);
            } else {
                user_error('getData on  ' . $component->name() . ' must return an array');
            }
        }
        return $data;
    }

public
function
getData
(
)
{
$
data
=
array
(
)
;
foreach
(
$
this
->
getComponents
(
)
as
$
component
)
{
$
orderdata
=
$
component
->
getData
(
$
this
->
order
)
;
if
(
is_array
(
$
orderdata
)
)
{
$
data
=
array_merge
(
$
data
,
$
orderdata
)
;
}
else
{
user_error
(
'getData on  '
.
$
component
->
name
(
)
.
' must return an array'
)
;
}
}
return
$
data
;
}

Get combined data

@return array map of field names to data values


public function setData($data)
    {
        foreach ($this->getComponents() as $component) {
            $component->setData($this->order, $this->dependantData($component, $data));
        }
    }

public function setData ( $ data ) { foreach ( $ this -> getComponents ( ) as $ component ) { $ component -> setData ( $ this -> order , $ this -> dependantData ( $ component , $ data ) ) ; } }

Set data on all components

@param array $data map of field names to data values


protected function dependantData($component, $data)
    {
        if (!$this->namespaced) { //no need to try and get un-namespaced dependant data
            return $data;
        }
        $dependantdata = array();
        foreach ($component->dependsOn() as $dependanttype) {
            $dependant = null;
            foreach ($this->components as $check) {
                if (get_class($check->Proxy()) == $dependanttype) {
                    $dependant = $check;
                    break;
                }
            }
            if (!$dependant) {
                user_error("Could not find a $dependanttype component, as depended by " . $component->name());
            }
            $dependantdata = array_merge(
                $dependantdata,
                $component->namespaceData($dependant->unnamespaceData($data))
            );
        }
        return array_merge($dependantdata, $data);
    }

Helper function for saving data from other components.

Helper function for saving data from other components .


public function current()
    {
        $session = ShopTools::getSession();
        //find order by id saved to session (allows logging out and retaining cart contents)
        if (!$this->order && $sessionid = $session->get(self::config()->cartid_session_name)) {
            $this->order = Order::get()->filter(
                [
                    'Status' => 'Cart',
                    'ID' => $sessionid,
                ]
            )->first();
        }
        if (!$this->calculateonce && $this->order) {
            $this->order->calculate();
            $this->calculateonce = true;
        }

        return $this->order ? $this->order : null;
    }

Get the current order, or return null if it doesn't exist.

@return Order

Get the current order or return null if it doesn t exist .


public function setCurrent(Order $cart)
    {
        if (!$cart->IsCart()) {
            trigger_error('Passed Order object is not cart status', E_ERROR);
        }
        $this->order = $cart;
        $session = ShopTools::getSession();
        $session->set(self::config()->cartid_session_name, $cart->ID);

        return $this;
    }

Set the current cart

@param Order $cart the Order to use as the current cart-content

@return ShoppingCart


protected function findOrMake()
    {
        if ($this->current()) {
            return $this->current();
        }
        $this->order = Order::create();
        if (Member::config()->login_joins_cart && ($member = Security::getCurrentUser())) {
            $this->order->MemberID = $member->ID;
        }
        $this->order->write();
        $this->order->extend('onStartOrder');

        $session = ShopTools::getSession();
        $session->set(self::config()->cartid_session_name, $this->order->ID);

        return $this->order;
    }

protected function findOrMake ( ) { if ( $ this -> current ( ) ) { return $ this -> current ( ) ; } $ this -> order = Order :: create ( ) ; if ( Member :: config ( ) -> login_joins_cart && ( $ member = Security :: getCurrentUser ( ) ) ) { $ this -> order -> MemberID = $ member -> ID ; } $ this -> order -> write ( ) ; $ this -> order -> extend ( 'onStartOrder' ) ; $ session = ShopTools :: getSession ( ) ; $ session -> set ( self :: config ( ) -> cartid_session_name , $ this -> order -> ID ) ; return $ this -> order ; }

Helper that only allows orders to be started internally.

@return Order

Helper that only allows orders to be started internally .


public function add(Buyable $buyable, $quantity = 1, $filter = [])
    {
        $order = $this->findOrMake();

        // If an extension throws an exception, error out
        try {
            $order->extend('beforeAdd', $buyable, $quantity, $filter);
        } catch (Exception $exception) {
            return $this->error($exception->getMessage());
        }

        if (!$buyable) {
            return $this->error(_t(__CLASS__ . '.ProductNotFound', 'Product not found.'));
        }

        $item = $this->findOrMakeItem($buyable, $quantity, $filter);
        if (!$item) {
            return false;
        }
        if (!$item->_brandnew) {
            $item->Quantity += $quantity;
        } else {
            $item->Quantity = $quantity;
        }

        // If an extension throws an exception, error out
        try {
            $order->extend('afterAdd', $item, $buyable, $quantity, $filter);
        } catch (Exception $exception) {
            return $this->error($exception->getMessage());
        }

        $item->write();
        $this->message(_t(__CLASS__ . '.ItemAdded', 'Item has been added successfully.'));

        return $item;
    }

Adds an item to the cart

@param Buyable $buyable
@param int     $quantity
@param array   $filter

@return boolean|OrderItem false or the new/existing item


public function remove(Buyable $buyable, $quantity = null, $filter = [])
    {
        $order = $this->current();

        if (!$order) {
            return $this->error(_t(__CLASS__ . '.NoOrder', 'No current order.'));
        }

        // If an extension throws an exception, error out
        try {
            $order->extend('beforeRemove', $buyable, $quantity, $filter);
        } catch (Exception $exception) {
            return $this->error($exception->getMessage());
        }

        $item = $this->get($buyable, $filter);

        if (!$item || !$this->removeOrderItem($item, $quantity)) {
            return false;
        }

        // If an extension throws an exception, error out
        // TODO: There should be a rollback
        try {
            $order->extend('afterRemove', $item, $buyable, $quantity, $filter);
        } catch (Exception $exception) {
            return $this->error($exception->getMessage());
        }

        $this->message(_t(__CLASS__ . '.ItemRemoved', 'Item has been successfully removed.'));

        return true;
    }

Remove an item from the cart.

@param Buyable $buyable
@param int     $quantity - number of items to remove, or leave null for all items (default)
@param array   $filter

@return boolean success/failure


public function removeOrderItem(OrderItem $item, $quantity = null)
    {
        $order = $this->current();

        if (!$order) {
            return $this->error(_t(__CLASS__ . '.NoOrder', 'No current order.'));
        }

        if (!$item || $item->OrderID != $order->ID) {
            return $this->error(_t(__CLASS__ . '.ItemNotFound', 'Item not found.'));
        }

        //if $quantity will become 0, then remove all
        if (!$quantity || ($item->Quantity - $quantity) <= 0) {
            $item->delete();
            $item->destroy();
        } else {
            $item->Quantity -= $quantity;
            $item->write();
        }

        return true;
    }

Remove a specific order item from cart

@param  OrderItem $item
@param  int       $quantity - number of items to remove or leave `null` to remove all items (default)
@return boolean success/failure


public function setQuantity(Buyable $buyable, $quantity = 1, $filter = [])
    {
        if ($quantity <= 0) {
            return $this->remove($buyable, $quantity, $filter);
        }

        $item = $this->findOrMakeItem($buyable, $quantity, $filter);

        if (!$item || !$this->updateOrderItemQuantity($item, $quantity, $filter)) {
            return false;
        }

        return $item;
    }

public function setQuantity ( Buyable $ buyable , $ quantity = 1 , $ filter = [ ] ) { if ( $ quantity <= 0 ) { return $ this -> remove ( $ buyable , $ quantity , $ filter ) ; } $ item = $ this -> findOrMakeItem ( $ buyable , $ quantity , $ filter ) ; if ( ! $ item || ! $ this -> updateOrderItemQuantity ( $ item , $ quantity , $ filter ) ) { return false ; } return $ item ; }

Sets the quantity of an item in the cart.
Will automatically add or remove item, if necessary.

@param Buyable $buyable
@param int     $quantity
@param array   $filter

@return boolean|OrderItem false or the new/existing item

Sets the quantity of an item in the cart . Will automatically add or remove item if necessary .


public function updateOrderItemQuantity(OrderItem $item, $quantity = 1, $filter = [])
    {
        $order = $this->current();

        if (!$order) {
            return $this->error(_t(__CLASS__ . '.NoOrder', 'No current order.'));
        }

        if (!$item || $item->OrderID != $order->ID) {
            return $this->error(_t(__CLASS__ . '.ItemNotFound', 'Item not found.'));
        }

        $buyable = $item->Buyable();
        // If an extension throws an exception, error out
        try {
            $order->extend('beforeSetQuantity', $buyable, $quantity, $filter);
        } catch (Exception $exception) {
            return $this->error($exception->getMessage());
        }

        $item->Quantity = $quantity;

        // If an extension throws an exception, error out
        try {
            $order->extend('afterSetQuantity', $item, $buyable, $quantity, $filter);
        } catch (Exception $exception) {
            return $this->error($exception->getMessage());
        }

        $item->write();
        $this->message(_t(__CLASS__ . '.QuantitySet', 'Quantity has been set.'));

        return true;
    }

Update quantity of a given order item

@param  OrderItem $item
@param  int       $quantity the new quantity to use
@param  array     $filter
@return boolean success/failure


private function findOrMakeItem(Buyable $buyable, $quantity = 1, $filter = [])
    {
        $order = $this->findOrMake();

        if (!$buyable || !$order) {
            return null;
        }

        $item = $this->get($buyable, $filter);

        if (!$item) {
            $member = Security::getCurrentUser();

            $buyable = $this->getCorrectBuyable($buyable);

            if (!$buyable->canPurchase($member, $quantity)) {
                return $this->error(
                    _t(
                        __CLASS__ . '.CannotPurchase',
                        'This {Title} cannot be purchased.',
                        '',
                        ['Title' => $buyable->i18n_singular_name()]
                    )
                );
                //TODO: produce a more specific message
            }

            $item = $buyable->createItem($quantity, $filter);
            $item->OrderID = $order->ID;
            $item->write();

            $order->Items()->add($item);

            $item->_brandnew = true; // flag as being new
        }

        return $item;
    }

private
function
findOrMakeItem
(
Buyable
$
buyable
,
$
quantity
=
1
,
$
filter
=
[
]
)
{
$
order
=
$
this
->
findOrMake
(
)
;
if
(
!
$
buyable
||
!
$
order
)
{
return
;
}
$
item
=
$
this
->
get
(
$
buyable
,
$
filter
)
;
if
(
!
$
item
)
{
$
member
=
Security
::
getCurrentUser
(
)
;
$
buyable
=
$
this
->
getCorrectBuyable
(
$
buyable
)
;
if
(
!
$
buyable
->
canPurchase
(
$
member
,
$
quantity
)
)
{
return
$
this
->
error
(
_t
(
__CLASS__
.
'.CannotPurchase'
,
'This {Title} cannot be purchased.'
,
''
,
[
'Title'
=>
$
buyable
->
i18n_singular_name
(
)
]
)
)
;
//TODO: produce a more specific message
}
$
item
=
$
buyable
->
createItem
(
$
quantity
,
$
filter
)
;
$
item
->
OrderID
=
$
order
->
ID
;
$
item
->
write
(
)
;
$
order
->
Items
(
)
->
add
(
$
item
)
;
$
item
->
_brandnew
=
true
;
// flag as being new
}
return
$
item
;
}

Finds or makes an order item for a given product + filter.

@param Buyable $buyable  the buyable
@param int     $quantity quantity to add
@param array   $filter

@return OrderItem the found or created item
@throws \SilverStripe\ORM\ValidationException

Finds or makes an order item for a given product + filter .


public function get(Buyable $buyable, $customfilter = array())
    {
        $order = $this->current();
        if (!$buyable || !$order) {
            return null;
        }

        $buyable = $this->getCorrectBuyable($buyable);

        $filter = array(
            'OrderID' => $order->ID,
        );

        $itemclass = Config::inst()->get(get_class($buyable), 'order_item');
        $relationship = Config::inst()->get($itemclass, 'buyable_relationship');
        $filter[$relationship . 'ID'] = $buyable->ID;
        $required = ['OrderID', $relationship . 'ID'];
        if (is_array($itemclass::config()->required_fields)) {
            $required = array_merge($required, $itemclass::config()->required_fields);
        }
        $query = new MatchObjectFilter($itemclass, array_merge($customfilter, $filter), $required);
        $item = $itemclass::get()->where($query->getFilter())->first();
        if (!$item) {
            return $this->error(_t(__CLASS__ . '.ItemNotFound', 'Item not found.'));
        }

        return $item;
    }

public
function
get
(
Buyable
$
buyable
,
$
customfilter
=
array
(
)
)
{
$
order
=
$
this
->
current
(
)
;
if
(
!
$
buyable
||
!
$
order
)
{
return
;
}
$
buyable
=
$
this
->
getCorrectBuyable
(
$
buyable
)
;
$
filter
=
array
(
'OrderID'
=>
$
order
->
ID
,
)
;
$
itemclass
=
Config
::
inst
(
)
->
get
(
get_class
(
$
buyable
)
,
'order_item'
)
;
$
relationship
=
Config
::
inst
(
)
->
get
(
$
itemclass
,
'buyable_relationship'
)
;
$
filter
[
$
relationship
.
'ID'
]
=
$
buyable
->
ID
;
$
required
=
[
'OrderID'
,
$
relationship
.
'ID'
]
;
if
(
is_array
(
$
itemclass
::
config
(
)
->
required_fields
)
)
{
$
required
=
array_merge
(
$
required
,
$
itemclass
::
config
(
)
->
required_fields
)
;
}
$
query
=
new
MatchObjectFilter
(
$
itemclass
,
array_merge
(
$
customfilter
,
$
filter
)
,
$
required
)
;
$
item
=
$
itemclass
::
get
(
)
->
where
(
$
query
->
getFilter
(
)
)
->
first
(
)
;
if
(
!
$
item
)
{
return
$
this
->
error
(
_t
(
__CLASS__
.
'.ItemNotFound'
,
'Item not found.'
)
)
;
}
return
$
item
;
}

Finds an existing order item.

@param Buyable $buyable
@param array   $customfilter

@return OrderItem the item requested or null


public function archiveorderid($requestedOrderId = null)
    {
        $session = ShopTools::getSession();
        $sessionId = $session->get(self::config()->cartid_session_name);
        $order = Order::get()
            ->filter('Status:not', 'Cart')
            ->byId($sessionId);

        if ($order && !$order->IsCart()) {
            OrderManipulationExtension::add_session_order($order);
        }
        // in case there was no order requested
        // OR there was an order requested AND it's the same one as currently in the session,
        // then clear the cart. This check is here to prevent clearing of the cart if the user just
        // wants to view an old order (via AccountPage).
        if (!$requestedOrderId || ($sessionId == $requestedOrderId)) {
            $this->clear();
        }
    }

Store old cart id in session order history

@param int|null $requestedOrderId optional parameter that denotes the order that was requested


public function value($incoming)
    {
        $this->Rate = self::config()->rate;
        //inclusive tax requires a different calculation
        return self::config()->exclusive
            ?
            $incoming * $this->Rate
            :
            $incoming - round($incoming / (1 + $this->Rate), Order::config()->rounding_precision);
    }

Get the tax amount to charge on the order.

Get the tax amount to charge on the order .


public static function price_for_display($price)
    {
        $currency = ShopConfigExtension::get_site_currency();
        $field = DBMoney::create_field(DBMoney::class, 0, 'Price');
        $field->setAmount($price);
        $field->setCurrency($currency);
        return $field;
    }

public static function price_for_display ( $ price ) { $ currency = ShopConfigExtension :: get_site_currency ( ) ; $ field = DBMoney :: create_field ( DBMoney :: class , 0 , 'Price' ) ; $ field -> setAmount ( $ price ) ; $ field -> setCurrency ( $ currency ) ; return $ field ; }

Convert a numeric price to the shop currency

@param  mixed $price the price to convert
@return DBMoney the price wrapped in a Money DBField to be used for templates or similar


public function imageByFilename(&$obj, $val, $record)
    {
        $filename = trim(strtolower(Convert::raw2sql($val)));
        $filenamedashes = str_replace(' ', '-', $filename);
        if ($filename
            && $image = Image::get()->whereAny(
                [
                    "LOWER(\"FileFilename\") LIKE '%$filename%'",
                    "LOWER(\"FileFilename\") LIKE '%$filenamedashes%'"
                ]
            )->first()
        ) { //ignore case
            if ($image instanceof Image && $image->exists()) {
                return $image;
            }
        }
        return null;
    }


public function setContent(&$obj, $val, $record)
    {
        $val = trim($val);
        if ($val) {
            $paragraphs = explode("\n", $val);
            $obj->Content = '
' . implode('

', $paragraphs) . '
';
        }
    }

public function setContent ( & $ obj , $ val , $ record ) { $ val = trim ( $ val ) ; if ( $ val ) { $ paragraphs = explode ( "\n" , $ val ) ; $ obj -> Content = '
' . implode ( '

' , $ paragraphs ) . '
' ; } }


public function getCountriesList($prefixisocode = false)
    {
        $countries = self::config()->iso_3166_country_codes;
        asort($countries);
        if ($allowed = $this->owner->AllowedCountries) {
            $allowed = json_decode($allowed);
            if (!empty($allowed)) {
                $countries = array_intersect_key($countries, array_flip($allowed));
            }
        }
        if ($prefixisocode) {
            foreach ($countries as $key => $value) {
                $countries[$key] = "$key - $value";
            }
        }
        return $countries;
    }

public
function
getCountriesList
(
$
prefixisocode
=
false
)
{
$
countries
=
self
::
config
(
)
->
iso_3166_country_codes
;
asort
(
$
countries
)
;
if
(
$
allowed
=
$
this
->
owner
->
AllowedCountries
)
{
$
allowed
=
json_decode
(
$
allowed
)
;
if
(
!
empty
(
$
allowed
)
)
{
$
countries
=
array_intersect_key
(
$
countries
,
array_flip
(
$
allowed
)
)
;
}
}
if
(
$
prefixisocode
)
{
foreach
(
$
countries
as
$
key
=>
$
value
)
{
$
countries
[
$
key
]
=
"$key - $value"
;
}
}
return
$
countries
;
}

Get list of allowed countries

@param boolean $prefixisocode - prefix the country code

@return array


public function getSingleCountry($fullname = false)
    {
        $countries = $this->getCountriesList();
        if (count($countries) == 1) {
            if ($fullname) {
                return array_pop($countries);
            } else {
                reset($countries);
                return key($countries);
            }
        }
        return null;
    }

public function getSingleCountry ( $ fullname = false ) { $ countries = $ this -> getCountriesList ( ) ; if ( count ( $ countries ) == 1 ) { if ( $ fullname ) { return array_pop ( $ countries ) ; } else { reset ( $ countries ) ; return key ( $ countries ) ; } } return null ; }

For shops that only sell to a single country,
this will return the country code, otherwise null.

@param string fullname get long form name of country

@return string country code

For shops that only sell to a single country this will return the country code otherwise null .


public static function countryCode2name($code)
    {
        $codes = self::config()->iso_3166_country_codes;
        if (isset($codes[$code])) {
            return $codes[$code];
        }
        return $code;
    }

public static function countryCode2name ( $ code ) { $ codes = self :: config ( ) -> iso_3166_country_codes ; if ( isset ( $ codes [ $ code ] ) ) { return $ codes [ $ code ] ; } return $ code ; }

Convert iso country code to English country name

@return string - name of country


public function Cart()
    {
        $order = ShoppingCart::curr();
        if (!$order || !$order->Items() || !$order->Items()->exists()) {
            return false;
        }

        return $order;
    }

public function Cart ( ) { $ order = ShoppingCart :: curr ( ) ; if ( ! $ order || ! $ order -> Items ( ) || ! $ order -> Items ( ) -> exists ( ) ) { return false ; } return $ order ; }

Get the cart, and do last minute calculation if necessary.

Get the cart and do last minute calculation if necessary .


public function convertArrayToValues(array $values)
    {
        $set = ArrayList::create();
        foreach ($values as $value) {
            $val = $this->Values()->find('Value', $value);
            if (!$val) {  //TODO: ignore case, if possible
                $val = AttributeValue::create();
                $val->Value = $value;
                $val->TypeID = $this->ID;
                $val->write();
            }
            $set->push($val);
        }

        return $set;
    }

public
function
convertArrayToValues
(
array
$
values
)
{
$
set
=
ArrayList
::
create
(
)
;
foreach
(
$
values
as
$
value
)
{
$
val
=
$
this
->
Values
(
)
->
find
(
'Value'
,
$
value
)
;
if
(
!
$
val
)
{
//TODO: ignore case, if possible
$
val
=
AttributeValue
::
create
(
)
;
$
val
->
Value
=
$
value
;
$
val
->
TypeID
=
$
this
->
ID
;
$
val
->
write
(
)
;
}
$
set
->
push
(
$
val
)
;
}
return
$
set
;
}

Finds or creates values for this type.

@param array $values

@return ArrayList


public function getDropDownField($emptystring = null, $values = null)
    {
        $values = ($values) ? $values : $this->Values()->sort(['Sort' => 'ASC', 'Value' => 'ASC']);

        if ($values->exists()) {
            $field = DropdownField::create(
                'ProductAttributes[' . $this->ID . ']',
                $this->Name,
                $values->map('ID', 'Value')
            );

            if ($emptystring) {
                $field->setEmptyString($emptystring);
            }

            return $field;
        }

        return null;
    }

public function getDropDownField ( $ emptystring = null , $ values = null ) { $ values = ( $ values ) ? $ values : $ this -> Values ( ) -> sort ( [ 'Sort' => 'ASC' , 'Value' => 'ASC' ] ) ; if ( $ values -> exists ( ) ) { $ field = DropdownField :: create ( 'ProductAttributes[' . $ this -> ID . ']' , $ this -> Name , $ values -> map ( 'ID' , 'Value' ) ) ; if ( $ emptystring ) { $ field -> setEmptyString ( $ emptystring ) ; } return $ field ; } return null ; }

Returns a dropdown field for the user to select a variant.

@param string    $emptyString
@param ArrayList $values

@return DropdownField

Returns a dropdown field for the user to select a variant .


public function ProductsShowable($recursive = true)
    {
        // Figure out the categories to check
        $groupids = array($this->ID);
        if (!empty($recursive) && self::config()->include_child_groups) {
            $groupids += $this->AllChildCategoryIDs();
        }
        $products = Product::get()->filterAny(
            [
            'ParentID' => $groupids,
            'ProductCategories.ID' => $groupids
            ]
        );
        if (self::config()->must_have_price) {
            if (Product::has_extension(ProductVariationsExtension::class)) {
                $products = $products->filterAny(
                    [
                    'BasePrice:GreaterThan' => 0,
                    'Variations.Price:GreaterThan' => 0
                    ]
                );
            } else {
                $products = $products->filter('BasePrice:GreaterThan', 0);
            }
        }

        $this->extend('updateProductsShowable', $products);

        return $products;
    }

public
function
ProductsShowable
(
$
recursive
=
true
)
{
// Figure out the categories to check
$
groupids
=
array
(
$
this
->
ID
)
;
if
(
!
empty
(
$
recursive
)
&&
self
::
config
(
)
->
include_child_groups
)
{
$
groupids
+=
$
this
->
AllChildCategoryIDs
(
)
;
}
$
products
=
Product
::
get
(
)
->
filterAny
(
[
'ParentID'
=>
$
groupids
,
'ProductCategories.ID'
=>
$
groupids
]
)
;
if
(
self
::
config
(
)
->
must_have_price
)
{
if
(
Product
::
has_extension
(
ProductVariationsExtension
::
class
)
)
{
$
products
=
$
products
->
filterAny
(
[
'BasePrice:GreaterThan'
=>
0
,
'Variations.Price:GreaterThan'
=>
0
]
)
;
}
else
{
$
products
=
$
products
->
filter
(
'BasePrice:GreaterThan'
,
0
)
;
}
}
$
this
->
extend
(
'updateProductsShowable'
,
$
products
)
;
return
$
products
;
}

Retrieve a set of products, based on the given parameters. Checks get query for sorting and pagination.

@param bool $recursive include sub-categories

@return DataList

Retrieve a set of products based on the given parameters . Checks get query for sorting and pagination .


public function AllChildCategoryIDs()
    {
        $ids = [$this->ID];
        $allids = [];
        do {
            $ids = ProductCategory::get()
                ->filter('ParentID', $ids)
                ->getIDList();
            $allids += $ids;
        } while (!empty($ids));

        return $allids;
    }

public function AllChildCategoryIDs ( ) { $ ids = [ $ this -> ID ] ; $ allids = [ ] ; do { $ ids = ProductCategory :: get ( ) -> filter ( 'ParentID' , $ ids ) -> getIDList ( ) ; $ allids += $ ids ; } while ( ! empty ( $ ids ) ) ; return $ allids ; }

Loop down each level of children to get all ids.

Loop down each level of children to get all ids .


public function ChildCategories($recursive = false)
    {
        $ids = array($this->ID);
        if ($recursive) {
            $ids += $this->AllChildCategoryIDs();
        }

        return ProductCategory::get()->filter('ParentID', $ids);
    }

public function ChildCategories ( $ recursive = false ) { $ ids = array ( $ this -> ID ) ; if ( $ recursive ) { $ ids += $ this -> AllChildCategoryIDs ( ) ; } return ProductCategory :: get ( ) -> filter ( 'ParentID' , $ ids ) ; }

Return children ProductCategory pages of this category.

@param bool $recursive

@return DataList

Return children ProductCategory pages of this category .


public function GroupsMenu()
    {
        if ($this->Parent() instanceof ProductCategory) {
            return $this->Parent()->GroupsMenu();
        }
        return ProductCategory::get()
            ->filter('ParentID', $this->ID);
    }

public function GroupsMenu ( ) { if ( $ this -> Parent ( ) instanceof ProductCategory ) { return $ this -> Parent ( ) -> GroupsMenu ( ) ; } return ProductCategory :: get ( ) -> filter ( 'ParentID' , $ this -> ID ) ; }

Recursively generate a product menu, starting from the topmost category.

@return DataList

Recursively generate a product menu starting from the topmost category .


public function NestedTitle($level = 10, $separator = ' > ', $field = 'MenuTitle')
    {
        $item = $this;
        while ($item && $level > 0) {
            $parts[] = $item->{$field};
            $item = $item->Parent;
            $level--;
        }
        return implode($separator, array_reverse($parts));
    }

public
function
NestedTitle
(
$
level
=
10
,
$
separator
=
' > '
,
$
field
=
'MenuTitle'
)
{
$
item
=
$
this
;
while
(
$
item
&&
$
level
>
0
)
{
$
parts
[
]
=
$
item
->
{
$
field
}
;
$
item
=
$
item
->
Parent
;
$
level
;
}
return
implode
(
$
separator
,
array_reverse
(
$
parts
)
)
;
}

Override the nested title defaults, to show deeper nesting in the CMS.

@param integer $level     nesting level
@param string  $separator seperate nesting with this string

Override the nested title defaults to show deeper nesting in the CMS .


public function ItemEditForm()
    {
        $form = parent::ItemEditForm();
        $printlink = $this->Link('printorder') . '?print=1';
        $printwindowjs = <<<JS
            window.open('$printlink', 'print_order', 'toolbar=0,scrollbars=1,location=1,statusbar=0,menubar=0,resizable=1,width=800,height=600,left = 50,top = 50');return false;
JS;
        $form->Actions()->push(
            LiteralField::create(
                'PrintOrder',
                "<button class=\"no-ajax grid-print-button btn action btn-primary font-icon-print\" onclick=\"javascript:$printwindowjs\">"
                . _t('SilverShop\Model\Order.Print', 'Print') . '</button>'
            )
        );

        return $form;
    }


public function printorder()
    {
        Requirements::clear();
        //include print javascript, if print argument is provided
        if (isset($_REQUEST['print']) && $_REQUEST['print']) {
            Requirements::customScript('if(document.location.href.indexOf(\'print=1\') > 0) {window.print();}');
        }
        $title = _t('SilverShop\Model\Order.Invoice', 'Invoice');
        if ($id = $this->popupController->getRequest()->param('ID')) {
            $title .= " #$id";
        }

        return $this->record->customise(
            [
                'SiteConfig' => SiteConfig::current_site_config(),
                'Title'      => $title,
            ]
        )->renderWith('SilverShop\Admin\OrderAdmin_Printable');
    }


private function nextstep()
    {
        $steps = $this->owner->getSteps();
        $found = false;
        foreach ($steps as $step => $class) {
            //determine if this is the current step
            if (method_exists($this, $step)) {
                $found = true;
            } elseif ($found) {
                return $step;
            }
        }
        return null;
    }

private
function
nextstep
(
)
{
$
steps
=
$
this
->
owner
->
getSteps
(
)
;
$
found
=
false
;
foreach
(
$
steps
as
$
step
=>
$
class
)
{
//determine if this is the current step
if
(
method_exists
(
$
this
,
$
step
)
)
{
$
found
=
true
;
}
elseif
(
$
found
)
{
return
$
step
;
}
}
return
;
}

Get the next step action

@return string|NULL


public function getList()
    {
        $list = parent::getList();

        if ($this->modelClass == Order::class) {
            // Exclude hidden statuses
            $list = $list->exclude('Status', Order::config()->hidden_status);
            $this->extend('updateList', $list);
        }

        return $list;
    }

public
function
getList
(
)
{
$
list
=
parent
::
getList
(
)
;
if
(
$
this
->
modelClass
==
Order
::
class
)
{
// Exclude hidden statuses
$
list
=
$
list
->
exclude
(
'Status'
,
Order
::
config
(
)
->
hidden_status
)
;
$
this
->
extend
(
'updateList'
,
$
list
)
;
}
return
$
list
;
}


public function getEditForm($id = null, $fields = null)
    {
        $form = parent::getEditForm($id, $fields);
        if ($this->modelClass == Order::class) {
            /** @var GridFieldConfig $config */
            $config = $form
                ->Fields()
                ->fieldByName($this->sanitiseClassName($this->modelClass))
                ->getConfig();

            $config
                ->getComponentByType(GridFieldSortableHeader::class)
                ->setFieldSorting([ 'StatusI18N' => 'Status' ]);

            $config
                ->getComponentByType(GridFieldDetailForm::class)
                ->setItemRequestClass(OrderGridFieldDetailForm_ItemRequest::class); //see below
        }

        if ($this->modelClass == OrderStatusLog::class) {
            /** @var GridFieldConfig $config */
            $config = $form
                ->Fields()
                ->fieldByName($this->sanitiseClassName($this->modelClass))
                ->getConfig();

            // Remove add new button
            $config->removeComponentsByType($config->getComponentByType(GridFieldAddNewButton::class));
        }

        return $form;
    }

public
function
getEditForm
(
$
id
=
,
$
fields
=
)
{
$
form
=
parent
::
getEditForm
(
$
id
,
$
fields
)
;
if
(
$
this
->
modelClass
==
Order
::
class
)
{
/** @var GridFieldConfig $config */
$
config
=
$
form
->
Fields
(
)
->
fieldByName
(
$
this
->
sanitiseClassName
(
$
this
->
modelClass
)
)
->
getConfig
(
)
;
$
config
->
getComponentByType
(
GridFieldSortableHeader
::
class
)
->
setFieldSorting
(
[
'StatusI18N'
=>
'Status'
]
)
;
$
config
->
getComponentByType
(
GridFieldDetailForm
::
class
)
->
setItemRequestClass
(
OrderGridFieldDetailForm_ItemRequest
::
class
)
;
//see below
}
if
(
$
this
->
modelClass
==
OrderStatusLog
::
class
)
{
/** @var GridFieldConfig $config */
$
config
=
$
form
->
Fields
(
)
->
fieldByName
(
$
this
->
sanitiseClassName
(
$
this
->
modelClass
)
)
->
getConfig
(
)
;
// Remove add new button
$
config
->
removeComponentsByType
(
$
config
->
getComponentByType
(
GridFieldAddNewButton
::
class
)
)
;
}
return
$
form
;
}

Replace gridfield detail form to include print functionality


private function getSubset(FieldList $fields, $subset = array())
    {
        if (empty($subset)) {
            return $fields;
        }
        $subfieldlist = FieldList::create();
        foreach ($subset as $field) {
            if ($field = $fields->fieldByName($field)) {
                $subfieldlist->push($field);
            }
        }
        return $subfieldlist;
    }

private function getSubset ( FieldList $ fields , $ subset = array ( ) ) { if ( empty ( $ subset ) ) { return $ fields ; } $ subfieldlist = FieldList :: create ( ) ; foreach ( $ subset as $ field ) { if ( $ field = $ fields -> fieldByName ( $ field ) ) { $ subfieldlist -> push ( $ field ) ; } } return $ subfieldlist ; }

Helper function for reducing a field set to a given subset,
in the given order.

@param FieldList $fields form fields to take a subset from.
@param array     $subset list of field names to return as subset

@return FieldList subset of form fields

Helper function for reducing a field set to a given subset in the given order .