示例#1
0
 /**
  * Load the datagrid
  */
 private function loadDataGrid()
 {
     // create a new source-object
     $source = new SpoonDataGridSourceDB(FrontendModel::getDB(), array(FrontendMailmotorModel::QRY_DATAGRID_BROWSE_SENT, array('sent', FRONTEND_LANGUAGE)));
     // create datagrid
     $this->dataGrid = new SpoonDataGrid($source);
     $this->dataGrid->setCompileDirectory(FRONTEND_CACHE_PATH . '/compiled_templates');
     // set hidden columns
     $this->dataGrid->setColumnsHidden(array('id', 'status'));
     // set headers values
     $headers['name'] = SpoonFilter::ucfirst(FL::lbl('Name'));
     $headers['send_on'] = SpoonFilter::ucfirst(FL::lbl('Sent'));
     // set headers
     $this->dataGrid->setHeaderLabels($headers);
     // sorting columns
     $this->dataGrid->setSortingColumns(array('name', 'send_on'), 'name');
     $this->dataGrid->setSortParameter('desc');
     // set colum URLs
     $this->dataGrid->setColumnURL('name', FrontendNavigation::getURLForBlock('mailmotor', 'detail') . '/[id]');
     // set column functions
     $this->dataGrid->setColumnFunction(array('SpoonDate', 'getTimeAgo'), array('[send_on]'), 'send_on', true);
     // add styles
     $this->dataGrid->setColumnAttributes('name', array('class' => 'title'));
     // set paging limit
     $this->dataGrid->setPagingLimit(self::MAILINGS_PAGING_LIMIT);
 }
