PageRenderTime 52ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/app/protected/modules/zurmo/components/ZurmoBaseController.php

https://bitbucket.org/andreustimm/zurmo
PHP | 1313 lines | 1108 code | 68 blank | 137 comment | 96 complexity | 31752869e5170eb7df02d917abe8a0e7 MD5 | raw file
Possible License(s): BSD-3-Clause, GPL-2.0, LGPL-3.0, LGPL-2.1, BSD-2-Clause

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /*********************************************************************************
  3. * Zurmo is a customer relationship management program developed by
  4. * Zurmo, Inc. Copyright (C) 2015 Zurmo Inc.
  5. *
  6. * Zurmo 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 ZURMO, ZURMO DISCLAIMS THE WARRANTY
  11. * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
  12. *
  13. * Zurmo 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 Zurmo, Inc. with a mailing address at 27 North Wacker Drive
  24. * Suite 370 Chicago, IL 60606. or at email address contact@zurmo.com.
  25. *
  26. * The interactive user interfaces in original and modified 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 Zurmo
  32. * logo and Zurmo copyright notice. If the display of the logo is not reasonably
  33. * feasible for technical reasons, the Appropriate Legal Notices must display the words
  34. * "Copyright Zurmo Inc. 2015. All rights reserved".
  35. ********************************************************************************/
  36. abstract class ZurmoBaseController extends Controller
  37. {
  38. const RIGHTS_FILTER_PATH = 'application.modules.zurmo.controllers.filters.RightsControllerFilter';
  39. const REQUIRED_ATTRIBUTES_FILTER_PATH = 'application.modules.zurmo.controllers.filters.RequiredAttributesControllerFilter';
  40. const ADMIN_VIEW_MOBILE_CHECK_FILTER_PATH = 'application.modules.zurmo.controllers.filters.AdminViewMobileCheckControllerFilter';
  41. const ROOT_USER_ACCESS_FILTER_PATH = 'application.modules.zurmo.controllers.filters.RootUserAccessControllerFilter';
  42. public function filters()
  43. {
  44. $moduleClassName = $this->resolveModuleClassNameForFilters();
  45. $filters = array();
  46. if (is_subclass_of($moduleClassName, 'SecurableModule'))
  47. {
  48. $filters[] = array(
  49. self::getRightsFilterPath(),
  50. 'moduleClassName' => $moduleClassName,
  51. 'rightName' => $moduleClassName::getAccessRight(),
  52. );
  53. $filters[] = array(
  54. self::getRightsFilterPath() . ' + create, createFromRelation, inlineCreateSave, copy, modalCreate',
  55. 'moduleClassName' => $moduleClassName,
  56. 'rightName' => $moduleClassName::getCreateRight(),
  57. );
  58. $filters[] = array(
  59. self::getRightsFilterPath() . ' + delete',
  60. 'moduleClassName' => $moduleClassName,
  61. 'rightName' => $moduleClassName::getDeleteRight(),
  62. );
  63. }
  64. $filters[] = array(
  65. self::getRightsFilterPath() . ' + massEdit, massEditProgressSave',
  66. 'moduleClassName' => 'ZurmoModule',
  67. 'rightName' => ZurmoModule::RIGHT_BULK_WRITE,
  68. );
  69. $filters['RIGHT_BULK_DELETE'] = array(
  70. self::getRightsFilterPath() . ' + massDelete, massDeleteProgress',
  71. 'moduleClassName' => 'ZurmoModule',
  72. 'rightName' => ZurmoModule::RIGHT_BULK_DELETE,
  73. );
  74. return $filters;
  75. }
  76. public function resolveModuleClassNameForFilters()
  77. {
  78. return get_class($this->getModule());
  79. }
  80. public function __construct($id, $module = null)
  81. {
  82. parent::__construct($id, $module);
  83. }
  84. public static function getRightsFilterPath()
  85. {
  86. return static::RIGHTS_FILTER_PATH;
  87. }
  88. protected function makeActionBarSearchAndListView($searchModel, $dataProvider,
  89. $actionBarViewClassName = 'SecuredActionBarForSearchAndListView',
  90. $viewPrefixName = null, $activeActionElementType = null,
  91. IntroView $introView = null)
  92. {
  93. assert('is_string($actionBarViewClassName)');
  94. assert('is_string($viewPrefixName) || $viewPrefixName == null');
  95. assert('is_string($activeActionElementType) || $activeActionElementType == null');
  96. if ($viewPrefixName == null)
  97. {
  98. $viewPrefixName = $this->getModule()->getPluralCamelCasedName();
  99. }
  100. $listModel = $searchModel->getModel();
  101. return new ActionBarSearchAndListView(
  102. $this->getId(),
  103. $this->getModule()->getId(),
  104. $searchModel,
  105. $listModel,
  106. $viewPrefixName,
  107. $dataProvider,
  108. GetUtil::resolveSelectedIdsFromGet(),
  109. $actionBarViewClassName,
  110. $activeActionElementType,
  111. $introView
  112. );
  113. }
  114. protected function makeActionBarAndListView($listModel, $dataProvider, $actionBarViewClassName = 'ActionBarForSearchAndListView',
  115. $viewPrefixName = null, $activeActionElementType = null)
  116. {
  117. assert('is_string($actionBarViewClassName)');
  118. assert('is_string($viewPrefixName) || $viewPrefixName == null');
  119. assert('is_string($activeActionElementType) || $activeActionElementType == null');
  120. if ($viewPrefixName == null)
  121. {
  122. $viewPrefixName = $this->getModule()->getPluralCamelCasedName();
  123. }
  124. return new ActionBarAndListView(
  125. $this->getId(),
  126. $this->getModule()->getId(),
  127. $listModel,
  128. $viewPrefixName,
  129. $dataProvider,
  130. GetUtil::resolveSelectedIdsFromGet(),
  131. $actionBarViewClassName,
  132. $activeActionElementType
  133. );
  134. }
  135. protected function makeListView(SearchForm $searchForm, $dataProvider, $listViewClassName = null)
  136. {
  137. assert('is_string($listViewClassName) || $listViewClassName == null');
  138. $listModel = $searchForm->getModel();
  139. if ($listViewClassName == null)
  140. {
  141. $listViewClassName = $this->getModule()->getPluralCamelCasedName() . 'ListView';
  142. }
  143. $listView = new $listViewClassName(
  144. $this->getId(),
  145. $this->getModule()->getId(),
  146. get_class($listModel),
  147. $dataProvider,
  148. GetUtil::resolveSelectedIdsFromGet(),
  149. null,
  150. array(),
  151. $searchForm->getListAttributesSelector(),
  152. $searchForm->getKanbanBoard());
  153. return $listView;
  154. }
  155. protected function resolveSearchDataProvider(
  156. $searchModel,
  157. $pageSize,
  158. $stateMetadataAdapterClassName = null,
  159. $stickySearchKey = null,
  160. $setSticky = true)
  161. {
  162. assert('$searchModel instanceof RedBeanModel || $searchModel instanceof ModelForm');
  163. assert('$stickySearchKey == null || is_string($stickySearchKey)');
  164. assert('is_bool($setSticky)');
  165. $listModelClassName = get_class($searchModel->getModel());
  166. static::resolveToTriggerOnSearchEvents($listModelClassName);
  167. $this->resolveKanbanBoardIsActiveByGet($searchModel);
  168. $dataCollection = $this->makeDataCollectionAndResolveSavedSearch($searchModel, $stickySearchKey, $setSticky);
  169. $pageSize = $this->resolvePageSizeForKanbanBoard($searchModel, $pageSize);
  170. $dataProvider = $this->makeRedBeanDataProviderByDataCollection($searchModel, $pageSize,
  171. $stateMetadataAdapterClassName, $dataCollection);
  172. return $dataProvider;
  173. }
  174. /**
  175. * @param $searchModel
  176. */
  177. private function resolveKanbanBoardIsActiveByGet($searchModel)
  178. {
  179. if (!$searchModel instanceof SearchForm || $searchModel->getKanbanBoard() == null)
  180. {
  181. return;
  182. }
  183. if (isset($_GET['kanbanBoard']) && $_GET['kanbanBoard'] && !Yii::app()->userInterface->isMobile())
  184. {
  185. $searchModel->getKanbanBoard()->setIsActive();
  186. }
  187. elseif (isset($_GET['kanbanBoard']) && !$_GET['kanbanBoard'])
  188. {
  189. $searchModel->getKanbanBoard()->setIsNotActive();
  190. $searchModel->getKanbanBoard()->setClearSticky();
  191. }
  192. elseif (Yii::app()->userInterface->isMobile())
  193. {
  194. $searchModel->getKanbanBoard()->setIsNotActive();
  195. }
  196. }
  197. /**
  198. * @param $searchModel
  199. * @param $pageSize
  200. * @return int
  201. */
  202. private function resolvePageSizeForKanbanBoard($searchModel, $pageSize)
  203. {
  204. if (!$searchModel instanceof SearchForm)
  205. {
  206. return $pageSize;
  207. }
  208. if ($searchModel->getKanbanBoard() !== null && $searchModel->getKanbanBoard()->getIsActive())
  209. {
  210. $pageSize = KanbanBoardExtendedGridView::resolvePageSizeForMaxCount();
  211. }
  212. return $pageSize;
  213. }
  214. private function makeDataCollectionAndResolveSavedSearch($searchModel, $stickySearchKey = null, $setSticky = true)
  215. {
  216. $dataCollection = new SearchAttributesDataCollection($searchModel);
  217. if ($searchModel instanceof SavedDynamicSearchForm)
  218. {
  219. $getData = GetUtil::getData();
  220. if ($stickySearchKey != null && isset($getData['clearingSearch']) && $getData['clearingSearch'])
  221. {
  222. StickySearchUtil::clearDataByKey($stickySearchKey);
  223. }
  224. if ($stickySearchKey != null && null != $stickySearchData = StickySearchUtil::getDataByKey($stickySearchKey))
  225. {
  226. SavedSearchUtil::resolveSearchFormByStickyDataAndModel($stickySearchData, $searchModel);
  227. SavedSearchUtil::resolveSearchFormByStickySortData($getData, $searchModel, $stickySearchData);
  228. SearchUtil::resolveSearchFormByStickyFilterByStarredData($getData, $searchModel, $stickySearchData);
  229. SearchUtil::resolveSearchFormByStickyFilteredByData($getData, $searchModel, $stickySearchData);
  230. SearchUtil::resolveSearchFormByStickyFilteredByData($getData, $searchModel, $stickySearchData);
  231. $dataCollection = new SavedSearchAttributesDataCollection($searchModel);
  232. }
  233. else
  234. {
  235. SavedSearchUtil::resolveSearchFormByData($getData, $searchModel);
  236. if ($searchModel->savedSearchId != null)
  237. {
  238. $dataCollection = new SavedSearchAttributesDataCollection($searchModel);
  239. }
  240. }
  241. if ($stickySearchKey != null && ($setSticky ||
  242. ($searchModel->getKanbanBoard() != null && $searchModel->getKanbanBoard()->getClearSticky())))
  243. {
  244. if ($stickySearchData == null)
  245. {
  246. $stickySearchData = array();
  247. }
  248. SavedSearchUtil::setDataByKeyAndDataCollection($stickySearchKey, $dataCollection, $stickySearchData);
  249. }
  250. $searchModel->loadSavedSearchUrl = Yii::app()->createUrl($this->getModule()->getId() . '/' . $this->getId() . '/list/');
  251. }
  252. return $dataCollection;
  253. }
  254. protected function resolveToTriggerOnSearchEvents($listModelClassName)
  255. {
  256. $pageVariableName = $listModelClassName . '_page';
  257. if (isset($_GET[$pageVariableName]) && $_GET[$pageVariableName] == null)
  258. {
  259. Yii::app()->gameHelper->triggerSearchModelsEvent($listModelClassName);
  260. }
  261. }
  262. protected function getDataProviderByResolvingSelectAllFromGet(
  263. $searchModel,
  264. $pageSize,
  265. $userId,
  266. $stateMetadataAdapterClassName = null,
  267. $stickySearchKey = null
  268. )
  269. {
  270. assert('$searchModel instanceof RedBeanModel || $searchModel instanceof ModelForm');
  271. assert('is_string($stickySearchKey) || $stickySearchKey == null');
  272. if ($_GET['selectAll'])
  273. {
  274. if (!isset($_GET[get_class($searchModel)]) && $stickySearchKey != null)
  275. {
  276. $resolvedStickySearchKey = $stickySearchKey;
  277. }
  278. else
  279. {
  280. $resolvedStickySearchKey = null;
  281. }
  282. return $this->resolveSearchDataProvider(
  283. $searchModel,
  284. $pageSize,
  285. $stateMetadataAdapterClassName,
  286. $resolvedStickySearchKey);
  287. }
  288. else
  289. {
  290. return null;
  291. }
  292. }
  293. /**
  294. * This method is called after a mass edit form is first submitted.
  295. * It is called from the actionMassEdit.
  296. * @see actionMassEdit in the module default controllers.
  297. */
  298. protected function processMassEdit(
  299. $pageSize,
  300. $activeAttributes,
  301. $selectedRecordCount,
  302. $pageViewClassName,
  303. $listModel,
  304. $title,
  305. $dataProvider = null
  306. )
  307. {
  308. // TODO: @Shoaibi/@Jason: Low: Deprecated
  309. // trigger_error('Deprecated');
  310. assert('$dataProvider == null || $dataProvider instanceof CDataProvider');
  311. $modelClassName = get_class($listModel);
  312. $selectedRecordCount = static::getSelectedRecordCountByResolvingSelectAllFromGet($dataProvider);
  313. if (isset($_POST[$modelClassName]))
  314. {
  315. PostUtil::sanitizePostForSavingMassEdit($modelClassName);
  316. //Generically test that the changes are valid before attempting to save on each model.
  317. $sanitizedPostData = PostUtil::sanitizePostByDesignerTypeForSavingModel(new $modelClassName(false), $_POST[$modelClassName]);
  318. $sanitizedOwnerPostData = PostUtil::sanitizePostDataToJustHavingElementForSavingModel($sanitizedPostData, 'owner');
  319. $sanitizedPostDataWithoutOwner = PostUtil::removeElementFromPostDataForSavingModel($sanitizedPostData, 'owner');
  320. $massEditPostDataWithoutOwner = PostUtil::removeElementFromPostDataForSavingModel($_POST['MassEdit'], 'owner');
  321. $listModel->setAttributes($sanitizedPostDataWithoutOwner);
  322. if ($listModel->validate(array_keys($massEditPostDataWithoutOwner)))
  323. {
  324. $passedOwnerValidation = true;
  325. if ($sanitizedOwnerPostData != null)
  326. {
  327. $listModel->setAttributes($sanitizedOwnerPostData);
  328. $passedOwnerValidation = $listModel->validate(array('owner'));
  329. }
  330. if ($passedOwnerValidation)
  331. {
  332. MassEditInsufficientPermissionSkipSavingUtil::clear($modelClassName);
  333. Yii::app()->gameHelper->triggerMassEditEvent(get_class($listModel));
  334. $this->saveMassEdit(
  335. get_class($listModel),
  336. $modelClassName,
  337. $selectedRecordCount,
  338. $dataProvider,
  339. $_GET[$modelClassName . '_page'],
  340. $pageSize
  341. );
  342. //cancel diminish of save scoring
  343. if ($selectedRecordCount > $pageSize)
  344. {
  345. $view = new $pageViewClassName(ZurmoDefaultViewUtil::
  346. makeStandardViewForCurrentUser($this,
  347. $this->makeMassEditProgressView(
  348. $listModel,
  349. 1,
  350. $selectedRecordCount,
  351. 1,
  352. $pageSize,
  353. $title,
  354. null)
  355. ));
  356. echo $view->render();
  357. Yii::app()->end(0, false);
  358. }
  359. else
  360. {
  361. $skipCount = MassEditInsufficientPermissionSkipSavingUtil::getCount($modelClassName);
  362. $successfulCount = MassEditInsufficientPermissionSkipSavingUtil::resolveSuccessfulCountAgainstSkipCount(
  363. $selectedRecordCount, $skipCount);
  364. MassEditInsufficientPermissionSkipSavingUtil::clear($modelClassName);
  365. $notificationContent = Zurmo::t('ZurmoModule', 'Successfully updated') . ' ' .
  366. $successfulCount . ' ' .
  367. LabelUtil::getUncapitalizedRecordLabelByCount($successfulCount) .
  368. '.';
  369. if ($skipCount > 0)
  370. {
  371. $notificationContent .= ' ' .
  372. MassEditInsufficientPermissionSkipSavingUtil::getSkipCountMessageContentByModelClassName(
  373. $skipCount, $modelClassName);
  374. }
  375. Yii::app()->user->setFlash('notification', $notificationContent);
  376. $this->redirect(array('default/'));
  377. Yii::app()->end(0, false);
  378. }
  379. }
  380. }
  381. }
  382. return $listModel;
  383. }
  384. /**
  385. * Called only during a mulitple phase save from mass edit. This occurs if the quantity of models to save
  386. * is greater than the pageSize. This signals a save that must be conducted in phases where each phase
  387. * updates a quantity of models no greater than the page size.
  388. */
  389. protected function processMassEditProgressSave(
  390. $modelClassName,
  391. $pageSize,
  392. $title,
  393. $dataProvider = null)
  394. {
  395. // TODO: @Shoaibi/@Jason: Low: Deprecated
  396. // trigger_error('Deprecated');
  397. assert('$dataProvider == null || $dataProvider instanceof CDataProvider');
  398. $listModel = new $modelClassName(false);
  399. $selectedRecordCount = static::getSelectedRecordCountByResolvingSelectAllFromGet($dataProvider);
  400. PostUtil::sanitizePostForSavingMassEdit($modelClassName);
  401. $this->saveMassEdit(
  402. get_class($listModel),
  403. $modelClassName,
  404. $selectedRecordCount,
  405. $dataProvider,
  406. $_GET[$modelClassName . '_page'],
  407. $pageSize
  408. );
  409. $view = $this->makeMassEditProgressView(
  410. $listModel,
  411. $_GET[$modelClassName . '_page'],
  412. $selectedRecordCount,
  413. $this->getMassEditProgressStartFromGet(
  414. $modelClassName,
  415. $pageSize
  416. ),
  417. $pageSize,
  418. $title,
  419. MassEditInsufficientPermissionSkipSavingUtil::getCount($modelClassName)
  420. );
  421. echo $view->renderRefreshJSONScript();
  422. }
  423. protected function makeMassEditProgressView(
  424. $model,
  425. $page,
  426. $selectedRecordCount,
  427. $start,
  428. $pageSize,
  429. $title,
  430. $skipCount)
  431. {
  432. // TODO: @Shoaibi/@Jason: Low: Deprecated
  433. // trigger_error('Deprecated');
  434. assert('$skipCount == null || is_int($skipCount)');
  435. return new MassEditProgressView(
  436. $this->getId(),
  437. $this->getModule()->getId(),
  438. $model,
  439. $selectedRecordCount,
  440. $start,
  441. $pageSize,
  442. $page,
  443. 'massEditProgressSave',
  444. $title,
  445. $skipCount
  446. );
  447. }
  448. /**
  449. * Called either from a mass edit save, or a mass edit progress save.
  450. */
  451. protected function saveMassEdit($modelClassName, $postVariableName, $selectedRecordCount, $dataProvider, $page, $pageSize)
  452. {
  453. // TODO: @Shoaibi/@Jason: Low: Deprecated
  454. // trigger_error('Deprecated');
  455. Yii::app()->gameHelper->muteScoringModelsOnSave();
  456. $modelsToSave = $this->getModelsToSave($modelClassName, $dataProvider, $selectedRecordCount, $page, $pageSize);
  457. foreach ($modelsToSave as $modelToSave)
  458. {
  459. if (ControllerSecurityUtil::doesCurrentUserHavePermissionOnSecurableItem($modelToSave, Permission::WRITE))
  460. {
  461. $sanitizedPostData = PostUtil::sanitizePostByDesignerTypeForSavingModel($modelToSave, $_POST[$modelClassName]);
  462. $sanitizedOwnerPostData = PostUtil::sanitizePostDataToJustHavingElementForSavingModel($sanitizedPostData, 'owner');
  463. $sanitizedPostDataWithoutOwner = PostUtil::removeElementFromPostDataForSavingModel($sanitizedPostData, 'owner');
  464. $modelToSave->setAttributes($sanitizedPostDataWithoutOwner);
  465. if ($sanitizedOwnerPostData != null)
  466. {
  467. $modelToSave->setAttributes($sanitizedOwnerPostData);
  468. }
  469. $modelToSave->save(false);
  470. }
  471. else
  472. {
  473. MassEditInsufficientPermissionSkipSavingUtil::setByModelIdAndName(
  474. $modelClassName, $modelToSave->id, $modelToSave->name);
  475. }
  476. }
  477. Yii::app()->gameHelper->unmuteScoringModelsOnSave();
  478. }
  479. /**
  480. * This method is called after a mass delete form is first submitted.
  481. * It is called from the actionMassDelete.
  482. * @see actionMassDelete in the module default controllers.
  483. */
  484. protected function processMassDelete(
  485. $pageSize,
  486. $activeAttributes,
  487. $selectedRecordCount,
  488. $pageViewClassName,
  489. $listModel,
  490. $title,
  491. $dataProvider = null,
  492. $redirectUrl = null
  493. )
  494. {
  495. // TODO: @Shoaibi/@Jason: Low: Deprecated
  496. // trigger_error('Deprecated');
  497. assert('$dataProvider == null || $dataProvider instanceof CDataProvider');
  498. $modelClassName = get_class($listModel);
  499. $selectedRecordCount = static::getSelectedRecordCountByResolvingSelectAllFromGet($dataProvider);
  500. if (isset($_POST['selectedRecordCount']))
  501. {
  502. $this->doMassDelete(
  503. get_class($listModel),
  504. $modelClassName,
  505. $selectedRecordCount,
  506. $dataProvider,
  507. $_GET[$modelClassName . '_page'],
  508. $pageSize
  509. );
  510. // Cancel diminish of save scoring
  511. if ($selectedRecordCount > $pageSize)
  512. {
  513. $view = new $pageViewClassName( ZurmoDefaultViewUtil::
  514. makeStandardViewForCurrentUser($this,
  515. $this->makeMassDeleteProgressView(
  516. $listModel,
  517. 1,
  518. $selectedRecordCount,
  519. 1,
  520. $pageSize,
  521. $title,
  522. null)
  523. ));
  524. echo $view->render();
  525. Yii::app()->end(0, false);
  526. }
  527. else
  528. {
  529. $skipCount = MassDeleteInsufficientPermissionSkipSavingUtil::getCount($modelClassName);
  530. $successfulCount = MassDeleteInsufficientPermissionSkipSavingUtil::resolveSuccessfulCountAgainstSkipCount(
  531. $selectedRecordCount, $skipCount);
  532. MassDeleteInsufficientPermissionSkipSavingUtil::clear($modelClassName);
  533. $notificationContent = $successfulCount . ' ' .
  534. LabelUtil::getUncapitalizedRecordLabelByCount($successfulCount) .
  535. ' ' . Zurmo::t('Core', 'successfully deleted') . '.';
  536. if ($skipCount > 0)
  537. {
  538. $notificationContent .= ' ' .
  539. MassDeleteInsufficientPermissionSkipSavingUtil::getSkipCountMessageContentByModelClassName(
  540. $skipCount, $modelClassName);
  541. }
  542. Yii::app()->user->setFlash('notification', $notificationContent);
  543. if ($redirectUrl === null)
  544. {
  545. $this->redirect(array('default/'));
  546. }
  547. else
  548. {
  549. $this->redirect($redirectUrl);
  550. }
  551. Yii::app()->end(0, false);
  552. }
  553. }
  554. return $listModel;
  555. }
  556. protected function processMassDeleteProgress(
  557. $modelClassName,
  558. $pageSize,
  559. $title,
  560. $dataProvider = null)
  561. {
  562. // TODO: @Shoaibi/@Jason: Low: Deprecated
  563. // trigger_error('Deprecated');
  564. assert('$dataProvider == null || $dataProvider instanceof CDataProvider');
  565. $listModel = new $modelClassName(false);
  566. $postData = PostUtil::getData();
  567. $selectedRecordCount = ArrayUtil::getArrayValue($postData, 'selectedRecordCount');
  568. $this->doMassDelete(
  569. get_class($listModel),
  570. $modelClassName,
  571. $selectedRecordCount,
  572. $dataProvider,
  573. $_GET[$modelClassName . '_page'],
  574. $pageSize
  575. );
  576. $view = $this->makeMassDeleteProgressView(
  577. $listModel,
  578. $_GET[$modelClassName . '_page'],
  579. $selectedRecordCount,
  580. $this->getMassDeleteProgressStartFromGet(
  581. $modelClassName,
  582. $pageSize
  583. ),
  584. $pageSize,
  585. $title,
  586. MassDeleteInsufficientPermissionSkipSavingUtil::getCount($modelClassName)
  587. );
  588. echo $view->renderRefreshJSONScript();
  589. }
  590. protected function makeMassDeleteProgressView(
  591. $model,
  592. $page,
  593. $selectedRecordCount,
  594. $start,
  595. $pageSize,
  596. $title,
  597. $skipCount)
  598. {
  599. // TODO: @Shoaibi/@Jason: Low: Deprecated
  600. // trigger_error('Deprecated');
  601. assert('$skipCount == null || is_int($skipCount)');
  602. return new MassDeleteProgressView(
  603. $this->getId(),
  604. $this->getModule()->getId(),
  605. $model,
  606. $selectedRecordCount,
  607. $start,
  608. $pageSize,
  609. $page,
  610. 'massDeleteProgress',
  611. $title,
  612. $skipCount
  613. );
  614. }
  615. protected function doMassDelete($modelClassName, $postVariableName, $selectedRecordCount, $dataProvider, $page, $pageSize)
  616. {
  617. // TODO: @Shoaibi/@Jason: Low: Deprecated
  618. // trigger_error('Deprecated');
  619. Yii::app()->gameHelper->muteScoringModelsOnDelete();
  620. $modelsToDelete = $this->getModelsToDelete($modelClassName, $dataProvider, $selectedRecordCount, $page, $pageSize);
  621. foreach ($modelsToDelete as $modelToDelete)
  622. {
  623. if (ControllerSecurityUtil::doesCurrentUserHavePermissionOnSecurableItem($modelToDelete, Permission::DELETE))
  624. {
  625. $modelToDelete->delete(false);
  626. }
  627. else
  628. {
  629. MassDeleteInsufficientPermissionSkipSavingUtil::setByModelIdAndName(
  630. $modelClassName, $modelToDelete->id, $modelToDelete->name);
  631. }
  632. }
  633. Yii::app()->gameHelper->unmuteScoringModelsOnDelete();
  634. }
  635. protected static function resolvePageValueForMassAction($modelClassName)
  636. {
  637. // TODO: @Shoaibi/@Jason: Low: Candidate for MassActionController
  638. return intval(Yii::app()->request->getQuery($modelClassName . '_page'));
  639. }
  640. protected function resolveReturnUrlForMassAction()
  641. {
  642. // TODO: @Shoaibi/@Jason: Low: Candidate for MassActionController
  643. return $this->createUrl('/' . $this->getModule()->getId() . '/' . $this->getId() . '/');
  644. }
  645. protected static function resolveViewIdByMassActionId($actionId, $returnProgressViewName, $moduleName = null)
  646. {
  647. // TODO: @Shoaibi/@Jason: Low: Candidate for MassActionController
  648. if (static::applyGenericViewIdGenerationRules($actionId)) // decides when to use generic rules to build ViewId
  649. {
  650. $viewNameSuffix = (!$returnProgressViewName)? 'View': 'ProgressView';
  651. $viewNamePrefix = static::resolveMassActionId($actionId, true);
  652. if (MassActionUtil::isMassEditLikeAction($actionId))
  653. {
  654. $viewNamePrefix = $moduleName . $viewNamePrefix;
  655. }
  656. return $viewNamePrefix . $viewNameSuffix;
  657. }
  658. else
  659. {
  660. throw new NotSupportedException();
  661. }
  662. }
  663. protected static function applyGenericViewIdGenerationRules($actionId)
  664. {
  665. // TODO: @Shoaibi/@Jason: Low: Candidate for MassActionController
  666. // overridden in Contacts/DefaultController
  667. return MassActionUtil::isMassEditOrDeleteLikeAction($actionId);
  668. }
  669. protected static function resolveTitleByMassActionId($actionId)
  670. {
  671. // TODO: @Shoaibi/@Jason: Low: Candidate for MassActionController
  672. if (MassActionUtil::isMassDeleteLikeAction($actionId))
  673. {
  674. return Zurmo::t('Core', 'Mass Delete');
  675. }
  676. elseif (MassActionUtil::isMassEditLikeAction($actionId))
  677. {
  678. return Zurmo::t('Core', 'Mass Update');
  679. }
  680. else
  681. {
  682. throw new NotSupportedException();
  683. }
  684. }
  685. /**
  686. * Check if form is posted. If form is posted attempt to save. If save is complete, confirm the current
  687. * user can still read the model. If not, then redirect the user to the index action for the module.
  688. */
  689. protected function attemptToSaveModelFromPost($model, $redirectUrlParams = null, $redirect = true, $returnOnValidate = false)
  690. {
  691. assert('$redirectUrlParams == null || is_array($redirectUrlParams) || is_string($redirectUrlParams)');
  692. $savedSuccessfully = false;
  693. $modelToStringValue = null;
  694. $postVariableName = get_class($model);
  695. if (isset($_POST[$postVariableName]))
  696. {
  697. $postData = $_POST[$postVariableName];
  698. $controllerUtil = static::getZurmoControllerUtil();
  699. $model = $controllerUtil->saveModelFromPost($postData, $model, $savedSuccessfully,
  700. $modelToStringValue, $returnOnValidate);
  701. }
  702. if ($savedSuccessfully && $redirect)
  703. {
  704. $this->actionAfterSuccessfulModelSave($model, $modelToStringValue, $redirectUrlParams);
  705. }
  706. return $model;
  707. }
  708. protected static function getZurmoControllerUtil()
  709. {
  710. return new ZurmoControllerUtil();
  711. }
  712. protected function actionAfterSuccessfulModelSave($model, $modelToStringValue, $redirectUrlParams = null)
  713. {
  714. assert('is_string($modelToStringValue)');
  715. assert('$redirectUrlParams == null || is_array($redirectUrlParams) || is_string($redirectUrlParams)');
  716. if (ControllerSecurityUtil::doesCurrentUserHavePermissionOnSecurableItem($model, Permission::READ))
  717. {
  718. if ($model instanceof RedBeanModel)
  719. {
  720. $this->beforeRedirect($model);
  721. }
  722. $this->redirectAfterSaveModel($model->id, $redirectUrlParams);
  723. }
  724. else
  725. {
  726. $notificationContent = Zurmo::t('ZurmoModule', 'You no longer have permissions to access {modelName}.',
  727. array('{modelName}' => $modelToStringValue)
  728. );
  729. Yii::app()->user->setFlash('notification', $notificationContent);
  730. $this->redirect(array($this->getId() . '/index'));
  731. }
  732. }
  733. protected function redirectAfterSaveModel($modelId, $urlParams = null)
  734. {
  735. if ($urlParams == null)
  736. {
  737. $urlParams = array($this->getId() . '/details', 'id' => $modelId);
  738. }
  739. $this->redirect($urlParams);
  740. }
  741. /**
  742. * @param string $modelClassName
  743. * @param int $id
  744. * @return mixed
  745. */
  746. protected static function getModelAndCatchNotFoundAndDisplayError($modelClassName, $id)
  747. {
  748. assert('is_string($modelClassName)');
  749. assert('is_int($id)');
  750. try
  751. {
  752. return $modelClassName::getById($id);
  753. }
  754. catch (NotFoundException $e)
  755. {
  756. $messageContent = Zurmo::t('ZurmoModule', 'The record you are trying to access does not exist.');
  757. $messageView = new ModelNotFoundView($messageContent);
  758. $view = new ModelNotFoundPageView($messageView);
  759. echo $view->render();
  760. Yii::app()->end(0, false);
  761. }
  762. }
  763. protected function triggerMassAction($modelClassName, $searchForm, $pageView, $title, $searchView = null,
  764. $stateMetadataAdapterClassName = null, $useModuleClassNameForItemLabel = true)
  765. {
  766. // TODO: @Shoaibi/@Jason: Low: Candidate for MassActionController
  767. $actionId = $this->getAction()->getId();
  768. $pageSize = static::resolvePageSizeByMassActionId($actionId);
  769. $model = new $modelClassName(false);
  770. $dataProvider = $this->getDataProviderByResolvingSelectAllFromGet(
  771. new $searchForm($model),
  772. $pageSize,
  773. Yii::app()->user->userModel->id,
  774. $stateMetadataAdapterClassName,
  775. $searchView
  776. );
  777. static::resolveOffsetForDataProvider($dataProvider, $actionId);
  778. if (MassActionUtil::isMassProgressLikeAction($actionId))
  779. {
  780. $this->massActionProgress($model, $pageSize, $title, $actionId, $dataProvider);
  781. }
  782. else
  783. {
  784. $this->massAction($model, $pageSize, $title, $pageView, $actionId, $dataProvider, $useModuleClassNameForItemLabel);
  785. }
  786. }
  787. protected function massActionProgress($model, $pageSize, $title, $actionId, $dataProvider)
  788. {
  789. // TODO: @Shoaibi/@Jason: Low: Candidate for MassActionController
  790. $this->processMassActionProgress(
  791. $model,
  792. $pageSize,
  793. $title,
  794. $actionId,
  795. $dataProvider
  796. );
  797. }
  798. protected function massAction($model, $pageSize, $title, $pageView, $actionId, $dataProvider, $useModuleClassNameForItemLabel = true)
  799. {
  800. // TODO: @Shoaibi/@Jason: Low: Candidate for MassActionController
  801. $activeAttributes = static::resolveActiveAttributesFromPostForMassAction($actionId);
  802. $selectedRecordCount = static::resolveSelectedRecordCountByMassActionId($actionId, $dataProvider, array());
  803. $model = $this->processMassAction(
  804. $pageSize,
  805. $selectedRecordCount,
  806. $pageView,
  807. $model,
  808. $title,
  809. $actionId,
  810. $dataProvider
  811. );
  812. $massActionView = $this->makeMassActionView(
  813. $model,
  814. $activeAttributes,
  815. $selectedRecordCount,
  816. $title,
  817. $actionId,
  818. $useModuleClassNameForItemLabel
  819. );
  820. $view = new $pageView(ZurmoDefaultViewUtil::makeStandardViewForCurrentUser(
  821. $this, $massActionView));
  822. echo $view->render();
  823. }
  824. protected function processMassAction($pageSize, $selectedRecordCount, $pageViewClassName, $listModel, $title,
  825. $actionId, $dataProvider = null)
  826. {
  827. // TODO: @Shoaibi/@Jason: Low: Candidate for MassActionController
  828. assert('$dataProvider == null || $dataProvider instanceof CDataProvider');
  829. $postSelectedRecordCount = Yii::app()->request->getPost('selectedRecordCount');
  830. $modelClassName = get_class($listModel);
  831. $postModelClassName = Yii::app()->request->getPost($modelClassName);
  832. if (isset($postSelectedRecordCount) || isset($postModelClassName))
  833. {
  834. $page = static::resolvePageValueForMassAction($modelClassName);
  835. $insufficientPermissionSkipSavingUtil = static::resolveInsufficientPermissionSkipSavingUtilByMassActionId($actionId);
  836. $start = ($selectedRecordCount > $pageSize)? 1: $selectedRecordCount;
  837. $skipCount = ($selectedRecordCount > $pageSize)? null: 0;
  838. $massActionSuccessful = static::processModelsForMassAction($listModel,
  839. $modelClassName,
  840. $selectedRecordCount,
  841. $dataProvider,
  842. $page,
  843. $pageSize,
  844. $insufficientPermissionSkipSavingUtil,
  845. $postModelClassName,
  846. $actionId);
  847. if ($massActionSuccessful)
  848. {
  849. $progressView = $this->makeMassActionProgressView(
  850. $listModel,
  851. 1,
  852. $selectedRecordCount,
  853. $start,
  854. $pageSize,
  855. $title,
  856. $skipCount,
  857. $actionId);
  858. if ($selectedRecordCount > $pageSize)
  859. {
  860. $view = new $pageViewClassName(
  861. ZurmoDefaultViewUtil::makeStandardViewForCurrentUser(
  862. $this, $progressView));
  863. echo $view->render();
  864. }
  865. else
  866. {
  867. $refreshScript = $progressView->renderRefreshScript();
  868. Yii::app()->user->setFlash('notification', $refreshScript['message']);
  869. $this->redirect($this->resolveReturnUrlForMassAction());
  870. }
  871. Yii::app()->end(0, false);
  872. }
  873. }
  874. return $listModel;
  875. }
  876. protected static function processModelsForMassAction($model, $modelClassName, $selectedRecordCount, $dataProvider,
  877. $page, $pageSize, $insufficientPermissionSkipSavingUtil,
  878. $postModelClassName, $actionId)
  879. {
  880. // TODO: @Shoaibi/@Jason: Low: Candidate for MassActionController
  881. $doMassActionFunctionName = 'processModelsForMassActionWithoutScoring';
  882. $doMassActionFunctionArguments = array(
  883. $modelClassName,
  884. $selectedRecordCount,
  885. $dataProvider,
  886. $page,
  887. $pageSize,
  888. $insufficientPermissionSkipSavingUtil,
  889. $actionId
  890. );
  891. if (MassActionUtil::isMassEditLikeAction($actionId) && !MassActionUtil::isMassProgressLikeAction($actionId))
  892. {
  893. $doMassActionFunctionName = 'processModelsForMassEditAction';
  894. array_unshift($doMassActionFunctionArguments, $postModelClassName, $model);
  895. }
  896. $doMassActionFunctionName = 'static::' . $doMassActionFunctionName;
  897. return call_user_func_array($doMassActionFunctionName, $doMassActionFunctionArguments);
  898. }
  899. protected static function processModelsForMassActionWithoutScoring($modelClassName, $selectedRecordCount, $dataProvider,
  900. $page, $pageSize, $insufficientPermissionSkipSavingUtil, $actionId)
  901. {
  902. // TODO: @Shoaibi/@Jason: Low: Candidate for MassActionController
  903. $returnValue = false;
  904. static::toggleMuteScoringModelValueByMassActionId($actionId, true);
  905. $modelsToProcess = static::getModelsToUpdate($modelClassName, $dataProvider, $selectedRecordCount, $page, $pageSize);
  906. $modelPermission = static::resolvePermissionOnSecurableItemByMassActionId($actionId);
  907. foreach ($modelsToProcess as $modelToProcess)
  908. {
  909. if (ControllerSecurityUtil::doesCurrentUserHavePermissionOnSecurableItem($modelToProcess, $modelPermission))
  910. {
  911. $function = 'processModelFor' . static::resolveMassActionId($actionId, true);
  912. $returnValue = static::$function($modelToProcess);
  913. }
  914. else
  915. {
  916. $insufficientPermissionSkipSavingUtil::setByModelIdAndName($modelClassName,
  917. $modelToProcess->id,
  918. $modelToProcess->name);
  919. }
  920. }
  921. static::toggleMuteScoringModelValueByMassActionId($actionId, false);
  922. return $returnValue;
  923. }
  924. protected static function processModelsForMassEditAction($postModelClassName, $model, $modelClassName, $selectedRecordCount, $dataProvider,
  925. $page, $pageSize, $insufficientPermissionSkipSavingUtil, $actionId)
  926. {
  927. // TODO: @Shoaibi/@Jason: Low: Candidate for MassActionController
  928. PostUtil::sanitizePostForSavingMassEdit($modelClassName);
  929. //Generically test that the changes are valid before attempting to save on each model.
  930. $sanitizedPostData = PostUtil::sanitizePostByDesignerTypeForSavingModel(new $modelClassName(false), $postModelClassName);
  931. $sanitizedOwnerPostData = PostUtil::sanitizePostDataToJustHavingElementForSavingModel($sanitizedPostData, 'owner');
  932. $sanitizedPostDataWithoutOwner = PostUtil::removeElementFromPostDataForSavingModel($sanitizedPostData, 'owner');
  933. $postMassEdit = Yii::app()->request->getPost('MassEdit');
  934. $massEditPostDataWithoutOwner = PostUtil::removeElementFromPostDataForSavingModel($postMassEdit, 'owner');
  935. $model->setAttributes($sanitizedPostDataWithoutOwner);
  936. if ($model->validate(array_keys($massEditPostDataWithoutOwner)))
  937. {

Large files files are truncated, but you can click here to view the full file