Improved profile fields code.
This commit is contained in:
parent
2e49940260
commit
f32624c61d
11 changed files with 528 additions and 221 deletions
|
@ -4,6 +4,7 @@ namespace Misuzu;
|
|||
use InvalidArgumentException;
|
||||
use Index\ByteFormat;
|
||||
use Misuzu\Parsers\Parser;
|
||||
use Misuzu\Profile\ProfileFields;
|
||||
use Misuzu\Users\User;
|
||||
use Misuzu\Users\UserNotFoundException;
|
||||
use Misuzu\Users\UserSession;
|
||||
|
@ -34,6 +35,7 @@ if($profileUser->isDeleted()) {
|
|||
|
||||
$notices = [];
|
||||
|
||||
$profileFields = new ProfileFields($db);
|
||||
$currentUser = User::getCurrent();
|
||||
$viewingAsGuest = $currentUser === null;
|
||||
$currentUserId = $viewingAsGuest ? 0 : $currentUser->getId();
|
||||
|
@ -72,19 +74,37 @@ if($isEditing) {
|
|||
if(!CSRF::validateRequest()) {
|
||||
$notices[] = 'Couldn\'t verify you, please refresh the page and retry.';
|
||||
} else {
|
||||
if(!empty($_POST['profile']) && is_array($_POST['profile'])) {
|
||||
$profileFieldsSubmit = filter_input(INPUT_POST, 'profile', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY);
|
||||
|
||||
if(!empty($profileFieldsSubmit)) {
|
||||
if(!$perms['edit_profile']) {
|
||||
$notices[] = 'You\'re not allowed to edit your profile';
|
||||
} else {
|
||||
$profileFields = $profileUser->profileFields(false);
|
||||
$profileFieldInfos = $profileFields->getFields();
|
||||
$profileFieldsSetInfos = [];
|
||||
$profileFieldsSetValues = [];
|
||||
$profileFieldsRemove = [];
|
||||
|
||||
foreach($profileFields as $profileField) {
|
||||
if(isset($_POST['profile'][$profileField->field_key])
|
||||
&& $profileField->field_value !== $_POST['profile'][$profileField->field_key]
|
||||
&& !$profileField->setFieldValue($_POST['profile'][$profileField->field_key])) {
|
||||
$notices[] = sprintf('%s was formatted incorrectly!', $profileField->field_title);
|
||||
foreach($profileFieldInfos as $fieldInfo) {
|
||||
$fieldName = $fieldInfo->getName();
|
||||
$fieldValue = empty($profileFieldsSubmit[$fieldName]) ? '' : (string)filter_var($profileFieldsSubmit[$fieldName]);
|
||||
|
||||
if(empty($profileFieldsSubmit[$fieldName])) {
|
||||
$profileFieldsRemove[] = $fieldInfo;
|
||||
continue;
|
||||
}
|
||||
|
||||
if($fieldInfo->checkValue($fieldValue)) {
|
||||
$profileFieldsSetInfos[] = $fieldInfo;
|
||||
$profileFieldsSetValues[] = $fieldValue;
|
||||
} else
|
||||
$notices[] = sprintf('%s isn\'t properly formatted.', $fieldInfo->getTitle());
|
||||
}
|
||||
|
||||
if(!empty($profileFieldsRemove))
|
||||
$profileFields->removeFieldValues($profileUser, $profileFieldsRemove);
|
||||
if(!empty($profileFieldsSetInfos))
|
||||
$profileFields->setFieldValues($profileUser, $profileFieldsSetInfos, $profileFieldsSetValues);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,7 +117,7 @@ if($isEditing) {
|
|||
$aboutValid = User::validateProfileAbout($aboutParse, $aboutText);
|
||||
|
||||
if($aboutValid === '')
|
||||
$currentUser->setProfileAboutText($aboutText)->setProfileAboutParser($aboutParse);
|
||||
$profileUser->setProfileAboutText($aboutText)->setProfileAboutParser($aboutParse);
|
||||
else switch($aboutValid) {
|
||||
case 'parser':
|
||||
$notices[] = 'The selected about section parser is invalid.';
|
||||
|
@ -121,7 +141,7 @@ if($isEditing) {
|
|||
$sigValid = User::validateForumSignature($sigParse, $sigText);
|
||||
|
||||
if($sigValid === '')
|
||||
$currentUser->setForumSignatureText($sigText)->setForumSignatureParser($sigParse);
|
||||
$profileUser->setForumSignatureText($sigText)->setForumSignatureParser($sigParse);
|
||||
else switch($sigValid) {
|
||||
case 'parser':
|
||||
$notices[] = 'The selected forum signature parser is invalid.';
|
||||
|
@ -146,7 +166,7 @@ if($isEditing) {
|
|||
$birthValid = User::validateBirthdate($birthYear, $birthMonth, $birthDay);
|
||||
|
||||
if($birthValid === '')
|
||||
$currentUser->setBirthdate($birthYear, $birthMonth, $birthDay);
|
||||
$profileUser->setBirthdate($birthYear, $birthMonth, $birthDay);
|
||||
else switch($birthValid) {
|
||||
case 'year':
|
||||
$notices[] = 'The given birth year is invalid.';
|
||||
|
@ -352,21 +372,64 @@ switch($profileMode) {
|
|||
$template = 'profile.index';
|
||||
$warnings = $profileUser->getProfileWarnings($currentUser);
|
||||
|
||||
$activeCategoryStats = $viewingAsGuest ? null : forum_get_user_most_active_category_info($profileUser->getId());
|
||||
$activeCategoryInfo = empty($activeCategoryStats->forum_id) ? null : forum_get($activeCategoryStats->forum_id);
|
||||
|
||||
$activeTopicStats = $viewingAsGuest ? null : forum_get_user_most_active_topic_info($profileUser->getId());
|
||||
$activeTopicInfo = empty($activeTopicStats->topic_id) ? null : forum_topic_get($activeTopicStats->topic_id);
|
||||
|
||||
Template::set([
|
||||
'profile_warnings' => $warnings,
|
||||
'profile_warnings_view_private' => $canManageWarnings,
|
||||
'profile_warnings_can_manage' => $canManageWarnings,
|
||||
'profile_active_category_stats' => $activeCategoryStats,
|
||||
'profile_active_category_info' => $activeCategoryInfo,
|
||||
'profile_active_topic_stats' => $activeTopicStats,
|
||||
'profile_active_topic_info' => $activeTopicInfo,
|
||||
]);
|
||||
|
||||
if(!$viewingAsGuest) {
|
||||
$activeCategoryStats = forum_get_user_most_active_category_info($profileUser->getId());
|
||||
$activeCategoryInfo = empty($activeCategoryStats->forum_id) ? null : forum_get($activeCategoryStats->forum_id);
|
||||
|
||||
$activeTopicStats = forum_get_user_most_active_topic_info($profileUser->getId());
|
||||
$activeTopicInfo = empty($activeTopicStats->topic_id) ? null : forum_topic_get($activeTopicStats->topic_id);
|
||||
|
||||
$profileFieldValues = $profileFields->getFieldValues($profileUser);
|
||||
$profileFieldInfos = $profileFieldInfos ?? $profileFields->getFields(fieldValueInfos: $isEditing ? null : $profileFieldValues);
|
||||
$profileFieldFormats = $profileFields->getFieldFormats(fieldValueInfos: $profileFieldValues);
|
||||
|
||||
$profileFieldRawValues = [];
|
||||
$profileFieldLinkValues = [];
|
||||
$profileFieldDisplayValues = [];
|
||||
|
||||
// using field infos as the basis for now, uses the correct ordering
|
||||
foreach($profileFieldInfos as $fieldInfo) {
|
||||
foreach($profileFieldValues as $fieldValueTest)
|
||||
if($fieldValueTest->getFieldId() === $fieldInfo->getId()) {
|
||||
$fieldValue = $fieldValueTest;
|
||||
break;
|
||||
}
|
||||
|
||||
$fieldName = $fieldInfo->getName();
|
||||
|
||||
if(isset($fieldValue)) {
|
||||
foreach($profileFieldFormats as $fieldFormatTest)
|
||||
if($fieldFormatTest->getId() === $fieldValue->getFormatId()) {
|
||||
$fieldFormat = $fieldFormatTest;
|
||||
break;
|
||||
}
|
||||
|
||||
$profileFieldRawValues[$fieldName] = $fieldValue->getValue();
|
||||
$profileFieldDisplayValues[$fieldName] = $fieldFormat->formatDisplay($fieldValue->getValue());
|
||||
if($fieldFormat->hasLinkFormat())
|
||||
$profileFieldLinkValues[$fieldName] = $fieldFormat->formatLink($fieldValue->getValue());
|
||||
}
|
||||
|
||||
unset($fieldValue);
|
||||
}
|
||||
|
||||
Template::set([
|
||||
'profile_active_category_stats' => $activeCategoryStats,
|
||||
'profile_active_category_info' => $activeCategoryInfo,
|
||||
'profile_active_topic_stats' => $activeTopicStats,
|
||||
'profile_active_topic_info' => $activeTopicInfo,
|
||||
'profile_fields_infos' => $profileFieldInfos,
|
||||
'profile_fields_raw_values' => $profileFieldRawValues,
|
||||
'profile_fields_display_values' => $profileFieldDisplayValues,
|
||||
'profile_fields_link_values' => $profileFieldLinkValues,
|
||||
]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -380,5 +443,6 @@ if(!empty($template)) {
|
|||
'profile_can_edit' => $canEdit,
|
||||
'profile_is_editing' => $isEditing,
|
||||
'profile_is_banned' => $isBanned,
|
||||
'profile_is_guest' => $viewingAsGuest,
|
||||
]);
|
||||
}
|
||||
|
|
|
@ -122,7 +122,7 @@ class Changelog {
|
|||
$query .= (++$args > 1 ? ' AND' : ' WHERE');
|
||||
$query .= sprintf(
|
||||
' change_id IN (SELECT change_id FROM msz_changelog_change_tags WHERE tag_id IN (%s))',
|
||||
implode(', ', array_fill(0, count($tags), '?'))
|
||||
msz_where_in_list($tags)
|
||||
);
|
||||
}
|
||||
$stmt = $this->cache->get($query);
|
||||
|
@ -177,7 +177,7 @@ class Changelog {
|
|||
$query .= (++$args > 1 ? ' AND' : ' WHERE');
|
||||
$query .= sprintf(
|
||||
' change_id IN (SELECT change_id FROM msz_changelog_change_tags WHERE tag_id IN (%s))',
|
||||
implode(', ', array_fill(0, count($tags), '?'))
|
||||
msz_where_in_list($tags)
|
||||
);
|
||||
}
|
||||
$query .= ' GROUP BY change_created, change_id ORDER BY change_created DESC, change_id DESC';
|
||||
|
|
|
@ -23,10 +23,6 @@ class DbConfig implements IConfig {
|
|||
return preg_match('#^([a-z][a-zA-Z0-9._]+)$#', $name) === 1;
|
||||
}
|
||||
|
||||
private static function whereInList(int $count): string {
|
||||
return implode(', ', array_fill(0, $count, '?'));
|
||||
}
|
||||
|
||||
public function reset(): void {
|
||||
$this->values = [];
|
||||
}
|
||||
|
@ -61,7 +57,7 @@ class DbConfig implements IConfig {
|
|||
|
||||
$stmt = $this->cache->get(sprintf(
|
||||
'SELECT COUNT(*) FROM msz_config WHERE config_name IN (%s)',
|
||||
self::whereInList($nameCount)
|
||||
msz_where_in_list($nameCount)
|
||||
));
|
||||
for($i = 0; $i < $nameCount; ++$i)
|
||||
$stmt->addParameter($i + 1, $names[$i]);
|
||||
|
@ -87,7 +83,7 @@ class DbConfig implements IConfig {
|
|||
$nameCount = count($names);
|
||||
$stmt = $this->cache->get(sprintf(
|
||||
'DELETE FROM msz_config WHERE config_name IN (%s)',
|
||||
self::whereInList($nameCount)
|
||||
msz_where_in_list($nameCount)
|
||||
));
|
||||
|
||||
for($i = 0; $i < $nameCount; ++$i)
|
||||
|
@ -147,7 +143,7 @@ class DbConfig implements IConfig {
|
|||
|
||||
$stmt = $this->cache->get(sprintf(
|
||||
'SELECT config_name, config_value FROM msz_config WHERE config_name IN (%s)',
|
||||
self::whereInList($nameCount)
|
||||
msz_where_in_list($nameCount)
|
||||
));
|
||||
for($i = 0; $i < $nameCount; ++$i)
|
||||
$stmt->addParameter($i + 1, $names[$i]);
|
||||
|
@ -271,7 +267,7 @@ class DbConfig implements IConfig {
|
|||
|
||||
$stmt = $this->cache->get(sprintf(
|
||||
'REPLACE INTO msz_config (config_name, config_value) VALUES %s',
|
||||
implode(', ', array_fill(0, $valueCount, '(?, ?)'))
|
||||
msz_where_in_list($valueCount, '(?, ?)')
|
||||
));
|
||||
|
||||
$args = 0;
|
||||
|
|
56
src/Profile/ProfileFieldFormatInfo.php
Normal file
56
src/Profile/ProfileFieldFormatInfo.php
Normal file
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
namespace Misuzu\Profile;
|
||||
|
||||
use Index\Data\IDbResult;
|
||||
|
||||
class ProfileFieldFormatInfo {
|
||||
private string $id;
|
||||
private string $fieldId;
|
||||
private ?string $regex;
|
||||
private ?string $linkFormat;
|
||||
private string $displayFormat;
|
||||
|
||||
public function __construct(IDbResult $result) {
|
||||
$this->id = (string)$result->getInteger(0);
|
||||
$this->fieldId = (string)$result->getInteger(1);
|
||||
$this->regex = $result->isNull(2) ? null : $result->getString(2);
|
||||
$this->linkFormat = $result->isNull(3) ? null : $result->getString(3);
|
||||
$this->displayFormat = $result->getString(4);
|
||||
}
|
||||
|
||||
public function getId(): string {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getFieldId(): string {
|
||||
return $this->fieldId;
|
||||
}
|
||||
|
||||
public function hasRegEx(): bool {
|
||||
return $this->regex !== null;
|
||||
}
|
||||
|
||||
public function getRegEx(): ?string {
|
||||
return $this->regex;
|
||||
}
|
||||
|
||||
public function hasLinkFormat(): bool {
|
||||
return $this->linkFormat !== null;
|
||||
}
|
||||
|
||||
public function getLinkFormat(): ?string {
|
||||
return $this->linkFormat;
|
||||
}
|
||||
|
||||
public function formatLink(string $value): ?string {
|
||||
return $this->linkFormat === null ? null : sprintf($this->linkFormat, $value);
|
||||
}
|
||||
|
||||
public function getDisplayFormat(): string {
|
||||
return $this->displayFormat;
|
||||
}
|
||||
|
||||
public function formatDisplay(string $value): string {
|
||||
return sprintf($this->displayFormat, $value);
|
||||
}
|
||||
}
|
48
src/Profile/ProfileFieldInfo.php
Normal file
48
src/Profile/ProfileFieldInfo.php
Normal file
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
namespace Misuzu\Profile;
|
||||
|
||||
use Index\Data\IDbResult;
|
||||
|
||||
class ProfileFieldInfo {
|
||||
private string $id;
|
||||
private int $order;
|
||||
private string $name;
|
||||
private string $title;
|
||||
private string $regex;
|
||||
|
||||
public function __construct(IDbResult $result) {
|
||||
$this->id = (string)$result->getInteger(0);
|
||||
$this->order = $result->getInteger(1);
|
||||
$this->name = $result->getString(2);
|
||||
$this->title = $result->getString(3);
|
||||
$this->regex = $result->getString(4);
|
||||
}
|
||||
|
||||
public function getId(): string {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getOrder(): int {
|
||||
return $this->order;
|
||||
}
|
||||
|
||||
public function getName(): string {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getTitle(): string {
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function getRegEx(): string {
|
||||
return $this->regex;
|
||||
}
|
||||
|
||||
public function checkValue(string $value): bool {
|
||||
return preg_match($this->regex, $value) === 1;
|
||||
}
|
||||
|
||||
public function matchValue(string $value): string|false {
|
||||
return preg_match($this->regex, $value, $matches) === 1 ? $matches[1] : false;
|
||||
}
|
||||
}
|
34
src/Profile/ProfileFieldValueInfo.php
Normal file
34
src/Profile/ProfileFieldValueInfo.php
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
namespace Misuzu\Profile;
|
||||
|
||||
use Index\Data\IDbResult;
|
||||
|
||||
class ProfileFieldValueInfo {
|
||||
private string $fieldId;
|
||||
private string $userId;
|
||||
private string $formatId;
|
||||
private string $value;
|
||||
|
||||
public function __construct(IDbResult $result) {
|
||||
$this->fieldId = (string)$result->getInteger(0);
|
||||
$this->userId = (string)$result->getInteger(1);
|
||||
$this->formatId = (string)$result->getInteger(2);
|
||||
$this->value = $result->getString(3);
|
||||
}
|
||||
|
||||
public function getFieldId(): string {
|
||||
return $this->fieldId;
|
||||
}
|
||||
|
||||
public function getUserId(): string {
|
||||
return $this->userId;
|
||||
}
|
||||
|
||||
public function getFormatId(): string {
|
||||
return $this->formatId;
|
||||
}
|
||||
|
||||
public function getValue(): string {
|
||||
return $this->value;
|
||||
}
|
||||
}
|
273
src/Profile/ProfileFields.php
Normal file
273
src/Profile/ProfileFields.php
Normal file
|
@ -0,0 +1,273 @@
|
|||
<?php
|
||||
namespace Misuzu\Profile;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
use Index\Data\IDbConnection;
|
||||
use Index\Data\IDbResult;
|
||||
use Misuzu\DbStatementCache;
|
||||
use Misuzu\Users\User;
|
||||
|
||||
class ProfileFields {
|
||||
private IDbConnection $dbConn;
|
||||
private DbStatementCache $cache;
|
||||
|
||||
public function __construct(IDbConnection $dbConn) {
|
||||
$this->dbConn = $dbConn;
|
||||
$this->cache = new DbStatementCache($dbConn);
|
||||
}
|
||||
|
||||
public function getFields(?array $fieldValueInfos = null): array {
|
||||
$hasFieldValueInfos = $fieldValueInfos !== null;
|
||||
if($hasFieldValueInfos && empty($fieldValueInfos))
|
||||
return [];
|
||||
|
||||
$query = 'SELECT field_id, field_order, field_key, field_title, field_regex FROM msz_profile_fields';
|
||||
if($hasFieldValueInfos)
|
||||
$query .= sprintf(' WHERE field_id IN (%s)', msz_where_in_list($fieldValueInfos));
|
||||
$query .= ' ORDER BY field_order ASC';
|
||||
|
||||
$stmt = $this->cache->get($query);
|
||||
$args = 0;
|
||||
|
||||
if($hasFieldValueInfos)
|
||||
foreach($fieldValueInfos as $fieldValueInfo) {
|
||||
if(!($fieldValueInfo instanceof ProfileFieldValueInfo))
|
||||
throw new InvalidArgumentException('All values in $fieldValueInfos must be of ProfileFieldValueInfo type.');
|
||||
$stmt->addParameter(++$args, $fieldValueInfo->getFieldId());
|
||||
}
|
||||
|
||||
$stmt->execute();
|
||||
$result = $stmt->getResult();
|
||||
$fields = [];
|
||||
|
||||
while($result->next())
|
||||
$fields[] = new ProfileFieldInfo($result);
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
public function getField(string $fieldId): ProfileFieldInfo {
|
||||
$stmt = $this->cache->get('SELECT field_id, field_order, field_key, field_title, field_regex FROM msz_profile_fields WHERE field_id = ?');
|
||||
$stmt->addParameter(1, $fieldId);
|
||||
$stmt->execute();
|
||||
|
||||
$result = $stmt->getResult();
|
||||
if(!$result->next())
|
||||
throw new RuntimeException('No field found with the provided field id.');
|
||||
|
||||
return new ProfileFieldInfo($result);
|
||||
}
|
||||
|
||||
public function getFieldFormats(
|
||||
?array $fieldInfos = null,
|
||||
?array $fieldValueInfos = null
|
||||
): array {
|
||||
$hasFieldInfos = $fieldInfos !== null;
|
||||
$hasFieldValueInfos = $fieldValueInfos !== null;
|
||||
|
||||
if($hasFieldInfos && empty($fieldInfos))
|
||||
return [];
|
||||
if($hasFieldValueInfos && empty($fieldValueInfos))
|
||||
return [];
|
||||
|
||||
$args = 0;
|
||||
$query = 'SELECT format_id, field_id, format_regex, format_link, format_display FROM msz_profile_fields_formats';
|
||||
|
||||
if($hasFieldInfos) {
|
||||
++$args;
|
||||
$query .= sprintf(' WHERE field_id IN (%s)', msz_where_in_list($fieldInfos));
|
||||
}
|
||||
if($hasFieldValueInfos)
|
||||
$query .= sprintf(' %s format_id IN (%s)',
|
||||
(++$args > 1 ? 'AND' : 'WHERE'),
|
||||
msz_where_in_list($fieldValueInfos)
|
||||
);
|
||||
|
||||
$stmt = $this->cache->get($query);
|
||||
$args = 0;
|
||||
|
||||
if($hasFieldInfos)
|
||||
foreach($fieldInfos as $fieldInfo) {
|
||||
if(!($fieldInfo instanceof ProfileFieldInfo))
|
||||
throw new InvalidArgumentException('All values in $fieldInfos must be of ProfileFieldInfo type.');
|
||||
$stmt->addParameter(++$args, $fieldInfo->getId());
|
||||
}
|
||||
|
||||
if($hasFieldValueInfos)
|
||||
foreach($fieldValueInfos as $fieldValueInfo) {
|
||||
if(!($fieldValueInfo instanceof ProfileFieldValueInfo))
|
||||
throw new InvalidArgumentException('All values in $fieldValueInfos must be of ProfileFieldValueInfo type.');
|
||||
$stmt->addParameter(++$args, $fieldValueInfo->getFormatId());
|
||||
}
|
||||
|
||||
$stmt->execute();
|
||||
$result = $stmt->getResult();
|
||||
$formats = [];
|
||||
|
||||
while($result->next())
|
||||
$formats[] = new ProfileFieldFormatInfo($result);
|
||||
|
||||
return $formats;
|
||||
}
|
||||
|
||||
public function getFieldFormat(string $formatId): ProfileFieldFormatInfo {
|
||||
$stmt = $this->cache->get('SELECT format_id, field_id, format_regex, format_link, format_display FROM msz_profile_fields_formats WHERE format_id = ?');
|
||||
$stmt->addParameter(1, $formatId);
|
||||
$stmt->execute();
|
||||
|
||||
$result = $stmt->getResult();
|
||||
if(!$result->next())
|
||||
throw new RuntimeException('No format found with the provided format id.');
|
||||
|
||||
return new ProfileFieldFormatInfo($result);
|
||||
}
|
||||
|
||||
public function selectFieldFormat(
|
||||
ProfileFieldInfo|string $fieldInfo,
|
||||
string $value
|
||||
): ProfileFieldFormatInfo {
|
||||
if($fieldInfo instanceof ProfileFieldInfo)
|
||||
$fieldInfo = $fieldInfo->getId();
|
||||
|
||||
$stmt = $this->cache->get('SELECT format_id, field_id, format_regex, format_link, format_display FROM msz_profile_fields_formats WHERE field_id = ? AND (format_regex IS NULL OR ? REGEXP format_regex) ORDER BY format_regex IS NULL ASC');
|
||||
$stmt->addParameter(1, $fieldInfo);
|
||||
$stmt->addParameter(2, $value);
|
||||
$stmt->execute();
|
||||
|
||||
$result = $stmt->getResult();
|
||||
if(!$result->next())
|
||||
throw new RuntimeException('Could not determine an appropriate format for this field (missing default formatting)');
|
||||
|
||||
return new ProfileFieldFormatInfo($result);
|
||||
}
|
||||
|
||||
public function getFieldValues(User|string $userInfo): array {
|
||||
if($userInfo instanceof User)
|
||||
$userInfo = $userInfo->getId();
|
||||
|
||||
// i don't really want to bother with the join for the ordering so i'll just do that somewhere in PHP for now
|
||||
// will probably add the ability for people to order them in whatever way they want, as well as visibility controls
|
||||
$stmt = $this->cache->get('SELECT field_id, user_id, format_id, field_value FROM msz_profile_fields_values WHERE user_id = ?');
|
||||
$stmt->addParameter(1, $userInfo);
|
||||
$stmt->execute();
|
||||
|
||||
$result = $stmt->getResult();
|
||||
$values = [];
|
||||
|
||||
while($result->next())
|
||||
$values[] = new ProfileFieldValueInfo($result);
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
public function getFieldValue(
|
||||
ProfileFieldInfo|string $fieldInfo,
|
||||
User|string $userInfo
|
||||
): ProfileFieldValueInfo {
|
||||
if($fieldInfo instanceof ProfileFieldInfo)
|
||||
$fieldInfo = $fieldInfo->getId();
|
||||
if($userInfo instanceof User)
|
||||
$userInfo = $userInfo->getId();
|
||||
|
||||
$stmt = $this->cache->get('SELECT field_id, user_id, format_id, field_value FROM msz_profile_fields_values WHERE field_id = ? AND user_id = ?');
|
||||
$stmt->addParameter(1, $fieldInfo);
|
||||
$stmt->addParameter(2, $userInfo);
|
||||
$stmt->execute();
|
||||
|
||||
$result = $stmt->getResult();
|
||||
|
||||
if(!$result->next())
|
||||
throw new RuntimeException('No value for this field and user combination found.');
|
||||
|
||||
return new ProfileFieldValueInfo($result);
|
||||
}
|
||||
|
||||
public function setFieldValues(
|
||||
User|string $userInfo,
|
||||
ProfileFieldInfo|string|array $fieldInfos,
|
||||
string|array $values
|
||||
): void {
|
||||
if(empty($fieldInfos))
|
||||
return;
|
||||
|
||||
if(!is_array($fieldInfos)) {
|
||||
if(is_array($values))
|
||||
throw new InvalidArgumentException('If $fieldInfos is not an array, $values may not be either');
|
||||
$fieldInfos = [$fieldInfos];
|
||||
$values = [$values];
|
||||
} elseif(!is_array($values))
|
||||
throw new InvalidArgumentException('If $fieldInfos is an array, $values must be as well.');
|
||||
|
||||
$fieldsCount = count($fieldInfos);
|
||||
if($fieldsCount !== count($values))
|
||||
throw new InvalidArgumentException('$fieldsInfos and $values have the same amount of values and be in the same order.');
|
||||
|
||||
if($userInfo instanceof User)
|
||||
$userInfo = $userInfo->getId();
|
||||
|
||||
$rows = [];
|
||||
|
||||
foreach($fieldInfos as $key => $fieldInfo) {
|
||||
if(is_string($fieldInfo))
|
||||
$fieldInfo = $this->getField($fieldId);
|
||||
elseif(!($fieldInfo instanceof ProfileFieldInfo))
|
||||
throw new InvalidArgumentException('Entries of $fieldInfos must either be field IDs or instances of ProfileFieldInfo.');
|
||||
|
||||
$value = $fieldInfo->matchValue($values[$key]);
|
||||
if($value === false)
|
||||
throw new InvalidArgumentException('One of the values in $values is not correct formatted.');
|
||||
|
||||
$rows[] = [
|
||||
$fieldInfo->getId(),
|
||||
$this->selectFieldFormat($fieldInfo, $value)->getId(),
|
||||
$value,
|
||||
];
|
||||
}
|
||||
|
||||
$args = 0;
|
||||
$stmt = $this->cache->get(
|
||||
'REPLACE INTO msz_profile_fields_values (field_id, user_id, format_id, field_value) VALUES '
|
||||
. msz_where_in_list($rows, '(?, ?, ?, ?)')
|
||||
);
|
||||
|
||||
foreach($rows as $row) {
|
||||
$stmt->addParameter(++$args, $row[0]);
|
||||
$stmt->addParameter(++$args, $userInfo);
|
||||
$stmt->addParameter(++$args, $row[1]);
|
||||
$stmt->addParameter(++$args, $row[2]);
|
||||
}
|
||||
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
public function removeFieldValues(
|
||||
User|string $userInfo,
|
||||
ProfileFieldInfo|string|array $fieldInfos
|
||||
): void {
|
||||
if(empty($fieldInfos))
|
||||
return;
|
||||
if($userInfo instanceof User)
|
||||
$userInfo = $userInfo->getId();
|
||||
|
||||
if(!is_array($fieldInfos))
|
||||
$fieldInfos = [$fieldInfos];
|
||||
|
||||
foreach($fieldInfos as $key => $value) {
|
||||
if($value instanceof ProfileFieldInfo)
|
||||
$fieldInfos[$key] = $value->getId();
|
||||
elseif(is_string($value))
|
||||
throw new InvalidArgumentException('$fieldInfos array may only contain string IDs or instances of ProfileFieldInfo');
|
||||
}
|
||||
|
||||
$args = 0;
|
||||
$stmt = $this->cache->get(sprintf(
|
||||
'DELETE FROM msz_profile_fields_values WHERE user_id = ? AND field_id IN (%s)',
|
||||
msz_where_in_list($fieldInfos)
|
||||
));
|
||||
$stmt->addParameter(++$args, $userInfo);
|
||||
foreach($fieldInfos as $value)
|
||||
$stmt->addParameter(++$args, $value);
|
||||
$stmt->execute();
|
||||
}
|
||||
}
|
|
@ -1,164 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu\Users;
|
||||
|
||||
use Misuzu\DB;
|
||||
|
||||
class ProfileField {
|
||||
// Database fields
|
||||
public $field_id;
|
||||
public $user_id;
|
||||
public $field_regex;
|
||||
public $field_value;
|
||||
public $field_order;
|
||||
public $field_key;
|
||||
public $field_title;
|
||||
public $format_id;
|
||||
public $format_regex;
|
||||
public $format_link;
|
||||
public $format_display;
|
||||
|
||||
public static function createField(
|
||||
string $fieldKey,
|
||||
string $fieldTitle,
|
||||
string $fieldRegex,
|
||||
int $fieldOrder
|
||||
): ?ProfileField {
|
||||
$createField = DB::prepare('
|
||||
INSERT INTO `msz_profile_fields` (
|
||||
`field_order`, `field_key`, `field_title`, `field_regex`
|
||||
) VALUES (:order, :key, :title, :regex)
|
||||
')->bind('order', $fieldOrder)->bind('key', $fieldKey)
|
||||
->bind('title', $fieldTitle)->bind('regex', $fieldRegex)
|
||||
->executeGetId();
|
||||
|
||||
if($createField < 1)
|
||||
return null;
|
||||
|
||||
return static::get($createField);
|
||||
}
|
||||
public static function createFormat(
|
||||
int $fieldId,
|
||||
string $formatDisplay = '%s',
|
||||
?string $formatLink = null,
|
||||
?string $formatRegex = null
|
||||
): ?ProfileField {
|
||||
$createFormat = DB::prepare('
|
||||
INSERT INTO `msz_profile_fields_formats` (
|
||||
`field_id`, `format_regex`, `format_link`, `format_display`
|
||||
) VALUES (:field, :regex, :link, :display)
|
||||
')->bind('field', $fieldId) ->bind('regex', $formatRegex)
|
||||
->bind('link', $formatLink)->bind('display', $formatDisplay)
|
||||
->executeGetId();
|
||||
|
||||
if($createFormat < 1)
|
||||
return null;
|
||||
|
||||
return static::get($createFormat);
|
||||
}
|
||||
|
||||
public static function get(int $fieldId): ?ProfileField {
|
||||
return DB::prepare(
|
||||
'SELECT `field_id`, `field_order`, `field_key`, `field_title`, `field_regex`'
|
||||
. ' FROM `msz_profile_fields`'
|
||||
. ' WHERE `field_id` = :field_id'
|
||||
)->bind('field_id', $fieldId)->fetchObject(ProfileField::class);
|
||||
}
|
||||
|
||||
public static function user(int $userId, bool $filterEmpty = true): array {
|
||||
$fields = DB::prepare(
|
||||
'SELECT pf.`field_id`, pf.`field_order`, pf.`field_key`, pf.`field_title`, pf.`field_regex`'
|
||||
. ', pff.`format_id`, pff.`format_regex`, pff.`format_link`, pff.`format_display`'
|
||||
. ', COALESCE(pfv.`user_id`, :user2) AS `user_id`, pfv.`field_value`'
|
||||
. ' FROM `msz_profile_fields` AS pf'
|
||||
. ' LEFT JOIN `msz_profile_fields_values` AS pfv ON pfv.`field_id` = pf.`field_id` AND pfv.`user_id` = :user1'
|
||||
. ' LEFT JOIN `msz_profile_fields_formats` AS pff ON pff.`field_id` = pf.`field_id` AND pff.`format_id` = pfv.`format_id`'
|
||||
. ' ORDER BY pf.`field_order`'
|
||||
)->bind('user1', $userId)->bind('user2', $userId)->fetchObjects(ProfileField::class);
|
||||
|
||||
if($filterEmpty) {
|
||||
$newFields = [];
|
||||
|
||||
foreach($fields as $field) {
|
||||
if(!empty($field->field_value))
|
||||
$newFields[] = $field;
|
||||
}
|
||||
|
||||
$fields = $newFields;
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
public function findDisplayFormat(string $value): int {
|
||||
if(!isset($this->field_id))
|
||||
return 0;
|
||||
|
||||
$format = DB::prepare('
|
||||
SELECT `format_id`
|
||||
FROM `msz_profile_fields_formats`
|
||||
WHERE `field_id` = :field
|
||||
AND `format_regex` IS NOT NULL
|
||||
AND :value REGEXP `format_regex`
|
||||
')->bind('field', $this->field_id)
|
||||
->bind('value', $value)
|
||||
->fetchColumn();
|
||||
|
||||
if($format < 1) {
|
||||
$format = DB::prepare('
|
||||
SELECT `format_id`
|
||||
FROM `msz_profile_fields_formats`
|
||||
WHERE `field_id` = :field
|
||||
AND `format_regex` IS NULL
|
||||
')->bind('field', $this->field_id)
|
||||
->fetchColumn(0, 0);
|
||||
}
|
||||
|
||||
return $format;
|
||||
}
|
||||
|
||||
// todo: use exceptions
|
||||
public function setFieldValue(string $value): bool {
|
||||
if(!isset($this->user_id, $this->field_id, $this->field_regex))
|
||||
return false;
|
||||
|
||||
if(empty($value)) {
|
||||
DB::prepare('
|
||||
DELETE FROM `msz_profile_fields_values`
|
||||
WHERE `user_id` = :user
|
||||
AND `field_id` = :field
|
||||
')->bind('user', $this->user_id)
|
||||
->bind('field', $this->field_id)
|
||||
->execute();
|
||||
$this->field_value = '';
|
||||
return true;
|
||||
}
|
||||
|
||||
if(preg_match($this->field_regex, $value, $matches)) {
|
||||
$value = $matches[1];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
$displayFormat = $this->findDisplayFormat($value);
|
||||
|
||||
if($displayFormat < 1)
|
||||
return false;
|
||||
|
||||
$updateField = DB::prepare('
|
||||
REPLACE INTO `msz_profile_fields_values`
|
||||
(`field_id`, `user_id`, `format_id`, `field_value`)
|
||||
VALUES
|
||||
(:field, :user, :format, :value)
|
||||
')->bind('field', $this->field_id)
|
||||
->bind('user', $this->user_id)
|
||||
->bind('format', $displayFormat)
|
||||
->bind('value', $value)
|
||||
->execute();
|
||||
|
||||
if(!$updateField)
|
||||
return false;
|
||||
|
||||
$this->field_value = $value;
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -335,12 +335,6 @@ class User implements HasRankInterface {
|
|||
return (int)$this->getBirthdate()->diff(new DateTime('now', new DateTimeZone('UTC')))->format('%y');
|
||||
}
|
||||
|
||||
public function profileFields(bool $filterEmpty = true): array {
|
||||
if(($userId = $this->getId()) < 1)
|
||||
return [];
|
||||
return ProfileField::user($userId, $filterEmpty);
|
||||
}
|
||||
|
||||
public function bumpActivity(string $lastRemoteAddress): void {
|
||||
$this->user_active = time();
|
||||
$this->last_ip = $lastRemoteAddress;
|
||||
|
|
|
@ -77,12 +77,11 @@
|
|||
{% endif %}
|
||||
|
||||
<div class="profile__content">
|
||||
{% set profile_fields = profile_user.profileFields(not (profile_is_editing and perms.edit_profile))|default([]) %}
|
||||
{% set show_profile_fields = profile_is_editing ? perms.edit_profile : profile_fields|length > 0 %}
|
||||
{% set show_profile_fields = not profile_is_guest and (profile_is_editing ? perms.edit_profile : profile_fields_display_values is not empty) %}
|
||||
{% set show_background_settings = profile_is_editing and perms.edit_background %}
|
||||
{% set show_birthdate = profile_is_editing and perms.edit_birthdate %}
|
||||
{% set show_active_forum_info = not profile_is_editing and (profile_active_category_info.forum_id|default(0) > 0 or profile_active_topic_info.topic_id|default(0) > 0) %}
|
||||
{% set show_sidebar = show_profile_fields or show_background_settings or show_birthdate or show_active_forum_info %}
|
||||
{% set show_sidebar = profile_is_guest or show_profile_fields or show_background_settings or show_birthdate or show_active_forum_info %}
|
||||
|
||||
{% if show_sidebar %}
|
||||
<div class="profile__content__side">
|
||||
|
@ -114,25 +113,26 @@
|
|||
{{ container_title('Elsewhere') }}
|
||||
|
||||
<div class="profile__accounts__content">
|
||||
{% for field in profile_fields %}
|
||||
<label class="profile__accounts__item">
|
||||
<div class="profile__accounts__title">
|
||||
{{ field.field_title }}
|
||||
</div>
|
||||
|
||||
{% if profile_is_editing %}
|
||||
{{ input_text('profile[' ~ field.field_key ~ ']', 'profile__accounts__input', field.field_value, data.type|default('text')) }}
|
||||
{% else %}
|
||||
<div class="profile__accounts__value">
|
||||
{% set profile_field_value = field.format_display|format(field.field_value) %}
|
||||
{% if field.format_link is empty %}
|
||||
{{ profile_field_value }}
|
||||
{% else %}
|
||||
<a href="{{ field.format_link|format(field.field_value) }}" class="profile__accounts__link" target="_blank" rel="noreferrer noopener">{{ profile_field_value }}</a>
|
||||
{% endif %}
|
||||
{% for fieldInfo in profile_fields_infos %}
|
||||
{% if profile_is_editing or profile_fields_display_values[fieldInfo.name] is defined %}
|
||||
<label class="profile__accounts__item">
|
||||
<div class="profile__accounts__title">
|
||||
{{ fieldInfo.title }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</label>
|
||||
|
||||
{% if profile_is_editing %}
|
||||
{{ input_text('profile[' ~ fieldInfo.name ~ ']', 'profile__accounts__input', profile_fields_raw_values[fieldInfo.name]|default('')) }}
|
||||
{% else %}
|
||||
<div class="profile__accounts__value">
|
||||
{% if profile_fields_link_values[fieldInfo.name] is defined %}
|
||||
<a href="{{ profile_fields_link_values[fieldInfo.name] }}" class="profile__accounts__link" target="_blank" rel="noreferrer noopener">{{ profile_fields_display_values[fieldInfo.name] }}</a>
|
||||
{% else %}
|
||||
{{ profile_fields_display_values[fieldInfo.name] }}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</label>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
<?php
|
||||
use Index\Colour\Colour;
|
||||
|
||||
function msz_where_in_list(Countable|array|int $count, string $repeat = '?', string $separator = ', '): string {
|
||||
if(is_countable($count))
|
||||
$count = count($count);
|
||||
return implode($separator, array_fill(0, $count, $repeat));
|
||||
}
|
||||
|
||||
// render_error and render_info need to be nuked from orbit
|
||||
|
||||
function render_error(int $code, string $template = 'errors.%d'): string {
|
||||
|
|
Loading…
Reference in a new issue