Overview

Packages

  • application
    • commands
    • components
      • actions
      • filters
      • leftWidget
      • permissions
      • sortableWidget
      • util
      • webupdater
      • x2flow
        • actions
        • triggers
      • X2GridView
      • X2Settings
    • controllers
    • models
      • embedded
    • modules
      • accounts
        • controllers
        • models
      • actions
        • controllers
        • models
      • calendar
        • controllers
        • models
      • charts
        • models
      • contacts
        • controllers
        • models
      • docs
        • components
        • controllers
        • models
      • groups
        • controllers
        • models
      • marketing
        • components
        • controllers
        • models
      • media
        • controllers
        • models
      • mobile
        • components
      • opportunities
        • controllers
        • models
      • products
        • controllers
        • models
      • quotes
        • controllers
        • models
      • services
        • controllers
        • models
      • template
        • models
      • users
        • controllers
        • models
      • workflow
        • controllers
        • models
      • x2Leads
        • controllers
        • models
  • None
  • system
    • base
    • caching
    • console
    • db
      • ar
      • schema
    • validators
    • web
      • actions
      • auth
      • helpers
      • widgets
        • captcha
        • pagers
  • zii
    • widgets
      • grid

Classes

  • AccountsGridViewProfileWidget
  • ActionMenu
  • ActionsGridViewProfileWidget
  • ActionsQuickCreateRelationshipBehavior
  • ActiveDateRangeInput
  • ApplicationConfigBehavior
  • Attachments
  • ChatBox
  • CommonControllerBehavior
  • ContactMapInlineTags
  • ContactsGridViewProfileWidget
  • CronForm
  • CSaveRelationsBehavior
  • DateRangeInputsWidget
  • DocsGridViewProfileWidget
  • DocViewer
  • DocViewerProfileWidget
  • EButtonColumnWithClearFilters
  • EmailDeliveryBehavior
  • EmailProgressControl
  • EncryptedFieldsBehavior
  • EventsChartProfileWidget
  • FileUploader
  • FontPickerInput
  • Formatter
  • FormView
  • GridViewWidget
  • History
  • IframeWidget
  • ImportExportBehavior
  • InlineActionForm
  • InlineEmailAction
  • InlineEmailForm
  • InlineEmailModelBehavior
  • InlineQuotes
  • JSONEmbeddedModelFieldsBehavior
  • JSONFieldsDefaultValuesBehavior
  • LeadRoutingBehavior
  • LeftWidget
  • LoginThemeHelper
  • LoginThemeHelperBase
  • MarketingGridViewProfileWidget
  • MediaBox
  • MessageBox
  • MobileFormatter
  • MobileFormLayoutRenderer
  • MobileLayoutRenderer
  • MobileLoginThemeHelper
  • MobileViewLayoutRenderer
  • ModelFileUploader
  • NewWebLeadsGridViewProfileWidget
  • NormalizedJSONFieldsBehavior
  • NoteBox
  • OnlineUsers
  • OpportunitiesGridViewProfileWidget
  • Panel
  • ProfileDashboardManager
  • ProfileGridViewWidget
  • ProfileLayoutEditor
  • ProfilesGridViewProfileWidget
  • Publisher
  • PublisherActionTab
  • PublisherCalendarEventTab
  • PublisherCallTab
  • PublisherCommentTab
  • PublisherEventTab
  • PublisherSmallCalendarEventTab
  • PublisherTab
  • PublisherTimeTab
  • QuickContact
  • QuickCreateRelationshipBehavior
  • QuotesGridViewProfileWidget
  • RecordAliasesWidget
  • RecordViewLayoutManager
  • RecordViewWidgetManager
  • RememberPagination
  • Reminders
  • ResponseBehavior
  • ResponsiveHtml
  • SearchIndexBehavior
  • ServicesGridViewProfileWidget
  • SmallCalendar
  • SmartActiveDataProvider
  • SmartDataProviderBehavior
  • SmartSort
  • SocialForm
  • SortableWidgetManager
  • SortableWidgets
  • TagBehavior
  • TagCloud
  • TemplatesGridViewProfileWidget
  • TimeZone
  • TopContacts
  • TopSites
  • TransformedFieldStorageBehavior
  • TranslationLogger
  • TwitterFeed
  • TwoColumnSortableWidgetManager
  • UpdaterBehavior
  • UpdatesForm
  • UserIdentity
  • UsersChartProfileWidget
  • WorkflowBehavior
  • X2ActiveGridView
  • X2ActiveGridViewForSortableWidgets
  • X2AssetManager
  • X2AuthManager
  • X2ChangeLogBehavior
  • X2ClientScript
  • X2Color
  • X2DateUtil
  • X2FixtureManager
  • X2FlowFormatter
  • X2GridView
  • X2GridViewBase
  • X2GridViewForSortableWidgets
  • X2GridViewSortableWidgetsBehavior
  • X2LeadsGridViewProfileWidget
  • X2LinkableBehavior
  • X2ListView
  • X2PillBox
  • X2ProgressBar
  • X2SmartSearchModelBehavior
  • X2TimestampBehavior
  • X2TranslationBehavior
  • X2UrlRule
  • X2WebModule
  • X2Widget
  • X2WidgetList
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /*****************************************************************************************
  3:  * X2Engine Open Source Edition is a customer relationship management program developed by
  4:  * X2Engine, Inc. Copyright (C) 2011-2016 X2Engine Inc.
  5:  * 
  6:  * This program is free software; you can redistribute it and/or modify it under
  7:  * the terms of the GNU Affero General Public License version 3 as published by the
  8:  * Free Software Foundation with the addition of the following permission added
  9:  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
 10:  * IN WHICH THE COPYRIGHT IS OWNED BY X2ENGINE, X2ENGINE DISCLAIMS THE WARRANTY
 11:  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 12:  * 
 13:  * This program is distributed in the hope that it will be useful, but WITHOUT
 14:  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 15:  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
 16:  * details.
 17:  * 
 18:  * You should have received a copy of the GNU Affero General Public License along with
 19:  * this program; if not, see http://www.gnu.org/licenses or write to the Free
 20:  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 21:  * 02110-1301 USA.
 22:  * 
 23:  * You can contact X2Engine, Inc. P.O. Box 66752, Scotts Valley,
 24:  * California 95067, USA. or at email address contact@x2engine.com.
 25:  * 
 26:  * The interactive user interfaces in modified source and object code versions
 27:  * of this program must display Appropriate Legal Notices, as required under
 28:  * Section 5 of the GNU Affero General Public License version 3.
 29:  * 
 30:  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
 31:  * these Appropriate Legal Notices must retain the display of the "Powered by
 32:  * X2Engine" logo. If the display of the logo is not reasonably feasible for
 33:  * technical reasons, the Appropriate Legal Notices must display the words
 34:  * "Powered by X2Engine".
 35:  *****************************************************************************************/
 36: 
 37: /**
 38:  * Consolidated class for common string formatting and parsing functions.
 39:  *
 40:  * @package application.components
 41:  */
 42: class Formatter {
 43: 
 44:     /**
 45:      * Removes invalid/corrupt multibyte sequences from text.
 46:      */
 47:     public static function mbSanitize($text) {
 48:         $newText = $text;
 49:         if (!mb_detect_encoding($text, Yii::app()->charset, true)) {
 50:             $newText = mb_convert_encoding($text, Yii::app()->charset, 'ISO-8859-1');
 51:         }
 52:         return $newText;
 53:     }
 54: 
 55:     /**
 56:      * Return a value cast after a named PHP type
 57:      * @param type $value
 58:      * @param type $type
 59:      * @return type
 60:      */
 61:     public static function typeCast($value,$type) {
 62:         switch($type) {
 63:             case 'bool':
 64:             case 'boolean':
 65:                 return (boolean) $value;
 66:             case 'double':
 67:                 return (double) $value;
 68:             case 'int':
 69:             case 'integer':
 70:                 return (integer) $value;
 71:             default:
 72:                 return (string) $value;
 73:         }
 74:     }
 75:     
 76:     /**
 77:      * Converts a record's Description or Background Info to deal with the discrepancy
 78:      * between MySQL/PHP line breaks and HTML line breaks.
 79:      */
 80:     public static function convertLineBreaks($text, $allowDouble = true, $allowUnlimited = false){
 81: 
 82:         if(preg_match("/<br \/>/", $text)){
 83:             $text = preg_replace("/<\/b>/", "</b><br />", $text, 1);
 84:             $text = preg_replace("/\s<b>/", "<br /><b>", $text, 1);
 85:             return $text;
 86:         }
 87: 
 88:         $text = mb_ereg_replace("\r\n", "\n", $text);  //convert microsoft's stupid CRLF to just LF
 89: 
 90:         if(!$allowUnlimited) {
 91:             // replaces 2 or more CR/LF chars with just 2
 92:             $text = mb_ereg_replace("[\r\n]{3,}", "\n\n", $text); 
 93:         }
 94: 
 95:         if($allowDouble) {
 96:             // replaces all remaining CR/LF chars with <br />
 97:             $text = mb_ereg_replace("[\r\n]", '<br />', $text); 
 98:         } else {
 99:             $text = mb_ereg_replace("[\r\n]+", '<br />', $text);
100:         }
101: 
102:         return $text;
103:     }
104: 
105:     /**
106:      * Parses a "formula" for the flow.
107:      *
108:      * If the first character in a string value in X2Flow is the "=" character, it
109:      * will be treated as valid PHP code to be executed. This function uses {@link getSafeWords}
110:      * to determine a list of functions which the user can execute in the code,
111:      * and strip any which are not allowed. This should generally be used for
112:      * mathematical operations, like calculating dynamic date offsets.
113:      *
114:      * @param string $input The code to be executed
115:      * @param array $params Optional extra parameters, notably the Model triggering the flow
116:      * @return array An array with the first element true or false corresponding to
117:      *  whether execution succeeded, the second, the value returned by the formula.
118:      */
119:     public static function parseFormula($input, array $params = array()){
120:         if(strpos($input,'=') !== 0) {
121:             return array(false,Yii::t('admin','Formula does not begin with "="'));
122:         }
123:         
124:         $formula = substr($input, 1); // Remove the "=" character from in front
125:         
126:         $replacementTokens = static::getReplacementTokens ($formula, $params, false, false);
127:         
128:         // Run through all short codes and ensure they're proper PHP expressions
129:         // that correspond to their value, i.e. strings will become string
130:         // expressions, integers will become integers, etc.
131:         //
132:         // This step is VITALLY IMPORTANT to the security and stability of
133:         // X2Flow's formula parsing.
134:         foreach(array_keys($replacementTokens) as $token) {
135:             $type = gettype($replacementTokens[$token]);
136: 
137:             if(!in_array($type,array("boolean","integer","double","string","NULL"))) {
138:                 // Safeguard against "array to string conversion" and "warning,
139:                 // object of class X could not be converted to string" errors.
140:                 // This case shouldn't happen and is not valid, so nothing
141:                 // smarter need be done here than to simply set the replacement
142:                 // value to its corresponding token.
143:                 $replacementTokens[$token] = var_export($token,true);
144: 
145:             } else if ($type === 'string') {
146:                 // Escape/convert values into valid PHP expressions
147:                 $replacementTokens[$token] = var_export($replacementTokens[$token],true);
148:             }
149:         }
150: 
151:         // Prepare formula for eval:
152:         if(strpos($formula, ';') !== strlen($formula) - 1){
153:             // Eval requires a ";" at the end to execute properly
154:             $formula .= ';';
155:         }
156:         if(strpos($formula, 'return ') !== 0){
157:             // Eval requires a "return" at the front.
158:             $formula = 'return '.$formula;
159:         }
160: 
161:         // Validity check: ensure the formula only consists of "safe" functions,
162:         // the existing variable tokens, spaces, and PHP operators:
163:         foreach(array_keys($replacementTokens) as $token) {
164:             $shortCodePatterns[] = preg_quote($token,'#');
165:         }
166:         // PHP operators
167:         $charOp = '[\[\]()<>=!^|?:*+%/\-\.]';
168:         $charOpOrWhitespace = '(?:'.$charOp.'|\s)';
169:         $phpOper = implode ('|', array (
170:             $charOpOrWhitespace,
171:             $charOpOrWhitespace.'and',
172:             $charOpOrWhitespace.'or',
173:             $charOpOrWhitespace.'xor',
174:             $charOpOrWhitespace.'false',
175:             $charOpOrWhitespace.'true',
176:         ));
177:         // allow empty string '' and prevent final single quote from being escaped
178:         $singleQuotedString = '\'\'|\'[^\']*[^\\\\\']\''; // Only simple strings currently supported
179:         $number = $charOpOrWhitespace.'[0-9]+(?:\.[0-9]+)?';
180:         $validPattern = '#^return(?:'
181:             .self::getSafeWords($charOpOrWhitespace)
182:             .(empty($shortCodePatterns)?'':('|'.implode('|',$shortCodePatterns)))
183:             .'|'.$phpOper
184:             .'|'.$number
185:             .'|'.$singleQuotedString.')*;$#i';
186: 
187:         if(!preg_match($validPattern,$formula)) {
188:             return array(
189:                 false,
190:                 Yii::t('admin','Input evaluates to an invalid formula: ').
191:                     strtr($formula,$replacementTokens));
192:         }
193: 
194:         try{
195:             $retVal = @eval(strtr($formula,$replacementTokens));
196:         }catch(Exception $e){
197:             return array(
198:                 false,
199:                 Yii::t('admin','Evaluated statement encountered an exception: '.$e->getMessage()));
200:         }
201: 
202:         return array(true,$retVal);
203:     }
204: 
205:     /**
206:      * Returns a list of safe functions for formula parsing
207:      *
208:      * This function will generate a string to be inserted into the regex defined
209:      * in the {@link parseFormula} function, where each function not listed in the
210:      * $safeWords array here will be stripped from code execution.
211:      * @return String A string with each function listed as to be inserted into
212:      * a regular expression.
213:      */
214:     private static function getSafeWords($prefix){
215:         $safeWords = array(
216:             $prefix.'echo[ (]',
217:             $prefix.'time[ (]',
218:         );
219:         return implode('|',$safeWords);
220:     }
221: 
222:     /**
223:      * Parses text for short codes and returns an associative array of them.
224:      *
225:      * @param string $value The value to parse
226:      * @param X2Model $model The model on which to operate with attribute replacement
227:      * @param bool $renderFlag The render flag to pass to {@link X2Model::getAttribute()}
228:      * @param bool $makeLinks If the render flag is set, determines whether to render attributes
229:      *  as links
230:      */
231:     protected static function getReplacementTokens(
232:         $value, array $params, $renderFlag, $makeLinks) {
233: 
234:         if (!isset ($params['model'])) throw new CException ('Missing model param');
235: 
236:         $model = $params['model'];
237: 
238:         // Pattern will match {attr}, {attr1.attr2}, {attr1.attr2.attr3}, etc.
239:         $codes = array();
240:         // Types of each value for the short codes:
241:         $codeTypes = array();
242:         $fieldTypes = array_map(function($f){return $f['phpType'];},Fields::getFieldTypes());
243:         $fields = $model->getFields(true);
244:         // check for variables
245:         preg_match_all('/{([a-z]\w*)(\.[a-z]\w*)*?}/i', trim($value), $matches); 
246: 
247:         if(!empty($matches[0])){
248:             foreach($matches[0] as $match){
249:                 $match = substr($match, 1, -1); // Remove the "{" and "}" characters
250:                 $attr = $match;
251:                 if(strpos($match, '.') !== false){ 
252:                     // We found a link attribute (i.e. {company.name})
253: 
254:                     $newModel = $model;
255:                     $pieces = explode('.',$match);
256:                     $first = array_shift($pieces);
257: 
258:                     $codes['{'.$match.'}'] = $newModel->getAttribute(
259:                         $attr, $renderFlag, $makeLinks);
260:                     $codeTypes[$match] = isset($fields[$attr])
261:                             && isset($fieldTypes[$fields[$attr]->type])
262:                             ? $fieldTypes[$fields[$attr]->type]
263:                             : 'string';
264: 
265:                 }else{ // Standard attribute
266:                     // Check if the attribute exists on the model
267:                     if($model->hasAttribute($match)){ 
268:                         $codes['{'.$match.'}'] = $model->getAttribute(
269:                             $match, $renderFlag, $makeLinks);
270:                         $codeTypes[$match] = isset($fields[$match]) 
271:                                 && isset($fieldTypes[$fields[$match]->type])
272:                                 ? $fieldTypes[$fields[$match]->type]
273:                                 : 'string';
274:                         
275:                     }
276:                 }
277:             }
278:         }
279: 
280:         $codes = self::castReplacementTokenTypes ($codes, $codeTypes);
281: 
282:         return $codes;
283:     }
284: 
285:     protected static function castReplacementTokenTypes (array $codes, array $codeTypes) {
286:         // ensure that value of replacement token is of an acceptable type
287:         foreach ($codes as $name => $val) {
288:             if(!in_array(gettype ($val),array("boolean","integer","double","string","NULL"))) {
289:                 // remove invalid value
290:                 unset ($codes[$name]);
291:             } elseif(isset($codeTypes[$name])) {
292:                 $codes[$name] = self::typeCast($val, $codeTypes[$name]);
293:             }
294:         }
295:         return $codes;
296:     }
297: 
298:     /**
299:      * Restore any special characters necessary for insertableAttributes that
300:      * may be mangled by HTMLPurifier
301:      * @param string $text
302:      */
303:     public static function restoreInsertableAttributes($text) {
304:         $characters = array(
305:             '%7B' => '{',
306:             '%7D' => '}',
307:         );
308:         return strtr ($text, $characters);
309:     }
310: 
311:     /*     * * Date Format Functions ** */
312: 
313:     /**
314:      * A function to convert a timestamp into a string stated how long ago an object
315:      * was created.
316:      *
317:      * @param $timestamp The time that the object was posted.
318:      * @return String How long ago the object was posted.
319:      */
320:     public static function timestampAge($timestamp){
321:         $age = time() - strtotime($timestamp);
322:         //return $age;
323:         if($age < 60) {
324:             // less than 1 min ago
325:             return Yii::t('app', 'Just now'); 
326:         }
327:         if($age < 3600) {
328:             // minutes (less than an hour ago)
329:             return Yii::t('app', '{n} minutes ago', array('{n}' => floor($age / 60))); 
330:         }
331:         if($age < 86400) {
332:             // hours (less than a day ago)
333:             return Yii::t('app', '{n} hours ago', array('{n}' => floor($age / 3600))); 
334:         }
335:         
336:         // days (more than a day ago)
337:         return Yii::t('app', '{n} days ago', array('{n}' => floor($age / 86400))); 
338:     }
339: 
340:     /**
341:      * Format a date to be long (September 25, 2011)
342:      * @param integer $timestamp Unix time stamp
343:      */
344:     public static function formatLongDate($timestamp){
345:         if(empty($timestamp)) {
346:             return '';
347:         } else {
348:             return Yii::app()->dateFormatter->format(
349:                 Yii::app()->locale->getDateFormat('long'), $timestamp);
350:         }
351:     }
352: 
353:     /**
354:      * Converts a yii date format string to a jquery ui date format string
355:      * For each of the format string specifications, see:
356:      * http://www.yiiframework.com/doc/api/1.1/CDateTimeParser
357:      * http://api.jqueryui.com/datepicker/
358:      */
359:     public static function yiiDateFormatToJQueryDateFormat ($format) {
360:         $tokens = CDateTimeParser::tokenize ($format);
361:         $jQueryFormat = '';
362:         foreach($tokens as $token) {
363:             switch($token) {
364:                 case 'yyyy':
365:                 case 'y':
366:                     $jQueryFormat .= 'yy';
367:                     break;
368:                 case 'yy':
369:                     $jQueryFormat .= 'y';
370:                     break;
371:                 case 'MMMM':
372:                     $jQueryFormat .= $token;
373:                     break;
374:                 case 'MMM':
375:                     $jQueryFormat .= 'M';
376:                     break;
377:                 case 'MM':
378:                     $jQueryFormat .= 'mm';
379:                     break;
380:                 case 'M':
381:                     $jQueryFormat .= 'm';
382:                     break;
383:                 case 'dd':
384:                     $jQueryFormat .= $token;
385:                     break;
386:                 case 'd':
387:                     $jQueryFormat .= $token;
388:                     break;
389:                 case 'h':
390:                 case 'H':
391:                     $jQueryFormat .= $token;
392:                     break;
393:                 case 'hh':
394:                 case 'HH':
395:                     $jQueryFormat .= $token;
396:                     break;
397:                 case 'm':
398:                     $jQueryFormat .= $token;
399:                     break;
400:                 case 'mm':
401:                     $jQueryFormat .= $token;
402:                     break;
403:                 case 's':
404:                     $jQueryFormat .= $token;
405:                     break;
406:                 case 'ss':
407:                     $jQueryFormat .= $token;
408:                     break;
409:                 case 'a':
410:                     $jQueryFormat .= $token;
411:                     break;
412:                 default:
413:                     $jQueryFormat .= $token;
414:                     break;
415:             }
416:         }
417:         return $jQueryFormat;
418:     }
419: 
420:     /**
421:      * Format dates for the date picker.
422:      * @param string $width A length keyword, i.e. "medium"
423:      * @return string
424:      */
425:     public static function formatDatePicker($width = ''){
426:         if(Yii::app()->locale->getId() == 'en'){
427:             if($width == 'medium')
428:                 return "M d, yy";
429:             else
430:                 return "MM d, yy";
431:         } else{
432:             $format = self::yiiDateFormatToJQueryDateFormat (
433:                 Yii::app()->locale->getDateFormat('medium')); 
434:             return $format;
435:         }
436:     }
437: 
438:     public static function secondsToHours ($seconds) {
439:         $decHours = $seconds / 3600;
440:         return Yii::t(
441:             'app', '{decHours} hours', 
442:             array ('{decHours}' => sprintf('%0.2f', $decHours)));
443:     }
444: 
445:     /**
446:      * Formats a time interval.
447:      *
448:      * @param integer $start Beginning of the interval
449:      * @param integer $duration Length of the interval
450:      */
451:     public static function formatTimeInterval($start,$end,$style=null) {
452:         $duration = $end-$start;
453:         $decHours = $duration/3600;
454:         $intHours = (int) $decHours;
455:         $intMinutes = (int) (($duration % 3600) / 60);
456:         if(empty($style)){
457:             // Default format
458:             $style = Yii::t('app', '{decHours} hours, starting {start}');
459:         }
460:         // Custom format
461:         return strtr($style, array(
462:                     '{decHours}' => sprintf('%0.2f', $decHours),
463:                     '{hoursColMinutes}' => sprintf('%d:%d',$intHours,$intMinutes),
464:                     '{hours}' => $intHours,
465:                     '{minutes}' => $intMinutes,
466:                     '{hoursMinutes}' => $intHours ? 
467:                         sprintf('%d %s %d %s', $intHours, Yii::t('app', 'hours'), 
468:                             $intMinutes, Yii::t('app', 'minutes')) : 
469:                         sprintf('%d %s', $intMinutes, Yii::t('app', 'minutes')),
470:                     '{quarterDecHours}' => sprintf(
471:                         '%0.2f '.Yii::t('app', 'hours'), 
472:                         round($duration / 900.0) * 0.25),
473:                     '{start}' => self::formatCompleteDate($start),
474:                     '{end}' => self::formatCompleteDate($end)
475:                 ));
476:     }
477: 
478:     /**
479:      * Formats time for the time picker.
480:      *
481:      * @param string $width
482:      * @return string
483:      */
484:     public static function formatTimePicker($width = '',$seconds = false){
485:         /*if(Yii::app()->locale->getLanguageId(Yii::app()->locale->getId()) == 'zh'){
486:             return "HH:mm".($seconds?':ss':'');
487:         }*/
488:         $format = Yii::app()->locale->getTimeFormat($seconds?'medium':'short');
489: 
490:         // jquery specifies hours/minutes as hh/mm instead of HH//MM
491:         //$format = strtolower($format); 
492: 
493:         // yii and jquery have different format to specify am/pm
494:         $format = str_replace('a', 'TT', $format); 
495:         return $format;
496:     }
497: 
498:     /**
499:      * Formats a full name according to the name format settigns
500:      * @param type $firstName
501:      * @param type $lastName
502:      */
503:     public static function fullName($firstName,$lastName) {
504:         return !empty(Yii::app()->settings->contactNameFormat) ? 
505:             strtr(Yii::app()->settings->contactNameFormat, compact('lastName', 'firstName')) : 
506:             "$firstName $lastName";
507:     }
508: 
509:     /**
510:      * Generates a column clause using CONCAT based on the full name format as
511:      * defined in the general settings
512:      * 
513:      * @param type $firstNameCol
514:      * @param type $lastNameCol
515:      * @param type $as
516:      * @return array An array with the first element being the SQL, the second any parameters to 
517:      *  bind.
518:      */
519:     public static function fullNameSelect($firstNameCol,$lastNameCol,$as=false) {
520:         $pre = ':fullName_'.uniqid().'_';
521:         $columns = array(
522:             'firstName' => $firstNameCol,
523:             'lastName' => $lastNameCol,
524:         );
525:         $format = empty(Yii::app()->settings->contactNameFormat)
526:                 ? 'firstName lastName'
527:                 : Yii::app()->settings->contactNameFormat;
528:         $placeholderPositions = array();
529:         foreach($columns as $placeholder => $columnName) {
530:             if(($pos = mb_strpos($format,$placeholder)) !== false) {
531:                 $placeholderPositions[$placeholder] = $pos;
532:             }
533:         }
534:         asort($placeholderPositions);
535:         $concatItems = array();
536:         $params = array();
537:         $lenTot = mb_strlen($format);
538:         $n_p = 0;
539:         $pos = 0;
540: 
541:         foreach($placeholderPositions as $placeholder => $position){
542:             // Get extraneous text into a parameter:
543:             if($position > $pos){
544:                 $leadIn = mb_substr($format,$pos,$position-$pos);
545:                 if(!empty($leadIn)) {
546:                     $concatItems[] = $param = "{$pre}_inter_$n_p";
547:                     $params[$param] = $leadIn;
548:                     $n_p++;
549:                 }
550:                 $pos += mb_strlen($leadIn);
551:             }
552:             $concatItems[] = "`{$columns[$placeholder]}`";
553:             $pos += mb_strlen($placeholder);
554:         }
555:         if($pos < $lenTot-1) {
556:             $trailing = mb_substr($format,$pos);
557:             $concatItems[] = $param = "{$pre}_trailing";
558:             $params[$param] = $trailing;
559:         }
560:         return array(
561:             "CONCAT(".implode(',', $concatItems).")".($as ? " AS `$as`" : ''),
562:             $params
563:         );
564:     }
565: 
566:     /**
567:      * Check if am/pm is being used in this locale.
568:      */
569:     public static function formatAMPM(){
570:         if(strstr(Yii::app()->locale->getTimeFormat(), "a") === false) {
571:             return false;
572:         } /*else if(Yii::app()->locale->getLanguageId(Yii::app()->locale->getId()) == 'zh') {
573:             // 24 hour format for china
574:             return false;
575:         } */else {
576:             return true;
577:         }
578:     }
579: 
580:     /*     * * Date Time Format Functions ** */
581: 
582:     public static function formatFeedTimestamp($timestamp){
583:         if (Yii::app()->dateFormatter->format(
584:                 Yii::app()->locale->getDateFormat('medium'), $timestamp) == 
585:             Yii::app()->dateFormatter->format(
586:                 Yii::app()->locale->getDateFormat('medium'), time())){
587: 
588:             $str = Yii::t('app', 'Today').' '.
589:                 Yii::app()->dateFormatter->format(
590:                     Yii::app()->locale->getTimeFormat('short'), $timestamp);
591:         }else{
592:             $str = 
593:                 Yii::app()->dateFormatter->format(
594:                     Yii::app()->locale->getDateFormat('medium'), $timestamp).
595:                 " ".
596:                 Yii::app()->dateFormatter->format(
597:                     Yii::app()->locale->getTimeFormat('short'), $timestamp);
598:         }
599:         return $str;
600:     }
601: 
602:     /**
603:      * Returns a formatted string for the end of the day.
604:      * @param integer $timestamp
605:      * @return string
606:      */
607:     public static function formatDateEndOfDay($timestamp){
608:         if(empty($timestamp)) {
609:             return '';
610:         } else if(Yii::app()->locale->getId() == 'en') {
611:             return Yii::app()->dateFormatter->format(
612:                 Yii::app()->locale->getDateFormat('medium').' '.
613:                     Yii::app()->locale->getTimeFormat('short'), 
614:                 strtotime("tomorrow", $timestamp) - 60);
615:         } /*else if(Yii::app()->locale->getLanguageId(Yii::app()->locale->getId()) == 'zh') {
616:             return Yii::app()->dateFormatter->format(Yii::app()->locale->getDateFormat('short').
617:                 ' '.'HH:mm', strtotime("tomorrow", $timestamp) - 60);
618:         } */else {
619:             return Yii::app()->dateFormatter->format(
620:                 Yii::app()->locale->getDateFormat('medium').' '.
621:                     Yii::app()->locale->getTimeFormat('short'), 
622:                 strtotime("tomorrow", $timestamp) - 60);
623:         }
624:     }
625: 
626:     /**
627:      * Cuts string short.
628:      * @param string $str String to be truncated.
629:      * @param integer $length Maximum length of the string
630:      * @param bool $encode Encode HTML special characters if true
631:      * @return string
632:      */
633:     public static function truncateText($str, $length = 30, $encode=false){
634: 
635:         if(mb_strlen($str, 'UTF-8') > $length - 3){
636:             if($length < 3)
637:                 $str = '';
638:             else
639:                 $str = trim(mb_substr($str, 0, $length - 3, 'UTF-8'));
640:             $str .= '...';
641:         }
642:         return $encode?CHtml::encode($str):$str;
643:     }
644: 
645:     /**
646:      * Converts CamelCased words into first-letter-capitalized, spaced words.
647:      * @param type $str
648:      * @return type
649:      */
650:     public static function deCamelCase($str){
651:         $str = preg_replace("/(([a-z])([A-Z])|([A-Z])([A-Z][a-z]))/", "\\2\\4 \\3\\5", $str);
652:         return ucfirst($str);
653:     }
654: 
655:     /**
656:      * Locale-dependent date string formatting.
657:      * @param integer $date Timestamp
658:      * @param string $width A length keyword, i.e. "medium"
659:      * @return string
660:      */
661:     public static function formatDate($date, $width = 'long', $informal = true){
662:         if(empty($date)){
663:             return '';
664:         }
665:         if(!is_numeric($date))
666:             $date = strtotime($date); // make sure $date is a proper timestamp
667: 
668:         $now = getDate();   // generate date arrays
669:         $due = getDate($date); // for calculations
670:         //$date = mktime(23,59,59,$due['mon'],$due['mday'],$due['year']);   // give them until 11:59 PM to finish the action
671:         //$due = getDate($date);
672:         $ret = '';
673: 
674:         if($informal && $due['year'] == $now['year']){  // is the due date this year?
675:             if($due['yday'] == $now['yday'] && $width == 'long') { // is the due date today?
676:                 $ret = Yii::t('app', 'Today');
677:             } else if($due['yday'] == $now['yday'] + 1 && $width == 'long') { // is it tomorrow? 
678:                 $ret = Yii::t('app', 'Tomorrow');
679:             } else {
680:                 $ret = Yii::app()->dateFormatter->format(
681:                     Yii::app()->locale->getDateFormat($width), $date); // any other day this year
682:             }
683:         } else{
684:             $ret = Yii::app()->dateFormatter->format(
685:                 Yii::app()->locale->getDateFormat($width), $date); // due date is after this year
686:         }
687:         return $ret;
688:     }
689: 
690:     public static function formatTime($date, $width = 'medium'){
691:         return Yii::app()->dateFormatter->formatDateTime($date, null, $width);
692:     }
693: 
694:     public static function formatDueDate($date, $dateWidth='long', $timeWidth='short'){
695:         if(!is_numeric($date))
696:             $date = strtotime($date); // make sure $date is a proper timestamp
697:         return date('l', $date)." ".
698:             Yii::app()->dateFormatter->formatDateTime($date, $dateWidth, null).
699:             " - ".Yii::app()->dateFormatter->formatDateTime($date, null, $timeWidth);
700:     }
701: 
702:     public static function formatCompleteDate($date){
703:         return Yii::app()->dateFormatter->formatDateTime($date, 'long');
704:     }
705: 
706:     /**
707:      * @param mixed $date timestamp
708:      * @return bool 
709:      */
710:     public static function isToday ($date) {
711:         return date ('Ymd') === date ('Ymd', $date);
712:     }
713: 
714:     public static function isThisYear ($date) {
715:         return date ('Y') === date ('Y', $date);
716:     }
717: 
718: //    public static function isThisWeek ($date) {
719: //        return date ('w') === date ('w', $date);
720: //    }
721: 
722:     public static function formatDateDynamic ($date) {
723:         if (self::isToday ($date)) {
724:             return Yii::app()->dateFormatter->format ('h:mm a', $date);
725:         } else if (self::isThisYear ($date)) {
726:             return Yii::app()->dateFormatter->format ('MMM d', $date);
727:         } else {
728:             return Yii::app()->dateFormatter->formatDateTime ($date, 'short', null);
729:         }
730:     }
731: 
732:     /**
733:      * Returns a formatted string for the date.
734:      *
735:      * @param integer $timestamp
736:      * @return string
737:      */
738:     public static function formatLongDateTime($timestamp){
739:         if(empty($timestamp))
740:             return '';
741:         else
742:             return Yii::app()->dateFormatter->formatDateTime($timestamp, 'long', 'medium');
743:     }
744: 
745:     /**
746:      * Formats the date and time for a given timestamp.
747:      * @param type $timestamp
748:      * @return string
749:      */
750:     public static function formatDateTime($timestamp){
751:         if(empty($timestamp)){
752:             return '';
753:         }else if(Yii::app()->locale->getId() == 'en'){
754:             return Yii::app()->dateFormatter->format(
755:                 Yii::app()->locale->getDateFormat('medium').' '.
756:                     Yii::app()->locale->getTimeFormat('short'), 
757:                 $timestamp);
758:         }/*else if(Yii::app()->locale->getLanguageId(Yii::app()->locale->getId()) == 'zh') {
759:             return Yii::app()->dateFormatter->format(
760:                 Yii::app()->locale->getDateFormat('medium').' '.'HH:mm', $timestamp);
761:         } */else {
762:             return Yii::app()->dateFormatter->format(
763:                 Yii::app()->locale->getDateFormat('medium').' '.
764:                     Yii::app()->locale->getTimeFormat('short'), 
765:                 $timestamp);
766:         }
767:     }
768: 
769:     /**
770:      * Adjust abbreviated months for French locale: The trailing . is removed
771:      * from the month names in CDateTimeParser.parseMonth(), resulting in French
772:      * DateTimes failing to parse.
773:      */
774:     public static function getPlainAbbrMonthNames() {
775:         $months = array_map (
776:             function($e) { return rtrim($e,'.'); },
777:             Yii::app()->getLocale()->getMonthNames ('abbreviated')
778:         );
779:         return array_values ($months);
780:     }
781: 
782:     /**
783:      * Obtain a Unix-style integer timestamp for a date format.
784:      *
785:      * @param string $date
786:      * @return mixed integer or false if parsing fails
787:      */
788:     public static function parseDate($date){
789:         if(Yii::app()->locale->getId() == 'en')
790:             return strtotime($date);
791:         else
792:             return CDateTimeParser::parse($date, Yii::app()->locale->getDateFormat('medium'));
793:     }
794: 
795:     /**
796:      * Parses both date and time into a Unix-style integer timestamp.
797:      * @param string $date
798:      * @return integer
799:      */
800:     public static function parseDateTime($date,$dateLength = 'medium', $timeLength = 'short'){
801:         if($date === null){
802:             return null;
803:         }elseif(is_numeric($date)){
804:             return $date;
805:         }elseif(Yii::app()->locale->getId() == 'en'){
806:             return strtotime($date);
807:         } else {
808:             return CDateTimeParser::parse(
809:                 $date, 
810:                 Yii::app()->locale->getDateFormat($dateLength).' '.
811:                 Yii::app()->locale->getTimeFormat($timeLength));
812:         }
813:     }
814: 
815:     /**
816:      * Convert currency to the proper format
817:      *
818:      * @param String $str The currency string
819:      * @param Boolean $keepCents Whether or not to keep the cents
820:      * @return String $str The modified currency string.
821:      */
822:     public static function parseCurrency($str, $keepCents){
823: 
824:         $cents = '';
825:         if($keepCents){
826:             $str = mb_ereg_match('[\.,]([0-9]{2})$', $str, $matches); // get cents
827:             $cents = $matches[1];
828:         }
829:         $str = mb_ereg_replace('[\.,][0-9]{2}$', '', $str); // remove cents
830:         $str = mb_ereg_replace('[^0-9]', '', $str);  //remove all non-numbers
831: 
832:         if(!empty($cents))
833:             $str .= ".$cents";
834: 
835:         return $str;
836:     }
837: 
838:     /**
839:      * Returns the body of an email without any HTML markup.
840:      *
841:      * This function will strip out email header tags, opened email tags, and all
842:      * HTML markup present in an Email type action so that the Action link can be
843:      * properly displayed without looking terrible
844:      * @param String $str Input string to be formatted
845:      * @return String The formatted string
846:      */
847:     public static function parseEmail($str){
848:         $str = preg_replace('/<\!--BeginOpenedEmail-->(.*?)<\!--EndOpenedEmail-->/s', '', $str);
849:         $str = preg_replace('/<\!--BeginActionHeader-->(.*?)<\!--EndActionHeader-->/s', '', $str);
850:         $str = strip_tags($str);
851:         return $str;
852:     }
853: 
854:     /**
855:      * Replace variables in dynamic text blocks.
856:      *
857:      * This function takes text with dynamic attributes such as {firstName} or
858:      * {company.symbol} or {time} and replaces them with appropriate values in
859:      * the text. It is possible to directly access attributes of the model,
860:      * attributes of related models to the model, or "short codes" which are
861:      * fixed variables, so to speak. That is the variable {time} corresponds
862:      * to a defined piece of code which returns the current time.
863:      *
864:      * @param String $value The text which should be searched for dynamic attributes.
865:      * @param X2Model $model The model which attributes should be taken from
866:      * @param String $type Optional, the type of content we're expecting to get. This
867:      * can determine if we should render what comes back via the {@link X2Model::renderAttribute}
868:      * function or just display what we get as is.
869:      * @param Array $params Optional extra parameters which may include default values
870:      * for the attributes in question.
871:      * @param bool $renderFlag (optional) If true, overrides use of $type parameter to determine
872:      *  if attribute should be rendered
873:      * @param bool $makeLinks If the render flag is set, determines whether to render attributes
874:      *  as links
875:      * @return String A modified version of $value with attributes replaced.
876:      */
877:     public static function replaceVariables(
878:         $value, $params, $type = '', $renderFlag=true, $makeLinks=true){
879: 
880:         if (!is_array ($params)) {
881:             $params = array ('model' => $params);
882:         }
883: 
884:         $matches = array();
885:         if($renderFlag && ($type === '' || $type === 'text' || $type === 'richtext')){
886:             $renderFlag = true;
887:         }else{
888:             $renderFlag = false;
889:         }
890:         
891:         $shortCodeValues = static::getReplacementTokens($value, $params, $renderFlag, $makeLinks);
892:         return strtr($value,$shortCodeValues);
893:     }
894: 
895:     /**
896:      * If text is greater than limit, it gets truncated and suffixed with an ellipsis  
897:      * @param string $text
898:      * @param int $limit
899:      * @return string 
900:      */
901:     public static function trimText ($text, $limit = 150) {
902:         if(mb_strlen($text,'UTF-8') > $limit) {
903:             return mb_substr($text, 0,$limit - 3, 'UTF-8').'...';
904:         } else {
905:             return $text;
906:         }
907:     }
908: 
909:     /**
910:      * @param float|int $value 
911:      * @return string value formatted as currency using app-wide currency setting
912:      */
913:     public static function formatCurrency ($value) {
914:         return Yii::app()->locale->numberFormatter->formatCurrency (
915:             $value, Yii::app()->params->currency);
916:     }
917: 
918:     public static function ucwordsSpecific($string, $delimiters = '', $encoding = NULL) {
919: 
920:         if ($encoding === NULL) {
921:             $encoding = mb_internal_encoding();
922:         }
923: 
924:         if (is_string($delimiters)) {
925:             $delimiters = str_split(str_replace(' ', '', $delimiters));
926:         }
927: 
928:         $delimiters_pattern1 = array();
929:         $delimiters_replace1 = array();
930:         $delimiters_pattern2 = array();
931:         $delimiters_replace2 = array();
932:         foreach ($delimiters as $delimiter) {
933:             $ucDelimiter = $delimiter;
934:             $delimiter = strtolower($delimiter);
935:             $uniqid = uniqid();
936:             $delimiters_pattern1[] = '/' . preg_quote($delimiter) . '/';
937:             $delimiters_replace1[] = $delimiter . $uniqid . ' ';
938:             $delimiters_pattern2[] = '/' . preg_quote($ucDelimiter . $uniqid . ' ') . '/';
939:             $delimiters_replace2[] = $ucDelimiter;
940:             $delimiters_cleanup_replace1[] = '/' . preg_quote($delimiter . $uniqid) . ' ' . '/';
941:             $delimiters_cleanup_pattern1[] = $delimiter;
942:         }
943:         $return_string = mb_strtolower($string, $encoding);
944:         //$return_string = $string;
945:         $return_string = preg_replace($delimiters_pattern1, $delimiters_replace1, $return_string);
946: 
947:         $words = explode(' ', $return_string);
948: 
949:         foreach ($words as $index => $word) {
950:             $words[$index] = mb_strtoupper(mb_substr($word, 0, 1, $encoding), $encoding) . 
951:                 mb_substr($word, 1, mb_strlen($word, $encoding), $encoding);
952:         }
953:         $return_string = implode(' ', $words);
954: 
955:         $return_string = preg_replace($delimiters_pattern2, $delimiters_replace2, $return_string);
956:         $return_string = preg_replace(
957:             $delimiters_cleanup_replace1, $delimiters_cleanup_pattern1, $return_string);
958: 
959:         return $return_string;
960:     }
961: 
962:     public static function isFormula ($val) {
963:         return preg_match ('/^=/', $val);
964:     }
965: 
966:     public static function isShortcode ($val) {
967:         return preg_match ('/^\{.*\}$/', $val);
968:     }
969: 
970: }
971: 
972: ?>
973: 
X2CRM Documentation API documentation generated by ApiGen 2.8.0