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

  • ActionsWidget
  • CallsWidget
  • ChartWidget
  • CommentsWidget
  • EmailsWidget
  • EventsWidget
  • InlineRelationshipsWidget
  • InlineTagsWidget
  • LoggedTimeWidget
  • PublisherWidget
  • QuotesWidget
  • SortableWidget
  • TransactionalViewWidget
  • TwitterFeedWidget
  • WebActivityWidget
  • WorkflowStageDetailsWidget
  • 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:  Yii::import ('application.components.X2Widget');
  38:  Yii::import ('application.components.sortableWidget.dataWidgets');
  39: 
  40: /**
  41:  * Base widget class for all of the profile widgets
  42:  * 
  43:  * @package application.components.sortableWidget
  44:  */
  45: abstract class SortableWidget extends X2Widget {
  46: 
  47:     const PROFILE_WIDGET_PATH_ALIAS = 'application.components.sortableWidget.profileWidgets';
  48:     const RECORD_VIEW_WIDGET_PATH_ALIAS = 
  49:         'application.components.sortableWidget.recordViewWidgets';
  50:      
  51: 
  52:     public static $createByDefault = true;
  53: 
  54:     public static $canBeCreated = true;
  55: 
  56:     /**
  57:      * @var string The type of widget that this is (profile). This value is used to detect the 
  58:      *  view files and the profile model JSON property which stores the widget layout for widgets 
  59:      *  of this type.
  60:      * 
  61:      *  The shared view file must have the following name:
  62:      *      <widget type>Widget.php
  63:      *
  64:      *  The profile model widget layout JSON property must have the following name
  65:      *      <widget type>WidgetLayout
  66:      */
  67:     public $widgetType;
  68: 
  69:     /**
  70:      * @var SortableWidgetManager $widgetManager
  71:      */
  72:     public $widgetManager; 
  73: 
  74:     /**
  75:      * @var string JS class which is used to manage the front-end behavior of this widget. This is 
  76:      *  the class which gets instantiated by the setup script.
  77:      */
  78:     public $sortableWidgetJSClass = 'SortableWidget';
  79: 
  80:     public $defaultTitle;
  81: 
  82:     /**
  83:      * @var string Used to distinguish widget clones from eachother
  84:      */
  85:     public $widgetUID = '';
  86: 
  87:     /**
  88:      * @var string A description of the widget
  89:      */
  90:     public $info = '';
  91: 
  92:     /**
  93:      * @var object The profile model associated with the widget 
  94:      */
  95:     public $profile;
  96:      
  97:     /**
  98:      * @var boolean Set to true when the current request is an ajax request
  99:      */
 100:     public $isAjaxRequest = false;
 101: 
 102:     /**
 103:      * @var string The name of the view file containing the widget contents
 104:      */
 105:     public $viewFile = '';
 106: 
 107:     /**
 108:      * @var bool If true, the widget can be relabeled by the user from the widget settings menu 
 109:      */
 110:     public $relabelingEnabled = false;
 111: 
 112:     /**
 113:      * @var bool If true, the widget can be deleted by the user from the widget settings menu 
 114:      */
 115:     public $canBeDeleted = false;
 116: 
 117:     /**
 118:      * A mixture of html and attributes inside curly braces. This gets used by renderWidget to 
 119:      * render widget elements specified in child classes. As with X2GridView, each attribute inside
 120:      * curly braces should have a corresponding method called render<attribute_name>. 
 121:      * @var string Specifies the widget layout.
 122:      */
 123:     public $template = '<div class="submenu-title-bar widget-title-bar">{widgetLabel}{closeButton}{minimizeButton}</div>{widgetContents}';
 124: 
 125:     /**
 126:      * @var array properties which will get passed to the constructor of the JS SortableWidget
 127:      *  class or subclass which corresponds with this widget. 
 128:      */
 129:     protected $_JSSortableWidgetParams;
 130: 
 131:     /**
 132:      * @var array translations to be passed along with $_JSSortableWidgetParams to the JS Sortable
 133:      *  Widget class constructor
 134:      */
 135:     protected $_translations;
 136: 
 137:     /**
 138:      * @var string CSS class to be added to the container element
 139:      */
 140:     protected $containerClass = 'sortable-widget-container x2-layout-island';
 141: 
 142:     /**
 143:      * Packages which will be registered when the widget content gets rendered.
 144:      */
 145:     protected $_packages;
 146: 
 147:     /**
 148:      * @var string This script gets registered when the widget content gets rendered.
 149:      */
 150:     protected $_setupScript;
 151: 
 152:     /**
 153:      * @var string This css gets registered when the widget container gets rendered.
 154:      */
 155:     protected $_sharedCss;
 156: 
 157:     /**
 158:      * @var string This css gets registered when the widget content gets rendered.
 159:      */
 160:     protected $_css;
 161: 
 162:     /**
 163:      * @var array Parameters to be passed to the specified view file referred to by the property
 164:      *  $viewFile
 165:      */
 166:     protected $_viewFileParams;
 167: 
 168:     /**
 169:      * @var string The name of the view file shared by all widgets of a certain type. This is the 
 170:      *  view file from which the view file specified in $viewFile gets rendered.
 171:      */
 172:     protected $_sharedViewFile; 
 173: 
 174:     /**
 175:      * @var array  
 176:      */
 177:     protected $_settingsFormFields;
 178: 
 179:     /**
 180:      * @var array Contains the structure and default values of the widget's settings. Used by
 181:      *  WidgetLayoutJSONFieldsBehavior to determine the structure of the widget layout JSON
 182:      *  string.
 183:      */
 184:     private static $_JSONPropertiesStructure;
 185: 
 186: 
 187:     private $_widgetLabel;
 188: 
 189:     private $_widgetProperties;
 190: 
 191:     /**
 192:      * Used for situations where widget type should resolve to parent type (e.g for 
 193:      * module-specific record view layouts)
 194:      */
 195:     public static function getParentType ($widgetType) {
 196:         if (!in_array ($widgetType, array ('data', 'recordView', 'profile'))) {
 197:             return 'recordView';
 198:         } else {
 199:             return $widgetType;
 200:         }
 201:     }
 202: 
 203:     /**
 204:      * Returns path alias of directory that contains widgets of the specified type
 205:      * @param string $widgetType
 206:      */
 207:     public static function getPathAlias ($widgetType) {
 208:         switch ($widgetType) {
 209:             case 'profile':
 210:                 $pathAlias = self::PROFILE_WIDGET_PATH_ALIAS;
 211:                 break;
 212:             case 'topics':
 213:             case 'recordView':
 214:                 $pathAlias = self::RECORD_VIEW_WIDGET_PATH_ALIAS;
 215:                 break;
 216:              
 217:             default:
 218:                 throw new CException ('invalid widget type');
 219:         }
 220:         return $pathAlias;
 221:     }
 222: 
 223:     /**
 224:      * @var string $widgetType
 225:      * @return string array of instantiatable widget class names
 226:      * @throws CException
 227:      */
 228:     public static function getWidgetSubtypes ($widgetType) {
 229:         static $cache = array ();
 230: 
 231:         if (!in_array ($widgetType, array ('profile', 'topics', 'recordView'
 232:             ))) {
 233: 
 234:             throw new CException ('invalid widget type');
 235:         }
 236: 
 237:         
 238: 
 239:         if (!isset ($cache[$widgetType])) {
 240:             $excludeList = array ('TemplatesGridViewProfileWidget.php');
 241:             $cache[$widgetType] = array_map (function ($file) {
 242:                     return preg_replace ("/\.php$/", '', $file);
 243:                 },
 244:                 array_filter (
 245:                     scandir(Yii::getPathOfAlias(self::getPathAlias ($widgetType))),
 246:                     function ($file) use ($excludeList) {
 247:                         return !in_array ($file, $excludeList) && preg_match ("/\.php$/", $file);
 248:                     }
 249:                 ));
 250:         }
 251:         return $cache[$widgetType];
 252:     }
 253: 
 254:     public static function subtypeIsValid ($widgetType, $widgetSubtype) {
 255:          
 256: 
 257:         if ($widgetType === 'profile' && $widgetSubtype === 'TemplatesGridViewProfileWidget' ||
 258:             in_array ($widgetSubtype, SortableWidget::getWidgetSubtypes ($widgetType))) {
 259: 
 260:             return true;
 261:         } else {
 262:             return false;
 263:         }
 264:     }
 265: 
 266:     public static function getCreatableWidgetOptions ($widgetType) {
 267:         $widgetSubtypes = self::getWidgetSubtypeOptions ($widgetType);
 268:         $filtered = array ();
 269:         foreach ($widgetSubtypes as $type => $label) {
 270:             if (preg_match ('/TemplatesGridViewProfileWidget$/', $type)) {
 271:                 $className = 'TemplatesGridViewProfileWidget';
 272:             } else {
 273:                 $className = $type;
 274:             }
 275:             if (class_exists ($className) && $className::$canBeCreated) $filtered[$type] = Yii::t('app',$label);
 276:         }    
 277:         return $filtered;
 278:     }    
 279: 
 280:     /**
 281:      * @var string $widgetType
 282:      * @return array associative array with widget class names as keys and widget labels as values
 283:      */
 284:     public static function getWidgetSubtypeOptions ($widgetType) {
 285:         static $cache = array ();
 286:         if (!isset ($cache[$widgetType])) {
 287:             $widgetSubtypes = self::getWidgetSubtypes ($widgetType);
 288: 
 289:             $cache[$widgetType] = array_combine (
 290:                 $widgetSubtypes,
 291:                 array_map (function ($widgetType) {
 292:                     $jsonPropertiesStruct = $widgetType::getJSONPropertiesStructure ();
 293:                     return $jsonPropertiesStruct['label'];
 294:                 }, $widgetSubtypes)
 295:             );
 296: 
 297:             // add custom module summary pseudo-subtypes
 298:             if ($widgetType === 'profile') {
 299:                 $customModules = Modules::model ()->getCustomModules (true);
 300: 
 301:                 foreach ($customModules as $module) {
 302:                     $modelName = ucfirst ($module->name);
 303:                     if ($module->name !== 'document' && class_exists ($modelName)) {
 304:                         // prefix widget class name with custom module model name and a delimiter
 305:                         $cache[$widgetType][$modelName.'::TemplatesGridViewProfileWidget'] =
 306:                             Yii::t(
 307:                                 'app', '{modelName} Summary', array ('{modelName}' => Modules::displayName(true, $module->name)));
 308:                     }
 309:                 }
 310:             }
 311:         }
 312:         return $cache[$widgetType];
 313:     }
 314: 
 315:     /**
 316:      * Used to render a widget and its associated scripts during an AJAX request 
 317:      * @param object $controller The instance of the controller that called this method. This 
 318:      *  allows us to call renderPartial, which is necessary if we want registered scripts to be 
 319:      *  included in the AJAX response.
 320:      * @param object $profile The profile model of the user for which this widget will be rendered
 321:      * @param string $widgetType The type of widget that this is (profile)
 322:      * @param array $extraWidgetParams Extra params to pass to the widget (optional)
 323:      */
 324:     public static function getWidgetContents (
 325:         $controller, $profile, $widgetType, $widgetUID, $extraWidgetParams=array ()) {
 326: 
 327:         return CJSON::encode (array (
 328:             'uid' => $widgetUID,
 329:             'widget' => $controller->renderPartial (
 330:                 'application.components.sortableWidget.views._ajaxWidgetContents',
 331:                 array (
 332:                     'widgetClass' => get_called_class (), 
 333:                     'widgetType' => $widgetType,
 334:                     'profile' => $profile,
 335:                     'widgetUID' => $widgetUID,
 336:                     'extraWidgetParams' => $extraWidgetParams,
 337:                 ), true, true)));
 338:     }
 339: 
 340:     /**
 341:      * @returns array the property _JSONPropertiesStructure
 342:      */
 343:     public static function getJSONPropertiesStructure () {
 344:         if (!isset (self::$_JSONPropertiesStructure)) {
 345:             self::$_JSONPropertiesStructure = array (
 346:                 'label' => '', // required
 347:                 'uid' => '', // required
 348:                 'hidden' => false, // required
 349:                 'minimized' => false, // required
 350:                 'containerNumber' => 1,
 351:                 'softDeleted' => false,
 352:             );
 353:         }
 354:         return self::$_JSONPropertiesStructure;
 355:     }
 356: 
 357:     /**
 358:      * Instantiates the widget
 359:      * @param string $widgetLayoutKey Key in widget layout associative array. Contains the widget
 360:      *  class name as well as the uid
 361:      * @param object profile
 362:      */
 363:     public static function instantiateWidget (
 364:         $widgetLayoutKey, $profile, $widgetType = 'profile', $options = array()) {
 365: 
 366:         list($widgetClass, $widgetUID) = SortableWidget::parseWidgetLayoutKey ($widgetLayoutKey);
 367:         if ($widgetClass::getJSONProperty (
 368:             $profile, 'softDeleted', $widgetType, $widgetUID)) {
 369: 
 370:             return;
 371:         }
 372:         return Yii::app()->controller->widget(
 373:             'application.components.sortableWidget.'.$widgetClass, array_merge(
 374:                 array(
 375:                     'widgetUID' => $widgetUID,
 376:                     'profile' => $profile,
 377:                     'widgetType' => $widgetType,
 378:                 )
 379:         , $options));
 380:     }
 381: 
 382:     /**
 383:      * @param $layoutKey The key in the widget layout associated with the widgets json properties.
 384:      *  The key contains both the widget class name and the widget's unique id
 385:      * @return array (<widget class name>, <widget uid>)
 386:      */
 387:     public static function parseWidgetLayoutKey ($widgetLayoutKey) {
 388:         if (preg_match ("/_(\w*)$/", $widgetLayoutKey, $matches)) {
 389:             $widgetUID = $matches[1];
 390:         } else {
 391:             $widgetUID = '';
 392:         }
 393: 
 394:         $widgetClass = preg_replace ("/_\w*/", '', $widgetLayoutKey);
 395:         return array ($widgetClass, $widgetUID);
 396:     }
 397: 
 398:     /**
 399:      * @param object $profile The profile model
 400:      * @param string $widgetType The type of the widget 
 401:      * @param string $widgetLayoutName The name of the layout that the widget belongs to
 402:      * @param array $widgetSettings (optional) Widget setting values indexed by setting names. 
 403:      *  Used to set initial values of widget settings
 404:      * @return array (<success>, <uid>)
 405:      */
 406:     public static function createSortableWidget (
 407:         $profile, $widgetClass, $widgetType, $widgetSettings=array ()) {
 408: 
 409:         if (!self::subtypeIsValid ($widgetType, $widgetClass)) {
 410:             return array (false, null);
 411:         }
 412: 
 413:         $widgetLayoutPropertyName = $widgetType . 'WidgetLayout';
 414:         $layout = $profile->$widgetLayoutPropertyName;
 415: 
 416:         // first look for a widget which has been soft deleted
 417:         if (isset ($layout[$widgetClass]) && $layout[$widgetClass]['softDeleted']) {
 418:             $layout[$widgetClass]['softDeleted'] = false;
 419: 
 420:             $profile->$widgetLayoutPropertyName = $layout;
 421:             if ($profile->save ()) {
 422:                 return array (true, '');
 423:             } else {
 424:                 return array (false, null);
 425:             }
 426:         }
 427: 
 428:         $uniqueId = uniqid ();
 429:         $widgetUniqueName = $widgetClass.'_'.$uniqueId;
 430:         while (true) {
 431:             if (!isset ($layout[$widgetUniqueName])) {
 432:                 break;
 433:             }
 434:             $uniqueId = uniqid ();
 435:             $widgetUniqueName = $widgetClass.'_'.$uniqueId;
 436:         }
 437: 
 438:         $layout[$widgetUniqueName] = array_merge (
 439:             array (
 440:                 'hidden' => false,
 441:                 'minimized' => false,
 442:             ), $widgetSettings);
 443: 
 444:         $profile->$widgetLayoutPropertyName = $layout;
 445: 
 446:         if ($profile->update ()) {
 447:             return array (true, $uniqueId);
 448:         } else {
 449:             return array (false, null);
 450:         }
 451:     }
 452: 
 453:     /**
 454:      * @param object $profile The profile model
 455:      * @param string $widgetClass 
 456:      * @param string $widgetUID 
 457:      * @param string $widgetLayoutName 
 458:      * @return bool true for success, false otherwise
 459:      */
 460:     public static function deleteSortableWidget (
 461:         $profile, $widgetClass, $widgetUID, $widgetLayoutName) {
 462: 
 463:         $widgetLayoutPropertyName = $widgetLayoutName . 'WidgetLayout';
 464:         $layout = $profile->$widgetLayoutPropertyName;
 465:         if ($widgetUID !== '')
 466:             $widgetKey = $widgetClass.'_'.$widgetUID;
 467:         else 
 468:             $widgetKey = $widgetClass;
 469: 
 470:         if (!isset ($layout[$widgetKey])) {
 471:             return false;
 472:         }
 473: 
 474:         if ($widgetUID === '') {
 475:             // default widgets don't actually get removed form the layout
 476:             $layout[$widgetKey]['softDeleted'] = true;
 477:         } else {
 478:             unset ($layout[$widgetKey]);
 479:         }
 480: 
 481:         $profile->$widgetLayoutPropertyName = $layout;
 482: 
 483:         if ($profile->update ()) {
 484:             return true;
 485:         } else {
 486:             return false;
 487:         }
 488:     }
 489: 
 490:     /**
 491:      * Sets the order of widgets specified in the widget layout JSON property of the Profile
 492:      * model
 493:      * @param object $profile The profile model 
 494:      * @param array $widgetOrder An array of strings where each string is a profile widget 
 495:      *  class name
 496:      * @param string $widgetType The type of widget that this is (profile)
 497:      * @returns boolean True for update success, false otherwise
 498:      */
 499:     public static function setSortOrder ($profile, $widgetOrder, $widgetType) {
 500:         $widgetLayoutName = $widgetType . 'WidgetLayout';
 501:         $layout = $profile->$widgetLayoutName;
 502:         $newLayout = array ();
 503: 
 504:         // remove entries from old layout in the specified order, pushing them onto the new layout
 505:         foreach ($widgetOrder as $widgetKey) {
 506:             if (in_array ($widgetKey, array_keys ($layout))) {
 507:                 $newLayout[$widgetKey] = $layout[$widgetKey];
 508:                 unset ($layout[$widgetKey]);
 509:             }
 510:         }
 511: 
 512:         // push any remaining widgets not specified in the widget order
 513:         foreach ($layout as $widgetClass => $settings) {
 514:             $newLayout[$widgetClass] = $layout[$widgetClass];
 515:         }
 516: 
 517:         $profile->$widgetLayoutName = $newLayout;
 518:         if ($profile->save ()) {
 519:             return true;
 520:         }
 521:         return false;
 522:     }
 523: 
 524:     /**
 525:      * Sets the value of a property, for the current widget class, of the widget layout JSON 
 526:      * object.
 527:      * @param object $profile The profile model
 528:      * @param string $key The name of the JSON property
 529:      * @param string $value The value the the JSON property will be set to
 530:      * @param string $widgetType The type of widget that this is (profile)
 531:      * @return boolean True for update success, false otherwise
 532:      * @deprecated use setJSONProperties instead
 533:      */
 534:     public static function setJSONProperty ($profile, $key, $value, $widgetType, $widgetUID) {
 535:         return self::setJSONProperties (
 536:             $profile, array ($key => $value), $widgetType, $widgetUID);
 537:     }
 538: 
 539:     /**
 540:      * Sets the values of a properties, for the current widget class, of the widget layout JSON 
 541:      * object.
 542:      * @param object $profile The profile model
 543:      * @param array $props
 544:      * @param string $widgetType The type of widget that this is (profile)
 545:      * @return boolean True for update success, false otherwise
 546:      */
 547:     public static function setJSONProperties ($profile, array $props, $widgetType, $widgetUID) {
 548:         $widgetLayoutName = $widgetType . 'WidgetLayout';
 549:         $widgetClass = get_called_class ();
 550: 
 551:         if ($widgetUID !== '')
 552:             $widgetKey = $widgetClass.'_'.$widgetUID;
 553:         else 
 554:             $widgetKey = $widgetClass;
 555: 
 556:         $layout = $profile->$widgetLayoutName;
 557:         $update = false;
 558:         foreach ($props as $key => $value) {
 559:             if (isset ($layout[$widgetKey])) {
 560:                 if (in_array ($key, array_keys ($layout[$widgetKey]))) {
 561:                     $layout[$widgetKey][$key] = $value;
 562:                     $update = true;
 563:                 }
 564:             }
 565:         }
 566:         if ($update) {
 567:             $profile->$widgetLayoutName = $layout;
 568:             if ($profile->update ()) {
 569:                 return true;
 570:             }
 571:         }
 572:         return false;
 573:     }
 574: 
 575:     /**
 576:      * Retrieves the value of a property, for the current widget class, of the widget layout 
 577:      * JSON object.
 578:      * @param object $profile The profile model
 579:      * @param string $key The name of the JSON property
 580:      * @param string $widgetType The type of widget that this is (profile)
 581:      * @return mixed null if the property cannot be retrieved, other the value of the property
 582:      */
 583:     public static function getJSONProperty ($profile, $key, $widgetType, $widgetUID) {
 584:         $properties = self::getJSONProperties($profile, $widgetType, $widgetUID);
 585:         if (isset($properties[$key])) {
 586:             return $properties[$key];
 587:         }
 588:         return null;
 589: 
 590: 
 591: 
 592:         // $widgetClass = get_called_class ();
 593:         // $widgetLayoutName = $widgetType . 'WidgetLayout';
 594: 
 595:         // if ($widgetUID !== '')
 596:         //     $widgetKey = $widgetClass.'_'.$widgetUID;
 597:         // else 
 598:         //     $widgetKey = $widgetClass;
 599: 
 600:         // $layout = $profile->$widgetLayoutName;
 601:         // if (isset ($layout[$widgetKey])) {
 602:         //     if (in_array ($key, array_keys ($layout[$widgetKey]))) {
 603:         //         return $layout[$widgetKey][$key];
 604:         //     }
 605:         // }
 606:         // return null;
 607:     }
 608: 
 609:     /**
 610:      * Retrieves the value of a property, for the current widget class, of the widget layout 
 611:      * JSON object.
 612:      * @param object $profile The profile model
 613:      * @param string $key The name of the JSON property
 614:      * @param string $widgetType The type of widget that this is (profile)
 615:      * @return mixed null if the property cannot be retrieved, other the value of the property
 616:      */
 617:     public static function getJSONProperties ($profile, $widgetType, $widgetUID) {
 618:         $widgetClass = get_called_class ();
 619:         $widgetLayoutName = $widgetType . 'WidgetLayout';
 620: 
 621:         if ($widgetUID !== '')
 622:             $widgetKey = $widgetClass.'_'.$widgetUID;
 623:         else 
 624:             $widgetKey = $widgetClass;
 625: 
 626:         $layout = $profile->$widgetLayoutName;
 627: 
 628:         if (isset ($layout[$widgetKey])) {
 629:             return $layout[$widgetKey];
 630:         }
 631:         return null;
 632:     }
 633: 
 634:     /**
 635:      * Used by renderWidgetContents to sort template elements
 636:      */
 637:     private static function compareOffset ($a, $b) {
 638:         return $a[1] > $b[1];
 639:     }
 640: 
 641:     /**
 642:      * @return string key which uniquely identifies this widget 
 643:      */
 644:     public function getWidgetKey () {
 645:         return get_called_class () . '_' . $this->widgetUID;
 646:     }
 647: 
 648:     /**
 649:      * Non-static wrapper around getJSONProperty which adds caching
 650:      * @param string $key The name of the JSON property
 651:      */
 652:     public function getWidgetProperty ($key) {
 653:         if (!isset ($this->_widgetProperties)) {
 654:             $this->getWidgetProperties ();
 655:         }
 656:         if (!isset ($this->_widgetProperties[$key])) {
 657:             return null;
 658:         }
 659:         return $this->_widgetProperties[$key];
 660:     }
 661: 
 662:     public function getWidgetProperties () {
 663:         if (!isset ($this->_widgetProperties)) {
 664:             $this->_widgetProperties = self::getJSONProperties (
 665:                 $this->profile, $this->widgetType, $this->widgetUID);
 666:         }
 667:         return $this->_widgetProperties;
 668:     }
 669: 
 670:     /**
 671:      * Non-static wrapper around setJSONProperties which caches the property value for 
 672:      * getWidgetProperty ()
 673:      * @param array $props
 674:      */
 675:     public function setWidgetProperties (array $props) {
 676:         if (self::setJSONProperties (
 677:             $this->profile, $props, $this->widgetType, $this->widgetUID)) {
 678: 
 679:             foreach ($props as $key => $value) {
 680:                 $this->_widgetProperties[$key] = $value;
 681:             }
 682:             return true;
 683:         }
 684:         return false;
 685:     }
 686: 
 687:     /**
 688:      * Non-static wrapper around setJSONProperty which caches the property value for 
 689:      * getWidgetProperty ()
 690:      * @param string $key The name of the JSON property
 691:      * @param string $value 
 692:      */
 693:     public function setWidgetProperty ($key, $value) {
 694:         if (self::setJSONProperties (
 695:             $this->profile, array ($key => $value), $this->widgetType, $this->widgetUID)) {
 696: 
 697:             $this->_widgetProperties[$key] = $value;
 698:             return true;
 699:         }
 700:         return false;
 701:     }
 702: 
 703:     /**
 704:      * @return string widget label 
 705:      */
 706:     public function getWidgetLabel () {
 707:         return Yii::t('app',$this->getWidgetProperty ('label'));
 708:     }
 709: 
 710:     public function getSharedViewFile () {
 711:         if (!isset ($this->_sharedViewFile)) {
 712:             $this->_sharedViewFile = self::getParentType ($this->widgetType) . 'Widget';
 713:         }
 714:         return $this->_sharedViewFile;
 715:     }
 716: 
 717:     public function setSharedViewFile ($sharedViewFile) {
 718:         $this->_sharedViewFile = $sharedViewFile;
 719:     }
 720: 
 721:     /**
 722:      * Magic getter. Returns this widget's packages. 
 723:      */
 724:     public function getPackages () {
 725:         if (!isset ($this->_packages)) {
 726:             $this->_packages = array_merge (parent::getPackages (), array (
 727:                 'auxlib' => array(
 728:                     'baseUrl' => Yii::app()->request->baseUrl,
 729:                     'js' => array(
 730:                         'js/auxlib.js',
 731:                     ),
 732:                 ),
 733:                 'SortableWidgetJS' => array(
 734:                     'baseUrl' => Yii::app()->request->baseUrl,
 735:                     'js' => array(
 736:                         'js/sortableWidgets/SortableWidget.js',
 737:                     ),
 738:                     'depends' => array ('auxlib', 'X2Widget')
 739:                 ),
 740:             ));
 741:         }
 742:         return $this->_packages;
 743:     }
 744: 
 745:     /**
 746:      * Magic setter
 747:      * @param array $packages
 748:      */
 749:     public function setPackages ($packages) {
 750:         $this->_packages = $packages;
 751:     }
 752: 
 753:     /**
 754:      * Add a package to the array of registered packages 
 755:      * @param array $package
 756:      */
 757:     public function addPackage ($package) {
 758:         $this->packages = array_merge ($this->packages, $package);
 759:     }
 760: 
 761:     public function getJSInstanceName () {
 762:         $widgetClass = get_called_class ();
 763:         return $widgetClass.$this->widgetUID;
 764:     }
 765: 
 766:     /**
 767:      * Magic getter. Returns this widget's setup script.
 768:      * @return string JS string which gets registered when widget content gets rendered
 769:      */
 770:     public function getSetupScript () {
 771:         if (!isset ($this->_setupScript)) {
 772:             $this->_setupScript = "
 773:                 $(function () {
 774:                     x2.".$this->getJSInstanceName ()." = new $this->sortableWidgetJSClass (".
 775:                         CJSON::encode ($this->getJSSortableWidgetParams ()).
 776:                     ");
 777:                 });
 778:             ";
 779:         }
 780:         return $this->_setupScript;
 781:     }
 782: 
 783:     /**
 784:      * Used to register all shared css before the widget container gets rendered.
 785:      */
 786:     public function registerSharedCss () {
 787:         $sharedCss = $this->sharedCss;
 788:         foreach ($sharedCss as $cssName => $css) {
 789:             Yii::app()->clientScript->registerCss($cssName, $css);
 790:         }
 791:         foreach ($this->sharedCssFileNames as $filename) {
 792:             Yii::app()->clientScript->registerCssFile(
 793:                 Yii::app()->theme->baseUrl.'/css/'.$filename); 
 794:         }
 795:     }
 796: 
 797:     /**
 798:      * Used to register all css before the widget gets rendered.
 799:      */
 800:     public function registerCss () {
 801:         $css = $this->css;
 802:         foreach ($css as $cssName => $css) {
 803:            Yii::app()->clientScript->registerCss($cssName, $css);
 804:         }
 805:     }
 806: 
 807:     /**
 808:      * Magic getter.
 809:      * Returns an array of parameters which should be passed to the view file associated with this
 810:      *  widget.
 811:      * @return array The value stored in $_viewFileParams 
 812:      */
 813:     public function getViewFileParams () {
 814:         if (!isset ($this->_viewFileParams)) {
 815:             $this->_viewFileParams = array (
 816:                 'isAjaxRequest' => $this->isAjaxRequest
 817:             );
 818:         }
 819:         return $this->_viewFileParams;
 820:     } 
 821: 
 822:     private $_errors = array ();
 823:     public function addError ($message) {
 824:         $this->_errors[] = $message;
 825:     }
 826: 
 827:     public function hasError () {
 828:         return count ($this->_errors);
 829:     }
 830: 
 831:     /**
 832:      * Renders widget components by parsing the template string and calling rendering methods
 833:      * for each template item. HTML contained in the template gets echoed out.
 834:      */
 835:     public function renderWidget () {
 836:         // don't render hidden widgets to prevent page load slow down
 837:         $hidden = self::getJSONProperty (
 838:             $this->profile, 'hidden', $this->widgetType, $this->widgetUID);
 839: 
 840:         if ($hidden !== null && $hidden) return;
 841: 
 842:         $this->registerCss ();
 843: 
 844:         // extract html strings and template strings from template
 845:         $itemMatches = array ();
 846:         $htmlMatches = array ();
 847:         // TODO: rename template property _template and add getTemplate method to base class
 848:         if (method_exists ($this, 'getTemplate')) {
 849:             $template = $this->getTemplate ();
 850:         } else {
 851:             $template = $this->template;
 852:         }
 853:         preg_match_all ("/(?:^([^{]+)\{)|(?:\}([^{]+)\{)|(?:\}([^{]+)$)/", $template, 
 854:             $htmlMatches, PREG_PATTERN_ORDER | PREG_OFFSET_CAPTURE);
 855:         preg_match_all ("/{([^}]+)}/", $template, $itemMatches,
 856:             PREG_PATTERN_ORDER | PREG_OFFSET_CAPTURE);
 857: 
 858:         $templateHTML = array ();
 859:         $templateItems = array ();
 860: 
 861:         // organize html string matches into a 2d array
 862:         for ($i = 1; $i < sizeof ($htmlMatches); ++$i) {
 863:             for ($j = 0; $j < sizeof ($htmlMatches[$i]); ++$j) {
 864:                 if (is_array ($htmlMatches[$i][$j]) && $htmlMatches[$i][$j][1] >= 0) {
 865:                     $templateHTML[] = array_merge ($htmlMatches[$i][$j], array ('html'));
 866:                 }
 867:             }
 868:         }
 869: 
 870:         // organize template string matches into a 2d array
 871:         for ($i = 0; $i < sizeof ($itemMatches[1]); ++$i) {
 872:             if (is_array ($itemMatches[1][$i]) && $itemMatches[1][$i][1] >= 0) {
 873:                 $templateItems[] = array_merge ($itemMatches[1][$i], array ('item'));
 874:             }
 875:         }
 876:         //AuxLib::debugLogR ($templateItems);
 877:         //AuxLib::debugLogR ($templateHTML);
 878: 
 879:         // merge the 2 arrays and sort them by string offset
 880:         $allTemplateItems = array_merge ($templateItems, $templateHTML);
 881:         usort ($allTemplateItems, array ('self', 'compareOffset'));
 882: 
 883:         //AuxLib::debugLogR ($allTemplateItems);
 884: 
 885:         // echo html, call functions corresponding to template items
 886:         for ($i = 0; $i < sizeof ($allTemplateItems); ++$i) {
 887:             if ($allTemplateItems[$i][2] == 'html') {
 888:                 echo $allTemplateItems[$i][0];
 889:             } else { // $allTemplateItems[$i][2] === 'item'
 890:                 $fnName = 'render' . ucfirst ($allTemplateItems[$i][0]); 
 891:                 if (method_exists ($this, $fnName)) {
 892:                     $this->$fnName ();
 893:                 }
 894:             }
 895:         }
 896:     }
 897: 
 898:     private $_minimized;
 899:     public function getMinimized () {
 900:         if (!isset ($this->_minimized)) {
 901:             $this->_minimized = self::getJSONProperty (
 902:                 $this->profile, 'minimized', $this->widgetType, $this->widgetUID);
 903:         }
 904:         return $this->_minimized;
 905:     }
 906: 
 907:     public function setMinimized ($minimized) {
 908:         $this->_minimized = $minimized;
 909:     }
 910: 
 911:     /**
 912:      * Renders widget contents contained in the view file pointed to by the viewFile property. 
 913:      * This gets called if {widgetContents} is contained in the template string.
 914:      */
 915:     public function renderWidgetContents () {
 916:         Yii::app()->clientScript->registerPackages ($this->packages);
 917: 
 918:         /*
 919:         If it's an ajax request, script must be placed at the end for it to be exectuted upon
 920:         ajax response. Otherwise, place script at POS_BEGIN to allow dependent script files to be
 921:         inserted afterwards.
 922:         */
 923:         Yii::app()->clientScript->registerScript (
 924:             get_called_class ().$this->widgetUID.'Script', $this->setupScript, 
 925:             ($this->isAjaxRequest ? CClientScript::POS_END: CClientScript::POS_BEGIN));
 926: 
 927:         $minimized = $this->getMinimized ();
 928:         echo "<div id='".get_called_class ()."-widget-content-container-".$this->widgetUID."'".
 929:             ($minimized ? " style='display: none;'" : '').">";
 930: 
 931:         if ($this->hasError ()) {
 932:             $this->renderErrors ();
 933:         } elseif ($this->viewFile) {
 934:             $this->render (
 935:                 'application.components.sortableWidget.views.'.$this->viewFile,
 936:                 $this->getViewFileParams ());
 937:         }
 938: 
 939:         echo "</div>";
 940:     }
 941: 
 942:     public function renderErrors () {
 943:         Yii::app()->controller->renderPartial (
 944:             'application.components.sortableWidget.views._widgetError', array (
 945:             'errors' => $this->_errors,
 946:         ));
 947:     }
 948: 
 949:     /**
 950:      * Renders the widget label saved in the profile JSON widget settings property
 951:      * This gets called if {widgetLabel} is contained in the template string.
 952:      */
 953:     public function renderWidgetLabel () {
 954:         $label = $this->getWidgetLabel ();
 955:         echo "<div class='widget-title'>".htmlspecialchars($label)."</div>";
 956:     }
 957: 
 958:     /**
 959:      * Renders the show/hide settings menu icon as well as the settings menu content 
 960:      */
 961:     public function renderSettingsMenu () {
 962:         $themeUrl = Yii::app()->theme->getBaseUrl();
 963:         $htmlStr = 
 964:             "<a href='#' class='widget-settings-button x2-icon-button' style='display:none;'>";
 965:         $htmlStr .= CHtml::tag(
 966:             'span', 
 967:             array (
 968:                 'title' => Yii::t('app', 'Show Widget Settings'),
 969:                 'class' => 'fa fa-cog fa-lg'
 970: 
 971:             ), ' ');
 972:         $htmlStr .= '</a>';
 973:         echo $htmlStr;
 974:         echo $this->settingsMenuContent;
 975:     }
 976: 
 977:     /**
 978:      * Renders a button which allows the user to minimize/maximize the widget.
 979:      * This gets called if {minimizeButton} is contained in the template string.
 980:      */
 981:     public function renderMinimizeButton () {
 982:         $themeUrl = Yii::app()->theme->getBaseUrl();
 983:         $htmlStr = 
 984:             "<a href='#' class='widget-minimize-button x2-icon-button' style='display:none;'>";
 985:         $minimized = self::getJSONProperty (
 986:             $this->profile, 'minimized', $this->widgetType, $this->widgetUID);
 987:         $htmlStr .= CHtml::openTag(
 988:             'span', 
 989:             array (
 990:                 'class' => 'fa fa-caret-left fa-lg',
 991:                 'title' => Yii::t('app', 'Maximize Widget'),
 992:                 'style' => ($minimized ? '': 'display: none;')
 993:             ));
 994: 
 995:         $htmlStr .= '</span>';
 996:         $htmlStr .= CHtml::openTag(
 997:             'span', 
 998:             array (
 999:                 'class' => 'fa fa-caret-down fa-lg',
1000:                 'title' => Yii::t('app', 'Minimize Widget'),
1001:                 'style' => ($minimized ? 'display: none;' : '')
1002:             ));
1003: 
1004:         $htmlStr .= '</span>';
1005:         $htmlStr .= '</a>';
1006:         echo $htmlStr;
1007:     }
1008: 
1009:     /**
1010:      * Renders a button which allows the user to hide/show the widget.
1011:      * This gets called if {closeButton} is contained in the template string.
1012:      */
1013:     public function renderCloseButton () {
1014:         $themeUrl = Yii::app()->theme->getBaseUrl();
1015:         echo "<a class='widget-close-button x2-icon-button' href='#' style='display:none;'>";
1016:         echo CHtml::tag('span',
1017:             array (
1018:                 'class' => 'fa fa-times fa-lg',
1019:                 'title' => Yii::t('app', 'Close Widget')
1020:             ), ' ');
1021:         echo "</a>";
1022:     }
1023: 
1024:     /**
1025:      * Render the widget container view
1026:      */
1027:     public function run () {
1028:         $hidden = self::getJSONProperty (
1029:             $this->profile, 'hidden', $this->widgetType, $this->widgetUID);
1030:         if ($hidden === null) $hidden = false;
1031:         $this->registerSharedCss ();
1032:         $this->render ('application.components.sortableWidget.views.'.$this->sharedViewFile,
1033:             array (
1034:                 'widgetClass' => get_called_class (),
1035:                 'profile' => $this->profile,
1036:                 'hidden' => $hidden,
1037:                 'widgetUID' => $this->widgetUID,
1038:             ));
1039:     }
1040: 
1041: 
1042:     /***********************************************************************
1043:     * Non-public instance methods 
1044:     ***********************************************************************/
1045: 
1046:     /**
1047:      * Override in child class. This content will be turned into a popup dropdown menu with the
1048:      * PopupDropdownMenu JS prototype.
1049:      */
1050:     protected function getSettingsMenuContent () {
1051:         $htmlStr = 
1052:             '<div class="widget-settings-menu-content" style="display:none;">
1053:                 <ul>'. 
1054:                     $this->getSettingsMenuContentEntries ().
1055:                 '</ul>
1056:             </div>';
1057:         $htmlStr .= $this->getSettingsMenuContentDialogs ();
1058:         return $htmlStr;
1059:     }
1060: 
1061: 
1062:     /**
1063:      * @return string HTML string containing settings menu options
1064:      */
1065:     protected function getSettingsMenuContentEntries () {
1066:         return ($this->relabelingEnabled ? 
1067:             '<li class="relabel-widget-button">'.
1068:                 X2Html::fa('fa-edit').
1069:                 Yii::t('app', 'Rename Widget').
1070:             '</li>' : '').
1071:         ($this->canBeDeleted ? 
1072:             '<li class="delete-widget-button">'.
1073:                 X2Html::fa('fa-trash').
1074:                 Yii::t('app', 'Delete Widget').
1075:             '</li>' : '');
1076:     }
1077: 
1078:     /**
1079:      * @return string HTML string containing dialog containers used by settings menu options
1080:      */
1081:     protected function getSettingsMenuContentDialogs () {
1082:         $htmlStr = '';
1083:         if ($this->relabelingEnabled) {
1084:             $htmlStr .= 
1085:                 '<div id="relabel-widget-dialog-'.$this->widgetUID.'" style="display: none;">
1086:                     <div>'.Yii::t('app', 'Enter a new name:').'</div>  
1087:                     <input class="new-widget-name">
1088:                 </div>';
1089:         }
1090:         if ($this->canBeDeleted) {
1091:             $htmlStr .= 
1092:                 '<div id="delete-widget-dialog-'.$this->widgetUID.'" style="display: none;">
1093:                     <div>'.
1094:                         Yii::t('app', 'Performing this action will cause this widget\'s settings '.
1095:                             'to be lost. This action cannot be undone.').
1096:                     '</div>  
1097:                 </div>';
1098:         }
1099:         return $htmlStr;
1100:     }
1101: 
1102:     /**
1103:      * Returns paths of shared css files relative to themes/x2engine/css. 
1104:      */
1105:     protected $_sharedCssFileNames;
1106:     public function getSharedCssFileNames () {
1107:         if (!isset ($this->_sharedCssFileNames)) {
1108:             $this->_sharedCssFileNames = array (
1109:                 'components/sortableWidget/SortableWidget.css',
1110:             );
1111:         }
1112:         return $this->_sharedCssFileNames;
1113:     }
1114: 
1115:     /**
1116:      * Magic getter. Returns this widget's shared css
1117:      * @return array key is the proposed name of the css string which should be passed as the first
1118:      *  argument to yii's registerCss. The value is the css string.
1119:      */
1120:     protected function getSharedCss () {
1121:         if (!isset ($this->_sharedCss)) {
1122:             $this->_sharedCss = array ();
1123:         }
1124:         return $this->_sharedCss;
1125:     }
1126: 
1127:     /**
1128:      * Magic getter. Returns this widget's css
1129:      * @return array key is the proposed name of the css string which should be passed as the first
1130:      *  argument to yii's registerCss. The value is the css string.
1131:      */
1132:     protected function getCss () {
1133:         if (!isset ($this->_css)) {
1134:             $this->_css = array ();
1135:         }
1136:         return $this->_css;
1137:     }
1138: 
1139:     /**
1140:      * @return array translations to pass to JS objects 
1141:      */
1142:     protected function getTranslations () {
1143:         if (!isset ($this->_translations )) {
1144:             $this->_translations = array ();
1145:             if ($this->relabelingEnabled) {
1146:                 $this->_translations = array_merge ($this->_translations, array (
1147:                     'Rename Widget' => Yii::t('app', 'Rename Widget'),
1148:                     'Cancel' => Yii::t('app', 'Cancel'),
1149:                     'Rename' => Yii::t('app', 'Rename'),
1150:                 ));
1151:             }
1152:             if ($this->canBeDeleted) {
1153:                 $this->_translations = array_merge ($this->_translations, array (
1154:                     'Cancel' => Yii::t('app', 'Cancel'),
1155:                     'Delete' => Yii::t('app', 'Delete'),
1156:                     'Are you sure you want to delete this widget?' => 
1157:                         Yii::t('app', 'Are you sure you want to delete this widget?'),
1158:                 ));
1159:             }
1160:         }
1161:         return $this->_translations;
1162:     }
1163: 
1164:     protected function getJSSortableWidgetParams () {
1165:         if (!isset ($this->_JSSortableWidgetParams)) {
1166:             $this->_JSSortableWidgetParams = array (
1167:                 'widgetClass'  => get_called_class (),
1168:                 'setPropertyUrl' => Yii::app()->controller->createUrl (
1169:                     '/profile/setWidgetSetting'),
1170:                 'cssSelectorPrefix' => $this->widgetType,
1171:                 'widgetType' => $this->widgetType,
1172:                 'widgetUID' => $this->widgetUID,
1173:                 'translations' => $this->getTranslations (),
1174:                 'deleteWidgetUrl' =>  Yii::app()->controller->createUrl (
1175:                     '/profile/deleteSortableWidget'),
1176:                 'hasError' => $this->hasError ()
1177:             );
1178:         }
1179:         return $this->_JSSortableWidgetParams;
1180:     }
1181: 
1182:     public function init () {
1183:         if (!isset ($this->namespace))
1184:             $this->namespace = $this->getWidgetKey ();
1185:         parent::init ();
1186:     }
1187: 
1188: }
1189: ?>
1190: 
X2CRM Documentation API documentation generated by ApiGen 2.8.0