From 44fdb6b79ebb134373e32f241de6ea1328ac31f3 Mon Sep 17 00:00:00 2001 From: nuxsmin Date: Mon, 7 Nov 2016 02:37:22 +0100 Subject: [PATCH] * [DEV] Closes #121. New password expiration time feature. --- ajax/ajax_accSave.php | 2 + inc/SP/Account/Account.class.php | 12 +- inc/SP/Account/AccountSearch.class.php | 14 +- inc/SP/Account/AccountsSearchItem.class.php | 10 + inc/SP/Controller/AccountController.class.php | 9 +- inc/SP/DataModel/AccountData.class.php | 41 + inc/sql/1.3.16100601.sql | 76 +- inc/sql/dbstructure.sql | 9 +- .../css/bootstrap-material-datetimepicker.css | 215 ++++ .../bootstrap-material-datetimepicker.min.css | 1 + inc/themes/material-blue/index.php | 15 +- inc/themes/material-blue/js/app-theme.js | 53 + inc/themes/material-blue/js/app-theme.min.js | 30 +- .../js/bootstrap-material-datetimepicker.js | 1105 +++++++++++++++++ .../bootstrap-material-datetimepicker.min.js | 48 + inc/themes/material-blue/js/moment.min.js | 7 + .../views/account/account-editpass.inc | 57 +- .../material-blue/views/account/account.inc | 18 +- .../material-blue/views/account/details.inc | 8 + .../views/accountsearch/rows.inc | 126 +- .../views/accountsearch/searchbox.inc | 1 + js/strings.js.php | 3 +- 22 files changed, 1693 insertions(+), 167 deletions(-) create mode 100644 inc/themes/material-blue/css/bootstrap-material-datetimepicker.css create mode 100644 inc/themes/material-blue/css/bootstrap-material-datetimepicker.min.css create mode 100644 inc/themes/material-blue/js/bootstrap-material-datetimepicker.js create mode 100644 inc/themes/material-blue/js/bootstrap-material-datetimepicker.min.js create mode 100644 inc/themes/material-blue/js/moment.min.js diff --git a/ajax/ajax_accSave.php b/ajax/ajax_accSave.php index 5f767ba8..28994e70 100644 --- a/ajax/ajax_accSave.php +++ b/ajax/ajax_accSave.php @@ -64,6 +64,7 @@ $accountLogin = Request::analyze('login'); $accountPassword = Request::analyzeEncrypted('pass'); $accountNotes = Request::analyze('notes'); $accountUrl = Request::analyze('url'); +$accountPassDateChange = Request::analyze('passworddatechange_unix', 0); // Checks $accountGroupEditEnabled = Request::analyze('groupEditEnabled', 0, false, 1); @@ -93,6 +94,7 @@ $AccountData->setAccountOtherUserEdit($accountUserEditEnabled); $AccountData->setAccountOtherGroupEdit($accountGroupEditEnabled); $AccountData->setAccountPass($accountPassword); $AccountData->setAccountIsPrivate($accountPrivateEnabled); +$AccountData->setAccountPassDateChange($accountPassDateChange); if (is_array($accountOtherUsers)) { $AccountData->setUsersId($accountOtherUsers); diff --git a/inc/SP/Account/Account.class.php b/inc/SP/Account/Account.class.php index e8c4b4b2..e66429d3 100644 --- a/inc/SP/Account/Account.class.php +++ b/inc/SP/Account/Account.class.php @@ -253,6 +253,8 @@ class Account extends AccountBase implements AccountInterface . 'dst.account_otherGroupEdit = src.acchistory_otherGroupEdit + 0,' . 'dst.account_pass = src.acchistory_pass,' . 'dst.account_IV = src.acchistory_IV ' + . 'dst.account_passDate = src.acchistory_passDate,' + . 'dst.account_passDateChange = src.acchistory_passDateChange ' . 'WHERE dst.account_id = :accountId'; $Data = new QueryData(); @@ -335,7 +337,9 @@ class Account extends AccountBase implements AccountInterface . 'account_userGroupId = :accountUserGroupId,' . 'account_otherUserEdit = :accountOtherUserEdit,' . 'account_otherGroupEdit = :accountOtherGroupEdit,' - . 'account_isPrivate = :accountIsPrivate'; + . 'account_isPrivate = :accountIsPrivate,' + . 'account_passDate = UNIX_TIMESTAMP(),' + . 'account_passDateChange = :accountPassDateChange'; $Data = new QueryData(); $Data->setQuery($query); @@ -352,6 +356,7 @@ class Account extends AccountBase implements AccountInterface $Data->addParam($this->accountData->getAccountOtherUserEdit(), 'accountOtherUserEdit'); $Data->addParam($this->accountData->getAccountOtherGroupEdit(), 'accountOtherGroupEdit'); $Data->addParam($this->accountData->getAccountIsPrivate(), 'accountIsPrivate'); + $Data->addParam($this->accountData->getAccountPassDateChange(), 'accountPassDateChange'); if (DB::getQuery($Data) === false) { return false; @@ -640,7 +645,9 @@ class Account extends AccountBase implements AccountInterface . 'account_pass = :accountPass,' . 'account_IV = :accountIV,' . 'account_userEditId = :accountUserEditId,' - . 'account_dateEdit = NOW() ' + . 'account_dateEdit = NOW(), ' + . 'account_passDate = UNIX_TIMESTAMP(), ' + . 'account_passDateChange = :accountPassDateChange ' . 'WHERE account_id = :accountId'; $Data = new QueryData(); @@ -649,6 +656,7 @@ class Account extends AccountBase implements AccountInterface $Data->addParam($this->accountData->getAccountIV(), 'accountIV'); $Data->addParam($this->accountData->getAccountUserEditId(), 'accountUserEditId'); $Data->addParam($this->accountData->getAccountId(), 'accountId'); + $Data->addParam($this->accountData->getAccountPassDateChange(), 'accountPassDateChange'); if (DB::getQuery($Data) === false) { diff --git a/inc/SP/Account/AccountSearch.class.php b/inc/SP/Account/AccountSearch.class.php index fdfb4c9e..1e49bd27 100644 --- a/inc/SP/Account/AccountSearch.class.php +++ b/inc/SP/Account/AccountSearch.class.php @@ -484,7 +484,7 @@ class AccountSearch */ private function analyzeQueryString() { - preg_match('/(user|group|file|tag):(.*)/i', $this->txtSearch, $filters); + preg_match('/(user|group|file|tag|expired):(.*)/i', $this->txtSearch, $filters); if (!is_array($filters) || count($filters) === 0) { return []; @@ -497,7 +497,7 @@ class AccountSearch $UserData = User::getItem()->getByLogin($filters[2])->getItemData(); $filtersData[] = [ 'type' => 'user', - 'query' => '(account_userId = ? OR accuser_userId ?)', + 'query' => 'account_userId = ? OR accuser_userId ?', 'values' => [$UserData->getUserId(), $UserData->getUserId()] ]; break; @@ -505,7 +505,7 @@ class AccountSearch $GroupData = GroupUtil::getGroupIdByName($filters[2]); $filtersData[] = [ 'type' => 'group', - 'query' => '(account_userGroupId = ? OR accgroup_groupId ?)', + 'query' => 'account_userGroupId = ? OR accgroup_groupId ?', 'values' => [$GroupData->getUsergroupId(), $GroupData->getUsergroupId()] ]; break; @@ -524,6 +524,14 @@ class AccountSearch 'values' => [$filters[2]] ]; break; + case 'expired': + $filtersData[] = + [ + 'type' => 'expired', + 'query' => 'account_passDateChange > 0 AND UNIX_TIMESTAMP() > account_passDateChange', + 'values' => [] + ]; + break; default: return $filtersData; } diff --git a/inc/SP/Account/AccountsSearchItem.class.php b/inc/SP/Account/AccountsSearchItem.class.php index fd185563..b2bcb862 100644 --- a/inc/SP/Account/AccountsSearchItem.class.php +++ b/inc/SP/Account/AccountsSearchItem.class.php @@ -369,4 +369,14 @@ class AccountsSearchItem return $accountNotes; } + + /** + * Develve si la clave ha caducado + * + * @return bool + */ + public function isPasswordExpired() + { + return $this->AccountSearchData->getAccountPassDateChange() > 0 && time() > $this->AccountSearchData->getAccountPassDateChange(); + } } \ No newline at end of file diff --git a/inc/SP/Controller/AccountController.class.php b/inc/SP/Controller/AccountController.class.php index b1364ce0..c75cfdcf 100644 --- a/inc/SP/Controller/AccountController.class.php +++ b/inc/SP/Controller/AccountController.class.php @@ -133,7 +133,6 @@ class AccountController extends ControllerBase implements ActionsInterface 'icon' => $this->icons->getIconAdd()->getIcon() ] ); - $this->view->assign('nextaction', Acl::ACTION_ACC_NEW); Session::setLastAcountId(0); $this->setCommonData(); @@ -184,6 +183,9 @@ class AccountController extends ControllerBase implements ActionsInterface $publicLinkUrl = (Checks::publicLinksIsEnabled() && $this->AccountData->getPublicLinkHash() ? Init::$WEBURI . '/?h=' . $this->AccountData->getPublicLinkHash() . '&a=link' : ''); $this->view->assign('publicLinkUrl', $publicLinkUrl); + + $this->view->assign('accountPassDate', gmdate('Y-m-d H:i:s', $this->AccountData->getAccountPassDate())); + $this->view->assign('accountPassDateChange', gmdate('Y-m-d', $this->AccountData->getAccountPassDateChange())); } $this->view->assign('actionId', $this->getAction()); @@ -257,7 +259,6 @@ class AccountController extends ControllerBase implements ActionsInterface 'icon' => $this->icons->getIconCopy()->getIcon() ] ); - $this->view->assign('nextaction', self::ACTION_ACC_COPY); $this->setCommonData(); } @@ -313,7 +314,6 @@ class AccountController extends ControllerBase implements ActionsInterface 'icon' => $this->icons->getIconEdit()->getIcon() ] ); - $this->view->assign('nextaction', self::ACTION_ACC_VIEW); $this->setCommonData(); } @@ -448,7 +448,8 @@ class AccountController extends ControllerBase implements ActionsInterface 'icon' => $this->icons->getIconEditPass()->getIcon() ] ); - $this->view->assign('nextaction', self::ACTION_ACC_VIEW); + + $this->view->assign('accountPassDateChange', gmdate('Y-m-d', $this->AccountData->getAccountPassDateChange())); } /** diff --git a/inc/SP/DataModel/AccountData.class.php b/inc/SP/DataModel/AccountData.class.php index a24a8f11..6d16261b 100644 --- a/inc/SP/DataModel/AccountData.class.php +++ b/inc/SP/DataModel/AccountData.class.php @@ -113,6 +113,15 @@ class AccountData extends DataModelBase implements JsonSerializable, DataModelIn * @var int */ public $account_isPrivate = 0; + /** + * @var int + */ + public $account_passDate = 0; + /** + * @var int + */ + public $account_passDateChange = 0; + /** * AccountData constructor. @@ -460,4 +469,36 @@ class AccountData extends DataModelBase implements JsonSerializable, DataModelIn { $this->account_isPrivate = (int)$account_isPrivate; } + + /** + * @return int + */ + public function getAccountPassDate() + { + return (int)$this->account_passDate; + } + + /** + * @param int $account_passDate + */ + public function setAccountPassDate($account_passDate) + { + $this->account_passDate = (int)$account_passDate; + } + + /** + * @return int + */ + public function getAccountPassDateChange() + { + return (int)$this->account_passDateChange; + } + + /** + * @param int $account_passDateChange + */ + public function setAccountPassDateChange($account_passDateChange) + { + $this->account_passDateChange = (int)$account_passDateChange; + } } \ No newline at end of file diff --git a/inc/sql/1.3.16100601.sql b/inc/sql/1.3.16100601.sql index c1fa56f5..5b4a3218 100644 --- a/inc/sql/1.3.16100601.sql +++ b/inc/sql/1.3.16100601.sql @@ -231,74 +231,18 @@ REFERENCES `usrGroups` (`usergroup_id`) ON DELETE CASCADE ON UPDATE CASCADE; -CREATE ALGORITHM = UNDEFINED - DEFINER = CURRENT_USER - SQL SECURITY DEFINER VIEW `account_search_v` AS - SELECT DISTINCT - `accounts`.`account_id` AS `account_id`, - `accounts`.`account_customerId` AS `account_customerId`, - `accounts`.`account_categoryId` AS `account_categoryId`, - `accounts`.`account_name` AS `account_name`, - `accounts`.`account_login` AS `account_login`, - `accounts`.`account_url` AS `account_url`, - `accounts`.`account_notes` AS `account_notes`, - `accounts`.`account_userId` AS `account_userId`, - `accounts`.`account_userGroupId` AS `account_userGroupId`, - `accounts`.`account_otherUserEdit` AS `account_otherUserEdit`, - `accounts`.`account_otherGroupEdit` AS `account_otherGroupEdit`, - `accounts`.`account_isPrivate` AS `account_isPrivate`, - `ug`.`usergroup_name` AS `usergroup_name`, - `categories`.`category_name` AS `category_name`, - `customers`.`customer_name` AS `customer_name`, - (SELECT count(0) - FROM `accFiles` - WHERE (`accFiles`.`accfile_accountId` = `accounts`.`account_id`)) AS `num_files` - FROM (((`accounts` - LEFT JOIN `categories` ON ((`accounts`.`account_categoryId` = `categories`.`category_id`))) LEFT JOIN - `usrGroups` `ug` ON ((`accounts`.`account_userGroupId` = `ug`.`usergroup_id`))) LEFT JOIN `customers` - ON ((`customers`.`customer_id` = `accounts`.`account_customerId`))); - +CREATE ALGORITHM=UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `account_search_v` AS select distinct `accounts`.`account_id` AS `account_id`,`accounts`.`account_customerId` AS `account_customerId`,`accounts`.`account_categoryId` AS `account_categoryId`,`accounts`.`account_name` AS `account_name`,`accounts`.`account_login` AS `account_login`,`accounts`.`account_url` AS `account_url`,`accounts`.`account_notes` AS `account_notes`,`accounts`.`account_userId` AS `account_userId`,`accounts`.`account_userGroupId` AS `account_userGroupId`,`accounts`.`account_otherUserEdit` AS `account_otherUserEdit`,`accounts`.`account_otherGroupEdit` AS `account_otherGroupEdit`,`accounts`.`account_isPrivate` AS `account_isPrivate`,`accounts`.`account_passDate` AS `account_passDate`,`accounts`.`account_passDateChange` AS `account_passDateChange`,`ug`.`usergroup_name` AS `usergroup_name`,`categories`.`category_name` AS `category_name`,`customers`.`customer_name` AS `customer_name`,(select count(0) from `accFiles` where (`accFiles`.`accfile_accountId` = `accounts`.`account_id`)) AS `num_files` from (((`accounts` left join `categories` on((`accounts`.`account_categoryId` = `categories`.`category_id`))) left join `usrGroups` `ug` on((`accounts`.`account_userGroupId` = `ug`.`usergroup_id`))) left join `customers` on((`customers`.`customer_id` = `accounts`.`account_customerId`))); ALTER TABLE `accounts` ADD COLUMN `account_isPrivate` BIT(1) NULL DEFAULT b'0' AFTER `account_otherUserEdit`; -CREATE - ALGORITHM = UNDEFINED - DEFINER = CURRENT_USER - SQL SECURITY DEFINER -VIEW `account_data_v` AS - SELECT - `accounts`.`account_id` AS `account_id`, - `accounts`.`account_name` AS `account_name`, - `accounts`.`account_categoryId` AS `account_categoryId`, - `accounts`.`account_userId` AS `account_userId`, - `accounts`.`account_customerId` AS `account_customerId`, - `accounts`.`account_userGroupId` AS `account_userGroupId`, - `accounts`.`account_userEditId` AS `account_userEditId`, - `accounts`.`account_login` AS `account_login`, - `accounts`.`account_url` AS `account_url`, - `accounts`.`account_notes` AS `account_notes`, - `accounts`.`account_countView` AS `account_countView`, - `accounts`.`account_countDecrypt` AS `account_countDecrypt`, - `accounts`.`account_dateAdd` AS `account_dateAdd`, - `accounts`.`account_dateEdit` AS `account_dateEdit`, - `accounts`.`account_otherUserEdit` AS `account_otherUserEdit`, - `accounts`.`account_otherGroupEdit` AS `account_otherGroupEdit`, - `accounts`.`account_isPrivate` AS `account_isPrivate`, - `categories`.`category_name` AS `category_name`, - `customers`.`customer_name` AS `customer_name`, - `ug`.`usergroup_name` AS `usergroup_name`, - `u1`.`user_name` AS `user_name`, - `u1`.`user_login` AS `user_login`, - `u2`.`user_name` AS `user_editName`, - `u2`.`user_login` AS `user_editLogin`, - `publicLinks`.`publicLink_hash` AS `publicLink_hash` - FROM - ((((((`accounts` - LEFT JOIN `categories` ON ((`accounts`.`account_categoryId` = `categories`.`category_id`))) - LEFT JOIN `usrGroups` `ug` ON ((`accounts`.`account_userGroupId` = `ug`.`usergroup_id`))) - LEFT JOIN `usrData` `u1` ON ((`accounts`.`account_userId` = `u1`.`user_id`))) - LEFT JOIN `usrData` `u2` ON ((`accounts`.`account_userEditId` = `u2`.`user_id`))) - LEFT JOIN `customers` ON ((`accounts`.`account_customerId` = `customers`.`customer_id`))) - LEFT JOIN `publicLinks` ON ((`accounts`.`account_id` = `publicLinks`.`publicLink_itemId`))) \ No newline at end of file +CREATE ALGORITHM=UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `account_data_v` AS select `accounts`.`account_id` AS `account_id`,`accounts`.`account_name` AS `account_name`,`accounts`.`account_categoryId` AS `account_categoryId`,`accounts`.`account_userId` AS `account_userId`,`accounts`.`account_customerId` AS `account_customerId`,`accounts`.`account_userGroupId` AS `account_userGroupId`,`accounts`.`account_userEditId` AS `account_userEditId`,`accounts`.`account_login` AS `account_login`,`accounts`.`account_url` AS `account_url`,`accounts`.`account_notes` AS `account_notes`,`accounts`.`account_countView` AS `account_countView`,`accounts`.`account_countDecrypt` AS `account_countDecrypt`,`accounts`.`account_dateAdd` AS `account_dateAdd`,`accounts`.`account_dateEdit` AS `account_dateEdit`,`accounts`.`account_otherUserEdit` AS `account_otherUserEdit`,`accounts`.`account_otherGroupEdit` AS `account_otherGroupEdit`,`accounts`.`account_isPrivate` AS `account_isPrivate`,`accounts`.`account_passDate` AS `account_passDate`,`accounts`.`account_passDateChange` AS `account_passDateChange`,`categories`.`category_name` AS `category_name`,`customers`.`customer_name` AS `customer_name`,`ug`.`usergroup_name` AS `usergroup_name`,`u1`.`user_name` AS `user_name`,`u1`.`user_login` AS `user_login`,`u2`.`user_name` AS `user_editName`,`u2`.`user_login` AS `user_editLogin`,`publicLinks`.`publicLink_hash` AS `publicLink_hash` from ((((((`accounts` left join `categories` on((`accounts`.`account_categoryId` = `categories`.`category_id`))) left join `usrGroups` `ug` on((`accounts`.`account_userGroupId` = `ug`.`usergroup_id`))) left join `usrData` `u1` on((`accounts`.`account_userId` = `u1`.`user_id`))) left join `usrData` `u2` on((`accounts`.`account_userEditId` = `u2`.`user_id`))) left join `customers` on((`accounts`.`account_customerId` = `customers`.`customer_id`))) left join `publicLinks` on((`accounts`.`account_id` = `publicLinks`.`publicLink_itemId`))); + +ALTER TABLE `accounts` + ADD COLUMN `account_passDate` INT UNSIGNED NULL AFTER `account_isPrivate`, + ADD COLUMN `account_passDateChange` INT UNSIGNED NULL AFTER `account_passDate`; + +ALTER TABLE `accHistory` + ADD COLUMN `accHistory_passDate` INT UNSIGNED NULL AFTER `accHistory_otherGroupEdit`, + ADD COLUMN `accHistory_passDateChange` INT UNSIGNED NULL AFTER `accHistory_passDate`; \ No newline at end of file diff --git a/inc/sql/dbstructure.sql b/inc/sql/dbstructure.sql index 151dc4a3..bbaaf849 100644 --- a/inc/sql/dbstructure.sql +++ b/inc/sql/dbstructure.sql @@ -112,6 +112,9 @@ CREATE TABLE `accounts` ( `account_dateEdit` datetime DEFAULT NULL, `account_otherGroupEdit` bit(1) DEFAULT b'0', `account_otherUserEdit` bit(1) DEFAULT b'0', + `account_isPrivate` bit(1) DEFAULT b'0', + `account_passDate` int(11) unsigned DEFAULT NULL, + `account_passDateChange` int(11) unsigned DEFAULT NULL, PRIMARY KEY (`account_id`), KEY `IDX_categoryId` (`account_categoryId`), KEY `IDX_userId` (`account_userGroupId`,`account_userId`), @@ -119,7 +122,7 @@ CREATE TABLE `accounts` ( KEY `fk_accounts_user_id` (`account_userId`), KEY `fk_accounts_user_edit_id` (`account_userEditId`), CONSTRAINT `fk_accounts_user_id` FOREIGN KEY (`account_userId`) REFERENCES `usrData` (`user_id`) ON DELETE NO ACTION ON UPDATE NO ACTION -) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `accFavorites`; @@ -191,6 +194,8 @@ CREATE TABLE `accHistory` ( `acchistory_mPassHash` varbinary(255) NOT NULL, `accHistory_otherUserEdit` bit(1) DEFAULT b'0', `accHistory_otherGroupEdit` bit(1) DEFAULT b'0', + `accHistory_passDate` int(10) unsigned DEFAULT NULL, + `accHistory_passDateChange` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`acchistory_id`), KEY `IDX_accountId` (`acchistory_accountId`), KEY `fk_accHistory_users_edit_id_idx` (`acchistory_userEditId`), @@ -198,7 +203,7 @@ CREATE TABLE `accHistory` ( KEY `fk_accHistory_categories_id` (`acchistory_categoryId`), KEY `fk_accHistory_customers_id` (`acchistory_customerId`), CONSTRAINT `fk_accHistory_users_id` FOREIGN KEY (`acchistory_userId`) REFERENCES `usrData` (`user_id`) ON DELETE NO ACTION ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `accTags`; diff --git a/inc/themes/material-blue/css/bootstrap-material-datetimepicker.css b/inc/themes/material-blue/css/bootstrap-material-datetimepicker.css new file mode 100644 index 00000000..17664bd7 --- /dev/null +++ b/inc/themes/material-blue/css/bootstrap-material-datetimepicker.css @@ -0,0 +1,215 @@ +.dtp { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.2); + z-index: 2000; + font-size: 15px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.dtp > .dtp-content { + background: #fff; + max-width: 300px; + box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12); + max-height: 520px; + position: relative; + left: 50%; +} + +.dtp > .dtp-content > .dtp-date-view > header.dtp-header { + background: #607D8B; + color: #fff; + text-align: center; + padding: 0.3em; +} + +.dtp div.dtp-date, .dtp div.dtp-time { + background: #607D8B; + text-align: center; + color: #fff; + padding: 10px; +} + +.dtp div.dtp-date > div { + padding: 0; + margin: 0; +} + +.dtp div.dtp-actual-month { + font-size: 1.5em; +} + +.dtp div.dtp-actual-num { + font-size: 3em; + line-height: 0.9; +} + +.dtp div.dtp-actual-maxtime { + font-size: 3em; + line-height: 0.9; +} + +.dtp div.dtp-actual-year { + font-size: 1.5em; + color: #fff; +} + +.dtp div.dtp-picker { + padding: 1em; + text-align: center; +} + +.dtp div.dtp-picker-month, .dtp div.dtp-actual-time { + font-weight: 500; + text-align: center; +} + +.dtp div.dtp-picker-month { + padding-bottom: 20px !important; + text-transform: uppercase !important; +} + +.dtp .dtp-close { + position: absolute; + top: 0.5em; + right: 1em; +} + +.dtp .dtp-close > a { + color: #fff; +} + +.dtp .dtp-close > a > i { + font-size: 1em; +} + +.dtp table.dtp-picker-days { + margin: 0; + min-height: 251px; +} + +.dtp table.dtp-picker-days, .dtp table.dtp-picker-days tr, .dtp table.dtp-picker-days tr > td { + border: none; +} + +.dtp table.dtp-picker-days tr > td { + font-weight: 700; + text-align: center; + padding: 0.5em; +} + +.dtp table.dtp-picker-days tr > td > span.dtp-select-day { + color: #BDBDBD !important; + padding: 0.4em 0.5em 0.5em 0.6em; +} + +.dtp table.dtp-picker-days tr > td > a, .dtp .dtp-picker-time > a { + color: #212121; + text-decoration: none; + padding: 0.4em; + border-radius: 5px !important; +} + +.dtp table.dtp-picker-days tr > td > a.selected { + background: #607D8B; + color: #fff; +} + +.dtp table.dtp-picker-days tr > th { + color: #757575; + text-align: center; + font-weight: 700; + padding: 0.4em 0.3em; +} + +.dtp .p10 > a { + color: #fff; + text-decoration: none; +} + +.dtp .p10 { + width: 10%; + display: inline-block; +} + +.dtp .p20 { + width: 20%; + display: inline-block; +} + +.dtp .p60 { + width: 60%; + display: inline-block; +} + +.dtp .p80 { + width: 80%; + display: inline-block; +} + +.dtp a.dtp-meridien-am, .dtp a.dtp-meridien-pm { + position: relative; + top: 10px; + color: #212121; + font-weight: 500; + padding: 0.7em 0.5em; + border-radius: 50% !important; + text-decoration: none; + background: #eee; + font-size: 1em; +} + +.dtp .dtp-actual-meridien a.selected { + background: #607D8B; + color: #fff; +} + +.dtp .dtp-picker-time > .dtp-select-hour { + cursor: pointer; +} + +.dtp .dtp-picker-time > .dtp-select-minute { + cursor: pointer; +} + +.dtp .dtp-buttons { + padding: 0 1em 1em 1em; + text-align: right; +} + +/*.dtp .dtp-buttons .btn-flat {*/ + /*border: none;*/ + /*padding: .5em;*/ + /*color: #fff;*/ + /*background-color: #607D8B;*/ +/*}*/ + +.dtp.hidden, .dtp .hidden { + display: none; +} + +.dtp .invisible { + visibility: hidden; +} + +.dtp .left { + float: left; +} + +.dtp .right { + float: right; +} + +.dtp .clearfix { + clear: both; +} + +.dtp .center { + text-align: center; +} \ No newline at end of file diff --git a/inc/themes/material-blue/css/bootstrap-material-datetimepicker.min.css b/inc/themes/material-blue/css/bootstrap-material-datetimepicker.min.css new file mode 100644 index 00000000..30dcf0cb --- /dev/null +++ b/inc/themes/material-blue/css/bootstrap-material-datetimepicker.min.css @@ -0,0 +1 @@ +.dtp{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.2);z-index:2000;font-size:15px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.dtp>.dtp-content{background:#fff;max-width:300px;box-shadow:0 2px 5px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12);max-height:520px;position:relative;left:50%}.dtp>.dtp-content>.dtp-date-view>header.dtp-header{background:#607d8b;color:#fff;text-align:center;padding:.3em}.dtp div.dtp-date,.dtp div.dtp-time{background:#607d8b;text-align:center;color:#fff;padding:10px}.dtp div.dtp-date>div{padding:0;margin:0}.dtp div.dtp-actual-month{font-size:1.5em}.dtp div.dtp-actual-num{font-size:3em;line-height:.9}.dtp div.dtp-actual-maxtime{font-size:3em;line-height:.9}.dtp div.dtp-actual-year{font-size:1.5em;color:#fff}.dtp div.dtp-picker{padding:1em;text-align:center}.dtp div.dtp-picker-month,.dtp div.dtp-actual-time{font-weight:500;text-align:center}.dtp div.dtp-picker-month{padding-bottom:20px !important;text-transform:uppercase !important}.dtp .dtp-close{position:absolute;top:.5em;right:1em}.dtp .dtp-close>a{color:#fff}.dtp .dtp-close>a>i{font-size:1em}.dtp table.dtp-picker-days{margin:0;min-height:251px}.dtp table.dtp-picker-days,.dtp table.dtp-picker-days tr,.dtp table.dtp-picker-days tr>td{border:0}.dtp table.dtp-picker-days tr>td{font-weight:700;text-align:center;padding:.5em}.dtp table.dtp-picker-days tr>td>span.dtp-select-day{color:#bdbdbd !important;padding:.4em .5em .5em .6em}.dtp table.dtp-picker-days tr>td>a,.dtp .dtp-picker-time>a{color:#212121;text-decoration:none;padding:.4em;border-radius:5px !important}.dtp table.dtp-picker-days tr>td>a.selected{background:#607d8b;color:#fff}.dtp table.dtp-picker-days tr>th{color:#757575;text-align:center;font-weight:700;padding:.4em .3em}.dtp .p10>a{color:#fff;text-decoration:none}.dtp .p10{width:10%;display:inline-block}.dtp .p20{width:20%;display:inline-block}.dtp .p60{width:60%;display:inline-block}.dtp .p80{width:80%;display:inline-block}.dtp a.dtp-meridien-am,.dtp a.dtp-meridien-pm{position:relative;top:10px;color:#212121;font-weight:500;padding:.7em .5em;border-radius:50% !important;text-decoration:none;background:#eee;font-size:1em}.dtp .dtp-actual-meridien a.selected{background:#607d8b;color:#fff}.dtp .dtp-picker-time>.dtp-select-hour{cursor:pointer}.dtp .dtp-picker-time>.dtp-select-minute{cursor:pointer}.dtp .dtp-buttons{padding:0 1em 1em 1em;text-align:right}.dtp.hidden,.dtp .hidden{display:none}.dtp .invisible{visibility:hidden}.dtp .left{float:left}.dtp .right{float:right}.dtp .clearfix{clear:both}.dtp .center{text-align:center} \ No newline at end of file diff --git a/inc/themes/material-blue/index.php b/inc/themes/material-blue/index.php index 81e49017..d06cb913 100644 --- a/inc/themes/material-blue/index.php +++ b/inc/themes/material-blue/index.php @@ -23,20 +23,23 @@ * */ -$themeInfo = array( +$themeInfo = [ 'name' => 'Material Blue', 'creator' => 'nuxsmin', 'version' => '1.0', 'targetversion' => '1.2.0', - 'js' => array( + 'js' => [ + 'moment.min.js', + 'bootstrap-material-datetimepicker.min.js', 'material.min.js', - 'app-theme.min.js'), - 'css' => array( + 'app-theme.min.js'], + 'css' => [ 'fonts.min.css', 'material.min.css', 'material-custom.min.css', + 'bootstrap-material-datetimepicker.min.css', 'jquery-ui.theme.min.css', 'styles.min.css', 'alertify-custom.min.css', - 'selectize.bootstrap3.min.css') -); + 'selectize.bootstrap3.min.css'] +]; diff --git a/inc/themes/material-blue/js/app-theme.js b/inc/themes/material-blue/js/app-theme.js index 0ec222ed..78611e57 100644 --- a/inc/themes/material-blue/js/app-theme.js +++ b/inc/themes/material-blue/js/app-theme.js @@ -275,6 +275,58 @@ sysPass.Theme = function (Common) { }); }; + /** + * Inicializar el selector de fecha + * @param $container + */ + var setupDatePicker = function ($container) { + log.info("setupDatePicker"); + + var datePickerOpts = { + format: "YYYY-MM-DD", + lang: "en", + weekStart: 0, + time: false, + cancelText: Common.config().LANG[44], + okText: Common.config().LANG[43], + clearText: Common.config().LANG[30], + nowText: Common.config().LANG[56], + minDate: new Date(), + triggerEvent: "dateIconClick" + }; + + // Actualizar el input oculto con la fecha en formato UNIX + var updateUnixInput = function ($obj, date) { + var unixtime; + + if (typeof date !== "undefined") { + unixtime = date; + } else { + unixtime = moment($obj.val()).format("X"); + } + + $obj.parent().find("input[name='passworddatechange_unix']").val(unixtime); + }; + + $container.find(".password-datefield__input").each(function () { + var $this = $(this); + + $this.bootstrapMaterialDatePicker(datePickerOpts); + + $this.parent().append(""); + + // Evento de click para el icono de calendario + $this.parent().next("i").on("click", function () { + $this.trigger("dateIconClick"); + }); + + // Actualizar el campo oculto cuando cambie la fecha + $this.on("change", function () { + updateUnixInput($this); + }); + }); + }; + /** * Triggers que se ejecutan en determinadas vistas */ @@ -337,6 +389,7 @@ sysPass.Theme = function (Common) { common: function ($container) { passwordDetect($container); activeTooltip($container); + setupDatePicker($container); $container.find(".download").button({ icons: {primary: "ui-icon-arrowthickstop-1-s"} diff --git a/inc/themes/material-blue/js/app-theme.min.js b/inc/themes/material-blue/js/app-theme.min.js index b15fba28..420566e7 100644 --- a/inc/themes/material-blue/js/app-theme.min.js +++ b/inc/themes/material-blue/js/app-theme.min.js @@ -1,16 +1,18 @@ -var $jscomp={scope:{},findInternal:function(a,g,e){a instanceof String&&(a=String(a));for(var h=a.length,f=0;f=c)continue;if(58<=c&&64>=c)continue;if(91<=c&&96>=c)continue;if(123<=c&&126>=c)continue}!a.passwordData.complexity.numbers&&48<=c&&57>=c||!a.passwordData.complexity.uppercase&& -65<=c&&90>=c||(k++,d+=String.fromCharCode(c))}$("#viewPass").attr("title",d);var e=zxcvbn(d);a.passwordData.passLength=d.length;b?(k=b.parent(),c=$("#"+b.attr("id")+"R"),a.outputResult(e,b),b=new MaterialTextfield,k.find("input:password").val(d),k.addClass(b.CssClasses_.IS_DIRTY).removeClass(b.CssClasses_.IS_INVALID),c.val(d).parent().addClass(b.CssClasses_.IS_DIRTY).removeClass(b.CssClasses_.IS_INVALID),a.encryptFormValue(c),k.find("#passLevel").show(500)):(a.outputResult(e),$("input:password, input.password").val(d), -$("#passLevel").show(500))},h=function(){$("
").dialog({modal:!0,title:a.config().LANG[29],width:"400px",open:function(){var b=$(this),k='