Newer
Older
use QUI\ERP\Products\Handler\Products;
use QUI\Lock\Locker;
use QUI\Memberships\Users\Handler as MembershipUsersHandler;
use QUI\Utils\Security\Orthos;
use QUI\ERP\Products\Search\BackendSearch;
use QUI\ERP\Products\Handler\Products as ProductsHandler;
use QUI\ERP\Products\Handler\Fields as ProductFields;

Patrick Müller
committed
use QUI\ERP\Plans\Handler as ErpPlansHandler;
use QUI\Interfaces\Users\User as QUIUserInterface;
/**
* User that is editing this Membership in this runtime
*
* @var QUIUserInterface
*/
protected $EditUser = null;
/**
* Set User that is editing this Membership in this runtime
*
* @param QUIUserInterface $EditUser
*/
public function setEditUser(QUIUserInterface $EditUser)
{
$this->EditUser = $EditUser;
}
/**
* Get IDs of all QUIQQER Groups
*
* @return int[]
*/
public function getGroupIds()
{
$groupIds = $this->getAttribute('groupIds');
return explode(",", trim($groupIds, ","));
}
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/**
* Get membership title
*
* @param Locale $Locale (optional)
* @return string - localized title
*/
public function getTitle($Locale = null)
{
if (is_null($Locale)) {
$Locale = QUI::getLocale();
}
$trans = json_decode($this->getAttribute('title'), true);
if (isset($trans[$Locale->getCurrent()])) {
return $trans[$Locale->getCurrent()];
}
return '';
}
/**
* Get membership description
*
* @param Locale $Locale (optional)
* @return string - localized description
*/
public function getDescription($Locale = null)
{
if (is_null($Locale)) {
$Locale = QUI::getLocale();
}
$trans = json_decode($this->getAttribute('description'), true);
if (isset($trans[$Locale->getCurrent()])) {
return $trans[$Locale->getCurrent()];
}
return '';
}
/**
* Get membership content
*
* @param Locale $Locale (optional)
* @return string - localized content
*/
public function getContent($Locale = null)
{
if (is_null($Locale)) {
$Locale = QUI::getLocale();
}
$trans = json_decode($this->getAttribute('content'), true);
if (isset($trans[$Locale->getCurrent()])) {
return $trans[$Locale->getCurrent()];
}
return '';
}
* Check if this membership is auto-extended
public function isAutoExtend()
return $this->getAttribute('autoExtend') ? true : false;
Permission::checkPermission(Handler::PERMISSION_EDIT, $this->EditUser);
$attributes = $this->getAttributes();
// check groups
if (empty($attributes['groupIds'])
) {

Patrick Müller
committed
throw new QUI\Memberships\Exception([
'quiqqer/memberships',
'exception.handler.no.groups'

Patrick Müller
committed
]);

Patrick Müller
committed
$attributes['groupIds'] = ','.$attributes['groupIds'].',';
if (empty($attributes['duration'])
|| $attributes['duration'] === 'infinite'
) {
$attributes['duration'] = 'infinite';
} else {
$duration = explode('-', $attributes['duration']);

Patrick Müller
committed
throw new QUI\Memberships\Exception([
'quiqqer/memberships',
'exception.membership.update.duration.invalid'

Patrick Müller
committed
]);
}
// edit user and timestamp
$attributes['editUser'] = QUI::getUserBySession()->getId();
$attributes['editDate'] = Utils::getFormattedTimestamp();
// autoExtend
if (empty($attributes['autoExtend'])) {
$attributes['autoExtend'] = 0;
} else {
$attributes['autoExtend'] = $attributes['autoExtend'] ? 1 : 0;
}

Patrick Müller
committed
* Only possible if membership has no users in it!

Patrick Müller
committed
* @throws \QUI\Memberships\Exception
* @throws \QUI\Permissions\Exception
* @throws \QUI\Exception
*/
public function delete()
{
Permission::checkPermission(Handler::PERMISSION_DELETE, $this->EditUser);
$MembershipUsers = MembershipUsersHandler::getInstance();
if (count($MembershipUsers->getIdsByMembershipId($this->id))) {

Patrick Müller
committed
throw new Exception([
'quiqqer/memberships',
'exception.membership.cannot.delete.with.users.left'

Patrick Müller
committed
]);
if ($this->isDefault()) {
$Conf = QUI::getPackage('quiqqer/memberships')->getConfig();
$Conf->set('memberships', 'defaultMembershipId', '0');
$Conf->save();
}
// remove from products
if (Utils::isQuiqqerProductsInstalled()) {
/** @var QUI\ERP\Products\Product\Product $Product */
foreach ($this->getProducts() as $Product) {

Patrick Müller
committed
$MembershipField = $Product->getField(
Handler::getProductMembershipField()->getId()
);

Patrick Müller
committed
$Product->deactivate();
$Product->save();
}
}
if (Utils::isQuiqqerContractsInstalled()) {
// @todo quiqqer/contracts abhandeln
}

Patrick Müller
committed
QUI::getEvents()->fireEvent('quiqqerMembershipsDelete', [$this->getId()]);
* Get a user of this membership (non-archived)
* @return QUI\Memberships\Users\MembershipUser
* @throws QUI\Memberships\Exception
*/
public function getMembershipUser($userId)
{

Patrick Müller
committed
$result = QUI::getDataBase()->fetch([
'select' => [

Patrick Müller
committed
],
'from' => MembershipUsersHandler::getInstance()->getDataBaseTableName(),

Patrick Müller
committed
'where' => [
'userId' => $userId,
'archived' => 0

Patrick Müller
committed
]
]);

Patrick Müller
committed
throw new Exception([
'quiqqer/memberships',
'exception.membership.user.not.found',

Patrick Müller
committed
[

Patrick Müller
committed
]
], 404);
}
return MembershipUsersHandler::getInstance()->getChild($result[0]['id']);
}
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
/**
* Get all membership user IDs
*
* @param bool $includeArchived (optional) - Include archived MembershipUsers
* @return int[]
*/
public function getMembershipUserIds($includeArchived = false)
{
$membershipUserIds = [];
$where = [
'membershipId' => $this->id,
'archived' => 0
];
if ($includeArchived) {
unset($where['archived']);
}
try {
$result = QUI::getDataBase()->fetch([
'select' => 'id',
'from' => QUI\Memberships\Users\Handler::getInstance()->getDataBaseTableName(),
'where' => $where
]);
} catch (\Exception $Exception) {
QUI\System\Log::writeException($Exception);
return $membershipUserIds;
}
foreach ($result as $row) {
$membershipUserIds[] = $row['id'];
}
return $membershipUserIds;
}
* Get IDs of all QUIQQER Groups that are UNIQUE to this membership
public function getUniqueGroupIds()
$Memberships = Handler::getInstance();
$groupIds = $this->getGroupIds();
$uniqueGroupIds = $groupIds;
foreach ($Memberships->getMembershipIdsByGroupIds($groupIds) as $membershipId) {
if ($membershipId == $this->getId()) {
continue;
}
$Membership = $Memberships->getChild($membershipId);
foreach ($Membership->getGroupIds() as $groupId) {
if (in_array($groupId, $groupIds)) {
$k = array_search($groupId, $uniqueGroupIds);
if ($k !== false) {
unset($uniqueGroupIds[$k]);
}
}
}
}
* Checks if this membership has an (active, non-archived) user assigned
*
* @param int $userId
* @return bool
*/
public function hasMembershipUserId($userId)
{

Patrick Müller
committed
$result = QUI::getDataBase()->fetch([

Patrick Müller
committed
'select' => [

Patrick Müller
committed
],
'from' => MembershipUsersHandler::getInstance()->getDataBaseTableName(),

Patrick Müller
committed
'where' => [

Patrick Müller
committed
]
]);
return current(current($result)) > 0;
}
/**
* @param bool $archivedOnly (optional) - search archived users only [default: false]
* @param bool $countOnly (optional) - get count for search result only [default: false]
* @return int[]|int - membership user IDs or count
public function searchUsers($searchParams, $archivedOnly = false, $countOnly = false)

Patrick Müller
committed
$membershipUserIds = [];
$Grid = new QUI\Utils\Grid($searchParams);
$gridParams = $Grid->parseDBParams($searchParams);
$tbl = MembershipUsersHandler::getInstance()->getDataBaseTableName();
$usersTbl = QUI::getDBTableName('users');

Patrick Müller
committed
$binds = [];
$where = [];
$sql = "SELECT COUNT(*)";
} else {
$sql = "SELECT `musers`.id";
}
$sql .= " FROM `".$tbl."` musers LEFT JOIN `".$usersTbl."` users";
$sql .= ' ON `musers`.userId = `users`.id';
// $where[] = '`musers`.userId = `users`.id';

Patrick Müller
committed
$where[] = '`musers`.membershipId = '.$this->id;
if ($archivedOnly === false) {
$where[] = '`musers`.archived = 0';
} else {
$where[] = '`musers`.archived = 1';
}
if (!empty($searchParams['search'])) {

Patrick Müller
committed
$whereOR = [];

Patrick Müller
committed
$searchColumns = [
'`users`.username',
'`users`.firstname',
'`users`.lastname'

Patrick Müller
committed
];
foreach ($searchColumns as $tbl => $column) {

Patrick Müller
committed
$whereOR[] = $column.' LIKE :search';
$binds['search'] = [
'value' => '%'.$searchParams['search'].'%',
'type' => \PDO::PARAM_STR

Patrick Müller
committed
];

Patrick Müller
committed
$where[] = '('.implode(' OR ', $whereOR).')';
if (!empty($searchParams['productId'])) {
$where[] = '`musers`.productId = :productId';

Patrick Müller
committed
$binds['productId'] = [
'value' => (int)$searchParams['productId'],
'type' => \PDO::PARAM_INT

Patrick Müller
committed
];
// build WHERE query string
if (!empty($where)) {

Patrick Müller
committed
$sql .= " WHERE ".implode(" AND ", $where);
}
// ORDER
if (!empty($searchParams['sortOn'])) {
$sortOn = Orthos::clear($searchParams['sortOn']);
switch ($sortOn) {
case 'username':
case 'firstname':
case 'lastname':

Patrick Müller
committed
$sortOn = '`users`.'.$sortOn;

Patrick Müller
committed
$sortOn = '`musers`.'.$sortOn;

Patrick Müller
committed
$order = "ORDER BY ".$sortOn;
if (isset($searchParams['sortBy']) &&
!empty($searchParams['sortBy'])
) {

Patrick Müller
committed
$order .= " ".Orthos::clear($searchParams['sortBy']);
} else {
$order .= " ASC";
}

Patrick Müller
committed
$sql .= " ".$order;
}
// LIMIT
if (!empty($gridParams['limit'])
&& !$countOnly
) {

Patrick Müller
committed
$sql .= " LIMIT ".$gridParams['limit'];
} else {
if (!$countOnly) {

Patrick Müller
committed
$sql .= " LIMIT ".(int)20;
$Stmt = QUI::getPDO()->prepare($sql);
// bind search values
foreach ($binds as $var => $bind) {

Patrick Müller
committed
$Stmt->bindValue(':'.$var, $bind['value'], $bind['type']);
}
try {
$Stmt->execute();
$result = $Stmt->fetchAll(\PDO::FETCH_ASSOC);
} catch (\Exception $Exception) {
QUI\System\Log::addError(

Patrick Müller
committed
self::class.' :: searchUsers() -> '.$Exception->getMessage()

Patrick Müller
committed
return [];
}
if ($countOnly) {
return (int)current(current($result));
}
foreach ($result as $row) {
$membershipUserIds[] = (int)$row['id'];
}
return $membershipUserIds;
}
/**
* Calculate the end date for this membership based on a given time
*
* @param int $start (optional) - UNIX timestamp; if omitted use time()
*/
public function calcEndDate($start = null)
{
if ($this->isInfinite()) {
return null;
}
if (is_null($start)) {
$start = time();
}
$start = Utils::getFormattedTimestamp($start);
$duration = explode('-', $this->getAttribute('duration'));
$durationCount = $duration[0];
$durationScope = $duration[1];

Patrick Müller
committed
$durationMode = Handler::getSetting('durationMode');
switch ($durationMode) {
case MembershipUsersHandler::DURATION_MODE_DAY:

Patrick Müller
committed
$endTime = strtotime($start.' +'.$durationCount.' '.$durationScope);

Patrick Müller
committed
$beginOfDay = strtotime("midnight", $endTime);
$end = strtotime("tomorrow", $beginOfDay) - 1;
break;
default:

Patrick Müller
committed
$end = strtotime($start.' +'.$durationCount.' '.$durationScope);

Patrick Müller
committed
}
return Utils::getFormattedTimestamp($end);
}
* Requires: quiqqer/products
*
* Get all products that have this membership assigned
*
* @return QUI\ERP\Products\Product\Product[]
*/
public function getProducts()
{
if (!Utils::isQuiqqerProductsInstalled()) {

Patrick Müller
committed
return [];

Patrick Müller
committed
$Search = new BackendSearch();
$MembershipField = Handler::getProductMembershipField();
if ($MembershipField === false) {
return [];
}
$result = $Search->search([
'fields' => [
$MembershipField->getId() => "$this->id" // has to be string
]
]);
} catch (\Exception $Exception) {
QUI\System\Log::writeException($Exception);
return [];

Patrick Müller
committed
$products = [];

Patrick Müller
committed
try {
$products[] = ProductsHandler::getProduct($id);
} catch (\Exception $Exception) {
QUI\System\Log::writeException($Exception);
}
}
return $products;
}
/**
* Requires: quiqqer/products
*
* Create a Product from this Membership
*
* Hint: Every time this method is called, a new Product is created, regardless
* of previous calls!
*
* @return QUI\ERP\Products\Product\Product|false
* @throws \QUI\Exception
*/
public function createProduct()
{
if (!Utils::isQuiqqerProductsInstalled()) {
return false;
}

Patrick Müller
committed
$categories = [];
$fields = [];
$Category = Handler::getProductCategory();
if ($Category) {
$categories[] = $Category;
}
$MembershipField = Handler::getProductMembershipField();
if ($MembershipField !== false) {

Patrick Müller
committed
$MembershipField->setOwnFieldStatus(true);
$MembershipField->setValue($this->id);
$fields[] = $MembershipField;
}
$MembershipFlagField = Handler::getProductMembershipFlagField();
if ($MembershipFlagField !== false) {

Patrick Müller
committed
$MembershipFlagField->setOwnFieldStatus(true);
$MembershipFlagField->setValue(true);
$fields[] = $MembershipFlagField;
}
// set title and description
$TitleField = ProductFields::getField(ProductFields::FIELD_TITLE);
$DescField = ProductFields::getField(ProductFields::FIELD_SHORT_DESC);
$title = json_decode($this->getAttribute('title'), true);
$description = json_decode($this->getAttribute('description'), true);
if (!empty($title)) {
$TitleField->setValue($title);
$fields[] = $TitleField;
}
if (!empty($description)) {
$DescField->setValue($description);
$fields[] = $DescField;
}
if ($this->isAutoExtend() && Utils::isQuiqqerErpPlansInstalled()) {
$Product = ProductsHandler::createProduct($categories, $fields, QUI\ERP\Plans\PlanProduct::class);
$Product->getField(ErpPlansHandler::FIELD_DURATION)->setValue($this->getAttribute('duration'));
$Product->getField(ErpPlansHandler::FIELD_AUTO_EXTEND)->setValue(true);
$Product->getField(ErpPlansHandler::FIELD_INVOICE_INTERVAL)->setValue($this->getAttribute('duration'));
$Product->getField(ErpPlansHandler::FIELD_MIN_DURATION)->setValue($this->getAttribute('duration'));
} else {
$Product = ProductsHandler::createProduct($categories, $fields);
}
if (!empty($categories)) {
$Product->setMainCategory($categories[0]);
}
$Product->save(QUI::getUsers()->getSystemUser());
QUI::getEvents()->fireEvent('quiqqerMembershipsCreateProduct', [$this, $Product]);
/**
* Locks editing of this membership for the current session user
*
* @return void
* @throws \QUI\Lock\Exception
* @throws \QUI\Exception
*/
public function lock()
{
Locker::lock(QUI::getPackage('quiqqer/memberships'), $this->getLockKey());
}
/**
* Unlock membership (requires permission!)
*
* @return void
* @throws \QUI\Permissions\Exception
* @throws \QUI\Lock\Exception
* @throws \QUI\Exception
*/
public function unlock()
{
Locker::unlockWithPermissions(
QUI::getPackage('quiqqer/memberships'),
$this->getLockKey(),
Handler::PERMISSION_FORCE_EDIT
);
}
/**
* Check if this membership is currently locked
*
* @return bool
* @throws \QUI\Exception
*/
public function isLocked()
{
return Locker::isLocked(QUI::getPackage('quiqqer/memberships'), $this->getLockKey());
}
/**
* Get membership lock key
*
* @return string
*/
protected function getLockKey()
{

Patrick Müller
committed
return 'membership_'.$this->id;
/**
* Get membership data for backend view/edit purposes
*
* @return array
*/
public function getBackendViewData()
{

Patrick Müller
committed
return [
'id' => $this->getId(),
'title' => $this->getTitle(),
'description' => $this->getDescription(),
'content' => $this->getContent()

Patrick Müller
committed
];
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
/**
* Check if this membership has an infinite duration (never expires)
*
* @return bool
*/
public function isInfinite()
{
return $this->getAttribute('duration') === 'infinite';
}
/**
* Check if this Membership is the default Memberships
*
* @return bool
*/
public function isDefault()
{
$DefaultMembership = Handler::getDefaultMembership();
if ($DefaultMembership === false) {
return false;
}
return $DefaultMembership->getId() === $this->getId();
}
/**
* Add user to the membership
*
* @param QUI\Users\User $User
* @return QUI\Memberships\Users\MembershipUser
* @throws \QUI\Exception
*/
public function addUser(QUI\Users\User $User)
{

Patrick Müller
committed
return MembershipUsersHandler::getInstance()->createChild([
'userId' => $User->getId(),
'membershipId' => $this->id
], $this->EditUser);