示例#2
0
    /**
     * Load the data, don't forget to validate the incoming data
     */
    private function getData()
    {
        // validate incoming parameters
        if ($this->URL->getParameter(1) === null) {
            $this->redirect(FrontendNavigation::getURL(404));
        }
        // fetch record
        $this->record = FrontendTagsModel::get($this->URL->getParameter(1));
        // validate record
        if (empty($this->record)) {
            $this->redirect(FrontendNavigation::getURL(404));
        }
        // fetch modules
        $this->modules = FrontendTagsModel::getModulesForTag($this->record['id']);
        // loop modules
        foreach ($this->modules as $module) {
            // set module class
            $class = 'Frontend' . SpoonFilter::toCamelCase($module) . 'Model';
            // get the ids of the items linked to the tag
            $otherIds = (array) FrontendModel::getDB()->getColumn('SELECT other_id
				 FROM modules_tags
				 WHERE module = ? AND tag_id = ?', array($module, $this->record['id']));
            // set module class
            $class = 'Frontend' . SpoonFilter::toCamelCase($module) . 'Model';
            // get the items that are linked to the tags
            $items = (array) FrontendTagsModel::callFromInterface($module, $class, 'getForTags', $otherIds);
            // add into results array
            if (!empty($items)) {
                $this->results[] = array('name' => $module, 'label' => FL::lbl(SpoonFilter::ucfirst($module)), 'items' => $items);
            }
        }
    }
示例#3
0
    /**
     * Get all items in a category
     *
     * @return	array
     * @param	int $categoryId			The id of the category.
     */
    public static function getQuestions($categoryId)
    {
        return (array) FrontendModel::getDB()->getRecords('SELECT i.*
															FROM faq_questions AS i
															WHERE i.category_id = ? AND i.language = ? AND i.hidden = ?
															ORDER BY i.sequence', array((int) $categoryId, FRONTEND_LANGUAGE, 'N'));
    }
示例#4
0
    /**
     * Build the language files
     *
     * @return	void
     * @param	string $language		The language to build the locale-file for.
     * @param	string $application		The application to build the locale-file for.
     */
    public static function buildCache($language, $application)
    {
        // get db
        $db = FrontendModel::getDB();
        // get types
        $types = $db->getEnumValues('locale', 'type');
        // get locale for backend
        $locale = (array) $db->getRecords('SELECT type, module, name, value
											FROM locale
											WHERE language = ? AND application = ?
											ORDER BY type ASC, name ASC, module ASC', array((string) $language, (string) $application));
        // start generating PHP
        $value = '<?php' . "\n";
        $value .= '/**' . "\n";
        $value .= ' *' . "\n";
        $value .= ' * This file is generated by Fork CMS, it contains' . "\n";
        $value .= ' * more information about the locale. Do NOT edit.' . "\n";
        $value .= ' * ' . "\n";
        $value .= ' * @author		Fork CMS' . "\n";
        $value .= ' * @generated	' . date('Y-m-d H:i:s') . "\n";
        $value .= ' */' . "\n";
        $value .= "\n";
        // loop types
        foreach ($types as $type) {
            // default module
            $modules = array('core');
            // continue output
            $value .= "\n";
            $value .= '// init var' . "\n";
            $value .= '$' . $type . ' = array();' . "\n";
            $value .= '$' . $type . '[\'core\'] = array();' . "\n";
            // loop locale
            foreach ($locale as $i => $item) {
                // types match
                if ($item['type'] == $type) {
                    // new module
                    if (!in_array($item['module'], $modules)) {
                        $value .= '$' . $type . '[\'' . $item['module'] . '\'] = array();' . "\n";
                        $modules[] = $item['module'];
                    }
                    // parse
                    if ($application == 'backend') {
                        $value .= '$' . $type . '[\'' . $item['module'] . '\'][\'' . $item['name'] . '\'] = \'' . str_replace('\\"', '"', addslashes($item['value'])) . '\';' . "\n";
                    } else {
                        $value .= '$' . $type . '[\'' . $item['name'] . '\'] = \'' . str_replace('\\"', '"', addslashes($item['value'])) . '\';' . "\n";
                    }
                    // unset
                    unset($locale[$i]);
                }
            }
        }
        // close php
        $value .= "\n";
        $value .= '?>';
        // store
        SpoonFile::setContent(constant(mb_strtoupper($application) . '_CACHE_PATH') . '/locale/' . $language . '.php', $value);
    }
示例#5
0
    /**
     * Get related "things" based on tags
     */
    private function getRelated()
    {
        // loop tags
        foreach ($this->tags as $tag) {
            // fetch entries
            $items = (array) FrontendModel::getDB()->getRecords('SELECT mt.module, mt.other_id
				 FROM modules_tags AS mt
				 INNER JOIN tags AS t ON t.id = mt.tag_id
				 WHERE t.language = ? AND t.tag = ?', array(FRONTEND_LANGUAGE, $tag));
            // loop items
            foreach ($items as $item) {
                // loop existing items
                foreach ($this->related as $related) {
                    // already exists
                    if ($item == $related) {
                        continue 2;
                    }
                }
                // add to list of related items
                $this->related[] = $item;
            }
        }
        // loop entries
        foreach ($this->related as $id => $entry) {
            // loop excluded records
            foreach ($this->exclude as $exclude) {
                // check if this entry should be excluded
                if ($entry['module'] == $exclude['module'] && $entry['other_id'] == $exclude['other_id']) {
                    unset($this->related[$id]);
                    continue 2;
                }
            }
            // set module class
            $class = 'Frontend' . SpoonFilter::toCamelCase($entry['module']) . 'Model';
            // get module record
            $this->related[$id] = FrontendTagsModel::callFromInterface($entry['module'], $class, 'getForTags', (array) array($entry['other_id']));
            if ($this->related[$id]) {
                $this->related[$id] = array_pop($this->related[$id]);
            }
            // remove empty items
            if (empty($this->related[$id])) {
                unset($this->related[$id]);
            }
        }
        // only show 3
        $this->related = array_splice($this->related, 0, 3);
    }
示例#6
0
    /**
     * Parse the search results for this module
     *
     * Note: a module's search function should always:
     * 		- accept an array of entry id's
     * 		- return only the entries that are allowed to be displayed, with their array's index being the entry's id
     *
     * @return	array
     * @param	array $ids		The ids of the found results.
     */
    public static function search(array $ids)
    {
        // get db
        $db = FrontendModel::getDB();
        // define ids's to ignore
        $ignore = array(404);
        // get items
        $items = (array) $db->getRecords('SELECT p.id, p.title, m.url, p.revision_id AS text
											FROM pages AS p
											INNER JOIN meta AS m ON p.meta_id = m.id
											INNER JOIN pages_templates AS t ON p.template_id = t.id
											WHERE p.id IN (' . implode(', ', $ids) . ') AND p.id NOT IN (' . implode(', ', $ignore) . ') AND p.status = ? AND p.hidden = ? AND p.language = ?', array('active', 'N', FRONTEND_LANGUAGE), 'id');
        // prepare items for search
        foreach ($items as &$item) {
            $item['text'] = implode(' ', (array) $db->getColumn('SELECT pb.html
																	FROM pages_blocks AS pb
																	WHERE pb.revision_id = ? AND pb.status = ?', array($item['text'], 'active')));
            $item['full_url'] = FrontendNavigation::getURL($item['id']);
        }
        // return
        return $items;
    }
示例#7
0
    /**
     * Increase the number of views for this item
     *
     * @param int $id
     * @param bool $useful
     * @param mixed[optional] $previousFeedback
     * @return array
     */
    public static function updateFeedback($id, $useful, $previousFeedback = null)
    {
        // feedback hasn't changed so don't update the counters
        if ($previousFeedback !== null && $useful == $previousFeedback) {
            return;
        }
        $db = FrontendModel::getDB(true);
        // update counter with current feedback (increase)
        if ($useful) {
            $db->execute('UPDATE faq_questions
			 SET num_usefull_yes = num_usefull_yes + 1
			 WHERE id = ?', array((int) $id));
        } else {
            $db->execute('UPDATE faq_questions
			 SET num_usefull_no = num_usefull_no + 1
			 WHERE id = ?', array((int) $id));
        }
        // update counter with previous feedback (decrease)
        if ($previousFeedback) {
            $db->execute('UPDATE faq_questions
			 SET num_usefull_yes = num_usefull_yes - 1
			 WHERE id = ?', array((int) $id));
        } elseif ($previousFeedback !== null) {
            $db->execute('UPDATE faq_questions
			 SET num_usefull_no = num_usefull_no - 1
			 WHERE id = ?', array((int) $id));
        }
    }
示例#8
0
    /**
     * Get all related items
     *
     * @param int $id The id of the item in the source-module.
     * @param int $module The source module.
     * @param int $otherModule The module wherein the related items should appear.
     * @param int[optional] $limit The maximum of related items to grab.
     * @return array
     */
    public static function getRelatedItemsByTags($id, $module, $otherModule, $limit = 5)
    {
        return (array) FrontendModel::getDB()->getColumn('SELECT t2.other_id
			 FROM modules_tags AS t
			 INNER JOIN modules_tags AS t2 ON t.tag_id = t2.tag_id
			 WHERE t.other_id = ? AND t.module = ? AND t2.module = ? AND (t2.module != t.module OR t2.other_id != t.other_id)
			 GROUP BY t2.other_id
			 ORDER BY COUNT(t2.tag_id) DESC
			 LIMIT ?', array((int) $id, (string) $module, (string) $otherModule, (int) $limit));
    }
示例#9
0
    /**
     * Get an item.
     *
     * @param string $id The id of the item to fetch.
     * @return array
     */
    public static function get($id)
    {
        return (array) FrontendModel::getDB()->getRecord('SELECT i.*
			 FROM content_blocks AS i
			 WHERE i.id = ? AND i.status = ? AND i.hidden = ? AND i.language = ?', array((int) $id, 'active', 'N', FRONTEND_LANGUAGE));
    }
示例#10
0
 /**
  * Logout a profile.
  *
  * @return	void
  */
 public static function logout()
 {
     // delete session records
     FrontendModel::getDB(true)->delete('profiles_sessions', 'session_id = ?', array(SpoonSession::getSessionId()));
     // set is_logged_in to false
     SpoonSession::set('frontend_profile_logged_in', false);
     // delete cookie
     SpoonCookie::delete('frontend_profile_secret_key');
 }
示例#11
0
    /**
     * Load a user profile by id.
     *
     * @return	void
     * @param	int $id		Profile id to load.
     */
    private function loadProfile($id)
    {
        // get profile data
        $profileData = (array) FrontendModel::getDB()->getRecord('SELECT p.id, p.email, p.status, p.display_name, UNIX_TIMESTAMP(p.registered_on) AS registered_on
																	FROM profiles AS p
																	WHERE p.id = ?', (int) $id);
        // set properties
        $this->setId($profileData['id']);
        $this->setEmail($profileData['email']);
        $this->setStatus($profileData['status']);
        $this->setDisplayName($profileData['display_name']);
        $this->setRegisteredOn($profileData['registered_on']);
        // get the groups (only the ones we still have access to)
        $this->groups = (array) FrontendModel::getDB()->getPairs('SELECT pg.id, pg.name
																FROM profiles_groups AS pg
																INNER JOIN profiles_groups_rights AS pgr ON pg.id = pgr.group_id
																WHERE pgr.profile_id = :id AND (pgr.expires_on IS NULL OR pgr.expires_on >= NOW())', array(':id' => (int) $id));
    }
示例#12
0
 /**
  * Unsubscribes an e-mail address from CampaignMonitor and our database
  *
  * @param string $email The e-mail address to unsubscribe.
  * @param string[optional] $groupId The id of the group to unsubscribe from.
  * @return bool
  */
 public static function unsubscribe($email, $groupId = null)
 {
     // get objects
     $db = FrontendModel::getDB(true);
     $cm = self::getCM();
     // set group ID
     $groupId = !empty($groupId) ? $groupId : FrontendMailmotorModel::getDefaultGroupID();
     // get group CM ID
     $groupCMId = self::getCampaignMonitorID('list', $groupId);
     // group exists
     if (FrontendMailmotorModel::existsGroup($groupId)) {
         try {
             // unsubscribe the email from this group
             $cm->unsubscribe($email, $groupCMId);
         } catch (Exception $e) {
             // stop here if something went wrong with CM
             return false;
         }
         // set variables
         $subscriber['status'] = 'unsubscribed';
         $subscriber['unsubscribed_on'] = FrontendModel::getUTCDate('Y-m-d H:i:s');
         // unsubscribe the user
         $db->update('mailmotor_addresses_groups', $subscriber, 'email = ? AND group_id = ?', array($email, $groupId));
         // user unsubscribed
         return true;
     }
     // user not unsubscribed
     return false;
 }
示例#13
0
 /**
  * Insert data fields.
  *
  * @param array $data The data to insert.
  * @return int
  */
 public static function insertDataField(array $data)
 {
     return FrontendModel::getDB(true)->insert('forms_data_fields', $data);
 }
示例#14
0
    /**
     * Load the data for the given user
     *
     * @return	void
     * @param	int $userId		The users id in the backend.
     */
    public function loadUser($userId)
    {
        // redefine
        $userId = (int) $userId;
        // get database instance
        $db = FrontendModel::getDB();
        // get user-data
        $userData = (array) $db->getRecord('SELECT u.id, u.email
											FROM users AS u
											WHERE u.id = ?
											LIMIT 1', array($userId));
        // if there is no data we have to destroy this object, I know this isn't a realistic situation
        if (empty($userData)) {
            throw new FrontendException('The user (' . $userId . ') doesn\'t exist.');
        }
        // set properties
        $this->setUserId($userData['id']);
        $this->setEmail($userData['email']);
        // get settings
        $settings = (array) $db->getPairs('SELECT us.name, us.value
											FROM users_settings AS us
											WHERE us.user_id = ?', array($userId));
        // loop settings and store them in the object
        foreach ($settings as $key => $value) {
            $this->settings[$key] = unserialize($value);
        }
    }
示例#15
0
    /**
     * Get all items
     *
     * @return	array
     */
    public static function getAll()
    {
        return (array) FrontendModel::getDB()->getRecords('SELECT *
															FROM location
															WHERE language = ?', array(FRONTEND_LANGUAGE));
    }
示例#16
0
    /**
     * Push a notification to Microsoft's notifications-server
     *
     * @param string $title The title for the tile to send.
     * @param string[optional] $count The count for the tile to send.
     * @param string[optional] $image The image for the tile to send.
     * @param string[optional] $backTitle The title for the tile backtround to send.
     * @param string[optional] $backText The text for the tile background to send.
     * @param string[optional] $backImage The image for the tile background to send.
     * @param string[optional] $tile The secondary tile to update.
     * @param string[optional] $uri The application uri to navigate to.
     */
    public static function pushToMicrosoftApp($title, $count = null, $image = null, $backTitle = null, $backText = null, $backImage = null, $tile = null, $uri = null)
    {
        // get ForkAPI-keys
        $publicKey = FrontendModel::getModuleSetting('core', 'fork_api_public_key', '');
        $privateKey = FrontendModel::getModuleSetting('core', 'fork_api_private_key', '');
        // no keys, so stop here
        if ($publicKey == '' || $privateKey == '') {
            return;
        }
        // get all microsoft channel uri's
        $channelUris = (array) FrontendModel::getDB()->getColumn('SELECT s.value
			 FROM users AS i
			 INNER JOIN users_settings AS s
			 WHERE i.active = ? AND i.deleted = ? AND s.name = ? AND s.value != ?', array('Y', 'N', 'microsoft_channel_uri', 'N;'));
        // no devices, so stop here
        if (empty($channelUris)) {
            return;
        }
        // init var
        $uris = array();
        // loop devices
        foreach ($channelUris as $row) {
            // unserialize
            $row = unserialize($row);
            // loop and add
            foreach ($row as $item) {
                $uris[] = $item;
            }
        }
        // no channel uri's, so stop here
        if (empty($uris)) {
            return;
        }
        // require the class
        require_once PATH_LIBRARY . '/external/fork_api.php';
        // create instance
        $forkAPI = new ForkAPI($publicKey, $privateKey);
        try {
            // push
            $forkAPI->microsoftPush($uris, $title, $count, $image, $backTitle, $backText, $backImage, $tile, $uri);
        } catch (Exception $e) {
            if (SPOON_DEBUG) {
                throw $e;
            }
        }
    }
示例#17
0
 /**
  * Update a profile.
  *
  * @param int $id The profile id.
  * @param array $values The values to update.
  * @return int
  */
 public static function update($id, array $values)
 {
     return (int) FrontendModel::getDB(true)->update('profiles', $values, 'id = ?', (int) $id);
 }
示例#18
0
    /**
     * Send an email
     *
     * @return	void
     * @param	int $id		The id of the mail to send.
     */
    public static function send($id)
    {
        // redefine
        $id = (int) $id;
        // get db
        $db = FrontendModel::getDB(true);
        // get record
        $emailRecord = (array) $db->getRecord('SELECT *
												FROM emails AS e
												WHERE e.id = ?', array($id));
        // mailer type
        $mailerType = FrontendModel::getModuleSetting('core', 'mailer_type', 'mail');
        // create new SpoonEmail-instance
        $email = new SpoonEmail();
        $email->setTemplateCompileDirectory(FRONTEND_CACHE_PATH . '/compiled_templates');
        // send via SMTP
        if ($mailerType == 'smtp') {
            // get settings
            $SMTPServer = FrontendModel::getModuleSetting('core', 'smtp_server');
            $SMTPPort = FrontendModel::getModuleSetting('core', 'smtp_port', 25);
            $SMTPUsername = FrontendModel::getModuleSetting('core', 'smtp_username');
            $SMTPPassword = FrontendModel::getModuleSetting('core', 'smtp_password');
            // set server and connect with SMTP
            $email->setSMTPConnection($SMTPServer, $SMTPPort, 10);
            // set authentication if needed
            if ($SMTPUsername !== null && $SMTPPassword !== null) {
                $email->setSMTPAuth($SMTPUsername, $SMTPPassword);
            }
        }
        // set some properties
        $email->setFrom($emailRecord['from_email'], $emailRecord['from_name']);
        $email->addRecipient($emailRecord['to_email'], $emailRecord['to_name']);
        $email->setReplyTo($emailRecord['reply_to_email']);
        $email->setSubject($emailRecord['subject']);
        $email->setHTMLContent($emailRecord['html']);
        $email->setCharset(SPOON_CHARSET);
        $email->setContentTransferEncoding('base64');
        if ($emailRecord['plain_text'] != '') {
            $email->setPlainContent($emailRecord['plain_text']);
        }
        // attachments added
        if (isset($emailRecord['attachments']) && $emailRecord['attachments'] !== null) {
            // unserialize
            $attachments = (array) unserialize($emailRecord['attachments']);
            // add attachments to email
            foreach ($attachments as $attachment) {
                $email->addAttachment($attachment);
            }
        }
        // send the email
        if ($email->send()) {
            // remove the email
            $db->delete('emails', 'id = ?', array($id));
            // trigger event
            FrontendModel::triggerEvent('core', 'after_email_sent', array('id' => $id));
        }
    }
示例#19
0
    /**
     * Fetch all the settings for a specific map
     *
     * @param int $mapId
     * @return array
     */
    public static function getMapSettings($mapId)
    {
        $mapSettings = (array) FrontendModel::getDB()->getPairs('SELECT s.name, s.value
			 FROM location_settings AS s
			 WHERE s.map_id = ?', array((int) $mapId));
        foreach ($mapSettings as $key => $value) {
            $mapSettings[$key] = unserialize($value);
        }
        return $mapSettings;
    }
示例#20
0
    /**
     * Validate searches: check everything that has been marked as 'inactive', if should still be inactive
     */
    public static function validateSearch()
    {
        // we'll iterate through the inactive search indices in little batches
        $offset = 0;
        $limit = 50;
        while (1) {
            // get the inactive indices
            $searchResults = (array) FrontendModel::getDB()->getRecords('SELECT module, other_id
				 FROM search_index
				 WHERE language = ? AND active = ?
				 GROUP BY module, other_id
				 LIMIT ?, ?', array(FRONTEND_LANGUAGE, 'N', $offset, $limit));
            // none found? good news!
            if (!$searchResults) {
                return;
            }
            // prepare to send to modules
            $moduleResults = array();
            // loop the resultset
            foreach ($searchResults as $searchResult) {
                $moduleResults[$searchResult['module']][] = $searchResult['other_id'];
            }
            // pass the results to the modules
            foreach ($moduleResults as $module => $otherIds) {
                // check if this module actually is prepared to handle searches (well it should, because else there shouldn't be any search indices)
                if (is_callable(array('Frontend' . SpoonFilter::ucfirst($module) . 'Model', 'search'))) {
                    $moduleResults[$module] = call_user_func(array('Frontend' . SpoonFilter::ucfirst($module) . 'Model', 'search'), $otherIds);
                    // update the ones that are allowed to be searched through
                    self::statusIndex($module, array_keys($moduleResults[$module]), true);
                }
            }
            // didn't even get the amount of result we asked for? no need to ask again!
            if (count($searchResults) < $offset) {
                return;
            }
            $offset += $limit;
        }
    }
示例#21
0
 /**
  * Unsubscribes an e-mail address
  *
  * @param string $email The mail address to unsubscribe.
  * @param string[optional] $groupId The id of the group to unsubscribe from.
  * @return bool
  */
 public static function unsubscribe($email, $groupId = null)
 {
     // get objects
     $db = FrontendModel::getDB(true);
     // set groupID
     $groupId = !empty($groupId) ? $groupId : self::getDefaultGroupID();
     // unsubscribe the user in CM
     if (self::existsGroup($groupId)) {
         // set variables
         $subscriber['status'] = 'unsubscribed';
         $subscriber['unsubscribed_on'] = FrontendModel::getUTCDate('Y-m-d H:i:s');
         // unsubscribe the user
         $db->update('mailmotor_addresses_groups', $subscriber, 'email = ? AND group_id = ?', array($email, $groupId));
         // user unsubscribed
         return true;
     }
     // user not unsubscribed
     return false;
 }
示例#22
0
    /**
     * Parse the search results for this module
     *
     * Note: a module's search function should always:
     * 		- accept an array of entry id's
     * 		- return only the entries that are allowed to be displayed, with their array's index being the entry's id
     *
     *
     * @return	array
     * @param	array $ids		The ids of the found results.
     */
    public static function search(array $ids)
    {
        // get items
        $items = (array) FrontendModel::getDB()->getRecords('SELECT i.id, i.title, i.introduction, i.text, m.url
																FROM blog_posts AS i
																INNER JOIN meta AS m ON i.meta_id = m.id
																WHERE i.status = ? AND i.hidden = ? AND i.language = ? AND i.publish_on <= ? AND i.id IN (' . implode(',', $ids) . ')', array('active', 'N', FRONTEND_LANGUAGE, date('Y-m-d H:i') . ':00'), 'id');
        // prepare items for search
        foreach ($items as &$item) {
            $item['full_url'] = FrontendNavigation::getURLForBlock('blog', 'detail') . '/' . $item['url'];
        }
        // return
        return $items;
    }
示例#23
0
    /**
     * Push a notification to Apple's notifications-server
     *
     * @return	void
     * @param	mixed $alert						The message/dictonary to send.
     * @param	int[optional] $badge				The number for the badge.
     * @param	string[optional] $sound				The sound that should be played.
     * @param 	array[optional] $extraDictionaries	Extra dictionaries.
     */
    public static function pushToAppleApp($alert, $badge = null, $sound = null, array $extraDictionaries = null)
    {
        // get ForkAPI-keys
        $publicKey = FrontendModel::getModuleSetting('core', 'fork_api_public_key', '');
        $privateKey = FrontendModel::getModuleSetting('core', 'fork_api_private_key', '');
        // validate keys
        if ($publicKey != '' && $privateKey != '') {
            // get all apple-device tokens
            $deviceTokens = (array) FrontendModel::getDB()->getColumn('SELECT s.value
																		FROM users AS i
																		INNER JOIN users_settings AS s
																		WHERE i.active = ? AND i.deleted = ? AND s.name = ? AND s.value != ?', array('Y', 'N', 'apple_device_token', 'N;'));
            // any devices?
            if (!empty($deviceTokens)) {
                // init var
                $tokens = array();
                // loop devices
                foreach ($deviceTokens as $row) {
                    // unserialize
                    $row = unserialize($row);
                    // loop and add
                    foreach ($row as $item) {
                        $tokens[] = $item;
                    }
                }
                // any tokens?
                if (!empty($tokens)) {
                    // require the class
                    require_once PATH_LIBRARY . '/external/fork_api.php';
                    // create instance
                    $forkAPI = new ForkAPI($publicKey, $privateKey);
                    try {
                        // push
                        $response = $forkAPI->applePush($tokens, $alert, $badge, $sound, $extraDictionaries);
                        if (!empty($response)) {
                            // get db
                            $db = FrontendModel::getDB(true);
                            // loop the failed keys and remove them
                            foreach ($response as $deviceToken) {
                                // get setting wherin the token is available
                                $row = $db->getRecord('SELECT i.*
														FROM users_settings AS i
														WHERE i.name = ? AND i.value LIKE ?', array('apple_device_token', '%' . $deviceToken . '%'));
                                // any rows?
                                if (!empty($row)) {
                                    // reset data
                                    $data = unserialize($row['value']);
                                    // loop keys
                                    foreach ($data as $key => $token) {
                                        // match and unset if needed.
                                        if ($token == $deviceToken) {
                                            unset($data[$key]);
                                        }
                                    }
                                    // no more tokens left?
                                    if (empty($data)) {
                                        $db->delete('users_settings', 'user_id = ? AND name = ?', array($row['user_id'], $row['name']));
                                    } else {
                                        $db->update('users_settings', array('value' => serialize($data)), 'user_id = ? AND name = ?', array($row['user_id'], $row['name']));
                                    }
                                }
                            }
                        }
                    } catch (Exception $e) {
                        if (SPOON_DEBUG) {
                            throw $e;
                        }
                    }
                }
            }
        }
    }