v7‰PNG  IHDR Ÿ f Õ†C1 sRGB ®Îé gAMA ± üa pHYs à ÃÇo¨d GIDATx^íÜL”÷ð÷Yçªö("Bh_ò«®¸¢§q5kÖ*:þ0A­ºšÖ¥]VkJ¢M»¶f¸±8\k2íll£1]q®ÙÔ‚ÆT PK"\(QllEmailTemplate.phpnu[email_template_layout_id) return null; $layout = $this->getDi()->emailTemplateLayoutTable->load($this->email_template_layout_id, false); if (!$layout) return null; return $layout->layout; } public function getLinkTitle() { return null; } /** * Check if user has no matches for $this->not_conditions * not conditions field format: * comma delimited: c1 - category#1, p22 - product#22, c-1 - ANY PRODUCT * @param User $user * @return boolean true if ok (no matching conditions found) */ public function checkNotConditions(User $user) { $pids = array(); $catp = null; foreach (array_filter(explode(',', $this->not_conditions)) as $s) { if ($s == 'c-1') { if ($this->not_conditions_future && $user->getFutureProductIds()) return false; if (!$this->not_conditions_expired) return $user->status != User::STATUS_ACTIVE; else return $user->status == User::STATUS_PENDING; } elseif ($s[0] == 'p') { $pids[] = substr($s, 1); } elseif ($s[0] == 'c') { if (!$catp) $catp = $this->getDi()->productCategoryTable->getCategoryProducts(true); if (!empty($catp[substr($s, 1)])) $pids = array_merge($pids, $catp[substr($s, 1)]); } } if (!$pids) return true; $userPids = $user->getActiveProductIds(); if ($this->not_conditions_expired) $userPids = array_merge($userPids, $user->getExpiredProductIds()); if ($this->not_conditions_future) $userPids = array_merge($userPids, $user->getFutureProductIds()); return !$pids || !array_intersect($pids, $userPids); } } /** * @package Am_Mail_Template */ class EmailTemplateTable extends ResourceAbstractTable { protected $_key = 'email_template_id'; protected $_table = '?_email_template'; protected $_recordClass = 'EmailTemplate'; public $_checkUnique = array( 'name', 'lang', 'day' ); // registry to do not send pending emails twice protected $_pendingSent = array(); public function getAccessType() { return ResourceAccess::EMAILTEMPLATE; } public function getAccessTitle() { return ___('E-Mail Messages'); } public function getPageId() { return 'emails'; } public function getTitleField() { return 'name'; } /** * * @param type $name * @param type $lang * @return EmailTemplate */ public function findFirstExact($name, $lang = null) { $row = $this->getDi()->db->selectRow("SELECT * FROM ?_email_template WHERE name=? {ORDER BY lang=? DESC, lang='en' DESC}", $name, is_null($lang) ? DBSIMPLE_SKIP : $lang); return $row ? $this->createRecord($row) : null; } // --------- ------------- -------------- -------------- ---------------/ function deleteByFields($name,$day=null){ $this->_db->query("DELETE FROM ?_email_template WHERE name=? {AND day=?}", $name, $day!="" ? $day : DBSIMPLE_SKIP ); } /** * Find exact template by criteria * @return EmailTemplate */ function getExact($name, $lang=null, $day=null) { $row = $this->getDi()->db->selectRow("SELECT * FROM ?_email_template WHERE name=? {AND lang=?} {AND day=?}", $name, is_null($lang) ? DBSIMPLE_SKIP : $lang, is_null($day) ? DBSIMPLE_SKIP : $day); return $row ? $this->createRecord($row) : null; } /** * Search available days options by criteria * @return array of int (available days) */ function getDays($name, $exclude=null){ return $this->getDi()->db->selectCol("SELECT DISTINCT day FROM ?_email_template WHERE name=? AND day IS NOT NULL {AND day<>?} ORDER BY day", $name, is_null($exclude) ? 0 : $exclude ); } public function getLanguages($name, $day=null, $exclude=null){ return $this->_db->selectCol("SELECT DISTINCT lang as ARRAY_KEY, lang FROM ?_email_template WHERE name=? {AND day=?} {AND lang<>?}", $name, is_null($day) ? DBSIMPLE_SKIP : $day, is_null($exclude) ? DBSIMPLE_SKIP : $exclude ); } public function sendProductWelcomeEmails(User $user, $invoiceOrAccess = null) { $product_ids = array(); if($invoiceOrAccess instanceof Access) { $product_ids[] = $invoiceOrAccess->product_id; } elseif($invoiceOrAccess instanceof Invoice) { $payment = current($invoiceOrAccess->getPaymentRecords()); $invoice = $invoiceOrAccess; $product_ids = $this->getDi()->db->selectCol('SELECT product_id from ?_access where invoice_id = ?',$invoiceOrAccess->invoice_id); } if (!count($product_ids)) return; $product_id = $product_ids[0]; $product = $this->getDi()->productTable->load($product_id); foreach ($this->getDi()->resourceAccessTable->getProductWelcomeEmails($product_ids) as $et) { // check if no matching not_conditions if (!$et->checkNotConditions($user)) continue; $recipients = array(); if($et->recipient_user) { $recipients[] = $user; } if ($et->recipient_aff && $user->aff_id && ($aff = $this->getDi()->userTable->load($user->aff_id, false))) { $recipients[] = $aff; } if($et->recipient_admin) { $recipients[] = Am_Mail_Template::TO_ADMIN; } if($et->recipient_emails) { foreach (array_map('trim', explode(',', $et->recipient_emails)) as $email) if($email) $recipients[] = $email; } $tpl = Am_Mail_Template::createFromEmailTemplate($et); $tpl->setLast_product_title($product->getTitle()); $tpl->setUser($user); if (isset($payment)) { $tpl->setPayment($payment); } if (isset($invoice)) { $tpl->setInvoice($invoice); } foreach ($recipients as $recipient) { $tpl->send($recipient); } } } /** * @link Invoice->start */ public function sendZeroAutoresponders(User $user, $invoiceOrAccess = null) { $this->sendProductWelcomeEmails($user, $invoiceOrAccess); foreach ($this->getDi()->resourceAccessTable-> getAllowedResources($user, ResourceAccess::EMAILTEMPLATE) as $et) { if (($et->name != EmailTemplate::AUTORESPONDER) || ($et->day != 1)) continue; // check if no matching not_conditions if (!$et->checkNotConditions($user)) continue; // don't send same e-mail twice $sent = (array)$user->data()->get('zero-autoresponder-sent'); if (in_array($et->pk(), $sent)) continue; $sent[] = $et->pk(); /// $recipients = array(); if($et->recipient_user) { $recipients[] = $user; } if ($et->recipient_aff && $user->aff_id && ($aff = $this->getDi()->userTable->load($user->aff_id, false))) { $recipients[] = $aff; } if($et->recipient_admin) { $recipients[] = Am_Mail_Template::TO_ADMIN; } if($et->recipient_emails) { foreach (array_map('trim', explode(',', $et->recipient_emails)) as $email) if($email) $recipients[] = $email; } $tpl = Am_Mail_Template::createFromEmailTemplate($et); if (!is_null($invoiceOrAccess)) { if($invoiceOrAccess instanceof Invoice) $tpl->setInvoice($invoiceOrAccess); $tpl->setLast_product_title($this->getLastProductTitle($et->fn_id, $invoiceOrAccess)); } $tpl->setUser($user); foreach ($recipients as $recipient) { $tpl->send($recipient); } // store sent emails $user->data()->set('zero-autoresponder-sent', $sent)->update(); } } public function sendZeroPayments(User $user, InvoicePayment $payment) { $this->getDi()->plugins_payment->loadEnabled()->getAllEnabled(); $invoice = $payment->getInvoice(); $product_ids = array_filter(array_map(function($el) { return $el->item_type == 'product' ? $el->item_id : null; }, $invoice->getItems())); if (!$product_ids) return; foreach ($this->getDi()->resourceAccessTable->getPaymentEmails($product_ids) as $et) { if (($et->name != EmailTemplate::PAYMENT) || ($et->day != 0)) continue; // check if no matching not_conditions if (!$et->checkNotConditions($user)) continue; $recipients = array(); if($et->recipient_user) { $recipients[] = $user; } if ($et->recipient_aff && $user->aff_id && ($aff = $this->getDi()->userTable->load($user->aff_id, false))) { $recipients[] = $aff; } if($et->recipient_admin) { $recipients[] = Am_Mail_Template::TO_ADMIN; } if($et->recipient_emails) { foreach (array_map('trim', explode(',', $et->recipient_emails)) as $email) if($email) $recipients[] = $email; } $tpl = Am_Mail_Template::createFromEmailTemplate($et); $tpl->setPayment($payment); $tpl->setInvoice($invoice = $payment->getInvoice()); $tpl->setInvoice_text($invoice->render('', $payment)); $tpl->setInvoice_html($invoice->renderHtml($payment)); $tpl->setUser($user); $tpl->setInvoice_items($invoice->getItems()); $tpl->setProduct($invoice->getItem(0)->tryLoadProduct()); if ($et->attach_pdf_invoice) { try{ $pdf = Am_Pdf_Invoice::create($payment); $pdf->setDi($this->getDi()); $tpl->getMail()->createAttachment( $pdf->render(), 'application/pdf', Zend_Mime::DISPOSITION_ATTACHMENT, Zend_Mime::ENCODING_BASE64, $pdf->getFileName() ); } catch(Exception $e) { $this->getDi()->errorLogTable->logException($e); } } foreach ($recipients as $recipient) { $tpl->send($recipient); } } } protected function getLastProductTitle($fn_id, $invoiceOrAccessOrUser) { if ($fn_id['fn'] == 'product_id') return $this->getDi()->productTable->load($fn_id['id'])->title; if ($invoiceOrAccessOrUser instanceof Invoice) return $this->getDi()->db->selectCell(" SELECT p.title FROM ?_invoice_item ii LEFT JOIN ?_product_product_category ppc ON (ppc.product_id = ii.item_id) LEFT JOIN ?_product p ON (p.product_id = ii.item_id) WHERE ii.invoice_id = ?d {AND ppc.product_category_id = ?d} ", $invoiceOrAccessOrUser->pk(), $fn_id['id'] != -1 ? $fn_id['id'] : DBSIMPLE_SKIP); if ($invoiceOrAccessOrUser instanceof Access) return $this->getDi()->db->selectCell(" SELECT p.title FROM ?_access a LEFT JOIN ?_product p ON (p.product_id = a.product_id) WHERE a.access_id = ?d ", $invoiceOrAccessOrUser->pk()); // only cron autoresponder if ($invoiceOrAccessOrUser instanceof User) { return $this->getDi()->db->selectCell(" SELECT title FROM ?_product WHERE product_id = ( SELECT a.product_id FROM ?_access a WHERE begin_date <= ? AND a.user_id = ?d AND a.product_id IN ( SELECT us.product_id FROM ?_user_status us {LEFT JOIN ?_product_product_category ppc ON (ppc.product_id = us.product_id)} WHERE us.user_id=?d AND us.status=?d {AND ppc.product_category_id = ?d} ) ORDER BY begin_date DESC LIMIT 1 ) ", $this->getDi()->sqlDate ,$invoiceOrAccessOrUser->pk(), $invoiceOrAccessOrUser->pk(), User::STATUS_ACTIVE, $fn_id['id'] != -1 ? $fn_id['id'] : DBSIMPLE_SKIP, $fn_id['id'] != -1 ? $fn_id['id'] : DBSIMPLE_SKIP); } return ''; } static function onInvoiceStarted(Am_Event_InvoiceStarted $event) { $invoice = $event->getInvoice(); $event->getDi()->emailTemplateTable->sendZeroAutoresponders($invoice->getUser(), $invoice); if ($event->getDi()->config->get('send_free_payment_admin') && floatval($invoice->first_total) == 0) { if ($et = Am_Mail_Template::load('send_free_payment_admin')) { $et->setUser($event->getUser()) ->setInvoice($invoice) ->setPayment($event->getPayment()) ->setInvoice_text($invoice->render()) ->setInvoice_html($invoice->renderHtml()); $et->send(Am_Mail_Template::TO_ADMIN); } } } static function onPaymentWithAccessAfterInsert(Am_Event_PaymentWithAccessAfterInsert $event) { /** * This e-mail is sent for first payment in invoice only * another template must be used for the following payments */ // if ($event->getInvoice()->getPaymentsCount() != 1) return; $event->getDi()->plugins_payment->loadEnabled()->getAllEnabled(); $products = array(); foreach ($event->getInvoice()->getProducts() as $product) $products[] = $product->getTitle(); if ($event->getDi()->config->get('send_payment_mail')) { $et = Am_Mail_Template::load('send_payment_mail', $event->getUser()->lang); if ($et && (($event->getPayment()->amount > 0))) { $et->setUser($event->getUser()) ->setInvoice($event->getInvoice()) ->setPayment($event->getPayment()) ->setInvoice_items($event->getInvoice()->getItems()) ->setInvoice_text($event->getInvoice()->render('', $event->getPayment())) ->setInvoice_html($event->getInvoice()->renderHtml($event->getPayment())) ->setProduct_title(implode (", ", $products)); if ($event->getDi()->config->get('send_pdf_invoice') && $event->getDi()->config->get('pdf_invoice_sent_user')) { try{ $event->getDi()->locale->changeLanguageTo($event->getUser()->lang); $invoice = Am_Pdf_Invoice::create($event->getPayment()); $invoice->setDi(Am_Di::getInstance()); $et->getMail()->createAttachment( $invoice->render(), 'application/pdf', Zend_Mime::DISPOSITION_ATTACHMENT, Zend_Mime::ENCODING_BASE64, $invoice->getFileName() ); $event->getDi()->locale->restoreLanguage(); } catch(Exception $e) { Am_Di::getInstance()->errorLogTable->logException($e); } } $et->send($event->getUser()); } } if ($event->getDi()->config->get('send_payment_admin')) { $et = Am_Mail_Template::load('send_payment_admin', $event->getUser()->lang); if ($et && (($event->getPayment()->amount > 0))) { $et->setUser($event->getUser()) ->setInvoice($event->getInvoice()) ->setPayment($event->getPayment()) ->setInvoice_items($event->getInvoice()->getItems()) ->setInvoice_text($event->getInvoice()->render('', $event->getPayment())) ->setInvoice_html($event->getInvoice()->renderHtml($event->getPayment())) ->setProduct_title(implode (", ", $products)); if ($event->getDi()->config->get('send_pdf_invoice', false) && $event->getDi()->config->get('pdf_invoice_sent_admin')) { try{ $invoice = Am_Pdf_Invoice::create($event->getPayment()); $invoice->setDi(Am_Di::getInstance()); $et->getMail()->createAttachment( $invoice->render(), 'application/pdf', Zend_Mime::DISPOSITION_ATTACHMENT, Zend_Mime::ENCODING_BASE64, $invoice->getFileName() ); } catch(Exception $e) { Am_Di::getInstance()->errorLogTable->logException($e); } } $et->send(Am_Mail_Template::TO_ADMIN); } } $event->getDi()->emailTemplateTable->sendZeroPayments($event->getUser(), $event->getPayment()); } protected function isNotificationRuleMatch(EmailTemplate $tmpl, Invoice $invoice) { if (!$tmpl->conditions) return true; $conds = array(); foreach(explode(',', $tmpl->conditions) as $item) { preg_match('/([A-Z]*)-(.*)/', $item, $match); $conds[$match[1]][] = $match[2]; } //check product conditions $product_ids = array(); if (isset($conds['CATEGORY'])) { $catProducts = $this->getDi()->productCategoryTable->getCategoryProducts(); foreach ($conds['CATEGORY'] as $cat_id) { if (isset($catProducts[$cat_id])) $product_ids = array_merge($product_ids, $catProducts[$cat_id]); } } if (isset($conds['PRODUCT'])) { $product_ids = array_merge($product_ids, $conds['PRODUCT']); } if (!count($product_ids) && isset($conds['CATEGORY'])) { //user set up categories without products return false; } if (count($product_ids)) { $invoice_product_id = array_map(function($a) {return $a->pk();}, $invoice->getProducts()); if (!array_intersect($product_ids, $invoice_product_id)) return false; } //check paysystem conditions if (isset($conds['PAYSYSTEM']) && !in_array($invoice->paysys_id, $conds['PAYSYSTEM'])) return false; return true; } /** * * @param string $name * @return array array day => array(templates for day) */ protected function findPendingNotificationRules($name) { $templates = $this->findByName($name); $days = array(); foreach ($templates as $tpl) { $dd = array_filter(explode(',', $tpl->days), function($a) {return $a!=="";}); foreach ($dd as $d) { $days[$d][] = $tpl; } } return $days; } protected function sendPendingNotifications($day, $invoices, $tpls, $sendCallback) { foreach ($invoices as $invoice) { $user = $invoice->getUser(); if (!$user) continue; foreach ($tpls as $tpl) { if ($this->isNotificationRuleMatch($tpl, $invoice)) { if (!empty($this->_pendingSent[$sendCallback[1]][$invoice->getUser()->pk()])) continue; $mailTpl = Am_Mail_Template::createFromEmailTemplate($tpl); $mailTpl->setUser($invoice->getUser()); $mailTpl->setInvoice($invoice); $mailTpl->setInvoice_text($invoice->render()); $mailTpl->setInvoice_html($invoice->renderHtml()); if ($invoice->due_date < sqlDate('+ 7 days')) { $invoice->updateQuick('due_date', sqlDate('+ 7 days')); } $mailTpl->setPaylink($this->getDi()->url('pay/' . $invoice->getSecureId('payment-link'),false,null)); $mailTpl->setDay($day); $products = array(); foreach ($invoice->getProducts() as $product) $products[] = $product->getTitle(); $mailTpl->setProduct_title(implode (", ", $products)); call_user_func($sendCallback, $mailTpl, $invoice); $this->_pendingSent[$sendCallback[1]][$invoice->getUser()->pk()] = true; break; } } } } protected function sendPendingNotificationToUser(Am_Mail_Template $mailTpl, Invoice $invoice) { $user = $invoice->getUser(); if ($user->unsubscribed) return; $mailTpl->send($user); } protected function sendPendingNotificationToAdmin(Am_Mail_Template $mailTpl, Invoice $invoice) { $mailTpl->sendAdmin(); } protected function _sendCronPendingNotifications($name, $sendCallback) { $days = $this->findPendingNotificationRules($name); unset($days[0]); //it should been send on invoice created foreach ($days as $d => $tpls) { if (substr($d, -1) == 'h') continue; //it should been send on hourly cron $date = $this->getDi()->dateTime; $date->modify('-' . $d . ' days'); $end_date = $date->format('Y-m-d H:i:59'); $date->modify('- 23 hours'); $date->modify('- 59 minutes'); $begin_date = $date->format('Y-m-d H:i:00'); $this->_sendCronPendingNotificationsByPeriod($d, $tpls, $sendCallback, array( $begin_date, $end_date )); } } protected function _sendCronHourlyPendingNotifications($name, $sendCallback) { $days = $this->findPendingNotificationRules($name); unset($days[0]); //it should been send on invoice created foreach ($days as $d => $tpls) { if (substr($d, -1) != 'h') continue; //it should been send on daily cron $d = str_replace('h', '', $d); $date = $this->getDi()->dateTime; $date->modify('-' . $d . ' hours'); $end_date = $date->format('Y-m-d H:i:59'); $date->modify('- 59 minutes'); $begin_date = $date->format('Y-m-d H:i:00');; $this->_sendCronPendingNotificationsByPeriod(0, $tpls, $sendCallback, array( $begin_date, $end_date )); } } protected function _sendCronPendingNotificationsByPeriod($d, $tpls, $sendCallback, $period) { list($begin_date, $end_date) = $period; $query = new Am_Query($this->getDi()->invoiceTable); $query = $query->addWhere('status=?', Invoice::PENDING) ->addWhere('tm_added>?', $begin_date) ->addWhere('tm_addedgetAlias(); $query->addWhere("NOT EXISTS (SELECT * FROM ?_invoice_payment ip WHERE ip.user_id = $t.user_id AND ip.dattm>=? AND ip.dattmgetFoundRows()) { $invoices = $query->selectPageRecords(0, $count); $this->sendPendingNotifications($d, $invoices, $tpls, $sendCallback); } } protected function _sendZeroPendingNotifications($name, $sendCallback, Invoice $invoice) { $days = $this->findPendingNotificationRules($name); if (isset($days[0])) { $tpls = $days[0]; $this->sendPendingNotifications(0, array($invoice), $tpls, $sendCallback); } } public function sendCronPendingNotifications() { $this->_sendCronPendingNotifications('pending_to_user', array($this, 'sendPendingNotificationToUser')); $this->_sendCronPendingNotifications('pending_to_admin', array($this, 'sendPendingNotificationToAdmin')); } public function sendCronHourlyPendingNotifications() { $this->_sendCronHourlyPendingNotifications('pending_to_user', array($this, 'sendPendingNotificationToUser')); $this->_sendCronHourlyPendingNotifications('pending_to_admin', array($this, 'sendPendingNotificationToAdmin')); } public function onInvoiceAfterInsert(Am_Event $event) { $invoice = $event->getInvoice(); if ($invoice->data()->get('added-by-admin')) return; // do not send automatic e-mails if invoice added by admin $this->_sendZeroPendingNotifications('pending_to_user', array($this, 'sendPendingNotificationToUser'), $invoice); $this->_sendZeroPendingNotifications('pending_to_admin', array($this, 'sendPendingNotificationToAdmin'), $invoice); } public function sendCronPayments() { $this->getDi()->plugins_payment->loadEnabled()->getAllEnabled(); $mails = $this->findBy(array('name' => EmailTemplate::PAYMENT)); if (!$mails) return; // nothing to send $byDatePayment = $byDateInvoice = array(); // templates by expiration date foreach ($mails as $et) { $et->_productIds = $et->findMatchingProductIds(); if ($et->day > 0) { $date = date('Y-m-d', strtotime("+{$et->day} days", $this->getDi()->time)); $byDateInvoice[$date][] = $et; } elseif($et->day < 0) { $date = date('Y-m-d', strtotime("{$et->day} days", $this->getDi()->time)); $byDatePayment[$date][] = $et; } } if(count($byDatePayment)) { $q = $this->getDi()->db->queryResultOnly("SELECT ip.*, GROUP_CONCAT(ii.item_id) as _product_id FROM ?_invoice_payment ip LEFT JOIN ?_invoice_item ii ON (ip.invoice_id = ii.invoice_id AND ii.item_type = 'product') WHERE DATE(dattm) IN (?a) GROUP BY ip.invoice_payment_id", array_keys($byDatePayment)); while ($row = $this->_db->fetchRow($q)) { $payment = $this->getDi()->invoicePaymentTable->createRecord($row); $invoice = $payment->getInvoice(); $user = $payment->getUser(); $this->_sendCronPayments($byDatePayment[sqlDate($payment->dattm)], explode(',', $row['_product_id']), $user, $payment, $invoice); } } if(count($byDateInvoice)) { $q = $this->getDi()->db->queryResultOnly("SELECT i.*, GROUP_CONCAT(ii.item_id) as _product_id FROM ?_invoice i LEFT JOIN ?_invoice_item ii ON (i.invoice_id = ii.invoice_id AND ii.item_type = 'product') WHERE i.rebill_date IN (?a) GROUP BY i.invoice_id", array_keys($byDateInvoice)); $maxPaymentId = $this->_db->selectCell("SELECT IFNULL(MAX(invoice_payment_id), 0)+1 FROM ?_invoice_payment"); while ($row = $this->_db->fetchRow($q)) { $invoice = $this->getDi()->invoiceTable->createRecord($row); $payment = $this->getDi()->invoicePaymentRecord; $payment->toggleFrozen(true); $payment->receipt_id = 'NOT PAID'; $payment->display_invoice_id = 'NOT PAID'; $payment->dattm = $invoice->rebill_date; $payment->amount = $invoice->second_total; $payment->invoice_payment_id = $maxPaymentId; $payment->invoice_id = $invoice->pk(); $payment->_setInvoice($invoice); $user = $invoice->getUser(); $this->_sendCronPayments($byDateInvoice[$invoice->rebill_date], explode(',', $row['_product_id']), $user, $payment, $invoice); } } } protected function _sendCronPayments($tmpls, $pids, $user, $payment, $invoice) { if ($user->unsubscribed||!$user->is_approved) return; foreach ($tmpls as $et) { if ($et->_productIds == ResourceAccess::ANY_PRODUCT || (array_intersect($pids, $et->_productIds))) { // check if no matching not_conditions if (!$et->checkNotConditions($user)) continue; $recipients = array(); if($et->recipient_user) { $recipients[] = $user; } if ($et->recipient_aff && $user->aff_id && ($aff = $this->getDi()->userTable->load($user->aff_id, false))) { $recipients[] = $aff; } if($et->recipient_admin) { $recipients[] = Am_Mail_Template::TO_ADMIN; } if($et->recipient_emails) { foreach (array_map('trim', explode(',', $et->recipient_emails)) as $email) if($email) $recipients[] = $email; } $tpl = Am_Mail_Template::createFromEmailTemplate($et); $tpl->setUser($user); $tpl->setPayment($payment); $tpl->setInvoice($invoice); $tpl->setInvoice_text($invoice->render('', $payment)); $tpl->setInvoice_html($invoice->renderHtml($payment)); $tpl->setInvoice_items($invoice->getItems()); $tpl->setProduct($invoice->getItem(0)->tryLoadProduct()); if ($et->attach_pdf_invoice) { try{ $pdf = Am_Pdf_Invoice::create($payment); $pdf->setDi($this->getDi()); $tpl->getMail()->createAttachment( $pdf->render(), 'application/pdf', Zend_Mime::DISPOSITION_ATTACHMENT, Zend_Mime::ENCODING_BASE64, $pdf->getFileName() ); } catch(Exception $e) { $this->getDi()->errorLogTable->logException($e); } } foreach ($recipients as $recipient) { $tpl->send($recipient); } } } } public function sendCronAutoresponders() { $userTable = $this->getDi()->userTable; $etCache = array(); $db = $this->getDi()->db; $q = $this->getDi()->resourceAccessTable->getResourcesForMembers(ResourceAccess::EMAILTEMPLATE)->query(); while ($res = $db->fetchRow($q)) { $user = $userTable->load($res['user_id'], false); if ($user->unsubscribed) continue; if (!$user) continue; // no user found if (!array_key_exists($res['resource_id'], $etCache)) $etCache[$res['resource_id']] = $this->load($res['resource_id'], false); if (!$etCache[$res['resource_id']]) continue; // no template found //do not send zero autoresponder second time if($etCache[$res['resource_id']]->day==1) if (in_array($res['resource_id'], (array)$user->data()->get('zero-autoresponder-sent'))) continue; if (($etCache[$res['resource_id']]->name != EmailTemplate::AUTORESPONDER)) continue; // check if no matching not_conditions if (!$etCache[$res['resource_id']]->checkNotConditions($user)) continue; $recipients = array(); if($etCache[$res['resource_id']]->recipient_user) { $recipients[] = $user; } if($etCache[$res['resource_id']]->recipient_aff && $user->aff_id && ($aff = $this->getDi()->userTable->load($user->aff_id, false))) { $recipients[] = $aff; } if($etCache[$res['resource_id']]->recipient_admin) { $recipients[] = Am_Mail_Template::TO_ADMIN; } if($etCache[$res['resource_id']]->recipient_emails) { foreach (array_map('trim', explode(',', $etCache[$res['resource_id']]->recipient_emails)) as $email) if($email) $recipients[] = $email; } $tpl = Am_Mail_Template::createFromEmailTemplate($etCache[$res['resource_id']]); $tpl->setLast_product_title($this->getLastProductTitle(array('fn' => $res['fn'], 'id' => $res['fn_id']), $user)); $tpl->setUser($user); foreach ($recipients as $recipient) { $tpl->send($recipient); } } } /** * @return PDOStatement */ public function sendCronExpires() { $mails = $this->findBy(array('name' => EmailTemplate::EXPIRE)); if (!$mails) return; // nothing to send $byDate = array(); // templates by expiration date foreach ($mails as $et) { $et->_productIds = $et->findMatchingProductIds(); /// $day = - $et->day; $string = $day . ' days'; if ($day >= 0) $string = "+$string"; if ($day == 0) $string = 'today'; $date = date('Y-m-d', strtotime($string, $this->getDi()->time)); $byDate[$date][] = $et; } $userTable = $this->getDi()->userTable; // now query expirations $q = $this->getDi()->accessTable->queryExpirations(array_keys($byDate)); $sent = array(); // user_id => array('tpl_id') while ($row = $this->_db->fetchRow($q)) { $user = $userTable->createRecord($row); if ($user->unsubscribed||!$user->is_approved) continue; foreach ($byDate[$row['_expire_date']] as $et) { if ($row['_recurring'] && !$et->recurring) continue; // do not send same template agian to the same user if (!empty($sent[$user->user_id]) && array_search($et->pk(), $sent[$user->user_id]) !== false) continue; if ($et->_productIds == ResourceAccess::ANY_PRODUCT || (in_array($row['_product_id'], $et->_productIds))) { // check if no matching not_conditions if (!$et->checkNotConditions($user)) continue; $recipients = array(); if($et->recipient_user) { $recipients[] = $user; } if ($et->recipient_aff && $user->aff_id && ($aff = $this->getDi()->userTable->load($user->aff_id, false))) { $recipients[] = $aff; } if($et->recipient_admin) { $recipients[] = Am_Mail_Template::TO_ADMIN; } if($et->recipient_emails) { foreach (array_map('trim', explode(',', $et->recipient_emails)) as $email) if($email) $recipients[] = $email; } $tpl = Am_Mail_Template::createFromEmailTemplate($et); $tpl->setUser($user); $tpl->setExpires(amDate($row['_expire_date'])); $tpl->setProduct_title($this->getDi()->productTable->load($row['_product_id'])->title); $tpl->setInvoice($row['_invoice_id'] ? $this->getDi()->invoiceTable->load($row['_invoice_id']) : array()); foreach ($recipients as $recipient) { $tpl->send($recipient); } $sent[$user->user_id][] = $et->pk(); } } } } } PK"\A  Video.phpnu[mime == 'audio/mpeg' ? 'audio' : 'video'; return $this->getDi()->url("$type/p/id/" . $this->video_id,null,false); } } class VideoTable extends ResourceAbstractTable { protected $_key = 'video_id'; protected $_table = '?_video'; protected $_recordClass = 'Video'; public function getAccessType() { return ResourceAccess::VIDEO; } public function getAccessTitle() { return ___('Video'); } public function getPageId() { return 'video'; } } PK"\mUser.phpnu[email, Am_Mail::LINK_USER); return null; } function __isset($name) { return ($name == 'unsubscribe_link'); } function delete() { if ($this->user_id <= 0) throw new Am_Exception_InternalError('Could not delete user, user_id is null'); $this->getDi()->hook->call(Am_Event::USER_BEFORE_DELETE, array('user' => $this)); $this->getDi()->invoiceTable->deleteByUserId($this->user_id); $this->getDi()->accessTable->deleteByUserId($this->user_id); $this->checkSubscriptions(false); $this->getDi()->hook->call(new Am_Event_SubscriptionRemoved($this)); parent::delete(); foreach (array('?_access_log', '?_access_cache', '?_user_status', '?_user_user_group') as $table) $this->getAdapter()->query("DELETE FROM $table WHERE user_id=?", $this->user_id); $this->getDi()->couponBatchTable->deleteByUserId($this->user_id); foreach ($this->getDi()->uploadTable->findBy(array( 'prefix' => Am_CustomFieldUpload::UPLOAD_PREFIX, 'user_id' => $this->user_id)) as $upload) { $upload->delete(); } $this->getDi()->hook->call(new Am_Event_UserAfterDelete($this)); } function insert($reload = true) { if (!isset($this->is_approved)) $this->is_approved = !$this->getDi()->config->get('manually_approve'); if (empty($this->remote_addr)) $this->remote_addr = htmlentities(@$_SERVER['REMOTE_ADDR']); if (empty($this->user_agent)) $this->user_agent = @$_SERVER['HTTP_USER_AGENT']; if (empty($this->added)) $this->added = $this->getDi()->sqlDateTime; $this->getDi()->hook->call(new Am_Event_UserBeforeInsert($this)); $ret = parent::insert($reload); if ($this->_passwordChanged) { $event = new Am_Event_SetPassword($this, $this->getPlaintextPass()); $this->getDi()->savedPassTable->setPass($event); $this->getDi()->hook->call($event); } if ($this->_passwordGenerated) { $c = new Am_Crypt_Aes128($this->getDi()->security->siteKey()); $pg = $c->encrypt($this->getPlaintextPass()); $this->getDi()->store->set('pass-generated-' . $this->pk(), $pg, '+6 hours'); } $this->getDi()->hook->call(new Am_Event_UserAfterInsert($this)); $this->_passwordChanged = false; return $ret; } function update() { $oldU = new stdclass; $oldU->user_id = null; if ($this->getDi()->hook->have(array( 'userBeforeUpdate', 'userAfterUpdate', 'subscriptionUpdated')) || $this->getDi()->config->get('manually_approve') ) { // do loading only if hooks are set $oldU = $this->getTable()->load($this->user_id, false); if (!$oldU) $oldU = new self; // avoid errors here $this->getDi()->hook->call(new Am_Event_UserBeforeUpdate($this, $oldU)); } $ret = parent::update(); if ($this->_passwordChanged) { $this->data()->set(self::NEED_SESSION_REFRESH, true)->update(); $event = new Am_Event_SetPassword($this, $this->getPlaintextPass()); $this->getDi()->savedPassTable->setPass($event); $this->getDi()->hook->call($event); $this->sendChangepassEmail(); } if ($oldU->user_id) { if ($this->is_approved && !$oldU->is_approved) $this->approve(); $this->getDi()->hook->call(new Am_Event_UserAfterUpdate($this, $oldU)); } if ($this->status && $oldU->user_id) { $this->getDi()->hook->call(new Am_Event_SubscriptionUpdated($this, $oldU)); } $this->_passwordChanged = false; return $ret; } function approve() { foreach ($this->getDi()->invoiceTable->findByUserId($this->pk()) as $invoice) $invoice->approve(); if (!$this->is_approved) { $this->is_approved = 1; $this->save(); } $this->sendSignupEmailIfNecessary(); } protected function _prepareForSet(&$vars) { if (isset($vars['pass'])) unset($vars['pass']); return parent::_prepareForSet($vars); } /** * @param string submitted password * @return bool true if ok, false if not */ function checkPassword($pass) { if (!strlen($pass) || !isset($this->pass) || !strlen($this->pass)) { return false; } $ph = new PasswordHash(8, true); return $ph->CheckPassword($pass, $this->pass); } /** * Set new password * (important! - it does not save the password!) */ function setPass($pass, $quick = false) { $this->_plaintextPass = $pass; $this->_passwordChanged = true; $this->pass = self::cryptPass($pass, $quick); $this->pass_dattm = $this->getDi()->sqlDateTime; return $this; } static function cryptPass($pass, $quick = false) { $ph = new PasswordHash($quick ? 4 : 8, true); return $ph->HashPassword($pass); } /** It is only exists after call of @method setPass() */ function getPlaintextPass() { return $this->_plaintextPass; } function getSavedPass() { return $this->getDi()->savedPassTable->findByUserId($this->pk()); } /** * Load generated password in plain-text format * it is only available 6 hours after signup and * supposed to be displayed on thanks page * * @return string|null */ function getStoredPlaintextPassword() { $pg = $this->getDi()->store->get('pass-generated-' . $this->pk()); if (!$pg) return null; $c = new Am_Crypt_Aes128($this->getDi()->security->siteKey()); return $c->decrypt($pg); } function getLoginCookie() { if (!$this->remember_key) { $this->updateQuick('remember_key', sha1(rand())); } return sha1($this->user_id . $this->login . md5($this->pass) . $this->remember_key); } function generateLogin() { // usernames to try $try = array(); if (!empty($this->email) && preg_match("/^([a-zA-Z0-9_]+)\@/", $this->email, $regs)) $try[] = $regs[1]; $fn = strtolower(preg_replace('/[^\w\d_]/', '', @$this->name_f)); $ln = strtolower(preg_replace('/[^\w\d_]/', '', @$this->name_l)); if ($fn || $ln) { if ($fn && $ln) { $try[] = $fn . '_' . $ln; } else { $try[] = $fn . $ln; } $try[] = $try[count($try) - 1] . rand(100, 999); } $e = new Am_Event(Am_Event::GENERATE_LOGIN, array('user' => $this)); $e->setReturn($try); $this->getDi()->hook->call($e); $try = $e->getReturn(); foreach ($try as $login) { if (strlen($login) > $this->getDi()->config->get('login_max_length')) $login = substr($login, 0, $this->getDi()->config->get('login_max_length')); if ((strlen($login) >= $this->getDi()->config->get('login_min_length')) && $this->getDi()->userTable->checkUniqLogin($login) && !$this->getDi()->banTable->findBan(array('login' => $login))) { $this->login = $login; return $this; } } // will generate it // a bit of configuration $min_length = $this->getDi()->config->get('login_min_length') < 4 ? 4 : $this->getDi()->config->get('login_min_length'); $max_length = $this->getDi()->config->get('login_max_length') > 10 ? 10 : $this->getDi()->config->get('login_max_length'); /// let's go do { $pass = $this->getDi()->security->randomString(rand($min_length, $max_length), "qwertyuiopasdfghjklzxcvbnm"); } while (!$this->getDi()->userTable->checkUniqLogin($pass)); $this->login = $pass; return $this; } function generatePassword() { // a bit of configuration $min_length = max($this->getDi()->config->get('pass_min_length', 8), 8); $max_length = min($this->getDi()->config->get('pass_max_length', 12), 14); $all_g = "aeiyo"; $all_gn = $all_g . "1234567890"; $all_s = "bcdfghjkmnpqrstwxz"; /// let's go $pass = ""; $length = rand($min_length, $max_length); for ($i = 0; $i < $length; $i++) { if ($i % 2) if ($i < $min_length) $pass .= $all_g[rand(0, strlen($all_g) - 1)]; else $pass .= $all_gn[rand(0, strlen($all_gn) - 1)]; else $pass .= $all_s[rand(0, strlen($all_s) - 1)]; } $this->_passwordGenerated = true; $this->setPass($pass); return $this; } private function _trueIfActive($v) { return $v == self::STATUS_ACTIVE; } private function _trueIfExpired($v) { return $v == self::STATUS_EXPIRED; } function checkSubscriptions($updateCache = false) { if (!$this->user_id) throw new Am_Exception_InternalError("Could not do User->checkSubscriptions() : user_id is empty"); if ($updateCache) $this->getDi()->resourceAccessTable->updateCache($this->user_id); $newStatus = $this->getDi()->accessTable->getStatusByUserId($this->user_id); $active = array_keys(array_filter($newStatus, array($this, '_trueIfActive'))); $oldStatus = $this->getProductsStatus(); $saved = array_keys(array_filter($oldStatus, array($this, '_trueIfActive'))); $merged = array_unique(array_merge($active, $saved)); $added = array_diff($merged, $saved); $deleted = array_diff($merged, $active); if ($active) $newUserStatus = self::STATUS_ACTIVE; elseif (array_filter($newStatus, array($this, '_trueIfExpired'))) $newUserStatus = self::STATUS_EXPIRED; else $newUserStatus = self::STATUS_PENDING; $statusChanged = ($newUserStatus != @$this->status); if ($statusChanged) { $this->updateQuick('status', $newUserStatus); } if ($added || $deleted || array_diff_assoc($newStatus, $oldStatus) || array_diff_assoc($oldStatus, $newStatus)) { $this->data()->set(self::NEED_SESSION_REFRESH, true)->update(); $this->getDi()->userStatusTable->setByUserId($this->user_id, $newStatus); foreach ($added as $product_id) { $e = new Am_Event_SubscriptionAdded($this, $this->getDi()->productTable->load($product_id)); $this->getDi()->hook->call($e); } foreach ($deleted as $product_id) { $e = new Am_Event_SubscriptionDeleted($this, $this->getDi()->productTable->load($product_id)); $this->getDi()->hook->call($e); } $e = new Am_Event_SubscriptionChanged($this, $added, $deleted); $this->getDi()->hook->call($e); } if ($statusChanged) { $this->sendSignupEmailIfNecessary(); } } /** * This function is called upon completion of payment * If user signup e-mail was not set before, it will be sent from there * It checks if : * - email is enabled in config * - user has at least one completed payment * - the e-mail was not sent before * - customer was approved */ function sendSignupEmailIfNecessary(InvoicePayment $p = null) { if (!$this->getDi()->config->get('send_signup_mail')) return; if ($this->data()->get('signup_email_sent')) return; // was already sent if (!$this->isApproved()) return; // is not yet approved $this->sendSignupEmail($p); } function sendSignupEmail(InvoicePayment $p = null) { if ($et = Am_Mail_Template::load('send_signup_mail', $this->lang)) { $et->setUser($this); //%password% placeholder will be available only in case auto_create //mode in payment system (user and payment created during same request) $pass = $this->getPlaintextPass(); $et->setPassword($pass ? $pass : ___('what you entered when creating your account')); if (empty($p)) $p = $this->getDi()->invoicePaymentTable->findFirstByUserId($this->user_id); if ($p) $et->setPayment($p); $et->send($this); $this->data()->set('signup_email_sent', 1)->update(); } } function sendRegistrationToAdminEmail() { if ($et = Am_Mail_Template::load('registration_mail_admin')) { $et->setUser($this); $et->setPassword($this->getPlaintextPass()); $et->sendAdmin(); } } function sendRegistrationEmail() { if ($et = Am_Mail_Template::load('registration_mail', $this->lang)) { $et->setUser($this); $et->setPassword($this->getPlaintextPass()); $et->send($this); } } function sendChangepassEmail() { if ($this->getDi()->config->get('changepass_mail') && ($et = Am_Mail_Template::load('changepass_mail', $this->lang))) { $et->setUser($this); $et->setPassword($this->getPlaintextPass()); $et->send($this); } } function sendNotApprovedEmail() { if ($et = Am_Mail_Template::load('manually_approve', $this->lang)) { $et->setUser($this); $et->send($this); } if ($et = Am_Mail_Template::load('manually_approve_admin')) { $et->setUser($this); $et->send(Am_Mail_Template::TO_ADMIN); } } /** * @return bool true if user have any active subscriptions */ function isActive() { return $this->status == self::STATUS_ACTIVE; } /** * Return true if customer paid at least once * @return bool */ function isPaid() { return (bool) $this->getAdapter()->selectCell( "SELECT SUM(amount)>0 FROM ?_invoice_payment p WHERE user_id=?d" , $this->user_id); } /** * @return true if user is approved */ function isApproved() { return!empty($this->is_approved) && ($this->is_approved > 0); } function isLocked() { return!empty($this->is_locked) && ($this->is_locked > 0); } function lock($flag = true) { $this->is_locked = (int)$flag; $this->save(); } /** * @return array of Access objects */ function getAccessRecords() { return $this->getDi()->accessTable->findByUserId($this->user_id); } function getActiveProducts() { return $this->getDi()->productTable->selectObjects("SELECT p.* FROM ?_user_status us " . "LEFT JOIN ?_product p USING(product_id) WHERE user_id=?d " . "AND status=?d " . "AND p.product_id IS NOT NULL " . "ORDER BY sort_order", $this->user_id, self::STATUS_ACTIVE); } function getExpiredProducs() { return $this->getDi()->productTable->loadIds($this->getExpiredProductIds()); } function getFutureProducts() { return $this->getDi()->productTable->loadIds($this->getFutureProductIds()); } /** * Returns max expiration date * if product or array of products are specified, returns it * for given products only */ function getExpire($productIdOrIds = array()) { $productIdOrIds = (array) $productIdOrIds; $productIdOrIds = array_filter(array_map('intval', $productIdOrIds)); return $this->getDi()->db->selectCell("SELECT MAX(expire_date) FROM ?_access WHERE user_id=?d { AND product_id IN (?a) }", $this->pk(), $productIdOrIds ? $productIdOrIds : DBSIMPLE_SKIP ); } function getBegin($productIdOrIds = array()) { $productIdOrIds = (array) $productIdOrIds; $productIdOrIds = array_filter(array_map('intval', $productIdOrIds)); return $this->getDi()->db->selectCell("SELECT MIN(begin_date) FROM ?_access WHERE user_id=?d AND begin_date > ? { AND product_id IN (?a) }", $this->pk(), sqlDate('now'), $productIdOrIds ? $productIdOrIds : DBSIMPLE_SKIP ); } function getRebill($productIdOrIds = array()) { $productIdOrIds = (array) $productIdOrIds; $productIdOrIds = array_filter(array_map('intval', $productIdOrIds)); return $this->getDi()->db->selectCell("SELECT MIN(i.rebill_date) FROM ?_access a LEFT JOIN ?_invoice_item ii ON a.invoice_id = ii.invoice_id AND a.product_id = ii.item_id AND ii.item_type = 'product' LEFT JOIN ?_invoice i ON a.invoice_id = i.invoice_id WHERE a.user_id=?d AND i.rebill_date IS NOT NULL AND i.status = ? AND i.rebill_date > ? AND ii.second_total > 0 {AND a.product_id IN (?a)}", $this->pk(), Invoice::RECURRING_ACTIVE, sqlDate('now'), $productIdOrIds ? $productIdOrIds : DBSIMPLE_SKIP ); } function getActiveProductsExpiration() { $ret = array(); foreach ($this->getActiveProductIds() as $pid) { $ret[$pid] = $this->getExpire($pid); } return $ret; } function getActiveCategoriesExpiration() { $p_map = $this->getActiveProductsExpiration(); $cp_map = $this->getDi()->productCategoryTable->getCategoryProducts(); $out = array_combine(array_keys($cp_map), array_fill(0, count($cp_map), null)); foreach ($cp_map as $cip => $pids) { foreach ($pids as $pid) { if (isset($p_map[$pid])) { $out[$cip] = max($out[$cip], $p_map[$pid]); } } } return array_filter($out); } function getFutureProductsBeginning() { $ret = array(); foreach ($this->getFutureProductIds() as $pid) { $ret[$pid] = $this->getBegin($pid); } return $ret; } function getActiveProductsRebill() { $ret = array(); foreach ($this->getActiveProductIds() as $pid) { $ret[$pid] = $this->getRebill($pid); } return $ret; } /** @return array of int active product# */ function getActiveProductIds() { return $this->getAdapter()->selectCol("SELECT product_id FROM ?_user_status WHERE user_id=?d AND status=?d", $this->user_id, self::STATUS_ACTIVE); } /** @return array of int expired product# */ function getExpiredProductIds() { return $this->getAdapter()->selectCol("SELECT product_id FROM ?_user_status WHERE user_id=?d AND status=?d", $this->user_id, self::STATUS_EXPIRED); } /** @return array of int future product# */ function getFutureProductIds() { return $this->getAdapter()->selectCol("SELECT DISTINCT a.product_id FROM ?_access a LEFT JOIN ?_user_status us USING(user_id,product_id) WHERE a.user_id = ? AND a.begin_date>? AND (us.status IS NULL OR us.status<>?)", $this->user_id, sqlDate('now'), self::STATUS_ACTIVE); } /** * @return array product_id => status (@see self::STATUS_ACTIVE, ... constants) */ function getProductsStatus() { return $this->getDi()->userStatusTable->getByUserId($this->user_id); } /// -- implementing IMailReceiver interface */ public function getEmail() { return $this->email; } public function getName() { return trim(@$this->name_f . ' ' . @$this->name_l); } public function isUnsubscribed() { return (bool) $this->unsubscribed; } public function canUnsubscribe() { return true; } /** param array $groups - array of id# */ function setGroups(array $groups) { $this->getAdapter()->query("DELETE FROM ?_user_user_group WHERE user_id=?d {AND user_group_id NOT IN (?a)}", $this->user_id, $groups ? $groups : DBSIMPLE_SKIP); if ($groups) { $vals = array(); foreach ($groups as $id) $vals[] = sprintf("(%d,%d)", $this->user_id, $id); $this->getAdapter()->query("INSERT IGNORE INTO ?_user_user_group (user_id, user_group_id) VALUES " . implode(", ", $vals)); } $this->checkSubscriptions(true); return $this; } /** @return array of id# */ function getGroups() { if (empty($this->user_id)) return array(); return $this->getAdapter()->selectCol( "SELECT DISTINCT user_group_id FROM ?_user_user_group WHERE user_id=?d", $this->user_id); } } /** * @method findFirstByLogin($login) * @method findFirstByEmail($login) */ class UserTable extends Am_Table_WithData { protected $_key = 'user_id'; protected $_table = '?_user'; protected $_recordClass = 'User'; protected $sort_order = array(); protected $_customFieldsConfigKey = 'member_fields'; public function clearPending($date, $incl_aff = false) { $q = $this->_db->queryResultOnly("SELECT u.* FROM ?_user u LEFT JOIN ?_access a ON a.user_id = u.user_id LEFT JOIN ?_invoice_payment ip ON ip.user_id = u.user_id WHERE u.status = 0 {AND IFNULL(u.is_affiliate,0)=?} AND a.access_id IS NULL AND ip.invoice_payment_id is NULL AND u.added < ? GROUP BY u.user_id", ($incl_aff ? DBSIMPLE_SKIP : 0), sqlTime($date)); while ($r = $this->_db->fetchRow($q)) { $u = $this->createRecord($r); $u->delete(); } } public function clearExpired($date, $incl_aff = false) { // this function deletes only 500 records at time, to do not work over limits $q = $this->_db->queryResultOnly("SELECT u.* FROM ?_user u LEFT JOIN ?_access a ON a.user_id = u.user_id LEFT JOIN ?_invoice_payment ip ON ip.user_id = u.user_id WHERE u.status = 2 {AND IFNULL(u.is_affiliate,0)=?} GROUP BY u.user_id HAVING GREATEST( IFNULL(MAX(ip.dattm), '2000-01-01'), IFNULL(MAX(a.expire_date), '2000-01-01')) < ? LIMIT 500 ", ($incl_aff ? DBSIMPLE_SKIP : 0), sqlTime($date)); while ($r = $this->_db->fetchRow($q)) { $u = $this->createRecord($r); $u->delete(); } } function getLoginRegex() { $regexp = $this->getDi()->config->get('login_disallow_spaces') ? '/^[-0-9a-zA-Z_]+$/D' : '/^([-0-9a-zA-Z_][-0-9a-zA-Z_ ]+[-0-9a-zA-Z_]|[-0-9a-zA-Z_]+)$/D'; $event = new Am_Event(Am_Event::GET_LOGIN_REGEX, array( 'login_disallow_spaces' => $this->getDi()->config->get('login_disallow_spaces') )); $event->setReturn($regexp); $this->getDi()->hook->call($event); return $event->getReturn(); } function getStrongPasswordRegex() { $regexp = '/^(?=.*[0-9].*[0-9])(?=.*[-!@#$%^&*().,=+`~{}\?].*[-!@#$%^&*().,=+`~{}\?])(?=.*[A-Z].*[A-Z])/'; $event = new Am_Event(Am_Event::GET_STRONG_PASSWORD_REGEX); $event->setReturn($regexp); $this->getDi()->hook->call($event); return $event->getReturn(); } /** * Check for username iniqueness only * @param string $login * @return bool True if record unique (no such login exists), false if not-unique */ function checkUniqLogin($login, $user_id = null) { $u = $this->_db->selectCell("SELECT user_id FROM ?_user WHERE login=? { AND user_id <> ?d}", $login, $user_id ? $user_id : DBSIMPLE_SKIP); if ($u) return 0; $event = $this->getDi()->hook->call(new Am_Event_CheckUniqLogin(null, array('login' => $login, 'userId' => $user_id))); return $event->isUnique() ? -1 : false; } /** * Check for username iniqueness only * @param string $login * @return bool True if record unique (no such login exists), false if not-unique */ function checkUniqEmail($email, $user_id = null) { $u = $this->_db->selectCell("SELECT user_id FROM ?_user WHERE email=? { AND user_id <> ?d}", $email, $user_id ? $user_id : DBSIMPLE_SKIP); if ($u) return 0; $event = $this->getDi()->hook->call(new Am_Event_CheckUniqEmail(null, array('email' => $email, 'userId' => $user_id))); return $event->isUnique() ? -1 : false; } function checkAllSubscriptionsFindChanged($limit = null) { $db = $this->_db; // update resource_access_cache $this->getDi()->resourceAccessTable->updateCache(); $db->query("DELETE FROM ?_user_status WHERE user_id NOT IN (SELECT user_id FROM ?_user)"); $db->query("DELETE FROM ?_access WHERE user_id NOT IN (SELECT user_id FROM ?_user)"); $db->query("DROP TABLE IF EXISTS ?_user_status_temp"); $db->query("CREATE TEMPORARY TABLE ?_user_status_temp ( user_id int not null, product_id int not null, status tinyint not null, INDEX(user_id,product_id)) "); // create table with calculated records from "access" $db->query(" INSERT INTO ?_user_status_temp SELECT a.user_id,a.product_id, CASE WHEN SUM(IF(a.expire_date>=?, 1, 0)) THEN 1 WHEN SUM(IF(a.expire_date< ?, 1, 0)) THEN 2 ELSE 0 END as status FROM ?_access a WHERE a.begin_date<=? GROUP BY user_id,product_id ", $this->getDi()->sqlDate, $this->getDi()->sqlDate, $this->getDi()->sqlDate); // now select differences $ids0 = $db->selectCol(" SELECT DISTINCT ms.user_id FROM ?_user_status ms LEFT JOIN ?_user_status_temp mst ON ms.user_id =mst.user_id AND ms.product_id=mst.product_id AND ms.status =mst.status WHERE mst.user_id IS NULL GROUP BY ms.user_id,ms.product_id {LIMIT ?d}", is_null($limit) ? DBSIMPLE_SKIP : $limit); if (!is_null($limit)) $limit = $limit - count($ids0); // select if there is no such a record in user_status table $ids1 = $db->selectCol("SELECT DISTINCT mst.user_id FROM ?_user_status_temp mst LEFT JOIN ?_user_status ms ON ms.user_id =mst.user_id AND ms.product_id=mst.product_id AND ms.status =mst.status WHERE ms.user_id IS NULL GROUP BY mst.user_id,mst.product_id {LIMIT ?d}", is_null($limit) ? DBSIMPLE_SKIP : $limit); if (!is_null($limit)) $limit = $limit - count($ids1); // select if user record has different "status" value $ids2 = $db->selectCol("SELECT DISTINCT m.user_id, CASE WHEN SUM(IF(mst.status=1, 1, 0)) THEN 1 WHEN SUM(IF(mst.status=2, 1, 0)) THEN 2 ELSE 0 END AS calcStatus, m.status FROM ?_user m LEFT JOIN ?_user_status_temp mst USING (user_id) GROUP BY m.user_id HAVING IFNULL(calcStatus,0) <> IFNULL(m.status, 0) {LIMIT ?d}", is_null($limit) ? DBSIMPLE_SKIP : $limit); return array_unique(array_merge($ids0, $ids1, $ids2)); } /** * If this function changed, check also AdminRebuildController - it must be changed too */ function checkAllSubscriptions() { while ($changed = $this->checkAllSubscriptionsFindChanged(1000)) { foreach ($changed as $user_id) { $u = $this->load($user_id, false); if ($u) $u->checkSubscriptions(false); // checked in checkAllSubscriptionsFindChanged } } } /** * Find user record by email (if $login looks like an email) * or by username * @param string $login e-mail or username * @return User|null */ function getByLoginOrEmail($login) { if (!strlen($login)) return null; if (strpos($login, '@') !== false) return $this->findFirstByEmail($login); else return $this->findFirstByLogin($login); } /** * Find record by login, check password and return it if all OK * with login/password * sets $resultCode from Am_Auth_Result * @return User */ function getAuthenticatedRow($login, $pass, & $code = null) { if (empty($login) || empty($pass)) { $code = Am_Auth_Result::INVALID_INPUT; return; } $u = $this->getByLoginOrEmail($login); if (!$u) { $code = Am_Auth_Result::USER_NOT_FOUND; return; } if (!$u->checkPassword($pass)) { $code = Am_Auth_Result::WRONG_CREDENTIALS; return; } $code = Am_Auth_Result::SUCCESS; return $u; } function getAuthenticatedCookieRow($cLogin, $cPass, & $code = null) { if (empty($cLogin) || empty($cPass)) { $code = Am_Auth_Result::INVALID_INPUT; return null; } $u = $this->getByLoginOrEmail($cLogin); if (!$u) { $code = Am_Auth_Result::USER_NOT_FOUND; return; } if ($u->getLoginCookie() !== $cPass) { $code = Am_Auth_Result::WRONG_CREDENTIALS; return null; } $code = Am_Auth_Result::SUCCESS; return $u; } function selectLast($num, $dateThreshold = null) { return $this->selectObjects("SELECT m.*, ROUND(SUM((p.amount - IFNULL(p.refund_amount, 0))/p.base_currency_multi), 2) AS paid, COUNT(p.invoice_payment_id) AS payments_count, sf.title AS saved_form_title FROM ?_user m LEFT JOIN ?_invoice_payment p USING (user_id) LEFT JOIN ?_saved_form sf ON m.saved_form_id=sf.saved_form_id {WHERE added > ?} GROUP BY m.user_id ORDER BY m.user_id DESC LIMIT ?d", $dateThreshold ?: DBSIMPLE_SKIP, $num); } }PK"\NzddSavedReport.phpnu[getDi()->adminTable->findBy() as $admin) { $frequency = $admin->getPref(Admin::PREF_REPORTS_SEND_FREQUENCY); if ($frequency == $event->getId()) { $content = ''; foreach($this->findByAdminId($admin->pk()) as $report) { $r = Am_Report_Abstract::createById($report->report_id); $r->applyConfigForm(new Am_Mvc_Request(unserialize($report->request))); $result = $r->getReport(); $output = new Am_Report_Text($result); $content .= $report->title . "\n----------------------------\n"; $content .= $output->render() . "\n"; } if ($content) { $mail = $this->getDi()->mail; $mail->addTo($admin->email); $mail->setSubject($this->getDi()->config->get('site_title') . ': Reports'); $mail->setBodyText($content); $mail->send(); } } } } } PK"\RPage.phpnu[path ? $this->getDi()->url("page/" . urlencode($this->path), null, false) : $this->getDi()->url("content/p/id/" . $this->page_id."/",null,false); } public function render(Am_View $view, $user = null, $use_layout = true) { $html = $this->html; $t = new Am_SimpleTemplate(); if ($user) $t->assign('user', $user); $t->assignStdVars(); $e = new Am_Event(Am_Event::PAGE_BEFORE_RENDER, array( 'template' => $t, 'user' => $user, 'page' => $this, 'html' => $html )); $this->getDi()->hook->call($e); $html = $t->render($e->getHtml()); if ($use_layout && $this->use_layout) { $view->content = '
' . $html . '
'; $view->title = $this->title; $view->meta_title = $this->meta_title ? $this->meta_title : $this->title; if ($this->meta_keywords) $view->headMeta()->setName('keywords', $this->meta_keywords); if ($this->meta_description) $view->headMeta()->setName('description', $this->meta_description); if ($this->meta_robots) $view->headMeta()->setName('robots', $this->meta_robots); return $view->render($this->tpl ? $this->tpl : 'layout.phtml'); } else return $html; } } class PageTable extends ResourceAbstractTable { protected $_key = 'page_id'; protected $_table = '?_page'; protected $_recordClass = 'Page'; public function getAccessType() { return ResourceAccess::PAGE; } public function getAccessTitle() { return ___('Pages'); } public function getPageId() { return 'pages'; } } PK"\40 Folder.phpnu[url; } public function getLinkTitle() { return $this->title ? $this->title : ___("Link"); } } class FolderTable extends ResourceAbstractTable { protected $_key = 'folder_id'; protected $_table = '?_folder'; protected $_recordClass = 'Folder'; public function getAccessType() { return ResourceAccess::FOLDER; } public function getAccessTitle() { return ___('Folders'); } public function getPageId() { return 'folders'; } } PK"\f`QLink.phpnu[url; } } class LinkTable extends ResourceAbstractTable { protected $_key = 'link_id'; protected $_table = '?_link'; protected $_recordClass = 'Link'; public function getAccessType() { return ResourceAccess::LINK; } public function getAccessTitle() { return ___('Links'); } public function getPageId() { return 'links'; } } PK"\VBan.phpnu[ 'xx', 'email'=>'xx', * 'login' => 'xxx') * @return array() if ok, array of keys matched like array('ip','login') */ function findBan(array $params) { $db = $this->_db; $where = array(); foreach ($params as $k => $v) $where[] = sprintf("(`type` = %s AND %s LIKE `value` )", $db->escape($k), $db->escape($v)); if (!$where) return array(); $arr = $db->selectCol($sql = "SELECT DISTINCT `type` FROM ?_ban WHERE " . join(" OR ", $where)); return $arr; } /** * Function suitable to use as "callback2" function in the Am_Form * it returns null if all ok, and dies or returns error message if banned action found * @param array $params * @return type */ function checkBan(array $params) { $foundBan = $this->findBan($params); foreach ($foundBan as $key) { $this->getDi()->errorLogTable->log("Attempt to signup from denied [$key]: " . htmlentities($params[$key])); if ($this->getDi()->config->get("ban.{$key}_action")=="die") { header("HTTP/1.0 500 Internal Error"); header("Status: 500 Internal Error"); exit(); } else { return ___("This %s is blocked. Please contact site support to find out why", $key); } return; ___('email'); ___('login'); ___('ip'); } } } PK"\͢I I AccessLog.phpnu[getDi()->sqlDateTime; return parent::insert($values, $returnInserted); } /** @access private */ function _resetOnce($flag) { $this->_logged = (bool)$flag; } function clearOld($date) { $this->_db->query("DELETE FROM ?_access_log WHERE `time` < ? ", $date . ' 00:00:00'); } function logOnce($user_id=null, $ip=null, $url=null, $referer=null) { if ($this->_logged) return; $this->log($user_id, $ip, $url, $referer); $this->_logged = true; } function log($user_id=null, $ip=null, $url=null, $referer=null) { $this->_db->query("INSERT INTO ?_access_log (time, user_id, remote_addr, url, referrer) VALUES (?, ?d, ?, ?, ?)" , $this->getDi()->sqlDateTime, get_first($user_id, $this->getDi()->auth->getUserId ()), get_first($ip, $_SERVER['REMOTE_ADDR']), get_first($url, $_SERVER['REQUEST_URI']), get_first($referer, @$_SERVER['HTTP_REFERER'])); } function isIpCountExceeded($user_id, $ip) { if (!$this->getDi()->config->get('max_ip_count')) return; $octets = 4 - $this->getDi()->config->get('max_ip_octets'); if ($octets < 1) $octets = 4; if ($octets == 4) { $ipCount = $this->_db->selectCell("SELECT COUNT(DISTINCT remote_addr) FROM ?_access_log WHERE user_id=?d AND time BETWEEN (? - INTERVAL ?d MINUTE) AND ? AND remote_addr <> ?", $user_id, $this->getDi()->sqlDateTime, $this->getDi()->config->get('max_ip_period', 10), $this->getDi()->sqlDateTime, $ip); } else { $ip_oct = implode(".", array_slice(explode(".", $ip), 0, $octets)); $ipCount = $this->_db->selectCell("SELECT COUNT(DISTINCT SUBSTRING_INDEX(remote_addr,'.',?d)) FROM ?_access_log WHERE user_id=?d AND time BETWEEN (? - INTERVAL ?d MINUTE) AND ? AND remote_addr NOT LIKE ?", $octets, $user_id, $this->getDi()->sqlDateTime, $this->getDi()->config->get('max_ip_period', 10), $this->getDi()->sqlDateTime, $ip_oct . '%'); } return $ipCount >= $this->getDi()->config->get('max_ip_count'); } } PK"\޼8.iiResourceAccess.phpnu[id; } public function getClass() { return $this->fn; } public function getClassTitle() { switch ($this->fn) { case self::FN_FREE : return ___('Free'); case self::FN_FREE_WITHOUT_LOGIN : return ___('Free'); case self::FN_CATEGORY : return ___('Category'); case self::FN_PRODUCT : return ___('Product'); case self::FN_USER_GROUP : return ___('User Group'); default: return $this->fn; }; } public function getTitle() { if ($this->fn == self::FN_FREE) return ___('Free Access'); if ($this->fn == self::FN_FREE_WITHOUT_LOGIN) return ___('Free Access without log-in'); $pr = null; if ($this->id) { if ($this->fn == self::FN_PRODUCT) $pr = $this->getDi()->productTable->load($this->id, false); elseif ($this->fn == self::FN_CATEGORY) { if ($this->id == self::ANY_PRODUCT) return ___('Any product'); else $pr = $this->getDi()->productCategoryTable->load($this->id, false); } elseif ($this->fn == self::FN_USER_GROUP) { $pr = $this->getDi()->userGroupTable->load($this->id, false); } } if (!$pr) return sprintf('(%s #%d)', $this->getClass(), $this->getId()); return sprintf('(%d) %s', $pr->pk(), $pr->title); } public function getStart() { if ($this->start_payments) return "{$this->start_payments}p"; return strlen($this->start_days) ? "{$this->start_days}d" : null; } public function getStop($parse_forever = true) { if ($this->stop_days == -1 && $parse_forever) return "forever"; return strlen($this->stop_days) ? "{$this->stop_days}d" : null; } public function hasCustomStartStop() { return strlen($this->stop_day) || strlen($this->start_day); } public function isAnyProducts() { return empty($this->product_id) && @$this->product_category_id <= 0; } public function isFree() { return ($this->fn == self::FN_FREE) || ($this->fn == self::FN_FREE_WITHOUT_LOGIN); } } class ResourceAccessTable extends Am_Table { protected $_key = 'resource_access_id'; protected $_table = '?_resource_access'; protected $_recordClass = 'ResourceAccess'; protected $_types = array(); /** * @return Am_Query */ protected function _getBaseQuery($joinConditions = "") { if ($joinConditions) $joinConditions = "(" . $joinConditions . ") AND "; $q = new Am_Query($this, 'r'); $q->clearFields(); $q->addField('DISTINCT r.resource_id', 'resource_id'); $q->addField('resource_type', 'resource_type'); $q->addField("fn", 'fn'); $q->addField("id", 'fn_id'); $q->leftJoin("?_access_cache", "c", " $joinConditions (((c.fn = r.fn) AND (c.id = r.id)) OR (r.fn='product_category_id' AND r.id=-1)) AND ( (c.status='active' AND r.start_days IS NULL AND r.stop_days IS NULL AND r.start_payments = 0) OR (c.status='active' AND c.days BETWEEN IFNULL(r.start_days,0) AND IFNULL(r.stop_days, 90000) AND c.payments_count >= IFNULL(r.start_payments,0)) OR (c.days >= IFNULL(r.start_days,0) AND r.stop_days = -1 AND c.payments_count >= IFNULL(r.start_payments,0)) )"); // is available if free, or if user has equal subscription record in access_cache $q->addWhere("(r.fn IN ('free', 'free_without_login') OR c.user_id IS NOT NULL)"); $q->addOrderRaw("(SELECT ras.sort_order FROM ?_resource_access_sort ras WHERE ras.resource_id=r.resource_id AND ras.resource_type=r.resource_type LIMIT 1), r.resource_id, r.resource_type"); return $q; } /** * Return resources currently allowed for user * @param User $user * @param array|single type constant from ResourceAccess $types * @return array of records (as array) */ function selectAllowedResources(User $user, $types = null) { // select product_id/product_category_id, type, number of days $q = $this->_getBaseQuery("c.user_id=".intval($user->pk())); if ($types !== null) $q->addWhere("resource_type IN (?a)", is_array($types) ? $types : array($types)); return $this->_db->fetchRows($q->query()); } /** * Return allowed product emails as objects * @return array of ResourceAbstract * @see self::selectAllowedResources */ function getProductWelcomeEmails($product_ids) { $ret = array(); $groups = $this->getDi()->db->selectCol("SELECT product_category_id from ?_product_product_category where product_id IN (?a)",$product_ids); $groups[]= -1; $q = new Am_Query($this, 'r'); $q->clearFields(); $q->addField('DISTINCT r.resource_id', 'resource_id'); $q->leftJoin('?_email_template', 'et', '(r.resource_id = et.email_template_id)'); $q->addWhere("resource_type = ?", ResourceAccess::EMAILTEMPLATE); $q->addWhere("(r.fn = 'product_id' AND r.id IN (?a) ) OR (r.fn = 'product_category_id' AND r.id IN (?a) )",$product_ids,$groups); $q->addWhere('et.name=?',EmailTemplate::PRODUCTWELCOME); $q->groupBy('resource_id'); $res = $this->_db->fetchRows($q->query()); $ret = array(); foreach ($res as $r) $ret[] = $this->getDi()->emailTemplateTable->load($r['resource_id']); return $ret; } /** * Return allowed product emails as objects * @return array of ResourceAbstract * @see self::selectAllowedResources */ function getPaymentEmails($product_ids) { $ret = array(); $groups = $this->getDi()->db->selectCol("SELECT product_category_id from ?_product_product_category where product_id IN (?a)",$product_ids); $groups[]= -1; $q = new Am_Query($this, 'r'); $q->clearFields(); $q->addField('DISTINCT r.resource_id', 'resource_id'); $q->leftJoin('?_email_template', 'et', '(r.resource_id = et.email_template_id)'); $q->addWhere("resource_type = ?", ResourceAccess::EMAILTEMPLATE); $q->addWhere("(r.fn = 'product_id' AND r.id IN (?a) ) OR (r.fn = 'product_category_id' AND r.id IN (?a) )",$product_ids,$groups); $q->addWhere('et.name=?',EmailTemplate::PAYMENT); $q->groupBy('resource_id'); $res = $this->_db->fetchRows($q->query()); $ret = array(); foreach ($res as $r) $ret[] = $this->getDi()->emailTemplateTable->load($r['resource_id']); return $ret; } /** * Return allowed resources as objects * @return array of ResourceAbstract * @see self::selectAllowedResources */ function getAllowedResources(User $user, $types = null, $groupByType = true) { $ret = array(); $res = $this->selectAllowedResources($user, $types = $this->getResourceTypes($types)); $ids = array(); $order = array(); $i = 0; foreach ($res as $k => $r) { // $ids[$r['resource_type']][$r['resource_id']] = $k; $ids[$r['resource_type']][$r['resource_id']] = array('fn' => $r['fn'], 'id' => $r['fn_id']); $order[$r['resource_type'].'_'.$r['resource_id']] = $i++; } $ret = array(); foreach ($ids as $resource_type => & $container) { $table = $this->getDi()->getService(lcfirst(toCamelCase($resource_type)) . 'Table'); /* @var $table Am_Table */ foreach ($table->loadIds(array_keys($container)) as $rec) { $id = $rec->pk(); if (isset($container[$id])) { $k = $order[$resource_type.'_'.$id]; // get position in result $rec->fn_id = $container[$id]; // assign product_title to email template $ret[$k] = $rec; } else { throw new Am_Exception_InternalError("->loadIds returned id[$id] not specified in request " . implode(",", array_keys($container))); } } } $event = new Am_Event(Am_Event::GET_ALLOWED_RESOURCES, array( 'user' => $user, 'types' => $types )); $event->setReturn($ret); $this->getDi()->hook->call($event); $ret = $event->getReturn(); ksort($ret); return $ret; } /** * Expand types constant to list of resource types * * @param string|array|null|enum(ResourceAccess::USER_VISIBLE_TYPES, ResourceAccess::USER_VISIBLE_PAGES) $types * @return array|null * @see Am_Event::GET_RESOURCE_TYPES */ protected function getResourceTypes($types) { if (is_null($types) || is_array($types) || !in_array($types, array(ResourceAccess::USER_VISIBLE_TYPES, ResourceAccess::USER_VISIBLE_PAGES))) return $types; if (isset($this->_types[$types])) return $this->_types[$types]; $res = array(); if ($types === ResourceAccess::USER_VISIBLE_TYPES) $res = array( ResourceAccess::FOLDER, ResourceAccess::FILE, ResourceAccess::PAGE, ResourceAccess::LINK, ResourceAccess::VIDEO, ); elseif ($types === ResourceAccess::USER_VISIBLE_PAGES) $res = array( ResourceAccess::FOLDER, ResourceAccess::PAGE, ResourceAccess::LINK, ); $event = new Am_Event(Am_Event::GET_RESOURCE_TYPES, array( 'type' => $types )); $event->setReturn($res); $this->getDi()->hook->call($event); $res = $event->getReturn(); $this->_types[$types] = $res; return $res; } function userHasAccess(User $user, $id, $type) { $q = $this->_getBaseQuery("c.user_id=".intval($user->pk())); $q->addWhere("resource_type=?", $type); $q->addWhere("resource_id=?", $id); $ret = (bool)$q->selectPageRecords(0, 1); $e = new Am_Event(Am_Event::USER_HAS_ACCESS, array( 'resource_id' => $id, 'resource_type' => $type, 'user' => $user )); $e->setReturn($ret); $this->getDi()->hook->call($e); return $e->getReturn(); } /** * Return true if not logged-in visitor has access * to the resource */ function guestHasAccess($id, $type) { $ret = (bool)$this->_db->selectCell("SELECT resource_access_id FROM {$this->_table} WHERE resource_type=? AND resource_id=? AND fn=?", $type, floatval($id), ResourceAccess::FN_FREE_WITHOUT_LOGIN); $e = new Am_Event(Am_Event::GUEST_HAS_ACCESS, array( 'resource_id' => $id, 'resource_type' => $type )); $e->setReturn($ret); $this->getDi()->hook->call($e); return $e->getReturn(); } function updateCache($userId = null) { if ($userId === null) { $this->_db->query("ALTER TABLE ?_access_cache DISABLE KEYS"); $this->_db->query("TRUNCATE TABLE ?_access_cache"); } else $this->_db->query("DELETE FROM ?_access_cache {WHERE user_id=?d}", $userId ? $userId : DBSIMPLE_SKIP); $productCatsCache = $this->getDi()->productCategoryTable->getCategoryProducts(); $dat = $this->getDi()->sqlDate; $today = $this->getDi()->sqlDate; $q = $this->_db->queryResultOnly(" SELECT user_id, product_id, UNIX_TIMESTAMP(begin_date) AS begin_date, UNIX_TIMESTAMP(LEAST(?, expire_date)) AS expire_date ,begin_date AS sql_begin_date ,expire_date AS sql_expire_date FROM ?_access WHERE {user_id = ?d AND } begin_date <= ? ORDER BY user_id ", $this->getDi()->sqlDate, $userId ? $userId : DBSIMPLE_SKIP, $this->getDi()->sqlDate); $rows = array(); $insert = array(); $lastUserId = null; while ($r = $this->_db->fetchRow($q)) { if (($r['user_id'] != $lastUserId) && $rows) { $insert = array_merge($insert, $this->_updateCacheUser($rows, $productCatsCache, $dat, $today)); if (count($insert) > 100) { $this->_insertCache($insert); $insert = array(); } $rows = array(); } $rows[] = $r; $lastUserId = $r['user_id']; } $this->_db->freeResult($q); if ($rows) $insert = array_merge($insert, $this->_updateCacheUser($rows, $productCatsCache, $dat, $today)); if ($insert) $this->_insertCache($insert); $this->_db->query("INSERT INTO ?_access_cache (user_id, fn, id, days, begin_date, expire_date, payments_count, status) SELECT user_id, 'user_group_id', user_group_id, NULL, NULL, NULL, 0, 'active' FROM ?_user_user_group WHERE 1 {AND user_id=?}", $userId === null ? DBSIMPLE_SKIP : $userId); if ($userId === null) { $this->_db->query("ALTER TABLE ?_access_cache ENABLE KEYS"); } $this->_db->query("UPDATE ?_access_cache ac SET payments_count = (SELECT COUNT(invoice_payment_id) FROM ?_invoice_payment ip INNER JOIN ?_invoice_item ii USING (invoice_id) WHERE ip.user_id = ac.user_id AND ii.item_id=ac.id) WHERE {ac.user_id=?d AND } ac.fn = 'product_id' ", $userId === null ? DBSIMPLE_SKIP : $userId); } function _insertCache(array $insert) { // todo - direct query : disable _expandPlaceholdersCallback $prefix = $this->_db->getPrefix(); $this->_db->queryQuick("INSERT IGNORE INTO {$prefix}access_cache (user_id, fn, id, days, begin_date, expire_date, status) VALUES\n" . implode(",", $insert)); } function _updateCacheUser(array $rows, array $productCatsCache, $dat, $today) { $active = $insert = array(); $min = $max = array(); foreach ($rows as $r) { $pid = $r['product_id']; $dates[$pid][] = array($r['begin_date']+43200, 0); $dates[$pid][] = array($r['expire_date']+43200, 1); if (empty($min[$pid]) || ($min[$pid] > $r['sql_begin_date'])) $min[$pid] = $r['sql_begin_date']; if (empty($max[$pid]) || ($max[$pid] < $r['sql_expire_date'])) $max[$pid] = $r['sql_expire_date']; if (($r['sql_begin_date'] <= $today) && ($today <= $r['sql_expire_date'])) { $active[$r['product_id']] = "active"; } elseif (empty($active[$r['product_id']])) { $active[$r['product_id']] = "expired"; } } $len = array(); foreach ($dates as $pid => $d) { sort($dates[$pid]); $len[$pid] = $this->_calcKleeLen($dates[$pid]); $insert[] = sprintf("(%d,'product_id',%d,%d,'%s','%s','%s')", $r['user_id'], $pid, $len[$pid], $min[$pid], $max[$pid], $active[$pid] ); } foreach ($productCatsCache as $pc => $pids) { $catDates = array(); $catActive = 'expired'; $catMax = $catMin = array(); foreach ($pids as $pid) { if (!empty($dates[$pid])) { $catDates = array_merge($catDates, $dates[$pid]); if ($active[$pid] == 'active') $catActive = 'active'; $catMin[$pid] = $min[$pid]; $catMax[$pid] = $max[$pid]; } } if (!$catDates) continue; if (count($catMax) == 1) // if there's only one product found { $pid = key($catMax); $insert[] = sprintf("(%d,'product_category_id',%d,%d,'%s','%s','%s')", $r['user_id'], $pc, $len[$pid], $min[$pid], $max[$pid], $active[$pid]); } else { sort($catDates); $insert[] = sprintf("(%d,'product_category_id',%d,%d,'%s','%s','%s')", $r['user_id'], $pc, $this->_calcKleeLen($catDates), min($catMin), max($catMax), $catActive ); } } return $insert; } function _calcKleeLen($datesArray) { $len = 0; $c = 0; foreach ($datesArray as $i => $v) { if ($c && $i) // count only inside an interval { $len += $v[0] - $datesArray[$i-1][0]; } if ($v[1]) // if end --$c; // we have finished an interval else { if (!$c) // opens new interval $len += 86400; ++$c; // we have started a new interval } } return round($len/86400); } /** * select resource accessible for customers using * records (user_id, resource_id, resource_type, login, email) * @return Am_Query */ function getResourcesForMembers($types = null, $condition="1=1") { if ($types && !is_array($types)) $types = (array)$types; $qfree = new Am_Query($this, 'rfree'); $qfree->crossJoin('?_user', 'u') ->clearFields() ->addField('u.user_id') ->addField('rfree.resource_id') ->addField('rfree.resource_type') ->addField('u.login') ->addField('u.email') ->addField("rfree.fn", 'fn') ->addField("rfree.id", 'fn_id') ->groupBy('user_id, resource_id, resource_type', 'u') ->addWhere("rfree.fn IN ('free', 'free_without_login')") ->addWhere("( (rfree.start_days IS NULL AND rfree.stop_days IS NULL) OR (CEIL((UNIX_TIMESTAMP() - UNIX_TIMESTAMP(u.added))/86400) BETWEEN IFNULL(rfree.start_days,0) AND IFNULL(rfree.stop_days, 90000)) OR (CEIL((UNIX_TIMESTAMP() - UNIX_TIMESTAMP(u.added))/86400) >= IFNULL(rfree.start_days,0) AND rfree.stop_days = -1) )"); if ($types) $qfree->addWhere('rfree.resource_type IN (?a) AND ' . $condition, $types); $q = $this->_getBaseQuery(); $q->clearFields(); $q->clearOrder(); $q->addField('DISTINCT c.user_id') ->addField('r.resource_id') ->addField('r.resource_type') ->addField('u.login') ->addField('u.email') ->addField("r.fn", 'fn') ->addField("r.id", 'fn_id') ->leftJoin('?_user', 'u', 'u.user_id=c.user_id') ->addOrder('user_id') // yes we need that subquery in subquery to mask field names // to get access of fields of main query (!) ->addOrderRaw("(SELECT _sort_order FROM ( SELECT sort_order as _sort_order, resource_type as _resource_type, resource_id as _resource_id FROM ?_resource_access_sort ras) AS _ras WHERE _resource_id=resource_id AND _resource_type=resource_type LIMIT 1), resource_id, resource_type") ->groupBy('user_id, resource_id, resource_type', 'c') // we will use separate query for free records ->addWhere("r.fn NOT IN ('free', 'free_without_login')") ->addUnion($qfree); if ($types) $q->addWhere('r.resource_type IN (?a) AND ' . $condition, $types); return $q; } function getFnValues() { return array( ResourceAccess::FN_CATEGORY, ResourceAccess::FN_PRODUCT, ResourceAccess::FN_USER_GROUP, ResourceAccess::FN_FREE, ResourceAccess::FN_FREE_WITHOUT_LOGIN, ); } /** * Return available types of resources * @return array * key: type * value: ResourceAccessTable */ function getAccessTables() { if (empty($this->_accessTables)) { $di = $this->getDi(); foreach(array( $di->folderTable, $di->fileTable, $di->pageTable, $di->integrationTable, $di->emailTemplateTable, $di->linkTable, $di->videoTable, ) as $t) $this->registerAccessTable($t); $di->hook->call(Am_Event::INIT_ACCESS_TABLES, array('registry' => $this)); } return $this->_accessTables; } function registerAccessTable(ResourceAbstractTable $t) { $this->_accessTables[$t->getAccessType()] = $t; } function syncSortOrder() { $db = $this->getDi()->db; // foreach ($this->getAccessTables() as $k => $t) { // delete records that are not found in master table $db->query("DELETE FROM ?_resource_access_sort WHERE resource_type=? AND NOT EXISTS ( SELECT * FROM ?# t WHERE t.?#=resource_id LIMIT 1 ) ", $k, $t->getName(), $t->getKeyField()); // add records that present in master table $x = (int)$db->selectCell("SELECT MAX(sort_order) FROM ?_resource_access_sort"); if (!$x) $x = 3000; $key = $t->getKeyField(); $db->query("INSERT IGNORE INTO ?_resource_access_sort SELECT null, $key as resource_id, '$k' as resource_type, $key + $x as sort_order FROM ?# {WHERE name IN (?a) } ", $t->getName(), $t instanceof EmailTemplateTable ? array(EmailTemplate::AUTORESPONDER, EmailTemplate::EXPIRE) : DBSIMPLE_SKIP); } } function clearAccess($id, $type) { return $this->getDi()->resourceAccessTable->deleteBy( array('resource_type' => $type, 'resource_id' => $id) ); } /** * Add a resource access record * @param int $recordId * @param enum $recordType * @param int $itemId product# or category# or -1 * @param string $startString 1d or 3m or 0d - for zero autoresponder * @param string $stopString * @param bool $isProduct is a product or category * @return ResourceAccess */ public function addAccessListItem($recordId, $recordType, $itemId, $startString, $stopString, $fn) { $fa = $this->getDi()->resourceAccessRecord; $fa->resource_type = $recordType; $fa->resource_id = $recordId; $fa->fn = $fn; $fa->id = $itemId; $fa->start_days = null; $fa->start_payments = 0; $fa->stop_days = null; if (preg_match('/^(\d+)p$/', strtolower($startString), $regs)) { $fa->start_payments = $regs[1]; } elseif (preg_match('/^(-?\d+)(\w+)$/', strtolower($startString), $regs)) { $fa->start_days = $regs[1]; } if (preg_match('/^(-?\d+)(\w+)$/', strtolower($stopString), $regs)) { $fa->stop_days = $regs[1]; } $fa->insert(); return $fa; } public function setAccess($recordId, $recordType, $access) { $this->clearAccess($recordId, $recordType); foreach(array( 'free' => ResourceAccess::FN_FREE, 'free_without_login' => ResourceAccess::FN_FREE_WITHOUT_LOGIN, 'product_id' => ResourceAccess::FN_PRODUCT, 'product_category_id' => ResourceAccess::FN_CATEGORY, 'user_group_id' => ResourceAccess::FN_USER_GROUP) as $key => $rtype) { if(!empty($access[$key])) { foreach ($access[$key] as $id => $params) { if (!is_array($params)) $params = json_decode($params, true); $this->addAccessListItem($recordId, $recordType, $id, $params['start'], $params['stop'], $rtype); } } } } public function getAccessList($recordId, $recordType) { return $this->findBy(array( 'resource_type' => $recordType, 'resource_id' => $recordId)); } }PK"\l46IICouponBatch.phpnu[deleteFromRelatedTable('?_coupon'); parent::delete(); } function generateCoupons($count, $minLen, $maxLen) { $table = $this->getDi()->couponTable; for ($i=0; $i<$count; $i++){ $coupon = $this->getDi()->couponRecord; $coupon->code = $table->generateCouponCode($minLen, $maxLen); $coupon->batch_id = $this->pk(); $coupon->insert(); } } /** * Retrive array of product ids coupon can be applied for * * @return array|null null in case of there is not any restriction */ function getApplicableProductIds() { //return null in case this field is empty if (!$this->product_ids) return null; $catProducts = $this->getDi()->productCategoryTable->getCategoryProducts(); $res = array_filter(explode(',',$this->product_ids)); foreach ($res as $k => $g) { if (preg_match('/CATEGORY-(\d*)/', $g, $match)) { unset($res[$k]); $catId = $match[1]; foreach ($catProducts[$catId] as $prId) { $res[] = $prId; } } } return $res; } function getOnlyApplicableProductIds() { $res = array_filter(explode(',',$this->product_ids)); $result = array(); foreach ($res as $k => $g) { if (!preg_match('/CATEGORY-(\d*)/', $g, $match)) { $result[] = $g; } } return $result; } function getOnlyApplicableCategoryIds() { $res = array_filter(explode(',',$this->product_ids)); $result = array(); foreach ($res as $k => $g) { if (preg_match('/CATEGORY-(\d*)/', $g, $match)) { $result[] = $match[1]; } } return $result; } function getRequireProduct() { return array_unique(array_filter($this->parseRequirementsGroup($this->require_product))); } function getPreventIfProduct() { return array_unique(array_filter($this->parseRequirementsGroup($this->prevent_if_product))); } /** * @param string $req * @return array */ protected function parseRequirementsGroup($req) { $catProducts = $this->getDi()->productCategoryTable->getCategoryProducts(); $res = array_filter(explode(',',$req)); foreach ($res as $k => $g) { if (preg_match('/CATEGORY-(\w*)-(\d*)/', $g, $match)) { unset($res[$k]); $status = $match[1]; $catId = $match[2]; foreach ($catProducts[$catId] as $prId) { $res[] = sprintf('%s-%d', $status, $prId); } } } return $res; } } class CouponBatchTable extends Am_Table { protected $_key = 'batch_id'; protected $_table = '?_coupon_batch'; protected $_recordClass = 'CouponBatch'; function deleteByUserId($user_id){ if (!$user_id) throw new Am_Exception_InternalError("user_id is empty in " . __METHOD__); $list = $this->_db->selectCol("SELECT batch_id FROM ?_coupon_batch WHERE user_id=?d", $user_id); if ($list) $this->_db->query("DELETE FROM ?_coupon_batch, ?_coupon WHERE batch_id IN (?a)", $list); } function getOptions($onlyEnabled = true) { return array_map(array('Am_Html', 'escape'), $this->_db->selectCol("SELECT batch_id as ARRAY_KEY, CONCAT('#', batch_id, ' ', IF(comment<>'', comment, CONCAT(discount, IF(discount_type='percent', '%', '$'), IF(begin_date, CONCAT(' (', begin_date, ' - ', expire_date, ')'), '')))) FROM ?_coupon_batch {WHERE is_disabled = ?}", $onlyEnabled ? 0 : DBSIMPLE_SKIP)); } } PK"\e Y UserNote.phpnu[selectObjects("SELECT n.*, u.name_f, u.name_l, u.login, u.email, a.login AS a_login, a.name_f AS a_name_f, a.name_l AS a_name_l FROM ?_user_note n LEFT JOIN ?_user u ON n.user_id = u.user_id LEFT JOIN ?_admin a ON n.admin_id = a.admin_id {WHERE n.dattm > ?} ORDER BY n.dattm DESC LIMIT ?d", $dateThreshold ?: DBSIMPLE_SKIP, $num); } }PK"\Œ^ResourceAbstractFile.phpnu[isLocal() ? (($upload = $this->getUpload()) ? $upload->name : basename($this->getFullPath())) : basename($this->path); } /** @return Am_Storage_File */ protected function getStorageFile() { if (!$this->_storageItem) $this->_storageItem = $this->getDi()->plugins_storage->getFile($this->path); return $this->_storageItem; } function isLocal() { return (bool)$this->getStorageFile()->getLocalPath(); } function getStorageId() { return $this->getStorageFile()->getStorageId(); } function getProtectedUrl($exptime, $force_download = true) { return $this->getStorageFile()->getUrl($exptime, $force_download); } function getFullPath() { return $this->getStorageFile()->getLocalPath(); } /** @return Upload|null */ function getUpload() { if ($this->_upload && $this->_upload->upload_id == $this->path) return $this->_upload; return ($this->path > 0) ? $this->_upload = $this->getDi()->uploadTable->load($this->path) : null; } function isExists() { return file_exists($this->getFullPath()); } function getMime() { return $this->mime ? $this->mime : 'application/octet-stream'; } function getSize() { return $this->isLocal() ? filesize($this->getFullPath()) : @$this->size; } function getName() { $upload = $this->getUpload(); return $upload ? $upload->getName() : $this->title; } }PK"\.I4 SavedPass.phpnu[getTable()->getPasswordFormats(); $callback = $formats[$this->format]; $salt = $this->salt ?: $this->pass; $arr = array($pass, & $salt, null); $hash = $callback ? call_user_func_array($callback, $arr) : SavedPassTable::crypt($pass, $this->format, $salt, null); return $pass && ($hash == $this->pass); } } class SavedPassTable extends Am_Table { const PASSWORD_PLAIN = 'plain'; const PASSWORD_MD5 = 'md5'; const PASSWORD_SHA1 = 'sha1'; const PASSWORD_CRYPT = 'crypt'; const PASSWORD_MD5_MD5_PASS_SALT = 'md5(md5(pass).salt)'; const PASSWORD_MD5_SALT_MD5_PASS = 'md5(salt.md5(pass))'; const PASSWORD_SHA1_SHA1_PASS_SALT = 'sha1(sha1(pass).salt)'; const PASSWORD_SHA256_SHA256_PASS_SALT = 'sha256(sha256(pass).salt)'; const PASSWORD_PHPASS = 'phpass'; const PASSWORD_PASSWORD_HASH = 'password_hash'; protected $_key = 'saved_pass_id'; protected $_table = '?_saved_pass'; protected $_recordClass = 'SavedPass'; /** @return SavedPass|null */ function findSaved(User $user, $format) { if ($format === SavedPassTable::PASSWORD_PHPASS) { $row = array( 'saved_pass_id' => 0, 'user_id' => $user->user_id, 'format' => $format, 'pass' => $user->pass, 'salt' => null, 'time' => null, ); } else { $row = $this->_db->selectRow( "SELECT * FROM ?_saved_pass WHERE user_id=?d AND format=?", $user->user_id, $format); } if ($row) return $this->createRecord()->fromRow($row); } function setPass(Am_Event_SetPassword $event) { $user = $event->getUser(); $pass = $event->getPassword(); $sql = array(); //// $obj = $this->getDi()->savedPassRecord; $obj->format = self::PASSWORD_PHPASS; $obj->pass = $user->pass; $obj->salt = null; $obj->toggleFrozen(true); $event->addSaved($obj); //// foreach ($this->getPasswordFormats() as $format => $callback) { $salt = null; $arr = array($pass, & $salt, $user); $password = $callback ? call_user_func_array($callback, $arr) : self::crypt($pass, $format, $salt, $user); $sql[] = $this->_db->expandPlaceholders( array("(?d, ?, ?, ?)", $user->user_id, $format, $password, $salt) ); $obj = $this->getDi()->savedPassRecord; $obj->format = $format; $obj->pass = $password; $obj->salt = $salt; $obj->toggleFrozen(true); $event->addSaved($obj); } if ($sql) $this->_db->query("INSERT INTO ?_saved_pass (user_id, format, pass, salt) VALUES " . implode(",", $sql) . " ON DUPLICATE KEY UPDATE pass=VALUES(pass), salt=VALUES(salt)"); } /** @return array of array(type, [optional]callback) */ function getPasswordFormats() { $types = array( self::PASSWORD_MD5_MD5_PASS_SALT => null, self::PASSWORD_CRYPT => null, self::PASSWORD_PASSWORD_HASH => null ); foreach ($this->getDi()->plugins_protect->loadEnabled()->getAllEnabled() as $pl) { $format = $pl->getPasswordFormat(); if ($format === null) continue; if ($format === self::PASSWORD_PHPASS) continue; if ($format === self::PASSWORD_PASSWORD_HASH) continue; $types[$format] = array($pl, 'cryptPassword'); } $e = new Am_Event(Am_Event::GET_PASSWORD_FORMATS); $e->setReturn($types); $this->getDi()->hook->call($e); return $e->getReturn(); } static function crypt($pass, $methodConst, & $salt = null, User $user = null) { switch ($methodConst) { case self::PASSWORD_PHPASS: $ph = new PasswordHash(8, true); // validation request if ($salt && $ph->CheckPassword($pass, $salt)) return $salt; // new password return $ph->HashPassword($pass); case self::PASSWORD_PASSWORD_HASH : if($salt && password_verify($pass, $salt)){ return $salt; } return password_hash($pass, PASSWORD_DEFAULT); case self::PASSWORD_PLAIN: return $pass; case self::PASSWORD_MD5: return md5($pass); case self::PASSWORD_SHA1: return sha1($pass); case self::PASSWORD_CRYPT: if (empty($salt)) { if (version_compare(PHP_VERSION, '5.5.0')>0) $salt = password_hash($pass, PASSWORD_BCRYPT); // avoid warning else $salt = crypt($pass); return $salt; } return crypt($pass, $salt); case self::PASSWORD_MD5_MD5_PASS_SALT: if (empty($salt)) $salt = Am_Di::getInstance()->security->randomString(3); return md5(md5($pass) . $salt); case self::PASSWORD_MD5_SALT_MD5_PASS: if (empty($salt)) $salt = Am_Di::getInstance()->security->randomString(3); return md5($salt . md5($pass)); case self::PASSWORD_SHA1_SHA1_PASS_SALT: if (empty($salt)) $salt = Am_Di::getInstance()->security->randomString(3); return sha1(sha1($pass) . $salt); case self::PASSWORD_SHA256_SHA256_PASS_SALT: if (empty($salt)) $salt = Am_Di::getInstance()->security->randomString(3); return hash('sha256', hash('sha256', $pass) . $salt); default: throw new Am_Exception_InternalError("Unknown crypt method passed: [" . htmlentities($methodConst) . "]"); } } }PK"\Dt UserGroup.phpnu[_childNodes; } function createChildNode() { $c = new self; $c->parent_id = $this->pk(); if (!$c->parent_id) throw new Am_Exception_InternalError("Could not add child node to not-saved object in ".__METHOD__); $this->_childNodes[] = $c; return $c; } public function fromRow(array $vars) { if (isset($vars['childNodes'])) { foreach ($vars['childNodes'] as $row) { $r = new self($this->getTable()); $r->fromRow($row); $this->_childNodes[] = $r; } unset($vars['childNodes']); } return parent::fromRow($vars); } public function delete() { $ret = parent::delete(); $this->getTable()->getAdapter()->query("DELETE FROM ?_resource_access WHERE fn='user_group_id' AND id=?d", $this->pk()); return $ret; } } class UserGroupTable extends Am_Table { protected $_key = 'user_group_id'; protected $_table = '?_user_group'; protected $_recordClass = 'UserGroup'; /** * @return ProductCategory */ function getTree() { $ret = array(); foreach ($this->_db->select("SELECT user_group_id AS ARRAY_KEY, parent_id AS PARENT_KEY, pc.* FROM ?_user_group AS pc ORDER BY 0+sort_order") as $r) { $ret[] = $this->createRecord($r); } return $ret; } function getSelectOptions(array $options = array()) { $ret = array(); $sql = "SELECT user_group_id AS ARRAY_KEY, parent_id, title FROM ?_user_group ORDER BY parent_id, 0+sort_order"; $rows = $this->_db->select($sql); foreach ($rows as $id => $r){ $parent_id_used = array( $id ); $title = $r['title']; $parent_id = $r['parent_id']; while ($parent_id) { // protect against endless cycle if (in_array($parent_id, $parent_id_used)) break; if (empty($rows[$parent_id])) break; $parent = $rows[$parent_id]; $title = $parent['title'] . '/' . $title; $parent_id = $parent['parent_id']; } $ret [ $id ] = $title; } return $ret; } function getOptions() { return $this->getSelectOptions(); } function moveNodes($fromId, $toId) { $this->_db->query("UPDATE {$this->_table} SET parent_id=?d WHERE parent_id=?d", $toId, $fromId); } public function delete($key) { parent::delete($key); $this->_db->query("DELETE FROM ?_user_user_group WHERE user_group_id=?d", $key); } } PK"\o<CurrencyExchange.phpnu[_db->selectCell( "SELECT rate FROM ?_currency_exchange WHERE currency=? AND `date`<=? ORDER BY `date` DESC LIMIT 1", $currency, $date); if (empty($ret)) $ret = 1.00; return $ret; } } PK"\>MuBB Upload.phpnu[name; } function setName($name){ $x=preg_split('/[\\\\\/]/', $name); $this->name = array_pop($x); return $this; } function getPrefix() { return $this->prefix; } /** Get full pathname to file */ function getPath(){ return $this->path; } function setPath($path){ $this->path = $path; return $this;} /** Return file size as received from upload */ function getSize(){ return $this->_size ? $this->_size : $this->_size = @filesize($this->getFullPath()); } function setSize($size){ $this->_size = $size; return $this;} function getSizeReadable(){ return Am_Storage_File::getSizeReadable($this->getSize()); } /** Return file mime type as submitted by user */ function getType(){ return $this->mime; } function setType($type){ $this->mime = $type; return $this;} /** Return filename without dir path */ function getFilename() { return basename($this->path); } /** For internal usage only */ function setFrom_FILES($record, $k = null) { $this->setName($k === null ? $record['name'] : $record['name'][$k]); $this->mime = self::getMimeType($this->getName()); $this->_tmp_name = $k === null ? $record['tmp_name'] : $record['tmp_name'][$k]; $this->_error = $k === null ? @$record['error'] : @$record['error'][$k]; $this->_size = filesize($this->_tmp_name); } protected function moveUploadedFile($source, $target) { return defined('AM_TEST') ? rename($source, $target) : move_uploaded_file($source, $target); } function moveUploaded($tempSeconds = null){ $this->delete = $tempSeconds > 0 ? $this->getDi()->time+$tempSeconds : null; if (!$this->isLoaded()) { //for re-upload do { $this->path = '.'.$this->prefix.'.'.uniqid(); $fullPath = $this->getFullPath(); } while (file_exists($fullPath)); } $fullPath = $this->getFullPath(); if (!$this->moveUploadedFile($this->_tmp_name, $fullPath)){ throw new Am_Exception_InternalError("Could not move uploaded file [$this->_tmp_name] to [$fullPath]"); } $this->save(); unset($this->_tmp_name); unset($this->_error); } function delete(){ $fullPath = $this->getFullPath(); if (file_exists($fullPath)) if (!unlink($fullPath)) throw new Am_Exception_InternalError("Could not delete [$fullPath], and corresponding database records also is not deleted"); parent::delete(); } function getStoreFolder() { return Am_Di::getInstance()->data_dir; } function getFullPath() { return $this->getTable()->getRoot() . DIRECTORY_SEPARATOR . @$this->path; } /** * Determine mime type by file extension * @return string, application/octet-stream if unknown */ static function getMimeType($filename){ $common_mime_types = array( "exe" => "application/octet-stream", "class" => "application/octet-stream", "so" => "application/octet-stream", "dll" => "application/octet-stream", "oda" => "application/oda", "hqx" => "application/mac-binhex40", "cpt" => "application/mac-compactpro", "doc" => "application/msword", "bin" => "application/octet-stream", "dms" => "application/octet-stream", "lha" => "application/octet-stream", "lzh" => "application/octet-stream", "pdf" => "application/pdf", "ai" => "application/postscript", "eps" => "application/postscript", "ps" => "application/postscript", "smi" => "application/smil", "smil" => "application/smil", "bcpio" => "application/x-bcpio", "wbxml" => "application/vnd.wap.wbxml", "wmlc" => "application/vnd.wap.wmlc", "wmlsc" => "application/vnd.wap.wmlscriptc", "ogx" => "application/ogg", "anx" => "application/annodex", "xspf" => "application/xspf+xml", "vcd" => "application/x-cdlink", "pgn" => "application/x-chess-pgn", "cpio" => "application/x-cpio", "csh" => "application/x-csh", "dcr" => "application/x-director", "dir" => "application/x-director", "dxr" => "application/x-director", "dvi" => "application/x-dvi", "spl" => "application/x-futuresplash", "gtar" => "application/x-gtar", "hdf" => "application/x-hdf", "skp" => "application/x-koan", "skd" => "application/x-koan", "js" => "application/x-javascript", "skt" => "application/x-koan", "skm" => "application/x-koan", "latex" => "application/x-latex", "nc" => "application/x-netcdf", "cdf" => "application/x-netcdf", "sh" => "application/x-sh", "shar" => "application/x-shar", "swf" => "application/x-shockwave-flash", "sit" => "application/x-stuffit", "sv4cpio" => "application/x-sv4cpio", "sv4crc" => "application/x-sv4crc", "tar" => "application/x-tar", "tcl" => "application/x-tcl", "tex" => "application/x-tex", "texinfo" => "application/x-texinfo", "texi" => "application/x-texinfo", "t" => "application/x-troff", "tr" => "application/x-troff", "roff" => "application/x-troff", "man" => "application/x-troff-man", "me" => "application/x-troff-me", "ms" => "application/x-troff-ms", "ustar" => "application/x-ustar", "src" => "application/x-wais-source", "xhtml" => "application/xhtml+xml", "xht" => "application/xhtml+xml", "zip" => "application/zip", "au" => "audio/basic", "snd" => "audio/basic", "mid" => "audio/midi", "midi" => "audio/midi", "kar" => "audio/midi", "mpga" => "audio/mpeg", "mp2" => "audio/mpeg", "mp3" => "audio/mpeg", "oga" => "audio/ogg", "ogg" => "audio/ogg", "spx" => "audio/ogg", "flac" => "audio/flac", "axa" => "audio/annodex", "aif" => "audio/x-aiff", "aiff" => "audio/x-aiff", "aifc" => "audio/x-aiff", "m3u" => "audio/x-mpegurl", "ram" => "audio/x-pn-realaudio", "rm" => "audio/x-pn-realaudio", "rpm" => "audio/x-pn-realaudio-plugin", "ra" => "audio/x-realaudio", "wav" => "audio/x-wav", "pdb" => "chemical/x-pdb", "xyz" => "chemical/x-xyz", "bmp" => "image/bmp", "gif" => "image/gif", "ief" => "image/ief", "jpeg" => "image/jpeg", "jpg" => "image/jpeg", "jpe" => "image/jpeg", "png" => "image/png", "tiff" => "image/tiff", "tif" => "image/tif", "djvu" => "image/vnd.djvu", "djv" => "image/vnd.djvu", "wbmp" => "image/vnd.wap.wbmp", "ras" => "image/x-cmu-raster", "pnm" => "image/x-portable-anymap", "pbm" => "image/x-portable-bitmap", "pgm" => "image/x-portable-graymap", "ppm" => "image/x-portable-pixmap", "rgb" => "image/x-rgb", "xbm" => "image/x-xbitmap", "xpm" => "image/x-xpixmap", "xwd" => "image/x-windowdump", "igs" => "model/iges", "iges" => "model/iges", "msh" => "model/mesh", "mesh" => "model/mesh", "silo" => "model/mesh", "wrl" => "model/vrml", "vrml" => "model/vrml", "mpeg" => "video/mpeg", "mpg" => "video/mpeg", "mpe" => "video/mpeg", "mp4" => "video/mp4", "qt" => "video/quicktime", "mov" => "video/quicktime", "mxu" => "video/vnd.mpegurl", "ogv" => "video/ogg", "axv" => "video/annodex", "webm" => "video/webm", "3gp" => "video/3gp", "avi" => "video/x-msvideo", "flv" => "video/x-flv", "f4v" => "video/x-f4v", "movie" => "video/x-sgi-movie", "css" => "text/css", "asc" => "text/plain", "txt" => "text/plain", "rtx" => "text/richtext", "rtf" => "text/rtf", "sgml" => "text/sgml", "sgm" => "text/sgml", "tsv" => "text/tab-seperated-values", "wml" => "text/vnd.wap.wml", "wmls" => "text/vnd.wap.wmlscript", "etx" => "text/x-setext", "xml" => "text/xml", "xsl" => "text/xml", "htm" => "text/html", "html" => "text/html", "shtml" => "text/html", "csv" => "text/csv" ); $ext = explode('.', $filename); $ext = strtolower(array_pop($ext)); if (array_key_exists($ext, $common_mime_types)) return $common_mime_types[$ext]; else return 'application/octet-stream'; } /** @return bool */ function isValid() { return file_exists($this->getFullPath()); } /** @return bool */ function isInsideStoreFolder() { $p = realpath($this->getFullPath()); $f = realpath($this->getTable()->getRoot()); return strlen($f) && (strpos($p, $f) === 0); } function insert($reload = true) { $this->uploaded = $this->getDi()->time; return parent::insert($reload); } } class UploadTable extends Am_Table_WithData { protected $_key = 'upload_id'; protected $_table = '?_upload'; protected $_recordClass = 'Upload'; const STORE_FIELD = 'field'; const STORE_SERIALIZE = 'serialize'; const STORE_IMPLODE = 'implode'; const STORE_DATA_BLOB_SERIALIZE = 'data-serialize'; protected $_usage = array(); protected $_root; public function init() { parent::init(); $this->_root = Am_Di::getInstance()->data_dir; $this->defineUsage('downloads', 'file', 'path', UploadTable::STORE_FIELD, "Protected File [%title%]", '/default/admin-content/p/files/index?_files_a=edit&_files_id=%file_id%'); $this->defineUsage('emailtemplate', 'email_template', 'attachments', UploadTable::STORE_IMPLODE, "Email Template [%name%, %lang%]", '/admin-setup/email'); $this->defineUsage('email-pending', 'email_template', 'attachments', UploadTable::STORE_IMPLODE, "Pending Notification Template [%email_template_id%]", '/admin-setup/email'); $this->defineUsage('email-messages', 'email_template', 'attachments', UploadTable::STORE_IMPLODE, "Autoresponder or Expiration E-Mail Template", '/default/admin-content/p/emails/index?_emails_a=edit&_emails_id=%email_template_id%'); $this->defineUsage('video', 'video', 'path', UploadTable::STORE_FIELD, "Protected Video [%title%]", '/default/admin-content/p/video/index?_video_a=edit&_video_id=%video_id%'); $this->defineUsage('email', 'email_sent', 'files', UploadTable::STORE_IMPLODE, "Sent email [%subject%, %desc_users%]", '/admin-email'); $this->defineUsage('user_note', 'helpdesk_note', 'attachments', UploadTable::STORE_IMPLODE, "User Note [%user_id%]", '/admin-user-note/index/user_id/%user_id%'); } function findByDelete($tm) { return $this->selectObjects("SELECT * FROM ?_upload WHERE `delete`>0 AND `delete`selectObjects( "SELECT * FROM ?_upload WHERE upload_id IN (?a) { AND prefix = ?}", $ids, $prefix === null ? DBSIMPLE_SKIP : $prefix); } function cleanUp() { $time = $this->getDi()->time; foreach ($this->findByDelete($time) as $upload) { try { if ($upload->isInsideStoreFolder()) $upload->delete(); } catch (Am_Exception_InternalError $e) { $this->getDi()->errorLogTable->logException($e); } } } /** * set store folder * @param string $dir */ function setRoot($dir) { $this->_root = $dir; return $this; } function getRoot() { return $this->_root; } /** * * @param string $prefix * @param string $table * @param string $field * @param string $store_type enum(self::STORE_FIELD, STORE_SERIALIZE, STORE_IMPLODE, STORE_DATA_BLOB_SERIALIZE) * @param string $msg usage description, %feld_name% placeholders expanded * @param string $link relative link to page where usage can be found, %feld_name% placeholders expanded */ public function defineUsage($prefix, $table, $field, $store_type, $msg, $link='') { $this->_usage[$prefix][] = array ( 'table' => $table, 'field' => $field, 'store_type' => $store_type, 'msg' => $msg, 'link' => $link ); } /** * * @param Upload $upload * @return array [(title, link)] */ public function findUsage(Upload $upload) { $res = array(); foreach ($this->_usage[$upload->prefix] as $u) { $res = array_merge($res, $this->_checkUsage($upload, $u)); } return $res; } protected function _checkUsage(Upload $upload, $usage) { $rows = array(); switch ($usage['store_type']) { case self::STORE_FIELD: $rows = $this->getDi()->db->query("SELECT * FROM ?_{$usage['table']} WHERE ?#=?", $usage['field'], $upload->pk()); break; case self::STORE_SERIALIZE: $len = strlen($upload->pk()); $rows = $this->getDi()->db->query("SELECT * FROM ?_{$usage['table']} WHERE (?# LIKE ? OR ?# LIKE ?)", $usage['field'], '%i:' . $upload->pk() . ';%', $usage['field'], '%s:' . $len . ':"' . $upload->pk() . '";%'); break; case self::STORE_IMPLODE: $rows = $this->getDi()->db->query("SELECT * FROM ?_{$usage['table']} WHERE ?#=? OR ?# LIKE ? OR ?# LIKE ? OR ?# LIKE ?", $usage['field'], $upload->pk(), $usage['field'], $upload->pk() . ',%', $usage['field'], '%,' . $upload->pk(), $usage['field'], '%,' . $upload->pk() . ',%'); break; case self::STORE_DATA_BLOB_SERIALIZE : $len = strlen($upload->pk()); $tbl = $usage['table'] . 'Table'; $keyField = $this->getDi()->{$tbl}->getKeyField(); $rows = $this->getDi()->db->query("SELECT t.* FROM ?_{$usage['table']} t LEFT JOIN ?_data d ON d.`table` = ? AND d.`id`=t.?# WHERE d.`key`=? AND (d.`blob` LIKE ? OR d.`blob` LIKE ?)", $usage['table'], $keyField, $usage['field'], '%i:' . $upload->pk() . ';%', '%s:' . $len . ':"' . $upload->pk() . '";%'); break; } if (empty($rows)) return array(); $result = array(); foreach ($rows as $row) { $result[] = array( 'title' => $this->_expandPlaceholders($usage['msg'], $row), 'link' => $this->_expandPlaceholders($usage['link'], $row) ); } return $result; } protected function _expandPlaceholders($str, $vars) { foreach ($vars as $k => $val) { $str = str_replace('%' . $k . '%', $val, $str); } return $str; } } PK"\F"pp Admin.phpnu[name_f . ' ' . $this->name_l; } public function checkPassword($pass) { $ph = new PasswordHash(8, true); return $pass && $ph->CheckPassword($pass, $this->pass); } public function setPass($pass) { $ph = new PasswordHash(12, true); $this->pass = $ph->HashPassword($pass); } public function hasPermission($perm, $priv = null) { if ($this->isSuper()) return true; if (empty($this->perms)) return false; $perms = $this->getPermissions(); if (empty($perms[$perm])) return false; if ($priv === null) return true; if (is_array($perms[$perm]) && empty($perms[$perm][$priv])) return false; return true; } public function checkPermission($perm, $priv = null) { if (!$this->hasPermission($perm, $priv)) throw new Am_Exception_AccessDenied(___("You have no permissions to perform requested operation")); } public function isAllowed($role = null, $resource = null, $privilege = null) { return $this->hasPermission($resource, $privilege); } public function setPermissions($perms) { $this->perms = json_encode($perms); $this->_perms = null; } public function getPermissions() { return json_decode($this->get('perms'), true); } public function setPreferences(array $prefs) { $this->prefs = serialize($prefs); } public function getPreferences() { return unserialize($this->prefs); } public function getPref($prefId, $default = null) { $prefs = $this->getPreferences(); return (isset($prefs[$prefId]) ? $prefs[$prefId] : $default); } public function setPref($prefId, $value) { $prefs = $this->getPreferences(); $prefs[$prefId] = $value; $this->setPreferences($prefs); $this->save(); } public function isSuper() { return (bool) $this->super_user; } public function delete() { parent::delete(); $this->getDi()->hook->call(Am_Event::ADMIN_AFTER_DELETE, array('admin'=>$this)); } } class AdminTable extends Am_Table_WithData { protected $_key = 'admin_id'; protected $_table = '?_admin'; protected $_recordClass = 'Admin'; function getAuthenticatedRow($login, $pass, & $code) { if (empty($login) || empty($pass)) { $code = Am_Auth_Result::INVALID_INPUT; return; } $u = $this->findFirstByLogin($login); if (!$u) { $code = Am_Auth_Result::USER_NOT_FOUND; return; } if (!$u->checkPassword($pass)) { $code = Am_Auth_Result::WRONG_CREDENTIALS; return; } $code = Am_Auth_Result::SUCCESS; return $u; } } PK"\\<\ FileDownload.phpnu[download_limit) return true; //no limits at all list($limit, $period) = explode(':', $file->download_limit); $cond = array( 'user_id' => $user->pk(), 'file_id' => $file->pk() ); if ($period != self::PERIOD_ALL) { $begin_date = $this->getDi()->dateTime; $begin_date->modify('-' . $period . ' seconds'); $begin_date = $begin_date->format('Y-m-d H:i:s'); $cond['dattm'] = '>' . $begin_date; } $count = $this->countBy($cond); return $limit > $count; } function logDownload(User $user, File $file, $ip = '') { $begin_date = $this->getDi()->dateTime; $begin_date->modify('-' . self::TOLERANCE . ' seconds'); $begin_date = $begin_date->format('Y-m-d H:i:s'); $count = $this->countBy(array( 'user_id' => $user->pk(), 'file_id' => $file->pk(), 'dattm' => '>' . $begin_date )); if (!$count) { $this->insert(array( 'user_id' => $user->pk(), 'file_id' => $file->pk(), 'dattm' => $this->getDi()->sqlDateTime, 'remote_addr' => $ip )); } } function selectLast($num, $dateThreshold = null) { return $this->selectObjects("SELECT t.dattm, f.title, f.file_id, t.user_id, u.login, u.email, CONCAT(u.name_f, ' ', u.name_l) AS name, u.name_f, u.name_l FROM ?_file_download t LEFT JOIN ?_user u ON t.user_id = u.user_id LEFT JOIN ?_file f ON t.file_id = f.file_id {WHERE t.dattm > ?} ORDER BY t.dattm DESC LIMIT ?d", $dateThreshold ?: DBSIMPLE_SKIP, $num); } }PK"\Integration.phpnu[getDi()->resourceAccessTable ->getAllowedResources($user, ResourceAccess::INTEGRATION) as $r) if ($r->plugin == $pluginId) $ret[] = $r; return $ret; } } PK"\  ProductCategory.phpnu[_childNodes; } function createChildNode() { $c = new self($this->getTable()); $c->parent_id = $this->pk(); if (!$c->parent_id) throw new Am_Exception_InternalError("Could not add child node to not-saved object in ".__METHOD__); $this->_childNodes[] = $c; return $c; } public function fromRow(array $vars) { if (isset($vars['childNodes'])) { foreach ($vars['childNodes'] as $row) { $r = new self($this->getTable()); $r->fromRow($row); $this->_childNodes[] = $r; } unset($vars['childNodes']); } return parent::fromRow($vars); } } class ProductCategoryTable extends Am_Table { protected $_key = 'product_category_id'; protected $_table = '?_product_category'; protected $_recordClass = 'ProductCategory'; const COUNT = 'count'; const ROOT = 'root'; /** * Do not include categories that do not have products. */ const EXCLUDE_EMPTY = 'exclude_empty'; /** * Do not include hidden categories. */ const EXCLUDE_HIDDEN = 'exclude_hidden'; /** * array of ctegory codes to include to list */ const INCLUDE_HIDDEN = 'include_hidden'; const ADMIN = 1; const USER = 2; protected $categoryProductsCache = null; /** * @todo protect against endless cycle (child[parent_id] <-> parent[parent_id]) * @return ProductCategory */ function getTree($cast_objects = true) { $ret = array(); foreach ($this->_db->select("SELECT product_category_id AS ARRAY_KEY, parent_id AS PARENT_KEY, pc.* FROM ?_product_category AS pc ORDER BY 0+sort_order") as $r) { $ret[] = $cast_objects ? $this->createRecord($r) : $r; } return $ret; } function getUserSelectOptions($options = array()) { return $this->getSelectOptions(self::USER, $options); } function getAdminSelectOptions($options = array()) { return $this->getSelectOptions(self::ADMIN, $options); } function getSelectOptions($type, array $options) { $ret = array(); $do_count = !empty($options[self::COUNT]); $exclude_empty = !empty($options[self::EXCLUDE_EMPTY]); $exclude_hidden = !empty($options[self::EXCLUDE_HIDDEN]); $include_hidden = isset($options[self::INCLUDE_HIDDEN]) ? $options[self::INCLUDE_HIDDEN] : array(); $root = (isset($options[self::ROOT]) && $options[self::ROOT]) ? $options[self::ROOT] : false; $exclude_disabled = $type == self::USER ? true : false; if ($do_count || $exclude_empty) $sql = "SELECT pc.product_category_id AS ARRAY_KEY, pc.parent_id AS PARENT_KEY, pc.product_category_id, pc.title, pc.code, COUNT(DISTINCT p.product_id) as `count` FROM ?_product_category pc LEFT JOIN ?_product_product_category ppc USING (product_category_id) LEFT JOIN ?_product p ON p.product_id = ppc.product_id AND p.is_archived=0 ".($exclude_disabled ? " AND p.is_disabled=0 " : "")." GROUP BY pc.product_category_id ORDER BY parent_id, 0+pc.sort_order"; else $sql = "SELECT product_category_id AS ARRAY_KEY, parent_id AS PARENT_KEY, product_category_id, title, code FROM ?_product_category ORDER BY parent_id, 0+sort_order"; $rows = $this->_db->select($sql); if ($root) { $rows = $this->extractSubtree($rows, $root); } foreach ($rows as $id => $r){ $this->renderNode($r, $id, $type, $options, '', $ret); } return $ret; } function getOptions() { return $this->getAdminSelectOptions(); } function getSubCategoryIds($cat_id) { $res = array(); $childNodes = $this->extractSubtree($this->getTree(false), $cat_id); while($node = array_shift($childNodes)) { array_push($res, $node['product_category_id']); foreach ($node['childNodes'] as $n) { array_push($childNodes, $n); } } return $res; } protected function extractSubtree($rows, $root) { $childNodes = $rows; while($node = array_shift($childNodes)) { if ($node['product_category_id'] == $root) return $node['childNodes']; foreach ($node['childNodes'] as $n) { array_push($childNodes, $n); } } return array(); } protected function renderNode($r, $id, $type, array $options, $title, &$ret) { $do_count = !empty($options[self::COUNT]); $exclude_empty = !empty($options[self::EXCLUDE_EMPTY]); $exclude_hidden = !empty($options[self::EXCLUDE_HIDDEN]); $include_hidden = isset($options[self::INCLUDE_HIDDEN]) ? $options[self::INCLUDE_HIDDEN] : array(); $exclude_disabled = self::USER ? true : false; $title .= ($title ? '/' : '') . $r['title']; $ctitle = $title; if($exclude_empty && !$r['count']) { foreach($r['childNodes'] as $cid => $c) $this->renderNode($c, $cid, $type, $options, $ctitle, $ret); return; } if($exclude_hidden && !(empty($r['code']) || in_array($r['code'], $include_hidden))) //assume to do not show child if parent is hidden return; if ($do_count) $title .= sprintf(' (%d)', $r['count']); $ret [ (($type == self::ADMIN) || !$r['code'] ? $id : $r['code']) ] = $title; foreach($r['childNodes'] as $cid => $c) $this->renderNode($c, $cid, $type, $options, $ctitle, $ret); } /** * Try to find by code and by id * if found by id, checks that code is empty */ function findByCodeThenId($codeOrId) { $r = $this->selectObjects("SELECT * FROM ?_product_category WHERE code=? OR (IFNULL(code,'')='' AND product_category_id=?) ORDER BY code=? DESC LIMIT 1", $codeOrId, $codeOrId, $codeOrId); if (!$r) return; $record = $r[0]; if ($record->code && ($record->code != $codeOrId)) throw new Am_Exception_InputError("Category #$record->product_category_id could not be accessed by id#"); return $record; } function moveNodes($fromId, $toId) { $this->_db->query("UPDATE {$this->_table} SET parent_id=?d WHERE parent_id=?d", $toId, $fromId); } public function delete($key) { parent::delete($key); $this->_db->query("DELETE FROM ?_product_product_category WHERE product_category_id=?d", $key); } /** * @return array { category_id => [product_id1, product_id2, ...] } */ public function getCategoryProducts($useCache = true) { if (!$useCache || ($this->categoryProductsCache === null)) { $this->categoryProductsCache = $this->_db->selectCol(" SELECT pc.product_category_id as ARRAY_KEY, GROUP_CONCAT(ppc.product_id) FROM ?_product_category AS pc LEFT JOIN ?_product_product_category AS ppc USING (product_category_id) GROUP BY pc.product_category_id"); foreach ($this->categoryProductsCache as & $prList) $prList = array_filter(explode(',', $prList)); } return $this->categoryProductsCache; } } PK"\37jjBillingPlan.phpnu[currency)) $this->currency = Am_Currency::getDefault(); } /** @return Product */ public function getProduct() { if ($this->_product && $this->_product->product_id == $this->product_id) return $this->_product; $this->_product = $this->getDi()->productTable->load($this->product_id, true); $this->_product->setBillingPlan($this); return $this->_product; } public function _setProduct(Product $p) { $this->_product = $p; } /** @return string saved or caclulated billing terms */ public function getTerms() { return $this->terms ? $this->terms : $this->getTermsText(); } /** @return string caclulated billing terms */ public function getTermsText() { $tt = new Am_TermsText($this); return $tt->getString(); } public function getCurrency($value = null) { $c = new Am_Currency($this->currency); if ($value) $c->setValue($value); return $c; } public function isFree() { return !$this->first_price && !$this->second_price; } public function delete() { parent::delete(); $this->_table->getAdapter()->query("DELETE FROM ?_product_upgrade WHERE to_billing_plan_id=?d OR from_billing_plan_id=?d", $this->plan_id, $this->plan_id); } } /** * @package Am_Invoice */ class BillingPlanTable extends Am_Table_WithData { protected $_key = 'plan_id'; protected $_table = '?_billing_plan'; protected $_recordClass = 'BillingPlan'; protected $productCache = array(); protected $useProductCache = true; // disable in admin cp function toggleProductCache($flag) { $this->useProductCache = (bool)$flag; } function getForProduct($product_id, $limit = null, $onlyEnabled = false) { if ($this->useProductCache) { $key = "$product_id-$limit-$onlyEnabled"; if (array_key_exists($key, $this->productCache)) return $this->productCache[$key]; } $ret = $this->selectObjects( "SELECT * FROM ?_billing_plan WHERE product_id=?d {AND (disabled IS NULL OR disabled = ?d )} ORDER BY sort_order, plan_id {LIMIT ?d}", $product_id, !$onlyEnabled ? DBSIMPLE_SKIP : 0, $limit === null ? DBSIMPLE_SKIP : $limit ); if ($this->useProductCache) $this->productCache[$key] = $ret; return $ret; } /** * Select All billing plans respecting sort preferencies from aMember CP -> Manage products */ function selectAllSorted() { return $this->selectObjects("SELECT bp.*, p.title as product_title FROM $this->_table bp INNER JOIN ?_product p USING (product_id) WHERE p.is_archived=0 ORDER BY 0+p.sort_order,p.title"); } /** * return array of product_id-billing_plan_id => Product Title (billing plan terms) */ public function getProductPlanOptions() { $ret = array(); foreach ($this->selectAllSorted() as $p) { $ret[ $p->product_id . '-' . $p->plan_id ] = $p->product_title . ' (' . $p->getTerms() . ')'; } return $ret; } }PK"\ںEmailTemplateLayout.phpnu[getAdapter()->query('UPDATE ?_email_template SET email_template_layout_id = NULL WHERE email_template_layout_id=?', $this->pk()); return parent::delete(); } } class EmailTemplateLayoutTable extends Am_Table { protected $_key = 'email_template_layout_id'; protected $_table = '?_email_template_layout'; protected $_recordClass = 'EmailTemplateLayout'; function getOptions() { return $this->_db->selectCol("SELECT email_template_layout_id as ARRAY_KEY, name FROM ?_email_template_layout ORDER BY name"); } }PK"\9InvoicePayment.phpnu[getInvoice()->first_total == 0) return false; // if there is free trial return ! $this->getAdapter()->selectCell(" SELECT COUNT(*) FROM ?_invoice_payment WHERE invoice_id=?d AND invoice_payment_id <> ?d AND dattm <= ? AND invoice_payment_id < ?d", $this->invoice_id, $this->invoice_payment_id, $this->dattm, $this->invoice_payment_id); } public function setFromTransaction(Invoice $invoice, Am_Paysystem_Transaction_Interface $transaction) { $this->dattm = $transaction->getTime()->format('Y-m-d H:i:s'); $this->invoice_id = $invoice->invoice_id; $this->invoice_public_id = $invoice->public_id; $this->user_id = $invoice->user_id; $this->currency = $invoice->currency; $this->paysys_id = $transaction->getPaysysId(); $this->receipt_id = $transaction->getReceiptId(); $this->transaction_id = $transaction->getUniqId(); /// get from invoice $isFirst = ! ((doubleval($invoice->first_total) === 0.0) || $invoice->getPaymentsCount()); $amount = $transaction->getAmount(); if ($amount<=0) $amount = $isFirst ? $invoice->first_total : $invoice->second_total; $this->amount = (double)$amount; $this->discount = $isFirst ? $invoice->first_discount : $invoice->second_discount; $this->tax = $isFirst ? $invoice->first_tax : $invoice->second_tax; $this->shipping = $isFirst ? $invoice->first_shipping : $invoice->second_shipping; return $this; } public function fromRow(array $vars) { if (!empty($vars['refund_dattm']) && $vars['refund_dattm'] == '0000-00-00 00:00:00') $vars['refund_dattm'] = null; return parent::fromRow($vars); } public function isRefunded(){ return !is_null($this->refund_dattm); } public function isFullRefunded() { return $this->amount <= $this->refund_amount; } public function refund(InvoiceRefund $r){ $this->updateQuick(array( 'refund_dattm' => $this->refund_dattm ?: $r->dattm, 'refund_amount' => $this->refund_amount + $r->amount )); } public function insert($reload = true) { if ($this->currency == Am_Currency::getDefault()) $this->base_currency_multi = 1.0; else $this->base_currency_multi = $this->getDi()->currencyExchangeTable->getRate($this->currency, sqlDate($this->dattm)); $this->getDi()->hook->call(new Am_Event(Am_Event::PAYMENT_BEFORE_INSERT, array('payment' => $this, 'invoice' => $this->getInvoice(), 'user' => $this->getInvoice()->getUser()))); parent::insert($reload); $this->setDisplayInvoiceId(); $this->getDi()->hook->call(new Am_Event_PaymentAfterInsert(null, array('payment' => $this, 'invoice' => $this->getInvoice(), 'user' => $this->getInvoice()->getUser()))); return $this; } /** * @return InvoiceRefund|null */ public function getRefund(){ if (!$this->isRefunded()) return null; if (empty($this->_refund)) $this->_refund = $this->getDi()->invoiceRefundTable->findFirstBy(array( 'invoice_payment_id' => $this->pk())); return $this->_refund; } /** * @return User */ public function getUser(){ if (empty($this->_user)) $this->_user = $this->getDi()->userTable->load($this->user_id); return $this->_user; } /** * @return Invoice */ public function getInvoice(){ if (empty($this->_invoice)) $this->_invoice = $this->getDi()->invoiceTable->load($this->invoice_id); return $this->_invoice; } public function _setInvoice(Invoice $invoice) { $this->_invoice = $invoice; return $this; } /** * @return Am_Currency */ function getCurrency($value = null) { $c = new Am_Currency($this->currency); if ($value) $c->setValue($value); return $c; } /** * * Set Invoice ID wich will be displayed in pdf invoice */ protected function setDisplayInvoiceId() { $e = new Am_Event(Am_Event::SET_DISPLAY_INVOICE_PAYMENT_ID,array('record'=>$this)); $e->setReturn($this->getInvoice()->public_id . '/' . $this->getInvoice()->getPaymentsCount()); $this->getDi()->hook->call($e); $this->display_invoice_id = $e->getReturn(); $this->updateSelectedFields('display_invoice_id'); } function getDisplayInvoiceId() { return $this->display_invoice_id ? $this->display_invoice_id : ($this->getInvoice()->public_id . '/' . $this->receipt_id); } } /** * @package Am_Invoice */ class InvoicePaymentTable extends Am_Table_WithData { protected $_key = 'invoice_payment_id'; protected $_table = '?_invoice_payment'; protected $_recordClass = 'InvoicePayment'; function getPaymentsCount($invoiceId){ ///// NEED REAL ATTENTON HERE: Added to handle imported payments correctly. ///// Import3 script create payments with zero amount, and these payments are counted as real payments ///// so updateRebillDate does not work if imported invoice have free trial period. return $this->_db->selectCell("SELECT COUNT(*) FROM ?_invoice_payment WHERE invoice_id=?d and amount>0", $invoiceId); } /** @return string */ function getLastReceiptId($invoiceId) { return $this->_db->selectCell("SELECT receipt_id FROM ?_invoice_payment WHERE invoice_id=?d ORDER BY dattm DESC LIMIT 1", $invoiceId); } public function insert(array $values, $returnInserted = false) { if (empty($values['dattm'])) $values['dattm'] = $this->getDi()->sqlDateTime; return parent::insert($values, $returnInserted); } function selectLast($num, $dateThreshold = null) { return $this->selectObjects("SELECT ip.*, refund_amount, refund_dattm, i.coupon_code, (SELECT GROUP_CONCAT(item_title SEPARATOR ', ') FROM ?_invoice_item WHERE invoice_id=ip.invoice_id) AS items, u.login, u.email, CONCAT(u.name_f, ' ', u.name_l) AS name, u.name_f, u.name_l, u.added, ip.invoice_public_id AS public_id FROM ?_invoice_payment ip LEFT JOIN ?_user u USING (user_id) LEFT JOIN ?_invoice i ON ip.invoice_id = i.invoice_id {WHERE ip.dattm > ?} ORDER BY ip.dattm DESC LIMIT ?d", $dateThreshold ?: DBSIMPLE_SKIP, $num); } } PK"\-99 Country.phpnu[status = self::STATUS_ADDED; parent::insert($reload); } function update() { $this->status = get_first($this->status, self::STATUS_CHANGED); parent::update(); } } class CountryTable extends Am_Table { protected $_key = 'country_id'; protected $_table = '?_country'; protected $_recordClass = 'Country'; function sortByTagAndTitle($a, $b) { if($a['tag'] != $b['tag']) return ($a['tag'] > $b['tag'] ? -1 : 1); return strcmp($a['title'], $b['title']); } function getTitleByCode($code) { return $this->_db->selectCell("SELECT title FROM ?_country WHERE country=?", $code); } function getOptions($addEmpty = false) { //if admin show all countries, if user show only active countries $where = defined('AM_ADMIN') ? '' : 'WHERE tag>=0'; $res = $this->_db->select("SELECT country as ARRAY_KEY, CASE WHEN tag<0 THEN CONCAT(title, ' (disabled)') ELSE title END AS title , tag FROM ?_country $where ORDER BY tag DESC, title"); if (strpos($this->getDi()->app->getDefaultLocale(), 'en')!==0) { $tr = $this->getDi()->locale->getTerritoryNames(); foreach ($res as $k => $v) if (array_key_exists($k, $tr)) $res[$k]['title'] = $tr[$k]; } uasort($res, array($this, 'sortByTagAndTitle')); $res = array_map(function($l) {return $l["title"];}, $res); if ($res && $addEmpty) { $res = array_merge(array('' => ___('[Select country]')), $res); } return $res; } }PK"\m Charge.phpnu[amount = $amount; $this->currency = $currency; $this->title = $title; $this->desc = $desc; $this->tax_group = $tax_group; } function getProductId() { return null; } function getType() { return 'charge'; } function getTitle() { return $this->title; } function getDescription() { return $this->desc; } function getFirstPrice() { return $this->amount; } function getFirstPeriod() { return Am_Period::MAX_SQL_DATE; } function getRebillTimes() { return 0; } function getSecondPrice() { return null; } function getSecondPeriod() { return null; } function getCurrencyCode() { return $this->currency; } function getTaxGroup() { return $this->tax_group; } function getIsTangible() { return false; } function getIsCountable() { return false; } function getQty() { return 1; } function getIsVariableQty() { return false; } function getBillingPlanId() { return null; } function getBillingPlanData() { return array(); } public function getOptions() { return null; } public function setOptions(array $options) { //nop } function calculateStartDate($paymentDate, Invoice $invoice) { return $paymentDate; } function findItem(array $existingInvoiceItems) { return null; } function addQty($requestedQty, $itemQty) { return 1; } }PK"\iс>ResourceCategory.phpnu[_childNodes; } function removeChildNode(ResourceCategory $node) { foreach ($this->_childNodes as $k => $n) { if ($n == $node) unset($this->_childNodes[$k]); } } function createChildNode() { $c = new self($this->getTable()); $c->parent_id = $this->pk(); if (!$c->parent_id) throw new Am_Exception_InternalError("Could not add child node to not-saved object in " . __METHOD__); $this->_childNodes[] = $c; return $c; } public function fromRow(array $vars) { if (isset($vars['childNodes'])) { foreach ($vars['childNodes'] as $row) { $r = new self($this->getTable()); $r->fromRow($row); $this->_childNodes[] = $r; } unset($vars['childNodes']); } return parent::fromRow($vars); } function getAllowedResources(User $user) { $res = array(); foreach ($this->getDi()->resourceAccessTable->getAllowedResources($user, ResourceAccess::USER_VISIBLE_TYPES) as $r) { if (in_array($this->pk(), $r->getCategories())) { $res[] = $r; } } return $res; } function _getAllowedResources(User $user, $resources) { $res = array(); foreach ($resources as $r) { if (isset($r->_categories[$this->pk()])) { $res[] = $r; } } return $res; } } class ResourceCategoryTable extends Am_Table { protected $_key = 'resource_category_id'; protected $_table = '?_resource_category'; protected $_recordClass = 'ResourceCategory'; const COUNT = 'count'; /** * Do not include categories that do not have products. */ const EXCLUDE_EMPTY = 'exclude_empty'; /** * @todo protect against endless cycle (child[parent_id] <-> parent[parent_id]) * @return ResourceCategory */ function getTree($cast_objects = true) { $ret = array(); foreach ($this->_db->select("SELECT resource_category_id AS ARRAY_KEY, parent_id AS PARENT_KEY, pc.* FROM ?_resource_category AS pc ORDER BY 0+sort_order") as $r) { $ret[] = $cast_objects ? $this->createRecord($r) : $r; } return $ret; } /** * retrieve category tree with access check * skip empty branches * * @param User $user */ function getAllowedTree(User $user) { $tree = $this->getTree(true); $this->_resources = $this->getDi()->resourceAccessTable->getAllowedResources($user, ResourceAccess::USER_VISIBLE_TYPES); $rc = $this->getAdapter()->selectCol("SELECT GROUP_CONCAT(resource_category_id), CONCAT(resource_id, '-', resource_type) AS ARRAY_KEY " . "FROM ?_resource_resource_category GROUP BY ARRAY_KEY;"); foreach($this->_resources as $k => $r) { $this->_resources[$k]->_categories = isset($rc[$r->pk() . '-' . $r->getAccessType()]) ? array_combine($_ = explode(',', $rc[$r->pk() . '-' . $r->getAccessType()]), $_) : array(); } foreach ($tree as $k => $node) { $node->cnt = $this->_calcCntAndFilterEmpty($node, $user); if (!$node->cnt) unset($tree[$k]); } return $tree; } protected function _calcCntAndFilterEmpty(ResourceCategory $node, User $user) { $res = $node->self_cnt = count($node->_getAllowedResources($user,$this->_resources)); foreach ($node->getChildNodes() as $n) { $n->cnt = $this->_calcCntAndFilterEmpty($n, $user); if (!$n->cnt) $node->removeChildNode($n); $res += $n->cnt; } return $res; } function getOptions() { $ret = array(); $rows = $this->_db->select("SELECT resource_category_id AS ARRAY_KEY, parent_id AS PARENT_KEY, resource_category_id, title FROM ?_resource_category ORDER BY parent_id, 0+sort_order"); foreach ($rows as $id => $r) { $this->renderNode($r, $id, '', array(), '', $ret); } return $ret; } protected function renderNode($r, $id, $type, array $options, $title, &$ret) { $do_count = !empty($options[self::COUNT]); $exclude_empty = !empty($options[self::EXCLUDE_EMPTY]); $title .= ( $title ? '/' : '') . $r['title']; $ctitle = $title; if ($exclude_empty && !$r['count']) { foreach ($r['childNodes'] as $cid => $c) $this->renderNode($c, $cid, $type, $options, $ctitle, $ret); return; } if ($do_count) $title .= sprintf(' (%d)', $r['count']); $ret[$id] = $title; foreach ($r['childNodes'] as $cid => $c) $this->renderNode($c, $cid, $type, $options, $ctitle, $ret); } function moveNodes($fromId, $toId) { $this->_db->query("UPDATE {$this->_table} SET parent_id=?d WHERE parent_id=?d", $toId, $fromId); } public function delete($key) { parent::delete($key); $this->_db->query("DELETE FROM ?_resource_resource_category WHERE resource_category_id=?d", $key); } } PK"\<** SavedForm.phpnu[getTable()->getTypeDef($this->type); } public function isSignup() { $typeDef = $this->getTypeDef(); return isset($typeDef['isSignup']) && $typeDef['isSignup']; } public function isCart() { return $this->type == self::T_CART; } public function isProfile() { return $this->type == self::T_PROFILE; } public function getDefaultFor() { return array_filter(explode(',',$this->default_for)); } public function setDefaultFor(array $values) { return $this->default_for = implode(',', array_unique(array_filter(array_map('filterId', $values)))); } public function addDefaultFor($d) { $def = $this->getDefaultFor(); $def[] = $d; $this->setDefaultFor($def); return $this; } public function delDefaultFor($d) { $def = $this->getDefaultFor(); array_remove_value($def, $d); $this->setDefaultFor($def); return $this; } public function isDefault($dConst) { return in_array($dConst, $this->getDefaultFor()); } public function getUrl($baseUrl) { $type = $this->getTypeDef(); if (empty($type['urlTemplate'])) return; if (is_array($type['urlTemplate'])) $add = call_user_func($type['urlTemplate'], $this); else $add = $type['urlTemplate']; return $baseUrl . $add; } /** @return Am_Form_Bricked */ public function createForm() { $type = $this->getTypeDef(); if (!$type['class']) throw new Am_Exception("Could not instantiate form - empty class in typeDef"); return new $type['class']; } public function generateCode() { do { $this->code = $this->getDi()->security->randomString(rand(8,9)); } while ($this->getTable()->findFirstByCode($this->code)); } function getFields() { return (array)json_decode($this->fields, true); } function setFields(array $fields) { $this->fields = json_encode($fields); } /** @return array of Am_Form_Brick */ function getBricks() { static $ret = null; if (is_null($ret)) { $ret = array(); foreach ($this->getFields() as $brickConfig) { if (strpos($brickConfig['id'],'PageSeparator')===0) continue; $b = Am_Form_Brick::createFromRecord($brickConfig); if (!$b) continue; $ret[] = $b; } $event = new Am_Event(Am_Event::SAVED_FORM_GET_BRICKS, array( 'type' => $this->type, 'code' => $this->code, 'savedForm' => $this )); $event->setReturn($ret); $this->getDi()->hook->call($event); $ret = $event->getReturn(); foreach ($ret as $brick) $brick->init(); } return $ret; } function isSingle() { $typeDef = $this->getTypeDef(); return isset($typeDef['isSingle']) && $typeDef['isSingle']; } function canDelete() { if (!$this->type) return true; $typeDef = $this->getTypeDef(); if (!empty($typeDef['noDelete'])) return false; if (!empty($this->default_for)) return false; return true; } public function setDefaultBricks() { $value = array(); foreach ($this->createForm()->getDefaultBricks() as $brick) $value[] = $brick->getRecord(); $this->setFields($value); return $this; } public function setDefaults() { if (empty($this->type)) throw new Am_Exception_InternalError("Error in ".__METHOD__." could not set defaults without type"); $typeDef = $this->getTypeDef(); if (!empty($typeDef['generateCode']) && empty($this->code)) $this->generateCode(); if (empty($this->title) && !empty($typeDef['defaultTitle'])) $this->title = $typeDef['defaultTitle']; if (empty($this->comment) && !empty($typeDef['defaultComment'])) $this->comment = $typeDef['defaultComment']; $this->setDefaultBricks(); return $this; } function findBrickConfigs($class, $id = null) { $ret = array(); foreach ($this->getFields() as $row) { if (empty($row['class']) || ($row['class'] != $class)) continue; if (($id === null) || ($row['id'] == $id)) $ret[] = $row; } return $ret; } function addBrickConfig($row) { $fields = $this->getFields(); $fields[] = $row; $this->setFields($fields); return $this; } function removeBrickConfig($class, $id = null) { $fields = $this->getFields(); $count = 0; foreach ($fields as $k => $row) { if ($row['class'] != $class) continue; if (($id !== null) && ($row['id'] == $id)) { $count++; unset($fields[$k]); } } if ($count) $this->setFields($fields); return $this; } /** * @return array|null */ function findBrickById($id) { $fields = $this->getFields(); foreach ($fields as $k => $row) { if (($id !== null) && ($row['id'] == $id)) { return $row; } } } public function insert($reload = true) { $table_name = $this->getTable()->getName(); $max = $this->getAdapter()->selectCell("SELECT MAX(sort_order) FROM {$table_name}"); $this->sort_order = $max + 1; return parent::insert($reload); } } /** * @package Am_SavedForm */ class SavedFormTable extends Am_Table { protected $_key = 'saved_form_id'; protected $_table = '?_saved_form'; protected $_recordClass = 'SavedForm'; protected $_eventCalled = false; private $types = array( SavedForm::T_SIGNUP => array( 'type' => SavedForm::T_SIGNUP, 'class' => 'Am_Form_Signup', 'title' => 'Signup Form', 'defaultTitle' => 'Signup Form', 'defaultComment' => 'customer signup/payment form', 'isSignup' => true, 'generateCode' => true, 'urlTemplate' => array('Am_Form_Signup', 'getSavedFormUrl'), ), SavedForm::T_PROFILE => array( 'type' => SavedForm::T_PROFILE, 'class' => 'Am_Form_Profile', 'title' => 'Profile Form', 'defaultTitle' => 'Customer Profile', 'defaultComment' => 'customer profile form', 'isSingle' => false, 'isSignup' => false, 'generateCode' => true, 'urlTemplate' => array('Am_Form_Profile', 'getSavedFormUrl'), ), ); public function getOptions($type = null) { return $this->_db->selectCol("SELECT saved_form_id as ARRAY_KEY, concat(title, ' (', comment, ')') FROM {$this->_table} {WHERE type=?} ORDER BY title", $type ? $type : DBSIMPLE_SKIP); } /** * @param int $d_const one from SavedForm::D_xxx constant * @return SavedForm|null */ function getDefault($dConst) { $row = $this->_db->selectRow(" SELECT * FROM ?_saved_form WHERE FIND_IN_SET(?, default_for) LIMIT 1", $dConst); if ($row) return $this->createRecord($row); } /** * @param string $type * @return SavedForm */ function getByType($type) { return $this->findFirstByType($type); } function addTypeDef(array $typeDef) { $this->types[$typeDef['type']] = $typeDef; return $this; } /** @return array typedef * @throws exception if not found */ function getTypeDef($type) { $this->runEventOnce(); if (empty($this->types[$type])) throw new Am_Exception_InternalError("Could not get typeDef for type [$type]"); return $this->types[$type]; } final public function getTypeDefs() { $this->runEventOnce(); return $this->types; } protected function runEventOnce() { if (!$this->_eventCalled) { $this->_eventCalled = true; $this->getDi()->hook->call(Am_Event::SAVED_FORM_TYPES, array('table' => $this)); } } function getTypeOptions() { foreach ($this->getTypeDefs() as $t) $ret[$t['type']] = $t['title']; return $ret; } function setDefault($d, $saved_form_id) { switch ($d) { case SavedForm::D_SIGNUP: case SavedForm::D_MEMBER: case SavedForm::D_PROFILE: $f = $this->load($saved_form_id); if ($f->type != SavedForm::T_SIGNUP && $f->type != SavedForm::T_PROFILE) throw new Am_Exception_InputError("Could not set default form - it has no 'signup' or 'profile' type"); $f->addDefaultFor($d)->update(); // now remove default from all other forms, there should not be too many foreach ($this->selectObjects(" SELECT * FROM ?_saved_form WHERE saved_form_id<>?d AND FIND_IN_SET(?, default_for)", $saved_form_id, $d) as $f) $f->delDefaultFor($d)->update(); break; default: throw new Am_Exception_InputError("Could not ".__METHOD__." for " . $d->$saved_form_id); } } public function getExistingTypes() { return $this->_db->selectCol("SELECT DISTINCT `type` FROM ?_saved_form WHERE type <> ''"); } } PK"\c:eFile.phpnu[getDi()->url("content/f/id/{$this->file_id}", false); } public function renderLink() { list($type, $subtype) = explode('/', $this->mime); $prefix = "am-resource-" . $this->getTable()->getAccessType(); return preg_replace('/(class="[-_a-zA-Z]*?)(")/', "$1 $prefix-$type $prefix-$type-$subtype$2", parent::renderLink()); } } class FileTable extends ResourceAbstractTable { protected $_key = 'file_id'; protected $_table = '?_file'; protected $_recordClass = 'File'; public function getAccessType() { return ResourceAccess::FILE; } public function getAccessTitle() { return ___('Files'); } public function getPageId() { return 'files'; } }PK"\ b:ȳ ErrorLog.phpnu[_db->query("DELETE FROM ?_error_log WHERE `time` < ? ", $date . ' 00:00:00'); } static function backtraceToString(array $trace) { $out = ""; foreach ($trace as $t) { if (isset($t['class'])) { $out .= $t['class'] . $t['type'] . $t['function']; } else { $out .= $t['function']; } if (!empty($t['file'])) { $file = str_replace(@ROOT_DIR . DIRECTORY_SEPARATOR, '', $t['file']); $out .= " [ $file"; if (!empty($t['line'])) $out .= " : {$t['line']}"; $out .= " ]"; } $out .= "\n"; } return $out; } function logException($e) { $error = $e->getMessage(); if (defined('AM_TEST') && AM_TEST) return false; $values = array( 'time' => $this->getDi()->sqlDateTime, 'user_id' => $this->getDi()->auth->getUserId(), 'remote_addr' => @$_SERVER['REMOTE_ADDR'], 'url' => htmlentities(@$_SERVER['REQUEST_URI']), 'referrer' => htmlentities(@$_SERVER['HTTP_REFERER']), 'error' => $error, 'trace' => "Exception " . get_class($e) . PHP_EOL . self::backtraceToString($e->getTrace()) ); if ($e instanceof Am_Exception_Db) { @$this->insert($values, false); } else { $this->insert($values, false); } } /** * Log a message * @param string $error */ function log($error) { if ($error instanceof Exception) return $this->logException($error); if (defined('AM_TEST') && AM_TEST) return false; if (!$this->getDi()->getService('db') || !@$this->getDi()->db || !$this->getDi()->db->_isConnected()) return; $trace = debug_backtrace(false); array_shift($trace); // remove ErrorLog->log entry array_shift($trace); // remove ErrorLog->log entry try { // necessary to avoid errors if init is not finished $user_id = $this->getDi()->auth->getUserId(); } catch (Exception $e) { $user_id = null; } $values = array( 'time' => $this->getDi()->sqlDateTime, 'user_id' => $user_id, 'remote_addr' => @$_SERVER['REMOTE_ADDR'], 'url' => htmlentities(@$_SERVER['REQUEST_URI']), 'referrer' => htmlentities(@$_SERVER['HTTP_REFERER']), 'error' => $error, 'trace' => self::backtraceToString($trace) ); $this->insert($values, false); } }PK"\ugg Product.phpnu[getBillingPlan()->rebill_times; } /** * Run htmlspecialchars(strip_tags()) for the string * It is useful for strings that may contain html entities but we would * not want to see it here, for example: product title or description * @param string string to escape * @return string escaped string */ static function stripEscape($string){ return htmlspecialchars(strip_tags($string), null, 'UTF-8', false); } /** * Return title of product */ function getTitle($escaped=true){ $title = ___($this->title); return $escaped ? $this->stripEscape($title) : str_ireplace('