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 Am/ShoppingCart.php000064400000004203152101601610010203 0ustar00invoice = $invoice; } function addItem($product, $qty = 1, $options = array()) { $this->invoice->add($product, $qty, $options); } function deleteItem($product) { if ($item = $this->getInvoice()->findItem('product', $product->pk())) $this->getInvoice()->deleteItem($item); } /** @return Invoice */ function getInvoice() { return $this->invoice; } /** * @return array of InvoiceItem */ function getItems() { return $this->invoice->getItems(); } /** * @return Am_Currency */ function getCurrency($amount) { return $this->invoice->getCurrency($amount); } function hasItem($product) { foreach($this->getItems() as $item) { if ($item->item_id == $product->pk()) return true; } return false; } function getItem($product) { foreach($this->getItems() as $item) { if ($item->item_id == $product->pk()) return $item; } return null; } function getText() { $items = $this->invoice->getItems(); if (!$this->invoice->getItems()) return ___('You have no items in shopping cart'); $c = count($items); return ___('You have %d items in shopping cart', $c); } /** * @param string $code * @return null|string null if ok, or error message */ function setCouponCode($code) { $this->invoice->setCouponCode($code); $errors = $this->invoice->validateCoupon(); if($errors) $this->invoice->setCouponCode (null); return $errors; } function getCouponCode() { $coupon = $this->invoice->getCoupon(); if ($coupon) return $coupon->code; } function setUser(User $user) { $this->invoice->setUser($user); } function calculate() { $this->invoice->calculate(); } function clear() { $this->invoice = null; } }Am/Form/Signup/Cart.php000064400000001225152101601610010644 0ustar00 $brick) { if (in_array($brick->getClass(), array('product', 'paysystem', 'coupon'))) unset($ret[$k]); } return $ret; } public function isHideBricks() { return false; } } SetupForms.php000064400000003637152101601610007366 0ustar00setTitle(___('Forms')); } function initElements() { $this->addUpload('form.logo_id', array('id'=>'form-logo'), array('prefix' => Bootstrap_Form::ADMIN_UPLOAD_PREFIX)) ->setLabel(___("PDF Logo\n" . "it must be png/jpeg/tiff file")) ->setAllowedMimeTypes(array( 'image/png', 'image/jpeg', 'image/tiff' )); $this->addElement('email_link', 'form.submit') ->setLabel(___('New Form Submit to Admin')); $this->addElement('email_link', 'form.completed_invoice') ->setLabel(___('Completed: New Invoice to User')); $this->addElement('email_link', 'form.approved_invoice') ->setLabel(___('Approved: New Invoice to User')); $this->addElement('email_checkbox', 'form.verified') ->setLabel(___('Verified Notification to User')); $this->addElement('email_checkbox', 'form.verified_admin') ->setLabel(___('Verified Notification to Admin')); $this->addElement('email_checkbox', 'form.approved') ->setLabel(___('Approved Notification to User')); $this->addElement('email_checkbox', 'form.approved_admin') ->setLabel(___('Approved Notification to Admin')); $this->addElement('email_checkbox', 'form.declined') ->setLabel(___('Declined Notification to User')); $this->addElement('email_checkbox', 'form.declined_admin') ->setLabel(___('Declined Notification to Admin')); $this->addElement('email_checkbox', 'form.resubmit') ->setLabel(___('Resubmit Notification to User')); $this->addElement('email_checkbox', 'form.resubmit_admin') ->setLabel(___('Resubmit Notification to Admin')); } }AffLead.php000064400000003000152101603130006520 0ustar00getDi()->sqlDateTime; return parent::insert($values, $returnInserted); } function log($aff_id, $banner_id, $user_id, $aff_click_id = null) { $click = $this->getDi()->affClickTable->load($aff_click_id, false); if ($click) { $referer = $click->referer; $first_visited = $click->time; $keyword_id = $click->keyword_id; } else { $referer = null; $first_visited = null; $keyword_id = null; } $this->_db->query("INSERT INTO $this->_table SET aff_id=?d, user_id=?, time=?, banner_id=?, remote_addr=?, referer=?, first_visited=?, keyword_id=? ", $aff_id, $user_id, $this->getDi()->sqlDateTime, $banner_id, $_SERVER['REMOTE_ADDR'], $referer, $first_visited, $keyword_id); } } AffBanner.php000064400000003754152101603130007100 0ustar00getTable()->getName(); $max = $this->getAdapter()->selectCell("SELECT MAX(sort_order) FROM {$table_name}"); $this->sort_order = $max + 1; return parent::insert($reload); } public function delete() { $ret = parent::delete(); $table_name = $this->getTable()->getName(); $this->getAdapter()->query("UPDATE {$table_name} SET sort_order=sort_order-1 WHERE sort_order>?", $this->sort_order); return $ret; } } class AffBannerTable extends Am_Table { protected $_key = 'banner_id'; protected $_table = '?_aff_banner'; protected $_recordClass = 'AffBanner'; function findActive($category = null){ return $this->selectObjects("SELECT * FROM ?_aff_banner WHERE is_disabled=0 AND {category = ?} {category IS NULL AND ?d=?d} ORDER BY sort_order", is_null($category) ? DBSIMPLE_SKIP : $category, !is_null($category) ? DBSIMPLE_SKIP : 1, 1); } function getCategories($onlyEnabled = false) { return $this->_db->selectCol("SELECT DISTINCT category, category AS ? FROM ?_aff_banner WHERE category IS NOT NULL {AND is_disabled = ?} ORDER BY category", DBSIMPLE_ARRAY_KEY, $onlyEnabled ? 0 : DBSIMPLE_SKIP); } } AffPayoutDetail.php000064400000001035152101603130010265 0ustar00getDi()->userTable->load($this->aff_id, false); } } class AffPayoutDetailTable extends Am_Table { protected $_key = 'payout_detail_id'; protected $_table = '?_aff_payout_detail'; } AffPayout.php000064400000002612152101603130007144 0ustar00getDi()->affPayoutDetailRecord; $detail->aff_id = $aff_id; $detail->amount = $amount; $detail->payout_id = $this->pk(); $detail->insert(); $this->total += $amount; return $detail; } /** @return Am_Aff_PayoutMethod|null */ function getPayoutMethod() { foreach (Am_Aff_PayoutMethod::getEnabled() as $k => $p) if ($k == $this->type) return $p; } function delete() { $this->getTable()->getAdapter()->query(" UPDATE ?_aff_commission SET payout_detail_id = NULL WHERE payout_detail_id IN ( SELECT payout_detail_id FROM ?_aff_payout_detail WHERE payout_id=?) ", $this->pk()); $this->deleteFromRelatedTable('?_aff_payout_detail'); return parent::delete(); } } class AffPayoutTable extends Am_Table { protected $_key = 'payout_id'; protected $_table = '?_aff_payout'; } AffClick.php000064400000005440152101603130006712 0ustar00getDi()->sqlDateTime; return parent::insert($values, $returnInserted); } function log(User $aff, AffBanner $banner = null, $referrer = null, $keyword_id = null) { $this->_db->query("INSERT INTO ?_aff_click SET aff_id=?d, time=?, banner_id=?d, remote_addr=?, referer=?, keyword_id = ?, user_agent = ? ", $aff->pk(), $this->getDi()->sqlDateTime, $banner ? $banner->pk() : null, $_SERVER['REMOTE_ADDR'], ($referrer ? $referrer : @$_SERVER['HTTP_REFERER']), $keyword_id, @$_SERVER['HTTP_USER_AGENT'] ); return $this->_db->selectCell("SELECT LAST_INSERT_ID()"); } function fetchByDate($date, $aff_id=null) { return $this->selectObjects( "SELECT * FROM ?_aff_click WHERE time BETWEEN ? AND ? { AND aff_id=?d}", date('Y-m-d 00:00:00', amstrtotime($date)), date('Y-m-d 23:59:59', amstrtotime($date)), $aff_id === null ? DBSIMPLE_SKIP : $aff_id); } function fetchByDateInterval($from, $to, $aff_id=null) { return $this->selectObjects( "SELECT * FROM ?_aff_click WHERE time BETWEEN ? AND ? { AND aff_id=?d}", date('Y-m-d 00:00:00', amstrtotime($from)), date('Y-m-d 23:59:59', amstrtotime($to)), $aff_id === null ? DBSIMPLE_SKIP : $aff_id); } /** * Find affililate by IP address if cookie is empty * @param type $ip * @return last aff_id in record with matching IP */ function findAffIdByIp($ip) { $startTm = sqlTime($this->getDi()->time - $this->getDi()->config->get('aff.cookie_lifetime', 365) * 24 * 3600); return $this->_db->selectCell("SELECT CONCAT(aff_id, '-', IFNULL(banner_id, '')) FROM $this->_table WHERE time >= ? AND remote_addr=? AND aff_id>0 ORDER BY time DESC LIMIT 1", $startTm, $ip); } function clearOld($date) { $this->_db->query("DELETE FROM ?_aff_click WHERE time < ?", $date); } } AffCommission.php000064400000022330152101603130010002 0ustar00record_type = self::COMMISSION; } public function _setPayment(InvoicePayment $payment=null){ $this->_payment = $payment; return $this; } public function _setInvoice(Invoice $invoice){ $this->_invoice = $invoice; return $this; } public function _setAff(User $aff){ $this->_aff = $aff; return $this; } public function getPayment() { if (empty($this->invoice_payment_id)) return null; return $this->_payment ? $this->_payment : $this->_payment=$this->getDi()->invoicePaymentTable->load($this->invoice_payment_id); } public function getAff() { return $this->_aff ? $this->_aff : $this->_aff=$this->getDi()->userTable->load($this->aff_id); } public function getInvoice() { if (empty($this->invoice_id)) return null; return $this->_invoice ? $this->_invoice : $this->_invoice=$this->getDi()->invoiceTable->load($this->invoice_id); } public function getProduct() { return $this->_product ? $this->_product : $this->_product = $this->getDi()->productTable->load($this->product_id); } function insert($reload = true) { $ret = parent::insert($reload); $this->getDi()->hook->call(Bootstrap_Aff::AFF_COMMISSION_AFTER_INSERT, array( 'commission' => $this, 'user' => $this->getInvoice() ? $this->getInvoice()->getUser() : null, 'aff' => $this->getAff(), 'invoice' => $this->getInvoice(), 'payment' => $this->getPayment(), )); return $ret; } function delete() { parent::delete(); $this->deleteFromRelatedTable('?_aff_commission_commission_rule'); } /** param array $rules - array of id# */ function setCommissionRules(array $rules) { $this->getAdapter()->query("DELETE FROM ?_aff_commission_commission_rule WHERE commission_id=?d {AND rule_id NOT IN (?a)}", $this->pk(), $rules ? $rules : DBSIMPLE_SKIP); if ($rules) { $vals = array(); foreach ($rules as $id) $vals[] = sprintf("(%d,%d)", $this->pk(), $id); $this->getAdapter()->query("INSERT IGNORE INTO ?_aff_commission_commission_rule (commission_id, rule_id) VALUES " . implode(", ", $vals)); $this->getDi()->hook->call(Bootstrap_Aff::AFF_COMMISSION_COMMISSION_RULE_AFTER_INSERT, array( 'commission' => $this, 'user' => $this->getInvoice() ? $this->getInvoice()->getUser() : null, 'aff' => $this->getAff(), 'invoice' => $this->getInvoice(), 'payment' => $this->getPayment(), )); } return $this; } } class AffCommissionTable extends Am_Table { protected $_key = 'commission_id'; protected $_table = '?_aff_commission'; protected $_recordClass = 'AffCommission'; /** * Return query with affiliates linked to sum of their unpaid commissions * made up to $toDate * @return Am_Query */ function fetchByDate($date, $aff_id=null) { return $this->selectObjects( "SELECT * FROM ?_aff_commission WHERE `date` = ? { AND aff_id=?d}", date('Y-m-d', amstrtotime($date)), $aff_id === null ? DBSIMPLE_SKIP : $aff_id); } function fetchByDateInterval($from, $to, $aff_id=null) { return $this->selectObjects( "SELECT c.*, p.title AS product_title FROM ?_aff_commission c LEFT JOIN ?_product p USING(product_id) WHERE `date` BETWEEN ? AND ? { AND aff_id=?d}", date('Y-m-d', amstrtotime($from)), date('Y-m-d', amstrtotime($to)), $aff_id === null ? DBSIMPLE_SKIP : $aff_id); } /** * Return commission stats * @param type $startTm * @param type $endTm * @return array with keys: count and amount */ function getAffStats($aff_id, $startTm, $endTm) { // return sales count of this affiliate for period return $this->_db->selectRow(" SELECT COUNT(DISTINCT(invoice_id)) AS `count`, SUM((SELECT COUNT(*) FROM ?_invoice_item WHERE invoice_id=inv.invoice_id AND (first_total>0 OR second_total>0))) AS `items_count`, SUM(p.amount) as `amount` FROM ?_invoice inv INNER JOIN ?_user u USING (user_id) LEFT JOIN ?_invoice_payment p USING (invoice_id) WHERE u.aff_id=?d AND inv.tm_started BETWEEN ? AND ? ", $aff_id, sqlTime($startTm), sqlTime($endTm . ' 23:59:59')); } /* * Find last records by invoice_id with the same date (for refunds) * @return array AffCommission */ function findLastRecordsByInvoiceId($invoice_id) { return $this->selectObjects("SELECT * FROM $this->_table WHERE invoice_id=?d AND record_type='commission' AND `date` = (SELECT MAX(`date`) FROM $this->_table WHERE invoice_id=?d)" , $invoice_id, $invoice_id); } /** * Generate payout records for records on $date * @param date $date today's date */ function runPayout($date) { $threseholdDate = sqlDate(amstrtotime($date) - 24 * 3600 * $this->getDi()->config->get('aff.payout_delay_days', 30)); // now select all affiliates to pay with one query $q = $this->_db->queryResultOnly(" SELECT SUM(IF(c.record_type=?,-c.amount,c.amount)) AS _total, a.* FROM ?_aff_commission c RIGHT JOIN ?_user a ON a.user_id=c.aff_id WHERE (c.record_type=? OR c.date<=?) AND (c.payout_detail_id IS NULL) GROUP BY c.aff_id HAVING _total > 0 AND _total >= ? AND a.aff_payout_type > '' ", AffCommission::VOID, AffCommission::VOID, $threseholdDate, (double)$this->getDi()->config->get('aff.payout_min', 0)); // then do job $payouts = array(); while ($row = $this->_db->fetchRow($q)) { $aff = $this->getDi()->userTable->createRecord($row); if (empty($payouts[$aff->aff_payout_type])) { $payout = $this->getDi()->affPayoutRecord; $payout->type = $aff->aff_payout_type; $payout->date = sqlDate($date); $payout->thresehold_date = $threseholdDate; $payout->insert(); $payouts[$aff->aff_payout_type] = $payout; } $detail = $payouts[$aff->aff_payout_type]->addDetail($aff->pk(), $row['_total']); $this->_db->query("UPDATE ?_aff_commission c SET c.payout_detail_id=?d WHERE aff_id=?d AND (c.record_type=? OR c.date<=?) AND (c.payout_detail_id IS NULL) ", $detail->pk(), $aff->pk(), AffCommission::VOID, $threseholdDate); } foreach ($payouts as $payout) $payout->update(); // store totals if ($payouts) { $this->getDi()->hook->call(Bootstrap_Aff::AFF_PAYOUT, array( 'payouts' => $payouts )); if ($et = Am_Mail_Template::load('aff.new_payouts')) $et->setUrl($this->getDi()->url('aff/admin-payout', null, false, true))->sendAdmin(); } } function void(AffCommission $comm, $date = null, $amount = null) { $void = $this->getDi()->affCommissionRecord; $void->fromRow($comm->toRow()); $void->date = $date ? $date : sqlDate('now'); $void->commission_id = null; $void->payout_detail_id = null; $void->record_type = AffCommission::VOID; $void->commission_id_void = $comm->pk(); $void->is_voided = 0; if ($amount) $void->amount = $amount; try { $void->insert(); $comm->updateQuick('is_voided', 1); } catch (Am_Exception_Db_NotUnique $e) { // already handled? keep silence } } function getStats($start, $stop) { return (double)$this->_db->selectCell("SELECT SUM(IF(record_type='commission', amount, -1 * amount)) FROM ?_aff_commission WHERE date BETWEEN ? AND ?", sqlDate($start), sqlDate($stop)); } } AffCommissionRule.php000064400000060646152101603130010646 0ustar00 ___("Custom Commission"), self::TYPE_MULTI => ___("Multiplier"), ); } function getTypeTitle() { $level = $this->tier + 1; $types = self::getTypes(); return $this->type == self::TYPE_GLOBAL ? ($this->tier > 0 ? ___('%d-Tier Affiliates Commission ', $level) : ___('Global Commission')) : $types[$this->type]; } function isGlobal() { if (empty($this->type)) return false; return self::isGlobalType($this->type); } static function isGlobalType($type) { return $type == self::TYPE_GLOBAL; } function match(Invoice $invoice, InvoiceItem $item, User $aff, $paymentNumber = 0, $tier = 0, $paymentDate = 'now') { if ($this->type == self::TYPE_GLOBAL) return $tier == $this->tier; if ($tier != 0) return false; // no custom rules for 2-tier // check conditions foreach ($this->getConditions() as $conditionType => $vars) { switch ($conditionType) { case self::COND_AFF_SALES_COUNT:; case self::COND_AFF_ITEMS_COUNT:; case self::COND_AFF_SALES_AMOUNT:; if (empty($vars['count']) || empty($vars['days'])) return false; $e = sqlDate($paymentDate); $b = sqlDate($e . '-' . $vars['days'] . ' days'); $stats = $this->getDi()->affCommissionTable->getAffStats($aff->pk(), $b, $e); switch ($conditionType) { case self::COND_AFF_ITEMS_COUNT: $key = 'items_count'; break; case self::COND_AFF_SALES_COUNT: $key = 'count'; break; default: $key = 'amount'; } if ($stats[$key] < $vars['count']) return false; break; case self::COND_AFF_GROUP_ID: if (!array_intersect($aff->getGroups(), (array)$vars)) return false; break; case self::COND_PRODUCT_ID: if (($item->item_type != 'product') || !in_array($item->item_id, (array)$vars)) return false; break; case self::COND_PRODUCT_CATEGORY_ID: if ($item->item_type != 'product') return false; $pr = $item->tryLoadProduct(); if (!$pr) return false; if (!array_intersect($pr->getCategories(), (array)$vars)) return false; break; case self::COND_NOT_PRODUCT_ID: if (($item->item_type == 'product') && in_array($item->item_id, (array)$vars)) return false; break; case self::COND_NOT_PRODUCT_CATEGORY_ID: if ($item->item_type == 'product' && ($pr = $item->tryLoadProduct()) && array_intersect($pr->getCategories(), (array)$vars)) { return false; } break; case self::COND_AFF_PRODUCT_ID: if (($item->item_type != 'product') || !array_intersect($aff->getActiveProductIds(), (array)$vars)) return false; break; case self::COND_AFF_PRODUCT_CATEGORY_ID: if ($item->item_type != 'product') return false; $p_c = $this->getDi()->productCategoryTable->getCategoryProducts(); $product_ids = array(); foreach ((array)$vars as $cid) { $product_ids = array_merge($product_ids, $p_c[$cid]); } if (!array_intersect($aff->getActiveProductIds(), $product_ids)) return false; break; case self::COND_COUPON: try { $coupon = $invoice->getCoupon(); } catch(Am_Exception $ex) { $this->getDi()->errorLogTable->logException($ex); break; } switch ($vars['type']) { case 'any' : $coupon_cond_match = (bool)$coupon; break; case 'coupon': $coupon_cond_match = $coupon && ($vars['code'] == $coupon->code); break; case 'batch' : $coupon_cond_match = $coupon && ($vars['batch_id'] == $coupon->batch_id); break; } if ($vars['used'] ? !$coupon_cond_match : $coupon_cond_match) return false; break; case self::COND_PAYSYS_ID : if (!in_array($invoice->paysys_id, (array)$vars)) return false; break; case self::COND_FIRST_TIME : if ($item->item_type == 'product' && $this->getDi()->accessTable->countBy(array( 'user_id' => $invoice->getUser()->pk(), 'product_id' => $item->item_id, 'begin_date' => '<=' . sqlDate($paymentDate), 'invoice_id' => '<>' . $invoice->pk())) > 0) return false; break; case self::COND_FIRST_TIME_PAYMENT : $cnt = $this->getDi()->invoicePaymentTable->countBy(array( 'user_id' => $invoice->getUser()->pk() )); if ($vars['first_time_payment'] && ($cnt!=1)) return false; if (!$vars['first_time_payment'] && ($cnt==1)) return false; break; case self::COND_UPGRADE : if (!$invoice->data()->get(Invoice::UPGRADE_INVOICE_ID)) return false; break; default: return false; } } return true; } function getConditions() { return (array)json_decode($this->conditions, true); } function setConditions(array $conditions) { $this->conditions = json_encode($conditions); return $this; } function render($pad = "") { $condition = $this->renderConditions(); if ($condition) $condition = $pad . " conditions: " . $condition. "\n"; return sprintf("%s%s (%s)\n", $pad, $this->comment, $this->renderCommission()). $condition; } function renderConditions() { $ret = array(); foreach ($this->getConditions() as $conditionType => $vars) { switch ($conditionType) { case self::COND_AFF_ITEMS_COUNT:; $ret[] = ___("Affiliate generated %d item sales last %d days", $vars['count'], $vars['days']); break; case self::COND_AFF_SALES_COUNT:; $ret[] = ___("Affiliate generated %d sales last %d days", $vars['count'], $vars['days']); break; case self::COND_AFF_SALES_AMOUNT:; $ret[] = ___("Affiliate generated %d%s in total sales amount last %d days", $vars['count'], Am_Currency::getDefault(), $vars['days']); break; case self::COND_AFF_GROUP_ID: $v = array(); foreach ($this->getDi()->userGroupTable->loadIds((array)$vars) as $group) $v[] = $group->title; $ret[] = ___("Affiliate Group IN (%s)", implode(", ", $v)); break; case self::COND_PRODUCT_ID: $v = array(); foreach ($this->getDi()->productTable->loadIds((array)$vars) as $product) $v[] = $product->title; $ret[] = ___("Products IN (%s)", implode(", ", $v)); break; case self::COND_PRODUCT_CATEGORY_ID: $v = array(); foreach ($this->getDi()->productCategoryTable->loadIds((array)$vars) as $product) $v[] = $product->title; $ret[] = ___("Product Category IN (%s)", implode(", ", $v)); break; case self::COND_NOT_PRODUCT_ID: $v = array(); foreach ($this->getDi()->productTable->loadIds((array)$vars) as $product) $v[] = $product->title; $ret[] = ___("Products NOT IN (%s)", implode(", ", $v)); break; case self::COND_NOT_PRODUCT_CATEGORY_ID: $v = array(); foreach ($this->getDi()->productCategoryTable->loadIds((array)$vars) as $product) $v[] = $product->title; $ret[] = ___("Product Category NOT IN (%s)", implode(", ", $v)); break; case self::COND_AFF_PRODUCT_ID: $v = array(); foreach ($this->getDi()->productTable->loadIds((array)$vars) as $product) $v[] = $product->title; $ret[] = ___("Affiliate has Products IN (%s)", implode(", ", $v)); break; case self::COND_AFF_PRODUCT_CATEGORY_ID: $v = array(); foreach ($this->getDi()->productCategoryTable->loadIds((array)$vars) as $product) $v[] = $product->title; $ret[] = ___("Affiliate has Products from Category IN (%s)", implode(", ", $v)); break; case self::COND_COUPON: $txt = ''; switch ($vars['type']) { case 'any' : $txt = $vars['used'] ? ___('Used Any Coupon') : ___("Did't Use Any Coupon"); break; case 'coupon' : $text = $vars['used'] ? ___('Used Coupon with Code: %s', $vars['code']) : ___("Did't Use Coupon with Code: %s", $vars['code']); break; case 'batch' : $options = $this->getDi()->couponBatchTable->getOptions(false); $txt = $vars['used'] ? ___('Used Coupon from Batch: %s', $options[$vars['batch_id']]) : ___("Did't Use Coupon from Batch: %s", $options[$vars['batch_id']]); break; } $ret[] = $txt; break; case self::COND_PAYSYS_ID: $ret[] = ___("Paysystem IN (%s)", implode(', ', (array)$vars)); break; case self::COND_FIRST_TIME: $ret[] = ___('First Time Purchase of Product'); break; case self::COND_FIRST_TIME_PAYMENT: $ret[] = ___('First Time Purchase: %s', $vars['first_time_payment'] ? ___('Yes') : ___('No')); break; case self::COND_UPGRADE: $ret[] = ___('Upgrade Invoice'); break; default: return false; } } return implode(" AND ", $ret); } /** * Return human-readable representation of commission */ function renderCommission() { $text = ""; if ($this->type == AffCommissionRule::TYPE_MULTI) return ___('commission found by next rules') . '× ' . $this->multi; if ($this->type == AffCommissionRule::TYPE_GLOBAL && $this->tier>0) return $this->first_payment_c . '%'; foreach (array('first_payment' => ___('First Payment'), 'recurring' => ___('Second and Subsequent Payments'), 'free_signup' => ___('Free Signup')) as $fieldName => $label) { if ($this->get($fieldName . '_c') <= 0) continue; $v = $this->get($fieldName . '_c') . $this->get($fieldName . '_t'); $text .= $label . ":" . $v . ", "; } return trim($text, ', '); } } class AffCommissionRuleTable extends Am_Table { protected $_key = 'rule_id'; protected $_table = '?_aff_commission_rule'; protected $_recordClass = 'AffCommissionRule'; protected $rulesCache = array(); /** * @return Am_Query with correct order set */ public function createQuery() { $q = new Am_Query($this); $q->setOrderRaw("IF(t.type='multi', 1, t.type+0), sort_order"); return $q; } public function _resetCache() { $this->rulesCache = array(); } /** * @param Invoice $invoice * @param int $paymentNumber number of payment for given invoice, staring from 0 * @param int $tier Affiliate level - starting from 0 * @return array matching the invoice */ public function findRules(Invoice $invoice, InvoiceItem $item, User $aff, $paymentNumber = 0, $tier = 0, $paymentDate = 'now') { if (!$this->rulesCache) { $this->rulesCache = $this->createQuery()->selectPageRecords(0, 99999); } $ret = array(); foreach ($this->rulesCache as $rule) { /* @var $rule AffCommissionRule */ if ($rule->match($invoice, $item, $aff, $paymentNumber, $tier, $paymentDate)) { $ret[] = $rule; if($rule->type == AffCommissionRule::TYPE_GLOBAL && $rule->tier > 0) $rule->first_payment_t = '%'; if ($rule->type != AffCommissionRule::TYPE_MULTI) break; // last rule } } return $ret; } public function calculate(Invoice $invoice, InvoiceItem $item, User $aff, $paymentNumber = 0, $tier = 0, $paymentAmount = 0.0, $paymentDate = 'now', & $matchedRules = array()) { // take aff.commission_days in account for 1-tier only if ($tier == 0 && ($commissionDays = $this->getDi()->config->get('aff.commission_days'))) { $signupDays = $this->getDi()->time - strtotime($invoice->getUser()->aff_added ? $invoice->getUser()->aff_added : $invoice->getUser()->added); $signupDays = intval($signupDays / (3600*24)); // to days if ($commissionDays < $signupDays) return; // no commission for this case, affiliate<->user relation is expired } $multi = 1.0; $isFirst = $paymentNumber<=1; $prefix = $isFirst && (float)$item->first_total ? 'first' : 'second'; if ($tier == 0) // for tier 0 get amount paid for given item { if ($invoice->get("{$prefix}_total") == 0) $paidForItem = 0; // avoid division by zero else $paidForItem = $paymentAmount * $item->get("{$prefix}_total") / $invoice->get("{$prefix}_total"); } else { // for higher tier just take amount paid to previous tier $paidForItem = $paymentAmount; } $paidForItem = $tier ? $paidForItem : $paidForItem/$invoice->base_currency_multi; $comm = 0; foreach ($this->findRules($invoice, $item, $aff, $paymentNumber, $tier, $paymentDate) as $rule) { array_push($matchedRules, $rule); // Second tier commission have to be calculated as percent from First tier commission. if ($tier > 0) { $comm = moneyRound($rule->first_payment_c * $paidForItem / 100); break; } if ($rule->type == AffCommissionRule::TYPE_MULTI) $multi *= $rule->multi; else { if ($paidForItem == 0) { // free signup? if (($paymentNumber == 0) && $rule->free_signup_c) { $comm = moneyRound($multi * $rule->free_signup_c); break; } } elseif ($isFirst) { // first payment if ($rule->first_payment_t == '%') { $comm = moneyRound($multi * $rule->first_payment_c * $paidForItem / 100); break; } else { $comm = moneyRound($multi * $rule->first_payment_c); break; } } else { // recurring payment if ($rule->recurring_t == '%') { $comm = moneyRound($multi * $rule->recurring_c * $paidForItem / 100); break; } else { $comm = moneyRound($multi * $rule->recurring_c); break; } } } } $event = new Am_Event(Bootstrap_Aff::AFF_COMMISSION_CALCULATE, array( 'invoice' => $invoice, 'invoiceItem' => $item, 'aff' => $aff, 'paymentNumber' => $paymentNumber, 'tier' => $tier, 'paymentAmount' => $paymentAmount, 'paymentDate' => $paymentDate )); $event->setReturn($comm); $this->getDi()->hook->call($event); return $event->getReturn(); } public function getMaxTier() { return $this->getDi()->db->selectCell("SELECT MAX(tier) FROM ?_aff_commission_rule"); } /** * Process invoice and insert necessary commissions for it * * External code should guarantee that this method with $payment = null will be called * only once for each user for First user invoice */ public function processPayment(Invoice $invoice, InvoicePayment $payment = null) { $aff = $this->getDi()->modules->loadGet('aff')->getAffiliate($invoice, $payment); if (!$aff) return; $user = $invoice->getUser(); if (!$user->aff_id) { $user->aff_id = $aff->pk(); $user->aff_added = sqlTime('now'); $user->data()->set('aff-source', 'invoice-' . $invoice->pk()); $user->save(); } // try to load other tier affiliate $aff_tier = $aff; $aff_tiers = array(); $aff_tiers_exists = array($aff->pk()); for ($tier=1; $tier<=$this->getMaxTier(); $tier++) { if (!$aff_tier->aff_id || ($aff_tier->pk() == $invoice->getUser()->pk())) break; $aff_tier = $this->getDi()->userTable->load($aff_tier->aff_id, false); if (!$aff_tier || //not exists !$aff_tier->is_affiliate || //not affiliate ($aff_tier->pk() == $invoice->getUser()->pk()) || //original user in_array($aff_tier->pk(), $aff_tiers_exists)) //already in chain break; $aff_tiers[$tier] = $aff_tier; $aff_tiers_exists[] = $aff_tier->pk(); } $isFirst = !$payment || $payment->isFirst(); //to define price field $paymentNumber = is_null($payment) ? 0 : $invoice->getPaymentsCount(); if (!$payment) $tax = 0; else $tax = $this->getDi()->config->get('aff.commission_include_tax', false) ? 0 : doubleval($payment->tax); $amount = $payment ? ($payment->amount - $tax): 0; $date = $payment ? $payment->dattm : 'now'; // now calculate commissions $items = is_null($payment) ? array_slice($invoice->getItems(), 0, 1) : $invoice->getItems(); foreach ($items as $item) { //we do not calculate commission for free items in invoice $prefix = $isFirst ? 'first' : 'second'; if (!is_null($payment) && !(float)$item->get("{$prefix}_total")) continue; $comm = $this->getDi()->affCommissionRecord; $comm->date = sqlDate($date); $comm->record_type = AffCommission::COMMISSION; $comm->invoice_id = $invoice->invoice_id; $comm->invoice_item_id = $item->invoice_item_id; $comm->invoice_payment_id = $payment ? $payment->pk() : null; $comm->receipt_id = $payment ? $payment->receipt_id : null; $comm->product_id = $item->item_id; $comm->is_first = $paymentNumber<=1; $comm->keyword_id = $invoice->keyword_id; $comm->_setPayment($payment); $comm->_setInvoice($invoice); $comm_tier = clone $comm; $rules = array(); $topay_this = $topay = $this->calculate($invoice, $item, $aff, $paymentNumber, 0, $amount, $date, $rules); if ($topay>0) { $comm->aff_id = $aff->pk(); $comm->amount = $topay; $comm->tier = 0; $comm->_setAff($aff); $comm->insert(); $comm->setCommissionRules(array_map(function ($el) {return $el->pk();}, $rules)); } foreach ($aff_tiers as $tier => $aff_tier) { $rules = array(); $topay_this = $this->calculate($invoice, $item, $aff_tier, $paymentNumber, $tier, $topay_this, $date, $rules); if ($topay_this>0) { $comm_this = clone $comm_tier; $comm_this->aff_id = $aff_tier->pk(); $comm_this->amount = $topay_this; $comm_this->tier = $tier; $comm_this->_setAff($aff_tier); $comm_this->insert(); $comm_this->setCommissionRules(array_map(function ($el) {return $el->pk();}, $rules)); } } } } /** * Process refund to rollback existing commissions */ public function processRefund(Invoice $invoice, InvoiceRefund $refund) { if ($refund->invoice_payment_id) $toRefund = $this->getDi()->affCommissionTable->findByInvoicePaymentId($refund->invoice_payment_id); else $toRefund = $this->getDi()->affCommissionTable->findLastRecordsByInvoiceId($refund->invoice_id); foreach ($toRefund as $affCommission) { $this->getDi()->affCommissionTable->void($affCommission, $refund->dattm); } } public function hasCustomRules() { return $this->_db->selectCell("SELECT COUNT(*) FROM $this->_table WHERE `type` NOT IN (?a)", array(AffCommissionRule::TYPE_GLOBAL,)); } }AffKeyword.php000064400000000771152101603130007313 0ustar00getDi()->userTable->load($this->aff_id, false); } } class AffKeywordTable extends Am_Table { protected $_key = 'keyword_id'; protected $_table = '?_aff_keyword'; } AffCommissionCommissionRule.php000064400000000453152101603130012675 0ustar00title = ___('Has Affiliate Commission'); $this->op = array( 'any' => ___('Any'), 'paid' => ___('Paid (Included to Payout)'), 'not-paid' => ___('Not Paid') ); } public function setFromRequest(array $input) { $this->type = @$input[$this->getId()]['type']; if ($this->type) return true; } public function getId() { return 'aff-with-comm'; } public function renderElement(HTML_QuickForm2_Container $form) { $form->options[___('Misc')][$this->getId()] = $this->title; $g = $form->addGroup($this->getId()) ->setLabel($this->title) ->setAttribute('id', $this->getId()) ->setAttribute('class', 'searchField empty'); $g->addSelect('type') ->loadOptions($this->op); } public function isEmpty() { return !$this->type; } public function getDescription() { return ___('Has %s Affiliate Commission', $this->op[$this->type]); } function _getWhere(Am_Query $db) { $a = $db->getAlias(); switch ($this->type) { case 'any': $c = ''; break; case 'paid' : $c = 'AND payout_detail_id IS NOT NULL'; break; case 'not-paid' : $c = 'AND payout_detail_id IS NULL'; break; } return "EXISTS (SELECT * FROM ?_aff_commission WHERE aff_id=$a.user_id $c)"; } }Am/Form/Element/AffCommissionSize.php000064400000001115152101603130013464 0ustar00addElement('text', $data.'_c', array('size' => 5, )); $this->addStatic()->setContent(' '); $sel = $this->addElement('select', $data.'_t', array('size' => 1,), array('options' => array( '%' => '%', '$' => Am_Currency::getDefault(), ) )); } }Am/Form/Signup/Aff.php000064400000001537152101603130010454 0ustar00 $b) { if (($b instanceof Am_Form_Brick_Product) || ($b instanceof Am_Form_Brick_Paysystem) || ($b instanceof Am_Form_Brick_Coupon)) unset($bricks[$k]); } return $bricks; } public function getDefaultBricks() { return array( new Am_Form_Brick_Name, new Am_Form_Brick_Email, new Am_Form_Brick_Login, new Am_Form_Brick_Password, new Am_Form_Brick_Address, new Am_Form_Brick_Agreement, new Am_Form_Brick_Payout ); } public function isHideBricks() { return true; } }Am/Form/Brick.php000064400000321524152101603130007546 0ustar00values if ($this->labels && is_int(key($this->labels))) { $ll = array_values($this->labels); $this->labels = array_combine($ll, $ll); } if ($id !== null) $this->setId($id); if ($config !== null) $this->setConfigArray($config); if ($this->hideIfLoggedInPossible() == self::HIDE_ALWAYS) $this->hideIfLoggedIn = true; // format labels } /** * this function can be used to bind some special processing * to hooks */ public function init() { } function getClass() { return fromCamelCase(str_replace('Am_Form_Brick_', '', get_class($this)), '-'); } function getName() { if (!$this->name) $this->name = str_replace('Am_Form_Brick_', '', get_class($this)); return $this->name; } function getId() { if (!$this->id) { $this->id = $this->getClass(); if ($this->isMultiple()) $this->id .= '-0'; } return $this->id; } function setId($id) { $this->id = (string) $id; } function getConfigArray() { return $this->config; } function setConfigArray(array $config) { $this->config = $config; } function getConfig($k, $default = null) { return array_key_exists($k, $this->config) ? $this->config[$k] : $default; } function getStdLabels() { return $this->labels; } function getCustomLabels() { return $this->customLabels; } function setCustomLabels(array $labels) { $this->customLabels = $labels; } function ___($id) { $args = func_get_args(); $args[0] = array_key_exists($id, $this->customLabels) ? $this->customLabels[$id] : $this->labels[$id]; return call_user_func_array('___', $args); } function initConfigForm(Am_Form $form) { } /** @return bool true if initConfigForm is overriden */ function haveConfigForm() { $r = new ReflectionMethod(get_class($this), 'initConfigForm'); return $r->getDeclaringClass()->getName() != __CLASS__; } function setFromRecord(array $brickConfig) { if ($brickConfig['id']) $this->id = $brickConfig['id']; $this->setConfigArray(empty($brickConfig['config']) ? array() : $brickConfig['config']); if (isset($brickConfig[self::HIDE])) $this->hideIfLoggedIn = $brickConfig[self::HIDE]; if (isset($brickConfig['labels'])) $this->customLabels = $brickConfig['labels']; return $this; } /** @return array */ function getRecord() { $ret = array( 'id' => $this->getId(), 'class' => $this->getClass(), ); if ($this->hideIfLoggedIn) $ret[self::HIDE] = $this->hideIfLoggedIn; if ($this->config) $ret['config'] = $this->config; if ($this->customLabels) $ret['labels'] = $this->customLabels; return $ret; } function isAcceptableForForm(Am_Form_Bricked $form) { return true; } public function hideIfLoggedIn() { return $this->hideIfLoggedIn; } public function hideIfLoggedInPossible() { return $this->hideIfLoggedInPossible; } /** if user can add many instances of brick right in the editor */ public function isMultiple() { return false; } static function createAvailableBricks($className) { return new $className; } /** * @param array $brickConfig - must have keys: 'id', 'class', may have 'hide', 'config' * * @return Am_Form_Brick */ static function createFromRecord(array $brickConfig) { if (empty($brickConfig['class'])) throw new Am_Exception_InternalError("Error in " . __METHOD__ . " - cannot create record without [class]"); if (empty($brickConfig['id'])) throw new Am_Exception_InternalError("Error in " . __METHOD__ . " - cannot create record without [id]"); $className = 'Am_Form_Brick_' . ucfirst(toCamelCase($brickConfig['class'])); if (!class_exists($className, true)) { Am_Di::getInstance()->errorLogTable->log("Missing form brick: [$className] - not defined"); return; } $b = new $className($brickConfig['id'], empty($brickConfig['config']) ? array() : $brickConfig['config']); if (array_key_exists(self::HIDE, $brickConfig)) $b->hideIfLoggedIn = (bool) @$brickConfig[self::HIDE]; if (!empty($brickConfig['labels'])) $b->setCustomLabels($brickConfig['labels']); return $b; } static function getAvailableBricks(Am_Form_Bricked $form) { $ret = array(); foreach (get_declared_classes () as $className) { if (is_subclass_of($className, 'Am_Form_Brick')) { $class = new ReflectionClass($className); if ($class->isAbstract()) continue; $obj = call_user_func(array($className, 'createAvailableBricks'), $className); if (!is_array($obj)) { $obj = array($obj); } foreach ($obj as $k => $o) if (!$o->isAcceptableForForm($form)) unset($obj[$k]); $ret = array_merge($ret, $obj); } } return $ret; } } class Am_Form_Brick_Name extends Am_Form_Brick { const DISPLAY_BOTH = 0; const DISPLAY_FIRSTNAME = 1; const DISPLAY_LASTNAME = 2; const DISPLAY_BOTH_SINGLE_INPUT = 3; protected $labels = array( 'First & Last Name', 'First Name', 'Last Name', 'Please enter your First Name', 'Please enter your First & Last Name', 'Please enter your Last Name', ); protected $hideIfLoggedInPossible = self::HIDE_DESIRED; public function __construct($id = null, $config = null) { $this->name = ___('Name'); parent::__construct($id, $config); } public function insertBrick(HTML_QuickForm2_Container $form) { if ($this->getConfig('two_rows') && $this->getConfig('display') != self::DISPLAY_BOTH_SINGLE_INPUT) { if ($this->getConfig('display') != self::DISPLAY_LASTNAME) { $row1 = $form->addGroup('')->setLabel($this->___('First Name')); if (!$this->getConfig('not_required')) { $row1->addRule('required'); } } if ($this->getConfig('display') != self::DISPLAY_FIRSTNAME) { $row2 = $form->addGroup('')->setLabel($this->___('Last Name')); if (!$this->getConfig('not_required')) { $row2->addRule('required'); } } $len = 30; } else { $row1 = $form->addGroup('', array('id' => 'name-0'))->setLabel($this->___('First & Last Name')); if (!$this->getConfig('not_required')) { $row1->addRule('required'); } $row2 = $row1; $len = 10; } if (!$this->getConfig('display') || $this->getConfig('display') == self::DISPLAY_FIRSTNAME) { $name_f = $row1->addElement('text', 'name_f', array('size' => $len)); if (!$this->getConfig('not_required')) { $name_f->addRule('required', $this->___('Please enter your First Name')); } $name_f->addRule('regex', $this->___('Please enter your First Name'), '/^[^=:<>{}()"]+$/D'); if ($this->getConfig('ucfirst')) $name_f->addFilter(function($v){return ucfirst(strtolower($v));}); if ($this->getConfig('disabled')) $name_f->toggleFrozen(true); $row1->addElement('html')->setHtml(' '); } if (!$this->getConfig('display') || $this->getConfig('display') == self::DISPLAY_LASTNAME) { $name_l = $row2->addElement('text', 'name_l', array('size' => $len)); if (!$this->getConfig('not_required')) { $name_l->addRule('required', $this->___('Please enter your Last Name')); } $name_l->addRule('regex', $this->___('Please enter your Last Name'), '/^[^=:<>{}()"]+$/D'); if ($this->getConfig('ucfirst')) $name_l->addFilter(function($v){return ucfirst(strtolower($v));}); if ($this->getConfig('disabled')) $name_l->toggleFrozen(true); } if ($this->getConfig('display') == self::DISPLAY_BOTH_SINGLE_INPUT) { $name = $row1->addElement('name', '_name', array('size' => 30)); if (!$this->getConfig('not_required')) { $name->addRule('required', $this->___('Please enter your First & Last Name')); } $name->addRule('regex', $this->___('Please enter your First & Last Name'), '/^[^=:<>{}()"]+$/D'); $filter = function ($v) {return trim($v);}; if ($this->getConfig('ucfirst')) { $filter = function ($v) use ($filter) { return ucwords(strtolower(call_user_func($filter, $v))); }; } $form->addFilter(function($v) use ($filter) { if (isset($v['_name'])) { list($v['name_f'], $v['name_l']) = array_pad(array_map($filter, explode(' ', $v['_name'], 2)), 2, ''); unset($v['_name']); } return $v; }); if ($this->getConfig('disabled')) $name->toggleFrozen(true); } } public function initConfigForm(Am_Form $form) { $form->addSelect('display') ->setId('name-display') ->loadOptions(array( self::DISPLAY_BOTH => ___('both First and Last Name'), self::DISPLAY_FIRSTNAME => ___('only First Name'), self::DISPLAY_LASTNAME => ___('only Last Name'), self::DISPLAY_BOTH_SINGLE_INPUT => ___('both First and Last Name in Single Input') ))->setLabel(___('User must provide')); $form->addAdvCheckbox('two_rows') ->setId('name-two_rows') ->setLabel(___('Display in 2 rows')); $form->addAdvCheckbox('not_required')->setLabel(___('Do not require to fill in these fields')); $form->addAdvCheckbox('ucfirst')->setLabel(___('Make the first letters of first and last name Uppercase')); $form->addAdvCheckbox('disabled')->setLabel(___('Read-only')); $both = self::DISPLAY_BOTH; $form->addScript() ->setScript(<<name = ___('HTML text'); parent::__construct($id, $config); } public function initConfigForm(Am_Form $form) { $form->addSelect('position', array('class' => 'brick-html-position')) ->setLabel(___('Position for HTML')) ->loadOptions(array( '' => ___('Default'), 'header' => ___('Above Form (Header)'), 'footer' => ___('Below Form (Footer)'), )); $form->addTextarea('html', array('rows' => 15, 'class' => 'el-wide')) ->setLabel(___('HTML Code that will be displayed')); $form->addText('label', array('class' => 'el-wide', 'rel' => 'position-default'))->setLabel(___('Label')); $form->addAdvCheckbox('no_label', array('rel' => 'position-default'))->setLabel(___('Remove Label')); $form->addMagicSelect('lang') ->setLabel(___("Language\n" . 'Display this brick only for the following languages. ' . 'Keep it empty to display for any language.')) ->loadOptions($this->getLangaugeList()); $form->addScript() ->setScript(<<languagesListUser; $_list = array(); if ($enabled = $di->config->get('lang.enabled', array())) foreach ($enabled as $lang) if (!empty($avail[$lang])) $_list[$lang] = $avail[$lang]; return $_list; } public function getLanguage() { $_list = $this->getLangaugeList(); $_locale = key(Zend_Locale::getDefault()); if (!array_key_exists($_locale, $_list)) list($_locale) = explode('_', $_locale); return $_locale; } public function insertBrick(HTML_QuickForm2_Container $form) { $lang = $this->getConfig('lang'); if ($lang && !in_array($this->getLanguage(), $lang)) return; switch ($this->getConfig('position')) { case 'header' : $form->addProlog($this->getConfig('html')); break; case 'footer' : $form->addEpilog($this->getConfig('html')); break; default: $attrs = $data = array(); $data['content'] = $this->getConfig('html'); if ($this->getConfig('no_label')) { $attrs['class'] = 'no-label'; } else { $data['label'] = $this->getConfig('label'); } $form->addStatic('html' . (++self::$counter), $attrs, $data); } } public function isMultiple() { return true; } } class Am_Form_Brick_JavaScript extends Am_Form_Brick { public function initConfigForm(Am_Form $form) { $form->addTextarea('code', array('rows' => 15, 'class' => 'el-wide')) ->setLabel(___("JavaScript Code\n" . "it will be injected on signup form")); } public function insertBrick(HTML_QuickForm2_Container $form) { $form->addScript()->setScript($this->getConfig('code')); } public function isMultiple() { return true; } } class Am_Form_Brick_Email extends Am_Form_Brick { protected $labels = array( "Your E-Mail Address\na confirmation email will be sent to you at this address", 'Please enter valid Email', 'Confirm Your E-Mail Address', 'E-Mail Address and E-Mail Address Confirmation are different. Please reenter both', 'An account with the same email already exists.', 'Please %slogin%s to your existing account.%sIf you have not completed payment, you will be able to complete it after login' ); protected $hideIfLoggedInPossible = self::HIDE_ALWAYS; public function __construct($id = null, $config = null) { $this->name = ___('E-Mail'); parent::__construct($id, $config); } public function initConfigForm(Am_Form $form) { $form->addAdvCheckbox('validate')->setLabel(___('Validate E-Mail Address by sending e-mail message with code')); $form->addAdvCheckbox('confirm') ->setLabel(___("Confirm E-Mail Address\n" . 'second field will be displayed to enter email address twice')) ->setId('email-confirm'); $form->addAdvCheckbox('do_not_allow_copy_paste') ->setLabel(___('Does not allow to Copy&Paste to confirmation field')) ->setId('email-do_not_allow_copy_paste'); $form->addAdvCheckbox("disabled")->setLabel(___('Read-only')); $form->addScript() ->setScript(<<auth->getUserId(); if (!$user_id) $user_id = Am_Di::getInstance()->session->signup_member_id; if (!Am_Di::getInstance()->userTable->checkUniqEmail($email, $user_id)) return $this->___('An account with the same email already exists.') . '
' . $this->___('Please %slogin%s to your existing account.%sIf you have not completed payment, you will be able to complete it after login', '', '', '
'); return Am_Di::getInstance()->banTable->checkBan(array('email' => $email)); } public function insertBrick(HTML_QuickForm2_Container $form) { $email = $form->addText('email', array('size' => 30)) ->setLabel($this->___("Your E-Mail Address\na confirmation email will be sent to you at this address")); $email->addRule('required', $this->___('Please enter valid Email')) ->addRule('callback', $this->___('Please enter valid Email'), array('Am_Validate', 'email')); if ($this->getConfig('disabled')) $email->toggleFrozen(true); $redirect = isset($_GET['amember_redirect_url']) ? $_GET['amember_redirect_url'] : $_SERVER['REQUEST_URI']; $email->addRule('callback2', '--wrong email--', array($this, 'check')) ->addRule('remote', '--wrong email--', array( 'url' => Am_Di::getInstance()->url('ajax', array('do'=>'check_uniq_email','_url'=> Am_Di::getInstance()->url('login', array('amember_redirect_url'=>$redirect), false ) ), false ))); if ($this->getConfig('confirm', 0)) { $email0 = $form->addText('_email', array('size' => 30)) ->setLabel($this->___("Confirm Your E-Mail Address")) ->setId('email-confirm'); $email0->addRule('required'); $email0->addRule('eq', $this->___('E-Mail Address and E-Mail Address Confirmation are different. Please reenter both'), $email); if ($this->getConfig('do_not_allow_copy_paste')) { $form->addScript() ->setScript(' jQuery(function(){ var $ = jQuery; jQuery("#email-confirm").bind("paste", function() { return false; }) })'); } return array($email, $email0); } } } class Am_Form_Brick_Login extends Am_Form_Brick { protected $labels = array( "Choose a Username\nit must be %d or more characters in length\nmay only contain letters, numbers, and underscores", 'Please enter valid Username. It must contain at least %d characters', 'Username contains invalid characters - please use digits, letters or spaces', 'Username contains invalid characters - please use digits, letters, dash and underscore', 'Username %s is already taken. Please choose another username', ); protected $hideIfLoggedInPossible = self::HIDE_ALWAYS; public function __construct($id = null, $config = null) { $this->name = ___("Username"); parent::__construct($id, $config); } public function insertBrick(HTML_QuickForm2_Container $form) { $len = Am_Di::getInstance()->config->get('login_min_length', 6); $login = $form->addText('login', array('size' => 30, 'maxlength' => Am_Di::getInstance()->config->get('login_max_length', 64))) ->setLabel($this->___("Choose a Username\nit must be %d or more characters in length\nmay only contain letters, numbers, and underscores", $len)); $login->addRule('required', sprintf($this->___('Please enter valid Username. It must contain at least %d characters'), $len)) ->addRule('length', sprintf($this->___('Please enter valid Username. It must contain at least %d characters'), $len), array($len, Am_Di::getInstance()->config->get('login_max_length', 64))) ->addRule('regex', !Am_Di::getInstance()->config->get('login_disallow_spaces') ? $this->___('Username contains invalid characters - please use digits, letters or spaces') : $this->___('Username contains invalid characters - please use digits, letters, dash and underscore'), Am_Di::getInstance()->userTable->getLoginRegex()) ->addRule('callback2', "--wrong login--", array($this, 'check')) ->addRule('remote', '--wrong login--', array( 'url' => Am_Di::getInstance()->url('ajax', array('do'=>'check_uniq_login'), false), )); if (!Am_Di::getInstance()->config->get('login_dont_lowercase')) $login->addFilter('strtolower'); $this->form = $form; } public function check($login) { if (!Am_Di::getInstance()->userTable->checkUniqLogin($login, Am_Di::getInstance()->session->signup_member_id)) return sprintf($this->___('Username %s is already taken. Please choose another username'), Am_Html::escape($login)); return Am_Di::getInstance()->banTable->checkBan(array('login' => $login)); } public function isAcceptableForForm(Am_Form_Bricked $form) { return $form instanceof Am_Form_Signup; } } class Am_Form_Brick_NewLogin extends Am_Form_Brick { protected $labels = array( "Username\nyou can choose new username here or keep it unchanged.\nUsername must be %d or more characters in length and may\nonly contain small letters, numbers, and underscore", "Please enter valid Username. It must contain at least %d characters", "Username contains invalid characters - please use digits, letters or spaces", "Username contains invalid characters - please use digits, letters, dash and underscore", 'Username %s is already taken. Please choose another username', ); public function __construct($id = null, $config = null) { $this->name = ___('Change Username'); parent::__construct($id, $config); } public function insertBrick(HTML_QuickForm2_Container $form) { $len = Am_Di::getInstance()->config->get('login_min_length', 6); $login = $form->addText('login', array('size' => 30, 'maxlength' => Am_Di::getInstance()->config->get('login_max_length', 64))) ->setLabel(sprintf($this->___("Username\nyou can choose new username here or keep it unchanged.\nUsername must be %d or more characters in length and may\nonly contain small letters, numbers, and underscore"), $len) ); if ($this->getConfig('disabled')) $login->toggleFrozen(true); $login ->addRule('required') ->addRule('length', sprintf($this->___("Please enter valid Username. It must contain at least %d characters"), $len), array($len, Am_Di::getInstance()->config->get('login_max_length', 64))) ->addRule('regex', !Am_Di::getInstance()->config->get('login_disallow_spaces') ? $this->___("Username contains invalid characters - please use digits, letters or spaces") : $this->___("Username contains invalid characters - please use digits, letters, dash and underscore"), Am_Di::getInstance()->userTable->getLoginRegex()) ->addRule('callback2', $this->___('Username %s is already taken. Please choose another username'), array($this, 'checkNewUniqLogin')); } function checkNewUniqLogin($login) { $auth_user = Am_Di::getInstance()->auth->getUser(); if (strcasecmp($login, $auth_user->login) !== 0) if (!$auth_user->getTable()->checkUniqLogin($login, $auth_user->pk())) return sprintf($this->___('Username %s is already taken. Please choose another username'), Am_Html::escape($login)); } public function isAcceptableForForm(Am_Form_Bricked $form) { return $form instanceof Am_Form_Profile; } public function initConfigForm(Am_Form $form) { $form->addAdvCheckbox("disabled")->setLabel(___('Read-only')); } } class Am_Form_Brick_Password extends Am_Form_Brick { protected $labels = array( "Choose a Password\nmust be %d or more characters", 'Confirm Your Password', 'Please enter Password', 'Password must contain at least %d letters or digits', 'Password and Password Confirmation are different. Please reenter both', 'Password should contain at least 2 capital letters, 2 or more numbers and 2 or more special chars', ); protected $hideIfLoggedInPossible = self::HIDE_ALWAYS; public function __construct($id = null, $config = null) { $this->name = ___("Password"); parent::__construct($id, $config); } public function initConfigForm(Am_Form $form) { $form->addAdvCheckbox('do_not_confirm') ->setLabel(___("Does not Confirm Password\n" . 'second field will not be displayed to enter password twice')) ->setId('password-do_not_confirm'); $form->addAdvCheckbox('do_not_allow_copy_paste') ->setLabel(___('Does not allow to Copy&Paste to confirmation field')) ->setId('password-do_not_allow_copy_paste'); $form->addScript() ->setScript(<<config->get('pass_min_length', 6); $pass = $form->addPassword('pass', array('size' => 30, 'autocomplete'=>'off', 'maxlength' => Am_Di::getInstance()->config->get('pass_max_length', 64))) ->setLabel($this->___("Choose a Password\nmust be %d or more characters", $len)); $pass->addRule('required', $this->___('Please enter Password')); $pass->addRule('length', sprintf($this->___('Password must contain at least %d letters or digits'), $len), array($len, Am_Di::getInstance()->config->get('pass_max_length', 64))); if (Am_Di::getInstance()->config->get('require_strong_password')) { $pass->addRule('regex', $this->___('Password should contain at least 2 capital letters, 2 or more numbers and 2 or more special chars'), Am_Di::getInstance()->userTable->getStrongPasswordRegex()); } if (!$this->getConfig('do_not_confirm')) { $pass0 = $form->addPassword('_pass', array('size' => 30, 'autocomplete'=>'off')) ->setLabel($this->___('Confirm Your Password')) ->setId('pass-confirm'); $pass0->addRule('required'); $pass0->addRule('eq', $this->___('Password and Password Confirmation are different. Please reenter both'), $pass); if ($this->getConfig('do_not_allow_copy_paste')) { $form->addScript() ->setScript(' jQuery(function($){ jQuery("#pass-confirm").bind("paste", function() { return false; }) })'); } return array($pass, $pass0); } else { $pass->setAttribute('class', 'am-pass-reveal am-with-action'); } return $pass; } public function isAcceptableForForm(Am_Form_Bricked $form) { return $form instanceof Am_Form_Signup; } } class Am_Form_Brick_NewPassword extends Am_Form_Brick { protected $labels = array( "Password", "Change", "Your Current Password\nif you are changing password, please\n enter your current password for validation", "New Password\nyou can choose new password here or keep it unchanged\nmust be %d or more characters", 'Confirm New Password', 'Please enter Password', 'Password must contain at least %d letters or digits', 'Password and Password Confirmation are different. Please reenter both', 'Please enter your current password for validation', 'Current password entered incorrectly, please try again', 'Password should contain at least 2 capital letters, 2 or more numbers and 2 or more special chars', ); public function __construct($id = null, $config = null) { $this->name = ___('Change Password'); parent::__construct($id, $config); } public function initConfigForm(Am_Form $form) { $form->addAdvCheckbox('do_not_ask_current_pass') ->setLabel(___("Does not Ask Current Password\n" . 'user will not need to enter his current password to change it')); $form->addAdvCheckbox('do_not_confirm') ->setLabel(___("Does not Confirm Password\n" . 'second field will not be displayed to enter password twice')) ->setId('new-password-do_not_confirm'); $form->addAdvCheckbox('do_not_allow_copy_paste') ->setLabel(___('Does not allow to Copy&Paste to confirmation field')) ->setId('new-password-do_not_allow_copy_paste'); $form->addScript() ->setScript(<<___('Change')); $form->addHtml() ->setLabel($this->___('Password')) ->setHtml(<<$change CUT ); $len = Am_Di::getInstance()->config->get('pass_min_length', 6); if (!$this->getConfig('do_not_ask_current_pass')) { $oldPass = $form->addPassword('_oldpass', array('size' => 30, 'autocomplete'=>'off', 'class'=>'am-change-pass')) ->setLabel($this->___("Your Current Password\nif you are changing password, please\n enter your current password for validation")); $oldPass->addRule('callback2', 'wrong', array($this, 'validateOldPass')); } $pass = $form->addPassword('pass', array('size' => 30, 'autocomplete'=>'off', 'maxlength' => Am_Di::getInstance()->config->get('pass_max_length', 64), 'class'=>'am-change-pass')) ->setLabel($this->___("New Password\nyou can choose new password here or keep it unchanged\nmust be %d or more characters", $len)); $pass->addRule('length', sprintf($this->___('Password must contain at least %d letters or digits'), $len), array($len, Am_Di::getInstance()->config->get('pass_max_length', 64))); if (Am_Di::getInstance()->config->get('require_strong_password')) { $pass->addRule('regex', $this->___('Password should contain at least 2 capital letters, 2 or more numbers and 2 or more special chars'), Am_Di::getInstance()->userTable->getStrongPasswordRegex()); } if (!$this->getConfig('do_not_confirm')) { $pass0 = $form->addPassword('_pass', array('size' => 30, 'autocomplete'=>'off', 'class'=>'am-change-pass')) ->setLabel($this->___('Confirm New Password')) ->setId('pass-confirm'); $pass0->addRule('eq', $this->___('Password and Password Confirmation are different. Please reenter both'), $pass); if ($this->getConfig('do_not_allow_copy_paste')) { $form->addScript() ->setScript(' jQuery(function($){ jQuery("#pass-confirm").bind("paste", function() { return false; }) })'); } return array($pass, $pass0); } return $pass; } public function validateOldPass($vars, HTML_QuickForm2_Element_InputPassword $el) { $vars = $el->getContainer()->getValue(); if ($vars['pass'] != '') { if ($vars['_oldpass'] == '') return $this->___('Please enter your current password for validation'); $protector = new Am_Auth_BruteforceProtector( Am_Di::getInstance()->db, Am_Di::getInstance()->config->get('protect.php_include.bruteforce_count', 5), Am_Di::getInstance()->config->get('protect.php_include.bruteforce_delay', 120), Am_Auth_BruteforceProtector::TYPE_USER); if ($wait = $protector->loginAllowed($_SERVER['REMOTE_ADDR'])) { return ___('Please wait %d seconds before next attempt', $wait); } if (!Am_Di::getInstance()->user->checkPassword($vars['_oldpass'])) { $protector->reportFailure($_SERVER['REMOTE_ADDR']); return $this->___('Current password entered incorrectly, please try again'); } } } public function isAcceptableForForm(Am_Form_Bricked $form) { return $form instanceof Am_Form_Profile; } } class Am_Form_Brick_Address extends Am_Form_Brick { protected $labels = array( 'Address Information' => 'Address Information', 'Street' => 'Street', 'Street (Second Line)' => 'Street (Second Line)', 'City' => 'City', 'State' => 'State', 'ZIP Code' => 'ZIP Code', 'Country' => 'Country', ); public function __construct($id = null, $config = null) { $this->name = ___('Address Information'); if (empty($config['fields'])) { $config['fields'] = array( 'street' => 1, 'city' => 1, 'country' => 1, 'state' => 1, 'zip' => 1, ); } parent::__construct($id, $config); } public function insertBrick(HTML_QuickForm2_Container $form) { $fieldSet = $this->getConfig('hide_fieldset') ? $form : $form->addElement('fieldset', 'address', array('id' => 'row-address-0'))->setLabel($this->___('Address Information')); foreach ($this->getConfig('fields', array()) as $f => $required) { switch ($f) { case 'street' : $street = $fieldSet->addText('street', array('class' => 'el-wide'))->setLabel($this->___('Street')); if ($required) $street->addRule('required', ___('Please enter %s', $this->___('Street'))); break; case 'street2' : $street = $fieldSet->addText('street2', array('class' => 'el-wide'))->setLabel($this->___('Street (Second Line)')); if ($required) $street->addRule('required', ___('Please enter %s', $this->___('Street (Second Line)'))); break; case 'city' : $city = $fieldSet->addText('city', array('class' => 'el-wide'))->setLabel($this->___('City')); if ($required) $city->addRule('required', ___('Please enter %s', $this->___('City'))); break; case 'zip' : $zip = $fieldSet->addText('zip')->setLabel($this->___('ZIP Code')); if ($required) $zip->addRule('required', ___('Please enter %s', $this->___('ZIP Code'))); break; case 'country' : $country = $fieldSet->addSelect('country')->setLabel($this->___('Country')) ->setId('f_country') ->loadOptions(Am_Di::getInstance()->countryTable->getOptions(true)); if ($required) $country->addRule('required', ___('Please enter %s', $this->___('Country'))); break; case 'state' : $group = $fieldSet->addGroup()->setLabel($this->___('State')); $stateSelect = $group->addSelect('state') ->setId('f_state') ->loadOptions($stateOptions = Am_Di::getInstance()->stateTable->getOptions(@$_REQUEST['country'], true)); $stateText = $group->addText('state')->setId('t_state'); $disableObj = $stateOptions ? $stateText : $stateSelect; $disableObj->setAttribute('disabled', 'disabled')->setAttribute('style', 'display: none'); if ($required) $group->addRule('required', ___('Please enter %s', $this->___('State'))); break; } } if ($this->getConfig('country_default')) { $form->addDataSource(new HTML_QuickForm2_DataSource_Array(array( 'country' => $this->getConfig('country_default') ))); } } public function setConfigArray(array $config) { // Deal with old style Address required field. if (isset($config['required']) && $config['required'] && !array_key_exists('street_display', $config)) { foreach (array('zip', 'street', 'city', 'state', 'country') as $f) { $config[$f . '_display'] = 1; // Required } } unset($config['required']); if (isset($config['street_display'])) { //backwards compatability //prev it stored as fieldName_display = enum(-1, 0, 1) //-1 - do not display // 0 - display // 1 - display and required isset($config['fields']) || ($config['fields'] = array()); $farr = array('street', 'street2', 'city', 'zip', 'country', 'state'); foreach ($farr as $f) { if (-1 != ($val = @$config[$f . '_display'])) { $config['fields'][$f] = (int) $val; } unset($config[$f . '_display']); } } parent::setConfigArray($config); } public function initConfigForm(Am_Form $form) { $farr = array('street', 'street2', 'city', 'zip', 'country', 'state'); $fieldsVal = $this->getConfig('fields'); $fields = $form->addElement(new Am_Form_Element_AddressFields('fields')); $fields->setLabel(___('Fields To Display')); foreach ($farr as $f) { $attr = array( 'data-label' => ucfirst($f) . ' required', 'data-value' => !empty($fieldsVal[$f]), ); $fields->addOption(ucfirst($f), $f, $attr); } $fields->setJsOptions('{ sortable : true, getOptionName : function (name, option) { return name.replace(/\[\]$/, "") + "[" + option.value + "]"; }, getOptionValue : function (option) { return jQuery(option).data("value"); }, onOptionAdded : function (context, option) { if (jQuery(context).find("input[type=hidden]").val() == 1) { jQuery(context).find("input[type=checkbox]").prop("checked", "checked"); } } }'); $form->addSelect('country_default')->setLabel('Default Country')->loadOptions(Am_Di::getInstance()->countryTable->getOptions(true)); $form->addAdvCheckbox('hide_fieldset')->setLabel(___('Hide Brick Title')); } } class Am_Form_Brick_Phone extends Am_Form_Brick { protected $labels = array( 'Phone Number' => 'Phone Number', ); public function insertBrick(HTML_QuickForm2_Container $form) { $phone = $form->addText('phone')->setLabel($this->___('Phone Number')); if ($this->getConfig('required')) { $phone->addRule('required', ___('Please enter %s', $this->___('Phone Number'))); } } public function initConfigForm(Am_Form $form) { $form->addAdvCheckbox('required')->setLabel(___('Required')); } } class Am_Form_Brick_Product extends Am_Form_Brick { const DISPLAY_ALL = 0; const DISPLAY_CATEGORY = 1; const DISPLAY_PRODUCT = 2; const DISPLAY_BP = 3; const REQUIRE_DEFAULT = 0; const REQUIRE_ALWAYS = 1; const REQUIRE_NEVER = 2; const REQUIRE_ALTERNATE = 3; protected $labels = array( 'Membership Type', 'Please choose a membership type', 'Add Membership' ); protected $hideIfLoggedInPossible = self::HIDE_DONT; protected static $bricksAdded = 0; protected static $bricksWhichCanBeRequiredAdded = 0; protected static $bricksAlternateAdded = 0; public function __construct($id = null, $config = null) { $this->name = ___('Product'); parent::__construct($id, $config); } function shortRender(Product $p, BillingPlan $plan = null) { return $p->getTitle() . ' - ' . $plan->getTerms(); } function renderProduct(Product $p, BillingPlan $plan = null, $short = false) { return $p->defaultRender($plan, $short); } function getProducts() { $ret = array(); switch ($this->getConfig('type', 0)) { case self::DISPLAY_CATEGORY: $ret = Am_Di::getInstance()->productTable->getVisible($this->getConfig('groups', array())); break; case self::DISPLAY_PRODUCT: $ret = array(); $ids = $this->getConfig('products', array()); $arr = Am_Di::getInstance()->productTable->loadIds($ids); foreach ($ids as $id) { foreach ($arr as $p) if ($p->product_id == $id) { if ($p->is_disabled) continue; $ret[] = $p; } } break; case self::DISPLAY_BP: $ret = array(); $ids = array_map('intval', $this->getConfig('bps', array())); //strip bp $arr = Am_Di::getInstance()->productTable->loadIds($ids); foreach ($ids as $id) { foreach ($arr as $p) if ($p->product_id == $id) { if ($p->is_disabled) continue; $ret[] = $p; } } break; default: $ret = Am_Di::getInstance()->productTable->getVisible(null); } $event = new Am_Event(Am_Event::SIGNUP_FORM_GET_PRODUCTS); $event->setReturn($ret); Am_Di::getInstance()->hook->call($event); return $event->getReturn(); } function getBillingPlans($products) { switch ($this->getConfig('type', 0)) { case self::DISPLAY_BP: $map = array(); foreach ($products as $p) { $map[$p->pk()] = $p; } $res = array(); foreach ($this->getConfig('bps', array()) as $item) { list($p_id, $bp_id) = explode('-', $item); if (isset($map[$p_id])) { foreach ($map[$p_id]->getBillingPlans(true) as $bp) { if ($bp->pk() == $bp_id) $res[] = $bp; } } } break; case self::DISPLAY_ALL: case self::DISPLAY_CATEGORY: case self::DISPLAY_PRODUCT: default: $res = array(); foreach ($products as $product) { $res = array_merge($res, $product->getBillingPlans(true)); } } $e = new Am_Event(Am_Event::SIGNUP_FORM_GET_BILLING_PLANS); $e->setReturn($res); Am_Di::getInstance()->hook->call($e); return $e->getReturn(); } function getProductsFiltered() { $products = $this->getProducts(); if ($this->getConfig('display-type', 'hide') == 'display') return $products; $user = Am_Di::getInstance()->auth->getUser(); $haveActive = $haveExpired = array(); if (!is_null($user)) { $haveActive = $user->getActiveProductIds(); $haveExpired = $user->getExpiredProductIds(); } $ret = Am_Di::getInstance()->productTable ->filterProducts($products, $haveActive, $haveExpired, $this->getConfig('input-type') == 'checkbox' ? true : false); $event = new Am_Event(Am_Event::SIGNUP_FORM_GET_PRODUCTS_FILTERED); $event->setReturn($ret); Am_Di::getInstance()->hook->call($event); return $event->getReturn(); } public function insertProductOptions(HTML_QuickForm2_Container $form, $pid, array $productOptions, BillingPlan $plan) { foreach ($productOptions as $option) { $elName = 'productOption[' . $pid . '][0][' . $option->name . ']'; $isEmpty = empty($_POST['productOption'][$pid][0][$option->name]); /* @var $option ProductOption */ $el = null; switch ($option->type) { case 'text': $el = $form->addElement('text', $elName); if ($isEmpty) $el->setValue($option->getDefault()); break; case 'radio': $el = $form->addElement('advradio', $elName); $el->loadOptions($option->getSelectOptionsWithPrice($plan)); if ($isEmpty) $el->setValue($option->getDefault()); break; case 'select': $el = $form->addElement('select', $elName); $el->loadOptions($option->getSelectOptionsWithPrice($plan)); if ($isEmpty) $el->setValue($option->getDefault()); break; case 'multi_select': $el = $form->addElement('magicselect', $elName); $el->loadOptions($option->getSelectOptionsWithPrice($plan)); if ($isEmpty) $el->setValue($option->getDefaults()); break; case 'textarea': $el = $form->addElement('textarea', $elName, 'class=el-wide rows=5'); if ($isEmpty) $el->setValue($option->getDefault()); break; case 'checkbox': $opts = $option->getSelectOptionsWithPrice($plan); if ($opts) { $el = $form->addGroup($elName); $el->setSeparator("
"); foreach ($opts as $k => $v) { $chkbox = $el->addAdvCheckbox(null, array('value' => $k))->setContent(___($v)); if ($isEmpty && in_array($k, (array)$option->getDefaults())) $chkbox->setAttribute('checked', 'checked'); } $el->addHidden(null, array('value' => '')); $el->addFilter('array_filter'); } else { $el = $form->addElement('advcheckbox', $elName); } break; case 'date': $el = $form->addElement('date', $elName); break; } if ($el && $option->is_required) { // onblur client set to only validate option fields with javascript // else there is a problem with hidden fields as quickform2 does not skip validation for hidden $el->addRule('required', ___('This field is required'), null, HTML_QuickForm2_Rule::ONBLUR_CLIENT); } $el->setLabel(___($option->title)); } } public function insertBrick(HTML_QuickForm2_Container $form) { $product_paysys = Am_Di::getInstance()->config->get('product_paysystem'); $base_name = 'product_id_' . $form->getId(); $name = self::$bricksAdded ? $base_name . '_' . self::$bricksAdded : $base_name; $productOptions = array(); $products = $this->getProductsFiltered(); if (!$products) { if ($this->getConfig('require', self::REQUIRE_DEFAULT) == self::REQUIRE_NEVER) return; throw new Am_Exception_QuietError(___("There are no products available for purchase. Please come back later.")); } self::$bricksAdded++; if ($this->getConfig('require', self::REQUIRE_DEFAULT) != self::REQUIRE_NEVER) self::$bricksWhichCanBeRequiredAdded++; if ($this->getConfig('require', self::REQUIRE_DEFAULT) == self::REQUIRE_ALTERNATE) self::$bricksAlternateAdded++; $options = $shortOptions = $attrs = $dataOptions = array(); if ($this->getConfig('empty-option')) { $shortOptions[null] = $this->getConfig('empty-option-text', ___('Please select')); $options[null] = '' . $shortOptions[null] . ''; $attrs[null] = array(); $dataOptions[null] = array( 'value' => null, 'label' => $options[null], 'selected' => false, 'variable_qty' => false, 'qty' => 1,); } foreach ($this->getBillingPlans($products) as $plan) { $p = $plan->getProduct(); $pid = $p->product_id . '-' . $plan->plan_id; $options[$pid] = $this->renderProduct($p, $plan); $shortOptions[$pid] = $this->shortRender($p, $plan); $attrs[$pid] = array( 'data-first_price' => $plan->first_price, 'data-second_price' => $plan->second_price, 'data-paysys' => $product_paysys && $plan->paysys_id ); $dataOptions[$pid] = array( 'label' => $options[$pid], 'value' => $pid, 'variable_qty' => $plan->variable_qty, 'qty' => $plan->qty, 'selected' => false, ); $productOptions[$pid] = $p->getOptions(); $billingPlans[$pid] = $plan; } $inputType = $this->getConfig('input-type', 'advradio'); if (count($options) == 1) { if ($this->getConfig('hide_if_one')) $inputType = 'none'; elseif ($inputType != 'checkbox') $inputType = 'hidden'; } $oel = null; //outer element $productOptionsDontHide = false; switch ($inputType) { case 'none': list($pid, $label) = each($options); $oel = $el = $form->addHidden($name, $attrs[$pid]); $el->setValue($pid); $el->toggleFrozen(true); $productOptionsDontHide = true; // normally options display with js but not in this case! break; case 'checkbox': $data = array(); foreach ($this->getBillingPlans($products) as $plan) { $p = $plan->getProduct(); $data[$p->product_id . '-' . $plan->pk()] = array( 'data-first_price' => $plan->first_price, 'data-second_price' => $plan->second_price, 'data-paysys' => $product_paysys && $plan->paysys_id, 'options' => array( 'value' => $p->product_id . '-' . $plan->pk(), 'label' => $this->renderProduct($p, $plan), 'variable_qty' => $plan->variable_qty, 'qty' => $plan->qty, 'selected' => false, ), ); } if ($this->getConfig('display-popup')) { $search = ''; if ($this->getConfig('cat-filter')) { $all_cats = array(); foreach ($this->getBillingPlans($products) as $plan) { $p = $plan->getProduct(); $p_cats = $p->getCategoryTitles(); $all_cats = array_merge($all_cats, $p_cats); $data[$p->product_id . '-' . $plan->pk()]['rel'] = implode(', ', array_merge(array('All'), $p_cats)); } $exclude = array_map(function($el) {return $el->title;}, $_ = Am_Di::getInstance()->productCategoryTable->loadIds($this->getConfig('cat-filter-exclude', array()))); $all_cats = array_unique($all_cats); $all_cats = array_diff($all_cats, $exclude); sort($all_cats); array_unshift($all_cats, 'All'); foreach ($all_cats as $t) { $search[] = sprintf('%s', $t, $t); } $search = sprintf('
%s
', implode(' | ', $search)); } $oel = $gr = $form->addGroup(); $gr->addStatic() ->setContent(sprintf('
', $name)); $gr->addStatic() ->setContent(sprintf('', $name, $this->___('Membership Type'), $this->___('Add Membership'))); $gr->addStatic() ->setContent(sprintf(''); $form->addScript() ->setScript(<<'). append(' '). append(jQuery(this).parent().html().replace(//g, '')) ); }) jQuery('#' + name + '-list input[type=checkbox]').change(); } jQuery(function(){ jQuery('.am-brick-product-popup-cat').click(function(){ jQuery(this).parent().find('a').removeClass('am-brick-product-popup-cat-active'); jQuery(this).addClass('am-brick-product-popup-cat-active'); var \$q = jQuery(this).closest('.am-brick-product-popup').find('input[type=checkbox]'); \$q.closest('label').show(). next().show(); \$q.not('[rel*="' + jQuery(this).data('title') + '"]').closest('label').hide(). next().hide(); }) jQuery('#$name').click(function(){ jQuery('#$name-list').amPopup({ title : jQuery(this).data('title'), width : 450, onClose: function(){ propogateChanges('$name'); } }); return false; }) propogateChanges('$name'); }); CUT ); } else { $oel = $el = $form->addElement(new Am_Form_Element_SignupCheckboxGroup($name, $data, 'checkbox')); } break; case 'select': $oel = $el = $form->addSelect($name); foreach ($shortOptions as $pid => $label) $el->addOption($label, $pid, empty($attrs[$pid]) ? null : $attrs[$pid]); break; case 'hidden': case 'advradio': default: $data = array(); $first = 0; foreach ($options as $pid => $label) { $data[$pid] = $attrs[$pid]; $data[$pid]['options'] = $dataOptions[$pid]; if (!$first++ && Am_Di::getInstance()->request->isGet()) // pre-check first option $data[$pid]['options']['selected'] = true; } $oel = $el = $form->addElement(new Am_Form_Element_SignupCheckboxGroup($name, $data, $inputType == 'advradio' ? 'radio' : $inputType)); break; } $oel->setLabel($this->___('Membership Type')); if ($this->getConfig('no_label')) { $oel->setAttribute('class', 'no-label'); } switch ($this->getConfig('require', self::REQUIRE_DEFAULT)) { case self::REQUIRE_DEFAULT : if (self::$bricksWhichCanBeRequiredAdded == 1) $el->addRule('required', $this->___('Please choose a membership type')); break; case self::REQUIRE_ALWAYS : $el->addRule('required', $this->___('Please choose a membership type')); break; case self::REQUIRE_NEVER : break; case self::REQUIRE_ALTERNATE : if (self::$bricksAlternateAdded == 1) { $f = $form; while ($container = $f->getContainer()) $f = $container; $f->addRule('callback2', $this->___('Please choose a membership type'), array($this, 'formValidate')); } break; default: throw new Am_Exception_InternalError('Unknown require type [%s] for product brick', $this->getConfig('require', self::REQUIRE_DEFAULT)); } if (self::$bricksAdded == 1) { $script = <<addScript()->setScript($script); } if (($d = $this->getConfig('default')) && $inputType != 'none' && $inputType != 'hidden') { $form->addDataSource(new HTML_QuickForm2_DataSource_Array(array( $name => ($inputType == 'checkbox' || $inputType == 'advradio') ? array($d) : $d ))); } foreach ($productOptions as $pid => $productOptions) { if ($productOptions) { $fs = $form->addElement('fieldset', '', array( 'class' => 'am-product-options-' . $pid, 'style' => $productOptionsDontHide ? '' : 'display:none;')); $this->insertProductOptions($fs, $pid, $productOptions, $billingPlans[$pid]); } } } public function initConfigForm(Am_Form $form) { $radio = $form->addSelect('type') ->setLabel(___('What to Display')); $radio->loadOptions(array( self::DISPLAY_ALL => ___('Display All Products'), self::DISPLAY_CATEGORY => ___('Products from selected Categories'), self::DISPLAY_PRODUCT => ___('Only Products selected below'), self::DISPLAY_BP => ___('Only Billing Plans selected below') )); $groups = $form->addMagicSelect('groups', array('data-type' => self::DISPLAY_CATEGORY,))->setLabel(___('Product Gategories')); $groups->loadOptions(Am_Di::getInstance()->productCategoryTable->getAdminSelectOptions(array(ProductCategoryTable::COUNT => 1))); $products = $form->addSortableMagicSelect('products', array('data-type' => self::DISPLAY_PRODUCT,))->setLabel(___('Product(s) to display')); $products->loadOptions(Am_Di::getInstance()->productTable->getOptions(true)); $bpOptions = array(); foreach (Am_Di::getInstance()->productTable->getVisible() as $product) { /* @var $product Product */ foreach ($product->getBillingOptions() as $bp_id => $title) { $bpOptions[$product->pk() . '-' . $bp_id] = sprintf('%s (%s)', $product->title, $title); } } $bps = $form->addSortableMagicSelect('bps', array('data-type' => self::DISPLAY_BP,))->setLabel(___('Billing Plan(s) to display')); $bps->loadOptions($bpOptions); $form->addSelect('default') ->setLabel(___('Select by default')) ->loadOptions(array(''=>'') + $bpOptions); $inputType = $form->addSelect('input-type')->setLabel(___('Input Type')); $inputType->loadOptions(array( 'advradio' => ___('Radio-buttons (one product can be selected)'), 'select' => ___('Select-box (one product can be selected)'), 'checkbox' => ___('Checkboxes (multiple products can be selected)'), )); $form->addAdvCheckbox('display-popup') ->setlabel(___('Display Products in Popup')); $form->addAdvCheckbox('cat-filter') ->setlabel(___('Add Category Filter to Popup')); $form->addMagicSelect('cat-filter-exclude', array('class'=>'cat-filter-exclude')) ->setLabel(___('Exclude the following categories from Filter')) ->loadOptions(Am_Di::getInstance()->productCategoryTable->getOptions()); $form->addSelect('display-type')->setLabel(___('If product is not available because of require/disallow settings')) ->loadOptions(array( 'hide' => ___('Remove It From Signup Form'), 'display' => ___('Display It Anyway') )); $form->addCheckboxedGroup('empty-option') ->setLabel(___("Add an 'empty' option to select box\nto do not choose any products")) ->addText('empty-option-text'); $form->addAdvCheckbox('hide_if_one') ->setLabel(___("Hide Select\n" . 'if there is only one choice')); $form->addAdvRadio('require') ->setLabel(___('Require Behaviour')) ->loadOptions(array( self::REQUIRE_DEFAULT => sprintf('%s: %s', ___('Default'), ___('Make this Brick Required Only in Case There is not any Required Brick on Page Above It')), self::REQUIRE_ALWAYS => sprintf('%s: %s', ___('Always'), ___('Force User to Choose Some Product from this Brick')), self::REQUIRE_NEVER => sprintf('%s: %s', ___('Never'), ___('Products in this Brick is Optional (Not Required)')), self::REQUIRE_ALTERNATE => sprintf('%s: %s', ___('Alternate'), ___('User can Choose Product in any Brick of Such Type on Page but he Should Choose at least One Product still')))) ->setValue(self::REQUIRE_DEFAULT); $formId = $form->getId(); $script = <<addScript()->setScript($script); $form->addAdvCheckbox('no_label')->setLabel(___('Remove Label')); } public function formValidate(array $values) { foreach ($values as $k => $v) if (strpos($k, 'product_id') === 0) if (!empty($v)) return; return $this->___('Please choose a membership type'); } public function isAcceptableForForm(Am_Form_Bricked $form) { return $form instanceof Am_Form_Signup; } public function isMultiple() { return true; } } class Am_Form_Brick_Paysystem extends Am_Form_Brick { protected $labels = array( 'Payment System', 'Please choose a payment system', ); protected $hide_if_one = false; protected $hideIfLoggedInPossible = self::HIDE_DONT; public function __construct($id = null, $config = null) { $this->name = ___('Payment System'); parent::__construct($id, $config); } function renderPaysys(Am_Paysystem_Description $p) { return sprintf('%s %s', $p->getId(), ___($p->getTitle()), $p->getId(), ___($p->getDescription())); } public function getPaysystems() { $psList = Am_Di::getInstance()->paysystemList->getAllPublic(); $_psList = array(); foreach ($psList as $k => $ps) { $_psList[$ps->getId()] = $ps; } $psEnabled = $this->getConfig('paysystems', array_keys($_psList)); $event = new Am_Event(Am_Event::SIGNUP_FORM_GET_PAYSYSTEMS); $event->setReturn($psEnabled); Am_Di::getInstance()->hook->call($event); $psEnabled = $event->getReturn(); //we want same order of paysystems as in $psEnabled $ret = array(); foreach ($psEnabled as $psId) { if (isset($_psList[$psId])) $ret[] = $_psList[$psId]; } return $ret; } public function insertBrick(HTML_QuickForm2_Container $form) { $paysystems = $this->getPaysystems(); if ((count($paysystems) == 1) && $this->getConfig('hide_if_one')) { reset($paysystems); $form->addHidden('paysys_id')->setValue(current($paysystems)->getId())->toggleFrozen(true); return; } $psOptions = $psHide = $psIndex = array(); foreach ($paysystems as $ps) { $psOptions[$ps->getId()] = $this->renderPaysys($ps); $psIndex[$ps->getId()] = $ps; $psHide[$ps->getId()] = Am_Di::getInstance()->plugins_payment->loadGet($ps->getId())->hideBricks(); } $psHide = json_encode($psHide); if (count($paysystems) != 1) { $attrs = array('id' => 'paysys_id'); $el0 = $el = $form->addAdvRadio('paysys_id', array('id' => 'paysys_id'), array('intrinsic_validation' => false)); $first = 0; foreach ($psOptions as $k => $v) { $attrs = array( 'data-recurring' => json_encode((bool)$psIndex[$k]->isRecurring()) ); if (!$first++ && Am_Di::getInstance()->request->isGet()) $attrs['checked'] = 'checked'; $el->addOption($v, $k, $attrs); } } else { /** @todo display html here */ reset($psOptions); $el = $form->addStatic('_paysys_id', array('id' => 'paysys_id'))->setContent(current($psOptions)); $el->toggleFrozen(true); $el0 = $form->addHidden('paysys_id')->setValue(key($psOptions)); } $el0->addRule('required', $this->___('Please choose a payment system'), // the following is added to avoid client validation if select is hidden null, HTML_QuickForm2_Rule::SERVER); $el0->addFilter('filterId'); $el->setLabel($this->___('Payment System')); /** * hide payment system selection if: * - there are only free products in the form * - there are selected products, and all of them are free * - option product_psysytem is enabled and user choose product with assign payment system */ $form->addScript()->setScript(<<0) || (jQuery(this).data('second_price')>0)) count_paid++; else count_free++; if (jQuery(this).data('second_price')>0) count_recurring++; if (jQuery(this).data('paysys')) count_paysys++; else count_no_paysys++; }); jQuery(":checkbox[name^='product_id'], select[name^='product_id'] option, :radio[name^='product_id'], input[type=hidden][name^='product_id']").each(function(){ if ((jQuery(this).data('first_price')>0) || (jQuery(this).data('second_price')>0)) total_count_paid++; else total_count_free++; if (jQuery(this).data('paysys')) total_count_paysys++; else total_count_no_paysys++; }); if (count_recurring) { jQuery('[type=radio][name=paysys_id]').each(function(){ if (!$(this).data('recurring') && this.checked) { $("[name='paysys_id'][data-recurring=true]:first").prop('checked', true); } $(this).closest('label').toggle($(this).data('recurring')); }) } else { jQuery('[type=radio][name=paysys_id]').closest('label').show(); } if ( ((count_free && !count_paid) || (!total_count_paid && total_count_free) || (!total_count_no_paysys && total_count_paysys) || (count_paysys)) && (total_count_paid + total_count_free)>0) { // hide select jQuery("#row-paysys_id").hide().after(""); } else { // show select jQuery("#row-paysys_id").show(); jQuery(".hidden-paysys_id").remove(); } }).change(); window.psHiddenBricks = []; jQuery("input[name='paysys_id']").change(function(){ if (!this.checked) return; var val = jQuery(this).val(); var hideBricks = $psHide; jQuery.each(window.psHiddenBricks, function(k,v){ jQuery('#row-'+v+'-0').show(); }); window.psHiddenBricks = hideBricks[val]; if (window.psHiddenBricks) { jQuery.each(window.psHiddenBricks, function(k,v){ jQuery('#row-'+v+'-0').hide(); }); } }).change(); }); CUT ); } public function isAcceptableForForm(Am_Form_Bricked $form) { return $form instanceof Am_Form_Signup; } public function initConfigForm(Am_Form $form) { Am_Di::getInstance()->plugins_payment->loadEnabled(); $ps = $form->addSortableMagicSelect('paysystems') ->setLabel(___("Payment Options\n" . 'if none selected, all enabled will be displayed')) ->loadOptions(Am_Di::getInstance()->paysystemList->getOptionsPublic()); $form->addAdvCheckbox('hide_if_one') ->setLabel(___("Hide Select\n" . 'if there is only one choice')); } } class Am_Form_Brick_Recaptcha extends Am_Form_Brick { protected $name = 'reCAPTCHA'; protected $labels = array( 'Anti Spam', 'Anti Spam check failed' ); /** @var HTML_QuickForm2_Element_Static */ protected $static; public function initConfigForm(Am_Form $form) { $form->addSelect('theme') ->setLabel(___("reCAPTCHA Theme")) ->loadOptions(array('light' => 'light', 'dark' => 'dark')); $form->addSelect('size') ->setLabel(___("reCAPTCHA Size")) ->loadOptions(array('normal' => 'normal', 'compact' => 'compact')); } public function insertBrick(HTML_QuickForm2_Container $form) { $captcha = $form->addGroup() ->setLabel($this->___("Anti Spam")); $captcha->addRule('callback', $this->___('Anti Spam check failed'), array($this, 'validate')); $this->static = $captcha->addStatic('captcha')->setContent(Am_Di::getInstance()->recaptcha ->render($this->getConfig('theme'), $this->getConfig('size'))); } public static function createAvailableBricks($className) { return Am_Recaptcha::isConfigured() ? parent::createAvailableBricks($className) : array(); } public function validate() { $form = $this->static; while ($np = $form->getContainer()) $form = $np; foreach ($form->getDataSources() as $ds) { if ($resp = $ds->getValue('g-recaptcha-response')) break; } $status = false; if ($resp) $status = Am_Di::getInstance()->recaptcha->validate($resp); return $status; } } class Am_Form_Brick_Coupon extends Am_Form_Brick { protected $labels = array( 'Enter coupon code', 'No coupons found with such coupon code', 'Please enter coupon code' ); protected $hideIfLoggedInPossible = self::HIDE_DONT; public function __construct($id = null, $config = null) { $this->name = ___('Coupon'); parent::__construct($id, $config); } public function initConfigForm(Am_Form $form) { $form->addAdvCheckbox('required') ->setLabel(___('Required')); } public function insertBrick(HTML_QuickForm2_Container $form) { $coupon = $form->addText('coupon', array('size' => 15)) ->setLabel($this->___('Enter coupon code')); if ($this->getConfig('required')) { $coupon->addRule('required', $this->___('Please enter coupon code')); } $coupon->addRule('callback2', '--error--', array($this, 'validateCoupon')) ->addRule('remote', '--error--', array( 'url' => Am_Di::getInstance()->url('ajax', array('do'=>'check_coupon'), false), )); } function validateCoupon($value) { if ($value == "") return null; $coupon = htmlentities($value); $coupon = Am_Di::getInstance()->couponTable->findFirstByCode($coupon); $msg = $coupon ? $coupon->validate(Am_Di::getInstance()->auth->getUserId()) : $this->___('No coupons found with such coupon code'); return $msg === null ? null : $msg; } public function isAcceptableForForm(Am_Form_Bricked $form) { return $form instanceof Am_Form_Signup; } } class Am_Form_Brick_Field extends Am_Form_Brick { const TYPE_NORMAL = 'normal'; const TYPE_READONLY = 'disabled'; const TYPE_HIDDEN = 'hidden'; protected $field = null; static function createAvailableBricks($className) { $res = array(); foreach (Am_Di::getInstance()->userTable->customFields()->getAll() as $field) { if (strpos($field->name, 'aff_') === 0) continue; // Do not create bricks for fields started with _ if (strpos($field->name, '_') === 0) continue; $res[] = new self('field-' . $field->getName()); } return $res; } public function __construct($id = null, $config = null) { $fieldName = str_replace('field-', '', $id); $this->field = Am_Di::getInstance()->userTable->customFields()->get($fieldName); // to make it fault-tolerant when customfield is deleted if (!$this->field) $this->field = new Am_CustomFieldText($fieldName, $fieldName); $this->labels = array($this->field->title, $this->field->description); parent::__construct($id, $config); } function getName() { return $this->field->title; } function insertBrick(HTML_QuickForm2_Container $form) { if (!$this->getConfig('skip_access_check') && isset($this->field->from_config) && $this->field->from_config) { $hasAccess = Am_Di::getInstance()->auth->getUserId() ? Am_Di::getInstance()->resourceAccessTable->userHasAccess(Am_Di::getInstance()->auth->getUser(), amstrtoint($this->field->name), Am_CustomField::ACCESS_TYPE) : Am_Di::getInstance()->resourceAccessTable->guestHasAccess(amstrtoint($this->field->name), Am_CustomField::ACCESS_TYPE); if (!$hasAccess) return; } $this->field->title = $this->___($this->field->title); $this->field->description = $this->___($this->field->description); if ($this->getConfig('validate_custom')) { $this->field->validateFunc = $this->getConfig('validate_func'); } switch ($this->getConfig('display_type', self::TYPE_NORMAL)) { case self::TYPE_HIDDEN : $v = $this->getConfig('value'); $form->addHidden($this->field->getName()) ->setValue($v ? $v : @$this->field->default); break; case self::TYPE_READONLY : $el = $this->field->addToQF2($form); $el->toggleFrozen(true); break; case self::TYPE_NORMAL : $this->field->addToQF2($form); break; default: throw new Am_Exception_InternalError(sprintf('Unknown display type [%s] in %s::%s', $this->getConfig('display_type', self::TYPE_NORMAL), __CLASS__, __METHOD__)); } if ($this->getConfig('cond_enabled')) { $c_cond = $this->getConfig('cond_type') > 0 ? '==' : '!='; $c_field_val = json_encode($this->getConfig('cond_field_val')); $c_field_name = $this->getConfig('cond_field'); $name = $this->field->name; $form->addScript() ->setScript(<< 1 ? el.filter("[value='" + $c_field_val + "']").val() : el.val(); break; } val = val || 0; jQuery('[name=$name]').closest('.row').toggle(val $c_cond $c_field_val); }).change(); CUT ); } } function getFieldName() { return $this->field->name; } public function initConfigForm(Am_Form $form) { $id = $this->field->name . '-display-type'; $id_value = $this->field->name . '-value'; $form->addSelect('display_type') ->setLabel(___('Display Type')) ->setId($id) ->loadOptions(array( self::TYPE_NORMAL => ___('Normal'), self::TYPE_READONLY => ___('Read-only'), self::TYPE_HIDDEN => ___('Hidden') )); $form->addText('value', array( 'placeholder' => ___('Keep empty to use default value from field settings'), 'class' => 'el-wide' )) ->setId($id_value) ->setLabel(___("Default Value for this field\n" . 'hidden field will be populated with this value')); $type_hidden = self::TYPE_HIDDEN; $form->addScript() ->setScript(<<field->name . '-validate_custom'; $id_validate_func = $this->field->name . '-validate_func'; $form->addAdvCheckbox('validate_custom', array('id' => $id_validate_custom)) ->setLabel(___("Use Custom Validation Settings\n" . 'otherwise Validation settings from field definition is used')); $form->addMagicSelect('validate_func', array('id' => $id_validate_func)) ->setLabel(___('Validation')) ->loadOptions(array( 'required' => ___('Required Value'), 'integer' => ___('Integer Value'), 'numeric' => ___('Numeric Value'), 'email' => ___('E-Mail Address'), 'emails' => ___('List of E-Mail Address'), 'url' => ___('URL'), 'ip' => ___('IP Address') )); $form->addScript() ->setScript(<<addAdvCheckbox('skip_access_check') ->setLabel(___("Do not check Access Permissions\n" . "for this field on this form (show it without any conditions)")); list($fields, $allOp) = $this->getEnumFieldOptions($this->field->name); if ($fields) { $allOp = json_encode($allOp); $current_val = json_encode($this->getConfig('cond_field_val')); $gr = $form->addGroup(); $gr->setLabel(___('Conditional')); $gr->setSeparator(' '); $gr->addAdvCheckbox('cond_enabled'); $gr->addSelect('cond_type') ->loadOptions(array( '1' => ___('Show'), '-1' => ___('Hide') )); $l_if = ___('if'); $gr->addHtml() ->setHtml(" $l_if "); $gr->addSelect('cond_field') ->loadOptions($fields); $gr->addHtml() ->setHtml(' = '); $gr->addSelect('cond_field_val', null, array('intrinsic_validation' => false)) ->loadOptions(array()); $id = $form->getId(); $gr->addScript() ->setScript(<<').text(cOpt[k]).attr('value', k) ); } if (current_val != undefined) { $("#$id [name=cond_field_val]").val(current_val); current_val = undefined; } else { $("#$id [name=cond_field_val]").val($("#$id [name=cond_field_val] option:first").attr('value')); } }).change(); }); CUT ); } } function getEnumFieldOptions($myName) { $fields = array(); $options = array(); foreach (Am_Di::getInstance()->userTable->customFields()->getAll() as $fd) { if ($myName == $fd->name) continue; if (in_array($fd->type, array('radio', 'select', 'checkbox', 'single_checkbox'))) { $fields[$fd->name] = $fd->title; $options[$fd->name] = $fd->type == 'single_checkbox' ? array(1 => ___('Checked'), 0 => ___('Unchecked')) : $fd->options; } } return array($fields, $options); } public function setConfigArray(array $config) { //backwards compatiability if (isset($config['disabled'])) { $config['display_type'] = $config['disabled'] ? self::TYPE_READONLY : self::TYPE_NORMAL; unset($config['disabled']); } if (!isset($config['display_type'])) $config['display_type'] = self::TYPE_NORMAL; parent::setConfigArray($config); } } class Am_Form_Brick_Agreement extends Am_Form_Brick { protected $labels = array( 'User Agreement', 'I have read and agree to the Terms & Conditions', 'Please agree to User Agreement', ); public function __construct($id = null, $config = null) { $this->name = ___('User Agreement'); parent::__construct($id, $config); } public function insertBrick(HTML_QuickForm2_Container $form) { if (($form instanceof Am_Form_Signup_Aff) || ($form->getId() == 'aff')) { // little trick - if we are in affiliate form, replace word "User" to "Affiliate" $this->labels['User Agreement'] = ___('Affiliate Agreement'); $this->labels['Please agree to User Agreement'] = ___('Please agree to Affiliate Agreement'); } if ($this->getConfig('do_not_show_agreement_text')) { $conteiner = $form; } else { $conteiner = $form->addFieldset()->setId('fieldset-agreement')->setLabel($this->___('User Agreement')); $agreement = $conteiner->addStatic('_agreement', array('class' => 'no-label')); $agreement->setContent('
' . $this->getText() . '
'); } $data = array(); if ($this->getConfig('no_label')) { $data['content'] = $this->___('I have read and agree to the Terms & Conditions'); } $checkbox = $conteiner->addAdvCheckbox('i_agree', array(), $data); if (!$this->getConfig('no_label')) { $checkbox->setLabel($this->___('I have read and agree to the Terms & Conditions')); } $checkbox->addRule('required', $this->___('Please agree to User Agreement')); if ($this->getConfig('checked')) { $checkbox->setAttribute('checked'); } } public function getText() { return $this->getConfig('isHtml') ? $this->getConfig('text') : Am_Html::escape($this->getConfig('text')); } public function isAcceptableForForm(Am_Form_Bricked $form) { return $form instanceof Am_Form_Signup; } public function initConfigForm(Am_Form $form) { $form->addAdvCheckbox("do_not_show_agreement_text") ->setLabel(___("Does not show Agreement Text\n" . 'display only tick box')) ->setId('do-not-show-agreement-text'); $form->addAdvCheckbox("isHtml") ->setLabel(___('Is Html?')) ->setAttribute('rel', 'agreement-text'); $form->addTextarea("text", array('rows' => 20, 'class' => 'el-wide')) ->setLabel(___('Agreement text')) ->setAttribute('rel', 'agreement-text'); $form->addScript() ->setScript(<<addAdvCheckbox('no_label') ->setLabel(___("Hide Label")); $form->addAdvCheckbox('checked') ->setLabel(___("Checked by Default")); } } class Am_Form_Brick_PageSeparator extends Am_Form_Brick { protected $labels = array( 'title', 'back', 'next', ); protected $hideIfLoggedInPossible = self::HIDE_DONT; public function __construct($id = null, $config = null) { $this->name = ___('Form Page Break'); parent::__construct($id, $config); } public function insertBrick(HTML_QuickForm2_Container $form) { // nop; } public function isAcceptableForForm(Am_Form_Bricked $form) { return (bool)$form->isMultiPage(); } public function isMultiple() { return true; } } class Am_Form_Brick_UserGroup extends Am_Form_Brick { protected $hideIfLoggedInPossible = self::HIDE_DONT; public function init() { Am_Di::getInstance()->hook->add(Am_Event::SIGNUP_USER_ADDED, array($this, 'assignGroups')); Am_Di::getInstance()->hook->add(Am_Event::SIGNUP_USER_UPDATED, array($this, 'assignGroups')); } public function assignGroups(Am_Event $event) { /* @var $user User */ $user = $event->getUser(); $existing = $user->getGroups(); $new = $this->getConfig('groups', array()); $user->setGroups(array_unique(array_merge($existing, $new))); } public function __construct($id = null, $config = null) { $this->name = ___('Assign User Groups (HIDDEN)'); parent::__construct($id, $config); } public function isAcceptableForForm(Am_Form_Bricked $form) { return $form instanceof Am_Form_Signup; } public function initConfigForm(Am_Form $form) { $form->addMagicSelect('groups') ->loadOptions(Am_Di::getInstance()->userGroupTable->getSelectOptions()) ->setLabel(___('Add user to these groups')); } public function insertBrick(HTML_QuickForm2_Container $form) { // nothing to do. } } class Am_Form_Brick_ManualAccess extends Am_Form_Brick { protected $hideIfLoggedInPossible = self::HIDE_DONT; public function init() { Am_Di::getInstance()->hook->add(Am_Event::SIGNUP_USER_ADDED, array($this, 'addAccess')); } public function addAccess(Am_Event $event) { /* @var $user User */ $user = $event->getUser(); $product_ids = $this->getConfig('product_ids'); if (!$product_ids) return; foreach ($product_ids as $id) { $product = Am_Di::getInstance()->productTable->load($id, false); if (!$product) continue; //calucalet access dates $invoice = Am_Di::getInstance()->invoiceRecord; $invoice->setUser($user); $invoice->add($product); $begin_date = $product->calculateStartDate(Am_Di::getInstance()->sqlDate, $invoice); $p = new Am_Period($product->getBillingPlan()->first_period); $expire_date = $p->addTo($begin_date); $access = Am_Di::getInstance()->accessRecord; $access->setForInsert(array( 'user_id' => $user->pk(), 'product_id' => $product->pk(), 'begin_date' => $begin_date, 'expire_date' => $expire_date, 'qty' => 1 )); $access->insert(); Am_Di::getInstance()->emailTemplateTable->sendZeroAutoresponders($user, $access); } } public function __construct($id = null, $config = null) { $this->name = ___('Add Subscription Before Payment (HIDDEN)'); parent::__construct($id, $config); } public function isAcceptableForForm(Am_Form_Bricked $form) { return $form instanceof Am_Form_Signup; } public function initConfigForm(Am_Form $form) { $form->addMagicSelect('product_ids') ->loadOptions(Am_Di::getInstance()->productTable->getOptions()) ->setLabel(___( "Add Subscription to the following products\n" . "right after signup form has been submitted, " . "subscription will be added only for new users")); } public function insertBrick(HTML_QuickForm2_Container $form) { // nothing to do. } } class Am_Form_Brick_Fieldset extends Am_Form_Brick { protected $labels = array( 'Fieldset title' ); public function __construct($id = null, $config = null) { $this->name = ___('Fieldset'); parent::__construct($id, $config); } public function insertBrick(HTML_QuickForm2_Container $form) { $fieldSet = $form->addElement('fieldset', 'fieldset')->setLabel($this->___('Fieldset title')); } public function isMultiple() { return true; } } class Am_Form_Brick_RandomQuestions extends Am_Form_Brick { protected $labels = array( 'Please answer above question', 'Your answer is wrong' ); public function __construct($id = null, $config = null) { $this->name = ___('Random Questions'); parent::__construct($id, $config); } public function isMultiple() { return false; } public function insertBrick(HTML_QuickForm2_Container $form) { if (!$this->getConfig('questions')) return; $questions = array(); foreach (explode(PHP_EOL, $this->getConfig('questions')) as $line) { $line = explode('|', $line); $questions[] = array_shift($line); } $q_id = array_rand($questions); $question = $form->addText('question') ->setLabel($questions[$q_id] . "\n" . $this->___('Please answer above question')); $question->addRule('callback', $this->___('Your answer is wrong'), array($this, 'validate')); $form->addHidden('q_id')->setValue($q_id)->toggleFrozen(true); //setValue does not work right second time $_POST['q_id_sent'] = @$_POST['q_id']; $_POST['q_id'] = $q_id; } public function initConfigForm(Am_Form $form) { $form->addTextarea('questions', array('rows' => 10, 'class'=>'el-wide')) ->setLabel(___("Questions with possible answers\n" . "one question per line\n" . "question and answers should be\n" . "separated by pipe, for example\n" . "Question1?|Answer1|Answer2|Answer3\n" . "Question2?|Answer1|Answer2\n" . "register of answers does not matter")); } public function validate($answer) { if (!$answer) return false; $lines = explode(PHP_EOL, $this->getConfig('questions')); $line = $lines[(isset($_POST['q_id_sent']) ? $_POST['q_id_sent'] : $_POST['q_id'])]; $q_ = explode('|', strtolower(trim($line))); array_shift($q_); if (@in_array(strtolower($answer), $q_)) return true; else return false; } } class Am_Form_Brick_Unsubscribe extends Am_Form_Brick { protected $labels = array( 'Unsubscribe from all e-mail messages' ); public function init() { Am_Di::getInstance()->hook->add(Am_Event::PROFILE_USER_UPDATED, array($this, 'triggerEvent')); } public function insertBrick(HTML_QuickForm2_Container $form) { $form->addAdvCheckbox('unsubscribed') ->setLabel($this->___('Unsubscribe from all e-mail messages')); } public function isAcceptableForForm(Am_Form_Bricked $form) { return $form instanceof Am_Form_Profile; } public function triggerEvent(Am_Event $e) { $oldUser = $e->getOldUser(); $user = $e->getUser(); if ($oldUser->unsubscribed != $user->unsubscribed) { Am_Di::getInstance()->hook->call(Am_Event::USER_UNSUBSCRIBED_CHANGED, array('user'=>$user, 'unsubscribed' => $user->unsubscribed)); } } } class Am_Form_Brick_VatId extends Am_Form_Brick { protected $labels = array( 'VAT Settings are incorrect - no Vat Id configured', 'Invalid VAT Id, please try again', 'Cannot validate VAT Id, please try again', 'Invalid EU VAT Id format', 'EU VAT Id (optional)', 'Please enter EU VAT Id' ); public function initConfigForm(Am_Form $form) { $form->addAdvCheckbox('dont_validate')->setLabel(___('Disable online VAT Id Validation')); $form->addAdvCheckbox('required')->setLabel(___('Required')); } public function insertBrick(HTML_QuickForm2_Container $form) { $el = $form->addText('tax_id')->setLabel($this->___("EU VAT Id (optional)")) ->addFilter(function($value) { return str_replace(' ', '', $value); }) ->addRule('regex', $this->___('Invalid EU VAT Id format'), '/^[A-Za-z]{2}[a-zA-Z0-9\s]+$/'); if (!$this->getConfig('dont_validate')) $el->addRule('callback2', '-error-', array($this, 'validate')); if ($this->getConfig('required')) $el->addRule('required', $this->___("Please enter EU VAT Id")); } public function validate($id) { if (!$id) return; //skip validation in case of VAT was not supplied $plugins = Am_Di::getInstance()->plugins_tax->getAllEnabled(); $me = is_array($plugins) ? $plugins[0]->getConfig('my_id') : ""; if (!$me) return $this->___('VAT Settings are incorrect - no Vat Id configured'); // check if response is cached $cacheKey = 'vat_check_' . preg_replace('/[^A-Z0-9a-z_]/', '_', $me) . '_' . preg_replace('/[^A-Z0-9a-z_]/', '_', $id); if (($ret = Am_Di::getInstance()->cache->load($cacheKey)) !== false) return $ret === 1 ? null : $this->___('Invalid VAT Id, please try again'); if (!strlen($id)) return $this->___('Invalid VAT Id, please try again'); $req = new Am_HttpRequest('http://ec.europa.eu/taxation_customs/vies/vatResponse.html', Am_HttpRequest::METHOD_POST); $req->addPostParameter('action', 'check') ->addPostParameter('check', 'Verify') ->addPostParameter('memberStateCode', strtoupper(substr($id, 0, 2))) ->addPostParameter('number', substr($id, 2)) ->addPostParameter('requesterMemberStateCode', '') ->addPostParameter('traderName', '') ->addPostParameter('traderCompanyType', '') ->addPostParameter('traderStreet', '') ->addPostParameter('traderPostalCode', '') ->addPostParameter('traderCity', '') ->addPostParameter('requesterNumber', ''); try { $resp = $req->send(); $ok = strpos($resp->getBody(), 'invalidStyle') === false; Am_Di::getInstance()->cache->save($ok ? 1 : 0, $cacheKey); if (!$ok) return $this->___('Invalid VAT Id, please try again'); } catch (Exception $e) { Am_Di::getInstance()->errorLogTable->log($e); return $this->___("Cannot validate VAT Id, please try again"); } } } class Am_Form_Brick_InvoiceSummary extends Am_Form_Brick { protected $hideIfLoggedInPossible = self::HIDE_DONT; public function __construct($id = null, $config = null) { $this->name = ___('Invoice Summary'); parent::__construct($id, $config); } public function initConfigForm(Am_Form $form) { $form->addSelect('position', array('class' => 'invoice-summary-position')) ->loadOptions(array( 'above' => ___('Above Form'), 'below' => ___('Below Form'), 'brick' => ___('Brick Position'), 'custom' => ___('Custom Element') ))->setLabel('Position'); $form->addText('selector', array('placeholder' => '#invoice-summary')) ->setLabel(___('CSS Selector for conteiner')); $form->addScript() ->setScript(<<getConfig('position', 'above')) { case 'above' : $form->addProlog('
'); break; case 'below' : $form->addEpilog('
'); break; case 'brick' : $form->addHtml(null, array('class'=>'row-wide')) ->setHtml('
'); break; default: $selector = $this->getConfig('selector', '#invoice-summary') ?: '#invoice-summary'; } $url = Am_Di::getInstance()->url('ajax/invoice-summary',null,false); $form->addScript() ->setScript(<<setHeader('Cache-Control', 'maxage=3600') ->setHeader('Pragma', 'no-cache') ->setHeader('Content-type', 'text/csv') ->setHeader('Content-Disposition', 'attachment; filename=' . $filename); foreach ($rows as & $r) { if (is_array($r)) { $out = ""; foreach ($r as $s) $out .= ( $out ? $delimiter : "") . amEscapeCsv($s, $delimiter); $out .= "\r\n"; $r = $out; } } $response->appendBody(implode("", $rows)); } abstract function export(AffPayout $payout, Am_Query $details, Am_Mvc_Response $response); static function static_addFields() { $fieldsManager = Am_Di::getInstance()->userTable->customFields(); foreach (self::getEnabled() as $o) $o->addFields($fieldsManager); } /** @return Am_Aff_PayoutMethod[] */ static function getEnabled() { if (!self::$enabled) foreach (Am_Di::getInstance()->config->get('aff.payout_methods', array()) as $methodName) { $className = __CLASS__ . '_' . ucfirst($methodName); if (!class_exists($className)) continue; $o = new $className; self::$enabled[$o->getId()] = $o; } return self::$enabled; } static function getAvailableOptions() { $ret = array(); foreach (get_declared_classes () as $className) if (strpos($className, __CLASS__ . '_') === 0) { $o = new $className; $ret[$o->getId()] = $o->getTitle(); } $event = new Am_Event(Bootstrap_Aff::AFF_GET_PAYOUT_OPTIONS); $event->setReturn($ret); Am_Di::getInstance()->hook->call($event); return $event->getReturn(); } static function getEnabledOptions() { $ret = array(); foreach (self::getEnabled() as $o) $ret[$o->getId()] = $o->getTitle(); return $ret; } } class Am_Aff_PayoutMethod_Paypal extends Am_Aff_PayoutMethod { public function export(AffPayout $payout, Am_Query $details, Am_Mvc_Response $response) { $q = $details->query(); while ($d = $payout->getDi()->db->fetchRow($q)) { $d = $payout->getDi()->affPayoutDetailTable->createRecord($d); /* @var $d AffPayoutDetail */ $aff = $d->getAff(); $rows[] = array( $aff->data()->get('aff_paypal_email'), moneyRound($d->amount), Am_Currency::getDefault(), $aff->user_id, "Affiliate commission to " . amDate($payout->thresehold_date), ); } $this->sendCsv("paypal-commission-" . $payout->payout_id . ".csv", $rows, $response); } public function addFields(Am_CustomFieldsManager $m) { $m->add(new Am_CustomFieldText('aff_paypal_email', ___('Affiliate Payout - Paypal E-Mail address'), ___('for affiliate commission payouts')))->size = 40; } } class Am_Aff_PayoutMethod_Webmoney extends Am_Aff_PayoutMethod { public function export(AffPayout $payout, Am_Query $details, Am_Mvc_Response $response) { $q = $details->query(); while ($d = $payout->getDi()->db->fetchRow($q)) { $d = $payout->getDi()->affPayoutDetailTable->createRecord($d); /* @var $d AffPayoutDetail */ $aff = $d->getAff(); $rows[] = array( $aff->data()->get('aff_webmoney_purse'), moneyRound($d->amount), Am_Currency::getDefault(), $aff->user_id, "Affiliate commission to " . amDate($payout->thresehold_date), ); } $this->sendCsv("webmoney-commission-" . $payout->payout_id . ".csv", $rows, $response); } public function addFields(Am_CustomFieldsManager $m) { $m->add(new Am_CustomFieldText('aff_webmoney_purse', ___('Affiliate Payout - WM purse'), ___('for affiliate commission payouts')))->size = 40; } } class Am_Aff_PayoutMethod_Check extends Am_Aff_PayoutMethod { public function getTitle() { return ___("Offline Check"); } public function export(AffPayout $payout, Am_Query $details, Am_Mvc_Response $response) { $q = $details->query(); $rows = array(array( ___("Check Payable To"), ___("Street"), ___("City"), ___("State"), ___("Country"), ___("ZIP"), ___("Amount"), ___("Currency"), ___("Comment"), ___("Username"), )); while ($d = $payout->getDi()->db->fetchRow($q)) { $d = $payout->getDi()->affPayoutDetailTable->createRecord($d); /* @var $d AffPayoutDetail */ $aff = $d->getAff(); $rows[] = array( $aff->data()->get('aff_check_payable_to'), $aff->data()->get('aff_check_street'), $aff->data()->get('aff_check_city'), $aff->data()->get('aff_check_state'), $aff->data()->get('aff_check_country'), $aff->data()->get('aff_check_zip'), moneyRound($d->amount), Am_Currency::getDefault(), ___("Affiliate commission to %s", amDate($payout->thresehold_date)), $aff->login, ); } $this->sendCsv("check-commission-" . $payout->payout_id . ".csv", $rows, $response); } public function addFields(Am_CustomFieldsManager $m) { $m->add(new Am_CustomFieldText('aff_check_payable_to', ___('Affiliate Check - Payable To')))->size = 40; $m->add(new Am_CustomFieldText('aff_check_street', ___('Affiliate Check - Street Address')))->size = 40; $m->add(new Am_CustomFieldText('aff_check_city', ___('Affiliate Check - City')))->size = 40; $m->add(new Am_CustomFieldText('aff_check_country', ___('Affiliate Check - Country'))); $m->add(new Am_CustomFieldText('aff_check_state', ___('Affiliate Check - State'))); $m->add(new Am_CustomFieldText('aff_check_zip', ___('Affiliate Check - ZIP Code')))->size = 10; } } class Am_Aff_PayoutMethod_Bacs extends Am_Aff_PayoutMethod { public function getTitle() { return ___("BACS"); } public function export(AffPayout $payout, Am_Query $details, Am_Mvc_Response $response) { $q = $details->query(); $rows = array(array( ___("Bank name"), ___("Account holder name"), ___("Account number"), ___("Sort code"), ___("Amount"), ___("Currency"), ___("Comment"), ___("Username"), )); while ($d = $payout->getDi()->db->fetchRow($q)) { $d = $payout->getDi()->affPayoutDetailTable->createRecord($d); /* @var $d AffPayoutDetail */ $aff = $d->getAff(); $rows[] = array( $aff->data()->get('aff_bacs_bank_name'), $aff->data()->get('aff_bacs_account_holder_name'), $aff->data()->get('aff_bacs_caccount_number'), $aff->data()->get('aff_bacs_sort_code'), moneyRound($d->amount), Am_Currency::getDefault(), ___("Affiliate commission to %s", amDate($payout->thresehold_date)), $aff->login, ); } $this->sendCsv("check-commission-" . $payout->payout_id . ".csv", $rows, $response); } public function addFields(Am_CustomFieldsManager $m) { $m->add(new Am_CustomFieldText('aff_bacs_bank_name', ___('Affiliate BACS - Bank name'))); $m->add(new Am_CustomFieldText('aff_bacs_account_holder_name', ___('Affiliate BACS - Account holder name'))); $m->add(new Am_CustomFieldText('aff_bacs_caccount_number', ___('Affiliate BACS - Account number'))); $m->add(new Am_CustomFieldText('aff_bacs_sort_code', ___('Affiliate BACS - Sort code'))); } } class Am_Aff_PayoutMethod_Moneybookers extends Am_Aff_PayoutMethod { public function getTitle() { return 'Skrill'; } public function export(AffPayout $payout, Am_Query $details, Am_Mvc_Response $response) { $q = $details->query(); $rows = array(array( ___("Skrill E-Mail"), ___("Amount"), ___("Currency"), ___("Comment"), ___("Username"), )); while ($d = $payout->getDi()->db->fetchRow($q)) { $d = $payout->getDi()->affPayoutDetailTable->createRecord($d); /* @var $d AffPayoutDetail */ $aff = $d->getAff(); $rows[] = array( $aff->data()->get('aff_moneybookers_email'), moneyRound($d->amount), Am_Currency::getDefault(), ___("Affiliate commission to %s", amDate($payout->thresehold_date)), $aff->login, ); } $this->sendCsv("check-commission-" . $payout->payout_id . ".csv", $rows, $response); } public function addFields(Am_CustomFieldsManager $m) { $m->add(new Am_CustomFieldText('aff_moneybookers_email', ___('Affiliate Payout - Skrill Account ID')))->size = 40; } } class Am_Aff_PayoutMethod_Propay extends Am_Aff_PayoutMethod { public function export(AffPayout $payout, Am_Query $details, Am_Mvc_Response $response) { $q = $details->query(); while ($d = $payout->getDi()->db->fetchRow($q)) { $d = $payout->getDi()->affPayoutDetailTable->createRecord($d); /* @var $d AffPayoutDetail */ $aff = $d->getAff(); $rows[] = array( $aff->data()->get('aff_propay_email'), moneyRound($d->amount), Am_Currency::getDefault(), $aff->user_id, ___("Affiliate commission to %s", amDate($payout->thresehold_date)), ); } $this->sendCsv("propay-commission-" . $payout->payout_id . ".csv", $rows, $response); } public function addFields(Am_CustomFieldsManager $m) { $m->add(new Am_CustomFieldText('aff_propay_email', ___('Affiliate Payout - Propay E-Mail address'), ___('for affiliate commission payouts')))->size = 40; } } class Am_Aff_PayoutMethod_Okpay extends Am_Aff_PayoutMethod { public function export(AffPayout $payout, Am_Query $details, Am_Mvc_Response $response) { $q = $details->query(); while ($d = $payout->getDi()->db->fetchRow($q)) { $d = $payout->getDi()->affPayoutDetailTable->createRecord($d); /* @var $d AffPayoutDetail */ $aff = $d->getAff(); $rows[] = array( $aff->data()->get('aff_okpay_wallet'), moneyRound($d->amount), Am_Currency::getDefault(), $aff->user_id, ___("Affiliate commission to %s", amDate($payout->thresehold_date)), ); } $this->sendCsv("okpay-commission-" . $payout->payout_id . ".csv", $rows, $response); } public function addFields(Am_CustomFieldsManager $m) { $m->add(new Am_CustomFieldText('aff_okpay_wallet', ___('Affiliate Payout - Okpay Wallet ID'), ___('for affiliate commission payouts')))->size = 40; } } class Am_Aff_PayoutMethod_Pagseguro extends Am_Aff_PayoutMethod { public function export(AffPayout $payout, Am_Query $details, Am_Mvc_Response $response) { $q = $details->query(); while ($d = $payout->getDi()->db->fetchRow($q)) { $d = $payout->getDi()->affPayoutDetailTable->createRecord($d); /* @var $d AffPayoutDetail */ $aff = $d->getAff(); $rows[] = array( $aff->data()->get('aff_pagseguro_email'), moneyRound($d->amount), Am_Currency::getDefault(), $aff->user_id, ___("Affiliate commission to %s", amDate($payout->thresehold_date)), ); } $this->sendCsv("pagseguro-commission-" . $payout->payout_id . ".csv", $rows, $response); } public function addFields(Am_CustomFieldsManager $m) { $m->add(new Am_CustomFieldText('aff_pagseguro_email', ___('Affiliate Payout - Pagseguro E-Mail address'), ___('for affiliate commission payouts')))->size = 40; } } class Am_Aff_PayoutMethod_Bitcoin extends Am_Aff_PayoutMethod { public function export(AffPayout $payout, Am_Query $details, Am_Mvc_Response $response) { $q = $details->query(); while ($d = $payout->getDi()->db->fetchRow($q)) { $d = $payout->getDi()->affPayoutDetailTable->createRecord($d); /* @var $d AffPayoutDetail */ $aff = $d->getAff(); $rows[] = array( $aff->data()->get('aff_bitcoin_wallet'), moneyRound($d->amount), Am_Currency::getDefault(), $aff->user_id, "Affiliate commission to " . amDate($payout->thresehold_date), ); } $this->sendCsv("bitcoint-commission-" . $payout->payout_id . ".csv", $rows, $response); } public function addFields(Am_CustomFieldsManager $m) { $m->add(new Am_CustomFieldText('aff_bitcoin_wallet', ___('Affiliate Payout - Bitcoin Wallet'), ___('for affiliate commission payouts')))->size = 40; } } /** * https://chexxinc.com */ class Am_Aff_PayoutMethod_Chexx extends Am_Aff_PayoutMethod { public function getTitle() { return ___("Chexx"); } public function export(AffPayout $payout, Am_Query $details, Am_Mvc_Response $response) { $q = $details->query(); $rows = array(array( ___("Payment Routing Number"), ___("Payment Type"), ___("Amount"), ___("Currency Code"), ___("Account Name"), ___("IBAN"), ___("BIC"), ___("Reference"), ___("Description"), )); while ($d = $payout->getDi()->db->fetchRow($q)) { $d = $payout->getDi()->affPayoutDetailTable->createRecord($d); /* @var $d AffPayoutDetail */ $aff = $d->getAff(); $rows[] = array( $aff->data()->get('aff_chexx_routing_number'), 'sepa_credit', moneyRound($d->amount), Am_Currency::getDefault(), $aff->data()->get('aff_chexx_account_holder_name'), $aff->data()->get('aff_chexx_iban'), $aff->data()->get('aff_chexx_bic'), $aff->login, ___("Affiliate commission up to %s", amDate($payout->thresehold_date)), ); } $this->sendCsv("chexx-commission-" . $payout->payout_id . ".csv", $rows, $response); } public function addFields(Am_CustomFieldsManager $m) { $m->add(new Am_CustomFieldText('aff_chexx_routing_number', ___('Affiliate Chexx - Payment Routing Number'))); $m->add(new Am_CustomFieldText('aff_chexx_account_holder_name', ___('Affiliate Chexx - Account Holder Name'))); $m->add(new Am_CustomFieldText('aff_chexx_iban', ___('Affiliate Chexx - IBAN'))); $m->add(new Am_CustomFieldText('aff_chexx_bic', ___('Affiliate Chexx - BIC'))); } }Am/Grid/Action/Aff/Void.php000064400000003767152101603130011316 0ustar00title = ___("Void"); $this->attributes['data-confirm'] = ___("Do you really want to void commission?"); parent::__construct($id, $title); } function run() { $form = new Am_Form_Admin('form-vomm-void'); $form->setAttribute('name', 'void'); $comm = $this->grid->getRecord(); $form->addText('amount', array('size' => 6)) ->setlabel(___('Void Amount')); foreach ($this->grid->getVariablesList() as $k) { $form->addHidden($this->grid->getId() . '_' . $k)->setValue($this->grid->getRequest()->get($k, "")); } $g = $form->addGroup(); $g->setSeparator(' '); $g->addSubmit('_save', array('value' => ___("Void"))); $g->addStatic() ->setContent(sprintf('%s', $this->grid->getBackUrl(), ___('Cancel'))); $form->setDataSources(array( $this->grid->getCompleteRequest(), new HTML_QuickForm2_DataSource_Array(array('amount' => $comm->amount)))); if ($form->isSubmitted() && $form->validate()) { $values = $form->getValue(); $this->void($values['amount']); $this->grid->redirectBack(); } else { echo $this->renderTitle(); echo $form; } } public function void($amount) { $record = $this->grid->getRecord(); if(!$record->is_voided) { Am_Di::getInstance()->affCommissionTable->void($record, sqlTime('now'), $amount); } $this->log(); $this->grid->redirectBack(); } public function isAvailable($record) { return (!$record->is_voided && ($record->record_type == AffCommission::COMMISSION)); } }Reports.php000064400000016571152101603130006715 0ustar00title = ___('Affiliate Clicks'); $this->description = ___('number of affiliate program clicks'); parent::__construct(); } public function getPointField() { return 'cl.time'; } public function getQuery() { $q = new Am_Query(new AffClickTable, 'cl'); $q->clearFields(); $q->addField('COUNT(DISTINCT cl.remote_addr) AS clicks'); $q->addField('COUNT(cl.log_id) AS clicks_all'); if ($this->aff_id) { $q->addWhere("aff_id = ?d", $this->aff_id); } return $q; } function getLines() { return array( new Am_Report_Line("clicks", ___('Unique Clicks')), new Am_Report_Line("clicks_all", ___('All Clicks')), ); } public function setAffId($aff_id) { $this->aff_id = (int)$aff_id; } } class Am_Report_AffStats extends Am_Report_Date { protected $aff_id; public function __construct() { $this->title = ___('Affiliate Sales'); $this->description = ___('affiliate program commissions'); } public function getPointField() { return 'cl.date'; } public function getQuery() { $q = new Am_Query(new AffCommissionTable, 'cl'); $q->clearFields(); $q->addField("SUM(IF(cl.record_type='commission', cl.amount, -cl.amount)) AS commission"); if ($this->aff_id) $q->addWhere("aff_id = ?d", $this->aff_id); return $q; } function getLines() { return array( new Am_Report_Line("commission", ___('Commission'), null, array('Am_Currency', 'render')), ); } public function setAffId($aff_id) { $this->aff_id = (int)$aff_id; } } class Am_Report_AffSales extends Am_Report_Date { protected $aff_id; public function __construct() { $this->title = ___('Affiliate Sales Number'); $this->description = ___('number of sales by affiliate'); } public function getPointField() { return 'cl.date'; } public function getQuery() { $q = new Am_Query(new AffCommissionTable, 'cl'); $q->clearFields(); $q->addField("COUNT(DISTINCT invoice_payment_id) AS sales"); if ($this->aff_id) $q->addWhere("aff_id = ?d and record_type='commission'", $this->aff_id); return $q; } function getLines() { return array( new Am_Report_Line("sales", ___('Sales')), ); } public function setAffId($aff_id) { $this->aff_id = (int)$aff_id; } } class Am_Report_AffUserPayout extends Am_Report_Abstract { const PERIOD_EXACT = 'exact'; public function __construct() { $this->title = ___('Affiliate Payout Amount by User'); } public function _initConfigForm(Am_Form $form) { parent::_initConfigForm($form); $period = $form->addSelect('period')->setLabel(___('Period')) ->loadOptions( array_merge($this->getDi()->interval->getOptions(), array(self::PERIOD_EXACT=> ___('Exact')))); $intervals = array(); foreach ($this->getDi()->interval->getIntervals() as $k => $v) { list($b, $e) = $v; $b = amDate($b); $e = amDate($e); $intervals[$k] = ($b == $e) ? $b : sprintf('%s—%s', $b, $e); } $period->setAttribute('data-intervals', json_encode($intervals)); $period_exact = self::PERIOD_EXACT; $script = <<').html(str)); jQuery(this).closest('.am-form').find('input[name=start], input[name=stop]'). closest('div.row'). toggle(jQuery(this).val() == '{$period_exact}'); }).change(); }) CUT; $form->addScript()->setScript($script); $start = $form->addDate('start')->setLabel(___('Start')); $start->addRule('required'); $stop = $form->addDate('stop')->setLabel(___('End')); $stop->addRule('required'); $form->addRule('callback', 'Start Date cannot be later than the End Date', array($this, 'checkStopDate')); $min = $form->addGroup() ->setLabel(___("Minimum Payout\n" . "include to report only affiliates with payout in qiven period more or equal to")); $min->setSeparator(' '); $min->addText('min', array('size'=>5, 'placeholder'=>'0.00')); $min->addStatic() ->setContent((string)Am_Currency::getDefault()); } protected function getFormDefaults() { return array( 'start' => sqlDate('-1 month'), 'stop' => sqlDate('now'), ); } public function checkStopDate($val) { $res = $val['stop']>$val['start']; if (!$res) { $elements = $this->getForm()->getElementsByName('start'); $elements[0]->setError('Start Date cannot be later than the End Date'); } return $res; } protected function getStartStop(array $values) { switch ($values['period']) { case self::PERIOD_EXACT : return array($values['start'], $values['stop']); default : return $this->getDi()->interval->getStartStop($values['period']); } } protected function runQuery() { $q = new Am_Query(new AffPayoutDetailTable, 'pd'); $q->leftJoin('?_aff_payout', 'ap', 'pd.payout_id=ap.payout_id'); $q->clearFields(); $q->addWhere('pd.is_paid=1'); $q->addField("SUM(pd.amount) AS amount"); $q->addField('pd.aff_id', self::POINT_FLD); $q->addWhere("ap.date BETWEEN ? AND ?", $this->start, $this->stop); $q->groupBy('aff_id'); $q->addHaving('amount>?', (float)$this->min); $this->stmt = $q->query(); } protected function processConfigForm(array $values) { list($start, $stop) = $this->getStartStop($values); $this->min = $values['min']; $this->setInterval($start, $stop); $op = $this->getDi()->db->selectCol(<<start, $this->stop); $quant = new Am_Report_Quant_Map($op); $this->setQuantity($quant); } public function setInterval($start, $stop) { $this->start = date('Y-m-d 00:00:00', strtotime($start)); $this->stop = date('Y-m-d 23:59:59', strtotime($stop)); return $this; } function getLines() { return array( new Am_Report_Line("amount", ___('Payout'), null, array('Am_Currency', 'render')) ); } public function getOutput(Am_Report_Result $result) { return array(new Am_Report_Table($result)); } }Form.php000064400000001606152101604200006152 0ustar00deleteFromRelatedTable('?_form_field'); $this->deleteFromRelatedTable('?_form_grid_field'); $this->deleteFromRelatedTable('?_form_user'); $this->deleteFromRelatedTable('?_form_data'); return parent::delete(); } function getFields($onlyPublic = true) { return $this->getDi()->formFieldTable->selectObjects("SELECT * FROM " . "?_form_field WHERE form_id=? {AND is_public=? }ORDER BY sort_order", $this->pk(), $onlyPublic ? 1 : DBSIMPLE_SKIP); } } class FormTable extends Am_Table { protected $_table = '?_form'; protected $_key = 'form_id'; protected $_recordClass = 'Form'; public function getOptions() { return $this->_db->selectCol("SELECT form_id as ARRAY_KEY, title FROM ?_form WHERE is_deleted=0"); } }FormCopy.php000064400000000314152101604200007000 0ustar00getDi()->formDataTable->findByFormUserId($this->pk()) as $d) { $data = $data + $d->getData($obfuscate); } return $data; } function saveData($vars) { $this->getDi()->formDataTable->deleteByFormUserId($this->pk()); foreach ($vars as $k => $v) { if ($k[0] == '_') continue; if (!($field_id = $this->getDi()->security->reveal($k))) continue; $d = $this->getDi()->formDataRecord; $d->form_id = $this->form_id; $d->form_user_id = $this->pk(); $d->user_id = $this->user_id; $field = $this->getDi()->formFieldTable->load($field_id); $d->setData($v, $field); $d->save(); } } function delete() { $_ = parent::delete(); $this->deleteFromRelatedTable('?_form_data'); } } class FormUserTable extends Am_Table { protected $_table = '?_form_user'; protected $_key = 'form_user_id'; protected $_recordClass = 'FormUser'; }Am/Form/Page.php000064400000005527152101604200007371 0ustar00record = $record; $this->fields = $fields; $this->pageNum = $pageNum; $this->pageTitle = $pageTitle; $this->pageDesc = $pageDesc; $this->lastPage = $lastPage; $this->savedData = $savedData; } public function getTitle() { return $this->record->title; } protected function populateForm() { $default = array(); foreach ($this->fields as $field) { $default = $default + $field->insertField($this->form); } if ($this->pageTitle) { $this->form->addProlog(<<{$this->pageTitle}

{$this->pageDesc}

CUT ); } if (!$this->form->isSubmitted()) { $this->form->setDataSources(array(new HTML_QuickForm2_DataSource_Array($default))); } if (!$this->form->isSubmitted() && $this->savedData) { $ds = $this->form->getDataSources(); array_unshift($ds, new HTML_QuickForm2_DataSource_Array($this->savedData)); $this->form->setDataSources($ds); } $group = $this->form->addGroup(null, 'id="buttons"'); $group->setSeparator(' '); if ($this->pageNum > 0) { $url = $this->getController()->getParentController()->getRequest()->getRequestUri(); $url = preg_replace('/\?.*/', '', $url); $link = Am_Html::escape($url) . '?' . Am_Html::escape($this->getButtonName('back')) . '=1'; $group->addHtml('back')->setHtml("Back"); } if ($this->lastPage) { $group->addSubmit($this->getButtonName('complete'), array('value' => 'Complete')); $group->addSubmit($this->getButtonName('next'), array('value' => 'Save', 'style'=>'float:right')); } else { $group->addSubmit($this->getButtonName('next'), array('value' => 'Next')); $group->addSubmit($this->getButtonName('save'), array('value' => 'Save', 'style'=>'float:right')); } $this->setDefaultAction('next', REL_ROOT_URL.'/application/default/views/public/img/empty.gif'); } public function setDefaultAction($actionName, $imageSrc = '') { parent::setDefaultAction($actionName, $imageSrc); $image = $this->form->getElementById('_qf_default'); if (!$image) return; $image->setAttribute('style', 'display: none;'); } }Am/Form/Element/Grid.php000064400000003315152101604200010764 0ustar00 $v) { if ($k == 'rows') continue; if ($k > $vars['rows']-1) unset($vars[$k]); } return $vars; } public function render(HTML_QuickForm2_Renderer $renderer) { $data = $this->getData(); $grid_rows = $data['grid_rows']; $name = $this->getName(); if (!$this->frozen) { $this->addHtml('js') ->setHtml(<<+ Add Item CUT ); } else { $this->addHtml('js') ->setHtml(<< jQuery(function(){ jQuery('#{$name}-table tr').slice(parseInt($('#{$name}-rows').val())+1).hide(); }); CUT ); } $renderer->startGroup($this); foreach ($this as $element) { $element->render($renderer); } $this->renderClientRules($renderer->getJavascriptBuilder()); $renderer->finishGroup($this); return $renderer; } }Am/Form/Controller/Action/Save.php000064400000000502152101604200012737 0ustar00storeValues(); $page->getController()->getParentController()->process($page->getController()->getValue(), $name, $page); } }Am/Form/Controller/Action/Complete.php000064400000000507152101604200013616 0ustar00storeValues(); $page->getController()->getParentController()->complete($page->getController()->getValue(), $name, $page); } }Am/Pdf/Cell.php000064400000006100152101604200007166 0ustar00 0, self::RIGHT => 0, self::BOTTOM => 0, self::LEFT => 0 ); public function __construct($content) { $this->content = $content; } public function setStyle($styles) { $this->styles = $styles; } public function setWidth($width) { $this->width = $width; } public function setPadding($top=0, $right=0, $bottom=0, $left=0) { $this->padding = array( self::TOP => $top, self::RIGHT => $right, self::BOTTOM => $bottom, self::LEFT => $left ); } public function getPadding($side) { return $this->padding[$side]; } public function getWidth() { return $this->width; } public function render(Am_Pdf_Page_Decorator $page, $x, $y) { if ($font = $this->getProperty('font')) { $fontTmp = $page->getFont(); $fontSizeTmp = $page->getFontSize(); $page->setFont($font['face'], $font['size']); } $lineHeight = $page->getFont()->getLineHeight()/100; $lineBegin = $y - $this->getPadding(self::TOP) - $lineHeight; $width = $this->getWidth() - $this->getPadding(self::LEFT) - $this->getPadding(self::RIGHT); switch ($this->getProperty('align', Am_Pdf_Page_Decorator::ALIGN_LEFT)) { case Am_Pdf_Page_Decorator::ALIGN_LEFT : $lineEnd = $page->drawTextWithFixedWidth($this->content, $x + 1 + $this->getPadding(self::LEFT), $lineBegin, $width); break; case Am_Pdf_Page_Decorator::ALIGN_RIGHT : $lineEnd = $page->drawTextWithFixedWidth($this->content, $x + $this->getWidth() - 1 - $this->getPadding(self::RIGHT), $lineBegin, $width, 'UTF-8', Am_Pdf_Page_Decorator::ALIGN_RIGHT); break; } $rowHeight = $lineBegin - $lineEnd; if ($font) { $page->setFont($fontTmp, $fontSizeTmp); } $shape = $this->getProperty('shape', array( 'type' => Zend_Pdf_Page::SHAPE_DRAW_STROKE, 'color' => new Zend_Pdf_Color_Html('#cccccc') )); return array( 'x' => $x, 'y' => $y, 'width' => $this->getWidth(), 'height' => $rowHeight + $this->getPadding(self::BOTTOM) + $this->getPadding(self::TOP), 'shape' => $shape); } protected function getProperty($propName, $default = null) { if (isset($this->styles[$propName])) { return $this->styles[$propName]; } else { return $default; } } }Am/Grid/Action/FormPdf.php000064400000011631152101604200011242 0ustar00form = $form = $this->grid->getRecord(); $this->dForm = $this->grid->getDi()->formTable->load($this->form->form_id); $this->user = $this->grid->getDi()->userTable->load($form->user_id); $padd = 20; $left = $padd; $right = Am_Pdf_Page_Decorator::PAGE_A4_WIDTH - $padd; $pdf = new Zend_Pdf(); $pdf->pages[0] = $pdf->newPage(Zend_Pdf_Page::SIZE_A4); $page = new Am_Pdf_Page_Decorator($pdf->pages[0]); $page->setFont(Zend_Pdf_Font::fontWithName(Zend_Pdf_Font::FONT_HELVETICA), 10); $pointer = Am_Pdf_Page_Decorator::PAGE_A4_HEIGHT; $this->renderHeader($page, $padd, $pointer); $that = $this; $table = new Am_Pdf_Table(function ($page, & $y) use ($pdf, $padd, $that) { $pdf->pages[] = $_ = $pdf->newPage(Zend_Pdf_Page::SIZE_A4); $p = new Am_Pdf_Page_Decorator($_); $p->setFont($page->getFont(), $page->getFontSize()); $pointer = Am_Pdf_Page_Decorator::PAGE_A4_HEIGHT; $that->renderHeader($p, $padd, $pointer); $y = $pointer; return $p; }); $table->setMargin(0, $padd, $padd, $padd); $data = $form->getData(false); foreach ($this->dForm->getFields(false) as $field) { if ($field->type == 'page-separator') { $table->addRow($field->title); } elseif($field->type == 'grid') { $table->addRow($field->title); foreach ($field->toPdf($data[$field->pk()]) as $row) { $table->addRow($row); } } else { $table->addRow($field->title, isset($data[$field->pk()]) ? $field->toPdf($data[$field->pk()]) : ''); } } $page->drawTable($table, 0, $pointer); $pCount = count($pdf->pages); $pIndex = 1; foreach ($pdf->pages as $p) { $pointer = Am_Pdf_Page_Decorator::PAGE_A4_HEIGHT; $pd = new Am_Pdf_Page_Decorator($p); $pd->nl($pointer); $pd->nl($pointer); $pd->drawText(sprintf('Page %d/%d', $pIndex++, $pCount), Am_Pdf_Page_Decorator::PAGE_A4_WIDTH - $padd, $pointer, 'UTF-8', Am_Pdf_Page_Decorator::ALIGN_RIGHT); } $helper = new Am_Mvc_Controller_Action_Helper_SendFile(); $helper->sendData($pdf->render(), 'application/pdf', sprintf('%s-%s.pdf', $this->dForm->title, sqlDate('now'))); } function renderHeader($page, $padd, & $pointer) { $page->nl($pointer); $page->nl($pointer); $this->renderLogo($page, $pointer); $page->setFont(Zend_Pdf_Font::fontWithName(Zend_Pdf_Font::FONT_HELVETICA_BOLD), 10); $page->drawText(sprintf('%s: %s, %s', $this->dForm->title, $this->user->getName(), amDatetime('now')), ceil(Am_Pdf_Page_Decorator::PAGE_A4_WIDTH/2), $pointer, 'UTF-8', Am_Pdf_Page_Decorator::ALIGN_CENTER); $page->setFont(Zend_Pdf_Font::fontWithName(Zend_Pdf_Font::FONT_HELVETICA), 10); $page->nl($pointer); } function renderLogo($page, &$pointer) { $logo_id = $this->grid->getDi()->config->get('form.logo_id'); $padd = 20; $yi = $pointer; if ($logo_id && ($upload = $this->grid->getDi()->uploadTable->load($logo_id, false))) { if (file_exists($upload->getFullPath())) { $image = null; switch ($upload->getType()) { case 'image/png' : $image = new Zend_Pdf_Resource_Image_Png($upload->getFullPath()); break; case 'image/jpeg' : $image = new Zend_Pdf_Resource_Image_Jpeg($upload->getFullPath()); break; case 'image/tiff' : $image = new Zend_Pdf_Resource_Image_Tiff($upload->getFullPath()); break; } if ($image) { $gh = 100; $gw = 200; $h = $image->getPixelHeight(); $w = $image->getPixelWidth(); $nh = $gh; $nw = ceil($w * $gh / $h); if ($nw > $gw) { $nw = $gw; $nh = ceil($h * $gw / $w); } $pos = ceil(Am_Pdf_Page_Decorator::PAGE_A4_WIDTH/2) - ceil($nw/2); $page->drawImage($image, $pos, $pointer - $nh, $pos + $nw, $pointer); $yi = $pointer - $nh; } } $page->nl($yi); $page->nl($yi); } $pointer = $yi; } }Am/Grid/Action/FormCopy.php000064400000004662152101604200011451 0ustar00getForm(); if ($form->isSubmitted() && $form->validate()) { $form_copy_id = $this->grid->getCompleteRequest()->getParam('form_copy_id'); $f = $this->grid->getRecord(); $r = $this->grid->getDi()->formCopyTable->load($form_copy_id); $nf = $this->grid->getDi()->formUserRecord; $nf->user_id = $f->user_id; $nf->form_id = $r->to_id; $nf->dattm = sqlTime('now'); $nf->status = 'pending'; $nf->save(); $old = array(); foreach (json_decode($r->field_map, true) as $oldf_id => $newf_id) { if (!$newf_id) continue; $old[] = $oldf_id; $case[] = "WHEN $oldf_id THEN $newf_id"; } if ($old) { $case = sprintf("CASE field_id %s END", implode("\n", $case)); $this->grid->getDi()->db->query(<<pk()}, $case,{$nf->form_id}, {$nf->user_id}, data, data_blob FROM ?_form_data WHERE form_user_id = {$f->pk()} AND field_id IN (?a); CUT , $old); } $this->grid->redirectBack(); } echo $this->renderTitle(); echo $form; } public function getForm() { $formUser = $this->grid->getRecord(); $options = $this->grid->getDi()->db->selectCol(<<form_id); $form = new Am_Form_Admin('form-data-copy', array('target' => '_top')); $form->setAction($this->grid->makeUrl(null)); $form->addElement('select', 'form_copy_id') ->setLabel(___('Target')) ->loadOptions($options); $form->addSaveButton(___('Copy')); foreach ($this->grid->getVariablesList() as $k) { if ($val = $this->grid->getRequest()->get($k)) { $form->addHidden($this->grid->getId() .'_'. $k)->setValue($val); } } return $form; } }FormGridField.php000064400000007170152101604200007726 0ustar00getDi()->security->obfuscate($this->pk()); $el = null; switch ($this->type) { case 'html': $form->addHtml(null, array('class'=>'row-wide')) ->setHtml($this->html); break; case 'text': case 'textarea': $el = $form->addElement($this->type, $name, array('class'=>'el-wide')) ->setLabel($this->title . "\n" . $this->description); break; case 'select': case 'advradio': $el = $form->addElement($this->type, $name) ->setLabel($this->title . "\n" . $this->description) ->loadOptions($this->getOptions()); if ($d = $this->getDefault()) { $default[$name] = $d[0]; } break; case 'date': $form->addDate($name) ->setLabel($this->title . "\n" . $this->description); break; case 'upload': $urlUpload = '/upload/upload'; $urlGet = '/upload/get'; $el = $form->addUpload($name, array(), array( 'secure' => true, 'prefix' => Bootstrap_Form::UPLOAD_PREFIX)) ->setLabel($this->title . "\n" . $this->description); if (defined('AM_ADMIN') && AM_ADMIN) { $urlUpload = '/admin-upload/upload'; $urlGet = '/admin-upload/get'; $el->toggleFrozen(true); } $el->setJsOptions(<<addGroup($name); $el->setLabel($this->title . "\n" . $this->description); $el->setSeparator("
"); foreach ($this->getOptions() as $k => $v) { $chkbox = $el->addAdvCheckbox(null, array('value' => $k))->setContent(___($v)); } $el->addHidden(null, array('value' => '')); $el->addFilter('array_filter'); if ($d = $this->getDefault()) { $default[$name] = $d; } break; } return $default; } function toPdf($v) { switch ($this->type) { case 'text': case 'textarea': return $v; case 'select': case 'advradio': case 'checkbox': $nv = array(); $op = $this->getOptions(); foreach((array)$v as $_) { $nv[] = isset($op[$_]) ? $op[$_] : $_; } return implode(', ', $nv); case 'date': return amDate($v); case 'upload': $upload = $this->getDi()->uploadTable->load($v, false); return $upload ? $upload->name : ''; } } function getOptions() { return $this->options ? json_decode($this->options, true) : array(); } function getDefault() { return $this->default ? json_decode($this->default, true) : array(); } } class FormGridFieldTable extends Am_Table { protected $_table = '?_form_grid_field'; protected $_key = 'grid_field_id'; protected $_recordClass = 'FormGridField'; }FormData.php000064400000001530152101604200006740 0ustar00field_id = $field->pk(); $d = $field->toTable($d); if (strlen($d) > 255) { $this->data_blob = $d; $this->data = null; } else { $this->data = $d; $this->data_blob = null; } return $this; } function getData($obfuscate = true) { $field = $this->getDi()->formFieldTable->load($this->field_id); return array( ($obfuscate ? $this->getDi()->security->obfuscate($field->pk()) : $field->pk()) => $field->fromTable(get_first($this->data, $this->data_blob)) ); } } class FormDataTable extends Am_Table { protected $_table = '?_form_data'; protected $_key = 'data_id'; protected $_recordClass = 'FormData'; }FormField.php000064400000022570152101604200007121 0ustar00getDi()->security->obfuscate($this->pk()); $el = null; switch ($this->type) { case 'grid': $grid_rows = $this->grid_rows ?: 10; $el = $form->addElement($this->type, $name, array('class' => 'row-wide'), array('grid_rows' => $grid_rows)) ->setLabel($this->title . "\n" . $this->description); $el->addHidden('rows', array('id' => $name . '-rows', 'class' => 'form-grid-rows', 'data-name' => $name)); $default[$name]['rows'] = 1; $el->addHtml()->setHtml(''); $fields = $this->getFields(); foreach ($fields as $f) { $el->addHtml()->setHtml( sprintf(''); } $el->addHtml()->setHtml(''); for ($i = 0; $i < $grid_rows; $i++) { $g = $el->addGroup($i); $g->addHtml()->setHtml(''); $my_default = array(); foreach ($fields as $f) { $g->addHtml()->setHtml(sprintf(''); } $default[$name][$i] = $my_default; $g->addHtml()->setHtml(''); } $el->addHtml()->setHtml('
', $this->getDi()->security->obfuscate($f->pk())) . Am_Html::escape($f->title) . '
', $this->getDi()->security->obfuscate($f->pk()))); $g->addHtml()->setHtml('
'); $my_default = $my_default + $f->insertField($g); $g->addHtml()->setHtml('
'); $g->addHtml()->setHtml('
'); foreach ($fields as $f) { if ($f->cond_enabled) { $c_cond = $f->cond_type > 0 ? '==' : '!='; $c_field_val = json_encode($f->cond_field_val); $c_field_name = $this->getDi()->security->obfuscate($f->cond_field_id); $f_name = $this->getDi()->security->obfuscate($f->pk()); $form->addScript() ->setScript(<< 0); }).change(); CUT ); } } break; case 'html': $form->addHtml(null, array('class' => 'row-wide')) ->setHtml($this->html); break; case 'text': case 'textarea': $el = $form->addElement($this->type, $name, array('class' => 'el-wide')) ->setLabel($this->title . "\n" . $this->description); break; case 'select': case 'advradio': $el = $form->addElement($this->type, $name) ->setLabel($this->title . "\n" . $this->description) ->loadOptions($this->getOptions()); if ($d = $this->getDefault()) { $default[$name] = $d[0]; } break; case 'date': $form->addDate($name) ->setLabel($this->title . "\n" . $this->description); break; case 'upload': $urlUpload = '/upload/upload'; $urlGet = '/upload/get'; $el = $form->addUpload($name, array(), array( 'secure' => true, 'prefix' => Bootstrap_Form::UPLOAD_PREFIX)) ->setLabel($this->title . "\n" . $this->description); if (defined('AM_ADMIN') && AM_ADMIN) { $urlUpload = '/admin-upload/upload'; $urlGet = '/admin-upload/get'; //$el->toggleFrozen(true); } $el->setJsOptions(<<addGroup($name); $el->setLabel($this->title . "\n" . $this->description); $el->setSeparator("
"); foreach ($this->getOptions() as $k => $v) { $chkbox = $el->addAdvCheckbox(null, array('value' => $k))->setContent(___($v)); } $el->addHidden(null, array('value' => '')); $el->addFilter('array_filter'); if ($d = $this->getDefault()) { $default[$name] = $d; } break; } if ($el && $this->is_required && !$skip_validation) { $el->addRule('required'); } if ($this->cond_enabled) { $c_cond = $this->cond_type > 0 ? '==' : '!='; $c_field_val = json_encode($this->cond_field_val); $c_field_name = $this->getDi()->security->obfuscate($this->cond_field_id); $form->addScript() ->setScript(<<getDi()->formGridFieldTable->selectObjects("SELECT * FROM " . "?_form_grid_field WHERE field_id=? ORDER BY sort_order", $this->pk()); } function getOptions() { return $this->options ? json_decode($this->options, true) : array(); } function getDefault() { return $this->default ? json_decode($this->default, true) : array(); } function toPdf($v) { switch ($this->type) { case 'grid' : unset($v['rows']); $result = array(); $fields = $this->getFields(); $data = array(); foreach ($v as $row) { $_ = array(); foreach ($row as $key => $val) { $_[$this->getDi()->security->reveal($key)] = $val; } $data[] = $_; } $h = array(); foreach ($fields as $f) { $h[] = $f->title; } $result[] = $h; foreach ($data as $row) { $r = array(); foreach ($fields as $f) { $r[] = isset($row[$f->pk()]) ? $f->toPdf($row[$f->pk()]) : ''; } $result[] = $r; } return $result; case 'text': case 'textarea': return $v; case 'select': case 'advradio': case 'checkbox': $nv = array(); $op = $this->getOptions(); foreach((array)$v as $_) { $nv[] = isset($op[$_]) ? $op[$_] : $_; } return implode(', ', $nv); case 'date': return amDate($v); case 'upload': $upload = $this->getDi()->uploadTable->load($v, false); if (!$upload) return ''; switch ($upload->getType()) { case 'image/png' : $image = new Zend_Pdf_Resource_Image_Png($upload->getFullPath()); break; case 'image/jpeg' : $image = new Zend_Pdf_Resource_Image_Jpeg($upload->getFullPath()); break; case 'image/tiff' : $image = new Zend_Pdf_Resource_Image_Tiff($upload->getFullPath()); break; } return $image ? $image : $upload->name; } } function toTable($v) { return is_array($v) ? json_encode($v) : $v; } function fromTable($v) { $_ = json_decode($v, true); return $_ ? $_ : $v; } function delete() { $_ = parent::delete(); $this->deleteFromRelatedTable('?_form_data'); $this->deleteFromRelatedTable('?_form_grid_field'); return $_; } } class FormFieldTable extends Am_Table { protected $_table = '?_form_field'; protected $_key = 'field_id'; protected $_recordClass = 'FormField'; }password.php000064400000024051152101611340007113 0ustar00 * @license http://www.opensource.org/licenses/mit-license.html MIT License * @copyright 2012 The Authors */ namespace { if (!defined('PASSWORD_DEFAULT')) { define('PASSWORD_BCRYPT', 1); define('PASSWORD_DEFAULT', PASSWORD_BCRYPT); /** * Hash the password using the specified algorithm * * @param string $password The password to hash * @param int $algo The algorithm to use (Defined by PASSWORD_* constants) * @param array $options The options for the algorithm to use * * @return string|false The hashed password, or false on error. */ function password_hash($password, $algo, array $options = array()) { if (!function_exists('crypt')) { trigger_error("Crypt must be loaded for password_hash to function", E_USER_WARNING); return null; } if (!is_string($password)) { trigger_error("password_hash(): Password must be a string", E_USER_WARNING); return null; } if (!is_int($algo)) { trigger_error("password_hash() expects parameter 2 to be long, " . gettype($algo) . " given", E_USER_WARNING); return null; } $resultLength = 0; switch ($algo) { case PASSWORD_BCRYPT: // Note that this is a C constant, but not exposed to PHP, so we don't define it here. $cost = 10; if (isset($options['cost'])) { $cost = $options['cost']; if ($cost < 4 || $cost > 31) { trigger_error(sprintf("password_hash(): Invalid bcrypt cost parameter specified: %d", $cost), E_USER_WARNING); return null; } } // The length of salt to generate $raw_salt_len = 16; // The length required in the final serialization $required_salt_len = 22; $hash_format = sprintf("$2y$%02d$", $cost); // The expected length of the final crypt() output $resultLength = 60; break; default: trigger_error(sprintf("password_hash(): Unknown password hashing algorithm: %s", $algo), E_USER_WARNING); return null; } $salt_requires_encoding = false; if (isset($options['salt'])) { switch (gettype($options['salt'])) { case 'NULL': case 'boolean': case 'integer': case 'double': case 'string': $salt = (string) $options['salt']; break; case 'object': if (method_exists($options['salt'], '__tostring')) { $salt = (string) $options['salt']; break; } case 'array': case 'resource': default: trigger_error('password_hash(): Non-string salt parameter supplied', E_USER_WARNING); return null; } if (PasswordCompat\binary\_strlen($salt) < $required_salt_len) { trigger_error(sprintf("password_hash(): Provided salt is too short: %d expecting %d", PasswordCompat\binary\_strlen($salt), $required_salt_len), E_USER_WARNING); return null; } elseif (0 == preg_match('#^[a-zA-Z0-9./]+$#D', $salt)) { $salt_requires_encoding = true; } } else { $buffer = ''; $buffer_valid = false; if (function_exists('mcrypt_create_iv') && !defined('PHALANGER')) { $buffer = mcrypt_create_iv($raw_salt_len, MCRYPT_DEV_URANDOM); if ($buffer) { $buffer_valid = true; } } if (!$buffer_valid && function_exists('openssl_random_pseudo_bytes')) { $buffer = openssl_random_pseudo_bytes($raw_salt_len); if ($buffer) { $buffer_valid = true; } } if (!$buffer_valid && @is_readable('/dev/urandom')) { $f = fopen('/dev/urandom', 'r'); $read = PasswordCompat\binary\_strlen($buffer); while ($read < $raw_salt_len) { $buffer .= fread($f, $raw_salt_len - $read); $read = PasswordCompat\binary\_strlen($buffer); } fclose($f); if ($read >= $raw_salt_len) { $buffer_valid = true; } } if (!$buffer_valid || PasswordCompat\binary\_strlen($buffer) < $raw_salt_len) { $bl = PasswordCompat\binary\_strlen($buffer); for ($i = 0; $i < $raw_salt_len; $i++) { if ($i < $bl) { $buffer[$i] = $buffer[$i] ^ chr(mt_rand(0, 255)); } else { $buffer .= chr(mt_rand(0, 255)); } } } $salt = $buffer; $salt_requires_encoding = true; } if ($salt_requires_encoding) { // encode string with the Base64 variant used by crypt $base64_digits = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; $bcrypt64_digits = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; $base64_string = base64_encode($salt); $salt = strtr(rtrim($base64_string, '='), $base64_digits, $bcrypt64_digits); } $salt = PasswordCompat\binary\_substr($salt, 0, $required_salt_len); $hash = $hash_format . $salt; $ret = crypt($password, $hash); if (!is_string($ret) || PasswordCompat\binary\_strlen($ret) != $resultLength) { return false; } return $ret; } /** * Get information about the password hash. Returns an array of the information * that was used to generate the password hash. * * array( * 'algo' => 1, * 'algoName' => 'bcrypt', * 'options' => array( * 'cost' => 10, * ), * ) * * @param string $hash The password hash to extract info from * * @return array The array of information about the hash. */ function password_get_info($hash) { $return = array( 'algo' => 0, 'algoName' => 'unknown', 'options' => array(), ); if (PasswordCompat\binary\_substr($hash, 0, 4) == '$2y$' && PasswordCompat\binary\_strlen($hash) == 60) { $return['algo'] = PASSWORD_BCRYPT; $return['algoName'] = 'bcrypt'; list($cost) = sscanf($hash, "$2y$%d$"); $return['options']['cost'] = $cost; } return $return; } /** * Determine if the password hash needs to be rehashed according to the options provided * * If the answer is true, after validating the password using password_verify, rehash it. * * @param string $hash The hash to test * @param int $algo The algorithm used for new password hashes * @param array $options The options array passed to password_hash * * @return boolean True if the password needs to be rehashed. */ function password_needs_rehash($hash, $algo, array $options = array()) { $info = password_get_info($hash); if ($info['algo'] != $algo) { return true; } switch ($algo) { case PASSWORD_BCRYPT: $cost = isset($options['cost']) ? $options['cost'] : 10; if ($cost != $info['options']['cost']) { return true; } break; } return false; } /** * Verify a password against a hash using a timing attack resistant approach * * @param string $password The password to verify * @param string $hash The hash to verify against * * @return boolean If the password matches the hash */ function password_verify($password, $hash) { if (!function_exists('crypt')) { trigger_error("Crypt must be loaded for password_verify to function", E_USER_WARNING); return false; } $ret = crypt($password, $hash); if (!is_string($ret) || PasswordCompat\binary\_strlen($ret) != PasswordCompat\binary\_strlen($hash) || PasswordCompat\binary\_strlen($ret) <= 13) { return false; } $status = 0; for ($i = 0; $i < PasswordCompat\binary\_strlen($ret); $i++) { $status |= (ord($ret[$i]) ^ ord($hash[$i])); } return $status === 0; } } } namespace PasswordCompat\binary { /** * Count the number of bytes in a string * * We cannot simply use strlen() for this, because it might be overwritten by the mbstring extension. * In this case, strlen() will count the number of *characters* based on the internal encoding. A * sequence of bytes might be regarded as a single multibyte character. * * @param string $binary_string The input string * * @internal * @return int The number of bytes */ function _strlen($binary_string) { if (function_exists('mb_strlen')) { return mb_strlen($binary_string, '8bit'); } return strlen($binary_string); } /** * Get a substring based on byte limits * * @see _strlen() * * @param string $binary_string The input string * @param int $start * @param int $length * * @internal * @return string The substring */ function _substr($binary_string, $start, $length) { if (function_exists('mb_substr')) { return mb_substr($binary_string, $start, $length, '8bit'); } return substr($binary_string, $start, $length); } } S3.php000064400000235721152101611340005546 0ustar00setAuth($accessKey, $secretKey); $this->useSSL = $useSSL; $this->setEndpoint($endpoint); $this->region = $region; } public function getServiceRegion() { return $this->region; } /** * It may be used to change request implementation or to mock requests * @param S3Request $request This */ public function setRequestClass($string) { $this->_requestClass = $string; } public function setExceptionClass($string) { $this->_exceptionClass = $string; } /** * @return S3Request */ protected function _getRequest($params) { $reflectionClass = new ReflectionClass($this->_requestClass); $args = func_get_args(); return $reflectionClass->newInstanceArgs($args); } /** * Set the sertvice endpoint * * @param string $host Hostname * @return void */ public function setEndpoint($host) { $this->endpoint = $host; } /** * Set AWS access key and secret key * * @param string $accessKey Access key * @param string $secretKey Secret key * @return void */ public function setAuth($accessKey, $secretKey) { $this->__accessKey = $accessKey; $this->__secretKey = $secretKey; } public function getSecretKey() { return $this->__secretKey; } public function getAccessKey(){ return $this->__accessKey; } /** * Check if AWS keys have been set * * @return boolean */ public function hasAuth() { return ($this->__accessKey !== null && $this->__secretKey !== null); } /** * Set SSL on or off * * @param boolean $enabled SSL enabled * @param boolean $validate SSL certificate validation * @return void */ public function setSSL($enabled, $validate = true) { $this->useSSL = $enabled; $this->useSSLValidation = $validate; } /** * Set SSL client certificates (experimental) * * @param string $sslCert SSL client certificate * @param string $sslKey SSL client key * @param string $sslCACert SSL CA cert (only required if you are having problems with your system CA cert) * @return void */ public function setSSLAuth($sslCert = null, $sslKey = null, $sslCACert = null) { $this->sslCert = $sslCert; $this->sslKey = $sslKey; $this->sslCACert = $sslCACert; } /** * Set proxy information * * @param string $host Proxy hostname and port (localhost:1234) * @param string $user Proxy username * @param string $pass Proxy password * @param constant $type CURL proxy type * @return void */ public function setProxy($host, $user = null, $pass = null, $type = CURLPROXY_SOCKS5) { $this->proxy = array('host' => $host, 'type' => $type, 'user' => null, 'pass' => 'null'); } /** * Set the error mode to exceptions * * @param boolean $enabled Enable exceptions * @return void */ public function setExceptions($enabled = true) { $this->useExceptions = $enabled; } /** * Set signing key * * @param string $keyPairId AWS Key Pair ID * @param string $signingKey Private Key * @param boolean $isFile Load protected key from file, set to false to load string * @return boolean */ public function setSigningKey($keyPairId, $signingKey, $isFile = true) { $this->__signingKeyPairId = $keyPairId; if (($this->__signingKeyResource = openssl_pkey_get_private($isFile ? file_get_contents($signingKey) : $signingKey)) !== false) return true; $this->__triggerError('S3::setSigningKey(): Unable to open load protected key: '.$signingKey, __FILE__, __LINE__); return false; } /** * Free signing key from memory, MUST be called if you are using setSigningKey() * * @return void */ public function freeSigningKey() { if ($this->__signingKeyResource !== false) openssl_free_key($this->__signingKeyResource); } /** * Internal error handler * * @internal Internal error handler * @param string $message Error message * @param string $file Filename * @param integer $line Line number * @param integer $code Error code * @return void */ protected function __triggerError($message, $file, $line, $code = 0) { if ($this->useExceptions) { $class = $this->_exceptionClass; throw new $class($message, $file, $line, $code); } else trigger_error($message, E_USER_WARNING); } /** * Get a list of buckets * * @param boolean $detailed Returns detailed bucket list when true * @return array | false */ public function listBuckets($detailed = false) { $rest = $this->_getRequest('GET', '', '', $this->endpoint); $rest = $rest->getResponse($this); if ($rest->error === false && $rest->code !== 200) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { $this->__triggerError(sprintf("S3::listBuckets(): [%s] %s", $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; } $results = array(); if (!isset($rest->body->Buckets)) return $results; if ($detailed) { if (isset($rest->body->Owner, $rest->body->Owner->ID, $rest->body->Owner->DisplayName)) $results['owner'] = array( 'id' => (string)$rest->body->Owner->ID, 'name' => (string)$rest->body->Owner->ID ); $results['buckets'] = array(); foreach ($rest->body->Buckets->Bucket as $b) $results['buckets'][] = array( 'name' => (string)$b->Name, 'time' => strtotime((string)$b->CreationDate) ); } else foreach ($rest->body->Buckets->Bucket as $b) $results[] = (string)$b->Name; return $results; } /* * Get contents for a bucket * * If maxKeys is null this method will loop through truncated result sets * * @param string $bucket Bucket name * @param string $prefix Prefix * @param string $marker Marker (last file listed) * @param string $maxKeys Max keys (maximum number of keys to return) * @param string $delimiter Delimiter * @param boolean $returnCommonPrefixes Set to true to return CommonPrefixes * @return array | false */ public function getBucket($bucket, $prefix = null, $marker = null, $maxKeys = null, $delimiter = null, $returnCommonPrefixes = false) { $rest = $this->_getRequest('GET', $bucket, '', $this->endpoint); if ($maxKeys == 0) $maxKeys = null; if ($prefix !== null && $prefix !== '') $rest->setParameter('prefix', $prefix); if ($marker !== null && $marker !== '') $rest->setParameter('marker', $marker); if ($maxKeys !== null && $maxKeys !== '') $rest->setParameter('max-keys', $maxKeys); if ($delimiter !== null && $delimiter !== '') $rest->setParameter('delimiter', $delimiter); $response = $rest->getResponse($this); if ($response->error === false && $response->code !== 200) $response->error = array('code' => $response->code, 'message' => 'Unexpected HTTP status'); if ($response->error !== false) { $this->__triggerError(sprintf("S3::getBucket(): [%s] %s", $response->error['code'], $response->error['message']), __FILE__, __LINE__); return false; } $results = array(); $nextMarker = null; if (isset($response->body, $response->body->Contents)) foreach ($response->body->Contents as $c) { $results[(string)$c->Key] = array( 'name' => (string)$c->Key, 'time' => strtotime((string)$c->LastModified), 'size' => (int)$c->Size, 'hash' => substr((string)$c->ETag, 1, -1) ); $nextMarker = (string)$c->Key; } if ($returnCommonPrefixes && isset($response->body, $response->body->CommonPrefixes)) foreach ($response->body->CommonPrefixes as $c) $results[(string)$c->Prefix] = array('prefix' => (string)$c->Prefix); if (isset($response->body, $response->body->IsTruncated) && (string)$response->body->IsTruncated == 'false') return $results; if (isset($response->body, $response->body->NextMarker)) $nextMarker = (string)$response->body->NextMarker; // Loop through truncated results if maxKeys isn't specified if ($maxKeys == null && $nextMarker !== null && (string)$response->body->IsTruncated == 'true') do { $rest = $this->_getRequest('GET', $bucket, '', $this->endpoint); if ($prefix !== null && $prefix !== '') $rest->setParameter('prefix', $prefix); $rest->setParameter('marker', $nextMarker); if ($delimiter !== null && $delimiter !== '') $rest->setParameter('delimiter', $delimiter); if (($response = $rest->getResponse($this)) == false || $response->code !== 200) break; if (isset($response->body, $response->body->Contents)) foreach ($response->body->Contents as $c) { $results[(string)$c->Key] = array( 'name' => (string)$c->Key, 'time' => strtotime((string)$c->LastModified), 'size' => (int)$c->Size, 'hash' => substr((string)$c->ETag, 1, -1) ); $nextMarker = (string)$c->Key; } if ($returnCommonPrefixes && isset($response->body, $response->body->CommonPrefixes)) foreach ($response->body->CommonPrefixes as $c) $results[(string)$c->Prefix] = array('prefix' => (string)$c->Prefix); if (isset($response->body, $response->body->NextMarker)) $nextMarker = (string)$response->body->NextMarker; } while ($response !== false && (string)$response->body->IsTruncated == 'true'); return $results; } /** * Put a bucket * * @param string $bucket Bucket name * @param constant $acl ACL flag * @param string $location Set as "EU" to create buckets hosted in Europe * @return boolean */ public function putBucket($bucket, $acl = self::ACL_PRIVATE, $location = false) { $rest = $this->_getRequest('PUT', $bucket, '', $this->endpoint); $rest->setAmzHeader('x-amz-acl', $acl); if ($location !== false) { $dom = new DOMDocument; $createBucketConfiguration = $dom->createElement('CreateBucketConfiguration'); $locationConstraint = $dom->createElement('LocationConstraint', $location); $createBucketConfiguration->appendChild($locationConstraint); $dom->appendChild($createBucketConfiguration); $rest->data = $dom->saveXML(); $rest->size = strlen($rest->data); $rest->setHeader('Content-Type', 'application/xml'); } $rest = $rest->getResponse($this); if ($rest->error === false && $rest->code !== 200) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { $this->__triggerError(sprintf("S3::putBucket({$bucket}, {$acl}, {$location}): [%s] %s", $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; } return true; } /** * Delete an empty bucket * * @param string $bucket Bucket name * @return boolean */ public function deleteBucket($bucket) { $rest = $this->_getRequest('DELETE', $bucket, '', $this->endpoint); $rest = $rest->getResponse($this); if ($rest->error === false && $rest->code !== 204) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { $this->__triggerError(sprintf("S3::deleteBucket({$bucket}): [%s] %s", $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; } return true; } /** * Create input info array for putObject() * * @param string $file Input file * @param mixed $md5sum Use MD5 hash (supply a string if you want to use your own) * @return array | false */ public function inputFile($file, $md5sum = true) { if (!file_exists($file) || !is_file($file) || !is_readable($file)) { $this->__triggerError('S3::inputFile(): Unable to open input file: '.$file, __FILE__, __LINE__); return false; } return array('file' => $file, 'size' => filesize($file), 'md5sum' => $md5sum !== false ? (is_string($md5sum) ? $md5sum : base64_encode(md5_file($file, true))) : ''); } /** * Create input array info for putObject() with a resource * * @param string $resource Input resource to read from * @param integer $bufferSize Input byte size * @param string $md5sum MD5 hash to send (optional) * @return array | false */ public function inputResource(&$resource, $bufferSize, $md5sum = '') { if (!is_resource($resource) || $bufferSize < 0) { $this->__triggerError('S3::inputResource(): Invalid resource or buffer size', __FILE__, __LINE__); return false; } $input = array('size' => $bufferSize, 'md5sum' => $md5sum); $input['fp'] =& $resource; return $input; } /** * Put an object * * @param mixed $input Input data * @param string $bucket Bucket name * @param string $uri Object URI * @param constant $acl ACL constant * @param array $metaHeaders Array of x-amz-meta-* headers * @param array $requestHeaders Array of request headers or content type as a string * @param constant $storageClass Storage class constant * @return boolean */ public function putObject($input, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $requestHeaders = array(), $storageClass = self::STORAGE_CLASS_STANDARD) { if ($input === false) return false; $rest = $this->_getRequest('PUT', $bucket, $uri, $this->endpoint); if (is_string($input)) $input = array( 'data' => $input, 'size' => strlen($input), 'md5sum' => base64_encode(md5($input, true)) ); // Data if (isset($input['fp'])) $rest->fp =& $input['fp']; elseif (isset($input['file'])) $rest->fp = @fopen($input['file'], 'rb'); elseif (isset($input['data'])) $rest->data = $input['data']; // Content-Length (required) if (isset($input['size']) && $input['size'] >= 0) $rest->size = $input['size']; else { if (isset($input['file'])) $rest->size = filesize($input['file']); elseif (isset($input['data'])) $rest->size = strlen($input['data']); } // Custom request headers (Content-Type, Content-Disposition, Content-Encoding) if (is_array($requestHeaders)) foreach ($requestHeaders as $h => $v) $rest->setHeader($h, $v); elseif (is_string($requestHeaders)) // Support for legacy contentType parameter $input['type'] = $requestHeaders; // Content-Type if (!isset($input['type'])) { if (isset($requestHeaders['Content-Type'])) $input['type'] =& $requestHeaders['Content-Type']; elseif (isset($input['file'])) $input['type'] = $this->__getMimeType($input['file']); else $input['type'] = 'application/octet-stream'; } if ($storageClass !== self::STORAGE_CLASS_STANDARD) // Storage class $rest->setAmzHeader('x-amz-storage-class', $storageClass); // We need to post with Content-Length and Content-Type, MD5 is optional if ($rest->size >= 0 && ($rest->fp !== false || $rest->data !== false)) { $rest->setHeader('Content-Type', $input['type']); if (isset($input['md5sum'])) $rest->setHeader('Content-MD5', $input['md5sum']); $rest->setAmzHeader('x-amz-acl', $acl); foreach ($metaHeaders as $h => $v) $rest->setAmzHeader('x-amz-meta-'.$h, $v); $rest->getResponse($this); } else $rest->response->error = array('code' => 0, 'message' => 'Missing input parameters'); if ($rest->response->error === false && $rest->response->code !== 200) $rest->response->error = array('code' => $rest->response->code, 'message' => 'Unexpected HTTP status'); if ($rest->response->error !== false) { $this->__triggerError(sprintf("S3::putObject(): [%s] %s", $rest->response->error['code'], $rest->response->error['message']), __FILE__, __LINE__); return false; } return true; } /** * Put an object from a file (legacy function) * * @param string $file Input file path * @param string $bucket Bucket name * @param string $uri Object URI * @param constant $acl ACL constant * @param array $metaHeaders Array of x-amz-meta-* headers * @param string $contentType Content type * @return boolean */ public function putObjectFile($file, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $contentType = null) { return $this->putObject($this->inputFile($file), $bucket, $uri, $acl, $metaHeaders, $contentType); } /** * Put an object from a string (legacy function) * * @param string $string Input data * @param string $bucket Bucket name * @param string $uri Object URI * @param constant $acl ACL constant * @param array $metaHeaders Array of x-amz-meta-* headers * @param string $contentType Content type * @return boolean */ public function putObjectString($string, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $contentType = 'text/plain') { return $this->putObject($string, $bucket, $uri, $acl, $metaHeaders, $contentType); } /** * Get an object * * @param string $bucket Bucket name * @param string $uri Object URI * @param mixed $saveTo Filename or resource to write to * @return mixed */ public function getObject($bucket, $uri, $saveTo = false) { $rest = $this->_getRequest('GET', $bucket, $uri, $this->endpoint); if ($saveTo !== false) { if (is_resource($saveTo)) $rest->fp =& $saveTo; else if (($rest->fp = @fopen($saveTo, 'wb')) !== false) $rest->file = realpath($saveTo); else $rest->response->error = array('code' => 0, 'message' => 'Unable to open save file for writing: '.$saveTo); } if ($rest->response->error === false) $rest->getResponse($this); if ($rest->response->error === false && $rest->response->code !== 200) $rest->response->error = array('code' => $rest->response->code, 'message' => 'Unexpected HTTP status'); if ($rest->response->error !== false) { $this->__triggerError(sprintf("S3::getObject({$bucket}, {$uri}): [%s] %s", $rest->response->error['code'], $rest->response->error['message']), __FILE__, __LINE__); return false; } return $rest->response; } /** * Get object information * * @param string $bucket Bucket name * @param string $uri Object URI * @param boolean $returnInfo Return response information * @return mixed | false */ public function getObjectInfo($bucket, $uri, $returnInfo = true) { $rest = $this->_getRequest('HEAD', $bucket, $uri, $this->endpoint); $rest = $rest->getResponse($this); if ($rest->error === false && ($rest->code !== 200 && $rest->code !== 404)) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { $this->__triggerError(sprintf("S3::getObjectInfo({$bucket}, {$uri}): [%s] %s", $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; } return $rest->code == 200 ? $returnInfo ? $rest->headers : true : false; } /** * Copy an object * * @param string $bucket Source bucket name * @param string $uri Source object URI * @param string $bucket Destination bucket name * @param string $uri Destination object URI * @param constant $acl ACL constant * @param array $metaHeaders Optional array of x-amz-meta-* headers * @param array $requestHeaders Optional array of request headers (content type, disposition, etc.) * @param constant $storageClass Storage class constant * @return mixed | false */ public function copyObject($srcBucket, $srcUri, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $requestHeaders = array(), $storageClass = self::STORAGE_CLASS_STANDARD) { $rest = $this->_getRequest('PUT', $bucket, $uri, $this->endpoint); $rest->setHeader('Content-Length', 0); foreach ($requestHeaders as $h => $v) $rest->setHeader($h, $v); foreach ($metaHeaders as $h => $v) $rest->setAmzHeader('x-amz-meta-'.$h, $v); if ($storageClass !== self::STORAGE_CLASS_STANDARD) // Storage class $rest->setAmzHeader('x-amz-storage-class', $storageClass); $rest->setAmzHeader('x-amz-acl', $acl); // Added rawurlencode() for $srcUri (thanks a.yamanoi) $rest->setAmzHeader('x-amz-copy-source', sprintf('/%s/%s', $srcBucket, rawurlencode($srcUri))); if (sizeof($requestHeaders) > 0 || sizeof($metaHeaders) > 0) $rest->setAmzHeader('x-amz-metadata-directive', 'REPLACE'); $rest = $rest->getResponse($this); if ($rest->error === false && $rest->code !== 200) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { $this->__triggerError(sprintf("S3::copyObject({$srcBucket}, {$srcUri}, {$bucket}, {$uri}): [%s] %s", $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; } return isset($rest->body->LastModified, $rest->body->ETag) ? array( 'time' => strtotime((string)$rest->body->LastModified), 'hash' => substr((string)$rest->body->ETag, 1, -1) ) : false; } /** * Set logging for a bucket * * @param string $bucket Bucket name * @param string $targetBucket Target bucket (where logs are stored) * @param string $targetPrefix Log prefix (e,g; domain.com-) * @return boolean */ public function setBucketLogging($bucket, $targetBucket, $targetPrefix = null) { // The S3 log delivery group has to be added to the target bucket's ACP if ($targetBucket !== null && ($acp = $this->getAccessControlPolicy($targetBucket, '')) !== false) { // Only add permissions to the target bucket when they do not exist $aclWriteSet = false; $aclReadSet = false; foreach ($acp['acl'] as $acl) if ($acl['type'] == 'Group' && $acl['uri'] == 'http://acs.amazonaws.com/groups/s3/LogDelivery') { if ($acl['permission'] == 'WRITE') $aclWriteSet = true; elseif ($acl['permission'] == 'READ_ACP') $aclReadSet = true; } if (!$aclWriteSet) $acp['acl'][] = array( 'type' => 'Group', 'uri' => 'http://acs.amazonaws.com/groups/s3/LogDelivery', 'permission' => 'WRITE' ); if (!$aclReadSet) $acp['acl'][] = array( 'type' => 'Group', 'uri' => 'http://acs.amazonaws.com/groups/s3/LogDelivery', 'permission' => 'READ_ACP' ); if (!$aclReadSet || !$aclWriteSet) $this->setAccessControlPolicy($targetBucket, '', $acp); } $dom = new DOMDocument; $bucketLoggingStatus = $dom->createElement('BucketLoggingStatus'); $bucketLoggingStatus->setAttribute('xmlns', 'http://s3.amazonaws.com/doc/2006-03-01/'); if ($targetBucket !== null) { if ($targetPrefix == null) $targetPrefix = $bucket . '-'; $loggingEnabled = $dom->createElement('LoggingEnabled'); $loggingEnabled->appendChild($dom->createElement('TargetBucket', $targetBucket)); $loggingEnabled->appendChild($dom->createElement('TargetPrefix', $targetPrefix)); // TODO: Add TargetGrants? $bucketLoggingStatus->appendChild($loggingEnabled); } $dom->appendChild($bucketLoggingStatus); $rest = $this->_getRequest('PUT', $bucket, '', $this->endpoint); $rest->setParameter('logging', null); $rest->data = $dom->saveXML(); $rest->size = strlen($rest->data); $rest->setHeader('Content-Type', 'application/xml'); $rest = $rest->getResponse($this); if ($rest->error === false && $rest->code !== 200) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { $this->__triggerError(sprintf("S3::setBucketLogging({$bucket}, {$uri}): [%s] %s", $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; } return true; } /** * Get logging status for a bucket * * This will return false if logging is not enabled. * Note: To enable logging, you also need to grant write access to the log group * * @param string $bucket Bucket name * @return array | false */ public function getBucketLogging($bucket) { $rest = $this->_getRequest('GET', $bucket, '', $this->endpoint); $rest->setParameter('logging', null); $rest = $rest->getResponse($this); if ($rest->error === false && $rest->code !== 200) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { $this->__triggerError(sprintf("S3::getBucketLogging({$bucket}): [%s] %s", $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; } if (!isset($rest->body->LoggingEnabled)) return false; // No logging return array( 'targetBucket' => (string)$rest->body->LoggingEnabled->TargetBucket, 'targetPrefix' => (string)$rest->body->LoggingEnabled->TargetPrefix, ); } /** * Disable bucket logging * * @param string $bucket Bucket name * @return boolean */ public function disableBucketLogging($bucket) { return $this->setBucketLogging($bucket, null); } /** * Get a bucket's location * * @param string $bucket Bucket name * @return string | false */ public function getBucketLocation($bucket) { $rest = $this->_getRequest('GET', $bucket, '', $this->endpoint); $rest->setParameter('location', null); $rest = $rest->getResponse($this); if ($rest->error === false && $rest->code !== 200) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { $this->__triggerError(sprintf("S3::getBucketLocation({$bucket}): [%s] %s", $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; } return (isset($rest->body[0]) && (string)$rest->body[0] !== '') ? (string)$rest->body[0] : 'US'; } /** * Set object or bucket Access Control Policy * * @param string $bucket Bucket name * @param string $uri Object URI * @param array $acp Access Control Policy Data (same as the data returned from getAccessControlPolicy) * @return boolean */ public function setAccessControlPolicy($bucket, $uri = '', $acp = array()) { $dom = new DOMDocument; $dom->formatOutput = true; $accessControlPolicy = $dom->createElement('AccessControlPolicy'); $accessControlList = $dom->createElement('AccessControlList'); // It seems the owner has to be passed along too $owner = $dom->createElement('Owner'); $owner->appendChild($dom->createElement('ID', $acp['owner']['id'])); $owner->appendChild($dom->createElement('DisplayName', $acp['owner']['name'])); $accessControlPolicy->appendChild($owner); foreach ($acp['acl'] as $g) { $grant = $dom->createElement('Grant'); $grantee = $dom->createElement('Grantee'); $grantee->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'); if (isset($g['id'])) { // CanonicalUser (DisplayName is omitted) $grantee->setAttribute('xsi:type', 'CanonicalUser'); $grantee->appendChild($dom->createElement('ID', $g['id'])); } elseif (isset($g['email'])) { // AmazonCustomerByEmail $grantee->setAttribute('xsi:type', 'AmazonCustomerByEmail'); $grantee->appendChild($dom->createElement('EmailAddress', $g['email'])); } elseif ($g['type'] == 'Group') { // Group $grantee->setAttribute('xsi:type', 'Group'); $grantee->appendChild($dom->createElement('URI', $g['uri'])); } $grant->appendChild($grantee); $grant->appendChild($dom->createElement('Permission', $g['permission'])); $accessControlList->appendChild($grant); } $accessControlPolicy->appendChild($accessControlList); $dom->appendChild($accessControlPolicy); $rest = $this->_getRequest('PUT', $bucket, $uri, $this->endpoint); $rest->setParameter('acl', null); $rest->data = $dom->saveXML(); $rest->size = strlen($rest->data); $rest->setHeader('Content-Type', 'application/xml'); $rest = $rest->getResponse($this); if ($rest->error === false && $rest->code !== 200) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { $this->__triggerError(sprintf("S3::setAccessControlPolicy({$bucket}, {$uri}): [%s] %s", $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; } return true; } /** * Get object or bucket Access Control Policy * * @param string $bucket Bucket name * @param string $uri Object URI * @return mixed | false */ public function getAccessControlPolicy($bucket, $uri = '') { $rest = $this->_getRequest('GET', $bucket, $uri, $this->endpoint); $rest->setParameter('acl', null); $rest = $rest->getResponse($this); if ($rest->error === false && $rest->code !== 200) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { $this->__triggerError(sprintf("S3::getAccessControlPolicy({$bucket}, {$uri}): [%s] %s", $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; } $acp = array(); if (isset($rest->body->Owner, $rest->body->Owner->ID, $rest->body->Owner->DisplayName)) $acp['owner'] = array( 'id' => (string)$rest->body->Owner->ID, 'name' => (string)$rest->body->Owner->DisplayName ); if (isset($rest->body->AccessControlList)) { $acp['acl'] = array(); foreach ($rest->body->AccessControlList->Grant as $grant) { foreach ($grant->Grantee as $grantee) { if (isset($grantee->ID, $grantee->DisplayName)) // CanonicalUser $acp['acl'][] = array( 'type' => 'CanonicalUser', 'id' => (string)$grantee->ID, 'name' => (string)$grantee->DisplayName, 'permission' => (string)$grant->Permission ); elseif (isset($grantee->EmailAddress)) // AmazonCustomerByEmail $acp['acl'][] = array( 'type' => 'AmazonCustomerByEmail', 'email' => (string)$grantee->EmailAddress, 'permission' => (string)$grant->Permission ); elseif (isset($grantee->URI)) // Group $acp['acl'][] = array( 'type' => 'Group', 'uri' => (string)$grantee->URI, 'permission' => (string)$grant->Permission ); else continue; } } } return $acp; } /** * Delete an object * * @param string $bucket Bucket name * @param string $uri Object URI * @return boolean */ public function deleteObject($bucket, $uri) { $rest = $this->_getRequest('DELETE', $bucket, $uri, $this->endpoint); $rest = $rest->getResponse($this); if ($rest->error === false && $rest->code !== 204) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { $this->__triggerError(sprintf("S3::deleteObject(): [%s] %s", $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; } return true; } /** * Get a query string authenticated URL * * @param string $bucket Bucket name * @param string $uri Object URI * @param integer $lifetime Lifetime in seconds * @param boolean $hostBucket Use the bucket name as the hostname * @param boolean $https Use HTTPS ($hostBucket should be false for SSL verification) * @return string */ public function getAuthenticatedURL($bucket, $uri, $lifetime, $hostBucket = false, $https = false, $force_download = true) { $uri = str_replace('%2F', '/', rawurlencode($uri)); // URI should be encoded (thanks Sean O'Dea) $r = $this->_getRequest('GET', $bucket, $uri, $this->endpoint); $expires = time() + $lifetime; return $r->getAuthenticatedURL($this, $bucket, $uri, $lifetime, $hostBucket, $https, $force_download); } /** * Get a CloudFront signed policy URL * * @param array $policy Policy * @return string */ public function getSignedPolicyURL($policy) { $data = json_encode($policy); $signature = ''; if (!openssl_sign($data, $signature, $this->__signingKeyResource)) return false; $encoded = str_replace(array('+', '='), array('-', '_', '~'), base64_encode($data)); $signature = str_replace(array('+', '='), array('-', '_', '~'), base64_encode($signature)); $url = $policy['Statement'][0]['Resource'] . '?'; foreach (array('Policy' => $encoded, 'Signature' => $signature, 'Key-Pair-Id' => $this->__signingKeyPairId) as $k => $v) $url .= $k.'='.str_replace('%2F', '/', rawurlencode($v)).'&'; return substr($url, 0, -1); } /** * Get a CloudFront canned policy URL * * @param string $string URL to sign * @param integer $lifetime URL lifetime * @return string */ public function getSignedCannedURL($url, $lifetime) { return $this->getSignedPolicyURL(array( 'Statement' => array( array('Resource' => $url, 'Condition' => array( 'DateLessThan' => array('AWS:EpochTime' => time() + $lifetime) )) ) )); } /** * Get upload POST parameters for form uploads * * @param string $bucket Bucket name * @param string $uriPrefix Object URI prefix * @param constant $acl ACL constant * @param integer $lifetime Lifetime in seconds * @param integer $maxFileSize Maximum filesize in bytes (default 5MB) * @param string $successRedirect Redirect URL or 200 / 201 status code * @param array $amzHeaders Array of x-amz-meta-* headers * @param array $headers Array of request headers or content type as a string * @param boolean $flashVars Includes additional "Filename" variable posted by Flash * @return object */ public function getHttpUploadPostParams($bucket, $uriPrefix = '', $acl = self::ACL_PRIVATE, $lifetime = 3600, $maxFileSize = 5242880, $successRedirect = "201", $amzHeaders = array(), $headers = array(), $flashVars = false) { // Create policy object $policy = new stdClass; $policy->expiration = gmdate('Y-m-d\TH:i:s\Z', (time() + $lifetime)); $policy->conditions = array(); $obj = new stdClass; $obj->bucket = $bucket; array_push($policy->conditions, $obj); $obj = new stdClass; $obj->acl = $acl; array_push($policy->conditions, $obj); $obj = new stdClass; // 200 for non-redirect uploads if (is_numeric($successRedirect) && in_array((int)$successRedirect, array(200, 201))) $obj->success_action_status = (string)$successRedirect; else // URL $obj->success_action_redirect = $successRedirect; array_push($policy->conditions, $obj); if ($acl !== self::ACL_PUBLIC_READ) array_push($policy->conditions, array('eq', '$acl', $acl)); array_push($policy->conditions, array('starts-with', '$key', $uriPrefix)); if ($flashVars) array_push($policy->conditions, array('starts-with', '$Filename', '')); foreach (array_keys($headers) as $headerKey) array_push($policy->conditions, array('starts-with', '$'.$headerKey, '')); foreach ($amzHeaders as $headerKey => $headerVal) { $obj = new stdClass; $obj->{$headerKey} = (string)$headerVal; array_push($policy->conditions, $obj); } array_push($policy->conditions, array('content-length-range', 0, $maxFileSize)); $policy = base64_encode(str_replace('\/', '/', json_encode($policy))); // Create parameters $params = new stdClass; $params->AWSAccessKeyId = $this->__accessKey; $params->key = $uriPrefix.'${filename}'; $params->acl = $acl; $params->policy = $policy; unset($policy); $params->signature = $this->__getHash($params->policy); if (is_numeric($successRedirect) && in_array((int)$successRedirect, array(200, 201))) $params->success_action_status = (string)$successRedirect; else $params->success_action_redirect = $successRedirect; foreach ($headers as $headerKey => $headerVal) $params->{$headerKey} = (string)$headerVal; foreach ($amzHeaders as $headerKey => $headerVal) $params->{$headerKey} = (string)$headerVal; return $params; } /** * Create a CloudFront distribution * * @param string $bucket Bucket name * @param boolean $enabled Enabled (true/false) * @param array $cnames Array containing CNAME aliases * @param string $comment Use the bucket name as the hostname * @param string $defaultRootObject Default root object * @param string $originAccessIdentity Origin access identity * @param array $trustedSigners Array of trusted signers * @return array | false */ public function createDistribution($bucket, $enabled = true, $cnames = array(), $comment = null, $defaultRootObject = null, $originAccessIdentity = null, $trustedSigners = array()) { if (!extension_loaded('openssl')) { $this->__triggerError(sprintf("S3::createDistribution({$bucket}, ".(int)$enabled.", [], '$comment'): %s", "CloudFront functionality requires SSL"), __FILE__, __LINE__); return false; } $useSSL = $this->useSSL; $this->useSSL = true; // CloudFront requires SSL $rest = $this->_getRequest('POST', '', '2010-11-01/distribution', 'cloudfront.amazonaws.com'); $rest->data = $this->__getCloudFrontDistributionConfigXML( $bucket.'.s3.amazonaws.com', $enabled, (string)$comment, (string)microtime(true), $cnames, $defaultRootObject, $originAccessIdentity, $trustedSigners ); $rest->size = strlen($rest->data); $rest->setHeader('Content-Type', 'application/xml'); $rest = $this->__getCloudFrontResponse($rest); $this->useSSL = $useSSL; if ($rest->error === false && $rest->code !== 201) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { $this->__triggerError(sprintf("S3::createDistribution({$bucket}, ".(int)$enabled.", [], '$comment'): [%s] %s", $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; } elseif ($rest->body instanceof SimpleXMLElement) return $this->__parseCloudFrontDistributionConfig($rest->body); return false; } /** * Get CloudFront distribution info * * @param string $distributionId Distribution ID from listDistributions() * @return array | false */ public function getDistribution($distributionId) { if (!extension_loaded('openssl')) { $this->__triggerError(sprintf("S3::getDistribution($distributionId): %s", "CloudFront functionality requires SSL"), __FILE__, __LINE__); return false; } $useSSL = $this->useSSL; $this->useSSL = true; // CloudFront requires SSL $rest = $this->_getRequest('GET', '', '2010-11-01/distribution/'.$distributionId, 'cloudfront.amazonaws.com'); $rest = $this->__getCloudFrontResponse($rest); $this->useSSL = $useSSL; if ($rest->error === false && $rest->code !== 200) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { $this->__triggerError(sprintf("S3::getDistribution($distributionId): [%s] %s", $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; } elseif ($rest->body instanceof SimpleXMLElement) { $dist = $this->__parseCloudFrontDistributionConfig($rest->body); $dist['hash'] = $rest->headers['hash']; $dist['id'] = $distributionId; return $dist; } return false; } /** * Update a CloudFront distribution * * @param array $dist Distribution array info identical to output of getDistribution() * @return array | false */ public function updateDistribution($dist) { if (!extension_loaded('openssl')) { $this->__triggerError(sprintf("S3::updateDistribution({$dist['id']}): %s", "CloudFront functionality requires SSL"), __FILE__, __LINE__); return false; } $useSSL = $this->useSSL; $this->useSSL = true; // CloudFront requires SSL $rest = $this->_getRequest('PUT', '', '2010-11-01/distribution/'.$dist['id'].'/config', 'cloudfront.amazonaws.com'); $rest->data = $this->__getCloudFrontDistributionConfigXML( $dist['origin'], $dist['enabled'], $dist['comment'], $dist['callerReference'], $dist['cnames'], $dist['defaultRootObject'], $dist['originAccessIdentity'], $dist['trustedSigners'] ); $rest->size = strlen($rest->data); $rest->setHeader('If-Match', $dist['hash']); $rest = $this->__getCloudFrontResponse($rest); $this->useSSL = $useSSL; if ($rest->error === false && $rest->code !== 200) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { $this->__triggerError(sprintf("S3::updateDistribution({$dist['id']}): [%s] %s", $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; } else { $dist = $this->__parseCloudFrontDistributionConfig($rest->body); $dist['hash'] = $rest->headers['hash']; return $dist; } return false; } /** * Delete a CloudFront distribution * * @param array $dist Distribution array info identical to output of getDistribution() * @return boolean */ public function deleteDistribution($dist) { if (!extension_loaded('openssl')) { $this->__triggerError(sprintf("S3::deleteDistribution({$dist['id']}): %s", "CloudFront functionality requires SSL"), __FILE__, __LINE__); return false; } $useSSL = $this->useSSL; $this->useSSL = true; // CloudFront requires SSL $rest = $this->_getRequest('DELETE', '', '2008-06-30/distribution/'.$dist['id'], 'cloudfront.amazonaws.com'); $rest->setHeader('If-Match', $dist['hash']); $rest = $this->__getCloudFrontResponse($rest); $this->useSSL = $useSSL; if ($rest->error === false && $rest->code !== 204) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { $this->__triggerError(sprintf("S3::deleteDistribution({$dist['id']}): [%s] %s", $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; } return true; } /** * Get a list of CloudFront distributions * * @return array */ public function listDistributions() { if (!extension_loaded('openssl')) { $this->__triggerError(sprintf("S3::listDistributions(): [%s] %s", "CloudFront functionality requires SSL"), __FILE__, __LINE__); return false; } $useSSL = $this->useSSL; $this->useSSL = true; // CloudFront requires SSL $rest = $this->_getRequest('GET', '', '2010-11-01/distribution', 'cloudfront.amazonaws.com'); $rest = $this->__getCloudFrontResponse($rest); $this->useSSL = $useSSL; if ($rest->error === false && $rest->code !== 200) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { $this->__triggerError(sprintf("S3::listDistributions(): [%s] %s", $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; } elseif ($rest->body instanceof SimpleXMLElement && isset($rest->body->DistributionSummary)) { $list = array(); if (isset($rest->body->Marker, $rest->body->MaxItems, $rest->body->IsTruncated)) { //$info['marker'] = (string)$rest->body->Marker; //$info['maxItems'] = (int)$rest->body->MaxItems; //$info['isTruncated'] = (string)$rest->body->IsTruncated == 'true' ? true : false; } foreach ($rest->body->DistributionSummary as $summary) $list[(string)$summary->Id] = $this->__parseCloudFrontDistributionConfig($summary); return $list; } return array(); } /** * List CloudFront Origin Access Identities * * @return array */ public function listOriginAccessIdentities() { if (!extension_loaded('openssl')) { $this->__triggerError(sprintf("S3::listOriginAccessIdentities(): [%s] %s", "CloudFront functionality requires SSL"), __FILE__, __LINE__); return false; } $this->useSSL = true; // CloudFront requires SSL $rest = $this->_getRequest('GET', '', '2010-11-01/origin-access-identity/cloudfront', 'cloudfront.amazonaws.com'); $rest = $this->__getCloudFrontResponse($rest); $useSSL = $this->useSSL; if ($rest->error === false && $rest->code !== 200) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { trigger_error(sprintf("S3::listOriginAccessIdentities(): [%s] %s", $rest->error['code'], $rest->error['message']), E_USER_WARNING); return false; } if (isset($rest->body->CloudFrontOriginAccessIdentitySummary)) { $identities = array(); foreach ($rest->body->CloudFrontOriginAccessIdentitySummary as $identity) if (isset($identity->S3CanonicalUserId)) $identities[(string)$identity->Id] = array('id' => (string)$identity->Id, 's3CanonicalUserId' => (string)$identity->S3CanonicalUserId); return $identities; } return false; } /** * Invalidate objects in a CloudFront distribution * * Thanks to Martin Lindkvist for S3::invalidateDistribution() * * @param string $distributionId Distribution ID from listDistributions() * @param array $paths Array of object paths to invalidate * @return boolean */ public function invalidateDistribution($distributionId, $paths) { if (!extension_loaded('openssl')) { $this->__triggerError(sprintf("S3::invalidateDistribution(): [%s] %s", "CloudFront functionality requires SSL"), __FILE__, __LINE__); return false; } $useSSL = $this->useSSL; $this->useSSL = true; // CloudFront requires SSL $rest = $this->_getRequest('POST', '', '2010-08-01/distribution/'.$distributionId.'/invalidation', 'cloudfront.amazonaws.com'); $rest->data = $this->__getCloudFrontInvalidationBatchXML($paths, (string)microtime(true)); $rest->size = strlen($rest->data); $rest = $this->__getCloudFrontResponse($rest); $this->useSSL = $useSSL; if ($rest->error === false && $rest->code !== 201) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { trigger_error(sprintf("S3::invalidate('{$distributionId}',{$paths}): [%s] %s", $rest->error['code'], $rest->error['message']), E_USER_WARNING); return false; } return true; } /** * Get a InvalidationBatch DOMDocument * * @internal Used to create XML in invalidateDistribution() * @param array $paths Paths to objects to invalidateDistribution * @return string */ protected function __getCloudFrontInvalidationBatchXML($paths, $callerReference = '0') { $dom = new DOMDocument('1.0', 'UTF-8'); $dom->formatOutput = true; $invalidationBatch = $dom->createElement('InvalidationBatch'); foreach ($paths as $path) $invalidationBatch->appendChild($dom->createElement('Path', $path)); $invalidationBatch->appendChild($dom->createElement('CallerReference', $callerReference)); $dom->appendChild($invalidationBatch); return $dom->saveXML(); } /** * Get a DistributionConfig DOMDocument * * http://docs.amazonwebservices.com/AmazonCloudFront/latest/APIReference/index.html?PutConfig.html * * @internal Used to create XML in createDistribution() and updateDistribution() * @param string $bucket S3 Origin bucket * @param boolean $enabled Enabled (true/false) * @param string $comment Comment to append * @param string $callerReference Caller reference * @param array $cnames Array of CNAME aliases * @param string $defaultRootObject Default root object * @param string $originAccessIdentity Origin access identity * @param array $trustedSigners Array of trusted signers * @return string */ protected function __getCloudFrontDistributionConfigXML($bucket, $enabled, $comment, $callerReference = '0', $cnames = array(), $defaultRootObject = null, $originAccessIdentity = null, $trustedSigners = array()) { $dom = new DOMDocument('1.0', 'UTF-8'); $dom->formatOutput = true; $distributionConfig = $dom->createElement('DistributionConfig'); $distributionConfig->setAttribute('xmlns', 'http://cloudfront.amazonaws.com/doc/2010-11-01/'); $origin = $dom->createElement('S3Origin'); $origin->appendChild($dom->createElement('DNSName', $bucket)); if ($originAccessIdentity !== null) $origin->appendChild($dom->createElement('OriginAccessIdentity', $originAccessIdentity)); $distributionConfig->appendChild($origin); if ($defaultRootObject !== null) $distributionConfig->appendChild($dom->createElement('DefaultRootObject', $defaultRootObject)); $distributionConfig->appendChild($dom->createElement('CallerReference', $callerReference)); foreach ($cnames as $cname) $distributionConfig->appendChild($dom->createElement('CNAME', $cname)); if ($comment !== '') $distributionConfig->appendChild($dom->createElement('Comment', $comment)); $distributionConfig->appendChild($dom->createElement('Enabled', $enabled ? 'true' : 'false')); $trusted = $dom->createElement('TrustedSigners'); foreach ($trustedSigners as $id => $type) $trusted->appendChild($id !== '' ? $dom->createElement($type, $id) : $dom->createElement($type)); $distributionConfig->appendChild($trusted); $dom->appendChild($distributionConfig); //var_dump($dom->saveXML()); return $dom->saveXML(); } /** * Parse a CloudFront distribution config * * See http://docs.amazonwebservices.com/AmazonCloudFront/latest/APIReference/index.html?GetDistribution.html * * @internal Used to parse the CloudFront DistributionConfig node to an array * @param object &$node DOMNode * @return array */ protected function __parseCloudFrontDistributionConfig(&$node) { if (isset($node->DistributionConfig)) return $this->__parseCloudFrontDistributionConfig($node->DistributionConfig); $dist = array(); if (isset($node->Id, $node->Status, $node->LastModifiedTime, $node->DomainName)) { $dist['id'] = (string)$node->Id; $dist['status'] = (string)$node->Status; $dist['time'] = strtotime((string)$node->LastModifiedTime); $dist['domain'] = (string)$node->DomainName; } if (isset($node->CallerReference)) $dist['callerReference'] = (string)$node->CallerReference; if (isset($node->Enabled)) $dist['enabled'] = (string)$node->Enabled == 'true' ? true : false; if (isset($node->S3Origin)) { if (isset($node->S3Origin->DNSName)) $dist['origin'] = (string)$node->S3Origin->DNSName; $dist['originAccessIdentity'] = isset($node->S3Origin->OriginAccessIdentity) ? (string)$node->S3Origin->OriginAccessIdentity : null; } $dist['defaultRootObject'] = isset($node->DefaultRootObject) ? (string)$node->DefaultRootObject : null; $dist['cnames'] = array(); if (isset($node->CNAME)) foreach ($node->CNAME as $cname) $dist['cnames'][(string)$cname] = (string)$cname; $dist['trustedSigners'] = array(); if (isset($node->TrustedSigners)) foreach ($node->TrustedSigners as $signer) { if (isset($signer->Self)) $dist['trustedSigners'][''] = 'Self'; elseif (isset($signer->KeyPairId)) $dist['trustedSigners'][(string)$signer->KeyPairId] = 'KeyPairId'; elseif (isset($signer->AwsAccountNumber)) $dist['trustedSigners'][(string)$signer->AwsAccountNumber] = 'AwsAccountNumber'; } $dist['comment'] = isset($node->Comment) ? (string)$node->Comment : null; return $dist; } /** * Grab CloudFront response * * @internal Used to parse the CloudFront S3Request::getResponse() output * @param object &$rest S3Request instance * @return object */ protected function __getCloudFrontResponse(&$rest) { $rest->getResponse($this); if ($rest->response->error === false && isset($rest->response->body) && is_string($rest->response->body) && substr($rest->response->body, 0, 5) == 'response->body = simplexml_load_string($rest->response->body); // Grab CloudFront errors if (isset($rest->response->body->Error, $rest->response->body->Error->Code, $rest->response->body->Error->Message)) { $rest->response->error = array( 'code' => (string)$rest->response->body->Error->Code, 'message' => (string)$rest->response->body->Error->Message ); unset($rest->response->body); } } return $rest->response; } /** * Get MIME type for file * * @internal Used to get mime types * @param string &$file File path * @return string */ public function __getMimeType(&$file) { $type = false; // Fileinfo documentation says fileinfo_open() will use the // MAGIC env var for the magic file if (extension_loaded('fileinfo') && isset($_ENV['MAGIC']) && ($finfo = finfo_open(FILEINFO_MIME, $_ENV['MAGIC'])) !== false) { if (($type = finfo_file($finfo, $file)) !== false) { // Remove the charset and grab the last content-type $type = explode(' ', str_replace('; charset=', ';charset=', $type)); $type = array_pop($type); $type = explode(';', $type); $type = trim(array_shift($type)); } finfo_close($finfo); // If anyone is still using mime_content_type() } elseif (function_exists('mime_content_type')) $type = trim(mime_content_type($file)); if ($type !== false && strlen($type) > 0) return $type; // Otherwise do it the old fashioned way $exts = array( 'jpg' => 'image/jpeg', 'gif' => 'image/gif', 'png' => 'image/png', 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'ico' => 'image/x-icon', 'swf' => 'application/x-shockwave-flash', 'pdf' => 'application/pdf', 'zip' => 'application/zip', 'gz' => 'application/x-gzip', 'tar' => 'application/x-tar', 'bz' => 'application/x-bzip', 'bz2' => 'application/x-bzip2', 'txt' => 'text/plain', 'asc' => 'text/plain', 'htm' => 'text/html', 'html' => 'text/html', 'css' => 'text/css', 'js' => 'text/javascript', 'xml' => 'text/xml', 'xsl' => 'application/xsl+xml', 'ogg' => 'application/ogg', 'mp3' => 'audio/mpeg', 'wav' => 'audio/x-wav', 'avi' => 'video/x-msvideo', 'mpg' => 'video/mpeg', 'mpeg' => 'video/mpeg', 'mov' => 'video/quicktime', 'flv' => 'video/x-flv', 'php' => 'text/x-php' ); $ext = strtolower(pathInfo($file, PATHINFO_EXTENSION)); return isset($exts[$ext]) ? $exts[$ext] : 'application/octet-stream'; } /** * Generate the auth string: "AWS AccessKey:Signature" * * @internal Used by S3Request::getResponse() * @param string $string String to sign * @return string */ public function __getSignature($string) { return 'AWS '.$this->__accessKey.':'.$this->__getHash($string); } /** * Creates a HMAC-SHA1 hash * * This uses the hash extension if loaded * * @internal Used by __getSignature() * @param string $string String to sign * @return string */ public function __getHash($string) { return base64_encode(extension_loaded('hash') ? hash_hmac('sha1', $string, $this->__secretKey, true) : pack('H*', sha1( (str_pad($this->__secretKey, 64, chr(0x00)) ^ (str_repeat(chr(0x5c), 64))) . pack('H*', sha1((str_pad($this->__secretKey, 64, chr(0x00)) ^ (str_repeat(chr(0x36), 64))) . $string))))); } } abstract class S3Request { protected $endpoint, $verb, $bucket, $uri, $resource = '', $parameters = array(), $amzHeaders = array(), $headers = array( 'Host' => '', 'Date' => '', 'Content-MD5' => '', 'Content-Type' => '' ); /** * Constructor * * @param string $verb Verb * @param string $bucket Bucket name * @param string $uri Object URI * @return mixed */ function __construct($verb, $bucket = '', $uri = '', $endpoint = 's3.amazonaws.com') { $this->endpoint = $endpoint; $this->verb = $verb; $this->bucket = $bucket; $this->uri = $uri !== '' ? '/'.str_replace('%2F', '/', rawurlencode($uri)) : '/'; if ($this->bucket !== '') { $this->headers['Host'] = $this->bucket.'.'.$this->endpoint; $this->resource = '/'.$this->bucket.$this->uri; } else { $this->headers['Host'] = $this->endpoint; $this->resource = $this->uri; } $this->headers['Date'] = gmdate('D, d M Y H:i:s T'); $this->response = new stdclass; $this->response->error = false; } /** * Set request parameter * * @param string $key Key * @param string $value Value * @return void */ public function setParameter($key, $value) { $this->parameters[$key] = $value; } /** * Set request header * * @param string $key Key * @param string $value Value * @return void */ public function setHeader($key, $value) { $this->headers[$key] = $value; } /** * Set x-amz-meta-* header * * @param string $key Key * @param string $value Value * @return void */ public function setAmzHeader($key, $value) { $this->amzHeaders[$key] = $value; } /** * Get the S3 response * * @return object | false */ function getResponse(S3 $s3) { $query = ''; if (sizeof($this->parameters) > 0) { $query = substr($this->uri, -1) !== '?' ? '?' : '&'; foreach ($this->parameters as $var => $value) if ($value == null || $value == '') $query .= $var.'&'; // Parameters should be encoded (thanks Sean O'Dea) else $query .= $var.'='.rawurlencode($value).'&'; $query = substr($query, 0, -1); $this->uri .= $query; if (array_key_exists('acl', $this->parameters) || array_key_exists('location', $this->parameters) || array_key_exists('torrent', $this->parameters) || array_key_exists('logging', $this->parameters)) $this->resource .= $query; } $url = ($s3->useSSL ? 'https://' : 'http://') . $this->headers['Host'].$this->uri; //var_dump($this->bucket, $this->uri, $this->resource, $url); // Headers $headers = array(); $amz = array(); foreach ($this->amzHeaders as $header => $value) if (strlen($value) > 0) $headers[] = $header.': '.$value; foreach ($this->headers as $header => $value) if (strlen($value) > 0) $headers[] = $header.': '.$value; // Collect AMZ headers for signature foreach ($this->amzHeaders as $header => $value) if (strlen($value) > 0) $amz[] = strtolower($header).':'.$value; // AMZ headers must be sorted if (sizeof($amz) > 0) { sort($amz); $amz = "\n".implode("\n", $amz); } else $amz = ''; $response = $this->_getResponse($s3, $url, $headers, $amz); // Parse body into XML if ($this->response->error === false && isset($this->response->headers['type']) && $this->response->headers['type'] == 'application/xml' && isset($this->response->body)) { $this->response->body = simplexml_load_string($this->response->body); // Grab S3 errors if (!in_array($this->response->code, array(200, 204, 206)) && isset($this->response->body->Code, $this->response->body->Message)) { $this->response->error = array( 'code' => (string)$this->response->body->Code, 'message' => (string)$this->response->body->Message ); if (isset($this->response->body->Resource)) $this->response->error['resource'] = (string)$this->response->body->Resource; unset($this->response->body); } } return $response; } abstract protected function _getResponse(S3 $s3, $url, $headers, $amz); } class S3Request_Curl extends S3Request { public $fp = false, $size = 0, $data = false, $response; /** * Get the S3 response * * @return object | false */ public function _getResponse(S3 $s3, $url, $headers, $amz) { // Basic setup $curl = curl_init(); curl_setopt($curl, CURLOPT_USERAGENT, 'S3/php'); if ($s3->useSSL) { // SSL Validation can now be optional for those with broken OpenSSL installations curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $s3->useSSLValidation ? 1 : 0); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $s3->useSSLValidation ? 1 : 0); if ($s3->sslKey !== null) curl_setopt($curl, CURLOPT_SSLKEY, $s3->sslKey); if ($s3->sslCert !== null) curl_setopt($curl, CURLOPT_SSLCERT, $s3->sslCert); if ($s3->sslCACert !== null) curl_setopt($curl, CURLOPT_CAINFO, $s3->sslCACert); } curl_setopt($curl, CURLOPT_URL, $url); if ($s3->proxy != null && isset($s3->proxy['host'])) { curl_setopt($curl, CURLOPT_PROXY, $s3->proxy['host']); curl_setopt($curl, CURLOPT_PROXYTYPE, $s3->proxy['type']); if (isset($s3->proxy['user'], $s3->proxy['pass']) && $proxy['user'] != null && $proxy['pass'] != null) curl_setopt($curl, CURLOPT_PROXYUSERPWD, sprintf('%s:%s', $s3->proxy['user'], $s3->proxy['pass'])); } if ($s3->hasAuth()) { // Authorization string (CloudFront stringToSign should only contain a date) $headers[] = 'Authorization: ' . $s3->__getSignature( $this->headers['Host'] == 'cloudfront.amazonaws.com' ? $this->headers['Date'] : $this->verb."\n".$this->headers['Content-MD5']."\n". $this->headers['Content-Type']."\n".$this->headers['Date'].$amz."\n".$this->resource ); } curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); curl_setopt($curl, CURLOPT_HEADER, false); curl_setopt($curl, CURLOPT_RETURNTRANSFER, false); curl_setopt($curl, CURLOPT_WRITEFUNCTION, array(&$this, '__responseWriteCallback')); curl_setopt($curl, CURLOPT_HEADERFUNCTION, array(&$this, '__responseHeaderCallback')); curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); // Request types switch ($this->verb) { case 'GET': break; case 'PUT': case 'POST': // POST only used for CloudFront if ($this->fp !== false) { curl_setopt($curl, CURLOPT_PUT, true); curl_setopt($curl, CURLOPT_INFILE, $this->fp); if ($this->size >= 0) curl_setopt($curl, CURLOPT_INFILESIZE, $this->size); } elseif ($this->data !== false) { curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->verb); curl_setopt($curl, CURLOPT_POSTFIELDS, $this->data); } else curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->verb); break; case 'HEAD': curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'HEAD'); curl_setopt($curl, CURLOPT_NOBODY, true); break; case 'DELETE': curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE'); break; default: break; } // Execute, grab errors if (curl_exec($curl)) $this->response->code = curl_getinfo($curl, CURLINFO_HTTP_CODE); else $this->response->error = array( 'code' => curl_errno($curl), 'message' => curl_error($curl), 'resource' => $this->resource ); @curl_close($curl); return $this->response; } public function __destruct() { // Clean up file resources if ($this->fp !== false && is_resource($this->fp)) fclose($this->fp); } /** * CURL write callback * * @param resource &$curl CURL resource * @param string &$data Data * @return integer */ protected function __responseWriteCallback(&$curl, &$data) { if (in_array($this->response->code, array(200, 206)) && $this->fp !== false) return fwrite($this->fp, $data); else $this->response->body .= $data; return strlen($data); } /** * CURL header callback * * @param resource &$curl CURL resource * @param string &$data Data * @return integer */ protected function __responseHeaderCallback(&$curl, &$data) { if (($strlen = strlen($data)) <= 2) return $strlen; if (substr($data, 0, 4) == 'HTTP') $this->response->code = (int)substr($data, 9, 3); else { $data = trim($data); if (strpos($data, ': ') === false) return $strlen; list($header, $value) = explode(': ', $data, 2); if ($header == 'Last-Modified') $this->response->headers['time'] = strtotime($value); elseif ($header == 'Content-Length') $this->response->headers['size'] = (int)$value; elseif ($header == 'Content-Type') $this->response->headers['type'] = $value; elseif ($header == 'ETag') $this->response->headers['hash'] = $value{0} == '"' ? substr($value, 1, -1) : $value; elseif (preg_match('/^x-amz-meta-.*$/', $header)) $this->response->headers[$header] = is_numeric($value) ? (int)$value : $value; } return $strlen; } } class S3Request_HttpRequest2 extends S3Request { public function _getResponse(S3 $s3, $url, $headers, $amz) { $req = new HTTP_Request2($url, $this->verb); if (!$s3->useSSLValidation) { $req->setConfig('ssl_verify_host', false); $req->setConfig('ssl_verify_peer', false); } if ($s3->proxy != null && isset($s3->proxy['host'])) { $req->setConfig('proxy_host'); $req->setConfig('proxy_host', $s3->proxy['host']); if (isset($s3->proxy['user'], $s3->proxy['pass']) && $proxy['user'] != null && $proxy['pass'] != null) { $req->setConfig('proxy_user', $s3->proxy['user']); $req->setConfig('proxy_password', $s3->proxy['pass']); } } if ($s3->hasAuth()) { // Authorization string (CloudFront stringToSign should only contain a date) $headers[] = 'Authorization: ' . $s3->__getSignature( $this->headers['Host'] == 'cloudfront.amazonaws.com' ? $this->headers['Date'] : $this->verb."\n".$this->headers['Content-MD5']."\n". $this->headers['Content-Type']."\n".$this->headers['Date'].$amz."\n".$this->resource ); } $req->setHeader($headers); $req->setConfig('follow_redirects', true); // Request types switch ($this->verb) { case 'GET': break; case 'PUT': case 'POST': // POST only used for CloudFront if ($this->fp !== false) { $req->setBody($this->fp); } elseif ($this->data !== false) { $req->setBody($this->data); } break; } try { $res = $req->send(); $this->response->code = $res->getStatus(); $this->response->body = $res->getBody(); $this->response->headers = $this->modHeaders($res->getHeader()); } catch (HTTP_Request2_Exception $e) { $this->response->error = array( 'code' => $e->getCode(), 'message' => $e->getMessage(), 'resource' => $this->resource ); } return $this->response; } function modHeaders($headers) { $ret = array(); foreach ($headers as $header => $value) { switch (strtolower($header)) { case 'last-modified': $ret['time'] = strtotime($value); break; case 'content-length': $ret['size'] = (int)$value; break; case 'content-type': $ret['type'] = $value; break; case 'etag': $ret['hash'] = $value{0} == '"' ? substr($value, 1, -1) : $value; break; default: if (preg_match('/^x-amz-meta-.*$/', $header)) $ret[$header] = is_numeric($value) ? (int)$value : $value; } } return $ret; } public function getAuthenticatedURL(S3 $s3, $bucket, $uri, $lifetime, $hostBucket = false, $https = false, $force_download = true) { $expires = $lifetime + time(); $filename = array_pop($_ = explode('/', $uri)); $params = array(); if ($force_download) { $params['response-content-disposition'] = "attachment;filename=$filename"; } $params['AWSAccessKeyId'] = $s3->getAccessKey(); $params['Expires'] = sprintf('%u', $expires); $params['Signature'] = $s3->__getHash("GET\n\n\n{$expires}\n/{$bucket}/{$uri}" . ($force_download ? "?response-content-disposition={$params['response-content-disposition']}" : '')); return sprintf(($https ? 'https' : 'http').'://%s/%s?%s', $hostBucket ? $bucket : $bucket.'.s3.amazonaws.com', $uri, http_build_query($params)); } } class S3Request_HttpRequest4 extends S3Request { const DEFAULT_PAYLOAD = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'; public function _getResponse(S3 $s3, $url, $headers, $amz) { $req = new HTTP_Request2($url, $this->verb); if (!$s3->useSSLValidation) { $req->setConfig('ssl_verify_host', false); $req->setConfig('ssl_verify_peer', false); } if ($s3->proxy != null && isset($s3->proxy['host'])) { $req->setConfig('proxy_host'); $req->setConfig('proxy_host', $s3->proxy['host']); if (isset($s3->proxy['user'], $s3->proxy['pass']) && $proxy['user'] != null && $proxy['pass'] != null) { $req->setConfig('proxy_user', $s3->proxy['user']); $req->setConfig('proxy_password', $s3->proxy['pass']); } } if ($s3->hasAuth()) { $headers = http_parse_headers(implode("\n", $headers).$amz."\n"); $headers = $this->signRequest($s3, $headers); $req->setHeader($headers); } $req->setConfig('follow_redirects', true); // Request types switch ($this->verb) { case 'GET': break; case 'PUT': case 'POST': // POST only used for CloudFront if ($this->fp !== false) { $req->setBody($this->fp); } elseif ($this->data !== false) { $req->setBody($this->data); } break; } try { $res = $req->send(); $this->response->code = $res->getStatus(); $this->response->body = $res->getBody(); $this->response->headers = $this->modHeaders($res->getHeader()); } catch (HTTP_Request2_Exception $e) { $this->response->error = array( 'code' => $e->getCode(), 'message' => $e->getMessage(), 'resource' => $this->resource ); } return $this->response; } function modHeaders($headers) { $ret = array(); foreach ($headers as $header => $value) { switch (strtolower($header)) { case 'last-modified': $ret['time'] = strtotime($value); break; case 'content-length': $ret['size'] = (int)$value; break; case 'content-type': $ret['type'] = $value; break; case 'etag': $ret['hash'] = $value{0} == '"' ? substr($value, 1, -1) : $value; break; default: if (preg_match('/^x-amz-meta-.*$/', $header)) $ret[$header] = is_numeric($value) ? (int)$value : $value; } } return $ret; } function signRequest(S3 $s3, $headers) { unset($headers['Date']); $secretKey = $s3->getSecretKey(); $timestamp = time(); $longDate = gmdate('Ymd\THis\Z', $timestamp); $headers['x-amz-date'] = $longDate; $shortDate = substr($longDate, 0, 8); $region = $s3->getServiceRegion(); $service = 's3'; $credentialScope = $this->createScope($shortDate, $region, $service); $payload = $this->getPayload($headers); $signingContext = $this->createSigningContext($s3, $headers, $payload); $signingContext['string_to_sign'] = $this->createStringToSign( $longDate, $credentialScope, $signingContext['canonical_request'] ); $signingKey = $this->getSigningKey($shortDate, $region, $service, $secretKey); $signature = hash_hmac('sha256', $signingContext['string_to_sign'], $signingKey); $headers['Authorization'] = "AWS4-HMAC-SHA256 " . "Credential={$s3->getAccessKey()}/{$credentialScope}, " . "SignedHeaders={$signingContext['signed_headers']}, Signature={$signature}"; $headers['x-amz-content-sha256'] = $this->getPayload($headers); return $headers; } private function getSigningKey($shortDate, $region, $service, $secretKey) { // Retrieve the hash form the cache or create it and add it to the cache $dateKey = hash_hmac('sha256', $shortDate, 'AWS4' . $secretKey, true); $regionKey = hash_hmac('sha256', $region, $dateKey, true); $serviceKey = hash_hmac('sha256', $service, $regionKey, true); $signingKey = hash_hmac('sha256', 'aws4_request', $serviceKey, true); return $signingKey; } private function createStringToSign($longDate, $credentialScope, $creq) { return "AWS4-HMAC-SHA256\n{$longDate}\n{$credentialScope}\n" . hash('sha256', $creq); } function getPayload(&$headers) { return isset($headers['x-amz-content-sha256']) ? $headers['x-amz-content-sha256'] : self::DEFAULT_PAYLOAD; } private function createScope($shortDate, $region, $service) { return $shortDate . '/' . $region . '/' . $service . '/aws4_request'; } private function createSigningContext(S3 $s3, $headers, $payload) { $signable = array( 'host' => true, 'date' => true, 'content-md5' => true ); // Normalize the path as required by SigV4 and ensure it's absolute $canon = $this->verb . "\n" . $this->createCanonicalizedPath($this->uri) . "\n" . $this->getCanonicalizedQueryString($this->uri) . "\n"; $canonHeaders = array(); foreach ($headers as $key => $values) { $key = strtolower($key); if (isset($signable[$key]) || substr($key, 0, 6) === 'x-amz-') { $canonHeaders[$key] = $key . ':' . preg_replace('/\s+/', ' ', $values); } } ksort($canonHeaders); $signedHeadersString = implode(';', array_keys($canonHeaders)); $canon .= implode("\n", $canonHeaders) . "\n\n" . $signedHeadersString . "\n" . $payload; return array( 'canonical_request' => $canon, 'signed_headers' => $signedHeadersString ); } protected function createCanonicalizedPath($uri) { $url = rawurldecode(parse_url($uri, PHP_URL_PATH)); $doubleEncoded = rawurlencode(ltrim($url, '/')); return '/' . str_replace('%2F', '/', $doubleEncoded); } private function getCanonicalizedQueryString($uri) { $query = parse_url($uri, PHP_URL_QUERY); parse_str($query, $queryParams); unset($queryParams['X-Amz-Signature']); if (empty($queryParams)) { return ''; } $qs = ''; ksort($queryParams); foreach ($queryParams as $key => $values) { if (is_array($values)) { sort($values); } elseif ($values === 0) { $values = array('0'); } elseif (!$values) { $values = array(''); } foreach ((array) $values as $value) { $qs .= rawurlencode($key) . '=' . rawurlencode($value) . '&'; } } return substr($qs, 0, -1); } function transformHeaders($headers, $amz) { $ret = array(); foreach($headers as $v){ list($h, $hv) = explode(":", $v); $h = trim($h); $hv = trim($hv); $ret[$h] = $hv; } foreach(explode("\n", $amz) as $v){ if(!$v) continue; list($h, $hv) = explode(":", $v); $h = trim($h); $hv = trim($hv); $ret[$h] = $hv; } return $ret; } function getAuthenticatedURL(S3 $s3, $bucket, $uri, $lifetime, $hostBucket = false, $https = false, $force_download = true) { unset($this->headers['Date']); unset($this->headers['Content-MD5']); $secretKey = $s3->getSecretKey(); $timestamp = time(); $longDate = gmdate('Ymd\THis\Z', $timestamp); $shortDate = substr($longDate, 0, 8); $region = $s3->getServiceRegion(); $service = 's3'; $credentialScope = $this->createScope($shortDate, $region, $service); $query = parse_url($uri, PHP_URL_QUERY); $queryParams = array(); $uri = parse_url($uri, PHP_URL_PATH); $queryParams['X-Amz-Algorithm'] = 'AWS4-HMAC-SHA256'; $queryParams['X-Amz-Credential'] = "{$s3->getAccessKey()}/{$credentialScope}"; $queryParams['X-Amz-Date'] = $longDate; $queryParams['X-Amz-Expires'] = $lifetime; $queryParams['X-Amz-SignedHeaders'] = 'host'; if ($force_download) { $filename = array_pop($_ = explode('/', $uri)); $queryParams['response-content-disposition'] = "attachment;filename=$filename"; } $payload = 'UNSIGNED-PAYLOAD'; $this->uri = $uri."?".http_build_query($queryParams); $signingContext = $this->createSigningContext($s3, $this->headers, $payload); $signingContext['string_to_sign'] = $this->createStringToSign( $longDate, $credentialScope, $signingContext['canonical_request'] ); $signingKey = $this->getSigningKey($shortDate, $region, $service, $secretKey); $signature = hash_hmac('sha256', $signingContext['string_to_sign'], $signingKey); $this->uri .= "&X-Amz-Signature={$signature}"; return sprintf(($https ? 'https' : 'http').'://%s/%s', $hostBucket ? $bucket : $bucket.".".$s3->endpoint, $this->uri); } } class S3Exception extends Exception { function __construct($message, $file, $line, $code = 0) { parent::__construct($message, $code); $this->file = $file; $this->line = $line; } } if (!function_exists('http_parse_headers')) { function http_parse_headers($raw_headers) { $headers = array(); $key = ''; // [+] foreach(explode("\n", $raw_headers) as $i => $h) { $h = explode(':', $h, 2); if (isset($h[1])) { if (!isset($headers[$h[0]])) $headers[$h[0]] = trim($h[1]); elseif (is_array($headers[$h[0]])) { // $tmp = array_merge($headers[$h[0]], array(trim($h[1]))); // [-] // $headers[$h[0]] = $tmp; // [-] $headers[$h[0]] = array_merge($headers[$h[0]], array(trim($h[1]))); // [+] } else { // $tmp = array_merge(array($headers[$h[0]]), array(trim($h[1]))); // [-] // $headers[$h[0]] = $tmp; // [-] $headers[$h[0]] = array_merge(array($headers[$h[0]]), array(trim($h[1]))); // [+] } $key = $h[0]; // [+] } else // [+] { // [+] if (substr($h[0], 0, 1) == "\t") // [+] $headers[$key] .= "\r\n\t".trim($h[0]); // [+] elseif (!$key) // [+] $headers[0] = trim($h[0]);trim($h[0]); // [+] } // [+] } return $headers; } }class-ftp-pure.php000064400000012443152101611340010120 0ustar00 // // function _settimeout($sock) { if(!@stream_set_timeout($sock, $this->_timeout)) { $this->PushError('_settimeout','socket set send timeout'); $this->_quit(); return FALSE; } return TRUE; } function _connect($host, $port) { $this->SendMSG("Creating socket"); $sock = @fsockopen($host, $port, $errno, $errstr, $this->_timeout); if (!$sock) { $this->PushError('_connect','socket connect failed', $errstr." (".$errno.")"); return FALSE; } $this->_connected=true; return $sock; } function _readmsg($fnction="_readmsg"){ if(!$this->_connected) { $this->PushError($fnction, 'Connect first'); return FALSE; } $result=true; $this->_message=""; $this->_code=0; $go=true; do { $tmp=@fgets($this->_ftp_control_sock, 512); if($tmp===false) { $go=$result=false; $this->PushError($fnction,'Read failed'); } else { $this->_message.=$tmp; if(preg_match("/^([0-9]{3})(-(.*[".CRLF."]{1,2})+\\1)? [^".CRLF."]+[".CRLF."]{1,2}$/", $this->_message, $regs)) $go=false; } } while($go); if($this->LocalEcho) echo "GET < ".rtrim($this->_message, CRLF).CRLF; $this->_code=(int)$regs[1]; return $result; } function _exec($cmd, $fnction="_exec") { if(!$this->_ready) { $this->PushError($fnction,'Connect first'); return FALSE; } if($this->LocalEcho) echo "PUT > ",$cmd,CRLF; $status=@fputs($this->_ftp_control_sock, $cmd.CRLF); if($status===false) { $this->PushError($fnction,'socket write failed'); return FALSE; } $this->_lastaction=time(); if(!$this->_readmsg($fnction)) return FALSE; return TRUE; } function _data_prepare($mode=FTP_ASCII) { if(!$this->_settype($mode)) return FALSE; if($this->_passive) { if(!$this->_exec("PASV", "pasv")) { $this->_data_close(); return FALSE; } if(!$this->_checkCode()) { $this->_data_close(); return FALSE; } $ip_port = explode(",", ereg_replace("^.+ \\(?([0-9]{1,3},[0-9]{1,3},[0-9]{1,3},[0-9]{1,3},[0-9]+,[0-9]+)\\)?.*".CRLF."$", "\\1", $this->_message)); $this->_datahost=$ip_port[0].".".$ip_port[1].".".$ip_port[2].".".$ip_port[3]; $this->_dataport=(((int)$ip_port[4])<<8) + ((int)$ip_port[5]); $this->SendMSG("Connecting to ".$this->_datahost.":".$this->_dataport); $this->_ftp_data_sock=@fsockopen($this->_datahost, $this->_dataport, $errno, $errstr, $this->_timeout); if(!$this->_ftp_data_sock) { $this->PushError("_data_prepare","fsockopen fails", $errstr." (".$errno.")"); $this->_data_close(); return FALSE; } else $this->_ftp_data_sock; } else { $this->SendMSG("Only passive connections available!"); return FALSE; } return TRUE; } function _data_read($mode=FTP_ASCII, $fp=NULL) { if(is_resource($fp)) $out=0; else $out=""; if(!$this->_passive) { $this->SendMSG("Only passive connections available!"); return FALSE; } while (!feof($this->_ftp_data_sock)) { $block=fread($this->_ftp_data_sock, $this->_ftp_buff_size); if($mode!=FTP_BINARY) $block=preg_replace("/\r\n|\r|\n/", $this->_eol_code[$this->OS_local], $block); if(is_resource($fp)) $out+=fwrite($fp, $block, strlen($block)); else $out.=$block; } return $out; } function _data_write($mode=FTP_ASCII, $fp=NULL) { if(is_resource($fp)) $out=0; else $out=""; if(!$this->_passive) { $this->SendMSG("Only passive connections available!"); return FALSE; } if(is_resource($fp)) { while(!feof($fp)) { $block=fread($fp, $this->_ftp_buff_size); if(!$this->_data_write_block($mode, $block)) return false; } } elseif(!$this->_data_write_block($mode, $fp)) return false; return TRUE; } function _data_write_block($mode, $block) { if($mode!=FTP_BINARY) $block=preg_replace("/\r\n|\r|\n/", $this->_eol_code[$this->OS_remote], $block); do { if(($t=@fwrite($this->_ftp_data_sock, $block))===FALSE) { $this->PushError("_data_write","Can't write to socket"); return FALSE; } $block=substr($block, $t); } while(!empty($block)); return true; } function _data_close() { @fclose($this->_ftp_data_sock); $this->SendMSG("Disconnected data from remote host"); return TRUE; } function _quit($force=FALSE) { if($this->_connected or $force) { @fclose($this->_ftp_control_sock); $this->_connected=false; $this->SendMSG("Socket closed"); } } } ?> class-ftp-sockets.php000064400000020422152101611340010614 0ustar00 // // function _settimeout($sock) { if(!@socket_set_option($sock, SOL_SOCKET, SO_RCVTIMEO, array("sec"=>$this->_timeout, "usec"=>0))) { $this->PushError('_connect','socket set receive timeout',socket_strerror(socket_last_error($sock))); @socket_close($sock); return FALSE; } if(!@socket_set_option($sock, SOL_SOCKET , SO_SNDTIMEO, array("sec"=>$this->_timeout, "usec"=>0))) { $this->PushError('_connect','socket set send timeout',socket_strerror(socket_last_error($sock))); @socket_close($sock); return FALSE; } return true; } function _connect($host, $port) { $this->SendMSG("Creating socket"); if(!($sock = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP))) { $this->PushError('_connect','socket create failed',socket_strerror(socket_last_error($sock))); return FALSE; } if(!$this->_settimeout($sock)) return FALSE; $this->SendMSG("Connecting to \"".$host.":".$port."\""); if (!($res = @socket_connect($sock, $host, $port))) { $this->PushError('_connect','socket connect failed',socket_strerror(socket_last_error($sock))); @socket_close($sock); return FALSE; } $this->_connected=true; return $sock; } function _readmsg($fnction="_readmsg"){ if(!$this->_connected) { $this->PushError($fnction,'Connect first'); return FALSE; } $result=true; $this->_message=""; $this->_code=0; $go=true; do { $tmp=@socket_read($this->_ftp_control_sock, 4096, PHP_BINARY_READ); if($tmp===false) { $go=$result=false; $this->PushError($fnction,'Read failed', socket_strerror(socket_last_error($this->_ftp_control_sock))); } else { $this->_message.=$tmp; $go = !preg_match("/^([0-9]{3})(-.+\\1)? [^".CRLF."]+".CRLF."$/Us", $this->_message, $regs); } } while($go); if($this->LocalEcho) echo "GET < ".rtrim($this->_message, CRLF).CRLF; $this->_code=(int)$regs[1]; return $result; } function _exec($cmd, $fnction="_exec") { if(!$this->_ready) { $this->PushError($fnction,'Connect first'); return FALSE; } if($this->LocalEcho) echo "PUT > ",$cmd,CRLF; $status=@socket_write($this->_ftp_control_sock, $cmd.CRLF); if($status===false) { $this->PushError($fnction,'socket write failed', socket_strerror(socket_last_error($this->stream))); return FALSE; } $this->_lastaction=time(); if(!$this->_readmsg($fnction)) return FALSE; return TRUE; } function _data_prepare($mode=FTP_ASCII) { if(!$this->_settype($mode)) return FALSE; $this->SendMSG("Creating data socket"); $this->_ftp_data_sock = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP); if ($this->_ftp_data_sock < 0) { $this->PushError('_data_prepare','socket create failed',socket_strerror(socket_last_error($this->_ftp_data_sock))); return FALSE; } if(!$this->_settimeout($this->_ftp_data_sock)) { $this->_data_close(); return FALSE; } if($this->_passive) { if(!$this->_exec("PASV", "pasv")) { $this->_data_close(); return FALSE; } if(!$this->_checkCode()) { $this->_data_close(); return FALSE; } $ip_port = explode(",", ereg_replace("^.+ \\(?([0-9]{1,3},[0-9]{1,3},[0-9]{1,3},[0-9]{1,3},[0-9]+,[0-9]+)\\)?.*".CRLF."$", "\\1", $this->_message)); $this->_datahost=$ip_port[0].".".$ip_port[1].".".$ip_port[2].".".$ip_port[3]; $this->_dataport=(((int)$ip_port[4])<<8) + ((int)$ip_port[5]); $this->SendMSG("Connecting to ".$this->_datahost.":".$this->_dataport); if(!@socket_connect($this->_ftp_data_sock, $this->_datahost, $this->_dataport)) { $this->PushError("_data_prepare","socket_connect", socket_strerror(socket_last_error($this->_ftp_data_sock))); $this->_data_close(); return FALSE; } else $this->_ftp_temp_sock=$this->_ftp_data_sock; } else { if(!@socket_getsockname($this->_ftp_control_sock, $addr, $port)) { $this->PushError("_data_prepare","can't get control socket information", socket_strerror(socket_last_error($this->_ftp_control_sock))); $this->_data_close(); return FALSE; } if(!@socket_bind($this->_ftp_data_sock,$addr)){ $this->PushError("_data_prepare","can't bind data socket", socket_strerror(socket_last_error($this->_ftp_data_sock))); $this->_data_close(); return FALSE; } if(!@socket_listen($this->_ftp_data_sock)) { $this->PushError("_data_prepare","can't listen data socket", socket_strerror(socket_last_error($this->_ftp_data_sock))); $this->_data_close(); return FALSE; } if(!@socket_getsockname($this->_ftp_data_sock, $this->_datahost, $this->_dataport)) { $this->PushError("_data_prepare","can't get data socket information", socket_strerror(socket_last_error($this->_ftp_data_sock))); $this->_data_close(); return FALSE; } if(!$this->_exec('PORT '.str_replace('.',',',$this->_datahost.'.'.($this->_dataport>>8).'.'.($this->_dataport&0x00FF)), "_port")) { $this->_data_close(); return FALSE; } if(!$this->_checkCode()) { $this->_data_close(); return FALSE; } } return TRUE; } function _data_read($mode=FTP_ASCII, $fp=NULL) { $NewLine=$this->_eol_code[$this->OS_local]; if(is_resource($fp)) $out=0; else $out=""; if(!$this->_passive) { $this->SendMSG("Connecting to ".$this->_datahost.":".$this->_dataport); $this->_ftp_temp_sock=socket_accept($this->_ftp_data_sock); if($this->_ftp_temp_sock===FALSE) { $this->PushError("_data_read","socket_accept", socket_strerror(socket_last_error($this->_ftp_temp_sock))); $this->_data_close(); return FALSE; } } while(($block=@socket_read($this->_ftp_temp_sock, $this->_ftp_buff_size, PHP_BINARY_READ))!==false) { if($block==="") break; if($mode!=FTP_BINARY) $block=preg_replace("/\r\n|\r|\n/", $this->_eol_code[$this->OS_local], $block); if(is_resource($fp)) $out+=fwrite($fp, $block, strlen($block)); else $out.=$block; } return $out; } function _data_write($mode=FTP_ASCII, $fp=NULL) { $NewLine=$this->_eol_code[$this->OS_local]; if(is_resource($fp)) $out=0; else $out=""; if(!$this->_passive) { $this->SendMSG("Connecting to ".$this->_datahost.":".$this->_dataport); $this->_ftp_temp_sock=socket_accept($this->_ftp_data_sock); if($this->_ftp_temp_sock===FALSE) { $this->PushError("_data_write","socket_accept", socket_strerror(socket_last_error($this->_ftp_temp_sock))); $this->_data_close(); return false; } } if(is_resource($fp)) { while(!feof($fp)) { $block=fread($fp, $this->_ftp_buff_size); if(!$this->_data_write_block($mode, $block)) return false; } } elseif(!$this->_data_write_block($mode, $fp)) return false; return true; } function _data_write_block($mode, $block) { if($mode!=FTP_BINARY) $block=preg_replace("/\r\n|\r|\n/", $this->_eol_code[$this->OS_remote], $block); do { if(($t=@socket_write($this->_ftp_temp_sock, $block))===FALSE) { $this->PushError("_data_write","socket_write", socket_strerror(socket_last_error($this->_ftp_temp_sock))); $this->_data_close(); return FALSE; } $block=substr($block, $t); } while(!empty($block)); return true; } function _data_close() { @socket_close($this->_ftp_temp_sock); @socket_close($this->_ftp_data_sock); $this->SendMSG("Disconnected data from remote host"); return TRUE; } function _quit() { if($this->_connected) { @socket_close($this->_ftp_control_sock); $this->_connected=false; $this->SendMSG("Socket closed"); } } } ?> DbSimple/Generic.php000064400000135420152101611340010327 0ustar00. * * Contains 3 classes: * - DbSimple_Generic: database factory class * - DbSimple_Generic_Database: common database methods * - DbSimple_Generic_Blob: common BLOB support * - DbSimple_Generic_LastError: error reporting and tracking * * Special result-set fields: * - ARRAY_KEY* ("*" means "anything") * - PARENT_KEY * * Transforms: * - GET_ATTRIBUTES * - CALC_TOTAL * - GET_TOTAL * - UNIQ_KEY * * Query attributes: * - BLOB_OBJ * - CACHE * * @author Dmitry Koterov, http://forum.dklab.ru/users/DmitryKoterov/ * @author Konstantin Zhinko, http://forum.dklab.ru/users/KonstantinGinkoTit/ * @author Ivan Borzenkov, http://forum.dklab.ru/users/Ivan1986/ * * @version 2.x $Id: Generic.php 226 2007-09-17 21:00:15Z dk $ */ @define('DBSIMPLE_SKIP', log(0)); /** * Имена специализированных колонок в резальтате, * которые используются как ключи в результирующем массиве */ @define('DBSIMPLE_ARRAY_KEY', 'ARRAY_KEY'); // hash-based resultset support @define('DBSIMPLE_PARENT_KEY', 'PARENT_KEY'); // forrest-based resultset support interface DbSimple_Interface { /** * object blob($blob_id) * Create new blob */ public function blob($blob_id = null); /** * void transaction($mode) * Create new transaction. */ public function transaction($mode=null); /** * mixed commit() * Commit the transaction. */ public function commit(); /** * mixed rollback() * Rollback the transaction. */ public function rollback(); /** * mixed select(string $query [, $arg1] [,$arg2] ...) * Execute query and return the result. */ public function select($query) ; /** * mixed selectPage(int &$total, string $query [, $arg1] [,$arg2] ...) * Execute query and return the result. * Total number of found rows (independent to LIMIT) is returned in $total * (in most cases second query is performed to calculate $total). */ public function selectPage(&$total, $query); /** * hash selectRow(string $query [, $arg1] [,$arg2] ...) * Return the first row of query result. * On errors return false and set last error. * If no one row found, return array()! It is useful while debugging, * because PHP DOES NOT generates notice on $row['abc'] if $row === null * or $row === false (but, if $row is empty array, notice is generated). */ public function selectRow(); /** * array selectCol(string $query [, $arg1] [,$arg2] ...) * Return the first column of query result as array. */ public function selectCol(); /** * scalar selectCell(string $query [, $arg1] [,$arg2] ...) * Return the first cell of the first column of query result. * If no one row selected, return null. */ public function selectCell(); /** * mixed query(string $query [, $arg1] [,$arg2] ...) * Alias for select(). May be used for INSERT or UPDATE queries. */ public function query(); /** * string escape(mixed $s, bool $isIdent=false) * Enclose the string into database quotes correctly escaping * special characters. If $isIdent is true, value quoted as identifier * (e.g.: `value` in MySQL, "value" in Firebird, [value] in MSSQL). */ public function escape($s, $isIdent=false); /** * DbSimple_SubQuery subquery(string $query [, $arg1] [,$arg2] ...) * Выполняет разворачивание плейсхолдеров без коннекта к базе * Нужно для сложных запросов, состоящих из кусков, которые полезно сохранить * */ public function subquery(); /** * callback setLogger(callback $logger) * Set query logger called before each query is executed. * Returns previous logger. */ public function setLogger($logger); /** * callback setCacher(callback $cacher) * Set cache mechanism called during each query if specified. * Returns previous handler. */ public function setCacher(Zend_Cache_Backend_Interface $cacher=null); /** * string setIdentPrefix($prx) * Set identifier prefix used for $_ placeholder. */ public function setIdentPrefix($prx); /** * string setCachePrefix($prx) * Set cache prefix used in key caclulation. */ public function setCachePrefix($prx); } /** * Base class for all databases. * Can create transactions and new BLOBs, parse DSNs. * * Logger is COMMON for multiple transactions. * Error handler is private for each transaction and database. */ abstract class DbSimple_Generic_Database extends DbSimple_Generic_LastError implements DbSimple_Interface { /** * Public methods. */ /** * object blob($blob_id) * Create new blob */ public function blob($blob_id = null) { $this->_resetLastError(); return $this->_performNewBlob($blob_id); } /** * void transaction($mode) * Create new transaction. */ public function transaction($mode=null) { $this->_resetLastError(); $this->_logQuery('-- START TRANSACTION '.$mode); return $this->_performTransaction($mode); } /** * mixed commit() * Commit the transaction. */ public function commit() { $this->_resetLastError(); $this->_logQuery('-- COMMIT'); return $this->_performCommit(); } /** * mixed rollback() * Rollback the transaction. */ public function rollback() { $this->_resetLastError(); $this->_logQuery('-- ROLLBACK'); return $this->_performRollback(); } /** * mixed select(string $query [, $arg1] [,$arg2] ...) * Execute query and return the result. */ public function select($query) { $args = func_get_args(); $total = false; return $this->_query($args, $total); } /** * mixed selectPage(int &$total, string $query [, $arg1] [,$arg2] ...) * Execute query and return the result. * Total number of found rows (independent to LIMIT) is returned in $total * (in most cases second query is performed to calculate $total). */ public function selectPage(&$total, $query) { $args = func_get_args(); array_shift($args); $total = true; return $this->_query($args, $total); } /** * hash selectRow(string $query [, $arg1] [,$arg2] ...) * Return the first row of query result. * On errors return false and set last error. * If no one row found, return array()! It is useful while debugging, * because PHP DOES NOT generates notice on $row['abc'] if $row === null * or $row === false (but, if $row is empty array, notice is generated). */ public function selectRow() { $args = func_get_args(); $total = false; $rows = $this->_query($args, $total); if (!is_array($rows)) return $rows; if (!count($rows)) return array(); reset($rows); return current($rows); } /** * array selectCol(string $query [, $arg1] [,$arg2] ...) * Return the first column of query result as array. */ public function selectCol() { $args = func_get_args(); $total = false; $rows = $this->_query($args, $total); if (!is_array($rows)) return $rows; $this->_shrinkLastArrayDimensionCallback($rows); return $rows; } /** * scalar selectCell(string $query [, $arg1] [,$arg2] ...) * Return the first cell of the first column of query result. * If no one row selected, return null. */ public function selectCell() { $args = func_get_args(); $total = false; $rows = $this->_query($args, $total); if (!is_array($rows)) return $rows; if (!count($rows)) return null; reset($rows); $row = current($rows); if (!is_array($row)) return $row; reset($row); return current($row); } /** * mixed query(string $query [, $arg1] [,$arg2] ...) * Alias for select(). May be used for INSERT or UPDATE queries. */ public function query() { $args = func_get_args(); $total = false; return $this->_query($args, $total); } /** * string escape(mixed $s, bool $isIdent=false) * Enclose the string into database quotes correctly escaping * special characters. If $isIdent is true, value quoted as identifier * (e.g.: `value` in MySQL, "value" in Firebird, [value] in MSSQL). */ public function escape($s, $isIdent=false) { return $this->_performEscape($s, $isIdent); } /** * DbSimple_SubQuery subquery(string $query [, $arg1] [,$arg2] ...) * Выполняет разворачивание плейсхолдеров без коннекта к базе * Нужно для сложных запросов, состоящих из кусков, которые полезно сохранить * */ public function subquery() { $args = func_get_args(); $this->_expandPlaceholders($args,$this->_placeholderNativeArgs !== null); return new DbSimple_SubQuery($args); } /** * callback setLogger(callback $logger) * Set query logger called before each query is executed. * Returns previous logger. */ public function setLogger($logger) { $prev = $this->_logger; $this->_logger = $logger; return $prev; } /** * callback setCacher(callback $cacher) * Set cache mechanism called during each query if specified. * Returns previous handler. */ public function setCacher(Zend_Cache_Backend_Interface $cacher=null) { $prev = $this->_cacher; $this->_cacher = $cacher; return $prev; } /** * string setIdentPrefix($prx) * Set identifier prefix used for $_ placeholder. */ public function setIdentPrefix($prx) { $old = $this->_identPrefix; if ($prx !== null) $this->_identPrefix = $prx; return $old; } /** * string setCachePrefix($prx) * Set cache prefix used in key caclulation. */ public function setCachePrefix($prx) { $old = $this->_cachePrefix; if ($prx !== null) $this->_cachePrefix = $prx; return $old; } /** * array getStatistics() * Returns various statistical information. */ public function getStatistics() { return $this->_statistics; } /** * string _performEscape(mixed $s, bool $isIdent=false) */ abstract protected function _performEscape($s, $isIdent=false); /** * object _performNewBlob($id) * * Returns new blob object. */ abstract protected function _performNewBlob($id=null); /** * list _performGetBlobFieldNames($resultResource) * Get list of all BLOB field names in result-set. */ abstract protected function _performGetBlobFieldNames($result); /** * mixed _performTransformQuery(array &$query, string $how) * * Transform query different way specified by $how. * May return some information about performed transform. */ abstract protected function _performTransformQuery(&$queryMain, $how); /** * resource _performQuery($arrayQuery) * Must return: * - For SELECT queries: ID of result-set (PHP resource). * - For other queries: query status (scalar). * - For error queries: false (and call _setLastError()). */ abstract protected function _performQuery($arrayQuery); /** * mixed _performFetch($resultResource) * Fetch ONE NEXT row from result-set. * Must return: * - For SELECT queries: all the rows of the query (2d arrray). * - For INSERT queries: ID of inserted row. * - For UPDATE queries: number of updated rows. * - For other queries: query status (scalar). * - For error queries: false (and call _setLastError()). */ abstract protected function _performFetch($result); /** * mixed _performTransaction($mode) * Start new transaction. */ abstract protected function _performTransaction($mode=null); /** * mixed _performCommit() * Commit the transaction. */ abstract protected function _performCommit(); /** * mixed _performRollback() * Rollback the transaction. */ abstract protected function _performRollback(); /** * string _performGetPlaceholderIgnoreRe() * Return regular expression which matches ignored query parts. * This is needed to skip placeholder replacement inside comments, constants etc. */ protected function _performGetPlaceholderIgnoreRe() { return ''; } /** * Returns marker for native database placeholder. E.g. in FireBird it is '?', * in PostgreSQL - '$1', '$2' etc. * * @param int $n Number of native placeholder from the beginning of the query (begins from 0!). * @return string String representation of native placeholder marker (by default - '?'). */ protected function _performGetNativePlaceholderMarker($n) { return '?'; } /** * array _query($query, &$total) * See _performQuery(). */ private function _query($query, &$total) { $this->_resetLastError(); // Fetch query attributes. $this->attributes = $this->_transformQuery($query, 'GET_ATTRIBUTES'); // Modify query if needed for total counting. if ($total) $this->_transformQuery($query, 'CALC_TOTAL'); $rows = false; $cache_it = false; // Кешер у нас либо null либо соответствует Zend интерфейсу if (!empty($this->attributes['CACHE']) && $this->_cacher) { $hash = $this->_cachePrefix . md5(serialize($query)); // Getting data from cache if possible $fetchTime = $firstFetchTime = 0; $qStart = microtime(true); $cacheData = unserialize($this->_cacher->load($hash)); $queryTime = microtime(true) - $qStart; $invalCache = isset($cacheData['invalCache']) ? $cacheData['invalCache'] : null; $result = isset($cacheData['result']) ? $cacheData['result'] : null; $rows = isset($cacheData['rows']) ? $cacheData['rows'] : null; $cache_params = $this->attributes['CACHE']; // Calculating cache time to live $re = '/ (?> ([0-9]+) #1 - hours h)? [ \t]* (?> ([0-9]+) #2 - minutes m)? [ \t]* (?> ([0-9]+) #3 - seconds s?)? (,)? /sx'; $m = null; preg_match($re, $cache_params, $m); $ttl = (isset($m[3])?$m[3]:0) + (isset($m[2])?$m[2]:0) * 60 + (isset($m[1])?$m[1]:0) * 3600; // Cutting out time param - now there are just fields for uniqKey or nothing $cache_params = trim(preg_replace($re, '', $cache_params, 1)); $uniq_key = null; // UNIQ_KEY calculation if (!empty($cache_params)) { $dummy = null; // There is no need in query, cos' needle in $this->attributes['CACHE'] $this->_transformQuery($dummy, 'UNIQ_KEY'); $uniq_key = call_user_func_array(array(&$this, 'select'), $dummy); $uniq_key = md5(serialize($uniq_key)); } // Check TTL? $ok = empty($ttl) || $cacheData; // Invalidate cache? if ($ok && $uniq_key == $invalCache) { $this->_logQuery($query); $this->_logQueryStat($queryTime, $fetchTime, $firstFetchTime, $rows); } else $cache_it = true; } if (false === $rows || true === $cache_it) { $this->_logQuery($query); // Run the query (counting time). $qStart = microtime(true); $result = $this->_performQuery($query); $fetchTime = $firstFetchTime = 0; if (is_resource($result)) { $rows = array(); // Fetch result row by row. $fStart = microtime(true); $row = $this->_performFetch($result); $firstFetchTime = microtime(true) - $fStart; if ($row !== false) { $rows[] = $row; while ($row=$this->_performFetch($result)) { $rows[] = $row; } } $fetchTime = microtime(true) - $fStart; } else { $rows = $result; } $queryTime = microtime(true) - $qStart; // Log query statistics. $this->_logQueryStat($queryTime, $fetchTime, $firstFetchTime, $rows); // Prepare BLOB objects if needed. if (is_array($rows) && !empty($this->attributes['BLOB_OBJ'])) foreach ($this->_performGetBlobFieldNames($result) as $name) foreach ($rows as $r=>$v) $rows[$r][$name] = $this->_performNewBlob($v[$name]); // Transform resulting rows. $result = $this->_transformResult($rows); // Storing data in cache if ($cache_it && $this->_cacher) { $this->_cacher->save( serialize(array( 'invalCache' => $uniq_key, 'result' => $result, 'rows' => $rows )), $hash, array(), $ttl==0?false:$ttl ); } } // Count total number of rows if needed. if (is_array($result) && $total) { $this->_transformQuery($query, 'GET_TOTAL'); $total = call_user_func_array(array(&$this, 'selectCell'), $query); } return $result; } /** * mixed _transformQuery(array &$query, string $how) * * Transform query different way specified by $how. * May return some information about performed transform. */ private function _transformQuery(&$query, $how) { // Do overriden transformation. $result = $this->_performTransformQuery($query, $how); if ($result === true) return $result; // Common transformations. switch ($how) { case 'GET_ATTRIBUTES': // Extract query attributes. $options = array(); $q = $query[0]; $m = null; while (preg_match('/^ \s* -- [ \t]+ (\w+): ([^\r\n]+) [\r\n]* /sx', $q, $m)) { $options[$m[1]] = trim($m[2]); $q = substr($q, strlen($m[0])); } return $options; case 'UNIQ_KEY': $q = $this->attributes['CACHE']; $i = 0; $query = " -- UNIQ_KEY\n"; while(preg_match('/(\w+)\.\w+/sx', $q, $m)) { if($i > 0)$query .= "\nUNION\n"; $query .= 'SELECT MAX('.$m[0].') AS M, COUNT(*) AS C FROM '.$m[1]; $q = substr($q, strlen($m[0])); $i++; } return true; } // No such transform. $this->_setLastError(-1, "No such transform type: $how", $query); } /** * void _expandPlaceholders(array &$queryAndArgs, bool $useNative=false) * Replace placeholders by quoted values. * Modify $queryAndArgs. */ public function _expandPlaceholders(&$queryAndArgs, $useNative=false) { $cacheCode = null; if ($this->_logger) { // Serialize is much faster than placeholder expansion. So use caching. $cacheCode = md5(serialize($queryAndArgs) . '|' . $useNative . '|' . $this->_identPrefix); if (isset($this->_placeholderCache[$cacheCode])) { $queryAndArgs = $this->_placeholderCache[$cacheCode]; return; } } if (!is_array($queryAndArgs)) { $queryAndArgs = array($queryAndArgs); } $this->_placeholderNativeArgs = $useNative? array() : null; $this->_placeholderArgs = array_reverse($queryAndArgs); $query = array_pop($this->_placeholderArgs); // array_pop is faster than array_shift // Do all the work. $this->_placeholderNoValueFound = false; $query = $this->_expandPlaceholdersFlow($query); if ($useNative) { array_unshift($this->_placeholderNativeArgs, $query); $queryAndArgs = $this->_placeholderNativeArgs; } else { $queryAndArgs = array($query); } if ($cacheCode) { $this->_placeholderCache[$cacheCode] = $queryAndArgs; } } /** * Do real placeholder processing. * Imply that all interval variables (_placeholder_*) already prepared. * May be called recurrent! */ private function _expandPlaceholdersFlow($query) { $re = '{ (?> # Ignored chunks. (?> # Comment. -- [^\r\n]* ) | (?> # DB-specifics. ' . trim($this->_performGetPlaceholderIgnoreRe()) . ' ) ) | (?> # Optional blocks \{ # Use "+" here, not "*"! Else nested blocks are not processed well. ( (?> (?>(\??)[^{}]+) | (?R) )* ) #1 \} ) | (?> # Placeholder (\?) ( [_dsafn&|\#]? ) #2 #3 ) }sx'; $query = preg_replace_callback( $re, array(&$this, '_expandPlaceholdersCallback'), $query ); return $query; } static $join = array( '|' => array('inner' => ' AND ', 'outer' => ') OR (',), '&' => array('inner' => ' OR ', 'outer' => ') AND (',), 'a' => array('inner' => ', ', 'outer' => '), (',), ); /** * string _expandPlaceholdersCallback(list $m) * Internal function to replace placeholders (see preg_replace_callback). */ private function _expandPlaceholdersCallback($m) { // Placeholder. if (!empty($m[3])) { $type = $m[4]; // Idenifier prefix. if ($type == '_') { return $this->_identPrefix; } // Value-based placeholder. if (!$this->_placeholderArgs) return 'DBSIMPLE_ERROR_NO_VALUE'; $value = array_pop($this->_placeholderArgs); // Skip this value? if ($value === DBSIMPLE_SKIP) { $this->_placeholderNoValueFound = true; return ''; } // First process guaranteed non-native placeholders. switch ($type) { case 's': if (!($value instanceof DbSimple_SubQuery)) return 'DBSIMPLE_ERROR_VALUE_NOT_SUBQUERY'; return $value->get($this->_placeholderNativeArgs); case '|': case '&': case 'a': if (!$value) $this->_placeholderNoValueFound = true; if (!is_array($value)) return 'DBSIMPLE_ERROR_VALUE_NOT_ARRAY'; $parts = array(); $multi = array(); //массив для двойной вложенности $mult = $type!='a' || is_int(key($value)) && is_array(current($value)); foreach ($value as $prefix => $field) { //превращаем $value в двумерный нуменованный массив if (!is_array($field)) { $field = array($prefix => $field); $prefix = 0; } $prefix = is_int($prefix) ? '' : $this->escape($prefix, true) . '.'; if (substr($prefix, 0, 2) == '?_') $prefix = $this->_identPrefix . substr($prefix, 2); //для мультиинсерта очищаем ключи - их быть не может по синтаксису if ($mult && $type=='a') $field = array_values($field); foreach ($field as $k => $v) { if ($v instanceof DbSimple_SubQuery) $v = $v->get($this->_placeholderNativeArgs); else $v = $v === null? 'NULL' : $this->escape($v); if (!is_int($k)) { $k = $this->escape($k, true); $parts[] = "$prefix$k=$v"; } else { $parts[] = $v; } } if ($mult) { $multi[] = join(self::$join[$type]['inner'], $parts); $parts = array(); } } return $mult ? join(self::$join[$type]['outer'], $multi) : join(', ', $parts); case "#": // Identifier. if (!is_array($value)) { if (substr($value, 0, 2) == '?_') $value = $this->_identPrefix . substr($value, 2); return $this->escape($value, true); } $parts = array(); foreach ($value as $table => $identifiers) { if (!is_array($identifiers)) $identifiers = array($identifiers); $prefix = ''; if (!is_int($table)) { if (substr($table, 0, 2) == '?_') $table = $this->_identPrefix . substr($table, 2); $prefix = $this->escape($table, true) . '.'; } foreach ($identifiers as $identifier) if ($identifier instanceof DbSimple_SubQuery) $parts[] = $identifier->get($this->_placeholderNativeArgs); else { if (!is_string($identifier)) return 'DBSIMPLE_ERROR_ARRAY_VALUE_NOT_STRING'; $parts[] = $prefix . ($identifier!='*'?$this->escape($identifier, true):'*'); } } return join(', ', $parts); case 'n': // NULL-based placeholder. return empty($value)? 'NULL' : intval($value); } // In non-native mode arguments are quoted. if ($value === null) return 'NULL'; switch ($type) { case 'd': return intval($value); case 'f': return str_replace(',', '.', floatval($value)); } // нативные плейсхолдеры необходимо обработать после числовых из-за бага в PDO // Native arguments are not processed. if ($this->_placeholderNativeArgs !== null) { $this->_placeholderNativeArgs[] = $value; return $this->_performGetNativePlaceholderMarker(count($this->_placeholderNativeArgs) - 1); } if (!is_scalar($value)) return 'DBSIMPLE_ERROR_VALUE_NOT_SCALAR'; // By default - escape as string. return $this->escape($value); } // Optional block. if (isset($m[1]) && strlen($block=$m[1])) { $prev = $this->_placeholderNoValueFound; if ($this->_placeholderNativeArgs !== null) $prevPh = $this->_placeholderNativeArgs; // Проверка на {? } - условный блок $skip = false; if ($m[2]=='?') { $skip = array_pop($this->_placeholderArgs) === DBSIMPLE_SKIP; $block[0] = ' '; } $block = $this->_expandOptionalBlock($block); if ($skip) $block = ''; if ($this->_placeholderNativeArgs !== null) if ($this->_placeholderNoValueFound) $this->_placeholderNativeArgs = $prevPh; $this->_placeholderNoValueFound = $prev; // recurrent-safe return $block; } // Default: skipped part of the string. return $m[0]; } private function _expandOptionalBlock($block) { $alts = array(); $alt = ''; $sub=0; $exp = explode('|',$block); // Оптимизация, так как в большинстве случаев | не используется if (count($exp)==1) $alts=$exp; else foreach ($exp as $v) { // Реализуем автоматный магазин для нахождения нужной скобки // На суммарную парность скобок проверять нет необходимости - об этом заботится регулярка $sub+=substr_count($v,'{'); $sub-=substr_count($v,'}'); if ($sub>0) $alt.=$v.'|'; else { $alts[]=$alt.$v; $alt=''; } } $r=''; foreach ($alts as $block) { $this->_placeholderNoValueFound = false; $block = $this->_expandPlaceholdersFlow($block); // Необходимо пройти все блоки, так как если пропустить оставшиесь, // то это нарушит порядок подставляемых значений if ($this->_placeholderNoValueFound == false && $r=='') $r = ' '.$block.' '; } return $r; } /** * void _setLastError($code, $msg, $query) * Set last database error context. * Aditionally expand placeholders. */ protected function _setLastError($code, $msg, $query) { if (is_array($query)) { $this->_expandPlaceholders($query, false); $query = $query[0]; } return parent::_setLastError($code, $msg, $query); } /** * Convert SQL field-list to COUNT(...) clause * (e.g. 'DISTINCT a AS aa, b AS bb' -> 'COUNT(DISTINCT a, b)'). */ private function _fieldList2Count($fields) { $m = null; if (preg_match('/^\s* DISTINCT \s* (.*)/sx', $fields, $m)) { $fields = $m[1]; $fields = preg_replace('/\s+ AS \s+ .*? (?=,|$)/sx', '', $fields); return "COUNT(DISTINCT $fields)"; } else { return 'COUNT(*)'; } } /** * array _transformResult(list $rows) * Transform resulting rows to various formats. */ private function _transformResult($rows) { // Результат не массив - выходим if (!is_array($rows) || !$rows) return $rows; // Find ARRAY_KEY* AND PARENT_KEY fields in field list. $pk = null; $ak = array(); foreach (array_keys(current($rows)) as $fieldName) if (0 == strncasecmp($fieldName, DBSIMPLE_ARRAY_KEY, strlen(DBSIMPLE_ARRAY_KEY))) $ak[] = $fieldName; elseif (0 == strncasecmp($fieldName, DBSIMPLE_PARENT_KEY, strlen(DBSIMPLE_PARENT_KEY))) $pk = $fieldName; if (!$ak) return $rows; natsort($ak); // sort ARRAY_KEY* using natural comparision // Tree-based array? Fields: ARRAY_KEY, PARENT_KEY if ($pk !== null) return $this->_transformResultToForest($rows, $ak[0], $pk); // Key-based array? Fields: ARRAY_KEY. return $this->_transformResultToHash($rows, $ak); } /** * Converts rowset to key-based array. * * @param array $rows Two-dimensional array of resulting rows. * @param array $ak List of ARRAY_KEY* field names. * @return array Transformed array. */ private function _transformResultToHash(array $rows, array $arrayKeys) { $result = array(); foreach ($rows as $row) { // Iterate over all of ARRAY_KEY* fields and build array dimensions. $current =& $result; foreach ($arrayKeys as $ak) { $key = $row[$ak]; unset($row[$ak]); // remove ARRAY_KEY* field from result row if ($key !== null) { $current =& $current[$key]; } else { // IF ARRAY_KEY field === null, use array auto-indices. $tmp = array(); $current[] =& $tmp; $current =& $tmp; unset($tmp); // we use $tmp, because don't know the value of auto-index } } $current = $row; // save the row in last dimension } return $result; } /** * Converts rowset to the forest. * * @param array $rows Two-dimensional array of resulting rows. * @param string $idName Name of ID field. * @param string $pidName Name of PARENT_ID field. * @return array Transformed array (tree). */ private function _transformResultToForest(array $rows, $idName, $pidName) { $children = array(); // children of each ID $ids = array(); // Collect who are children of whom. foreach ($rows as $i=>$r) { $row =& $rows[$i]; $id = $row[$idName]; if ($id === null) { // Rows without an ID are totally invalid and makes the result tree to // be empty (because PARENT_ID = null means "a root of the tree"). So // skip them totally. continue; } $pid = $row[$pidName]; if ($id == $pid) $pid = null; $children[$pid][$id] =& $row; if (!isset($children[$id])) $children[$id] = array(); $row['childNodes'] =& $children[$id]; $ids[$id] = true; } // Root elements are elements with non-found PIDs. $forest = array(); foreach ($rows as $i=>$r) { $row =& $rows[$i]; $id = $row[$idName]; $pid = $row[$pidName]; if ($pid == $id) $pid = null; if (!isset($ids[$pid])) { $forest[$row[$idName]] =& $row; } unset($row[$idName]); unset($row[$pidName]); } return $forest; } /** * Replaces the last array in a multi-dimensional array $V by its first value. * Used for selectCol(), when we need to transform (N+1)d resulting array * to Nd array (column). */ private function _shrinkLastArrayDimensionCallback(&$v) { if (!$v) return; reset($v); if (!is_array($firstCell = current($v))) { $v = $firstCell; } else { array_walk($v, array(&$this, '_shrinkLastArrayDimensionCallback')); } } /** * void _logQuery($query, $noTrace=false) * Must be called on each query. * If $noTrace is true, library caller is not solved (speed improvement). */ protected function _logQuery($query, $noTrace=false) { if (!$this->_logger) return; $this->_expandPlaceholders($query, false); $args = array(); $args[] =& $this; $args[] = $query[0]; $args[] = $noTrace? null : $this->findLibraryCaller(); return call_user_func_array($this->_logger, $args); } /** * void _logQueryStat($queryTime, $fetchTime, $firstFetchTime, $rows) * Log information about performed query statistics. */ protected function _logQueryStat($queryTime, $fetchTime, $firstFetchTime, $rows) { // Always increment counters. $this->_statistics['time'] += $queryTime; $this->_statistics['count']++; // If no logger, economize CPU resources and actually log nothing. if (!$this->_logger) return; $dt = round($queryTime * 1000); $firstFetchTime = round($firstFetchTime*1000); $tailFetchTime = round($fetchTime * 1000) - $firstFetchTime; $log = " -- "; if ($firstFetchTime + $tailFetchTime) { $log = sprintf(" -- %d ms = %d+%d".($tailFetchTime? "+%d" : ""), $dt, $dt-$firstFetchTime-$tailFetchTime, $firstFetchTime, $tailFetchTime); } else { $log = sprintf(" -- %d ms", $dt); } $log .= "; returned "; if (!is_array($rows)) { $log .= $this->escape($rows); } else { $detailed = null; if (count($rows) == 1) { $len = 0; $values = array(); foreach ($rows[0] as $k=>$v) { $len += strlen($v); if ($len > $this->MAX_LOG_ROW_LEN) { break; } $values[] = $v === null? 'NULL' : $this->escape($v); } if ($len <= $this->MAX_LOG_ROW_LEN) { $detailed = "(" . preg_replace("/\r?\n/", "\\n", join(', ', $values)) . ")"; } } if ($detailed) { $log .= $detailed; } else { $log .= count($rows). " row(s)"; } } $this->_logQuery($log, true); } /** * mixed _cache($hash, $result=null) * Calls cache mechanism if possible. */ private function _cache($hash, $result=null) { if (is_callable($this->_cacher)) { return call_user_func($this->_cacher, $hash, $result); } else if (is_object($this->_cacher) && method_exists($this->_cacher, 'get') && method_exists($this->_cacher, 'save')) { if (null === $result) return $this->_cacher->get($hash); else $this->_cacher->save($result, $hash); } else return false; } // Identifiers prefix (used for ?_ placeholder). protected $_identPrefix = ''; /* changed to protected by alex@cgi-central.net */ // Queries statistics. private $_statistics = array( 'time' => 0, 'count' => 0, ); private $_cachePrefix = ''; private $_logger = null; private $_cacher = null; private $_placeholderArgs, $_placeholderNativeArgs, $_placeholderCache=array(); private $_placeholderNoValueFound; /** * When string representation of row (in characters) is greater than this, * row data will not be logged. */ private $MAX_LOG_ROW_LEN = 128; } /** * Интерфейс BLOB. * Описывает функции обекта типа BLOB - ему должны соответствовать классы, возвращаемые в качестве BLOB полей. */ interface DbSimple_Generic_Blob { /** * string read(int $length) * Returns following $length bytes from the blob. */ public function read($len); /** * string write($data) * Appends data to blob. */ public function write($data); /** * int length() * Returns length of the blob. */ public function length(); /** * blobid close() * Closes the blob. Return its ID. No other way to obtain this ID! */ public function close(); } /** * Класс для хранения подзапроса - результата выполнения функции * DbSimple_Generic_Database::subquery * */ class DbSimple_SubQuery { private $query=array(); public function __construct(array $q) { $this->query = $q; } /** * Возвращает сам запрос и добавляет плейсхолдеры в массив переданный по ссылке * * @param &array|null - ссылка на массив плейсхолдеров * @return string */ public function get(&$ph) { if ($ph !== null) $ph = array_merge($ph, array_slice($this->query,1,null,true)); return $this->query[0]; } } /** * Support for error tracking. * Can hold error messages, error queries and build proper stacktraces. */ abstract class DbSimple_Generic_LastError { public $error = null; public $errmsg = null; private $showStack = false; private $errorHandler = null; private $ignoresInTraceRe = 'DbSimple_.*::.* | call_user_func.*'; /** * abstract void _logQuery($query) * Must be overriden in derived class. */ protected abstract function _logQuery($query); /** * void _resetLastError() * Reset the last error. Must be called on correct queries. */ protected function _resetLastError() { $this->error = $this->errmsg = null; } /** * void _setLastError(int $code, string $message, string $query) * Fill $this->error property with error information. Error context * (code initiated the query outside DbSimple) is assigned automatically. */ protected function _setLastError($code, $msg, $query) { $context = "unknown"; $this->error = array( 'code' => $code, 'message' => rtrim($msg), 'query' => $query, 'context' => '', ); if ($t = $this->findLibraryCaller($this->showStack)) { if (!$this->showStack) $this->error['context'] = (isset($t['file'])? $t['file'] : '?') . ' line ' . (isset($t['line'])? $t['line'] : '?'); else { $this->error['context'] = (isset($t[0]['file'])? $t[0]['file'] : '?') . ' line ' . (isset($t[0]['line'])? $t[0]['line'] : '?'); $this->error['stack'] = array(); foreach($t as $f) $this->error['stack'][] = (isset($f['class']) ? $f['class'].$f['type'] : ''). (isset($f['function']) ? $f['function'] : '?').'() '. (isset($f['file'])? $f['file'] : '?') . ':' . (isset($f['line'])? $f['line'] : '?'); } } $this->errmsg = rtrim($msg) . ($this->error['context']? ' at '.$this->error['context'] : ''); $this->_logQuery(" -- error #".$code.": ".preg_replace('/(\r?\n)+/s', ' ', $this->errmsg)); if (is_callable($this->errorHandler)) { call_user_func($this->errorHandler, $this->errmsg, $this->error); } return false; } /** * callback setErrorHandler(callback $handler, bool $stack) * Set new error handler called on database errors. * Handler gets 3 arguments: * - error message * - full error context information (last query etc.) */ public function setErrorHandler($handler, $stack=false) { $prev = $this->errorHandler; $this->errorHandler = $handler; $this->showStack = $stack; // In case of setting first error handler for already existed // error - call the handler now (usual after connect()). if (!$prev && $this->error) call_user_func($this->errorHandler, $this->errmsg, $this->error); return $prev; } /** * void addIgnoreInTrace($reName) * Add regular expression matching ClassName::functionName or functionName. * Matched stack frames will be ignored in stack traces passed to query logger. */ public function addIgnoreInTrace($name) { $this->ignoresInTraceRe .= "|" . $name; } /** * array of array findLibraryCaller() * Return part of stacktrace before calling first library method. * Used in debug purposes (query logging etc.). */ public function findLibraryCaller($all = false) { $caller = call_user_func( array(&$this, 'debug_backtrace_smart'), $this->ignoresInTraceRe, !$all ); return $caller; } /** * array debug_backtrace_smart($ignoresRe=null, $returnCaller=false) * * Return stacktrace. Correctly work with call_user_func* * (totally skip them correcting caller references). * If $returnCaller is true, return only first matched caller, * not all stacktrace. * * @version 2.03 */ private function debug_backtrace_smart($ignoresRe=null, $returnCaller=false) { $trace = debug_backtrace(); if ($ignoresRe !== null) $ignoresRe = "/^(?>{$ignoresRe})$/six"; $smart = array(); $framesSeen = 0; for ($i=0, $n=count($trace); $i<$n; $i++) { $t = $trace[$i]; if (!$t) continue; // Next frame. $next = isset($trace[$i+1])? $trace[$i+1] : null; // Dummy frame before call_user_func* frames. if (!isset($t['file'])) { $t['over_function'] = $trace[$i+1]['function']; $t = $t + $trace[$i+1]; $trace[$i+1] = null; // skip call_user_func on next iteration $next = isset($trace[$i+2])? $trace[$i+2] : null; // Correct Next frame. } // Skip myself frame. if (++$framesSeen < 2) continue; // 'class' and 'function' field of next frame define where // this frame function situated. Skip frames for functions // situated in ignored places. if ($ignoresRe && $next) { // Name of function "inside which" frame was generated. $frameCaller = (isset($next['class'])? $next['class'].'::' : '') . (isset($next['function'])? $next['function'] : ''); if (preg_match($ignoresRe, $frameCaller)) continue; } // On each iteration we consider ability to add PREVIOUS frame // to $smart stack. if ($returnCaller) return $t; $smart[] = $t; } return $smart; } } abstract class DbSimple_MySql extends DbSimple_Generic_Database {} class DbSimple_Mypdo extends DbSimple_MySql { public $PDO; public function __construct($dsn) { $base = preg_replace('{^/}s', '', $dsn['path']); if (!class_exists('PDO')) return $this->_setLastError("-1", "PDO extension is not loaded", "PDO"); try { if(strpos($dsn['host'], ':') !== false) list($host,$socket) = @explode(':',$dsn['host']); else{ $host= $dsn['host']; $socket = ''; } $this->PDO = new PDO($d = 'mysql:host='.$host.(empty($dsn['port'])?'':';port='.$dsn['port']).(empty($socket)?'':';unix_socket='.$socket).';dbname='.$base, $dsn['user'], isset($dsn['pass'])?$dsn['pass']:'', array( PDO::ATTR_ERRMODE => PDO::ERRMODE_SILENT, PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true, PDO::ATTR_PERSISTENT => isset($dsn['persist']) && $dsn['persist'], PDO::ATTR_TIMEOUT => isset($dsn['timeout']) && $dsn['timeout'] ? $dsn['timeout'] : 0, //to fix the next error on some setups "This command is not supported in the prepared statement protocol yet" //uncomment next line //PDO::ATTR_EMULATE_PREPARES => true, //did not work reliable PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES '.(isset($dsn['enc'])?$dsn['enc']:'UTF8'), )); } catch (PDOException $e) { $code = $e->getCode(); if (empty($code) && preg_match('/^SQLSTATE\[\d+\]\s+\[(\d+)\]/', $e->getMessage(), $regs)) { $code = $regs[1]; } $this->_setLastError($code , $e->getMessage(), 'new PDO'); } } /** @access private */ public function _isConnected() { return (bool)$this->PDO; } protected function _performGetPlaceholderIgnoreRe() { return ' " (?> [^"\\\\]+|\\\\"|\\\\)* " | \' (?> [^\'\\\\]+|\\\\\'|\\\\)* \' | ` (?> [^`]+ | ``)* ` | # backticks /\* .*? \*/ # comments '; } protected function _performEscape($s, $isIdent=false) { if (!$isIdent) { return $this->PDO->quote($s); } else { return "`" . str_replace('`', '``', $s) . "`"; } } protected function _performTransaction($parameters=null) { return $this->PDO->beginTransaction(); } protected function _performCommit() { return $this->PDO->commit(); } protected function _performRollback() { return $this->PDO->rollBack(); } protected function _performQuery($queryMain, $resultOnly = false) { $this->_lastQuery = $queryMain; $this->_expandPlaceholders($queryMain, false); if (!$this->PDO) throw new Exception("Trying to run query, but not connected to database"); $p = $this->PDO->query($queryMain[0]); if (!$p) return $this->_setDbError($p,$queryMain[0]); if ($p->errorCode()!=0) return $this->_setDbError($p,$queryMain[0]); if ($resultOnly) return $p; if (preg_match('/^\s* INSERT \s+/six', $queryMain[0])) return $this->PDO->lastInsertId(); if ($p->columnCount()==0) return $p->rowCount(); //Если у нас в запросе есть хотя-бы одна колонка - это по любому будет select $p->setFetchMode(PDO::FETCH_ASSOC); $res = $p->fetchAll(); $p->closeCursor(); return $res; } protected function _performTransformQuery(&$queryMain, $how) { // If we also need to calculate total number of found rows... switch ($how) { // Prepare total calculation (if possible) case 'CALC_TOTAL': $m = null; if (preg_match('/^(\s* SELECT)(.*)/six', $queryMain[0], $m)) $queryMain[0] = $m[1] . ' SQL_CALC_FOUND_ROWS' . $m[2]; return true; // Perform total calculation. case 'GET_TOTAL': // Built-in calculation available? $queryMain = array('SELECT FOUND_ROWS()'); return true; } return false; } protected function _setDbError($obj,$q) { $info=$obj?$obj->errorInfo():$this->PDO->errorInfo(); return $this->_setLastError($info[1], $info[2], $q); } protected function _performNewBlob($id=null) { } protected function _performGetBlobFieldNames($result) { return array(); } protected function _performFetch($result) { return $result; } /* added by alex@cgi-central.net */ /** @return PDOStatement */ public function queryResultOnly($sql) { $query = func_get_args(); $this->_logQuery($query); // Run the query (counting time). $qStart = microtime(true); $result = $this->_performQuery($query, true); $queryTime = microtime(true) - $qStart; // Log query statistics. $this->_logQueryStat($queryTime, null, null, null); return $result; } /* added by alex@cgi-central.net */ /** * Runs a query without any additional overal with no substitution * @return PDOStatement */ public function queryQuick($sql) { $p = $this->PDO->query($sql); if (!$p) return $this->_setDbError($p, $sql); if ($p->errorCode()!=0) return $this->_setDbError($p, $sql); return $p; } public function fetchRow($st) { return $st->fetch(PDO::FETCH_ASSOC); } public function fetchRows($st) { return $st->fetchAll(PDO::FETCH_ASSOC); } public function fetchArray($st) { return $st->fetch(PDO::FETCH_ASSOC); } public function freeResult($st) { $st->closeCursor(); } public function getPrefix() { return @$this->_identPrefix; } public function expandPlaceholders($queryAndArgs) { if (is_array($queryAndArgs)) { if (count($queryAndArgs)>1) $this->_expandPlaceholders($queryAndArgs); return $queryAndArgs[0]; } else { return (string)$queryAndArgs; } } function escapeWithPlaceholders($s, $_){ $queryAndArgs = func_get_args(); $this->_expandPlaceholders($queryAndArgs); return $queryAndArgs[0]; } public function quote($s) { return trim($this->PDO->quote($s), "'"); } public function __sleep() { return array(); } } class DbSimple_Mypdo_Blob implements DbSimple_Generic_Blob { // MySQL does not support separate BLOB fetching. private $blobdata = null; private $curSeek = 0; public function __construct(&$database, $blobdata=null) { $this->blobdata = $blobdata; $this->curSeek = 0; } public function read($len) { $p = $this->curSeek; $this->curSeek = min($this->curSeek + $len, strlen($this->blobdata)); return substr($this->blobdata, $this->curSeek, $len); } public function write($data) { $this->blobdata .= $data; } public function close() { return $this->blobdata; } public function length() { return strlen($this->blobdata); } } PasswordHash.php000064400000015256152101611340007666 0ustar00 in 2004-2006 and placed in # the public domain. Revised in subsequent years, still public domain. # # There's absolutely no warranty. # # The homepage URL for this framework is: # # http://www.openwall.com/phpass/ # # Please be sure to update the Version line if you edit this file in any way. # It is suggested that you leave the main version number intact, but indicate # your project name (after the slash) and add your own revision information. # # Please do not change the "private" password hashing method implemented in # here, thereby making your hashes incompatible. However, if you must, please # change the hash type identifier (the "$P$") to something different. # # Obviously, since this code is in the public domain, the above are not # requirements (there can be none), but merely suggestions. # class PasswordHash { var $itoa64; var $iteration_count_log2; var $portable_hashes; var $random_state; function __construct($iteration_count_log2, $portable_hashes) { $this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31) $iteration_count_log2 = 8; $this->iteration_count_log2 = $iteration_count_log2; $this->portable_hashes = $portable_hashes; $this->random_state = microtime(); if (function_exists('getmypid')) $this->random_state .= getmypid(); } function get_random_bytes($count) { $output = ''; if (@is_readable('/dev/urandom') && ($fh = @fopen('/dev/urandom', 'rb'))) { $output = fread($fh, $count); fclose($fh); } if (strlen($output) < $count) { $output = ''; for ($i = 0; $i < $count; $i += 16) { $this->random_state = md5(microtime() . $this->random_state); $output .= pack('H*', md5($this->random_state)); } $output = substr($output, 0, $count); } return $output; } function encode64($input, $count) { $output = ''; $i = 0; do { $value = ord($input[$i++]); $output .= $this->itoa64[$value & 0x3f]; if ($i < $count) $value |= ord($input[$i]) << 8; $output .= $this->itoa64[($value >> 6) & 0x3f]; if ($i++ >= $count) break; if ($i < $count) $value |= ord($input[$i]) << 16; $output .= $this->itoa64[($value >> 12) & 0x3f]; if ($i++ >= $count) break; $output .= $this->itoa64[($value >> 18) & 0x3f]; } while ($i < $count); return $output; } function gensalt_private($input) { $output = '$P$'; $output .= $this->itoa64[min($this->iteration_count_log2 + ((PHP_VERSION >= '5') ? 5 : 3), 30)]; $output .= $this->encode64($input, 6); return $output; } function crypt_private($password, $setting) { $output = '*0'; if (substr($setting, 0, 2) == $output) $output = '*1'; $id = substr($setting, 0, 3); # We use "$P$", phpBB3 uses "$H$" for the same thing if ($id != '$P$' && $id != '$H$') return $output; $count_log2 = strpos($this->itoa64, $setting[3]); if ($count_log2 < 7 || $count_log2 > 30) return $output; $count = 1 << $count_log2; $salt = substr($setting, 4, 8); if (strlen($salt) != 8) return $output; # We're kind of forced to use MD5 here since it's the only # cryptographic primitive available in all versions of PHP # currently in use. To implement our own low-level crypto # in PHP would result in much worse performance and # consequently in lower iteration counts and hashes that are # quicker to crack (by non-PHP code). if (PHP_VERSION >= '5') { $hash = md5($salt . $password, TRUE); do { $hash = md5($hash . $password, TRUE); } while (--$count); } else { $hash = pack('H*', md5($salt . $password)); do { $hash = pack('H*', md5($hash . $password)); } while (--$count); } $output = substr($setting, 0, 12); $output .= $this->encode64($hash, 16); return $output; } function gensalt_extended($input) { $count_log2 = min($this->iteration_count_log2 + 8, 24); # This should be odd to not reveal weak DES keys, and the # maximum valid value is (2**24 - 1) which is odd anyway. $count = (1 << $count_log2) - 1; $output = '_'; $output .= $this->itoa64[$count & 0x3f]; $output .= $this->itoa64[($count >> 6) & 0x3f]; $output .= $this->itoa64[($count >> 12) & 0x3f]; $output .= $this->itoa64[($count >> 18) & 0x3f]; $output .= $this->encode64($input, 3); return $output; } function gensalt_blowfish($input) { # This one needs to use a different order of characters and a # different encoding scheme from the one in encode64() above. # We care because the last character in our encoded string will # only represent 2 bits. While two known implementations of # bcrypt will happily accept and correct a salt string which # has the 4 unused bits set to non-zero, we do not want to take # chances and we also do not want to waste an additional byte # of entropy. $itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; $output = '$2a$'; $output .= chr(ord('0') + $this->iteration_count_log2 / 10); $output .= chr(ord('0') + $this->iteration_count_log2 % 10); $output .= '$'; $i = 0; do { $c1 = ord($input[$i++]); $output .= $itoa64[$c1 >> 2]; $c1 = ($c1 & 0x03) << 4; if ($i >= 16) { $output .= $itoa64[$c1]; break; } $c2 = ord($input[$i++]); $c1 |= $c2 >> 4; $output .= $itoa64[$c1]; $c1 = ($c2 & 0x0f) << 2; $c2 = ord($input[$i++]); $c1 |= $c2 >> 6; $output .= $itoa64[$c1]; $output .= $itoa64[$c2 & 0x3f]; } while (1); return $output; } function HashPassword($password) { $random = ''; if (CRYPT_BLOWFISH == 1 && !$this->portable_hashes) { $random = $this->get_random_bytes(16); $hash = crypt($password, $this->gensalt_blowfish($random)); if (strlen($hash) == 60) return $hash; } if (CRYPT_EXT_DES == 1 && !$this->portable_hashes) { if (strlen($random) < 3) $random = $this->get_random_bytes(3); $hash = crypt($password, $this->gensalt_extended($random)); if (strlen($hash) == 20) return $hash; } if (strlen($random) < 6) $random = $this->get_random_bytes(6); $hash = $this->crypt_private($password, $this->gensalt_private($random)); if (strlen($hash) == 34) return $hash; # Returning '*' on error is safe here, but would _not_ be safe # in a crypt(3)-like function used _both_ for generating new # hashes and for validating passwords against existing hashes. return '*'; } function CheckPassword($password, $stored_hash) { $hash = $this->crypt_private($password, $stored_hash); if ($hash[0] == '*') $hash = crypt($password, $stored_hash); return $hash == $stored_hash; } } ?> pear/HTML/Common2.php000064400000035134152101611340010262 0ustar00 * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The names of the authors may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * @category HTML * @package HTML_Common2 * @author Alexey Borzov * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version CVS: $Id: Common2.php 304516 2010-10-19 18:43:58Z avb $ * @link http://pear.php.net/package/HTML_Common2 */ /** * Base class for HTML classes * * Implements methods for working with HTML attributes, parsing and generating * attribute strings. Port of HTML_Common class for PHP4 originally written by * Adam Daniel with contributions from numerous other developers. * * @category HTML * @package HTML_Common2 * @author Alexey Borzov * @version Release: 2.0.0 */ abstract class HTML_Common2 { /** * Associative array of attributes * @var array */ protected $attributes = array(); /** * List of attribites changes to which will be announced via onAttributeChange() * method rather than performed by HTML_Common2 class itself * @var array * @see onAttributeChange() */ protected $watchedAttributes = array(); /** * Indentation level of the element * @var int */ private $_indentLevel = 0; /** * Comment associated with the element * @var string */ private $_comment = null; /** * Global options for all elements generated by subclasses of HTML_Common2 * * Preset options are * - 'charset': charset parameter used in htmlspecialchars() calls, * defaults to 'ISO-8859-1' * - 'indent': string used to indent HTML elements, defaults to "\11" * - 'linebreak': string used to indicate linebreak, defaults to "\12" * * @var array */ private static $_options = array( 'charset' => 'ISO-8859-1', 'indent' => "\11", 'linebreak' => "\12" ); /** * Sets global option(s) * * @param string|array Option name or array ('option name' => 'option value') * @param mixed Option value, if first argument is not an array */ public static function setOption($nameOrOptions, $value = null) { if (is_array($nameOrOptions)) { foreach ($nameOrOptions as $k => $v) { self::setOption($k, $v); } } else { $linebreaks = array('win' => "\15\12", 'unix' => "\12", 'mac' => "\15"); if ('linebreak' == $nameOrOptions && isset($linebreaks[$value])) { $value = $linebreaks[$value]; } self::$_options[$nameOrOptions] = $value; } } /** * Returns global option(s) * * @param string Option name * @return mixed Option value, null if option does not exist, * array of all options if $name is not given */ public static function getOption($name = null) { if (null === $name) { return self::$_options; } else { return isset(self::$_options[$name])? self::$_options[$name]: null; } } /** * Parses the HTML attributes given as string * * @param string HTML attribute string * @return array An associative aray of attributes */ protected static function parseAttributes($attrString) { $attributes = array(); if (preg_match_all( "/(([A-Za-z_:]|[^\\x00-\\x7F])([A-Za-z0-9_:.-]|[^\\x00-\\x7F])*)" . "([ \\n\\t\\r]+)?(=([ \\n\\t\\r]+)?(\"[^\"]*\"|'[^']*'|[^ \\n\\t\\r]*))?/", $attrString, $regs )) { for ($i = 0; $i < count($regs[1]); $i++) { $name = trim($regs[1][$i]); $check = trim($regs[0][$i]); $value = trim($regs[7][$i]); if ($name == $check) { $attributes[strtolower($name)] = strtolower($name); } else { if (!empty($value) && ($value[0] == '\'' || $value[0] == '"')) { $value = substr($value, 1, -1); } $attributes[strtolower($name)] = $value; } } } return $attributes; } /** * Creates a valid attribute array from either a string or an array * * @param mixed Array of attributes or HTML attribute string * @return array An associative aray of attributes */ protected static function prepareAttributes($attributes) { $prepared = array(); if (is_string($attributes)) { return self::parseAttributes($attributes); } elseif (is_array($attributes)) { foreach ($attributes as $key => $value) { if (is_int($key)) { $key = strtolower($value); $prepared[$key] = $key; } else { $prepared[strtolower($key)] = (string)$value; } } } return $prepared; } /** * Removes an attribute from an attribute array * * @param array Attribute array * @param string Name of attribute to remove */ protected static function removeAttributeArray(array &$attributes, $name) { unset($attributes[strtolower($name)]); } /** * Creates HTML attribute string from array * * @param array Attribute array * @return string Attribute string */ protected static function getAttributesString(array $attributes) { $str = ''; $charset = self::getOption('charset'); foreach ($attributes as $key => $value) { $str .= ' ' . $key . '="' . htmlspecialchars($value, ENT_QUOTES, $charset) . '"'; } return $str; } /** * Class constructor, sets default attributes * * @param mixed Array of attribute 'name' => 'value' pairs or HTML attribute string */ public function __construct($attributes = null) { $this->mergeAttributes($attributes); } /** * Sets the value of the attribute * * @param string Attribute name * @param string Attribute value (will be set to $name if omitted) * @return HTML_Common2 */ public function setAttribute($name, $value = null) { $name = strtolower($name); if (is_null($value)) { $value = $name; } if (in_array($name, $this->watchedAttributes)) { $this->onAttributeChange($name, $value); } else { $this->attributes[$name] = (string)$value; } return $this; } /** * Returns the value of an attribute * * @param string Attribute name * @return string Attribute value, null if attribute does not exist */ public function getAttribute($name) { $name = strtolower($name); return isset($this->attributes[$name])? $this->attributes[$name]: null; } /** * Sets the attributes * * @param mixed Array of attribute 'name' => 'value' pairs or HTML attribute string * @return HTML_Common2 */ public function setAttributes($attributes) { $attributes = self::prepareAttributes($attributes); $watched = array(); foreach ($this->watchedAttributes as $watchedKey) { if (isset($attributes[$watchedKey])) { $this->setAttribute($watchedKey, $attributes[$watchedKey]); unset($attributes[$watchedKey]); } else { $this->removeAttribute($watchedKey); } if (isset($this->attributes[$watchedKey])) { $watched[$watchedKey] = $this->attributes[$watchedKey]; } } $this->attributes = array_merge($watched, $attributes); return $this; } /** * Returns the attribute array or string * * @param bool Whether to return attributes as string * @return mixed Either an array or string of attributes */ public function getAttributes($asString = false) { if ($asString) { return self::getAttributesString($this->attributes); } else { return $this->attributes; } } /** * Merges the existing attributes with the new ones * * @param mixed Array of attribute 'name' => 'value' pairs or HTML attribute string * @return HTML_Common2 */ public function mergeAttributes($attributes) { $attributes = self::prepareAttributes($attributes); foreach ($this->watchedAttributes as $watchedKey) { if (isset($attributes[$watchedKey])) { $this->onAttributeChange($watchedKey, $attributes[$watchedKey]); unset($attributes[$watchedKey]); } } $this->attributes = array_merge($this->attributes, $attributes); return $this; } /** * Removes an attribute * * @param string Name of attribute to remove * @return HTML_Common2 */ public function removeAttribute($attribute) { if (in_array(strtolower($attribute), $this->watchedAttributes)) { $this->onAttributeChange(strtolower($attribute), null); } else { self::removeAttributeArray($this->attributes, $attribute); } return $this; } /** * Sets the indentation level * * @param int * @return HTML_Common2 */ public function setIndentLevel($level) { $level = intval($level); if (0 <= $level) { $this->_indentLevel = $level; } return $this; } /** * Gets the indentation level * * @return int */ public function getIndentLevel() { return $this->_indentLevel; } /** * Returns the string to indent the element * * @return string */ protected function getIndent() { return str_repeat(self::getOption('indent'), $this->getIndentLevel()); } /** * Sets the comment for the element * * @param string * @return HTML_Common2 */ public function setComment($comment) { $this->_comment = $comment; return $this; } /** * Returns the comment associated with the element * * @return string */ public function getComment() { return $this->_comment; } /** * Checks whether the element has given CSS class * * @param string Class name * @return bool */ public function hasClass($class) { $regex = '/(^|\s)' . preg_quote($class, '/') . '(\s|$)/'; return (bool)preg_match($regex, $this->getAttribute('class')); } /** * Adds the given CSS class(es) to the element * * @param string|array Class name, multiple class names separated by * whitespace, array of class names * @return HTML_Common2 */ public function addClass($class) { if (!is_array($class)) { $class = preg_split('/\s+/', $class, null, PREG_SPLIT_NO_EMPTY); } $curClass = preg_split('/\s+/', $this->getAttribute('class'), null, PREG_SPLIT_NO_EMPTY); foreach ($class as $c) { if (!in_array($c, $curClass)) { $curClass[] = $c; } } $this->setAttribute('class', implode(' ', $curClass)); return $this; } /** * Removes the given CSS class(es) from the element * * @param string|array Class name, multiple class names separated by * whitespace, array of class names * @return HTML_Common2 */ public function removeClass($class) { if (!is_array($class)) { $class = preg_split('/\s+/', $class, null, PREG_SPLIT_NO_EMPTY); } $curClass = array_diff( preg_split('/\s+/', $this->getAttribute('class'), null, PREG_SPLIT_NO_EMPTY), $class ); if (0 == count($curClass)) { $this->removeAttribute('class'); } else { $this->setAttribute('class', implode(' ', $curClass)); } return $this; } /** * Returns the HTML representation of the element * * This magic method allows using the instances of HTML_Common2 in string * contexts * * @return string */ abstract public function __toString(); /** * Called if trying to change an attribute with name in $watchedAttributes * * This method is called for each attribute whose name is in the * $watchedAttributes array and which is being changed by setAttribute(), * setAttributes() or mergeAttributes() or removed via removeAttribute(). * Note that the operation for the attribute is not carried on after calling * this method, it is the responsibility of this method to change or remove * (or not) the attribute. * * @param string Attribute name * @param string Attribute value, null if attribute is being removed */ protected function onAttributeChange($name, $value = null) { } } ?> pear/HTML/QuickForm2/DataSource/Array.php000064400000006747152101611340014052 0ustar00, * Bertrand Mansion * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The names of the authors may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * @category HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version SVN: $Id: Array.php 311435 2011-05-26 10:30:06Z avb $ * @link http://pear.php.net/package/HTML_QuickForm2 */ /** * Interface for data sources used by HTML_QuickForm2 objects */ require_once 'HTML/QuickForm2/DataSource.php'; /** * Array-based data source for HTML_QuickForm2 objects * * @category HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @version Release: @package_version@ */ class HTML_QuickForm2_DataSource_Array implements HTML_QuickForm2_DataSource { /** * Array containing elements' values * @var array */ protected $values; /** * Class constructor, initializes the values array * * @param array Array containing the elements' values */ public function __construct($values = array()) { $this->values = $values; } public function getValue($name) { if (empty($this->values)) { return null; } if (strpos($name, '[')) { $tokens = explode('[', str_replace(']', '', $name)); $value = $this->values; do { $token = array_shift($tokens); if (!is_array($value) || !isset($value[$token])) { return null; } $value = $value[$token]; } while (!empty($tokens)); return $value; } elseif (isset($this->values[$name])) { return $this->values[$name]; } else { return null; } } } ?> pear/HTML/QuickForm2/DataSource/Session.php000064400000006145152101611340014407 0ustar00, * Bertrand Mansion * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The names of the authors may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * @category HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version SVN: $Id: Session.php 311435 2011-05-26 10:30:06Z avb $ * @link http://pear.php.net/package/HTML_QuickForm2 */ /** Interface for data sources containing submitted values */ require_once 'HTML/QuickForm2/DataSource/Submit.php'; /** Array-based data source for HTML_QuickForm2 objects */ require_once 'HTML/QuickForm2/DataSource/Array.php'; /** * Class presenting the values stored in session by Controller as submitted ones * * This is a less hackish implementation of loadValues() method in old * HTML_QuickForm_Controller. The values need to be presented as submitted so * that elements like checkboxes and multiselects do not try to use default * values from subsequent datasources. * * @category HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @version Release: @package_version@ */ class HTML_QuickForm2_DataSource_Session extends HTML_QuickForm2_DataSource_Array implements HTML_QuickForm2_DataSource_Submit { /** * File upload data is not stored in the session */ public function getUpload($name) { return null; } } ?> pear/HTML/QuickForm2/DataSource/Submit.php000064400000005774152101611340014236 0ustar00, * Bertrand Mansion * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The names of the authors may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * @category HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version SVN: $Id: Submit.php 311435 2011-05-26 10:30:06Z avb $ * @link http://pear.php.net/package/HTML_QuickForm2 */ /** * Interface for data sources used by HTML_QuickForm2 objects */ require_once 'HTML/QuickForm2/DataSource.php'; /** * Interface for data sources containing submitted values * * This interface provides method for getting information on uploaded files. * Additionally some elements will only consider getting their values from data * sources implementing this interface. * * @category HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @version Release: @package_version@ */ interface HTML_QuickForm2_DataSource_Submit extends HTML_QuickForm2_DataSource { /** * Returns the information about uploaded file * * If data source doesn't such information it should return null * * @param string Name of file upload field * @return array|null Information on uploaded file, from $_FILES array */ public function getUpload($name); } ?> pear/HTML/QuickForm2/DataSource/SuperGlobal.php000064400000013701152101611340015177 0ustar00, * Bertrand Mansion * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The names of the authors may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * @category HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version SVN: $Id: SuperGlobal.php 311435 2011-05-26 10:30:06Z avb $ * @link http://pear.php.net/package/HTML_QuickForm2 */ /** * Interface for data sources containing submitted values */ require_once 'HTML/QuickForm2/DataSource/Submit.php'; /** * Array-based data source for HTML_QuickForm2 objects */ require_once 'HTML/QuickForm2/DataSource/Array.php'; /** * Data source for HTML_QuickForm2 objects based on superglobal arrays * * @category HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @version Release: @package_version@ */ class HTML_QuickForm2_DataSource_SuperGlobal extends HTML_QuickForm2_DataSource_Array implements HTML_QuickForm2_DataSource_Submit { /** * Information on file uploads (from $_FILES) * @var array */ protected $files = array(); /** * Keys present in the $_FILES array * @var array */ private static $_fileKeys = array('name', 'type', 'size', 'tmp_name', 'error'); /** * Class constructor, intializes the internal arrays from superglobals * * @param string Request method (GET or POST) * @param bool Whether magic_quotes_gpc directive is on */ public function __construct($requestMethod = 'POST', $magicQuotesGPC = false) { if (!$magicQuotesGPC) { if ('GET' == strtoupper($requestMethod)) { $this->values = $_GET; } else { $this->values = $_POST; $this->files = $_FILES; } } else { if ('GET' == strtoupper($requestMethod)) { $this->values = $this->arrayMapRecursive('stripslashes', $_GET); } else { $this->values = $this->arrayMapRecursive('stripslashes', $_POST); foreach ($_FILES as $key1 => $val1) { foreach ($val1 as $key2 => $val2) { if ('name' == $key2) { $this->files[$key1][$key2] = $this->arrayMapRecursive( 'stripslashes', $val2 ); } else { $this->files[$key1][$key2] = $val2; } } } } } } /** * A recursive version of array_map() function * * @param callback Callback function to apply * @param mixed Input array * @return array with callback applied */ protected function arrayMapRecursive($callback, $arr) { if (!is_array($arr)) { return call_user_func($callback, $arr); } $mapped = array(); foreach ($arr as $k => $v) { $mapped[$k] = is_array($v)? $this->arrayMapRecursive($callback, $v): call_user_func($callback, $v); } return $mapped; } public function getUpload($name) { if (empty($this->files)) { return null; } if (false !== ($pos = strpos($name, '['))) { $tokens = explode('[', str_replace(']', '', $name)); $base = array_shift($tokens); $value = array(); if (!isset($this->files[$base]['name'])) { return null; } foreach (self::$_fileKeys as $key) { $value[$key] = $this->files[$base][$key]; } do { $token = array_shift($tokens); if (!isset($value['name'][$token])) { return null; } foreach (self::$_fileKeys as $key) { $value[$key] = $value[$key][$token]; } } while (!empty($tokens)); return $value; } elseif(isset($this->files[$name])) { return $this->files[$name]; } else { return null; } } } ?> pear/HTML/QuickForm2/Rule.php000064400000033775152101611340011652 0ustar00, * Bertrand Mansion * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The names of the authors may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * @category HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version SVN: $Id: Rule.php 310525 2011-04-26 18:42:03Z avb $ * @link http://pear.php.net/package/HTML_QuickForm2 */ /** * Abstract base class for HTML_QuickForm2 rules * * This class provides methods that allow chaining several rules together. * Its validate() method executes the whole rule chain starting from this rule. * * @category HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @version Release: @package_version@ */ abstract class HTML_QuickForm2_Rule { /** * Constant showing that validation should be run server-side * @see HTML_QuickForm2_Node::addRule() */ const SERVER = 1; /** * Constant showing that validation should be run client-side (on form submit) * @see HTML_QuickForm2_Node::addRule() */ const CLIENT = 2; /** * Constant showing that validation should be run client-side (on form submit and on leaving the field) * @see HTML_QuickForm2_Node::addRule() */ const ONBLUR_CLIENT = 6; /** * A combination of SERVER and CLIENT constants * @see HTML_QuickForm2_Node::addRule() */ const CLIENT_SERVER = 3; /** * A combination of SERVER and ONBLUR_CLIENT constants * @see HTML_QuickForm2_Node::addRule() */ const ONBLUR_CLIENT_SERVER = 7; /** * An element whose value will be validated by this rule * @var HTML_QuickForm2_Node */ protected $owner; /** * An error message to display if validation fails * @var string */ protected $message; /** * Configuration data for the rule * @var mixed */ protected $config; /** * Rules chained to this via "and" and "or" operators * * The contents can be described as "disjunctive normal form", where an outer * array represents a disjunction of conjunctive clauses represented by inner * arrays. * * @var array */ protected $chainedRules = array(array()); /** * Class constructor * * @param HTML_QuickForm2_Node Element to validate * @param string Error message to display if validation fails * @param mixed Configuration data for the rule */ public function __construct(HTML_QuickForm2_Node $owner, $message = '', $config = null) { $this->setOwner($owner); $this->setMessage($message); $this->setConfig($config); } /** * Merges local configuration with that provided for registerRule() * * Default behaviour is for global config to override local one, different * Rules may implement more complex merging behaviours. * * @param mixed Local configuration * @param mixed Global configuration, usually provided to {@link HTML_QuickForm2_Factory::registerRule()} * @return mixed Merged configuration */ public static function mergeConfig($localConfig, $globalConfig) { return is_null($globalConfig)? $localConfig: $globalConfig; } /** * Sets configuration data for the rule * * @param mixed Rule configuration data (specific for a Rule) * @return HTML_QuickForm2_Rule * @throws HTML_QuickForm2_InvalidArgumentException in case of invalid * configuration data */ public function setConfig($config) { $this->config = $config; return $this; } /** * Returns the rule's configuration data * * @return mixed Configuration data (specific for a Rule) */ public function getConfig() { return $this->config; } /** * Sets the error message output by the rule * * @param string Error message to display if validation fails * @return HTML_QuickForm2_Rule */ public function setMessage($message) { $this->message = (string)$message; return $this; } /** * Returns the error message output by the rule * * @return string Error message */ public function getMessage() { return $this->message; } /** * Sets the element that will be validated by this rule * * @param HTML_QuickForm2_Node Element to validate * @throws HTML_QuickForm2_InvalidArgumentException if trying to set * an instance of HTML_QuickForm2_Element_Static as rule owner */ public function setOwner(HTML_QuickForm2_Node $owner) { // Very little sense to validate static elements as they're, well, static. // If someone comes up with a validation rule for these, he can override // setOwner() there... if ($owner instanceof HTML_QuickForm2_Element_Static) { throw new HTML_QuickForm2_InvalidArgumentException( get_class($this) . ' cannot validate Static elements' ); } if (null !== $this->owner) { $this->owner->removeRule($this); } $this->owner = $owner; } /** * Adds a rule to the chain with an "and" operator * * Evaluation is short-circuited, next rule will not be evaluated if the * previous one returns false. The method is named this way because "and" is * a reserved word in PHP. * * @param HTML_QuickForm2_Rule * @return HTML_QuickForm2_Rule first rule in the chain (i.e. $this) * @throws HTML_QuickForm2_InvalidArgumentException when trying to add * a "required" rule to the chain */ public function and_(HTML_QuickForm2_Rule $next) { if ($next instanceof HTML_QuickForm2_Rule_Required) { throw new HTML_QuickForm2_InvalidArgumentException( 'and_(): Cannot add a "required" rule' ); } $this->chainedRules[count($this->chainedRules) - 1][] = $next; return $this; } /** * Adds a rule to the chain with an "or" operator * * Evaluation is short-circuited, next rule will not be evaluated if the * previous one returns true. The method is named this way because "or" is * a reserved word in PHP. * * @param HTML_QuickForm2_Rule * @return HTML_QuickForm2_Rule first rule in the chain (i.e. $this) * @throws HTML_QuickForm2_InvalidArgumentException when trying to add * a "required" rule to the chain */ public function or_(HTML_QuickForm2_Rule $next) { if ($next instanceof HTML_QuickForm2_Rule_Required) { throw new HTML_QuickForm2_InvalidArgumentException( 'or_(): Cannot add a "required" rule' ); } $this->chainedRules[] = array($next); return $this; } /** * Performs validation * * The whole rule chain is executed. Note that the side effect of this * method is setting the error message on element if validation fails * * @return boolean Whether the element is valid */ public function validate() { $globalValid = false; $localValid = $this->validateOwner(); foreach ($this->chainedRules as $item) { foreach ($item as $multiplier) { if (!($localValid = $localValid && $multiplier->validate())) { break; } } if ($globalValid = $globalValid || $localValid) { break; } $localValid = true; } $globalValid or $this->setOwnerError(); return $globalValid; } /** * Validates the owner element * * @return bool Whether owner element is valid according to the rule */ abstract protected function validateOwner(); /** * Sets the error message on the owner element */ protected function setOwnerError() { if (strlen($this->getMessage()) && !$this->owner->getError()) { $this->owner->setError($this->getMessage()); } } /** * Returns the client-side validation callback * * This essentially builds a Javascript version of validateOwner() method, * with element ID and Rule configuration hardcoded. * * @return string Javascript function to validate the element's value * @throws HTML_QuickForm2_Exception if Rule can only be run server-side */ protected function getJavascriptCallback() { throw new HTML_QuickForm2_Exception( get_class($this) . ' does not implement javascript validation' ); } /** * Returns IDs of form fields that should trigger "live" Javascript validation * * This returns IDs that are linked to the rule itself. * * @return array */ protected function getOwnJavascriptTriggers() { return $this->owner->getJavascriptTriggers(); } /** * Returns IDs of form fields that should trigger "live" Javascript validation * * This returns IDs that are linked to the rule itself and its chained * rules. Live validation will be be triggered by 'blur' or 'change' event * on any of the elements whose IDs are returned by this method. * * @return array */ protected function getJavascriptTriggers() { $triggers = array_flip($this->getOwnJavascriptTriggers()); foreach ($this->chainedRules as $item) { foreach ($item as $multiplier) { foreach ($multiplier->getJavascriptTriggers() as $trigger) { $triggers[$trigger] = true; } } } return array_keys($triggers); } /** * Returns the client-side representation of the Rule * * The Javascript object returned contains the following fields: * - callback: {@see getJavascriptCallback()} * - elementId: element ID to set error for if validation fails * - errorMessage: error message to set if validation fails * - chained: chained rules, array of arrays like in $chainedRules property * * @return string * @throws HTML_QuickForm2_Exception if Rule or its chained Rules can only * be run server-side */ public function getJavascript($outputTriggers = true) { HTML_QuickForm2_Loader::loadClass('HTML_QuickForm2_JavascriptBuilder'); $js = $this->getJavascriptCallback() . ",\n\t'" . $this->owner->getId() . "', " . HTML_QuickForm2_JavascriptBuilder::encode($this->getMessage()); $js = $outputTriggers && count($triggers = $this->getJavascriptTriggers()) ? 'new qf.LiveRule(' . $js . ', ' . HTML_QuickForm2_JavascriptBuilder::encode($triggers) : 'new qf.Rule(' . $js; if (count($this->chainedRules) > 1 || count($this->chainedRules[0]) > 0) { $chained = array(); foreach ($this->chainedRules as $item) { $multipliers = array(); foreach ($item as $multiplier) { $multipliers[] = $multiplier->getJavascript(false); } $chained[] = '[' . implode(",\n", $multipliers) . ']'; } $js .= ",\n\t [" . implode(",\n", $chained) . "]"; } return $js . ')'; } /** * (( defined by alex@cgi-central.net - it is too wordy to build * rules without such easy chaining )) * Adds a validation rule to the owner of this validation rule * * @param HTML_QuickForm2_Rule|string Validation rule or rule type * @param string Message to display if validation * @param mixed Additional data for the rule * @return HTML_QuickForm2_Rule The added rule * @throws HTML_QuickForm2_InvalidArgumentException if $rule is of a * wrong type or rule name isn't registered with Factory * @throws HTML_QuickForm2_NotFoundException if class for a given rule * name cannot be found */ public function addRule($rule, $message = '', $options = null) { return $this->owner->addRule($rule, $message, $options); } /** @return HTML_QuickForm2_Node */ public function getOwner() { return $this->owner; } } pear/HTML/QuickForm2/Element/InputImage.php000064400000013103152101611340014355 0ustar00 elements * * PHP version 5 * * LICENSE: * * Copyright (c) 2006-2011, Alexey Borzov , * Bertrand Mansion * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The names of the authors may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * @category HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version SVN: $Id: InputImage.php 311435 2011-05-26 10:30:06Z avb $ * @link http://pear.php.net/package/HTML_QuickForm2 */ /** * Base class for elements */ require_once 'HTML/QuickForm2/Element/Input.php'; /** * Class for elements * * @category HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @version Release: @package_version@ */ class HTML_QuickForm2_Element_InputImage extends HTML_QuickForm2_Element_Input { protected $attributes = array('type' => 'image'); /** * Coordinates of user click within the image, array contains keys 'x' and 'y' * @var array */ protected $coordinates = null; /** * Image buttons can not be frozen * * @param bool Whether element should be frozen or editable. This * parameter is ignored in case of image elements * @return bool Always returns false */ public function toggleFrozen($freeze = null) { return false; } /** * Image button's value cannot be set via this method * * @param mixed Element's value, this parameter is ignored * @return HTML_QuickForm2_Element_InputImage */ public function setValue($value) { return $this; } /** * Returns the element's value * * The value is only returned if the form was actually submitted and this * image button was clicked. Returns null in all other cases. * * @return array|null An array with keys 'x' and 'y' containing the * coordinates of user click if the image was clicked, * null otherwise */ public function getRawValue() { return $this->getAttribute('disabled')? null: $this->coordinates; } /** * Returns the HTML representation of the element * * The method changes the element's name to foo[bar][] if it was foo[bar] * originally. If it is not done, then one of the click coordinates will be * lost, see {@link http://bugs.php.net/bug.php?id=745} * * @return string */ public function __toString() { if (false === strpos($this->attributes['name'], '[') || '[]' == substr($this->attributes['name'], -2)) { return parent::__toString(); } else { $this->attributes['name'] .= '[]'; $html = parent::__toString(); $this->attributes['name'] = substr($this->attributes['name'], 0, -2); return $html; } } protected function updateValue() { foreach ($this->getDataSources() as $ds) { if ($ds instanceof HTML_QuickForm2_DataSource_Submit) { $name = $this->getName(); if (false === strpos($name, '[') && null !== ($value = $ds->getValue($name . '_x'))) { $this->coordinates = array( 'x' => $value, 'y' => $ds->getValue($name . '_y') ); return; } elseif (false !== strpos($name, '[')) { if ('[]' == substr($name, -2)) { $name = substr($name, 0, -2); } if (null !== ($value = $ds->getValue($name))) { $this->coordinates = array( 'x' => $value[0], 'y' => $value[1] ); return; } } } } $this->coordinates = null; } } ?> pear/HTML/QuickForm2/Element/Static.php000064400000021234152101611340013546 0ustar00, * Bertrand Mansion * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The names of the authors may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * @category HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version SVN: $Id: Static.php 311477 2011-05-27 09:01:44Z avb $ * @link http://pear.php.net/package/HTML_QuickForm2 */ /** * Base class for simple HTML_QuickForm2 elements (not Containers) */ require_once 'HTML/QuickForm2/Element.php'; /** * Class for static elements that only contain text or markup * * @category HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @version Release: @package_version@ */ class HTML_QuickForm2_Element_Static extends HTML_QuickForm2_Element { /** * Name of the tag to wrap around static element's content * @var string */ protected $tagName = null; /** * Whether to output closing tag when $tagName is set and element's content is empty * @var bool */ protected $forceClosingTag = true; /** * Contains options and data used for the element creation * - content: Content of the static element * @var array */ protected $data = array('content' => ''); /** * Class constructor * * Static element can understand the following keys in $data parameter: * - 'content': content of the static element, e.g. text or markup * - 'tagName': name of the tag to wrap around content, e.g. 'div'. * Using tag names corresponding to form elements will cause an Exception * - 'forceClosingTag': whether to output closing tag in case of empty * content, <foo></foo> vs. <foo /> * * @param string Element name * @param mixed Attributes (either a string or an array) * @param array Additional element data */ public function __construct($name = null, $attributes = null, array $data = array()) { if (!empty($data['tagName'])) { $this->setTagName( $data['tagName'], !array_key_exists('forceClosingTag', $data) || $data['forceClosingTag'] ); } unset($data['tagName'], $data['forceClosingTag']); parent::__construct($name, $attributes, $data); } /** * Intercepts setting 'name' and 'id' attributes * * Overrides parent method to allow removal of 'name' attribute on Static * elements * * @param string Attribute name * @param string Attribute value, null if attribute is being removed * @throws HTML_QuickForm2_InvalidArgumentException if trying to * remove a required attribute */ protected function onAttributeChange($name, $value = null) { if ('name' == $name && null === $value) { unset($this->attributes['name']); } else { parent::onAttributeChange($name, $value); } } /** * Sets the element's name * * Passing null here will remove the name attribute * * @param string|null * @return HTML_QuickForm2_Element_Static */ public function setName($name) { if (null !== $name) { return parent::setName($name); } else { return $this->removeAttribute('name'); } } public function getType() { return 'static'; } /** * Static element can not be frozen * * @param bool Whether element should be frozen or editable. This * parameter is ignored in case of static elements * @return bool Always returns false */ public function toggleFrozen($freeze = null) { return false; } /** * Sets the contents of the static element * * @param string Static content * @return HTML_QuickForm2_Element_Static */ function setContent($content) { $this->data['content'] = $content; return $this; } /** * Returns the contents of the static element * * @return string */ function getContent() { return $this->data['content']; } /** * Static element's content can also be set via this method * * @param mixed Element's value, this parameter is ignored * @return HTML_QuickForm2_Element_Static */ public function setValue($value) { $this->setContent($value); return $this; } /** * Static elements have no value * * @return null */ public function getRawValue() { return null; } public function __toString() { $prefix = $this->getIndent(); if ($comment = $this->getComment()) { $prefix .= '' . HTML_Common2::getOption('linebreak') . $this->getIndent(); } if (!$this->tagName) { return $prefix . $this->getContent(); } elseif ('' != $this->getContent()) { return $prefix . '<' . $this->tagName . $this->getAttributes(true) . '>' . $this->getContent() . 'tagName . '>'; } else { return $prefix . '<' . $this->tagName . $this->getAttributes(true) . ($this->forceClosingTag ? '>tagName . '>' : ' />'); } } public function getJavascriptValue($inContainer = false) { return ''; } public function getJavascriptTriggers() { return array(); } /** * Called when the element needs to update its value from form's data sources * * Static elements content can be updated with default form values. */ protected function updateValue() { foreach ($this->getDataSources() as $ds) { if (!$ds instanceof HTML_QuickForm2_DataSource_Submit && null !== ($value = $ds->getValue($this->getName()))) { $this->setContent($value); return; } } } /** * Sets the name of the HTML tag to wrap around static element's content * * @param string tag name * @param bool whether to output closing tag in case of empty contents * @throws HTML_QuickForm2_InvalidArgumentException when trying to set a tag * name corresponding to a form element * @return HTML_QuickForm2_Element_Static */ public function setTagName($name, $forceClosing = true) { // Prevent people shooting themselves in the proverbial foot if (in_array(strtolower($name), array('form', 'fieldset', 'button', 'input', 'select', 'textarea')) ) { throw new HTML_QuickForm2_InvalidArgumentException( "Do not use tag name '{$name}' with Static element, use proper element class" ); } $this->tagName = (string)$name; $this->forceClosingTag = (bool)$forceClosing; return $this; } } ?>pear/HTML/QuickForm2/Element/InputHidden.php000064400000006053152101611340014534 0ustar00 elements * * PHP version 5 * * LICENSE: * * Copyright (c) 2006-2011, Alexey Borzov , * Bertrand Mansion * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The names of the authors may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * @category HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version SVN: $Id: InputHidden.php 311435 2011-05-26 10:30:06Z avb $ * @link http://pear.php.net/package/HTML_QuickForm2 */ /** * Base class for elements */ require_once 'HTML/QuickForm2/Element/Input.php'; /** * Class for elements * * @category HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @version Release: @package_version@ */ class HTML_QuickForm2_Element_InputHidden extends HTML_QuickForm2_Element_Input { protected $attributes = array('type' => 'hidden'); /** * Hidden elements can not be frozen * * @param bool Whether element should be frozen or editable. This * parameter is ignored in case of hidden elements * @return bool Always returns false */ public function toggleFrozen($freeze = null) { return false; } public function render(HTML_QuickForm2_Renderer $renderer) { $renderer->renderHidden($this); $this->renderClientRules($renderer->getJavascriptBuilder()); return $renderer; } } ?> pear/HTML/QuickForm2/Element/InputRadio.php000064400000005250152101611340014375 0ustar00 elements * * PHP version 5 * * LICENSE: * * Copyright (c) 2006-2011, Alexey Borzov , * Bertrand Mansion * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The names of the authors may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * @category HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version SVN: $Id: InputRadio.php 309777 2011-03-28 10:41:11Z avb $ * @link http://pear.php.net/package/HTML_QuickForm2 */ /** * Base class for checkboxes and radios */ require_once 'HTML/QuickForm2/Element/InputCheckable.php'; /** * Class for elements * * @category HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @version Release: @package_version@ */ class HTML_QuickForm2_Element_InputRadio extends HTML_QuickForm2_Element_InputCheckable { protected $attributes = array('type' => 'radio', 'value' => 'on'); protected $frozenHtml = array( 'checked' => '(x)', 'unchecked' => '( )' ); } ?> pear/HTML/QuickForm2/Element/Select.php000064400000046205152101611340013543 0ustar00 elements * * PHP version 5 * * LICENSE: * * Copyright (c) 2006-2011, Alexey Borzov , * Bertrand Mansion * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The names of the authors may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * @category HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version SVN: $Id: Select.php 311435 2011-05-26 10:30:06Z avb $ * @link http://pear.php.net/package/HTML_QuickForm2 */ /** * Base class for simple HTML_QuickForm2 elements */ require_once 'HTML/QuickForm2/Element.php'; /** * Collection of s * * This class handles the output of