Merge branch 'develop' of github.com:humhub/humhub into develop

# Conflicts:
#	protected/humhub/docs/CHANGELOG_DEV.md
This commit is contained in:
buddh4 2019-12-02 15:26:19 +01:00
commit c432cbc942
9 changed files with 278 additions and 83 deletions

View File

@ -15,7 +15,9 @@ HumHub Change Log (DEVELOP)
- Enh #3697: Stay of module update page after updating a module
- Fix #3692: Icon Upload Problems
- Fix #3705: Don't render empty navigation/menu
- Fix #3706: Space mentioning broken
- Fix #3706: Space mentioning broken
- Fix #3742: OAuth timeout doesn't respect configured timeout
- Enh: Added `DateHelper:getUserTimeZone()`, `DateHelper:getSystemTimeZone()`, `DateHelper:isInDbFormat()`
- Fix #3711: Fullscreen Richtext menu broken on ios safari

View File

@ -8,6 +8,9 @@
namespace humhub\libs;
use DateTimeZone;
use Yii;
/**
* Utility class for date issues
*
@ -16,12 +19,59 @@ namespace humhub\libs;
*/
class DateHelper
{
const DB_DATE_FORMAT = 'Y-m-d H:i:s';
const DB_DATE_FORMAT_PHP = 'php:Y-m-d H:i:s';
const REGEX_DBFORMAT_DATE = '/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/';
const REGEX_DBFORMAT_DATETIME = '/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1]) (\d{2}):(\d{2}):(\d{2})$/';
/**
* Returns the user timeZone or app timezone as fallback.
*
* @return DateTimeZone|string
* @since v1.4
*/
public static function getUserTimeZone($asString = false)
{
$tz = Yii::$app->user->isGuest
? Yii::$app->timeZone
: Yii::$app->user->getTimeZone();
if(!$tz) {
$tz = Yii::$app->timeZone;
}
return $asString ? $tz : new DateTimeZone($tz);
}
/**
* @param bool $asString
* @return DateTimeZone|string
* @since v1.4
*/
public static function getSystemTimeZone($asString = false)
{
return $asString ? Yii::$app->timeZone : new DateTimeZone(Yii::$app->timeZone);
}
/**
* Checks whether the given value is a db date format or not.
*
* @param string $value the date value
* @return boolean
* @since v1.4
*/
public static function isInDbFormat($value)
{
return (preg_match(self::REGEX_DBFORMAT_DATE, $value) || preg_match(self::REGEX_DBFORMAT_DATETIME, $value));
}
/**
* Parses a date and optionally a time if timeAttribute is specified.
*
* @param string $value
* @param string $timeValue optional time value
* @return int timestamp in utc
* @throws \Exception
*/
public static function parseDateTimeToTimestamp($value, $timeValue = null)
{
@ -33,9 +83,10 @@ class DateHelper
* an given pattern or the default pattern 'Y-m-d' if no pattern is provided.
*
* @param string $value date value
* @param string $pattern pattern
* @param string $pattern pattern
* @param string $timeValue optional time value
* @return int timestamp in utc
* @throws \Exception
*/
public static function parseDateTime($value, $pattern = 'Y-m-d', $timeValue = null)
{

View File

@ -37,13 +37,7 @@ class DbDateValidator extends DateValidator
public $timeAttribute = '';
/**
* @var defines the source time zone which is by default the user time zone, this can be used if a form uses an own
* timestamp setting.
*/
public $timeZone;
/**
* @var string attribute name to save converted value to
* @var string attribute name to save converted value to
*/
public $targetAttribute = null;
@ -52,10 +46,14 @@ class DbDateValidator extends DateValidator
*/
public function init()
{
if ($this->format === null) {
if (!$this->format) {
$this->format = Yii::$app->formatter->dateInputFormat;
}
if(!$this->timeZone) {
$this->timeZone = DateHelper::getUserTimeZone(true);
}
parent::init();
}
@ -64,13 +62,14 @@ class DbDateValidator extends DateValidator
*/
public function validateAttribute($model, $attribute)
{
// If no source timeZone
if (empty($this->timeZone)) {
$this->timeZone = (!\Yii::$app->formatter->timeZone) ? \Yii::$app->timeZone : \Yii::$app->formatter->timeZone;
// If the date is already in system format, we do not need any further translation or parsing
if(DateHelper::isInDbFormat($model->$attribute)) {
return;
}
$timestamp = $this->parseDateTimeValue($model->$attribute, $this->getTimeValue($model));
$timeValue = $this->getTimeValue($model);
$timestamp = $this->parseDateTimeValue($model->$attribute, $timeValue);
if ($timestamp === false) {
$this->addError($model, $attribute, $this->message, []);
@ -78,17 +77,14 @@ class DbDateValidator extends DateValidator
$this->addError($model, $attribute, $this->tooSmall, ['min' => $this->minString]);
} elseif ($this->max !== null && $timestamp > $this->max) {
$this->addError($model, $attribute, $this->tooBig, ['max' => $this->maxString]);
} elseif (!$this->isInDbFormat($model->$attribute)) {
} else {
// If there is no error, and attribute is not yet in DB Format - convert to DB
$date = new \DateTime();
$date = new \DateTime(null, new \DateTimeZone('UTC'));
$date->setTimestamp($timestamp);
if ($this->hasTime()) {
if ($timeValue) {
// Convert timestamp to apps timeZone
$date->setTimezone(new \DateTimeZone(\Yii::$app->timeZone));
} else {
// If we do not need to respect time, set timezone to utc
// To ensure we're saving 00:00:00 time infos.
$date->setTimezone(new \DateTimeZone('UTC'));
$date->setTimezone(DateHelper::getSystemTimeZone());
}
$targetAttribute = ($this->targetAttribute === null) ? $attribute : $this->targetAttribute;
@ -99,6 +95,30 @@ class DbDateValidator extends DateValidator
}
}
/**
* Parses a date and a time value if timeAttribute is specified.
*
* @param string $value
* @return int timestamp in utc
* @throws \Exception
*/
protected function parseDateTimeValue($value, $timeValue = null)
{
// It's already a database datetime / no conversion needed.
if (DateHelper::isInDbFormat($value)) {
return strtotime($value);
}
$timestamp = $this->parseDateValue($value);
if ($this->hasTime() && !empty($timeValue)) {
$timestamp += $this->parseTimeValue($timeValue);
$timestamp = $this->fixTimestampTimeZone($timestamp, $this->timeZone);
}
return $timestamp;
}
/**
* Checks a time attribute name is given, if empty don't handle time
*
@ -110,18 +130,18 @@ class DbDateValidator extends DateValidator
}
/**
* Returns time value
* Returns time value if provided by the model
*
* @return string time value (e.g. 12:00)
* @return string|null time value (e.g. 12:00)
*/
protected function getTimeValue($model)
{
if ($this->hasTime()) {
$attributeName = $this->timeAttribute;
return $model->$attributeName;
return $model->$attributeName ?: null;
}
return '';
return null;
}
/**
@ -129,54 +149,13 @@ class DbDateValidator extends DateValidator
*
* @param string $value
* @return int timestamp in utc
* @throws \Exception
*/
public static function parseDateTime($value, $timeValue = null)
{
return (new self())->parseDateTimeValue($value, $timeValue);
}
/**
* Parses a date and optionally a time if timeAttribute is specified.
*
* @param string $value
* @return int timestamp in utc
*/
protected function parseDateTimeValue($value, $timeValue = "")
{
// It's already a database datetime / no conversion needed.
if ($this->isInDbFormat($value)) {
return strtotime($value);
}
$timestamp = $this->parseDateValue($value);
if ($this->hasTime() && $timeValue != "") {
$timestamp += $this->parseTimeValue($timeValue);
$timestamp = $this->fixTimestampTimeZone($timestamp, $this->timeZone);
}
return $timestamp;
}
/**
* Converts the given timestamp from user (or configured) timezone to a utc timestamp
*
* @param long $ts the timestamp
* @param String $timeZone users timezone
* @return long the timestamp in utc
*/
protected function fixTimestampTimeZone($ts, $timeZone)
{
// Create date string
$fromDateTime = new \DateTime("@" . $ts);
// Create date object
$toDateTime = \DateTime::createFromFormat('Y-m-d H:i:s', $fromDateTime->format('Y-m-d H:i:s'), new \DateTimeZone($timeZone));
$toDateTime->setTimezone(new \DateTimeZone('UTC'));
return $toDateTime->getTimestamp();
}
/**
* Parses given time value (hh:mm) to seconds
*
@ -190,13 +169,22 @@ class DbDateValidator extends DateValidator
}
/**
* Checks whether the given value is a db date format or not.
* Converts the given timestamp from user (or configured) timezone to a utc timestamp
*
* @param string $value the date value
* @return boolean
* @param int $ts the timestamp
* @param String $timeZone users timezone
* @return int
* @throws \Exception
*/
protected function isInDbFormat($value)
protected function fixTimestampTimeZone($ts, $timeZone)
{
return (preg_match(self::REGEX_DBFORMAT_DATE, $value) || preg_match(self::REGEX_DBFORMAT_DATETIME, $value));
// Create date string
$fromDateTime = new \DateTime('@' . $ts);
// Create date object
$toDateTime = \DateTime::createFromFormat('Y-m-d H:i:s', $fromDateTime->format('Y-m-d H:i:s'), new \DateTimeZone($timeZone));
$toDateTime->setTimezone(new \DateTimeZone('UTC'));
return $toDateTime->getTimestamp();
}
}

View File

@ -9,6 +9,7 @@
namespace humhub\modules\user\authclient;
use humhub\modules\user\authclient\interfaces\StandaloneAuthClient;
use Yii;
/**
* Extended version of AuthAction with AuthClient support which are not handled
@ -22,12 +23,14 @@ class AuthAction extends \yii\authclient\AuthAction
/**
* @inheritdoc
*
*
* @param StandaloneAuthClient $client
* @return \yii\web\Response response instance.
*/
public function auth($client, $authUrlParams = [])
{
Yii::$app->session->set('loginRememberMe', (boolean) Yii::$app->request->get('rememberMe'));
if ($client instanceof StandaloneAuthClient) {
return $client->authAction($this);
}

View File

@ -170,7 +170,9 @@ class AuthClientHelpers
$registration->enableUserApproval = false;
}
unset($attributes['id']);
// remove potentially unsafe attributes
unset($attributes['id'], $attributes['guid'], $attributes['contentcontainer_id'], $attributes['auth_mode'], $attributes['status']);
$registration->getUser()->setAttributes($attributes, false);
$registration->getProfile()->setAttributes($attributes, false);
$registration->getGroupUser()->setAttributes($attributes, false);

View File

@ -84,7 +84,7 @@ class AuthController extends Controller
return $this->onAuthSuccess($login->authClient);
}
// Self Invite
// Self Invite
$invite = new Invite();
$invite->scenario = 'invite';
if ($invite->load(Yii::$app->request->post()) && $invite->selfInvite()) {
@ -119,7 +119,7 @@ class AuthController extends Controller
return $this->redirect(['/user/account/connected-accounts']);
}
// Login existing user
// Login existing user
$user = AuthClientHelpers::getUserByAuthClient($authClient);
if ($user !== null) {
@ -177,12 +177,12 @@ class AuthController extends Controller
$success = false;
if ($user->status == User::STATUS_ENABLED) {
$duration = 0;
if ($authClient instanceof BaseFormAuth) {
if ($authClient->login->rememberMe) {
$duration = Yii::$app->getModule('user')->loginRememberMeDuration;
}
}
if (
($authClient instanceof BaseFormAuth && $authClient->login->rememberMe) ||
!empty(Yii::$app->session->get('loginRememberMe'))) {
$duration = Yii::$app->getModule('user')->loginRememberMeDuration;
}
AuthClientHelpers::updateUser($authClient, $user);
if ($success = Yii::$app->user->login($user, $duration)) {

View File

@ -0,0 +1,100 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2018 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\tests\codeception\unit\libs;
use Yii;
use humhub\libs\DbDateValidator;
use tests\codeception\_support\HumHubDbTestCase;
/**
* Class MimeHelperTest
*/
class DBDateValidatorTest extends HumHubDbTestCase
{
public function _before()
{
parent::_before();
Yii::$app->timeZone = 'Europe/Berlin';
Yii::$app->language = 'en-US';
$this->becomeUser('admin');
Yii::$app->user->identity->time_zone = 'Europe/London';
}
public function testInitValues()
{
$validator = new DbDateValidator();
$this->assertEquals('Europe/London', $validator->timeZone);
$this->assertEquals('short', $validator->format);
$this->assertEquals('en-US', $validator->locale);
}
public function testParseDateWithoutTimeValueFormatUS()
{
// No time translation, since no time value given
$model = new DateValidatorTestModel(['date' => '12/1/19']);
$validator = new DbDateValidator();
$validator->validateAttribute($model, 'date');
$this->assertEmpty($model->getErrors());
$this->assertEquals('2019-12-01 00:00:00', $model->date);
}
public function testParseDateWithoutTimeValueFormatDe()
{
// No time translation, since no time value given
Yii::$app->language = 'de';
$model = new DateValidatorTestModel(['date' => '01.12.19']);
$validator = new DbDateValidator();
$this->assertEquals('de', $validator->locale);
$validator->validateAttribute($model, 'date');
$this->assertEmpty($model->getErrors());
$this->assertEquals('2019-12-01 00:00:00', $model->date);
}
public function testParseDateWithTimeValueFormatUS()
{
// Translate from Cairo (UTC +2) to Berlin (UTC + 1)
$model = new DateValidatorTestModel(['date' => '12/1/19', 'time' => '12 PM']);
$validator = new DbDateValidator(['timeAttribute' => 'time', 'timeZone' => 'Africa/Cairo']);
$validator->validateAttribute($model, 'date');
$this->assertEmpty($model->getErrors());
$this->assertEquals('2019-12-01 11:00:00', $model->date);
}
public function testParseDateWithTimeValueFormatDE()
{
// Translate from Cairo (UTC +2) to Berlin (UTC + 1)
Yii::$app->language = 'de';
$model = new DateValidatorTestModel(['date' => '01.12.19', 'time' => '12:00']);
$validator = new DbDateValidator(['timeAttribute' => 'time', 'timeZone' => 'Africa/Cairo']);
$validator->validateAttribute($model, 'date');
$this->assertEmpty($model->getErrors());
$this->assertEquals('2019-12-01 11:00:00', $model->date);
}
public function testDoubleValidation()
{
// Ensure that double validation does not translate the value two times
$model = new DateValidatorTestModel(['date' => '12/1/19', 'time' => '12 PM']);
$validator = new DbDateValidator(['timeAttribute' => 'time', 'timeZone' => 'Africa/Cairo']);
$validator->validateAttribute($model, 'date');
$validator->validateAttribute($model, 'date');
$this->assertEquals('2019-12-01 11:00:00', $model->date);
}
public function testValidateWithUnsetTimeAttribute()
{
// No time given, so do not translate
$model = new DateValidatorTestModel(['date' => '12/1/19']);
$validator = new DbDateValidator(['timeAttribute' => 'time']);
$validator->validateAttribute($model, 'date');
$this->assertEquals('2019-12-01 00:00:00', $model->date);
$this->assertEmpty($model->getErrors());
}
}

View File

@ -0,0 +1,35 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2018 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\tests\codeception\unit\libs;
use Codeception\Test\Unit;
use humhub\libs\DateHelper;
use humhub\libs\DbDateValidator;
use Yii;
/**
* Class MimeHelperTest
*/
class DateHelperTest extends Unit
{
public function _before()
{
parent::_before();
Yii::$app->timeZone = 'Europe/Berlin';
Yii::$app->formatter->timeZone = 'UTC';
}
public function testIsInDBFormat()
{
$this->assertTrue(DateHelper::isInDbFormat('2019-12-01'));
$this->assertTrue(DateHelper::isInDbFormat('2019-12-01 12:30:00'));
$this->assertFalse(DateHelper::isInDbFormat('2019-13-01'));
$this->assertFalse(DateHelper::isInDbFormat('2019-13-01 12:30:00'));
}
}

View File

@ -0,0 +1,14 @@
<?php
namespace humhub\tests\codeception\unit\libs;
use yii\base\Model;
class DateValidatorTestModel extends Model
{
public $date;
public $time;
}