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='