first commit

This commit is contained in:
salam committed 2026-10-06 19:40:28 +02:00
commit e81864e683
2984 files changed
+267797

No files matched your search

@@ -0,0 +1,3 @@
files:
- source: /Resources/Private/Language/
translation: /%original_path%/%two_letters_code%.%original_file_name%
@@ -0,0 +1,17 @@
#!/bin/bash
## Description: import given test data into ddev (database and configurations)
## Usage: initialize
## Example: "ddev import-data"
if ! command -v "git-lfs" &> /dev/null
then
echo "COMMAND "git lfs" could not be found"
echo "Please install git-lfs support, see: https://git-lfs.github.com/"
exit
fi
git lfs pull
ddev import-db --src=.project/data/db.sql.gz
cp .ddev/typo3/AdditionalConfiguration.php .Build/Web/typo3conf/
ddev typo3cms install:setup
+10
View File
@@ -0,0 +1,10 @@
#!/bin/bash
# This assumes that the typo3 command will be in the $PATH; if in vendor/bin/ it will be
## Description: Run TYPO3 CLI (typo3) command inside the web container
## Usage: typo3 [args]
## Example: "ddev typo3 site:list" or "ddev typo3 list" or "ddev typo3 extension:list"
## ProjectTypes: typo3
.Build/bin/typo3 "$@"
+10
View File
@@ -0,0 +1,10 @@
#!/bin/bash
# This assumes that the typo3cms command will be in the $PATH; if in vendor/bin/ it will be
## Description: Run TYPO3 Console (typo3cms) command inside the web container
## Usage: typo3cms [args]
## Example: "ddev typo3cms cache:flush" or "ddev typo3cms database:export"
## ProjectTypes: typo3
.Build/bin/typo3cms "$@"
@@ -0,0 +1,299 @@
name: femanager7
type: typo3
docroot: .Build/Web/
php_version: "8.0"
webserver_type: nginx-fpm
router_http_port: "80"
router_https_port: "443"
xdebug_enabled: false
additional_hostnames: []
additional_fqdns: []
database:
type: mariadb
version: "10.3"
hooks:
post-start:
- exec: composer install
- exec: cp .ddev/typo3/AdditionalConfiguration.php .Build/Web/typo3conf/
use_dns_when_possible: true
composer_version: "2"
web_environment:
- TYPO3_CONTEXT=Development/DDEV
- TYPO3_INSTALL_DB_DRIVER=mysql
- TYPO3_INSTALL_DB_USER=db
- TYPO3_INSTALL_DB_PASSWORD=db
- TYPO3_INSTALL_DB_HOST=db
- TYPO3_INSTALL_DB_PORT=3306
- TYPO3_INSTALL_DB_USE_EXISTING=1
- TYPO3_INSTALL_DB_DBNAME=db
- TYPO3_INSTALL_SITE_SETUP_TYPE=no
- TYPO3_INSTALL_WEB_SERVER_CONFIG=none
nodejs_version: "18"
corepack_enable: false
# Key features of DDEV's config.yaml:
# name: <projectname> # Name of the project, automatically provides
# http://projectname.ddev.site and https://projectname.ddev.site
# type: <projecttype> # backdrop, craftcms, django4, drupal, drupal6, drupal7, laravel, magento, magento2, php, python, shopware6, silverstripe, typo3, wordpress
# See https://ddev.readthedocs.io/en/stable/users/quickstart/ for more
# information on the different project types
# "drupal" covers recent Drupal 8+
# docroot: <relative_path> # Relative path to the directory containing index.php.
# php_version: "8.2" # PHP version to use, "5.6", "7.0", "7.1", "7.2", "7.3", "7.4", "8.0", "8.1", "8.2", "8.3"
# You can explicitly specify the webimage but this
# is not recommended, as the images are often closely tied to DDEV's' behavior,
# so this can break upgrades.
# webimage: <docker_image> # nginx/php docker image.
# database:
# type: <dbtype> # mysql, mariadb, postgres
# version: <version> # database version, like "10.11" or "8.0"
# MariaDB versions can be 5.5-10.8 and 10.11, MySQL versions can be 5.5-8.0
# PostgreSQL versions can be 9-16.
# router_http_port: <port> # Port to be used for http (defaults to global configuration, usually 80)
# router_https_port: <port> # Port for https (defaults to global configuration, usually 443)
# xdebug_enabled: false # Set to true to enable Xdebug and "ddev start" or "ddev restart"
# Note that for most people the commands
# "ddev xdebug" to enable Xdebug and "ddev xdebug off" to disable it work better,
# as leaving Xdebug enabled all the time is a big performance hit.
# xhprof_enabled: false # Set to true to enable Xhprof and "ddev start" or "ddev restart"
# Note that for most people the commands
# "ddev xhprof" to enable Xhprof and "ddev xhprof off" to disable it work better,
# as leaving Xhprof enabled all the time is a big performance hit.
# webserver_type: nginx-fpm, apache-fpm, or nginx-gunicorn
# timezone: Europe/Berlin
# This is the timezone used in the containers and by PHP;
# it can be set to any valid timezone,
# see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
# For example Europe/Dublin or MST7MDT
# composer_root: <relative_path>
# Relative path to the Composer root directory from the project root. This is
# the directory which contains the composer.json and where all Composer related
# commands are executed.
# composer_version: "2"
# You can set it to "" or "2" (default) for Composer v2 or "1" for Composer v1
# to use the latest major version available at the time your container is built.
# It is also possible to use each other Composer version channel. This includes:
# - 2.2 (latest Composer LTS version)
# - stable
# - preview
# - snapshot
# Alternatively, an explicit Composer version may be specified, for example "2.2.18".
# To reinstall Composer after the image was built, run "ddev debug refresh".
# nodejs_version: "20"
# change from the default system Node.js version to any other version.
# Numeric version numbers can be complete (i.e. 18.15.0) or
# incomplete (18, 17.2, 16). 'lts' and 'latest' can be used as well along with
# other named releases.
# see https://www.npmjs.com/package/n#specifying-nodejs-versions
# Note that you can continue using 'ddev nvm' or nvm inside the web container
# to change the project's installed node version if you need to.
# corepack_enable: false
# Change to 'true' to 'corepack enable' and gain access to latest versions of yarn/pnpm
# additional_hostnames:
# - somename
# - someothername
# would provide http and https URLs for "somename.ddev.site"
# and "someothername.ddev.site".
# additional_fqdns:
# - example.com
# - sub1.example.com
# would provide http and https URLs for "example.com" and "sub1.example.com"
# Please take care with this because it can cause great confusion.
# upload_dirs: "custom/upload/dir"
#
# upload_dirs:
# - custom/upload/dir
# - ../private
#
# would set the destination paths for ddev import-files to <docroot>/custom/upload/dir
# When Mutagen is enabled this path is bind-mounted so that all the files
# in the upload_dirs don't have to be synced into Mutagen.
# disable_upload_dirs_warning: false
# If true, turns off the normal warning that says
# "You have Mutagen enabled and your 'php' project type doesn't have upload_dirs set"
# ddev_version_constraint: ""
# Example:
# ddev_version_constraint: ">= 1.22.4"
# This will enforce that the running ddev version is within this constraint.
# See https://github.com/Masterminds/semver#checking-version-constraints for
# supported constraint formats
# working_dir:
# web: /var/www/html
# db: /home
# would set the default working directory for the web and db services.
# These values specify the destination directory for ddev ssh and the
# directory in which commands passed into ddev exec are run.
# omit_containers: [db, ddev-ssh-agent]
# Currently only these containers are supported. Some containers can also be
# omitted globally in the ~/.ddev/global_config.yaml. Note that if you omit
# the "db" container, several standard features of DDEV that access the
# database container will be unusable. In the global configuration it is also
# possible to omit ddev-router, but not here.
# performance_mode: "global"
# DDEV offers performance optimization strategies to improve the filesystem
# performance depending on your host system. Should be configured globally.
#
# If set, will override the global config. Possible values are:
# - "global": uses the value from the global config.
# - "none": disables performance optimization for this project.
# - "mutagen": enables Mutagen for this project.
# - "nfs": enables NFS for this project.
#
# See https://ddev.readthedocs.io/en/stable/users/install/performance/#nfs
# See https://ddev.readthedocs.io/en/stable/users/install/performance/#mutagen
# fail_on_hook_fail: False
# Decide whether 'ddev start' should be interrupted by a failing hook
# host_https_port: "59002"
# The host port binding for https can be explicitly specified. It is
# dynamic unless otherwise specified.
# This is not used by most people, most people use the *router* instead
# of the localhost port.
# host_webserver_port: "59001"
# The host port binding for the ddev-webserver can be explicitly specified. It is
# dynamic unless otherwise specified.
# This is not used by most people, most people use the *router* instead
# of the localhost port.
# host_db_port: "59002"
# The host port binding for the ddev-dbserver can be explicitly specified. It is dynamic
# unless explicitly specified.
# mailpit_http_port: "8025"
# mailpit_https_port: "8026"
# The Mailpit ports can be changed from the default 8025 and 8026
# host_mailpit_port: "8025"
# The mailpit port is not normally bound on the host at all, instead being routed
# through ddev-router, but it can be bound directly to localhost if specified here.
# webimage_extra_packages: [php7.4-tidy, php-bcmath]
# Extra Debian packages that are needed in the webimage can be added here
# dbimage_extra_packages: [telnet,netcat]
# Extra Debian packages that are needed in the dbimage can be added here
# use_dns_when_possible: true
# If the host has internet access and the domain configured can
# successfully be looked up, DNS will be used for hostname resolution
# instead of editing /etc/hosts
# Defaults to true
# project_tld: ddev.site
# The top-level domain used for project URLs
# The default "ddev.site" allows DNS lookup via a wildcard
# If you prefer you can change this to "ddev.local" to preserve
# pre-v1.9 behavior.
# ngrok_args: --basic-auth username:pass1234
# Provide extra flags to the "ngrok http" command, see
# https://ngrok.com/docs/ngrok-agent/config or run "ngrok http -h"
# disable_settings_management: false
# If true, DDEV will not create CMS-specific settings files like
# Drupal's settings.php/settings.ddev.php or TYPO3's AdditionalConfiguration.php
# In this case the user must provide all such settings.
# You can inject environment variables into the web container with:
# web_environment:
# - SOMEENV=somevalue
# - SOMEOTHERENV=someothervalue
# no_project_mount: false
# (Experimental) If true, DDEV will not mount the project into the web container;
# the user is responsible for mounting it manually or via a script.
# This is to enable experimentation with alternate file mounting strategies.
# For advanced users only!
# bind_all_interfaces: false
# If true, host ports will be bound on all network interfaces,
# not the localhost interface only. This means that ports
# will be available on the local network if the host firewall
# allows it.
# default_container_timeout: 120
# The default time that DDEV waits for all containers to become ready can be increased from
# the default 120. This helps in importing huge databases, for example.
#web_extra_exposed_ports:
#- name: nodejs
# container_port: 3000
# http_port: 2999
# https_port: 3000
#- name: something
# container_port: 4000
# https_port: 4000
# http_port: 3999
# Allows a set of extra ports to be exposed via ddev-router
# Fill in all three fields even if you don’t intend to use the https_port!
# If you don’t add https_port, then it defaults to 0 and ddev-router will fail to start.
#
# The port behavior on the ddev-webserver must be arranged separately, for example
# using web_extra_daemons.
# For example, with a web app on port 3000 inside the container, this config would
# expose that web app on https://<project>.ddev.site:9999 and http://<project>.ddev.site:9998
# web_extra_exposed_ports:
# - name: myapp
# container_port: 3000
# http_port: 9998
# https_port: 9999
#web_extra_daemons:
#- name: "http-1"
# command: "/var/www/html/node_modules/.bin/http-server -p 3000"
# directory: /var/www/html
#- name: "http-2"
# command: "/var/www/html/node_modules/.bin/http-server /var/www/html/sub -p 3000"
# directory: /var/www/html
# override_config: false
# By default, config.*.yaml files are *merged* into the configuration
# But this means that some things can't be overridden
# For example, if you have 'use_dns_when_possible: true'' you can't override it with a merge
# and you can't erase existing hooks or all environment variables.
# However, with "override_config: true" in a particular config.*.yaml file,
# 'use_dns_when_possible: false' can override the existing values, and
# hooks:
# post-start: []
# or
# web_environment: []
# or
# additional_hostnames: []
# can have their intended affect. 'override_config' affects only behavior of the
# config.*.yaml file it exists in.
# Many DDEV commands can be extended to run tasks before or after the
# DDEV command is executed, for example "post-start", "post-import-db",
# "pre-composer", "post-composer"
# See https://ddev.readthedocs.io/en/stable/users/extend/custom-commands/ for more
# information on the commands that can be extended and the tasks you can define
# for them. Example:
#hooks:
# post-start:
# - exec: composer install -d /var/www/html
@@ -0,0 +1,13 @@
version: '3.6'
services:
chrome:
container_name: ddev-${DDEV_SITENAME}-chromedriver
image: seleniarm/standalone-chromium:111.0
restart: "no"
volumes:
- /dev/shm:/dev/shm
ports:
- 4444
- "15723:5900"
external_links:
- ddev-router:$DDEV_HOSTNAME
@@ -0,0 +1,61 @@
<?php
/**
* #ddev-generated: Automatically generated TYPO3 AdditionalConfiguration.php file.
* ddev manages this file and may delete or overwrite the file unless this comment is removed.
* It is recommended that you leave this file alone.
*/
if (getenv('IS_DDEV_PROJECT') == 'true') {
$GLOBALS['TYPO3_CONF_VARS'] = array_replace_recursive(
$GLOBALS['TYPO3_CONF_VARS'],
[
'DB' => [
'Connections' => [
'Default' => [
'dbname' => 'db',
'host' => 'db',
'password' => 'db',
'port' => '3306',
'user' => 'db',
],
],
],
// This GFX configuration allows processing by installed ImageMagick 6
'GFX' => [
'processor' => 'ImageMagick',
'processor_path' => '/usr/bin/',
'processor_path_lzw' => '/usr/bin/',
],
// This mail configuration sends all emails to mailhog
'MAIL' => [
'transport' => 'smtp',
'transport_smtp_server' => 'localhost:1025',
],
'SYS' => [
'trustedHostsPattern' => '.*.*',
'devIPmask' => '*',
'displayErrors' => 1,
],
'FE' => [
'cacheHash' => [
'enforceValidation' => true,
'excludedParameters' => [
'L',
'pk_campaign',
'pk_kwd',
'utm_source',
'utm_medium',
'utm_campaign',
'utm_term',
'utm_content',
'gclid',
'fbclid',
'msclkid',
'pid',
],
],
],
]
);
}
@@ -0,0 +1,20 @@
base: 'https://femanager.ddev.site/'
baseVariants: { }
errorHandling: { }
languages:
-
title: English
enabled: true
base: /
typo3Language: default
locale: en_US.UTF-8
iso-639-1: en
websiteTitle: ''
navigationTitle: ''
hreflang: en-US
direction: ltr
flag: en-us-gb
languageId: '0'
rootPageId: 1
routes: { }
websiteTitle: ''
@@ -0,0 +1,17 @@
name: TERUpload
on:
push:
tags:
- "**"
jobs:
TERUpload:
runs-on: ubuntu-latest
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v1
- uses: tomasnorre/typo3-upload-ter@v2
with:
api-token: ${{ secrets.TYPO3_API_TOKEN }}
@@ -0,0 +1,17 @@
name: test documentation
on: [ push, pull_request ]
jobs:
tests:
name: documentation
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Test if the documentation will render without warnings
run: |
mkdir -p Documentation-GENERATED-temp \
&& docker run --rm --pull always -v $(pwd):/project \
ghcr.io/typo3-documentation/render-guides:latest --config=Documentation --no-progress --fail-on-log
@@ -0,0 +1,119 @@
name: testing
on:
push:
pull_request:
schedule:
- cron: '0 2 * * *'
jobs:
php-lint:
name: "PHP linter"
runs-on: ubuntu-20.04
steps:
- name: "Checkout"
uses: actions/checkout@v2
- name: "Install PHP"
uses: shivammathur/setup-php@v2
with:
php-version: "${{ matrix.php-version }}"
coverage: none
tools: composer:v2
- name: "Run PHP lint"
run: "composer test:php:lint"
strategy:
fail-fast: false
matrix:
php-version:
- 7.4
- 8.0
- 8.1
typoscript-lint:
name: "TypoScript linter"
runs-on: ubuntu-20.04
steps:
- name: "Checkout"
uses: actions/checkout@v2
- name: "Run TypoScript lint"
uses: TYPO3-Continuous-Integration/TYPO3-CI-Typoscript-Lint@v1
with:
files: "./Configuration"
config_file: ".project/tests/typoscript-lint.yml"
php-cs-fixer:
name: "PHP CS Fixer"
runs-on: ubuntu-20.04
needs: php-lint
steps:
- name: "Checkout"
uses: actions/checkout@v2
- name: "Install PHP"
uses: shivammathur/setup-php@v2
with:
php-version: 7.4
- name: "Composer Install"
run: "composer install"
- name: "Run PHP CS Fixer"
run: "composer test:php:cs"
php-rector:
name: "PHP Rector"
runs-on: ubuntu-20.04
needs: php-lint
steps:
- name: "Checkout"
uses: actions/checkout@v2
- name: "Install PHP"
uses: shivammathur/setup-php@v2
with:
php-version: 7.4
- name: "Composer Install"
run: "composer install"
- name: "Run PHP Rector"
run: "composer test:rector"
unit-tests:
name: "PHP Unit Tests"
runs-on: ubuntu-20.04
needs: php-lint
steps:
- name: Checkout
uses: actions/checkout@v2
- name: "Install PHP"
uses: shivammathur/setup-php@v2
with:
php-version: "${{ matrix.php-version }}"
coverage: none
tools: composer:v2
- name: "Composer Install"
run: "composer install"
- name: "Run Unit Tests"
run: "composer test:unit"
strategy:
fail-fast: false
matrix:
php-version:
- 7.4
- 8.0
- 8.1
behaviour-tests:
name: "PHP Behat Tests"
runs-on: ubuntu-20.04
needs: php-lint
services:
chrome:
image: selenium/standalone-chrome-debug
steps:
- name: Checkout
uses: actions/checkout@v2
- uses: ddev/github-action-setup-ddev@v1
- name: "Composer Install"
run: "ddev composer install"
- name: "Initialize TYPO3 in ddev"
run: "ddev initialize"
- name: "Run Behat Tests"
run: "ddev composer test:behaviour:ddev"
strategy:
fail-fast: false
matrix:
php-version:
- 7.4
- 8.0
- 8.1
@@ -0,0 +1,546 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Controller;
use In2code\Femanager\DataProcessor\DataProcessorRunner;
use In2code\Femanager\Domain\Model\Log;
use In2code\Femanager\Domain\Model\User;
use In2code\Femanager\Domain\Repository\UserGroupRepository;
use In2code\Femanager\Domain\Repository\UserRepository;
use In2code\Femanager\Domain\Service\SendMailService;
use In2code\Femanager\Event\FinalCreateEvent;
use In2code\Femanager\Event\FinalUpdateEvent;
use In2code\Femanager\Finisher\FinisherRunner;
use In2code\Femanager\Utility\BackendUtility;
use In2code\Femanager\Utility\ConfigurationUtility;
use In2code\Femanager\Utility\FrontendUtility;
use In2code\Femanager\Utility\HashUtility;
use In2code\Femanager\Utility\LocalizationUtility;
use In2code\Femanager\Utility\LogUtility;
use In2code\Femanager\Utility\StringUtility;
use In2code\Femanager\Utility\UserUtility;
use TYPO3\CMS\Backend\Utility\BackendUtility as BackendUtilityCore;
use TYPO3\CMS\Core\Http\ApplicationType;
use TYPO3\CMS\Core\Messaging\AbstractMessage;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException;
use TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
/**
* Class AbstractController
*/
abstract class AbstractController extends ActionController
{
/**
* @var UserRepository
*/
protected $userRepository;
/**
* @var UserGroupRepository
*/
protected $userGroupRepository;
/**
* @var PersistenceManager
*/
protected $persistenceManager;
/**
* @var SendMailService
*/
protected $sendMailService;
/**
* @var FinisherRunner
*/
protected $finisherRunner;
/**
* @var LogUtility
*/
protected $logUtility;
/**
* Content Object
*
* @var ContentObjectRenderer
*/
public $contentObject;
/**
* Former known as piVars
*
* @var array
*/
public $pluginVariables;
/**
* TypoScript
*
* @var array
*/
public $config;
/**
* Complete Configuration
*
* @var array
*/
public $allConfig;
/**
* Module Configuration for Backend
* this is a merge configuration -> TypoScript -> PageTSConfig -> UserTSConfig
*
* @var array
*/
public $moduleConfig;
/**
* Current logged in user object
*
* @var User
*/
public $user;
/**
* All available usergroups
*
* @var object
*/
public $allUserGroups;
/**
* AbstractController constructor.
*
* @param UserRepository $userRepository
* @param UserGroupRepository $userGroupRepository
* @param PersistenceManager $persistenceManager
* @param SendMailService $sendMailService
* @param FinisherRunner $finisherRunner
* @param LogUtility $logUtility
*/
public function __construct(
UserRepository $userRepository,
UserGroupRepository $userGroupRepository,
PersistenceManager $persistenceManager,
SendMailService $sendMailService,
FinisherRunner $finisherRunner,
LogUtility $logUtility
) {
$this->userRepository = $userRepository;
$this->userGroupRepository = $userGroupRepository;
$this->persistenceManager = $persistenceManager;
$this->sendMailService = $sendMailService;
$this->finisherRunner = $finisherRunner;
$this->logUtility = $logUtility;
}
/**
* Prefix method to createAction()
* Create Confirmation from Admin is not necessary
*
* @param User $user
*/
public function createAllConfirmed(User $user)
{
$this->userRepository->add($user);
$this->persistenceManager->persistAll();
$this->logUtility->log(Log::STATUS_NEWREGISTRATION, $user);
$this->finalCreate($user, 'new', 'createStatus');
}
/**
* Prefix method to updateAction()
* Update Confirmation from Admin is not necessary
*
* @param User $user
*/
public function updateAllConfirmed(User $user)
{
// send notify email to admin
$existingUser = clone $this->userRepository->findByUid($user->getUid());
if (ConfigurationUtility::notifyAdminAboutEdits($this->settings) === true) {
$this->sendMailService->send(
'updateNotify',
StringUtility::makeEmailArray(
ConfigurationUtility::getValue('edit/email/notifyAdmin/receiver/email/value', $this->settings) ?:
ConfigurationUtility::getValue('edit/notifyAdmin', $this->settings),
$this->settings['edit']['email']['notifyAdmin']['receiver']['name']['value'] ?? null
),
StringUtility::makeEmailArray($user->getEmail(), $user->getUsername()),
'Profile update',
[
'user' => $user,
'changes' => UserUtility::getDirtyPropertiesFromUser($user),
'settings' => $this->settings,
],
$this->config['edit.']['email.']['notifyAdmin.'] ?? []
);
}
$this->userRepository->update($user);
$this->persistenceManager->persistAll();
$this->eventDispatcher->dispatch(new FinalUpdateEvent($user));
$this->logUtility->log(Log::STATUS_PROFILEUPDATED, $user, ['existingUser' => $existingUser]);
$this->redirectByAction('edit');
$this->addFlashMessage(LocalizationUtility::translate('update'));
}
/**
* Prefix method to updateAction(): Update must be confirmed by Admin
*
* @param User $user
*/
public function updateRequest($user)
{
if ($this->settings['edit']['confirmByAdmin'] ?? null) {
$dirtyProperties = UserUtility::getDirtyPropertiesFromUser($user);
$user = UserUtility::rollbackUserWithChangeRequest($user, $dirtyProperties);
$this->sendMailService->send(
'updateRequest',
StringUtility::makeEmailArray(
$this->settings['edit']['confirmByAdmin'] ?? '',
$this->settings['edit']['email']['updateRequest']['sender']['name']['value'] ?? null
),
StringUtility::makeEmailArray($user->getEmail(), $user->getUsername()),
'New Profile change request',
[
'user' => $user,
'changes' => $dirtyProperties,
'hash' => HashUtility::createHashForUser($user),
],
$this->config['edit.']['email.']['updateRequest.'] ?? []
);
$this->logUtility->log(
Log::STATUS_PROFILEUPDATEREFUSEDADMIN,
$user,
['dirtyProperties' => $dirtyProperties]
);
$this->redirectByAction('edit', 'requestRedirect');
$this->addFlashMessage(LocalizationUtility::translate('updateRequest'));
} else {
$this->logUtility->log(
Log::STATUS_PROFILEUPDATEREFUSEDADMIN,
$user,
['message' => 'settings[edit][confirmByAdmin] is missing!']
);
}
}
/**
* Some additional actions after profile creation
*
* @param User $user
* @param string $action
* @param string $redirectByActionName Action to redirect
* @param bool $login Login after creation
* @param string $status
* @param bool $backend Don't redirect if called from backend action
*/
public function finalCreate(
$user,
string $action,
string $redirectByActionName,
bool $login = true,
string $status = '',
bool $backend = false
): void {
$this->loginPreflight($user, $login);
$variables = ['user' => $user, 'settings' => $this->settings, 'hash' => HashUtility::createHashForUser($user)];
if (ConfigurationUtility::getValue(
'new./email./createUserNotify./sender./email./value',
$this->config
) && ConfigurationUtility::getValue('new./email./createUserNotify./sender./name./value', $this->config)) {
$this->sendMailService->send(
'createUserNotify',
StringUtility::makeEmailArray($user->getEmail(), $user->getFirstName() . ' ' . $user->getLastName()),
StringUtility::makeEmailArray(
ConfigurationUtility::getValue(
'new./email./createUserNotify./sender./email./value',
$this->config
),
ConfigurationUtility::getValue('new./email./createUserNotify./sender./name./value', $this->config)
),
$this->contentObject->cObjGetSingle(
(string)ConfigurationUtility::getValue('new./email./createUserNotify./subject', $this->config),
(array)ConfigurationUtility::getValue('new./email./createUserNotify./subject.', $this->config),
),
$variables,
ConfigurationUtility::getValue('new./email./createUserNotify.', $this->config)
);
}
$createAdminNotify = ConfigurationUtility::getValue(
'new./email./createAdminNotify./receiver./email./value',
$this->config
);
if (!$createAdminNotify) {
$createAdminNotify = $this->settings['new']['notifyAdmin'];
}
// send notify email to admin
if ($createAdminNotify) {
$this->sendMailService->send(
'createNotify',
StringUtility::makeEmailArray(
$createAdminNotify
),
StringUtility::makeEmailArray($user->getEmail(), $user->getUsername()),
$this->contentObject->cObjGetSingle(
(string)ConfigurationUtility::getValue('new./email./createAdminNotify./subject', $this->config),
(array)ConfigurationUtility::getValue('new./email./createAdminNotify./subject.', $this->config)
),
$variables,
ConfigurationUtility::getValue('new./email./createAdminNotify.', $this->config)
);
}
$this->eventDispatcher->dispatch(new FinalCreateEvent($user, $action));
$this->finisherRunner->callFinishers($user, $this->actionMethodName, $this->settings, $this->contentObject);
if ($backend === false) {
$this->redirectByAction($action, ($status ? $status . 'Redirect' : 'redirect'));
$this->addFlashMessage(LocalizationUtility::translate('create'));
$this->redirect($redirectByActionName);
}
}
/**
* Log user in
*
* @param User $user
* @param $login
* @throws IllegalObjectTypeException
*/
protected function loginPreflight(User $user, $login)
{
if ($login) {
// persist user (otherwise login may not be possible)
$this->userRepository->update($user);
$this->persistenceManager->persistAll();
if (ConfigurationUtility::getValue('new./login', $this->config) === '1') {
UserUtility::login($user, ConfigurationUtility::getValue('persistence./storagePid', $this->allConfig));
$this->addFlashMessage(LocalizationUtility::translate('login'), '', AbstractMessage::NOTICE);
}
}
}
/**
* Redirect by TypoScript setting
* [userConfirmation|userConfirmationRefused|adminConfirmation|
* adminConfirmationRefused|adminConfirmationRefusedSilent]Redirect
*
* @param string $action "new", "edit"
* @param string $category "redirect", "requestRedirect" value from TypoScript
*/
protected function redirectByAction($action = 'new', $category = 'redirect')
{
$target = null;
// redirect from TypoScript cObject
if ($this->contentObject->cObjGetSingle(
(string)ConfigurationUtility::getValue($action . './' . $category, $this->config),
(array)ConfigurationUtility::getValue($action . './' . $category . '.', $this->config),
)
) {
$target = $this->contentObject->cObjGetSingle(
(string)ConfigurationUtility::getValue($action . './' . $category, $this->config),
array_merge_recursive(
(array)ConfigurationUtility::getValue($action . './' . $category . '.', $this->config),
[
'linkAccessRestrictedPages' => 1,
]
)
);
}
// if redirect target
if ($target) {
$this->redirectToUri(StringUtility::removeDoubleSlashesFromUri($target));
}
}
/**
* Check if user is authenticated and params are valid
*
* @param User $user
* @param int $uid Given fe_users uid
* @param string $receivedToken Token
*/
protected function isSpoof($user, $uid, $receivedToken)
{
$errorOnProfileUpdate = false;
$knownToken = GeneralUtility::hmac($user->getUid(), (string)$user->getCrdate()->getTimestamp());
//check if the params are valid
if (!is_string($receivedToken) || !hash_equals($knownToken, $receivedToken)) {
$errorOnProfileUpdate = true;
}
//check if the logged user is allowed to edit / delete this record
if ($user->getUid() !== (int)$uid && $uid > 0) {
$errorOnProfileUpdate = true;
}
if ($errorOnProfileUpdate === true) {
return true;
}
return false;
}
/**
* Assigns all values, which should be available in all views
*/
public function assignForAll()
{
$jsLabels = [
'loading_states' => LocalizationUtility::translate('js.loading_states'),
'please_choose' => LocalizationUtility::translate('pleaseChoose'),
];
$this->view->assignMultiple(
[
'languageUid' => FrontendUtility::getFrontendLanguageUid(),
'storagePid' => $this->allConfig['persistence']['storagePid'] ?? 0,
'Pid' => FrontendUtility::getCurrentPid(),
'data' => $this->contentObject->data,
'useStaticInfoTables' => ExtensionManagementUtility::isLoaded('static_info_tables'),
'jsLabels' => json_encode($jsLabels),
]
);
}
public function initializeAction()
{
$this->controllerContext = $this->buildControllerContext();
$this->user = UserUtility::getCurrentUser();
$this->contentObject = $this->configurationManager->getContentObject();
$this->pluginVariables = $this->request->getArguments();
$this->moduleConfig = [];
$this->allConfig = $this->configurationManager->getConfiguration(
ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK
);
$this->config = $this->configurationManager->getConfiguration(
ConfigurationManagerInterface::CONFIGURATION_TYPE_FULL_TYPOSCRIPT
);
$this->config = $this->config[BackendUtility::getPluginOrModuleString() . '.']['tx_femanager.']['settings.'] ?? [];
if (ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isBackend()) {
$config = BackendUtility::loadTS($this->allConfig['settings']['configPID'] ?? null);
if (is_array($config['plugin.']['tx_femanager.']['settings.'] ?? null)) {
$this->config = $config['plugin.']['tx_femanager.']['settings.'];
$this->settings = $this->config;
}
$this->moduleConfig = $config['module.']['tx_femanager.'] ?? '';
// Retrieve page TSconfig of the current page
$pageTsConfig = BackendUtilityCore::getPagesTSconfig(BackendUtility::getPageIdentifier());
if (is_array($pageTsConfig['module.']['tx_femanager.'] ?? null)) {
$this->moduleConfig = array_merge($this->moduleConfig, $pageTsConfig['module.']['tx_femanager.'] ?? []);
}
// Retrieve user TSconfig of currently logged in user
$userTsConfig = $GLOBALS['BE_USER']->getTSConfig();
if ((is_array($userTsConfig['tx_femanager.']) ?? null) && (is_array($this->moduleConfig)?? null)) {
$this->moduleConfig = array_merge_recursive($this->moduleConfig, $userTsConfig['tx_femanager.']);
}
}
$this->setAllUserGroups();
$this->checkTypoScript();
$this->checkStoragePid();
$dataProcessorRunner = $this->objectManager->get(DataProcessorRunner::class);
$this->pluginVariables = $dataProcessorRunner->callClasses(
$this->request->getArguments(),
$this->settings,
$this->contentObject,
$this->arguments
);
$this->request->setArguments($this->pluginVariables);
}
/**
* Deactivate errormessages in flashmessages
*
* @return bool
*/
protected function getErrorFlashMessage()
{
return false;
}
protected function checkStoragePid()
{
if ((int)($this->allConfig['persistence']['storagePid'] ?? 0) === 0
&& GeneralUtility::_GP('type') !== '1548935210'
) {
$this->addFlashMessage(LocalizationUtility::translate('error_no_storagepid'), '', AbstractMessage::ERROR);
}
}
protected function checkTypoScript()
{
if (ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'] ?? null)->isBackend()) {
if (($this->config['_TypoScriptIncluded'] ?? '1') !== '1') {
$this->addFlashMessage(
(string)LocalizationUtility::translate('error_no_typoscript_be'),
'',
AbstractMessage::ERROR
);
}
} else {
$typoscriptIncluded = ConfigurationUtility::getValue('_TypoScriptIncluded', $this->settings);
if ($typoscriptIncluded !== '1' && !GeneralUtility::_GP('eID')) {
$this->addFlashMessage(
(string)LocalizationUtility::translate('error_no_typoscript'),
'',
AbstractMessage::ERROR
);
}
}
}
protected function setAllUserGroups()
{
$controllerName = strtolower($this->controllerContext->getRequest()->getControllerName());
$removeFromUserGroupSelection = $this->settings[$controllerName]['misc']['removeFromUserGroupSelection'] ?? '';
$this->allUserGroups = $this->userGroupRepository->findAllForFrontendSelection($removeFromUserGroupSelection);
}
/**
* Send email to user for confirmation
*
* @param User $user
*/
public function sendCreateUserConfirmationMail(User $user)
{
$this->sendMailService->send(
'createUserConfirmation',
StringUtility::makeEmailArray($user->getEmail(), $user->getUsername()),
[
ConfigurationUtility::getValue('new./email./createUserConfirmation./sender./email./value', $this->config) =>
ConfigurationUtility::getValue('new./email./createUserConfirmation./sender./name./value', $this->config),
],
$this->contentObject->cObjGetSingle(
(string)ConfigurationUtility::getValue('new./email./createUserConfirmation./subject', $this->config),
(array)ConfigurationUtility::getValue('new./email./createUserConfirmation./subject.', $this->config)
),
[
'user' => $user,
'hash' => HashUtility::createHashForUser($user),
],
ConfigurationUtility::getValue('new./email./createUserConfirmation.', $this->config)
);
}
}
@@ -0,0 +1,18 @@
<?php
namespace In2code\Femanager\Controller;
use In2code\Femanager\Domain\Service\RatelimiterService;
abstract class AbstractFrontendController extends AbstractController
{
/**
* @var RatelimiterService
*/
protected $ratelimiterService;
public function injectRatelimiterService(RatelimiterService $ratelimiterService): void
{
$this->ratelimiterService = $ratelimiterService;
}
}
@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Controller;
use In2code\Femanager\DataProvider\CountryZonesDataProvider;
use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
class DataController extends ActionController
{
protected $countryZonesDataProvider;
public function __construct(CountryZonesDataProvider $countryZonesDataProvider)
{
$this->countryZonesDataProvider = $countryZonesDataProvider;
}
public function getStatesForCountryAction(string $country): ResponseInterface
{
$countryZones = $this->countryZonesDataProvider->getCountryZonesForCountryIso3($country);
$jsonData = [];
foreach ($countryZones as $countryZone) {
$jsonData[$countryZone->getIsoCode()] = $countryZone->getLocalName();
}
return $this->jsonResponse(json_encode($jsonData));
}
}
@@ -0,0 +1,255 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Controller;
use In2code\Femanager\Domain\Model\Log;
use In2code\Femanager\Domain\Model\User;
use In2code\Femanager\Domain\Model\UserGroup;
use In2code\Femanager\Event\AfterUserUpdateEvent;
use In2code\Femanager\Event\BeforeUpdateUserEvent;
use In2code\Femanager\Event\DeleteUserEvent;
use In2code\Femanager\Utility\ConfigurationUtility;
use In2code\Femanager\Utility\FrontendUtility;
use In2code\Femanager\Utility\HashUtility;
use In2code\Femanager\Utility\LocalizationUtility;
use In2code\Femanager\Utility\ObjectUtility;
use In2code\Femanager\Utility\StringUtility;
use In2code\Femanager\Utility\UserUtility;
use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Core\Messaging\AbstractMessage;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Annotation\Validate;
use TYPO3\CMS\Extbase\Http\ForwardResponse;
/**
* Class EditController
*/
class EditController extends AbstractFrontendController
{
public function editAction(): ResponseInterface
{
$token = '';
if ($this->user) {
$token = GeneralUtility::hmac($this->user->getUid(), (string)$this->user->getCrdate()->getTimestamp());
}
$this->view->assignMultiple([
'user' => $this->user,
'allUserGroups' => $this->allUserGroups,
'token' => $token
]);
$this->assignForAll();
return $this->htmlResponse();
}
public function initializeUpdateAction()
{
if ($this->keepPassword()) {
unset($this->pluginVariables['user']['password']);
unset($this->pluginVariables['password_repeat']);
}
$this->request->setArguments($this->pluginVariables);
}
/**
* @param User $user
* @Validate("In2code\Femanager\Domain\Validator\ServersideValidator", param="user")
* @Validate("In2code\Femanager\Domain\Validator\PasswordValidator", param="user")
* @Validate("In2code\Femanager\Domain\Validator\CaptchaValidator", param="user")
*/
public function updateAction(User $user)
{
$currentUser = UserUtility::getCurrentUser();
$userValues = $this->request->getArgument('user') ?? [];
$token = $this->request->getArgument('token') ?? null;
$identity = (int)($userValues['__identity'] ?? 0);
$isSpoof = $this->isSpoof($currentUser, $identity, $token);
if (!$currentUser instanceof User || $identity === 0 || $token === null || $isSpoof) {
$logStatus = $isSpoof ? Log::STATUS_PROFILEUPDATEATTEMPTEDSPOOF : Log::STATUS_PROFILEUPDATEREFUSEDSECURITY;
$logContext = $isSpoof ? $currentUser : $user;
$this->logUtility->log($logStatus, $logContext);
$this->addFlashMessage(
LocalizationUtility::translateByState(Log::STATUS_PROFILEUPDATEREFUSEDSECURITY),
'',
AbstractMessage::ERROR
);
return new ForwardResponse('edit');
}
$response = $this->redirectIfNoChangesOnObject($user);
if ($response !== null) {
return $response;
}
$user = FrontendUtility::forceValues(
$user,
ConfigurationUtility::getValue('edit./forceValues./beforeAnyConfirmation.', $this->config)
);
$this->emailForUsername($user);
UserUtility::convertPassword(
$user,
ConfigurationUtility::getValue('edit/misc/passwordSave', $this->settings)
);
$this->eventDispatcher->dispatch(new BeforeUpdateUserEvent($user));
if (!empty($this->settings['edit']['confirmByAdmin'])) {
$this->updateRequest($user);
} else {
$this->updateAllConfirmed($user);
}
$this->redirect('edit');
}
/**
* @param User $user User object
* @param string $hash
* @param string $status could be "confirm", "refuse", "silentRefuse"
*/
public function confirmUpdateRequestAction(User $user, $hash, $status = 'confirm'): ResponseInterface
{
$this->view->assign('user', $user);
if (!HashUtility::validHash($hash, $user) || !$user->getTxFemanagerChangerequest()) {
$this->addFlashMessage(LocalizationUtility::translate('updateFailedProfile'), '', AbstractMessage::ERROR);
return $this->htmlResponse(null);
}
switch ($status) {
case 'confirm':
$this->statusConfirm($user);
break;
case 'refuse':
$this->statusRefuse($user);
break;
case 'silentRefuse':
$this->logUtility->log(Log::STATUS_PROFILEUPDATEREFUSEDADMIN, $user);
$this->addFlashMessage(LocalizationUtility::translateByState(Log::STATUS_PROFILEUPDATEREFUSEDADMIN));
break;
default:
}
$user->setTxFemanagerChangerequest('');
$this->userRepository->update($user);
$this->eventDispatcher->dispatch(new AfterUserUpdateEvent($user, $hash, $status));
return $this->htmlResponse();
}
/**
* Status update confirmation
*
* @param User $user
*/
protected function statusConfirm(User $user)
{
$values = GeneralUtility::xml2array($user->getTxFemanagerChangerequest());
foreach ((array)$values as $field => $value) {
if ($field !== 'usergroup' && method_exists($user, 'set' . ucfirst($field))) {
$user->{'set' . ucfirst($field)}($value['new']);
} else {
$user->removeAllUsergroups();
$usergroupUids = GeneralUtility::trimExplode(',', $value['new'], true);
foreach ($usergroupUids as $usergroupUid) {
/** @var UserGroup $usergroup */
$usergroup = $this->userGroupRepository->findByUid($usergroupUid);
$user->addUsergroup($usergroup);
}
}
}
$user = FrontendUtility::forceValues($user, $this->config['edit.']['forceValues.']['onAdminConfirmation.']);
$this->logUtility->log(Log::STATUS_PROFILEUPDATECONFIRMEDADMIN, $user);
$this->addFlashMessage(LocalizationUtility::translate('updateProfile'));
}
/**
* Status update refused
*
* @param User $user
*/
protected function statusRefuse(User $user)
{
$this->sendMailService->send(
'updateRequestRefused',
StringUtility::makeEmailArray($user->getEmail(), $user->getFirstName() . ' ' . $user->getLastName()),
['sender@femanager.org' => 'Sender Name'],
'Your change request was refused',
[
'user' => $user,
'settings' => $this->settings
],
$this->config['edit.']['email.']['updateRequestRefused.']
);
$this->logUtility->log(Log::STATUS_PROFILEUPDATEREFUSEDADMIN, $user);
$this->addFlashMessage(LocalizationUtility::translateByState(Log::STATUS_PROFILEUPDATEREFUSEDADMIN));
}
/**
* action delete
*
* @param User $user
*/
public function deleteAction(User $user)
{
$currentUser = UserUtility::getCurrentUser();
$token = $this->request->hasArgument('token') ? $this->request->getArgument('token') : null;
$uid = $this->request->hasArgument('user') ? $this->request->getArgument('user') : null;
if ($currentUser === null ||
$token === null ||
$uid === null ||
$this->isSpoof($currentUser, (int)$uid, $token)
) {
$this->logUtility->log(Log::STATUS_PROFILEUPDATEREFUSEDSECURITY, $user);
$this->addFlashMessage(
LocalizationUtility::translateByState(Log::STATUS_PROFILEUPDATEREFUSEDSECURITY),
'',
AbstractMessage::ERROR
);
return new ForwardResponse('edit');
}
$this->eventDispatcher->dispatch(new DeleteUserEvent($user));
$this->logUtility->log(Log::STATUS_PROFILEDELETE, $user);
$this->addFlashMessage(LocalizationUtility::translateByState(Log::STATUS_PROFILEDELETE));
$this->userRepository->remove($user);
$this->redirectByAction('delete');
$this->redirect('edit');
}
/**
* Check if password should be kept
*
* If password is empty
* If password repeat is also empty
* If keepPasswordIfEmpty configuration is turned on
*
* @return bool
*/
protected function keepPassword()
{
return !empty($this->settings['edit']['misc']['keepPasswordIfEmpty']) &&
empty($this->pluginVariables['user']['password']) &&
empty($this->pluginVariables['password_repeat']);
}
/**
* Check: If there are no changes, simple redirect back
*/
protected function redirectIfNoChangesOnObject(User $user)
{
if (!ObjectUtility::isDirtyObject($user)) {
$this->addFlashMessage(LocalizationUtility::translate('noChanges'), '', AbstractMessage::NOTICE);
return $this->redirect('edit');
}
return null;
}
/**
* @param User $user
*/
protected function emailForUsername(User $user)
{
$fillEmailWithUsername = ConfigurationUtility::getValue('edit/fillEmailWithUsername', $this->settings);
if ($fillEmailWithUsername === '1') {
$user->setEmail($user->getUsername());
}
}
}
@@ -0,0 +1,306 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Controller;
use In2code\Femanager\Domain\Model\Log;
use In2code\Femanager\Domain\Model\User;
use In2code\Femanager\Event\InviteUserConfirmedEvent;
use In2code\Femanager\Event\InviteUserCreateEvent;
use In2code\Femanager\Event\InviteUserEditEvent;
use In2code\Femanager\Event\InviteUserUpdateEvent;
use In2code\Femanager\Utility\ConfigurationUtility;
use In2code\Femanager\Utility\FrontendUtility;
use In2code\Femanager\Utility\HashUtility;
use In2code\Femanager\Utility\LocalizationUtility;
use In2code\Femanager\Utility\StringUtility;
use In2code\Femanager\Utility\UserUtility;
use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Core\Messaging\AbstractMessage;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Annotation\Validate;
use TYPO3\CMS\Extbase\Http\ForwardResponse;
/**
* Class InvitationController
*/
class InvitationController extends AbstractFrontendController
{
/**
* action new
*/
public function newAction(): ResponseInterface
{
$permissionCheck = $this->allowedUserForInvitationNewAndCreate();
if ($permissionCheck instanceof ResponseInterface) {
return $permissionCheck;
}
$this->view->assign('allUserGroups', $this->allUserGroups);
$this->assignForAll();
return $this->htmlResponse();
}
/**
* action create
*
* @param User $user
* @Validate("In2code\Femanager\Domain\Validator\ServersideValidator", param="user")
* @Validate("In2code\Femanager\Domain\Validator\PasswordValidator", param="user")
* @Validate("In2code\Femanager\Domain\Validator\CaptchaValidator", param="user")
*/
public function createAction(User $user)
{
if ($this->ratelimiterService->isLimited()) {
$this->addFlashMessage(
LocalizationUtility::translate('ratelimiter_too_many_attempts'),
'',
AbstractMessage::ERROR
);
$this->redirect('status');
}
$permissionCheck = $this->allowedUserForInvitationNewAndCreate();
if ($permissionCheck instanceof ResponseInterface) {
return $permissionCheck;
}
$user->setDisable(true);
$user = FrontendUtility::forceValues(
$user,
ConfigurationUtility::getValue('invitation./forceValues./beforeAnyConfirmation.', $this->config)
);
$user = UserUtility::fallbackUsernameAndPassword($user);
if (ConfigurationUtility::getValue('invitation/fillEmailWithUsername', $this->settings) === '1') {
$user->setEmail($user->getUsername());
}
UserUtility::hashPassword(
$user,
ConfigurationUtility::getValue('invitation/misc/passwordSave', $this->settings)
);
$this->eventDispatcher->dispatch(new InviteUserCreateEvent($user));
$this->ratelimiterService->consumeSlot();
$this->createAllConfirmed($user);
}
/**
* Prefix method to createAction()
* Create Confirmation from Admin is not necessary
*
* @param User $user
*/
public function createAllConfirmed(User $user)
{
$this->userRepository->add($user);
$this->persistenceManager->persistAll();
$this->addFlashMessage(LocalizationUtility::translate('createAndInvited'));
$this->logUtility->log(Log::STATUS_INVITATIONPROFILECREATED, $user);
// send confirmation mail to user
$this->sendMailService->send(
'invitation',
StringUtility::makeEmailArray($user->getEmail(), $user->getUsername()),
StringUtility::makeEmailArray($user->getEmail(), $user->getUsername()),
'Profile creation with invitation',
[
'user' => $user,
'settings' => $this->settings,
'hash' => HashUtility::createHashForUser($user)
],
$this->config['invitation.']['email.']['invitation.']
);
// send notify email to admin
$notifyAdminStep1 = ConfigurationUtility::getValue('invitation/notifyAdminStep1', $this->settings);
if ($notifyAdminStep1) {
$this->sendMailService->send(
'invitationNotifyStep1',
StringUtility::makeEmailArray(
$notifyAdminStep1,
ConfigurationUtility::getValue('invitation/email/invitationAdminNotifyStep1/receiver/name/value', $this->settings)
),
StringUtility::makeEmailArray($user->getEmail(), $user->getUsername()),
'Profile creation with invitation - Step 1',
[
'user' => $user,
'settings' => $this->settings
],
ConfigurationUtility::getValue('invitation./email./invitationAdminNotifyStep1.', $this->config)
);
}
$this->eventDispatcher->dispatch(new InviteUserConfirmedEvent($user));
$this->redirectByAction('invitation', 'redirectStep1');
$this->redirect('new');
}
/**
* action edit
*
* @param int $user User UID
* @param string $hash
*/
public function editAction($user, $hash = null): ResponseInterface
{
$user = $this->userRepository->findByUid($user);
// User must exist and hash must be valid
if ($user === null || !HashUtility::validHash($hash, $user)) {
$this->addFlashMessage(LocalizationUtility::translate('createFailedProfile'), '', AbstractMessage::ERROR);
$this->redirect('status');
}
// User must not be deleted (deleted = 0) and not be activated (disable = 1)
if ($user->getDisable() == 0) {
$this->addFlashMessage(LocalizationUtility::translate('userAlreadyConfirmed'), '', AbstractMessage::ERROR);
$this->redirect('status');
}
$user->setDisable(false);
$this->userRepository->update($user);
$this->persistenceManager->persistAll();
$this->eventDispatcher->dispatch(new InviteUserEditEvent($user, $hash));
$this->view->assignMultiple(
[
'user' => $user,
'hash' => $hash
]
);
$this->assignForAll();
return $this->htmlResponse();
}
/**
* action update
*
* @param User $user
* @param string $hash
* @Validate("In2code\Femanager\Domain\Validator\ServersideValidator", param="user")
* @Validate("In2code\Femanager\Domain\Validator\PasswordValidator", param="user")
*/
public function updateAction(User $user, $hash = null)
{
if (!HashUtility::validHash($hash, $user)) {
$this->addFlashMessage(
LocalizationUtility::translateByState(Log::STATUS_PROFILEUPDATEREFUSEDSECURITY),
'',
AbstractMessage::ERROR
);
$this->redirect('status');
}
$this->addFlashMessage(LocalizationUtility::translate('createAndInvitedFinished'));
$this->logUtility->log(Log::STATUS_INVITATIONPROFILEENABLED, $user);
$notifyAdmin = ConfigurationUtility::getValue('invitation/notifyAdmin', $this->settings);
if ($notifyAdmin) {
$this->sendMailService->send(
'invitationNotify',
StringUtility::makeEmailArray(
$notifyAdmin,
ConfigurationUtility::getValue('invitation/email/invitationAdminNotify/receiver/name/value', $this->settings)
),
StringUtility::makeEmailArray($user->getEmail(), $user->getUsername()),
'Profile creation with invitation - Final',
[
'user' => $user,
'settings' => $this->settings
],
ConfigurationUtility::getValue('invitation./email./invitationAdminNotify.', $this->config)
);
}
$user = UserUtility::overrideUserGroup($user, $this->settings, 'invitation');
UserUtility::hashPassword($user, ConfigurationUtility::getValue('invitation/misc/passwordSave', $this->settings));
$this->userRepository->update($user);
$this->persistenceManager->persistAll();
$this->eventDispatcher->dispatch(new InviteUserUpdateEvent($user));
$this->redirectByAction('invitation', 'redirectPasswordChanged');
$this->redirect('status');
}
/**
* action delete
*
* @param int $user User UID
* @param string $hash
*/
public function deleteAction($user, $hash = null)
{
$user = $this->userRepository->findByUid($user);
if ($user !== null && HashUtility::validHash($hash, $user)) {
$this->logUtility->log(Log::STATUS_PROFILEDELETE, $user);
$this->addFlashMessage(LocalizationUtility::translateByState(Log::STATUS_INVITATIONPROFILEDELETEDUSER));
// send notify email to admin
$notifyAdmin = ConfigurationUtility::getValue('invitation/notifyAdmin', $this->settings);
if ($notifyAdmin) {
$this->sendMailService->send(
'invitationRefused',
StringUtility::makeEmailArray(
$notifyAdmin,
ConfigurationUtility::getValue('invitation/email/invitationRefused/receiver/name/value', $this->settings)
),
StringUtility::makeEmailArray($user->getEmail(), $user->getUsername()),
'Profile deleted from User after invitation - Step 1',
[
'user' => $user,
'settings' => $this->settings
],
$this->config['invitation.']['email.']['invitationRefused.']
);
}
$this->userRepository->remove($user);
$this->redirectByAction('invitation', 'redirectDelete');
$this->redirect('status');
} else {
$this->addFlashMessage(
LocalizationUtility::translateByState(Log::STATUS_INVITATIONHASHERROR),
'',
AbstractMessage::ERROR
);
$this->redirect('status');
}
}
/**
* Restricted Action to show messages
*/
public function statusAction(): ResponseInterface
{
return $this->htmlResponse();
}
/**
* Check if user is allowed to see this action
*
* @return true|ForwardResponse
*/
protected function allowedUserForInvitationNewAndCreate()
{
if (empty($this->settings['invitation']['allowedUserGroups'] ?? [])) {
return true;
}
$allowedUsergroupUids = GeneralUtility::trimExplode(
',',
$this->settings['invitation']['allowedUserGroups'] ?? [],
true
);
$currentUsergroupUids = UserUtility::getCurrentUsergroupUids();
// compare allowedUsergroups with currentUsergroups
if (count(array_intersect($allowedUsergroupUids, $currentUsergroupUids))) {
return true;
}
// current user is not allowed
$this->addFlashMessage(
LocalizationUtility::translateByState(Log::STATUS_INVITATIONRESTRICTEDPAGE),
'',
AbstractMessage::ERROR
);
return new ForwardResponse('status');
}
}
@@ -0,0 +1,562 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Controller;
use In2code\Femanager\Domain\Model\Log;
use In2code\Femanager\Domain\Model\User;
use In2code\Femanager\Domain\Service\AutoAdminConfirmationService;
use In2code\Femanager\Event\BeforeUserConfirmEvent;
use In2code\Femanager\Event\BeforeUserCreateEvent;
use In2code\Femanager\Event\CreateConfirmationRequestEvent;
use In2code\Femanager\Utility\ConfigurationUtility;
use In2code\Femanager\Utility\FrontendUtility;
use In2code\Femanager\Utility\HashUtility;
use In2code\Femanager\Utility\LocalizationUtility;
use In2code\Femanager\Utility\StringUtility;
use In2code\Femanager\Utility\UserUtility;
use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Core\Crypto\PasswordHashing\InvalidPasswordHashException;
use TYPO3\CMS\Core\Http\ServerRequestFactory;
use TYPO3\CMS\Core\Messaging\AbstractMessage;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Annotation\Validate;
use TYPO3\CMS\Extbase\Event\Mvc\AfterRequestDispatchedEvent;
use TYPO3\CMS\Extbase\Mvc\Exception\StopActionException;
use TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException;
use TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException;
/**
* Class NewController
*/
class NewController extends AbstractFrontendController
{
/**
* Render registration form
*
*/
public function newAction(): ResponseInterface
{
$this->view->assignMultiple(
[
'allUserGroups' => $this->allUserGroups
]
);
$this->assignForAll();
return $this->htmlResponse();
}
/**
* action create
*
* @param User $user
* @throws InvalidPasswordHashException
* @throws StopActionException
* @Validate("In2code\Femanager\Domain\Validator\ServersideValidator", param="user")
* @Validate("In2code\Femanager\Domain\Validator\PasswordValidator", param="user")
* @Validate("In2code\Femanager\Domain\Validator\CaptchaValidator", param="user")
*/
public function createAction(User $user)
{
if ($this->ratelimiterService->isLimited()) {
$this->addFlashMessage(
LocalizationUtility::translate('ratelimiter_too_many_attempts'),
'',
AbstractMessage::ERROR
);
$this->redirect('createStatus');
}
$user = UserUtility::overrideUserGroup($user, $this->settings);
$configuration = ConfigurationUtility::getValue('new./forceValues./beforeAnyConfirmation.', $this->config);
$user = FrontendUtility::forceValues($user, $configuration);
$user = UserUtility::fallbackUsernameAndPassword($user);
$user = UserUtility::takeEmailAsUsername($user, $this->settings);
UserUtility::hashPassword($user, ConfigurationUtility::getValue('new/misc/passwordSave', $this->settings));
$this->eventDispatcher->dispatch(new BeforeUserCreateEvent($user));
$this->ratelimiterService->consumeSlot();
if ($this->isAllConfirmed()) {
$this->createAllConfirmed($user);
} else {
$this->createRequest($user);
}
$this->redirect('createStatus');
}
/**
* Dispatcher action for every confirmation request
*
* @param int $user User UID (user could be hidden)
* @param string $hash Given hash
* @param string $status
* "userConfirmation", "userConfirmationRefused", "adminConfirmation",
* "adminConfirmationRefused", "adminConfirmationRefusedSilent"
* @param string $adminHash
* @throws IllegalObjectTypeException
* @throws StopActionException
*/
public function confirmCreateRequestAction(int $user, string $hash, string $status = 'adminConfirmation', string $adminHash = null)
{
$backend = false;
$furtherFunctions = false;
$user = $this->userRepository->findByUid($user);
$this->eventDispatcher->dispatch(new BeforeUserConfirmEvent($user, $hash, $status));
if ($user === null) {
$this->addFlashMessage(LocalizationUtility::translate('missingUserInDatabase'), '', AbstractMessage::ERROR);
$this->redirect('new');
}
$request = ServerRequestFactory::fromGlobals();
// check if the the request was triggered via Backend
if ($request->hasHeader('Accept')) {
$accept = $request->getHeader('Accept')[0];
if (false !== strpos($accept, 'application/json')) {
$backend = true;
}
}
switch ($status) {
case 'userConfirmation':
if ($status === 'userConfirmation' && ConfigurationUtility::getValue(
'new./email./createUserConfirmation./confirmUserConfirmation',
$this->config
) == '1') {
$this->view->assignMultiple(
[
'user' => $user,
'status' => 'confirmUser',
'hash' => $hash,
]
);
$this->assignForAll();
return $this->htmlResponse();
}
$furtherFunctions = $this->statusUserConfirmation($user, $hash, $status);
break;
case 'confirmDeletion':
$furtherFunctions = $this->statusUserConfirmationRefused($user, $hash);
break;
case 'confirmedByUser':
$furtherFunctions = $this->statusUserConfirmation($user, $hash, $status);
break;
case 'userConfirmationRefused':
if (ConfigurationUtility::getValue('new./email./createUserConfirmation./confirmUserConfirmationRefused', $this->config) == '1') {
$this->view->assignMultiple(
[
'user' => $user,
'status' => 'confirmDeletion',
'hash' =>$hash
]
);
$this->assignForAll();
return $this->htmlResponse();
}
$furtherFunctions = $this->statusUserConfirmationRefused($user, $hash);
break;
case 'adminConfirmation':
if ($status === 'adminConfirmation' && ConfigurationUtility::getValue(
'new./email./createUserConfirmation./confirmAdminConfirmation',
$this->config
) == '1') {
if (!HashUtility::validHash($adminHash, $user, 'admin')) {
$this->addFlashMessage(
LocalizationUtility::translate('error_not_authorized'),
'',
ContextualFeedbackSeverity::ERROR
);
throw new PropagateResponseException($this->redirect('new'), 1743766811);
}
$this->view->assignMultiple(
[
'user' => $user,
'status' => 'confirmAdmin',
'hash' => $hash,
]
);
$this->assignForAll();
return $this->htmlResponse();
}
if (($status === 'adminConfirmationRefused' || $status === 'adminConfirmationRefusedSilent') &&
ConfigurationUtility::getValue(
'new./email./createUserConfirmation./confirmAdminConfirmation',
$this->config
) == '1') {
if (!HashUtility::validHash($adminHash, $user, 'admin')) {
$this->addFlashMessage(
LocalizationUtility::translate('error_not_authorized'),
'',
ContextualFeedbackSeverity::ERROR
);
throw new PropagateResponseException($this->redirect('new'), 1743766811);
}
$this->view->assignMultiple(
[
'user' => $user,
'status' => 'confirmAdminRefused',
'silent' => $status === 'adminConfirmationRefusedSilent',
'hash' => $hash,
]
);
$this->assignForAll();
return $this->htmlResponse();
}
$furtherFunctions = $this->statusAdminConfirmation($user, $hash, $status, $backend);
break;
case 'confirmedByAdmin':
$furtherFunctions = $this->statusAdminConfirmation($user, $hash, $status, $backend);
break;
case 'confirmedByAdminRefused':
$furtherFunctions = $this->statusAdminConfirmationRefused($user, $hash, $status, $backend);
break;
case 'adminConfirmationRefused':
// Admin refuses profile
if (ConfigurationUtility::getValue(
'new./email./createUserConfirmation./confirmAdminConfirmation',
$this->config
) == '1') {
$this->view->assignMultiple(
[
'user' => $user,
'status' => 'confirmAdminRefused',
'hash' => $hash,
]
);
$this->assignForAll();
return $this->htmlResponse();
}
break;
case 'adminConfirmationRefusedSilent':
// Admin refuses profile
if (ConfigurationUtility::getValue(
'new./email./createUserConfirmation./confirmAdminConfirmation',
$this->config
) == '1') {
$this->view->assignMultiple(
[
'user' => $user,
'status' => 'confirmAdminRefused',
'hash' => $hash,
]
);
$this->assignForAll();
return $this->htmlResponse();
}
$furtherFunctions = $this->statusAdminConfirmationRefused($user, $hash, $status);
break;
case 'confirmedByAdminRefusedSilent':
$furtherFunctions = $this->statusAdminConfirmationRefused($user, $hash, $status);
break;
default:
$furtherFunctions = false;
}
if ($backend) {
$this->eventDispatcher->dispatch(new AfterRequestDispatchedEvent($this->request, $this->response));
$this->persistenceManager->persistAll();
// this request was triggered via Backend Module "Frontend users", so we stop here and provide a feedback to the BE
echo json_encode(['status' => 'okay']) . PHP_EOL;
die();
}
if ($furtherFunctions) {
$this->redirectByAction('new', $status . 'Redirect');
}
$this->redirect('new');
}
/**
* Status action: User confirmation
*
* @param User $user
* @param string $hash
* @param string $status
* @return bool allow further functions
* @throws UnsupportedRequestTypeException
* @throws IllegalObjectTypeException
*/
protected function statusUserConfirmation(User $user, string $hash, string $status)
{
if (HashUtility::validHash($hash, $user)) {
if ($user->getTxFemanagerConfirmedbyuser()) {
$this->addFlashMessage(LocalizationUtility::translate('userAlreadyConfirmed'), '', AbstractMessage::ERROR);
$this->redirect('new');
}
$user = FrontendUtility::forceValues($user, ConfigurationUtility::getValue('new./forceValues./onUserConfirmation.', $this->config));
$user->setTxFemanagerConfirmedbyuser(true);
$this->userRepository->update($user);
$this->persistenceManager->persistAll();
$this->logUtility->log(Log::STATUS_REGISTRATIONCONFIRMEDUSER, $user);
if ($this->isAdminConfirmationMissing($user)) {
$this->createAdminConfirmationRequest($user);
} else {
$user->setDisable(false);
$this->logUtility->log(Log::STATUS_NEWREGISTRATION, $user);
$this->finalCreate($user, 'new', 'createStatus', true, $status);
}
} else {
$this->addFlashMessage(LocalizationUtility::translate('createFailedProfile'), '', AbstractMessage::ERROR);
return false;
}
return true;
}
/**
* Status action: User confirmation refused
*
* @param User $user
* @param string $hash
* @return bool allow further functions
* @throws IllegalObjectTypeException
*/
protected function statusUserConfirmationRefused(User $user, $hash)
{
if (HashUtility::validHash($hash, $user)) {
$this->logUtility->log(Log::STATUS_REGISTRATIONREFUSEDUSER, $user);
$this->addFlashMessage(LocalizationUtility::translate('createProfileDeleted'));
$this->userRepository->remove($user);
} else {
$this->addFlashMessage(LocalizationUtility::translate('createFailedProfile'), '', AbstractMessage::ERROR);
return false;
}
return true;
}
/**
* Status action: Admin confirmation
*
* @param User $user
* @param string $hash
* @param string $status
* @return bool allow further functions
*/
protected function statusAdminConfirmation(User $user, $hash, $status, $backend = false)
{
if (HashUtility::validHash($hash, $user)) {
if ($user->getTxFemanagerConfirmedbyadmin()) {
$this->addFlashMessage(LocalizationUtility::translate('userAlreadyConfirmed'), '', AbstractMessage::ERROR);
$this->redirect('new');
}
$user = FrontendUtility::forceValues($user, ConfigurationUtility::getValue('new./forceValues./onAdminConfirmation.', $this->config));
$user->setTxFemanagerConfirmedbyadmin(true);
$user->setDisable(false);
$this->userRepository->update($user);
$this->logUtility->log(Log::STATUS_REGISTRATIONCONFIRMEDADMIN, $user);
$this->finalCreate($user, 'new', 'createStatus', false, $status, $backend);
} else {
$this->addFlashMessage(LocalizationUtility::translate('createFailedProfile'), '', AbstractMessage::ERROR);
return false;
}
return true;
}
/**
* Status action: Admin refused profile creation (normal or silent)
*
* @param User $user
* @param $hash
* @param $status
* @return bool allow further functions
* @throws IllegalObjectTypeException
*/
protected function statusAdminConfirmationRefused(User $user, $hash, $status)
{
if (HashUtility::validHash($hash, $user)) {
$this->logUtility->log(Log::STATUS_REGISTRATIONREFUSEDADMIN, $user);
$this->addFlashMessage(LocalizationUtility::translate('createProfileDeleted'));
if ($status !== 'adminConfirmationRefusedSilent') {
$this->sendMailService->send(
'CreateUserNotifyRefused',
StringUtility::makeEmailArray(
$user->getEmail(),
$user->getFirstName() . ' ' . $user->getLastName()
),
['sender@femanager.org' => 'Sender Name'],
'Your profile was refused',
['user' => $user],
ConfigurationUtility::getValue('new./email./createUserNotifyRefused.', $this->config)
);
}
$this->userRepository->remove($user);
} else {
$this->addFlashMessage(LocalizationUtility::translate('createFailedProfile'), '', AbstractMessage::ERROR);
return false;
}
return true;
}
/**
* Just for showing informations after user creation
*/
public function createStatusAction(): ResponseInterface
{
$this->assignForAll();
return $this->htmlResponse();
}
/**
* Postfix method to createAction(): Create must be confirmed by Admin or User
*
* @param User $user
*/
protected function createRequest(User $user)
{
$user->setDisable(true);
$this->userRepository->add($user);
$this->persistenceManager->persistAll();
$this->logUtility->log(Log::STATUS_PROFILECREATIONREQUEST, $user);
if (!empty($this->settings['new']['confirmByUser'])) {
$this->createUserConfirmationRequest($user);
$this->redirectByAction('new', 'requestRedirect');
} elseif (!empty($this->settings['new']['confirmByAdmin'])) {
$this->createAdminConfirmationRequest($user);
$this->redirectByAction('new', 'requestRedirect');
}
}
/**
* Send email to user for confirmation
*
* @param User $user
* @throws UnsupportedRequestTypeException
*/
protected function createUserConfirmationRequest(User $user)
{
$this->sendCreateUserConfirmationMail($user);
$this->addFlashMessage(LocalizationUtility::translate('createRequestWaitingForUserConfirm'));
$this->redirectByAction('new', 'requestRedirect');
}
/**
* Send email to admin for confirmation
*
* @param User $user
* @throws UnsupportedRequestTypeException
*/
protected function createAdminConfirmationRequest(User $user)
{
$aacService = $this->objectManager->get(
AutoAdminConfirmationService::class,
$user,
$this->settings,
$this->contentObject
);
if ($aacService->isAutoAdminConfirmationFullfilled()) {
$user->setDisable(false);
$this->eventDispatcher->dispatch(
new CreateConfirmationRequestEvent($user, CreateConfirmationRequestEvent::MODE_AUTOMATIC)
);
$this->createAllConfirmed($user);
} else {
$this->eventDispatcher->dispatch(
new CreateConfirmationRequestEvent($user, CreateConfirmationRequestEvent::MODE_MANUAL)
);
$this->sendMailService->send(
'createAdminConfirmation',
StringUtility::makeEmailArray(
$this->settings['new']['confirmByAdmin'] ?? '',
$this->settings['new']['email']['createAdminConfirmation']['receiver']['name']['value'] ?? ''
),
StringUtility::makeEmailArray($user->getEmail(), $user->getUsername()),
'New Registration request',
[
'user' => $user,
'hash' => HashUtility::createHashForUser($user),
'adminHash' => HashUtility::createHashForUser($user, 'admin'),
],
ConfigurationUtility::getValue('new./email./createAdminConfirmation.', $this->config)
);
$this->addFlashMessage(LocalizationUtility::translate('createRequestWaitingForAdminConfirm'));
}
}
/**
* @return bool
*/
protected function isAllConfirmed()
{
return empty($this->settings['new']['confirmByUser']) && empty($this->settings['new']['confirmByAdmin']);
}
/**
* @param User $user
* @return bool
*/
protected function isAdminConfirmationMissing(User $user)
{
return !empty($this->settings['new']['confirmByAdmin']) && !$user->getTxFemanagerConfirmedbyadmin();
}
/**
* Just for showing empty dialogue to resend confirmation mail
*/
public function resendConfirmationDialogueAction(): ResponseInterface
{
return $this->htmlResponse();
}
/**
* re-sends a confirmation email if given mail is valid
*
* @throws UnsupportedRequestTypeException
* @throws StopActionException
*/
public function resendConfirmationMailAction()
{
// @todo find a better way to fetch the data
$result = GeneralUtility::_GP('tx_femanager_pi1');
if (is_array($result)) {
if (GeneralUtility::validEmail($result['user']['email'])) {
$user = $this->userRepository->findFirstByEmail($result['user']['email']);
if (is_a($user, User::class)) {
$this->sendCreateUserConfirmationMail($user);
$this->addFlashMessage(
LocalizationUtility::translate('resendConfirmationMailSend'),
'',
AbstractMessage::INFO
);
$this->redirect('resendConfirmationDialogue');
}
}
}
$this->addFlashMessage(
LocalizationUtility::translate('resendConfirmationMailFail'),
LocalizationUtility::translate('validationError'),
AbstractMessage::ERROR
);
$this->redirect('resendConfirmationDialogue');
}
}
@@ -0,0 +1,276 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Controller;
use In2code\Femanager\Domain\Model\User;
use In2code\Femanager\Event\AdminConfirmationUserEvent;
use In2code\Femanager\Event\RefuseUserEvent;
use In2code\Femanager\Utility\ConfigurationUtility;
use In2code\Femanager\Utility\HashUtility;
use In2code\Femanager\Utility\LocalizationUtility;
use In2code\Femanager\Utility\UserUtility;
use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Backend\Routing\UriBuilder;
use TYPO3\CMS\Core\Messaging\AbstractMessage;
use TYPO3\CMS\Core\Site\SiteFinder;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Http\ForwardResponse;
/**
* Class UserBackendController
*/
class UserBackendController extends AbstractController
{
protected $configPID;
/**
* @param array $filter
*/
public function listAction(array $filter = []): ResponseInterface
{
$uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
$this->view->assignMultiple(
[
'users' => $this->userRepository->findAllInBackend($filter),
'moduleUri' => $uriBuilder->buildUriFromRoute('tce_db'),
'action' => 'list',
'loginAsEnabled' => $this->loginAsEnabled()
]
);
return $this->htmlResponse();
}
/**
* @param array $filter
*/
public function confirmationAction(array $filter = []): ResponseInterface
{
$this->configPID = $this->getConfigPID();
$uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
$this->view->assignMultiple(
[
'users' => $this->userRepository->findAllInBackendForConfirmation(
$filter,
ConfigurationUtility::isBackendModuleFilterUserConfirmation()
),
'moduleUri' => $uriBuilder->buildUriFromRoute('tce_db'),
'action' => 'confirmation'
]
);
return $this->htmlResponse();
}
/**
* @param User $user
*/
public function userLogoutAction(User $user)
{
$queryParams = $this->request->getQueryParams();
if (empty($queryParams['id']) || $queryParams['id'] !== (string)$user->getPid()) {
return new ForwardResponse('list');
}
UserUtility::removeFrontendSessionToUser($user);
$this->addFlashMessage('User successfully logged out');
$this->redirect('list');
}
/**
* @param int $userIdentifier
*/
public function confirmUserAction(int $userIdentifier)
{
$this->configPID = $this->getConfigPID();
$user = $this->userRepository->findByUid($userIdentifier);
$queryParams = $this->request->getQueryParams();
if (empty($queryParams['id']) || $user === null || $queryParams['id'] !== (string)$user->getPid()) {
return new ForwardResponse('list');
}
$this->eventDispatcher->dispatch(new AdminConfirmationUserEvent($user));
$jsonResult = $this->getFrontendRequestResult('adminConfirmation', $userIdentifier, $user);
if ($jsonResult['status'] ?? false) {
$this->addFlashMessage(
LocalizationUtility::translate(
'BackendConfirmationFlashMessageConfirmed',
'femanager',
[$user->getUsername()]
),
'User Confirmation'
);
} else {
$this->addFlashMessage(
LocalizationUtility::translate(
'BackendConfirmationFlashMessageFailed',
'femanager',
[$user->getUsername()]
),
'User Confirmation',
AbstractMessage::ERROR
);
}
$this->redirect('confirmation');
}
/**
* @param int $userIdentifier
*/
public function refuseUserAction(int $userIdentifier)
{
$this->configPID = $this->getConfigPID();
$user = $this->userRepository->findByUid($userIdentifier);
$queryParams = $this->request->getQueryParams();
if (empty($queryParams['id']) || $user === null || $queryParams['id'] !== (string)$user->getPid()) {
return new ForwardResponse('list');
}
$this->eventDispatcher->dispatch(new RefuseUserEvent($user));
$jsonResult = $this->getFrontendRequestResult('adminConfirmationRefused', $userIdentifier, $user);
if ($jsonResult['status'] ?? false) {
$this->addFlashMessage(
LocalizationUtility::translate(
'BackendConfirmationFlashMessageRefused',
'femanager',
[$user->getUsername()]
)
);
} else {
$this->addFlashMessage(
LocalizationUtility::translate(
'BackendConfirmationFlashMessageFailed',
'femanager',
[$user->getUsername()]
),
'User Confirmation',
AbstractMessage::ERROR
);
}
$this->redirect('confirmation');
}
/**
* @param array $filter
*/
public function listOpenUserConfirmationsAction(array $filter = []): ResponseInterface
{
$uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
$this->view->assignMultiple(
[
'users' => $this->userRepository->findAllInBackendForConfirmation(
$filter,
false
),
'moduleUri' => $uriBuilder->buildUriFromRoute('tce_db'),
'action' => 'listOpenUserConfirmations'
]
);
return $this->htmlResponse();
}
/**
* @param int $userIdentifier
*/
public function resendUserConfirmationRequestAction(int $userIdentifier)
{
$user = $this->userRepository->findByUid($userIdentifier);
$queryParams = $this->request->getQueryParams();
if (empty($queryParams['id']) || $user === null || $queryParams['id'] !== (string)$user->getPid()) {
return new ForwardResponse('list');
}
$this->sendCreateUserConfirmationMail($user);
$this->addFlashMessage(
LocalizationUtility::translate(
'BackendConfirmationFlashMessageReSend',
'femanager',
[$user->getUsername()]
),
'',
AbstractMessage::OK
);
$this->redirect('listOpenUserConfirmations');
}
/**
* @return int
*/
public function getConfigPID()
{
if (isset($this->moduleConfig['settings.']['configPID']) && $this->moduleConfig['settings.']['configPID'] > 0) {
return (int)$this->moduleConfig['settings.']['configPID'];
}
$this->addFlashMessage(
LocalizationUtility::translate(
'BackendMissingConfigPID',
'femanager'
),
'Backend Configuration',
AbstractMessage::ERROR
);
return 0;
}
/**
* @param $status
* @param $userIdentifier
* @param $user
*/
public function getFrontendRequestResult($status, $userIdentifier, $user)
{
/** @var SiteFinder $siteFinder */
$siteFinder = GeneralUtility::makeInstance(SiteFinder::class);
$site = $siteFinder->getSiteByPageId($this->configPID);
$url = $site->getRouter()->generateUri(
$this->configPID,
[
'tx_femanager_pi1' => [
'user' => $userIdentifier,
'hash' => HashUtility::createHashForUser($user),
'status' => $status,
'action' => 'confirmCreateRequest',
'controller' => 'New'
]
]
);
$response = GeneralUtility::makeInstance(RequestFactory::class)->request((string)$url, 'GET', ['headers' => ['accept' => 'application/json']]);
if ($response->getStatusCode() >= 300) {
$content = $response->getReasonPhrase();
$GLOBALS['BE_USER']->writelog(4, 0, 1, 0, 'femanager: Frontend request failed.', $content);
$this->addFlashMessage(
LocalizationUtility::translate(
'BackendConfirmationFlashMessageFailed',
'femanager'
),
'User Confirmation',
AbstractMessage::ERROR
);
} else {
$content = $response->getBody()->getContents();
return json_decode($content, true);
}
}
private function loginAsEnabled(): bool
{
if ($GLOBALS['BE_USER']->user['admin'] === 1) {
return true;
}
$tsConfigEnableLoginAs = (int)($GLOBALS['BE_USER']->getTSConfig()['tx_femanager.']['UserBackend.']['enableLoginAs'] ?? 0);
return $tsConfigEnableLoginAs === 1;
}
}
@@ -0,0 +1,157 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Controller;
use In2code\Femanager\Domain\Model\Log;
use In2code\Femanager\Domain\Model\User;
use In2code\Femanager\Domain\Validator\ClientsideValidator;
use In2code\Femanager\Event\ImpersonateEvent;
use In2code\Femanager\Utility\BackendUserUtility;
use In2code\Femanager\Utility\FrontendUtility;
use In2code\Femanager\Utility\LocalizationUtility;
use In2code\Femanager\Utility\UserUtility;
use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Core\Error\Http\UnauthorizedException;
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
/**
* Class UserController
*/
class UserController extends AbstractFrontendController
{
/**
* @param array $filter
*/
public function listAction(array $filter = []): ResponseInterface
{
$this->view->assignMultiple(
[
'users' => $this->userRepository->findByUsergroups(
$this->settings['list']['usergroup'] ?? '',
$this->settings,
$filter
),
'filter' => $filter
]
);
$this->assignForAll();
return $this->htmlResponse();
}
/**
* Enforce user setting from FlexForm and ignore &tx_femanager_pi1[user]=421
*/
public function initializeShowAction()
{
$arguments = $this->request->getArguments();
if (!empty($this->settings['show']['user']) ?? '') {
unset($arguments['user']);
}
$this->request->setArguments($arguments);
}
/**
* @param User $user
*/
public function showAction(User $user = null): ResponseInterface
{
$this->view->assign('user', $this->getUser($user));
$this->assignForAll();
return $this->htmlResponse();
}
/**
* @param User $user
* @throws \Exception
*/
public function imageDeleteAction(User $user)
{
if (UserUtility::getCurrentUser() !== $user) {
throw new UnauthorizedException('You are not allowed to delete this image', 1516373759972);
}
$user->setImage($this->objectManager->get(ObjectStorage::class));
$this->userRepository->update($user);
$this->logUtility->log(Log::STATUS_PROFILEUPDATEIMAGEDELETE, $user);
$this->addFlashMessage(LocalizationUtility::translateByState(Log::STATUS_PROFILEUPDATEIMAGEDELETE));
$this->redirectToUri(FrontendUtility::getUriToCurrentPage());
}
/**
* Call this Action from eID to validate field values
*
* @param string $validation Validation string like "required, email, min(10)"
* @param string $value Given Field value
* @param string $field Fieldname like "username" or "email"
* @param User $user Existing User
* @param string $additionalValue Additional Values
* @param int $plugin tt_content.uid of the femanager plugin
* @param string $referrerAction current action name
*/
public function validateAction(
$validation = null,
$value = null,
$field = null,
User $user = null,
$additionalValue = '',
int $plugin = 0,
string $referrerAction = ''
): ResponseInterface {
$clientsideValidator = $this->objectManager->get(ClientsideValidator::class);
$result = $clientsideValidator
->setValidationSettingsString($validation)
->setValue($value)
->setFieldName($field)
->setUser($user)
->setAdditionalValue($additionalValue)
->setPlugin($plugin)
->setActionName($referrerAction)
->validateField();
$this->view->assignMultiple(
[
'isValid' => $result,
'messages' => $clientsideValidator->getMessages(),
'validation' => $validation,
'value' => $value,
'fieldname' => $field,
'user' => $user
]
);
return $this->htmlResponse();
}
/**
* Simulate frontenduser login for backend adminstrators only
*
* @param User $user
* @throws UnauthorizedException
*/
public function loginAsAction(User $user)
{
$this->eventDispatcher->dispatch(new ImpersonateEvent($user));
if (!BackendUserUtility::isAdminAuthentication()) {
throw new UnauthorizedException(LocalizationUtility::translate('error_not_authorized'), 1516373787864);
}
UserUtility::login($user);
$this->redirectByAction('loginAs', 'redirect');
$this->redirectToUri('/');
}
/**
* @param User $user
* @return User
*/
protected function getUser(User $user = null)
{
if ($user === null) {
if (is_numeric($this->settings['show']['user'])) {
$user = $this->userRepository->findByUid($this->settings['show']['user']);
} elseif ($this->settings['show']['user'] === '[this]') {
$user = $this->user;
}
}
return $user;
}
}
@@ -0,0 +1,72 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\DataProcessor;
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Extbase\Mvc\Controller\Arguments;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
/**
* Class AbstractDataProcessor
*/
abstract class AbstractDataProcessor implements DataProcessorInterface
{
/**
* @var array
*/
protected $configuration = [];
/**
* @var array
*/
protected $settings = [];
/**
* @var ContentObjectRenderer|null
*/
protected $contentObject;
/**
* @var Arguments|null
*/
protected $controllerArguments;
/**
* AbstractDataProcessor constructor.
*
* @param array $configuration
* @param array $settings
* @param ContentObjectRenderer $contentObject
* @param Arguments $controllerArguments
*/
public function __construct(
array $configuration,
array $settings,
ContentObjectRenderer $contentObject,
Arguments $controllerArguments
) {
$this->configuration = $configuration;
$this->settings = $settings;
$this->contentObject = $contentObject;
$this->controllerArguments = $controllerArguments;
}
public function initializeDataProcessor()
{
}
/**
* @param string $path
* @return mixed
*/
public function getConfiguration(string $path = '')
{
$configuration = $this->configuration;
if (!empty($path)) {
$configuration = ArrayUtility::getValueByPath($configuration, $path, '.');
}
return $configuration;
}
}
@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\DataProcessor;
/**
* Class CleanUserGroup to clean empty usergroup arguments
*/
class CleanUserGroup extends AbstractDataProcessor
{
/**
* @param array $arguments
* @return array
*/
public function process(array $arguments): array
{
if (is_array($arguments['user']['usergroup'] ?? null) && empty($arguments['user']['usergroup'][0]) && empty($arguments['user']['usergroup'][0]['__identity'])) {
unset($arguments['user']['usergroup'][0]);
}
return $arguments;
}
}
@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\DataProcessor;
/**
* Interface DataProcessorInterface
*/
interface DataProcessorInterface
{
public function initializeDataProcessor();
/**
* @param array $arguments
* @return array
*/
public function process(array $arguments): array;
}
@@ -0,0 +1,125 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\DataProcessor;
use In2code\Femanager\Utility\ConfigurationUtility;
use In2code\Femanager\Utility\FrontendUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\Controller\Arguments;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
/***************************************************************
* Copyright notice
*
* (c) 2017 Alex Kellner <alexander.kellner@in2code.de>, in2code.de
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
/**
* Class DataProcessorRunner
*/
class DataProcessorRunner
{
/**
* @var ContentObjectRenderer
*/
protected $contentObject;
/**
* TypoScript settings
*
* @var array
*/
protected $settings = [];
/**
* @var string
*/
protected $interface = DataProcessorInterface::class;
/**
* Call classes after submit but before action
*
* @param array $arguments
* @param array $settings
* @param ContentObjectRenderer $contentObject
* @param Arguments $controllerArguments
* @return array
* @throws \Exception
*/
public function callClasses(
array $arguments,
array $settings,
ContentObjectRenderer $contentObject,
Arguments $controllerArguments
): array {
foreach ($this->getClasses($settings) as $configuration) {
$class = $configuration['class'];
if (!class_exists($class)) {
throw new \UnexpectedValueException(
'DataProcessor class ' . $class . ' does not exists - check if file is loaded correctly',
1516373818752
);
}
if (is_subclass_of($class, $this->interface)) {
/** @var AbstractDataProcessor $dataProcessor */
/** @noinspection PhpMethodParametersCountMismatchInspection */
$dataProcessor = GeneralUtility::makeInstance(
$class,
$configuration['config'] ?? [],
$settings,
$contentObject,
$controllerArguments
);
$dataProcessor->initializeDataProcessor();
$arguments = $dataProcessor->process($arguments);
} else {
throw new \UnexpectedValueException('Finisher does not implement ' . $this->interface, 1516373829946);
}
}
return $arguments;
}
/**
* Get all classes to this event from typoscript and sort them
*
* @param array $settings
* @return array
*/
protected function getClasses(array $settings): array
{
$allDataProcessors = ConfigurationUtility::getValue('dataProcessors', $settings);
ksort($allDataProcessors);
$dataProcessors = [];
foreach ($allDataProcessors as $dataProcessor) {
if (isset($dataProcessor['events']) === true && $dataProcessor['events'] !== []) {
foreach ($dataProcessor['events'] as $controllerName => $actionList) {
if ($controllerName === FrontendUtility::getControllerName()) {
if (GeneralUtility::inList($actionList, FrontendUtility::getActionName())) {
$dataProcessors[] = $dataProcessor;
}
}
}
}
}
return $dataProcessors;
}
}
@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\DataProcessor;
use In2code\Femanager\Utility\LocalizationUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Property\TypeConverter\DateTimeConverter;
/**
* Class DateConverter
*/
class DateConverter extends AbstractDataProcessor
{
/**
* @param array $arguments
* @return array
*/
public function process(array $arguments): array
{
if (!empty($this->controllerArguments['user'])) {
foreach (GeneralUtility::trimExplode(',', $this->getConfiguration('fieldNames'), true) as $fieldName) {
$this->controllerArguments['user']
->getPropertyMappingConfiguration()
->forProperty($fieldName)
->setTypeConverterOption(
DateTimeConverter::class,
DateTimeConverter::CONFIGURATION_DATE_FORMAT,
LocalizationUtility::translate('tx_femanager_domain_model_user.dateFormat')
);
}
}
return $arguments;
}
}
@@ -0,0 +1,168 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\DataProcessor;
use In2code\Femanager\Domain\Service\FileService;
use In2code\Femanager\Utility\ConfigurationUtility;
use In2code\Femanager\Utility\FileUtility;
use In2code\Femanager\Utility\FrontendUtility;
use In2code\Femanager\Utility\ObjectUtility;
use In2code\Femanager\Utility\StringUtility;
use TYPO3\CMS\Core\Utility\File\BasicFileUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Class ImageManipulation
*/
class ImageManipulation extends AbstractDataProcessor
{
/**
* @param array $arguments
* @return array
*/
public function process(array $arguments): array
{
$this->allowImageProperties();
foreach ($this->getPropertiesForUpload() as $property) {
if ($this->isFileIdentifierGiven($arguments, $property) || $this->isUploadError($arguments, $property)) {
unset($arguments['user'][$property]);
} else {
// Convert property name
$property = lcfirst(GeneralUtility::underscoredToUpperCamelCase($property));
// file upload given
foreach ($arguments['user'][$property] ?? [] as $fileItem) {
/** @noinspection PhpMethodParametersCountMismatchInspection */
$fileService = GeneralUtility::makeInstance(
FileService::class,
$this->getNewImageName($fileItem),
$fileItem
);
if ($fileService->isEverythingValid()) {
FileUtility::createFolderIfNotExists($this->getUploadFolder());
$pathAndFilename = $this->upload($fileItem);
$fileIdentifier = $fileService->indexFile($pathAndFilename);
$identifier = $this->createSysFileRelation($fileIdentifier);
$arguments['user'][$property] = [$identifier];
} else {
unset($arguments['user'][$property]);
}
}
}
}
return $arguments;
}
/**
* @param int $fileIdentifier
* @return int
*/
protected function createSysFileRelation(int $fileIdentifier): int
{
$properties = [
'pid' => FrontendUtility::getCurrentPid(),
'uid_local' => $fileIdentifier,
'tstamp' => time(),
'crdate' => time()
];
foreach ($this->getConfiguration('sysFileRelation') as $field => $value) {
$properties[$field] = $value;
}
$databaseConnectionForPages = ObjectUtility::getConnectionPool()->getConnectionForTable('sys_file_reference');
$databaseConnectionForPages->insert(
'sys_file_reference',
$properties
);
return (int)$databaseConnectionForPages->lastInsertId('sys_file_reference');
}
/**
* @param array $fileItem
* @return string New filename (absolute with path)
* @throws \Exception
*/
protected function upload(array $fileItem): string
{
$basicFileFunctions = GeneralUtility::makeInstance(BasicFileUtility::class);
$uniqueFileName = $basicFileFunctions->getUniqueName(
$this->getNewImageName($fileItem),
$this->getUploadFolder()
);
if (GeneralUtility::upload_copy_move($fileItem['tmp_name'], $uniqueFileName)) {
return $uniqueFileName;
}
throw new \UnexpectedValueException(
'File "' . $this->getNewImageName($fileItem) . '" could not be uploaded!',
1516373841798
);
}
/**
* @return array
*/
protected function getPropertiesForUpload(): array
{
$propertylist = $this->getConfiguration('propertyNamesForUpload');
return GeneralUtility::trimExplode(',', $propertylist, true);
}
/**
* @param array $fileItem
* @return string
*/
protected function getNewImageName(array $fileItem): string
{
$imageName = '';
if (!empty($fileItem['name'])) {
$imageName = $fileItem['name'];
}
$imageName = StringUtility::cleanString($imageName);
return $imageName;
}
/**
* @param bool $absolute
* @return string
*/
protected function getUploadFolder(bool $absolute = true): string
{
$path = (string)ConfigurationUtility::getConfiguration('misc.uploadFolder');
if ($absolute === true) {
$path = GeneralUtility::getFileAbsFileName($path);
}
return $path;
}
protected function allowImageProperties()
{
if (!empty($this->controllerArguments['user'])) {
$this->controllerArguments['user']->getPropertyMappingConfiguration()->forProperty(
'image'
)->allowProperties(0);
}
}
/**
* @param array $arguments
* @param $property
* @return bool
*/
protected function isFileIdentifierGiven(array $arguments, $property): bool
{
return !empty($arguments['user'][$property][0]['__identity']);
}
/**
* @param array $arguments
* @param $property
* @return bool
*/
protected function isUploadError(array $arguments, $property): bool
{
return !empty($arguments['user'][$property][0]['error']);
}
}
@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\DataProvider;
use SJBR\StaticInfoTables\Domain\Model\Country;
use SJBR\StaticInfoTables\Domain\Repository\CountryRepository;
use TYPO3\CMS\Core\Utility\GeneralUtility;
class CountryDataProvider
{
protected $countryRepository;
public function __construct()
{
$this->countryRepository = GeneralUtility::makeInstance(CountryRepository::class);
}
/** @return Country[] */
public function getCountries(): array
{
return $this->countryRepository->findAllOrderedBy('shortNameLocal')->toArray();
}
}
@@ -0,0 +1,61 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\DataProvider;
use SJBR\StaticInfoTables\Domain\Model\Country;
use SJBR\StaticInfoTables\Domain\Model\CountryZone;
use SJBR\StaticInfoTables\Domain\Repository\CountryRepository;
use SJBR\StaticInfoTables\Domain\Repository\CountryZoneRepository;
use TYPO3\CMS\Core\Utility\GeneralUtility;
class CountryZonesDataProvider
{
private $countryZoneRepository;
private $countryRepository;
public function __construct()
{
// Can not autowire optional dependencies
$this->countryZoneRepository = GeneralUtility::makeInstance(CountryZoneRepository::class);
$this->countryRepository = GeneralUtility::makeInstance(CountryRepository::class);
}
/** @return CountryZone[] */
public function getCountryZonesForCountryIso3(string $countryIso3): array
{
$country = $this->getCountryForIsoCode3($countryIso3);
if (null === $country) {
return [];
}
$countryZones = $this->countryZoneRepository->findByCountry($country)->toArray();
usort(
$countryZones,
function (CountryZone $left, CountryZone $right) {
return strcasecmp($left->getLocalName(), $right->getLocalName());
}
);
return $countryZones;
}
public function hasCountryZonesForCountryIso3(string $countryIso3): bool
{
return $this->countryZoneRepository->countByCountryIsoCodeA3($countryIso3) > 0;
}
/**
* @param string $countryIso3
* @return Country|null
*/
private function getCountryForIsoCode3(string $countryIso3): ?Country
{
$countries = $this->countryRepository->findAllowedByIsoCodeA3($countryIso3);
$country = null;
if (count($countries) === 1) {
$country = array_values($countries)[0];
}
return $country;
}
}
@@ -0,0 +1,268 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\DataProvider;
class FallbackCountryDataProvider
{
protected static $countries = [
'AND' => 'Andorra',
'ARE' => 'الإمارات العربيّة المتّحدة',
'AFG' => 'افغانستان',
'ATG' => 'Antigua and Barbuda',
'AIA' => 'Anguilla',
'ALB' => 'Shqipëria',
'ARM' => 'Հայաստան',
'ANT' => 'Nederlandse Antillen',
'AGO' => 'Angola',
'ATA' => 'Antarctica',
'ARG' => 'Argentina',
'ASM' => 'Amerika Samoa',
'AUT' => 'Österreich',
'AUS' => 'Australia',
'ABW' => 'Aruba',
'AZE' => 'Azərbaycan',
'BIH' => 'BiH/БиХ',
'BRB' => 'Barbados',
'BGD' => 'বাংলাদেশ',
'BEL' => 'Belgique',
'BFA' => 'Burkina',
'BGR' => 'България (Bulgaria)',
'BHR' => 'البحري',
'BDI' => 'Burundi',
'BEN' => 'Bénin',
'BMU' => 'Bermuda',
'BRN' => 'دارالسلام',
'BOL' => 'Bolivia',
'BRA' => 'Brasil',
'BHS' => 'The Bahamas',
'BTN' => 'Druk-Yul',
'BVT' => 'Bouvet Island',
'BWA' => 'Botswana',
'BLR' => 'Беларусь',
'BLZ' => 'Belize',
'CAN' => 'Canada',
'CCK' => 'Cocos (Keeling) Islands',
'COD' => 'Congo',
'CAF' => 'Centrafrique',
'COG' => 'Congo-Brazzaville',
'CHE' => 'Schweiz',
'CIV' => 'Côte d’Ivoire',
'COK' => 'Cook Islands',
'CHL' => 'Chile',
'CMR' => 'Cameroun',
'CHN' => '中华',
'COL' => 'Colombia',
'CRI' => 'Costa Rica',
'CUB' => 'Cuba',
'CPV' => 'Cabo Verde',
'CXR' => 'Christmas Island',
'CYP' => 'Κύπρος / Kıbrıs',
'CZE' => 'Česko',
'DEU' => 'Deutschland',
'DJI' => 'Djibouti',
'DNK' => 'Danmark',
'DMA' => 'Dominica',
'DOM' => 'Quisqueya',
'DZA' => 'الجزائ',
'ECU' => 'Ecuador',
'EST' => 'Eesti',
'EGY' => 'مصر',
'ESH' => 'الصحراء الغربي',
'ERI' => 'ኤርትራ',
'ESP' => 'España',
'ETH' => 'ኢትዮጵያ',
'FIN' => 'Suomi',
'FJI' => 'Fiji / Viti',
'FLK' => 'Falkland Islands',
'FSM' => 'Micronesia',
'FRO' => 'Føroyar / Færøerne',
'FRA' => 'France',
'GAB' => 'Gabon',
'GBR' => 'United Kingdom',
'GRD' => 'Grenada',
'GEO' => 'საქართველო',
'GUF' => 'Guyane française',
'GHA' => 'Ghana',
'GIB' => 'Gibraltar',
'GRL' => 'Grønland',
'GMB' => 'Gambia',
'GIN' => 'Guinée',
'GLP' => 'Guadeloupe',
'GNQ' => 'Guinea Ecuatorial',
'GRC' => 'Ελλάδα',
'SGS' => 'South Georgia and the South Sandwich Islands',
'GTM' => 'Guatemala',
'GUM' => 'Guåhån',
'GNB' => 'Guiné-Bissau',
'GUY' => 'Guyana',
'HKG' => '香港',
'HND' => 'Honduras',
'HRV' => 'Hrvatska',
'HTI' => 'Ayiti',
'HUN' => 'Magyarország',
'IDN' => 'Indonesia',
'IRL' => 'Éire',
'ISR' => 'ישראל',
'IND' => 'India',
'IOT' => 'British Indian Ocean Territory',
'IRQ' => 'العراق / عيَراق',
'IRN' => 'ايران',
'ISL' => 'Ísland',
'ITA' => 'Italia',
'JAM' => 'Jamaica',
'JOR' => 'أردنّ',
'JPN' => '日本',
'KEN' => 'Kenya',
'KGZ' => 'Кыргызстан',
'KHM' => 'Kâmpŭchea',
'KIR' => 'Kiribati',
'COM' => 'اتحاد القمر',
'KNA' => 'Saint Kitts and Nevis',
'PRK' => '북조선',
'KOR' => '한국',
'KWT' => 'الكويت',
'CYM' => 'Cayman Islands',
'KAZ' => 'Қазақстан /Казахстан',
'LAO' => 'ເມືອງລາວ',
'LBN' => 'لبنان',
'LCA' => 'Saint Lucia',
'LIE' => 'Liechtenstein',
'LKA' => 'ශ්‍රී ලංකා / இலங்கை',
'LBR' => 'Liberia',
'LSO' => 'Lesotho',
'LTU' => 'Lietuva',
'LUX' => 'Luxemburg',
'LVA' => 'Latvija',
'LBY' => 'ليبيا',
'MAR' => 'المغربية',
'MCO' => 'Monaco',
'MDA' => 'Moldova',
'MDG' => 'Madagascar',
'MHL' => 'Marshall Islands',
'MKD' => 'Македонија',
'MLI' => 'Mali',
'MMR' => 'Myanmar',
'MNG' => 'Монгол Улс',
'MAC' => '澳門 / Macau',
'MNP' => 'Northern Marianas',
'MTQ' => 'Martinique',
'MRT' => 'الموريتانية',
'MSR' => 'Montserrat',
'MLT' => 'Malta',
'MUS' => 'Mauritius',
'MDV' => 'ޖުމުހޫރިއްޔ',
'MWI' => 'Malawi',
'MEX' => 'México',
'MYS' => 'مليسيا',
'MOZ' => 'Moçambique',
'NAM' => 'Namibia',
'NCL' => 'Nouvelle-Calédonie',
'NER' => 'Niger',
'NFK' => 'Norfolk Island',
'NGA' => 'Nigeria',
'NIC' => 'Nicaragua',
'NLD' => 'Nederland',
'NOR' => 'Norge',
'NPL' => 'नेपाल',
'NRU' => 'Naoero',
'NIU' => 'Niue',
'NZL' => 'New Zealand / Aotearoa',
'OMN' => 'عُمان',
'PAN' => 'Panamá',
'PER' => 'Perú',
'PYF' => 'Polynésie française',
'PNG' => 'Papua New Guinea / Papua Niugini',
'PHL' => 'Philippines',
'PAK' => 'پاکستان',
'POL' => 'Polska',
'SPM' => 'Saint-Pierre-et-Miquelon',
'PCN' => 'Pitcairn Islands',
'PRI' => 'Puerto Rico',
'PRT' => 'Portugal',
'PLW' => 'Belau / Palau',
'PRY' => 'Paraguay',
'QAT' => 'قطر',
'REU' => 'Réunion',
'ROU' => 'România',
'RUS' => 'Россия',
'RWA' => 'Rwanda',
'SAU' => 'السعودية',
'SLB' => 'Solomon Islands',
'SYC' => 'Seychelles',
'SDN' => 'Sénégal',
'SWE' => 'Sverige',
'SGP' => 'Singapore',
'SHN' => 'Saint Helena, Ascension and Tristan da Cunha',
'SVN' => 'Slovenija',
'SJM' => 'Svalbard',
'SVK' => 'Slovensko',
'SLE' => 'Sierra Leone',
'SMR' => 'San Marino',
'SEN' => 'Sénégal',
'SOM' => 'Soomaaliya',
'SUR' => 'Suriname',
'STP' => 'São Tomé e Príncipe',
'SLV' => 'El Salvador',
'SYR' => 'سوري',
'SWZ' => 'weSwatini',
'TCA' => 'Turks and Caicos Islands',
'TCD' => 'Tchad',
'ATF' => 'Terres australes fran‡aises',
'TGO' => 'Togo',
'THA' => 'ไทย',
'TJK' => 'Тоҷикистон',
'TKL' => 'Tokelau',
'TKM' => 'Türkmenistan',
'TUN' => 'التونسية',
'TON' => 'Tonga',
'TLS' => 'Timor Lorosa\'e',
'TUR' => 'Türkiye',
'TTO' => 'Trinidad and Tobago',
'TUV' => 'Tuvalu',
'TWN' => '中華',
'TZA' => 'Tanzania',
'UKR' => 'Україна',
'UGA' => 'Uganda',
'UMI' => 'United States Minor Outlying Islands',
'USA' => 'United States',
'URY' => 'Uruguay',
'UZB' => 'O‘zbekiston',
'VAT' => 'Vaticano',
'VCT' => 'Saint Vincent and the Grenadines',
'VEN' => 'Venezuela',
'VGB' => 'British Virgin Islands',
'VIR' => 'US Virgin Islands',
'VNM' => 'Việt Nam',
'VUT' => 'Vanuatu',
'WLF' => 'Wallis and Futuna',
'WSM' => 'Samoa',
'YEM' => 'اليمنية',
'MYT' => 'Mayotte',
'ZAF' => 'Afrika-Borwa',
'ZMB' => 'Zambia',
'ZWE' => 'Zimbabwe',
'PSE' => 'فلسطين',
'CSG' => 'Србија и Црна Гора',
'ALA' => 'Åland',
'HMD' => 'Heard Island and McDonald Islands',
'MNE' => 'Crna Gora',
'SRB' => 'Srbija',
'JEY' => 'Jersey',
'GGY' => 'Guernsey',
'IMN' => 'Mann / Mannin',
'MAF' => 'Saint-Martin',
'BLM' => 'Saint-Barthélemy',
'BES' => 'Bonaire, Sint Eustatius en Saba',
'CUW' => 'Curaçao',
'SXM' => 'Sint Maarten',
'SSD' => 'South Sudan'
];
/** @return string[] */
public function getCountries(): array
{
return self::$countries;
}
}
@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\DependencyInjection\CompilerPass;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMap;
class ChangeClassDatamapPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition(DataMap::class)) {
return;
}
$definition = $container->findDefinition(DataMap::class);
$definition->setClass(\In2code\Femanager\Persistence\Generic\Mapper\DataMap::class);
}
}
@@ -0,0 +1,111 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Domain\Model;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
/**
* Class Log
*/
class Log extends AbstractEntity
{
const STATUS_NEWREGISTRATION = 101;
const STATUS_REGISTRATIONCONFIRMEDUSER = 102;
const STATUS_REGISTRATIONCONFIRMEDADMIN = 103;
const STATUS_REGISTRATIONREFUSEDUSER = 104;
const STATUS_REGISTRATIONREFUSEDADMIN = 105;
const STATUS_PROFILECREATIONREQUEST = 106;
const STATUS_PROFILEUPDATED = 201;
const STATUS_PROFILEUPDATECONFIRMEDADMIN = 202;
const STATUS_PROFILEUPDATEREFUSEDADMIN = 203;
const STATUS_PROFILEUPDATEREQUEST = 204;
const STATUS_PROFILEUPDATEREFUSEDSECURITY = 205;
const STATUS_PROFILEUPDATEIMAGEDELETE = 206;
const STATUS_PROFILEUPDATEATTEMPTEDSPOOF = 207;
const STATUS_PROFILEDELETE = 301;
const STATUS_INVITATIONPROFILECREATED = 401;
const STATUS_INVITATIONPROFILEDELETEDUSER = 402;
const STATUS_INVITATIONHASHERROR = 403;
const STATUS_INVITATIONRESTRICTEDPAGE = 404;
const STATUS_INVITATIONPROFILEENABLED = 405;
/**
* title
*
* @var string
*/
protected $title;
/**
* state
*
* @var int
*/
protected $state;
/**
* user
*
* @var User
*/
protected $user;
/**
* @param string $title
* @return Log
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* @return string
*/
public function getTitle()
{
return $this->title;
}
/**
* @param int $state
* @return Log
*/
public function setState($state)
{
$this->state = $state;
return $this;
}
/**
* @return int
*/
public function getState()
{
return $this->state;
}
/**
* Set user
*
* @param User $user
* @return Log
*/
public function setUser(User $user)
{
$this->user = $user;
return $this;
}
/**
* Get user
*
* @return User
*/
public function getUser()
{
return $this->user;
}
}
@@ -0,0 +1,434 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Domain\Model;
use In2code\Femanager\Utility\UserUtility;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Domain\Model\FrontendUser;
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
/**
* Class User
*/
class User extends FrontendUser
{
const TABLE_NAME = 'fe_users';
/**
* @var string
*/
protected $txFemanagerChangerequest;
/**
* @var \DateTime
*/
protected $crdate;
/**
* @var \DateTime
*/
protected $tstamp;
/**
* @var bool
*/
protected $disable;
/**
* @var bool
*/
protected $txFemanagerConfirmedbyuser;
/**
* @var bool
*/
protected $txFemanagerConfirmedbyadmin;
/**
* @var bool
*/
protected $isOnline = false;
/**
* @var bool
*/
protected $ignoreDirty = false;
/**
* @var int
*/
protected $gender;
/**
* @var \DateTime
*/
protected $dateOfBirth;
/**
* termsAndConditions
*
* @var bool
*/
protected $terms = false;
/**
* the datetime the user accepted the terms
*
* @var \DateTime
*/
protected $termsDateOfAcceptance;
/**
* @var string
*/
protected $txExtbaseType;
/**
* Created Password in Cleartext (if generated Password)
* will of course not be persistent and lives until runtime end
*
* @var string
*/
protected $passwordAutoGenerated;
/**
* @var string
*/
protected $state = '';
public function removeAllUsergroups()
{
$this->usergroup = new ObjectStorage();
}
/**
* @param string $txFemanagerChangerequest
* @return User
*/
public function setTxFemanagerChangerequest($txFemanagerChangerequest)
{
$this->txFemanagerChangerequest = $txFemanagerChangerequest;
return $this;
}
/**
* @return string
*/
public function getTxFemanagerChangerequest()
{
return $this->txFemanagerChangerequest;
}
/**
* @param \DateTime $crdate
* @return User
*/
public function setCrdate($crdate)
{
$this->crdate = $crdate;
return $this;
}
/**
* @return \DateTime
*/
public function getCrdate()
{
if ($this->crdate === null) {
// timestamp is zero
$this->crdate = new \DateTime('01.01.1970');
}
return $this->crdate;
}
/**
* @param \DateTime $tstamp
* @return User
*/
public function setTstamp($tstamp)
{
$this->tstamp = $tstamp;
return $this;
}
/**
* @return \DateTime
*/
public function getTstamp()
{
return $this->tstamp;
}
/**
* @param bool $disable
* @return User
*/
public function setDisable($disable)
{
$this->disable = $disable;
return $this;
}
/**
* @return bool
*/
public function getDisable()
{
return $this->disable;
}
/**
* @param \bool $txFemanagerConfirmedbyadmin
* @return User
*/
public function setTxFemanagerConfirmedbyadmin($txFemanagerConfirmedbyadmin)
{
$this->txFemanagerConfirmedbyadmin = $txFemanagerConfirmedbyadmin;
return $this;
}
/**
* @return \bool
*/
public function getTxFemanagerConfirmedbyadmin()
{
return $this->txFemanagerConfirmedbyadmin;
}
/**
* @param \bool $txFemanagerConfirmedbyuser
* @return User
*/
public function setTxFemanagerConfirmedbyuser($txFemanagerConfirmedbyuser)
{
$this->txFemanagerConfirmedbyuser = $txFemanagerConfirmedbyuser;
return $this;
}
/**
* @return \bool
*/
public function getTxFemanagerConfirmedbyuser()
{
return $this->txFemanagerConfirmedbyuser;
}
/**
* @param bool $ignoreDirty
* @return User
*/
public function setIgnoreDirty($ignoreDirty)
{
$this->ignoreDirty = $ignoreDirty;
return $this;
}
/**
* @return bool
*/
public function getIgnoreDirty()
{
return $this->ignoreDirty;
}
/**
* Returns the gender
*
* @return int $gender
*/
public function getGender()
{
return $this->gender;
}
/**
* Sets the gender
*
* @param int $gender
* @return User
*/
public function setGender($gender)
{
$this->gender = $gender;
return $this;
}
/**
* Returns the dateOfBirth
*
* @return \DateTime $dateOfBirth
*/
public function getDateOfBirth()
{
return $this->dateOfBirth;
}
/**
* Sets the dateOfBirth
*
* @param \DateTime $dateOfBirth
* @return User
*/
public function setDateOfBirth($dateOfBirth)
{
if ($dateOfBirth instanceof \DateTime) {
$dateOfBirth->setTime(0, 0, 0);
}
$this->dateOfBirth = $dateOfBirth;
return $this;
}
/**
* Returns, whether the user has accepted terms and conditions
*
* @return bool
*/
public function isTerms(): bool
{
return $this->terms;
}
/**
* Set whether the user has accepted terms and conditions
*
* @param bool $terms
* @return User
*/
public function setTerms(bool $terms)
{
$this->terms = $terms;
$this->setTermsDateOfAcceptance();
return $this;
}
/**
* @return \DateTime
*/
public function getTermsDateOfAcceptance()
{
return $this->termsDateOfAcceptance;
}
/**
* set terms date to now if it's not set yet
*
* @return User
*/
protected function setTermsDateOfAcceptance()
{
if ($this->termsDateOfAcceptance === null) {
$now = new \DateTime();
$this->termsDateOfAcceptance = $now;
}
return $this;
}
/**
* @return bool
*/
public function getIsOnline(): bool
{
return $this->isOnline();
}
/**
* Check if last FE login was within the last 2h
*
* @return bool
*/
public function isOnline(): bool
{
if ($this->getLastlogin() !== null && method_exists($this->getLastlogin(), 'getTimestamp')
&& $this->getLastlogin()->getTimestamp() > (time() - 2 * 60 * 60)
&& UserUtility::checkFrontendSessionToUser($this)
) {
return true;
}
return $this->isOnline;
}
/**
* @param string $txExtbaseType
* @return User
*/
public function setTxExtbaseType($txExtbaseType)
{
$this->txExtbaseType = $txExtbaseType;
return $this;
}
/**
* @return string
*/
public function getTxExtbaseType()
{
return $this->txExtbaseType;
}
public function getFirstImage()
{
$images = $this->getImage();
foreach ($images as $image) {
return $image;
}
return null;
}
/**
* @param string $passwordAutoGenerated
* @return User
*/
public function setPasswordAutoGenerated($passwordAutoGenerated)
{
$this->passwordAutoGenerated = $passwordAutoGenerated;
return $this;
}
/**
* @return string
*/
public function getPasswordAutoGenerated()
{
return $this->passwordAutoGenerated;
}
public function getState(): string
{
return $this->state;
}
public function setState(string $state): void
{
$this->state = $state;
}
public function getInactiveSince(): ?\DateTime
{
$timestampLastSession = GeneralUtility::makeInstance(ConnectionPool::class)
->getConnectionForTable('fe_sessions')
->select(['ses_tstamp'], 'fe_sessions', ['ses_userid' => $this->getUid()])
->fetchOne();
if ($timestampLastSession) {
$inactiveSince = new \DateTime();
$inactiveSince->setTimestamp($timestampLastSession);
return $inactiveSince;
}
return null;
}
/**
* Workaround to disable persistence in updateAction
*
* @param null $propertyName
* @return bool
*/
public function _isDirty($propertyName = null)
{
return $this->getIgnoreDirty() ? false : parent::_isDirty($propertyName);
}
}
@@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Domain\Model;
use TYPO3\CMS\Extbase\Domain\Model\FrontendUserGroup;
/**
* Class UserGroup
*/
class UserGroup extends FrontendUserGroup
{
}
@@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Domain\Repository;
use TYPO3\CMS\Extbase\Persistence\Repository;
/**
* Class LogRepository
*/
class LogRepository extends Repository
{
}
@@ -0,0 +1,132 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Domain\Repository;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\ForwardCompatibility\Result;
use In2code\Femanager\Utility\ObjectUtility;
use LogicException;
use PDO;
use TYPO3\CMS\Core\Service\FlexFormService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Class PluginRepository
*/
class PluginRepository
{
const TABLE_NAME = 'tt_content';
protected FlexFormService $flexFormService;
/**
* @var array<string, string>
*/
protected array $scaString = [
'new' => 'New->new;New->create;New->createStatus;New->confirmCreateRequest;',
'edit' => 'Edit->edit;Edit->update;Edit->delete;Edit->confirmUpdateRequest;User->imageDelete;',
'invitation' => 'Invitation->new;Invitation->create;Invitation->edit;'
. 'Invitation->update;Invitation->delete;Invitation->status;',
];
/**
* @param FlexFormService|null $flexFormService
*/
public function __construct(FlexFormService $flexFormService = null)
{
$this->flexFormService = $flexFormService ?? GeneralUtility::makeInstance(FlexFormService::class);
}
/**
* @param int $contentIdentifier
* @return string
* @throws \Exception
* @throws Exception
*/
public function getControllerNameByPluginSettings(int $contentIdentifier): string
{
$queryBuilder = ObjectUtility::getQueryBuilder(self::TABLE_NAME);
$flexFormQuery = $queryBuilder
->select('pi_flexform')
->from(self::TABLE_NAME)
->where('uid=' . $contentIdentifier)
->execute();
if (! $flexFormQuery instanceof Result) {
throw new \Exception(
'Something went wrong while getting FlexForm-value from Query.',
1638443805
);
}
$flexForm = (string)$flexFormQuery->fetchOne();
return $this->getViewFromFlexForm($flexForm);
}
/**
* @param string $view can be "new", "edit" or "invitation"
* @param int $pageIdentifier
* @return bool
* @throws \Exception
* @throws Exception
*/
public function isPluginWithViewOnGivenPage(string $view, int $pageIdentifier): bool
{
$queryBuilder = ObjectUtility::getQueryBuilder(self::TABLE_NAME);
$pluginConfigurationQuery = $queryBuilder
->select('pi_flexform')
->from(self::TABLE_NAME)
->where(
$queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($pageIdentifier, PDO::PARAM_INT)),
$queryBuilder->expr()->eq('CType', $queryBuilder->createNamedParameter('list')),
$queryBuilder->expr()->eq('list_type', $queryBuilder->createNamedParameter('femanager_pi1'))
)
->execute();
if (! $pluginConfigurationQuery instanceof Result) {
throw new \Exception(
'Something went wrong while getting PluginConfigurations from query.',
1638443806
);
}
$pluginConfigurations = $pluginConfigurationQuery->fetchAllAssociative();
foreach ($pluginConfigurations as $pluginConfiguration) {
if ($this->isViewInPluginConfiguration($view, (string)$pluginConfiguration['pi_flexform'])) {
return true;
}
}
return false;
}
/**
* @param string $flexForm
* @return string
*/
protected function getViewFromFlexForm(string $flexForm): string
{
$view = '';
$settings = $this->flexFormService->convertFlexFormContentToArray($flexForm);
if (!empty($settings['switchableControllerActions'])
&& in_array($settings['switchableControllerActions'], $this->scaString)) {
$view = array_search($settings['switchableControllerActions'], $this->scaString);
}
if (! is_string($view)) {
return '';
}
return $view;
}
/**
* @param string $view
* @param string $pluginConfiguration
* @return bool
* @throws LogicException
*/
protected function isViewInPluginConfiguration(string $view, string $pluginConfiguration): bool
{
$flexFormArray = $this->flexFormService->convertFlexFormContentToArray($pluginConfiguration);
if (array_key_exists($view, $this->scaString)) {
return $this->scaString[$view] === $flexFormArray['switchableControllerActions'];
}
throw new LogicException('Given view is not allowed', 1541506310);
}
}
@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Domain\Repository;
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
use TYPO3\CMS\Extbase\Persistence\Repository;
/**
* Class UserGroupRepository
*/
class UserGroupRepository extends Repository
{
/**
* Find all groups and respect exclude list
*
* @param string $removeList commaseparated list
* @return QueryResultInterface
*/
public function findAllForFrontendSelection($removeList)
{
$query = $this->createQuery();
if ($removeList) {
$query->matching($query->logicalNot($query->in('uid', explode(',', $removeList))));
}
$query->setOrderings(['title' => QueryInterface::ORDER_ASCENDING]);
return $query->execute();
}
}
@@ -0,0 +1,323 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Domain\Repository;
use In2code\Femanager\Domain\Model\User;
use In2code\Femanager\Utility\BackendUserUtility;
use In2code\Femanager\Utility\BackendUtility;
use TYPO3\CMS\Core\Database\QueryGenerator;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
use TYPO3\CMS\Extbase\Persistence\Repository;
/**
* Class UserRepository
*/
class UserRepository extends Repository
{
/**
* Overload Find by UID to also get hidden records
*
* @param int $uid fe_users UID
* @return User
*/
public function findByUid($uid)
{
$query = $this->createQuery();
$this->ignoreEnableFieldsAndStoragePage($query);
$query->getQuerySettings()->setRespectSysLanguage(false);
$and = [$query->equals('uid', $uid)];
/** @var User $user */
$user = $query->matching($query->logicalAnd($and))->execute()->getFirst();
return $user;
}
/**
* Find users by commaseparated usergroup list
*
* @param string $userGroupList commaseparated list of usergroup uids
* @param array $settings Flexform and TypoScript Settings
* @param array $filter Filter Array
* @return QueryResultInterface|array
*/
public function findByUsergroups($userGroupList, $settings, $filter)
{
$query = $this->createQuery();
// where
$and = [
$query->greaterThan('uid', 0)
];
if (!empty($userGroupList)) {
$selectedUsergroups = GeneralUtility::trimExplode(',', $userGroupList, true);
$logicalOr = [];
foreach ($selectedUsergroups as $group) {
$logicalOr[] = $query->contains('usergroup', $group);
}
$and[] = $query->logicalOr($logicalOr);
}
if (!empty($filter['searchword'])) {
$searchwords = GeneralUtility::trimExplode(' ', $filter['searchword'], true);
$fieldsToSearch = GeneralUtility::trimExplode(
',',
$settings['list']['filter']['searchword']['fieldsToSearch'],
true
);
foreach ($searchwords as $searchword) {
$logicalOr = [];
foreach ($fieldsToSearch as $searchfield) {
$logicalOr[] = $query->like($searchfield, '%' . $searchword . '%');
}
$and[] = $query->logicalOr($logicalOr);
}
}
$query->matching($query->logicalAnd($and));
// sorting
$sorting = QueryInterface::ORDER_ASCENDING;
if ($settings['list']['sorting'] === 'desc') {
$sorting = QueryInterface::ORDER_DESCENDING;
}
$field = preg_replace('/[^a-zA-Z0-9_-]/', '', $settings['list']['orderby']);
$query->setOrderings([$field => $sorting]);
// set limit
if ((int)$settings['list']['limit'] > 0) {
$query->setLimit((int)$settings['list']['limit']);
}
$users = $query->execute();
return $users;
}
/**
* Check if there is already an entry in the table
*
* @param $field
* @param $value
* @param User $user Existing User
* @return User|null
*/
public function checkUniqueDb($field, $value, User $user = null)
{
$query = $this->createQuery();
$this->ignoreEnableFieldsAndStoragePageAndStarttime($query);
$and = [$query->equals($field, $value)];
if ($user !== null && method_exists($user, 'getUid')) {
$and[] = $query->logicalNot($query->equals('uid', $user->getUid()));
}
$constraint = $query->logicalAnd($and);
$query->matching($constraint);
/** @var User $user */
$user = $query->execute()->getFirst();
return $user;
}
/**
* Check if there is already an entry in the table on current page
*
* @param $field
* @param $value
* @param User $user Existing User
* @return User|null
*/
public function checkUniquePage($field, $value, User $user = null)
{
$query = $this->createQuery();
$query->getQuerySettings()->setIgnoreEnableFields(true);
$and = [
$query->equals($field, $value),
$query->equals('deleted', 0)
];
if ($user !== null && method_exists($user, 'getUid')) {
$and[] = $query->logicalNot($query->equals('uid', (int)$user->getUid()));
}
$constraint = $query->logicalAnd($and);
$query->matching($constraint);
/** @var User $user */
$user = $query->execute()->getFirst();
return $user;
}
/**
* Find All for Backend Actions
*
* @param array $filter Filter Array
* @return QueryResultInterface|array
*/
public function findAllInBackend(array $filter)
{
$query = $this->createQuery();
$this->ignoreEnableFieldsAndStoragePage($query);
$and = [$query->greaterThan('uid', 0)];
$and = $this->filterByPage($and, $query);
$and = $this->filterBySearchword($filter, $query, $and);
$query->matching($query->logicalAnd($and));
$query->setOrderings(['username' => QueryInterface::ORDER_ASCENDING]);
$records = $query->execute();
return $records;
}
/**
* Find All for Backend Actions
*
* @param array $filter Filter Array
* @param bool $userConfirmation Show only fe_users which are confirmed by the user?
* @return QueryResultInterface|array
*/
public function findAllInBackendForConfirmation(array $filter, bool $userConfirmation = false)
{
$query = $this->createQuery();
$this->ignoreEnableFieldsAndStoragePage($query);
$and = [$query->equals('disable', true)];
$and = $this->filterByPage($and, $query);
$and = $this->filterBySearchword($filter, $query, $and);
$and = $this->filterByUserConfirmation($and, $query, $userConfirmation);
$query->matching($query->logicalAnd($and));
$query->setOrderings(['username' => QueryInterface::ORDER_ASCENDING]);
$records = $query->execute();
return $records;
}
/**
* Find all users from current page or from any subpage
* If no page id given or if on rootpage (pid 0):
* - Don't show any users for editors
* - Show all users for admins
*
* @param array $and
* @param QueryInterface $query
* @return array
*/
protected function filterByPage(array $and, QueryInterface $query): array
{
if (BackendUtility::getPageIdentifier() > 0) {
$and[] = $query->in('pid', $this->getTreeList(BackendUtility::getPageIdentifier()));
} else {
if (!BackendUserUtility::isAdminAuthentication()) {
$and[] = $query->equals('uid', 0);
}
}
return $and;
}
/**
* @param array $filter
* @param QueryInterface $query
* @param array $and
* @return array
*/
protected function filterBySearchword(array $filter, QueryInterface $query, array $and): array
{
if (!empty($filter['searchword'])) {
$searchwords = GeneralUtility::trimExplode(' ', $filter['searchword'], true);
foreach ($searchwords as $searchword) {
$or = [];
$or[] = $query->like('address', '%' . $searchword . '%');
$or[] = $query->like('city', '%' . $searchword . '%');
$or[] = $query->like('company', '%' . $searchword . '%');
$or[] = $query->like('country', '%' . $searchword . '%');
$or[] = $query->like('email', '%' . $searchword . '%');
$or[] = $query->like('fax', '%' . $searchword . '%');
$or[] = $query->like('first_name', '%' . $searchword . '%');
$or[] = $query->like('image', '%' . $searchword . '%');
$or[] = $query->like('last_name', '%' . $searchword . '%');
$or[] = $query->like('middle_name', '%' . $searchword . '%');
$or[] = $query->like('name', '%' . $searchword . '%');
$or[] = $query->like('telephone', '%' . $searchword . '%');
$or[] = $query->like('title', '%' . $searchword . '%');
$or[] = $query->like('usergroup.title', '%' . $searchword . '%');
$or[] = $query->like('username', '%' . $searchword . '%');
$or[] = $query->like('www', '%' . $searchword . '%');
$or[] = $query->like('zip', '%' . $searchword . '%');
$and[] = $query->logicalOr($or);
}
}
return $and;
}
/**
* @param array $and
* @param QueryInterface $query
* @param bool $userConfirmation
* @return array
*/
protected function filterByUserConfirmation(array $and, QueryInterface $query, bool $userConfirmation): array
{
$and[] = $query->equals('txFemanagerConfirmedbyuser', $userConfirmation);
return $and;
}
/**
* Get all children pids of a start pid
*
* @param int $pageIdentifier
* @return array
*/
protected function getTreeList($pageIdentifier)
{
$queryGenerator = $this->objectManager->get(QueryGenerator::class);
$treeList = $queryGenerator->getTreeList($pageIdentifier, 99, 0, '1');
return GeneralUtility::trimExplode(',', $treeList, true);
}
/**
* @param QueryInterface $query
*/
protected function ignoreEnableFieldsAndStoragePage(QueryInterface $query)
{
$query->getQuerySettings()->setRespectStoragePage(false);
$query->getQuerySettings()->setIgnoreEnableFields(true);
$query->getQuerySettings()->setEnableFieldsToBeIgnored(['disabled']);
}
/**
* @param QueryInterface $query
*/
protected function ignoreEnableFieldsAndStoragePageAndStarttime(QueryInterface $query)
{
$this->ignoreEnableFieldsAndStoragePage($query);
$query->getQuerySettings()->setEnableFieldsToBeIgnored(['disabled', 'starttime']);
}
/**
* Find All
*
* @param string $mail
* @return QueryResultInterface|array
*/
public function findFirstByEmail(string $mail)
{
$query = $this->createQuery();
$this->ignoreEnableFieldsAndStoragePage($query);
$and = [
$query->equals('txFemanagerConfirmedbyuser', false),
$query->equals('email', $mail)
];
$query->matching($query->logicalAnd($and));
$query->setOrderings(['uid' => QueryInterface::ORDER_DESCENDING]);
return $query->execute()->getFirst();
}
}
@@ -0,0 +1,91 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Domain\Service\AutoAdminConfirmation;
use In2code\Femanager\Domain\Model\User;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
/**
* Class AbstractConfirmation
*/
abstract class AbstractConfirmation implements ConfirmationInterface
{
/**
* @var array
*/
protected $config = [];
/**
* @var User
*/
protected $user;
/**
* @var array
*/
protected $settings = [];
/**
* @var ContentObjectRenderer
*/
protected $contentObject;
/**
* AbstractConfirmation constructor.
*
* @param array $config
* @param User $user
* @param array $settings
* @param ContentObjectRenderer $contentObject
*/
public function __construct(array $config, User $user, array $settings, ContentObjectRenderer $contentObject)
{
$this->config = $config;
$this->user = $user;
$this->settings = $settings;
$this->contentObject = $contentObject;
}
/**
* Skip manual confirmation from admin?
*
* @return bool
*/
public function isAutoConfirmed(): bool
{
return false;
}
/**
* @return array
*/
public function getConfig(): array
{
return $this->config;
}
/**
* @return User
*/
public function getUser(): User
{
return $this->user;
}
/**
* @return array
*/
public function getSettings(): array
{
return $this->settings;
}
/**
* @return ContentObjectRenderer
*/
public function getContentObject(): ContentObjectRenderer
{
return $this->contentObject;
}
}
@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Domain\Service\AutoAdminConfirmation;
/**
* Interface ConfirmationInterface
*/
interface ConfirmationInterface
{
/**
* @return bool
*/
public function isAutoConfirmed(): bool;
}
@@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Domain\Service\AutoAdminConfirmation;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Class EmailDomainConfirmation
*/
class EmailDomainConfirmation extends AbstractConfirmation
{
/**
* @return bool
*/
public function isAutoConfirmed(): bool
{
if (!$this->isException() && $this->isGivenDomainsPartOfEmail()) {
return true;
}
return false;
}
/**
* @return bool
*/
protected function isGivenDomainsPartOfEmail(): bool
{
$domains = GeneralUtility::trimExplode(',', $this->getConfig()['confirmByEmailDomains'], true);
foreach ($domains as $domain) {
if (stristr($this->getEmailDomain(), $domain)) {
return true;
}
}
return false;
}
protected function isException(): bool
{
$exceptionDomains =
GeneralUtility::trimExplode(',', $this->getConfig()['confirmByEmailDomainsExceptions'], true);
return in_array($this->getEmailDomain(), $exceptionDomains);
}
/**
* Get domain of an email address: "alex@in2code.de" => "in2code.de"
*
* @return string
*/
protected function getEmailDomain(): string
{
$email = $this->getUser()->getEmail();
$parts = explode('@', $email);
return $parts[1];
}
}
@@ -0,0 +1,101 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Domain\Service;
use In2code\Femanager\Domain\Model\User;
use In2code\Femanager\Domain\Service\AutoAdminConfirmation\ConfirmationInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
/**
* Class AutoAdminConfirmationService
*/
class AutoAdminConfirmationService
{
/**
* @var User
*/
protected $user;
/**
* @var array
*/
protected $settings = [];
/**
* @var ContentObjectRenderer
*/
protected $contentObject;
/**
* @var string
*/
protected $confirmInterface = ConfirmationInterface::class;
/**
* AutoAdminConfirmationService constructor.
*
* @param User $user
* @param array $settings
* @param ContentObjectRenderer $contentObject
*/
public function __construct(User $user, array $settings, ContentObjectRenderer $contentObject)
{
$this->user = $user;
$this->settings = $settings;
$this->contentObject = $contentObject;
}
/**
* Loop through all AutoAdminConfirmation classes
*
* @return bool
*/
public function isAutoAdminConfirmationFullfilled(): bool
{
$autoConfirmation = false;
foreach ($this->getConfirmationClasses() as $classConfiguration) {
/** @var ConfirmationInterface $confirmation */
$confirmation = GeneralUtility::makeInstance(
$classConfiguration['class'],
$classConfiguration['config'],
$this->user,
$this->settings,
$this->contentObject
);
if ($confirmation->isAutoConfirmed() === true) {
$autoConfirmation = true;
}
}
return $autoConfirmation;
}
/**
* @return array
* @throws \Exception
*/
protected function getConfirmationClasses(): array
{
$classes = [];
if (!empty($this->settings['autoAdminConfirmation'])) {
foreach ($this->settings['autoAdminConfirmation'] as $configuration) {
$className = $configuration['class'];
if (!class_exists($className)) {
throw new \UnexpectedValueException(
'Class ' . $className . ' does not exists - check if file was loaded with autoloader',
1516373867533
);
}
if (!is_subclass_of($className, $this->confirmInterface)) {
throw new \UnexpectedValueException(
'Class ' . $className . ' does not implement interface ' . $this->confirmInterface,
1516373878882
);
}
$classes[] = $configuration;
}
}
return $classes;
}
}
@@ -0,0 +1,123 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Domain\Service;
use In2code\Femanager\Utility\ConfigurationUtility;
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Resource\Security\FileNameValidator;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
/**
* Class FileService
*/
class FileService
{
/**
* @var string
*/
protected $fileName = '';
/**
* @var array
*/
protected $fileArray = [];
/**
* @var string
*/
protected $fallbackExtensions = 'jpg,jpeg,png,gif,bmp,tif,tiff';
/**
* FileService constructor.
*
* @param string $filename Filename like (upload.png)
* @param array $fileArray From PHP with tmp_name, etc...
*/
public function __construct(string $filename, array $fileArray)
{
$this->fileName = $filename;
$this->fileArray = $fileArray;
}
/**
* @return bool
* @todo add filesize check
*/
public function isEverythingValid(): bool
{
return $this->validFileExtension();
}
/**
* Check extension of given filename
*
* @return bool If Extension is allowed
*/
protected function validFileExtension(): bool
{
$extensionList = ConfigurationUtility::getConfiguration('misc.uploadFileExtension');
if (!empty($extensionList)) {
$extensionList = str_replace(' ', '', $extensionList);
} else {
$extensionList = $this->fallbackExtensions;
}
$fileInfo = pathinfo($this->fileName);
return !empty($fileInfo['extension']) &&
GeneralUtility::inList($extensionList, strtolower($fileInfo['extension'])) &&
GeneralUtility::makeInstance(FileNameValidator::class)->isValid($this->fileName) &&
GeneralUtility::validPathStr($this->fileName);
}
/**
* Create sys_file entry for given filename and return uid
*
* @param string $file absolute path and filename
* @return int
*/
public function indexFile(string $file): int
{
$fileIdentifier = 0;
if (file_exists($file)) {
$resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);
$file = $resourceFactory->getFileObjectFromCombinedIdentifier($this->getCombinedIdentifier($file));
$fileIdentifier = (int)$file->getProperty('uid');
}
return $fileIdentifier;
}
/**
* build combined identifier from absolute filename:
* "/var/www/fileadmin/folder/test.pdf" => "1:folder/test.pdf"
*
* @param string $file relative path and filename
* @return string
*/
protected function getCombinedIdentifier(string $file): string
{
// @Todo Make it a bit less ugly
$file = PathUtility::getRelativePathTo($file);
$identifier = $this->substituteFileadminFromPathAndName($file);
return '1:' . $identifier;
}
/**
* "fileadmin/downloads/test.pdf" => "/downloads/test.pdf"
*
* @param string $pathAndName
* @return string
*/
protected function substituteFileadminFromPathAndName(string $pathAndName): string
{
$substituteString = 'fileadmin/';
if (substr($pathAndName, 0, strlen($substituteString)) === $substituteString) {
$pathAndName = str_replace($substituteString, '', $pathAndName);
}
if (substr($pathAndName, 0, 1) !== '/') {
$pathAndName = '/' . $pathAndName;
}
return $pathAndName;
}
}
@@ -0,0 +1,258 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Domain\Service;
use In2code\Femanager\Domain\Model\User;
use In2code\Femanager\Finisher\AbstractFinisher;
use In2code\Femanager\Utility\StringUtility;
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
/**
* Class FinisherService
*/
class FinisherService
{
/**
* @var ContentObjectRenderer
*/
protected $contentObject;
/**
* Classname
*
* @var string
*/
protected $class = '';
/**
* Path that should be required
*
* @var string|null
*/
protected $requirePath;
/**
* Finisher Configuration
*
* @var array
*/
protected $configuration = [];
/**
* @var User
*/
protected $user;
/**
* @var array
*/
protected $settings;
/**
* Controller actionName - usually "createAction" or "confirmationAction"
*
* @var null
*/
protected $actionMethodName;
/**
* @var string
*/
protected $finisherInterface = 'In2code\Femanager\Finisher\FinisherInterface';
/**
* @var ObjectManagerInterface
*/
protected $objectManager;
public function injectObjectManager(ObjectManagerInterface $objectManager)
{
$this->objectManager = $objectManager;
}
/**
* @return string
*/
public function getClass()
{
return $this->class;
}
/**
* @param string $class
* @return FinisherService
*/
public function setClass($class)
{
$this->class = $class;
return $this;
}
/**
* @return string|null
*/
public function getRequirePath()
{
return $this->requirePath;
}
/**
* Set require path and do a require_once
*
* @param string|null $requirePath
* @return FinisherService
*/
public function setRequirePath($requirePath)
{
$this->requirePath = $requirePath;
if ($this->getRequirePath() && file_exists($this->getRequirePath())) {
require_once($this->getRequirePath());
}
return $this;
}
/**
* @return array
*/
public function getConfiguration()
{
return $this->configuration;
}
/**
* @param array $configuration
* @return FinisherService
*/
public function setConfiguration($configuration)
{
$this->configuration = $configuration;
return $this;
}
/**
* @return User
*/
public function getUser()
{
return $this->user;
}
/**
* @param User $user
* @return FinisherService
*/
public function setUser($user)
{
$this->user = $user;
return $this;
}
/**
* @return array
*/
public function getSettings()
{
return $this->settings;
}
/**
* @param array $settings
* @return FinisherService
*/
public function setSettings($settings)
{
$this->settings = $settings;
return $this;
}
public function getActionMethodName()
{
return $this->actionMethodName;
}
/**
* @param null $actionMethodName
* @return FinisherService
*/
public function setActionMethodName($actionMethodName)
{
$this->actionMethodName = $actionMethodName;
return $this;
}
/**
* Start implementation
*
* @throws \Exception
*/
public function start()
{
if (!class_exists($this->getClass())) {
throw new \UnexpectedValueException(
'Class ' . $this->getClass() . ' does not exists - check if file was loaded with autoloader',
1516373888508
);
}
if (is_subclass_of($this->getClass(), $this->finisherInterface)) {
/** @var AbstractFinisher $finisher */
$finisher = $this->objectManager->get(
$this->getClass(),
$this->getUser(),
$this->getConfiguration(),
$this->getSettings(),
$this->getActionMethodName(),
$this->contentObject
);
$finisher->initializeFinisher();
$this->callFinisherMethods($finisher);
} else {
throw new \UnexpectedValueException(
'Finisher does not implement ' . $this->finisherInterface,
1516373899775
);
}
}
/**
* Call methods in finisher class
*
* @param AbstractFinisher $finisher
*/
protected function callFinisherMethods(AbstractFinisher $finisher)
{
foreach (get_class_methods($finisher) as $method) {
if (!StringUtility::endsWith($method, 'Finisher') || strpos($method, 'initialize') === 0) {
continue;
}
$this->callInitializeFinisherMethod($finisher, $method);
$finisher->{$method}();
}
}
/**
* Call initializeFinisherMethods like "initializeSaveFinisher()"
*
* @param AbstractFinisher $finisher
* @param string $finisherMethod
*/
protected function callInitializeFinisherMethod(AbstractFinisher $finisher, $finisherMethod)
{
if (method_exists($finisher, 'initialize' . ucfirst($finisherMethod))) {
$finisher->{'initialize' . ucfirst($finisherMethod)}();
}
}
/**
* @param User $user
* @param array $settings
* @param ContentObjectRenderer $contentObject
*/
public function __construct(User $user, array $settings, ContentObjectRenderer $contentObject)
{
$this->setUser($user);
$this->setSettings($settings);
$this->contentObject = $contentObject;
}
}
@@ -0,0 +1,127 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Domain\Service;
use TYPO3\CMS\Core\Cache\CacheManager;
use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Crypto\Random;
use TYPO3\CMS\Core\SingletonInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
class RatelimiterService implements SingletonInterface
{
const CACHE_IDENTIFIER = 'femanager_ratelimiter';
const DEFAULT_CONFIG = ['timeframe' => 60, 'limit' => 3];
const LIMIT_IP = 'limit_ip_';
const SESSION_KEY = 'tx_femanager_ratelimiter';
/** @var FrontendInterface */
protected $cache;
/** @var int */
protected $limit;
/** @var int */
protected $timeframe;
public function __construct()
{
$this->cache = GeneralUtility::makeInstance(CacheManager::class)->getCache(self::CACHE_IDENTIFIER);
$setup = $this->getTSFE()->tmpl->setup;
$config = $setup['plugin.']['tx_femanager.']['settings.']['ratelimiter.'] ?? self::DEFAULT_CONFIG;
$this->timeframe = (int)$config['timeframe'];
$this->limit = (int)$config['limit'];
}
protected function getTSFE(): TypoScriptFrontendController
{
return $GLOBALS['TSFE'];
}
public function isLimited(): bool
{
if ($this->limit > 0) {
$userIp = GeneralUtility::getIndpEnv('REMOTE_ADDR');
$userIpAccess = $this->getToken(self::LIMIT_IP, $userIp);
return count($userIpAccess) >= $this->limit;
}
return false;
}
public function consumeSlot()
{
if ($this->limit > 0) {
$userIp = GeneralUtility::getIndpEnv('REMOTE_ADDR');
$this->consumeToken(self::LIMIT_IP, $userIp);
}
}
protected function getCookie()
{
$this->touchCookie();
return $this->getTSFE()->fe_user->getSessionData(self::SESSION_KEY);
}
public function touchCookie()
{
$feUser = $this->getTSFE()->fe_user;
$identifier = $feUser->getSessionData(self::SESSION_KEY);
if (null === $identifier) {
$unique = GeneralUtility::makeInstance(Random::class)->generateRandomHexString(16);
$feUser->setAndSaveSessionData(self::SESSION_KEY, $unique);
}
}
protected function consumeToken(string $tokenName, string $value): array
{
$cacheID = $this->getCacheID($tokenName, $value);
$token = $this->retrieveToken($cacheID);
$token[] = GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('date', 'timestamp');
$this->cache->set($cacheID, $token, [], $this->timeframe);
return $token;
}
protected function getCacheID(string $tokenName, string $value): string
{
return $tokenName . hash('sha1', $value);
}
protected function retrieveToken(string $cacheID): array
{
$token = [];
if ($this->cache->has($cacheID)) {
$token = $this->cache->get($cacheID);
$token = $this->filterExpiredToken($token);
}
return $token;
}
protected function filterExpiredToken(array $token): array
{
$slidingWindowStartTime = GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('date', 'timestamp') - $this->timeframe;
foreach ($token as $idx => $accessTime) {
if ($accessTime < $slidingWindowStartTime) {
unset($token[$idx]);
}
}
return $token;
}
protected function getToken(string $tokenName, string $value): array
{
$cacheID = $this->getCacheID($tokenName, $value);
return $this->retrieveToken($cacheID);
}
}
@@ -0,0 +1,294 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Domain\Service;
use In2code\Femanager\Event\AfterMailSendEvent;
use In2code\Femanager\Event\BeforeMailBodyRenderEvent;
use In2code\Femanager\Event\BeforeMailSendEvent;
use In2code\Femanager\Utility\ConfigurationUtility;
use In2code\Femanager\Utility\ObjectUtility;
use In2code\Femanager\Utility\TemplateUtility;
use Psr\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Mime\Part\DataPart;
use TYPO3\CMS\Core\Mail\Mailer;
use TYPO3\CMS\Core\Mail\MailMessage;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Class SendMailService
*/
class SendMailService
{
/**
* Content Object
*
* @var object|null
*/
public ?object $contentObject = null;
/**
* @var Mailer|null
*/
private ?Mailer $mailer = null;
/**
* @var EventDispatcherInterface|null
*/
private ?EventDispatcherInterface $dispatcher = null;
/**
* SendMailService constructor.
*/
public function __construct(Mailer $mailer, EventDispatcherInterface $dispatcher)
{
$this->contentObject = ObjectUtility::getContentObject();
$this->mailer = $mailer;
$this->dispatcher = $dispatcher;
}
/**
* @param array $variables
*/
protected function contentObjectStart(array $variables): void
{
if (
empty($variables['user'] ?? []) === false &&
method_exists(($variables['user'] ?? null), '_getProperties') === true
) {
$this->contentObject->start($variables['user']->_getProperties());
}
}
/**
* Generate and send Email
*
* @param string $template Template file in Templates/Email/
* @param array $receiver Combination of Email => Name
* @param array $sender Combination of Email => Name
* @param string $subject Mail subject
* @param array $variables Variables for assignMultiple
* @param array $typoScript Add TypoScript to overwrite values
* @return bool mail was sent?
*/
public function send(
string $template,
array $receiver,
array $sender,
string $subject,
array $variables = [],
array $typoScript = []
): bool {
if (false === $this->isMailEnabled($typoScript, $receiver)) {
return false;
}
$this->contentObjectStart($variables);
$email = GeneralUtility::makeInstance(MailMessage::class);
$variables = $this->embedImages($variables, $typoScript, $email);
$this->prepareMailObject($template, $receiver, $sender, $subject, $variables, $email);
$this->overwriteEmailReceiver($typoScript, $email);
$this->overwriteEmailSender($typoScript, $email);
$this->setSubject($typoScript, $email);
$this->setCc($typoScript, $email);
$this->setPriority($typoScript, $email);
$this->setAttachments($typoScript, $email);
$this->dispatcher->dispatch(new BeforeMailSendEvent($email, $variables, $this));
$email->send();
$this->dispatcher->dispatch(new AfterMailSendEvent($email, $variables, $this));
return $email->isSent();
}
/**
* Generate Email Body
*
* @param string $template Template file in Templates/Email/
* @param array $variables Variables for assignMultiple
* @return string
*/
protected function getMailBody(string $template, array $variables): string
{
$standAloneView = TemplateUtility::getDefaultStandAloneView();
$standAloneView->setTemplatePathAndFilename($this->getRelativeEmailPathAndFilename($template));
$standAloneView->assignMultiple($variables);
$this->dispatcher->dispatch(new BeforeMailBodyRenderEvent($standAloneView, $variables, $this));
return $standAloneView->render();
}
/**
* @param array $variables
* @param array $typoScript
* @param MailMessage $email
* @return array
*/
protected function embedImages(array $variables, array $typoScript, MailMessage $email): array
{
$images = $this->contentObject->cObjGetSingle(
$typoScript['embedImage'] ?? 'TEXT',
$typoScript['embedImage.'] ?? []
);
if (!$images) {
return $variables;
}
$images = GeneralUtility::trimExplode(',', $images, true);
$imageVariables = [];
foreach ($images as $path) {
$name = basename($path);
$imagePart = DataPart::fromPath($path);
$contentType = $imagePart->getMediaType() . '/' . $imagePart->getMediaSubtype();
$email->embedFromPath($path, $name, $contentType);
$imageVariables[] = 'cid:' . $name;
}
return array_merge($variables, ['embedImages' => $imageVariables]);
}
/**
* @param string $template
* @param array $receiver
* @param array $sender
* @param string $subject
* @param array $variables
* @param MailMessage $email
*/
protected function prepareMailObject(
string $template,
array $receiver,
array $sender,
string $subject,
array $variables,
MailMessage $email
): void {
$html = $this->getMailBody($template, $variables);
$email->setTo($receiver)
->setFrom($sender)
->setSubject($subject)
->html($html);
}
/**
* @param array $typoScript
* @param MailMessage $email
*/
protected function overwriteEmailReceiver(array $typoScript, MailMessage $email): void
{
$emailAddress = $this->contentObject->cObjGetSingle(
(string)ConfigurationUtility::getValue('receiver./email', $typoScript),
(array)ConfigurationUtility::getValue('receiver./email.', $typoScript)
);
$name = $this->contentObject->cObjGetSingle(
(string)ConfigurationUtility::getValue('receiver./name', $typoScript),
(array)ConfigurationUtility::getValue('receiver./name.', $typoScript)
);
if ($emailAddress && $name) {
$email->setTo([$emailAddress => $name]);
}
}
/**
* @param array $typoScript
* @param MailMessage $email
*/
protected function overwriteEmailSender(array $typoScript, MailMessage $email): void
{
$emailAddress = $this->contentObject->cObjGetSingle(
(string)ConfigurationUtility::getValue('sender./email', $typoScript),
(array)ConfigurationUtility::getValue('sender./email.', $typoScript)
);
$name = $this->contentObject->cObjGetSingle(
(string)ConfigurationUtility::getValue('sender./name', $typoScript),
(array)ConfigurationUtility::getValue('sender./name.', $typoScript)
);
if ($emailAddress && $name) {
$email->setFrom([$emailAddress => $name]);
}
}
/**
* @param array $typoScript
* @param MailMessage $email
*/
protected function setSubject(array $typoScript, MailMessage $email): void
{
if ($this->contentObject->cObjGetSingle((string)$typoScript['subject'], (array)$typoScript['subject.'])) {
$email->setSubject($this->contentObject->cObjGetSingle((string)$typoScript['subject'], (array)$typoScript['subject.']));
}
}
/**
* @param array $typoScript
* @param MailMessage $email
*/
protected function setCc(array $typoScript, MailMessage $email): void
{
if ($this->contentObject->cObjGetSingle((string)$typoScript['cc'], (array)$typoScript['cc.'])) {
$email->setCc($this->contentObject->cObjGetSingle((string)$typoScript['cc'], (array)$typoScript['cc.']));
}
}
/**
* @param array $typoScript
* @param MailMessage $email
*/
protected function setPriority(array $typoScript, MailMessage $email): void
{
$priority = (int)$this->contentObject->cObjGetSingle(
(string)ConfigurationUtility::getValue('priority', $typoScript),
(array)ConfigurationUtility::getValue('priority.', $typoScript)
);
if ($priority) {
$email->priority($priority);
}
}
/**
* @param array $typoScript
* @param MailMessage $email
*/
protected function setAttachments(array $typoScript, MailMessage $email): void
{
if ($this->contentObject->cObjGetSingle($typoScript['attachments'] ?? '', $typoScript['attachments.'] ?? [])) {
$files = GeneralUtility::trimExplode(
',',
$this->contentObject->cObjGetSingle(
$typoScript['attachments'] ?? '',
$typoScript['attachments.'] ?? []
),
true
);
foreach ($files as $file) {
$email->attachFromPath($file);
}
}
}
/**
* Get path and filename for mail template
*
* @param string $fileName
* @return string
*/
protected function getRelativeEmailPathAndFilename(string $fileName): string
{
return TemplateUtility::getTemplatePath('Email/' . ucfirst($fileName) . '.html');
}
/**
* @param array $typoScript
* @param array $receiver
* @return bool
*/
protected function isMailEnabled(array $typoScript, array $receiver): bool
{
$cObjectName = (string)ConfigurationUtility::getValue('_enable', $typoScript);
$cObjectConf = (array)ConfigurationUtility::getValue('_enable.', $typoScript);
return $this->contentObject->cObjGetSingle($cObjectName, $cObjectConf) && count($receiver) > 0;
}
}
@@ -0,0 +1,135 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Domain\Service;
use In2code\Femanager\Domain\Model\User;
use TYPO3\CMS\Core\Log\LogLevel;
use TYPO3\CMS\Core\Log\LogManager;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
/**
* Class SendParametersService
*/
class SendParametersService
{
/**
* @var ConfigurationManagerInterface
*/
protected $configurationManager;
/**
* @var ContentObjectRenderer
*/
protected $contentObject;
/**
* @var array
*/
protected $configuration = [];
/**
* @var array
*/
protected $properties = [];
/**
* Constructor
*/
public function __construct($configuration)
{
$this->configuration = $configuration;
}
/**
* @param ConfigurationManagerInterface $configurationManager
*/
public function injectConfigurationManagerInterface(ConfigurationManagerInterface $configurationManager)
{
$this->configurationManager = $configurationManager;
}
/**
* SendPost - Send values via curl to target
*
* @param User $user User properties
*/
public function send(User $user)
{
$this->initialize($user);
$this->contentObject->start($this->properties);
if ($this->isTurnedOn()) {
$curlObject = curl_init();
curl_setopt($curlObject, CURLOPT_URL, $this->getUri());
curl_setopt($curlObject, CURLOPT_POST, 1);
curl_setopt($curlObject, CURLOPT_POSTFIELDS, $this->getData());
curl_setopt($curlObject, CURLOPT_RETURNTRANSFER, true);
if ($GLOBALS['FE']['debug'] === 1) {
curl_setopt($curlObject, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($curlObject, CURLOPT_SSL_VERIFYPEER, false);
}
curl_exec($curlObject);
curl_close($curlObject);
$this->log();
}
}
/**
* Get URI for curl request
*
* @return string
*/
protected function getUri()
{
return $this->configuration['targetUrl'];
}
/**
* Get data array
*
* @return string
*/
protected function getData()
{
return $this->contentObject->cObjGetSingle((string)$this->configuration['data'], (array)$this->configuration['data.']);
}
/**
* Write to devlog
*
* @return bool
*/
protected function log()
{
if (!empty($this->configuration['debug'])) {
GeneralUtility::makeInstance(LogManager::class)->getLogger(__CLASS__)->log(LogLevel::INFO, 'femanager sendpost values', [
'url' => $this->getUri(),
'data' => $this->getData(),
'properties' => $this->properties
]);
}
}
/**
* @return bool
*/
protected function isTurnedOn()
{
return $this->contentObject->cObjGetSingle((string)$this->configuration['_enable'], (array)$this->configuration['_enable.'])
=== '1';
}
/**
* Initialize
*
* @param User $user
*/
protected function initialize(User $user)
{
$this->properties = $user->_getCleanProperties();
$this->contentObject = $this->configurationManager->getContentObject();
}
}
@@ -0,0 +1,109 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Domain\Service;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Class StoreInDatabaseService
*/
class StoreInDatabaseService
{
/**
* Database Table to store
*
* @var string
*/
protected $table = '';
/**
* Array with fieldname=>value
*
* @var array
*/
protected $properties = [];
/**
* @var \TYPO3\CMS\CORE\Database\ConnectionPool
*/
protected $databaseConnection;
/**
* Executes the storage
*
* @return int uid of inserted record
*/
public function execute()
{
$insertTable = $this->databaseConnection->getConnectionForTable($this->getTable());
$insertTable->insert(
$this->getTable(),
$this->getProperties()
);
return (int)$insertTable->lastInsertId($this->getTable());
}
/**
* Set TableName
*
* @param string $table
*/
public function setTable($table)
{
$table = preg_replace('/[^a-zA-Z0-9_-]/', '', $table);
$this->table = $table;
}
/**
* Get TableName
*
* @return string
*/
public function getTable()
{
return $this->table;
}
/**
* Read properties
*
* @return array
*/
public function getProperties()
{
return $this->properties;
}
/**
* Add property/value pair to array
*
* @param $propertyName
* @param $value
*/
public function addProperty($propertyName, $value)
{
$propertyName = preg_replace('/[^a-zA-Z0-9_-]/', '', $propertyName);
$this->properties[$propertyName] = $value;
}
/**
* Remove property/value pair form array by its key
*
* @param $propertyName
*/
public function removeProperty($propertyName)
{
unset($this->properties[$propertyName]);
}
/**
* Initialize
*/
public function __construct()
{
$this->databaseConnection = GeneralUtility::makeInstance(ConnectionPool::class);
}
}
@@ -0,0 +1,134 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Domain\Service;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
/**
* Class ValidationSettingsService
*/
class ValidationSettingsService
{
/**
* Needed for validation settings. Should be "new", "edit" or "invitation"
*
* @var string
*/
protected $controllerName = '';
/**
* Needed for validation settings. Should be "validation" or "validationEdit"
*
* @var string
*/
protected $validationName = '';
/**
* Validation names with simple configuration
*
* @var array
*/
protected $simpleValidations = [
'date',
'email',
'intOnly',
'lettersOnly',
'unicodeLettersOnly',
'required',
'uniqueInDb',
'uniqueInPage'
];
/**
* @param string $controllerName
* @param string $validationName
*/
public function __construct(string $controllerName, string $validationName)
{
$this->controllerName = $controllerName;
$this->validationName = $validationName;
}
/**
* Get validation string like
* required, email, min(10), max(10), intOnly,
* lettersOnly, unicodeLettersOnly, uniqueInPage, uniqueInDb, date,
* mustInclude(number|letter|special), inList(1|2|3)
*
* @param string $fieldName Fieldname
* @return string
*/
public function getValidationStringForField(string $fieldName): string
{
$string = '';
$validationSettings = $this->getSettings()[$this->controllerName][$this->validationName][$fieldName] ?? [];
if (is_array($validationSettings)) {
foreach ($validationSettings as $validation => $configuration) {
if ($string !== '') {
$string .= ',';
}
$string .= $this->getSingleValidationString($validation, $configuration);
}
}
return $string;
}
/**
* @param string $type
* @return bool
*/
public function isValidationEnabled(string $type = 'client'): bool
{
$validationSetting = $this->getSettings()[$this->controllerName]['validation']['_enable'][$type] ?? '0';
return $validationSetting === '1';
}
/**
* @param string $validation
* @param string $configuration
* @return string
*/
protected function getSingleValidationString($validation, $configuration)
{
$string = '';
if ($this->isSimpleValidation($validation) && $configuration === '1') {
$string = $validation;
}
if (!$this->isSimpleValidation($validation)) {
$string = $validation;
$string .= '(' . str_replace(',', '|', $configuration) . ')';
}
return $string;
}
/**
* Check if validation is simple or extended
*
* @param string $validation
* @return bool
*/
protected function isSimpleValidation($validation): bool
{
if (in_array($validation, $this->simpleValidations)) {
return true;
}
return false;
}
/**
* @return array
*/
protected function getSettings(): array
{
$configurationManager = GeneralUtility::makeInstance(ConfigurationManagerInterface::class);
return (array)$configurationManager->getConfiguration(
ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS,
'femanager',
'femanager_pi1'
);
}
}
@@ -0,0 +1,490 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Domain\Validator;
use In2code\Femanager\Domain\Model\User;
use In2code\Femanager\Domain\Repository\PluginRepository;
use In2code\Femanager\Domain\Repository\UserRepository;
use In2code\Femanager\Event\UniqueUserEvent;
use In2code\Femanager\Utility\FrontendUtility;
use Psr\EventDispatcher\EventDispatcherInterface;
use TYPO3\CMS\Core\EventDispatcher\EventDispatcher;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Extbase\Validation\Validator\AbstractValidator as AbstractValidatorExtbase;
/**
* Class GeneralValidator
*/
abstract class AbstractValidator extends AbstractValidatorExtbase
{
protected ?UserRepository $userRepository = null;
public ?ConfigurationManagerInterface $configurationManager = null;
protected ?EventDispatcherInterface $eventDispatcher = null;
/**
* Former known as piVars
*
* @var array
*/
public $pluginVariables;
/**
* Validationsettings
*/
public $validationSettings = [];
/**
* Is Valid
*/
protected $isValid = true;
/**
* @param UserRepository $userRepository
*/
public function injectUserRepository(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
}
/**
* @param ConfigurationManagerInterface $configurationManager
*/
public function injectConfigurationManagerInterface(ConfigurationManagerInterface $configurationManager)
{
$this->configurationManager = $configurationManager;
}
/**
* @param EventDispatcherInterface $eventDispatcher
*/
public function injectEventDispatcherInterface(EventDispatcherInterface $eventDispatcher)
{
$this->eventDispatcher = $eventDispatcher;
}
public function initializeObject(): void
{
if ($this->configurationManager === null) {
$this->configurationManager = GeneralUtility::makeInstance(ConfigurationManagerInterface::class);
}
if ($this->eventDispatcher === null) {
$this->eventDispatcher = GeneralUtility::makeInstance(EventDispatcher::class);
}
if ($this->userRepository === null) {
$this->userRepository = GeneralUtility::makeInstance(UserRepository::class);
}
}
/**
* Validation for required
*
* @param mixed $value
* @return \bool
*/
protected function validateRequired($value)
{
if (!is_object($value)) {
if (is_numeric($value)) {
return true;
}
return !empty($value);
}
if ((is_array($value) || $value instanceof \Countable) && count($value) > 0) {
return true;
}
if ($value instanceof \DateTime) {
return true;
}
return false;
}
/**
* Validation for email
*
* @param string $value
* @return \bool
*/
protected function validateEmail($value)
{
return GeneralUtility::validEmail($value);
}
/**
* Validation for Minimum of characters
*
* @param string $value
* @param string $validationSetting
* @return \bool
*/
protected function validateMin($value, $validationSetting)
{
if (mb_strlen($value) < $validationSetting) {
return false;
}
return true;
}
/**
* Validation for Maximum of characters
*
* @param string $value
* @param string $validationSetting
* @return \bool
*/
protected function validateMax($value, $validationSetting)
{
if (mb_strlen($value) > $validationSetting) {
return false;
}
return true;
}
/**
* Validation for Numbers only
*
* @param string $value
* @return \bool
*/
protected function validateInt($value)
{
return is_numeric($value);
}
/**
* Validation for Letters (a-zA-Z), hyphen and underscore
*
* @param string $value
* @return \bool
*/
protected function validateLetters($value)
{
if (preg_replace('/[^a-zA-Z_-]/', '', $value) === $value) {
return true;
}
return false;
}
/**
* Validation for all Unicode letters, hyphen and underscore
*
* @param string $value
* @return \bool
*/
protected function validateUnicodeLetters($value)
{
return (bool)preg_match('/^[\pL_-]+$/u', $value);
}
/**
* Validation for Unique in sysfolder
*
* @param string $value
* @param string $field
* @param User $user Existing User
* @return \bool
*/
protected function validateUniquePage($value, $field, User $user = null)
{
$foundUser = $this->userRepository->checkUniquePage($field, $value, $user);
return !is_object($foundUser);
}
/**
* Validation for Unique in the db
*
* @param string $value
* @param string $field Fieldname like "username" or "email"
* @param User $user Existing User
* @return \bool
*/
protected function validateUniqueDb($value, $field, User $user = null)
{
$foundUser = $this->userRepository->checkUniqueDb($field, $value, $user);
$uniqueDb = !is_object($foundUser);
// return false, if is not unique
return $this->eventDispatcher->dispatch(new UniqueUserEvent($value, $field, $user, $uniqueDb))->isUnique();
}
/**
* Validation for "Must include some characters"
*
* @param string $value
* @param string $validationSettingList
* @return \bool
*/
protected function validateMustInclude($value, $validationSettingList)
{
$isValid = true;
$validationSettings = GeneralUtility::trimExplode(',', $validationSettingList, true);
foreach ($validationSettings as $validationSetting) {
switch ($validationSetting) {
case 'number':
if (!$this->stringContainsNumber($value)) {
$isValid = false;
}
break;
case 'letter':
if (!$this->stringContainsLetter($value)) {
$isValid = false;
}
break;
case 'uppercase':
if (!$this->stringContainsUppercase($value)) {
$isValid = false;
}
break;
case 'special':
if (!$this->stringContainsSpecialCharacter($value)) {
$isValid = false;
}
break;
case 'space':
if (!$this->stringContainsSpaceCharacter($value)) {
$isValid = false;
}
break;
default:
}
}
return $isValid;
}
/**
* Validation for "Must not include some characters"
*
* @param string $value
* @param string $validationSettingList
* @return \bool
*/
protected function validateMustNotInclude($value, $validationSettingList)
{
$isValid = true;
$validationSettings = GeneralUtility::trimExplode(',', $validationSettingList, true);
foreach ($validationSettings as $validationSetting) {
switch ($validationSetting) {
case 'number':
if ($this->stringContainsNumber($value)) {
$isValid = false;
}
break;
case 'letter':
if ($this->stringContainsLetter($value)) {
$isValid = false;
}
break;
case 'uppercase':
if ($this->stringContainsUppercase($value)) {
$isValid = false;
}
break;
case 'special':
if ($this->stringContainsSpecialCharacter($value)) {
$isValid = false;
}
break;
case 'space':
if ($this->stringContainsSpaceCharacter($value)) {
$isValid = false;
}
break;
default:
}
}
return $isValid;
}
/**
* String contains number?
*
* @param string $value
* @return bool
*/
protected function stringContainsNumber($value)
{
return strlen(preg_replace('/[^0-9]/', '', $value)) > 0;
}
/**
* String contains letter?
*
* @param string $value
* @return bool
*/
protected function stringContainsLetter($value)
{
return strlen(preg_replace('/[^a-zA-Z_-]/', '', $value)) > 0;
}
/**
* String contains uppercase letter?
*
* @param string $value
* @return bool
*/
protected function stringContainsUppercase($value)
{
return strlen(preg_replace('/[^A-Z]/', '', $value)) > 0;
}
/**
* String contains special character?
*
* @param string $value
* @return bool
*/
protected function stringContainsSpecialCharacter($value)
{
return strlen(preg_replace('/[^a-zA-Z0-9]/', '', $value)) !== strlen($value);
}
/**
* String contains space character?
*
* @param string $value
* @return bool
*/
protected function stringContainsSpaceCharacter($value)
{
return strpos($value, ' ') !== false;
}
/**
* Validation for checking if values are in a given list
*
* @param string $value
* @param string $validationSettingList
* @return \bool
*/
protected function validateInList($value, $validationSettingList)
{
$valueList = GeneralUtility::trimExplode(',', $value, true);
$validationSettings = GeneralUtility::trimExplode(',', $validationSettingList, true);
$diff = array_diff($valueList, $validationSettings);
return empty($diff);
}
/**
* Validation for comparing two fields
*
* @param string $value
* @param string $value2
* @return \bool
*/
protected function validateSameAs($value, $value2)
{
if ($value === $value2) {
return true;
}
return false;
}
/**
* Validation for checking if values is in date format
*
* @param string $value
* @param string $validationSetting
* @return \bool
*/
protected function validateDate($value, $validationSetting)
{
$dateParts = [];
switch ($validationSetting) {
case 'd.m.Y':
if (preg_match('/^([0-9]{2})\.([0-9]{2})\.([0-9]{4})$/', $value, $dateParts)) {
if (checkdate((int)$dateParts[2], (int)$dateParts[1], (int)$dateParts[3])) {
return true;
}
}
break;
case 'm/d/Y':
if (preg_match('/^([0-9]{2})\/([0-9]{2})\/([0-9]{4})$/', $value, $dateParts)) {
if (checkdate((int)$dateParts[1], (int)$dateParts[2], (int)$dateParts[3])) {
return true;
}
}
break;
default:
}
return false;
}
protected function init()
{
$this->initializeObject();
$this->setPluginVariables();
$this->setValidationSettings();
}
protected function setPluginVariables()
{
$this->pluginVariables = GeneralUtility::_GP('tx_femanager_pi1');
}
protected function setValidationSettings()
{
$config = $this->configurationManager->getConfiguration(
ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS,
'Femanager',
'Pi1'
);
$this->validationSettings = $config[$this->getControllerName()][$this->getValidationName()];
}
/**
* @return string
*/
protected function getValidationName(): string
{
$validationName = 'validation';
if ($this->getControllerName() === 'invitation' && $this->getActionName() === 'edit') {
$validationName = 'validationEdit';
}
return $validationName;
}
/**
* @return string
*/
protected function getActionName(): string
{
return $this->pluginVariables['__referrer']['@action'];
}
/**
* Get controller name in lowercase
*
* @return string
*/
protected function getControllerName(): string
{
$controllerName = 'new';
if ($this->pluginVariables['__referrer']['@controller'] === 'Edit') {
$controllerName = 'edit';
} elseif ($this->pluginVariables['__referrer']['@controller'] === 'Invitation') {
$controllerName = 'invitation';
}
$this->checkAllowedControllerName($controllerName);
return $controllerName;
}
/**
* @param string $controllerName
*/
protected function checkAllowedControllerName(string $controllerName)
{
$pluginRepository = GeneralUtility::makeInstance(PluginRepository::class);
$pageIdentifier = FrontendUtility::getCurrentPid();
if ($pluginRepository->isPluginWithViewOnGivenPage($controllerName, $pageIdentifier) === false) {
throw new \LogicException('ControllerName is not allowed', 1541506524);
}
}
}
@@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Domain\Validator;
use SJBR\SrFreecap\Domain\Repository\WordRepository;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Class CaptchaValidator
*/
class CaptchaValidator extends AbstractValidator
{
/**
* Validation of given Params
*
* @param $user
* @return bool
*/
public function isValid($user)
{
$this->init();
if (!$this->captchaEnabled() || $this->validCaptcha()) {
return true;
}
$this->addError('validationErrorCaptcha', 0, ['fieldName' => 'captcha']);
return false;
}
/**
* Check if captcha is valid
*
* @return bool
*/
protected function validCaptcha()
{
$isValid = false;
$wordRepository = GeneralUtility::makeInstance(WordRepository::class);
$wordObject = $wordRepository->getWord();
$wordHash = $wordObject->getWordHash();
if (!empty($wordHash) && !empty($this->pluginVariables['captcha'])) {
if ($wordObject->getHashFunction() == 'md5') {
if (md5(strtolower(mb_convert_encoding((string)$this->pluginVariables['captcha'], 'ISO-8859-1'))) == $wordHash) {
$wordRepository->cleanUpWord();
$isValid = true;
}
}
}
return $isValid;
}
/**
* Check if captcha is enabled (TypoScript, and sr_freecap loaded)
*
* @return bool
*/
protected function captchaEnabled()
{
return ExtensionManagementUtility::isLoaded('sr_freecap')
&& !empty($this->validationSettings['captcha']['captcha']);
}
}
@@ -0,0 +1,482 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Domain\Validator;
use In2code\Femanager\Domain\Model\User;
use In2code\Femanager\Domain\Repository\PluginRepository;
use In2code\Femanager\Domain\Service\ValidationSettingsService;
use In2code\Femanager\Utility\LocalizationUtility;
use In2code\Femanager\Utility\StringUtility;
use SJBR\SrFreecap\Domain\Repository\WordRepository;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Class ClientsideValidator
*/
class ClientsideValidator extends AbstractValidator
{
/**
* Validation settings string
* possible validations for each field are:
* required, email, min(12), max(13), intOnly, lettersOnly,unicodeLettersOnly
* uniqueInPage, uniqueInDb, date, mustInclude(number,letter,special),
* inList(1,2,3)
*
* @var string
*/
protected $validationSettingsString;
/**
* Field Value
*
* @var string
*/
protected $value;
/**
* Field Name
*
* @var string
*/
protected $fieldName;
/**
* User
*
* @var User
*/
protected $user;
/**
* Error message container
*
* @var array
*/
protected $messages = [];
/**
* Additional Values (for comparing a value with another)
*
* @var string
*/
protected $additionalValue;
/**
* @var int
*/
protected $plugin = 0;
/**
* @var string
*/
protected $actionName = '';
/**
* Validate Field
*
* @return bool
*/
public function validateField()
{
if ($this->isValidationSettingsDifferentToGlobalSettings()) {
$this->addMessage('validationErrorGeneral');
return false;
}
foreach ($this->getValidationSettings() as $validationSetting) {
switch ($validationSetting) {
case 'required':
if (!$this->validateRequired($this->getValue())) {
$this->addMessage('validationErrorRequired');
$this->isValid = false;
}
break;
case 'email':
if ($this->getValue() && !$this->validateEmail($this->getValue())) {
$this->addMessage('validationErrorEmail');
$this->isValid = false;
}
break;
case stristr($validationSetting, 'min('):
if ($this->getValue() &&
!$this->validateMin($this->getValue(), StringUtility::getValuesInBrackets($validationSetting))
) {
$this->addMessage('validationErrorMin');
$this->isValid = false;
}
break;
case stristr($validationSetting, 'max('):
if ($this->getValue() &&
!$this->validateMax($this->getValue(), StringUtility::getValuesInBrackets($validationSetting))
) {
$this->addMessage('validationErrorMax');
$this->isValid = false;
}
break;
case 'intOnly':
if ($this->getValue() && !$this->validateInt($this->getValue())) {
$this->addMessage('validationErrorInt');
$this->isValid = false;
}
break;
case 'lettersOnly':
if ($this->getValue() && !$this->validateLetters($this->getValue())) {
$this->addMessage('validationErrorLetters');
$this->isValid = false;
}
break;
case 'unicodeLettersOnly':
if ($this->getValue() && !$this->validateUnicodeLetters($this->getValue())) {
$this->addMessage('validationErrorLetters');
$this->isValid = false;
}
break;
case 'uniqueInPage':
if ($this->getValue() &&
!$this->validateUniquePage($this->getValue(), $this->getFieldName(), $this->getUser())
) {
$this->addMessage('validationErrorUniquePage');
$this->isValid = false;
}
break;
case 'uniqueInDb':
if ($this->getValue() &&
!$this->validateUniqueDb($this->getValue(), $this->getFieldName(), $this->getUser())
) {
$this->addMessage('validationErrorUniqueDb');
$this->isValid = false;
}
break;
case stristr($validationSetting, 'mustInclude('):
if ($this->getValue() &&
!$this->validateMustInclude(
$this->getValue(),
StringUtility::getValuesInBrackets($validationSetting)
)
) {
$this->addMessage('validationErrorMustInclude');
$this->isValid = false;
}
break;
case stristr($validationSetting, 'mustNotInclude('):
if ($this->getValue() &&
!$this->validateMustNotInclude(
$this->getValue(),
StringUtility::getValuesInBrackets($validationSetting)
)
) {
$this->addMessage('validationErrorMustNotInclude');
$this->isValid = false;
}
break;
case stristr($validationSetting, 'inList('):
if (!$this->validateInList(
$this->getValue(),
StringUtility::getValuesInBrackets($validationSetting)
)) {
$this->addMessage('validationErrorInList');
$this->isValid = false;
}
break;
case stristr($validationSetting, 'sameAs('):
if (!$this->validateSameAs($this->getValue(), $this->getAdditionalValue())) {
$this->addMessage('validationErrorSameAs');
$this->isValid = false;
}
break;
case 'date':
if ($this->getValue() &&
!$this->validateDate(
$this->getValue(),
LocalizationUtility::translate('tx_femanager_domain_model_user.dateFormat')
)
) {
$this->addMessage('validationErrorDate');
$this->isValid = false;
}
break;
case stristr($validationSetting, 'captcha('):
if (ExtensionManagementUtility::isLoaded('sr_freecap')) {
$wordRepository = GeneralUtility::makeInstance(
WordRepository::class
);
$wordObject = $wordRepository->getWord();
$wordHash = $wordObject->getWordHash();
$userVal = md5(strtolower(mb_convert_encoding($this->getValue(), 'ISO-8859-1')));
if ($wordHash !== $userVal) {
$this->addMessage('validationErrorCaptcha', 'captcha');
$this->isValid = false;
}
}
break;
default:
// e.g. search for method validateCustom()
$mainSetting = StringUtility::getValuesBeforeBrackets($validationSetting);
if (method_exists($this, 'validate' . ucfirst($mainSetting))) {
if (!$this->{'validate' . ucfirst($mainSetting)}(
$this->getValue(),
StringUtility::getValuesInBrackets($validationSetting)
)) {
$this->addMessage('validationError' . ucfirst($mainSetting));
$this->isValid = false;
}
}
}
}
return $this->isValid;
}
/**
* This function checks the given validation string from user input against settings in TypoScript. If both strings
* do not match, it could be possible that there is a manipulation. In this case, we stop validation and return a
* global error message
*
* @return bool
*/
protected function isValidationSettingsDifferentToGlobalSettings(): bool
{
return $this->getValidationSettingsString() !== $this->getValidationSettingsFromTypoScript();
}
/**
* Set validation
*
* @param string $validationSettingsString
* @return ClientsideValidator
*/
public function setValidationSettingsString($validationSettingsString)
{
$this->validationSettingsString = $validationSettingsString;
return $this;
}
/**
* @return string
*/
public function getValidationSettingsString()
{
return $this->validationSettingsString;
}
/**
* @return string
*/
public function getValidationSettingsFromTypoScript(): string
{
$controllerName = $this->getControllerName();
$validationService = GeneralUtility::makeInstance(
ValidationSettingsService::class,
$controllerName,
$this->getValidationName()
);
return $validationService->getValidationStringForField($this->fieldName);
}
/**
* @return array
*/
protected function getValidationSettings(): array
{
$validationSettings = GeneralUtility::trimExplode(',', $this->validationSettingsString, true);
$validationSettings = str_replace('|', ',', $validationSettings);
return $validationSettings;
}
/**
* @param string $value
* @return ClientsideValidator
*/
public function setValue($value)
{
$this->value = $value;
return $this;
}
/**
* @return string
*/
public function getValue()
{
return $this->value;
}
/**
* Add a message to the errormessage array
*
* @param string $message
*/
public function addMessage($message)
{
$this->messages = array_merge($this->messages, [$message]);
}
/**
* @param array $messages
* @return ClientsideValidator
*/
public function setMessages($messages)
{
$this->messages = $messages;
return $this;
}
/**
* @return array
*/
public function getMessages()
{
return $this->messages;
}
/**
* @param string $fieldName
* @return ClientsideValidator
*/
public function setFieldName($fieldName)
{
$this->fieldName = $fieldName;
return $this;
}
/**
* @return string
*/
public function getFieldName()
{
return $this->fieldName;
}
/**
* @param User $user
* @return ClientsideValidator
*/
public function setUser(User $user = null)
{
$this->user = $user;
return $this;
}
/**
* @return User
*/
public function getUser()
{
return $this->user;
}
/**
* @param string $additionalValue
* @return ClientsideValidator
*/
public function setAdditionalValue($additionalValue)
{
$this->additionalValue = $additionalValue;
return $this;
}
/**
* @return string
*/
public function getAdditionalValue()
{
return $this->additionalValue;
}
/**
* @return int
*/
public function getPlugin(): int
{
return $this->plugin;
}
/**
* @param int $plugin
* @return ClientsideValidator
*/
public function setPlugin(int $plugin)
{
$this->plugin = $plugin;
return $this;
}
/**
* @return string
*/
public function getActionName(): string
{
return $this->actionName;
}
/**
* @param string $actionName
* @return ClientsideValidator
*/
public function setActionName(string $actionName)
{
$this->actionName = $actionName;
return $this;
}
/**
* @return string
*/
protected function getValidationName(): string
{
$validationName = 'validation';
if ($this->getControllerName() === 'invitation' && $this->getActionName() === 'edit') {
$validationName = 'validationEdit';
}
return $validationName;
}
/**
* @return string
*/
protected function getControllerName(): string
{
$pluginRepository = GeneralUtility::makeInstance(PluginRepository::class);
$controllerName = $pluginRepository->getControllerNameByPluginSettings($this->getPlugin());
return $controllerName;
}
/**
* @param mixed $value
*/
protected function isValid($value)
{
}
}
@@ -0,0 +1,145 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Domain\Validator;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Extbase\Validation\Validator\AbstractValidator as AbstractValidatorExtbase;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
/**
* Class PasswordValidator
*/
class PasswordValidator extends AbstractValidatorExtbase
{
public ?ConfigurationManagerInterface $configurationManager = null;
/**
* Content Object
*
* @var object
*/
protected $cObj;
/**
* Plugin Variables
*
* @var array
*/
public $piVars = [];
/**
* TypoScript Configuration
*
* @var array
*/
public $configuration = [];
/**
* Action Name
*
* @var string
*/
protected $actionName;
/**
* @param ConfigurationManagerInterface $configurationManager
*/
public function injectConfigurationManagerInterface(ConfigurationManagerInterface $configurationManager)
{
$this->configurationManager = $configurationManager;
}
public function initializeObject(): void
{
if ($this->configurationManager === null) {
$this->configurationManager = GeneralUtility::makeInstance(ConfigurationManagerInterface::class);
}
}
/**
* Validation of given Params
*
* @param $user
* @return bool
*/
public function isValid($user)
{
$this->initializeObject();
$this->init();
// if password fields are not active or if keep function active
if (!$this->passwordFieldsAdded() || $this->keepPasswordIfEmpty()) {
return true;
}
$password = $user->getPassword();
$passwordRepeat = isset($this->piVars['password_repeat']) ? $this->piVars['password_repeat'] : '';
if ($password !== $passwordRepeat) {
$this->addError('validationErrorPasswordRepeat', 0, ['field' => 'password']);
return false;
}
return true;
}
/**
* Check if Passwords are empty and if keep configuration is active
*
* @return bool
*/
protected function keepPasswordIfEmpty()
{
if (isset($this->configuration['edit']['misc']['keepPasswordIfEmpty']) &&
$this->configuration['edit']['misc']['keepPasswordIfEmpty'] === '1' &&
isset($this->piVars['user']['password']) && $this->piVars['user']['password'] === '' &&
isset($this->piVars['password_repeat']) && $this->piVars['password_repeat'] === ''
) {
return true;
}
return false;
}
/**
* Check if password fields are added with flexform
*
* @return bool
*/
protected function passwordFieldsAdded()
{
$flexFormValues = GeneralUtility::xml2array($this->cObj->data['pi_flexform']);
if (is_array($flexFormValues)) {
$fields =
$flexFormValues['data'][$this->actionName]['lDEF']['settings.' . $this->actionName . '.fields']['vDEF'] ?? [];
if (empty($fields) || GeneralUtility::inList($fields, 'password')) {
// password fields are added to form
return true;
}
}
// password fields are not added to form
return false;
}
/**
* Initialize Validator Function
*/
protected function init()
{
$this->configuration = $this->configurationManager->getConfiguration(
ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS,
'Femanager',
'Pi1'
);
$this->cObj = $this->configurationManager->getContentObject();
$this->piVars = GeneralUtility::_GP('tx_femanager_pi1');
$this->actionName = $this->piVars['__referrer']['@action'];
}
public function setContentObjectRenderer(ContentObjectRenderer $cObj): void
{
$this->cObj = $cObj;
}
}
@@ -0,0 +1,403 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Domain\Validator;
use In2code\Femanager\Domain\Model\User;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Domain\Model\FileReference;
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
/**
* Class ServersideValidator
*/
class ServersideValidator extends AbstractValidator
{
/**
* Validation of given Params
*
* @param User $user
* @return bool
*/
public function isValid($user): bool
{
$this->init();
if ($this->validationSettings['_enable']['server'] === '1') {
foreach ($this->validationSettings as $fieldName => $validations) {
if ($this->shouldBeValidated($user, $fieldName, $validations)) {
$value = $this->getValue($user, $fieldName);
foreach ($validations as $validation => $validationSetting) {
switch ($validation) {
case 'required':
$this->checkRequiredValidation($validationSetting, $value, $fieldName);
break;
case 'email':
$this->checkEmailValidation($value, $validationSetting, $fieldName);
break;
case 'min':
$this->checkMinValidation($value, $validationSetting, $fieldName);
break;
case 'max':
$this->checkMaxValidation($value, $validationSetting, $fieldName);
break;
case 'intOnly':
$this->checkIntOnlyValidation($value, $validationSetting, $fieldName);
break;
case 'lettersOnly':
$this->checkLetterOnlyValidation($value, $validationSetting, $fieldName);
break;
case 'unicodeLettersOnly':
$this->checkUnicodeLetterOnlyValidation($value, $validationSetting, $fieldName);
break;
case 'uniqueInPage':
$this->checkUniqueInPageValidation($user, $value, $validationSetting, $fieldName);
break;
case 'uniqueInDb':
$this->checkUniqueInDbValidation($user, $value, $validationSetting, $fieldName);
break;
case 'mustInclude':
$this->checkMustIncludeValidation($value, $validationSetting, $fieldName);
break;
case 'mustNotInclude':
$this->checkMustNotIncludeValidation($value, $validationSetting, $fieldName);
break;
case 'inList':
$this->checkInListValidation($value, $validationSetting, $fieldName);
break;
case 'sameAs':
$this->checkSameAsValidation($user, $validationSetting, $value, $fieldName);
break;
case 'date':
// Nothing to do. ServersideValidator runs after converter
// If dateTimeConverter exception $value is the old DateTime Object => True
// If dateTimeConverter runs well we have an DateTime Object => True
break;
default:
// e.g. search for method validateCustom()
$this->checkAnyValidation($validation, $value, $validationSetting, $fieldName);
}
}
}
}
}
return $this->isValid;
}
/**
* @param $validationSetting
* @param $value
* @param $fieldName
*/
protected function checkRequiredValidation($validationSetting, $value, $fieldName)
{
if ($validationSetting === '1' && !$this->validateRequired($value)) {
$this->addErrorForProperty($fieldName, 'validationErrorRequired', 0, ['field' => $fieldName]);
$this->isValid = false;
}
}
/**
* @param $value
* @param $validationSetting
* @param $fieldName
*/
protected function checkEmailValidation($value, $validationSetting, $fieldName)
{
if (!empty($value) && $validationSetting === '1' && !$this->validateEmail($value)) {
$this->addErrorForProperty($fieldName, 'validationErrorEmail', 0, ['field' => $fieldName]);
$this->isValid = false;
}
}
/**
* @param $value
* @param $validationSetting
* @param $fieldName
*/
protected function checkMinValidation($value, $validationSetting, $fieldName)
{
if (!empty($value) && !$this->validateMin($value, $validationSetting)) {
$this->addErrorForProperty($fieldName, 'validationErrorMin', 0, ['field' => $fieldName]);
$this->isValid = false;
}
}
/**
* @param $value
* @param $validationSetting
* @param $fieldName
*/
protected function checkMaxValidation($value, $validationSetting, $fieldName)
{
if (!empty($value) && !$this->validateMax($value, $validationSetting)) {
$this->addErrorForProperty($fieldName, 'validationErrorMax', 0, ['field' => $fieldName]);
$this->isValid = false;
}
}
/**
* @param $value
* @param $validationSetting
* @param $fieldName
*/
protected function checkIntOnlyValidation($value, $validationSetting, $fieldName)
{
if (!empty($value) && $validationSetting === '1' && !$this->validateInt($value)) {
$this->addErrorForProperty($fieldName, 'validationErrorInt', 0, ['field' => $fieldName]);
$this->isValid = false;
}
}
/**
* @param $value
* @param $validationSetting
* @param $fieldName
*/
protected function checkLetterOnlyValidation($value, $validationSetting, $fieldName)
{
if (!empty($value) && $validationSetting === '1' && !$this->validateLetters($value)) {
$this->addErrorForProperty($fieldName, 'validationErrorLetters', 0, ['field' => $fieldName]);
$this->isValid = false;
}
}
/**
* @param $value
* @param $validationSetting
* @param $fieldName
*/
protected function checkUnicodeLetterOnlyValidation($value, $validationSetting, $fieldName)
{
if (!empty($value) && $validationSetting === '1' && !$this->validateUnicodeLetters($value)) {
$this->addErrorForProperty($fieldName, 'validationErrorLetters', 0, ['field' => $fieldName]);
$this->isValid = false;
}
}
/**
* @param $user
* @param $value
* @param $validationSetting
* @param $fieldName
*/
protected function checkUniqueInPageValidation($user, $value, $validationSetting, $fieldName)
{
if (
!empty($value) &&
$validationSetting === '1' &&
!$this->validateUniquePage($value, $fieldName, $user)
) {
$this->addErrorForProperty($fieldName, 'validationErrorUniquePage', 0, ['field' => $fieldName]);
$this->isValid = false;
}
}
/**
* @param $user
* @param $value
* @param $validationSetting
* @param $fieldName
*/
protected function checkUniqueInDbValidation($user, $value, $validationSetting, $fieldName)
{
if (
!empty($value) &&
$validationSetting === '1' &&
!$this->validateUniqueDb($value, $fieldName, $user)
) {
$this->addErrorForProperty($fieldName, 'validationErrorUniqueDb', 0, ['field' => $fieldName]);
$this->isValid = false;
}
}
/**
* @param $value
* @param $validationSetting
* @param $fieldName
*/
protected function checkMustIncludeValidation($value, $validationSetting, $fieldName)
{
if (!empty($value) && !$this->validateMustInclude($value, $validationSetting)) {
$this->addErrorForProperty($fieldName, 'validationErrorMustInclude', 0, ['field' => $fieldName]);
$this->isValid = false;
}
}
/**
* @param $value
* @param $validationSetting
* @param $fieldName
*/
protected function checkMustNotIncludeValidation($value, $validationSetting, $fieldName)
{
if (!empty($value) && !$this->validateMustNotInclude($value, $validationSetting)) {
$this->addErrorForProperty($fieldName, 'validationErrorMustNotInclude', 0, ['field' => $fieldName]);
$this->isValid = false;
}
}
/**
* @param $value
* @param $validationSetting
* @param $fieldName
*/
protected function checkInListValidation($value, $validationSetting, $fieldName)
{
if (!$this->validateInList($value, $validationSetting)) {
$this->addErrorForProperty($fieldName, 'validationErrorInList', 0, ['field' => $fieldName]);
$this->isValid = false;
}
}
/**
* @param $user
* @param $validationSetting
* @param $value
* @param $fieldName
*/
protected function checkSameAsValidation($user, $validationSetting, $value, $fieldName)
{
if (method_exists($user, 'get' . ucfirst($validationSetting))) {
$valueToCompare = $user->{'get' . ucfirst($validationSetting)}();
if (!$this->validateSameAs($value, $valueToCompare)) {
$this->addErrorForProperty($fieldName, 'validationErrorSameAs', 0, ['field' => $fieldName]);
$this->isValid = false;
}
}
}
/**
* @param $validation
* @param $value
* @param $validationSetting
* @param $fieldName
*/
protected function checkAnyValidation($validation, $value, $validationSetting, $fieldName)
{
if (method_exists($this, 'validate' . ucfirst($validation))) {
if (!$this->{'validate' . ucfirst($validation)}($value, $validationSetting)) {
$this->addErrorForProperty($fieldName, 'validationError' . ucfirst($validation), 0, ['field' => $fieldName]);
$this->isValid = false;
}
}
}
/**
* @param User $user
* @param string $fieldName
* @return string
*/
protected function getValue($user, $fieldName)
{
$value = $this->getValueFromProperty($user, $fieldName);
if ($value instanceof ObjectStorage) {
$values = [];
foreach ($value as $object) {
if (method_exists($object, 'getUid')) {
$values[] = $object->getUid();
}
if ($object instanceof FileReference) {
return true;
}
}
return implode(',', $values);
}
if (is_object($value)) {
if (method_exists($value, 'getUid')) {
return $value->getUid();
}
if (method_exists($value, 'getFirst')) {
return $value->getFirst()->getUid();
}
if (method_exists($value, 'current')) {
$current = $value->current();
if ($current instanceof FileReference) {
return true;
}
if ($current !== null) {
if (method_exists($current, 'getUid')) {
return $current->getUid();
}
}
}
}
return $value;
}
/**
* @param $user
* @param $fieldName
* @param array $validationSettings
* @return bool
*/
protected function shouldBeValidated($user, $fieldName, array $validationSettings): bool
{
return is_object($user)
&& $this->propertyHasGetterMethod($user, $fieldName)
&& $this->evaluateConditions($user, $fieldName, $validationSettings);
}
protected function evaluateConditions(User $user, string $fieldName, array $validationSettings)
{
if (array_key_exists('if', $validationSettings) && class_exists($validationSettings['if'])) {
$object = GeneralUtility::makeInstance($validationSettings['if']);
if ($object instanceof ValidationConditionInterface) {
return $object->shouldValidate($user, $fieldName, $validationSettings);
}
}
return true;
}
/**
* @param $user
* @param $fieldName
* @return mixed
*/
protected function getValueFromProperty($user, $fieldName)
{
$value = '';
try {
$value = ObjectAccess::getProperty($user, $fieldName);
} catch (\Exception $exception) {
unset($exception);
}
return $value;
}
/**
* @return bool
*/
protected function propertyHasGetterMethod($user, $fieldName): bool
{
try {
ObjectAccess::getProperty($user, $fieldName);
$getterExists = true;
} catch (\Exception $exception) {
unset($exception);
$getterExists = false;
}
return $getterExists === true;
}
}
@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Domain\Validator;
use In2code\Femanager\DataProvider\CountryZonesDataProvider;
use In2code\Femanager\Domain\Model\User;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
class ShouldValidateStateCondition implements ValidationConditionInterface
{
protected $isStaticInfoTablesLoaded;
protected $countryZonesDataProvider;
public function __construct()
{
$this->isStaticInfoTablesLoaded = ExtensionManagementUtility::isLoaded('static_info_tables');
if ($this->isStaticInfoTablesLoaded) {
$this->countryZonesDataProvider = GeneralUtility::makeInstance(CountryZonesDataProvider::class);
}
}
public function shouldValidate(User $user, string $fieldName, array $validationSettings): bool
{
return $this->isStaticInfoTablesLoaded
&& $this->countryHasZones(ObjectAccess::getProperty($user, 'country'));
}
protected function countryHasZones($countryIso3)
{
return $this->countryZonesDataProvider->hasCountryZonesForCountryIso3($countryIso3);
}
}
@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Domain\Validator;
use In2code\Femanager\Domain\Model\User;
interface ValidationConditionInterface
{
public function shouldValidate(User $user, string $fieldName, array $validationSettings): bool;
}
@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Event;
class AdminConfirmationUserEvent extends UserEvent
{
}
@@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Event;
use In2code\Femanager\Domain\Service\SendMailService;
use Symfony\Component\Mime\Email;
class AfterMailSendEvent
{
/**
* @var Email
*/
private $email;
/**
* @var array
*/
private $variables;
/**
* @var SendMailService
*/
private $service;
public function __construct(Email $email, array $variables, SendMailService $service)
{
$this->email = $email;
$this->variables = $variables;
$this->service = $service;
}
/**
* @return Email
*/
public function getEmail(): Email
{
return $this->email;
}
/**
* @return SendMailService
*/
public function getService(): SendMailService
{
return $this->service;
}
/**
* @return array
*/
public function getVariables(): array
{
return $this->variables;
}
}
@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Event;
use In2code\Femanager\Domain\Model\User;
class AfterUserUpdateEvent extends UserEvent
{
/**
* @var string
*/
private $hash;
/**
* @var string
*/
private $status;
public function __construct(User $user, string $hash, string $status)
{
parent::__construct($user);
$this->hash = $hash;
$this->status = $status;
}
/**
* @return string
*/
public function getStatus(): string
{
return $this->status;
}
/**
* @return string
*/
public function getHash(): string
{
return $this->hash;
}
}
@@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Event;
use In2code\Femanager\Domain\Service\SendMailService;
use TYPO3\CMS\Fluid\View\StandaloneView;
class BeforeMailBodyRenderEvent
{
/**
* @var StandaloneView
*/
private $standAloneView;
/**
* @var array
*/
private $variables;
/**
* @var SendMailService
*/
private $service;
public function __construct(StandaloneView $standAloneView, array $variables, SendMailService $service)
{
$this->standAloneView = $standAloneView;
$this->variables = $variables;
$this->service = $service;
}
/**
* @return StandaloneView
*/
public function getStandaloneView(): StandaloneView
{
return $this->standAloneView;
}
/**
* @return SendMailService
*/
public function getService(): SendMailService
{
return $this->service;
}
/**
* @return array
*/
public function getVariables(): array
{
return $this->variables;
}
/**
* @param StandaloneView $standAloneView
*/
public function setStandaloneView(StandaloneView $standAloneView): void
{
$this->standAloneView = $standAloneView;
}
}
@@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Event;
use In2code\Femanager\Domain\Service\SendMailService;
use Symfony\Component\Mime\Email;
class BeforeMailSendEvent
{
/**
* @var Email
*/
private $email;
/**
* @var array
*/
private $variables;
/**
* @var SendMailService
*/
private $service;
public function __construct(Email $email, array $variables, SendMailService $service)
{
$this->email = $email;
$this->variables = $variables;
$this->service = $service;
}
/**
* @return Email
*/
public function getEmail(): Email
{
return $this->email;
}
/**
* @return SendMailService
*/
public function getService(): SendMailService
{
return $this->service;
}
/**
* @return array
*/
public function getVariables(): array
{
return $this->variables;
}
/**
* @param Email $email
*/
public function setEmail(Email $email): void
{
$this->email = $email;
}
}
@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Event;
class BeforeUpdateUserEvent extends UserEvent
{
}
@@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Event;
use In2code\Femanager\Domain\Model\User;
class BeforeUserConfirmEvent
{
/**
* @var User|null
*/
private $user;
/**
* @var string
*/
private $hash;
/**
* @var string
*/
private $status;
public function __construct(
?User $user,
string $hash,
string $status
) {
$this->user = $user;
$this->hash = $hash;
$this->status = $status;
}
/**
* @return User|null
*/
public function getUser(): ?User
{
return $this->user;
}
/**
* @return string
*/
public function getHash(): string
{
return $this->hash;
}
/**
* @return string
*/
public function getStatus(): string
{
return $this->status;
}
}
@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Event;
class BeforeUserCreateEvent extends UserEvent
{
}
@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Event;
use In2code\Femanager\Domain\Model\User;
class CreateConfirmationRequestEvent extends UserEvent
{
public const MODE_AUTOMATIC = 'automatic';
public const MODE_MANUAL = 'manual';
/**
* @var string
*/
private $mode;
public function __construct(User $user, string $mode)
{
parent::__construct($user);
$this->mode = $mode;
}
/**
* @return string
*/
public function getMode(): string
{
return $this->mode;
}
}
@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Event;
class DeleteUserEvent extends UserEvent
{
}
@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Event;
use In2code\Femanager\Domain\Model\User;
class FinalCreateEvent extends UserEvent
{
/**
* @var string
*/
private $action;
public function __construct(?User $user, string $action)
{
parent::__construct($user);
$this->action = $action;
}
public function getAction(): string
{
return $this->action;
}
}
@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Event;
class FinalUpdateEvent extends UserEvent
{
}
@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Event;
class ImpersonateEvent extends UserEvent
{
}
@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Event;
class InviteUserConfirmedEvent extends UserEvent
{
}
@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Event;
class InviteUserCreateEvent extends UserEvent
{
}
@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Event;
use In2code\Femanager\Domain\Model\User;
class InviteUserEditEvent extends UserEvent
{
/**
* @var string
*/
private $hash;
public function __construct(?User $user, string $hash)
{
parent::__construct($user);
$this->hash = $hash;
}
/**
* @return string
*/
public function getHash(): string
{
return $this->hash;
}
}
@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Event;
class InviteUserUpdateEvent extends UserEvent
{
}
@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Event;
class RefuseUserEvent extends UserEvent
{
}
@@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Event;
use In2code\Femanager\Domain\Model\User;
class UniqueUserEvent
{
/**
* @var string
*/
private $emailOrUsername;
/**
* @var string
*/
private $fieldName;
/**
* @var User|null
*/
private $user;
/**
* @var bool
*/
private $unique;
public function __construct(
string $emailOrUsername,
string $fieldName,
?User $user,
bool $unique
) {
$this->emailOrUsername = $emailOrUsername;
$this->fieldName = $fieldName;
$this->user = $user;
$this->unique = $unique;
}
public function setUnique(bool $unique): void
{
$this->unique = $unique;
}
public function isUnique(): bool
{
return $this->unique;
}
public function getEmailOrUsername(): string
{
return $this->emailOrUsername;
}
public function getFieldName(): string
{
return $this->fieldName;
}
public function getUser(): ?User
{
return $this->user;
}
}
@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Event;
use In2code\Femanager\Domain\Model\User;
class UserEvent
{
protected $user;
public function __construct(?User $user)
{
$this->user = $user;
}
public function getUser(): ?User
{
return $this->user;
}
}
@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Event;
use In2code\Femanager\Domain\Model\User;
class UserLogEvent extends UserEvent
{
/**
* @var int
*/
private $state;
/**
* @var array
*/
private $additionalProperties;
public function __construct(?User $user, int $state, array $additionalProperties = [])
{
parent::__construct($user);
$this->state = $state;
$this->additionalProperties = $additionalProperties;
}
public function getAdditionalProperties(): array
{
return $this->additionalProperties;
}
public function getState(): int
{
return $this->state;
}
}
@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Exception;
/**
* Class FileNotFoundException
*/
class FileNotFoundException extends \Exception
{
}
@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Exception;
/**
* NotPaginatableException
*/
class NotPaginatableException extends \Exception
{
}
@@ -0,0 +1,138 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Finisher;
use In2code\Femanager\Domain\Model\User;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
/**
* Class AbstractFinisher
*/
abstract class AbstractFinisher implements FinisherInterface
{
/**
* @var User
*/
protected $user;
/**
* Extension settings
*
* @var array
*/
protected $settings;
/**
* Finisher service configuration
*
* @var array
*/
protected $configuration;
/**
* Controller actionName - usually "createAction" or "confirmationAction"
*
* @var null
*/
protected $actionMethodName;
/**
* @var ContentObjectRenderer
*/
protected $contentObject;
/**
* @return User
*/
public function getUser()
{
return $this->user;
}
/**
* @param User $user
* @return AbstractFinisher
*/
public function setUser($user)
{
$this->user = $user;
return $this;
}
/**
* @return array
*/
public function getSettings()
{
return $this->settings;
}
/**
* @param array $settings
* @return AbstractFinisher
*/
public function setSettings($settings)
{
$this->settings = $settings;
return $this;
}
/**
* @return array
*/
public function getConfiguration()
{
return $this->configuration;
}
/**
* @param array $configuration
* @return AbstractFinisher
*/
public function setConfiguration($configuration)
{
$this->configuration = $configuration;
return $this;
}
public function getActionMethodName()
{
return $this->actionMethodName;
}
/**
* @param null $actionMethodName
* @return AbstractFinisher
*/
public function setActionMethodName($actionMethodName)
{
$this->actionMethodName = $actionMethodName;
return $this;
}
public function initializeFinisher()
{
}
/**
* @param User $user
* @param array $configuration
* @param array $settings
* @param ContentObjectRenderer $contentObject
* @param string $actionMethodName
*/
public function __construct(
User $user,
array $configuration,
array $settings,
$actionMethodName,
ContentObjectRenderer $contentObject
) {
$this->setUser($user);
$this->setConfiguration($configuration);
$this->setSettings($settings);
$this->setActionMethodName($actionMethodName);
$this->contentObject = $contentObject;
}
}
@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Finisher;
use In2code\Femanager\Domain\Model\User;
/**
* Interface FinisherInterface
*/
interface FinisherInterface
{
/**
* @return User
*/
public function getUser();
/**
* @param User $user
* @return AbstractFinisher
*/
public function setUser($user);
/**
* Get settings
*
* @return array
*/
public function getSettings();
/**
* Set settings
*
* @param array $settings
* @return AbstractFinisher
*/
public function setSettings($settings);
public function getActionMethodName();
/**
* @param null $actionMethodName
* @return AbstractFinisher
*/
public function setActionMethodName($actionMethodName);
public function initializeFinisher();
}
@@ -0,0 +1,90 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Finisher;
use In2code\Femanager\Domain\Model\User;
use In2code\Femanager\Domain\Service\FinisherService;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
/**
* Class FinisherRunner
*/
class FinisherRunner
{
/**
* @var ObjectManagerInterface
*/
protected $objectManager;
/**
* @var ConfigurationManagerInterface
*/
protected $configurationManager;
/**
* @var ContentObjectRenderer
*/
protected $contentObject;
/**
* TypoScript settings
*
* @var array
*/
protected $settings = [];
/**
* FinisherRunner constructor.
* @param ObjectManagerInterface $objectManager
* @param ConfigurationManagerInterface $configurationManager
*/
public function __construct(
ObjectManagerInterface $objectManager,
ConfigurationManagerInterface $configurationManager
) {
$this->objectManager = $objectManager;
$this->configurationManager = $configurationManager;
}
/**
* Call finisher classes after submit
*
* @param User $user
* @param string $actionMethodName
* @param array $settings
* @param ContentObjectRenderer $contentObject
*/
public function callFinishers(
User $user,
$actionMethodName,
$settings,
ContentObjectRenderer $contentObject
) {
foreach ($this->getFinisherClasses($settings) as $finisherSettings) {
/** @var FinisherService $finisherService */
$finisherService = $this->objectManager->get(FinisherService::class, $user, $settings, $contentObject);
$finisherService->setClass($finisherSettings['class']);
$finisherService->setRequirePath($finisherSettings['require'] ?? '');
$finisherService->setConfiguration($finisherSettings['config'] ?? []);
$finisherService->setActionMethodName($actionMethodName);
$finisherService->start();
}
}
/**
* Get all finisher classes from typoscript and sort them
*
* @param array $settings
* @return array
*/
protected function getFinisherClasses($settings)
{
$finishers = (array)$settings['finishers'];
ksort($finishers);
return $finishers;
}
}
@@ -0,0 +1,192 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Finisher;
use In2code\Femanager\Domain\Service\StoreInDatabaseService;
use In2code\Femanager\Utility\StringUtility;
use TYPO3\CMS\Core\TypoScript\TypoScriptService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
/**
* Class SaveToAnyTableFinisher
*/
class SaveToAnyTableFinisher extends AbstractFinisher implements FinisherInterface
{
/**
* Inject a complete new content object
*
* @var ContentObjectRenderer
*/
protected $contentObject;
/**
* @var ObjectManager
*/
protected $objectManager;
/**
* @var TypoScriptService
*/
protected $typoScriptService;
/**
* @var array
*/
protected $configuration = [];
/**
* @var int
*/
protected $lastGeneratedUid = 0;
/**
* @var array
*/
protected $dataArray = [];
/**
* @param ContentObjectRenderer $contentObject
*/
public function injectContentObjectRenderer(ContentObjectRenderer $contentObject)
{
$this->contentObject = $contentObject;
}
/**
* @param ObjectManager $objectManager
*/
public function injectObjectManager(ObjectManager $objectManager)
{
$this->objectManager = $objectManager;
}
/**
* @param TypoScriptService $typoScriptService
*/
public function injectTypoScriptService(TypoScriptService $typoScriptService)
{
$this->typoScriptService = $typoScriptService;
}
/**
* Overwrite configuration with
* plugin.tx_femanager.settings.new.storeInDatabase
*/
public function initializeFinisher()
{
if ($this->typoScriptService == null) {
$this->typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class);
}
$configuration = $this->typoScriptService->convertPlainArrayToTypoScriptArray($this->settings);
if (!empty($configuration['new.']['storeInDatabase.'])) {
$this->configuration = $configuration['new.']['storeInDatabase.'];
}
}
/**
* Store to any table
*/
public function storeFinisher()
{
if (!empty($this->configuration)) {
$this->addArrayToDataArray($this->user->_getProperties());
foreach ((array)array_keys($this->configuration) as $table) {
$this->storeForTable($table);
}
}
}
/**
* Store for a given table
*
* @param string $table
*/
protected function storeForTable($table)
{
if ($this->isTableEnabled($table)) {
$this->contentObject->start($this->getDataArray());
/** @var StoreInDatabaseService $storeInDatabase */
$storeInDatabase = $this->objectManager->get(StoreInDatabaseService::class);
$storeInDatabase->setTable($table);
$this->setPropertiesForTable($table, $storeInDatabase);
$this->addArrayToDataArray(['uid_' . $table => $storeInDatabase->execute()]);
}
}
/**
* Store properties for a table
*
* @param string $table
* @param StoreInDatabaseService $storeInDatabase
*/
protected function setPropertiesForTable($table, StoreInDatabaseService $storeInDatabase)
{
foreach ($this->configuration[$table] as $field => $value) {
if (!$this->isSkippedKey($field)) {
$value = $this->contentObject->cObjGetSingle(
(string)$this->configuration[$table][$field],
(array)$this->configuration[$table][$field . '.']
);
$storeInDatabase->addProperty($field, $value);
}
}
}
/**
* Check if this table is enabled in TypoScript
*
* @param string $table
* @return bool
*/
protected function isTableEnabled($table)
{
return $this->contentObject->cObjGetSingle(
(string)$this->configuration[$table]['_enable'],
(array)$this->configuration[$table]['_enable.']
) === '1';
}
/**
* Should this key skipped because it starts with _ or ends with .
*
* @param string $key
* @return bool
*/
protected function isSkippedKey($key)
{
return StringUtility::startsWith($key, '_') || StringUtility::endsWith($key, '.');
}
/**
* Add array to dataArray
*
* @param array $array
*/
protected function addArrayToDataArray(array $array)
{
$dataArray = $this->getDataArray();
$dataArray = array_merge($dataArray, $array);
$this->setDataArray($dataArray);
}
/**
* @return array
*/
public function getDataArray()
{
return $this->dataArray;
}
/**
* @param array $dataArray
* @return SaveToAnyTableFinisher
*/
public function setDataArray($dataArray)
{
$this->dataArray = $dataArray;
return $this;
}
}
@@ -0,0 +1,124 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Finisher;
use TYPO3\CMS\Core\Http\RequestFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
/**
* Class SendParametersFinisher
*/
class SendParametersFinisher extends AbstractFinisher implements FinisherInterface
{
/**
* Inject a complete new content object
*
* @var ContentObjectRenderer
*/
protected $contentObject;
/**
* @var ConfigurationManagerInterface
*/
protected $configurationManager;
/**
* TypoScript configuration part sendPost
*
* @var array
*/
protected $configuration;
/**
* @param ConfigurationManagerInterface $configurationManager
*/
public function injectConfigurationManagerInterface(ConfigurationManagerInterface $configurationManager)
{
$this->configurationManager = $configurationManager;
}
/**
* @param ContentObjectRenderer $contentObject
*/
public function injectContentObjectRenderer(ContentObjectRenderer $contentObject)
{
$this->contentObject = $contentObject;
}
/**
* Initialize
*/
public function initializeFinisher()
{
$this->contentObject->start($this->user->_getProperties());
$typoScript = $this->configurationManager->getConfiguration(
ConfigurationManagerInterface::CONFIGURATION_TYPE_FULL_TYPOSCRIPT
);
$this->configuration = $typoScript['plugin.']['tx_femanager.']['settings.']['new.']['sendPost.'];
}
/**
* Send values via curl to a third party software
*/
public function sendFinisher()
{
if ($this->isEnabled()) {
$curlSettings = $this->getCurlSettings();
/** @var RequestFactory $requestFactory */
$requestFactory = GeneralUtility::makeInstance(RequestFactory::class);
$params = $curlSettings['params'];
$parsedParams = [];
parse_str($params, $parsedParams);
$requestFactory->request($curlSettings['url'], 'POST', ['form_params' => $parsedParams]);
}
}
/**
* CURL settings
*
* @return array
*/
protected function getCurlSettings()
{
return [
'url' => $this->getTargetUrl(),
'params' => $this->getData()
];
}
/**
* Get parameters
*
* @return string
*/
protected function getData()
{
return $this->contentObject->cObjGetSingle((string)$this->configuration['data'], (array)$this->configuration['data.']);
}
protected function getTargetUrl()
{
$linkConfiguration = [
'parameter' => $this->configuration['targetUrl'],
'forceAbsoluteUrl' => '1',
'returnLast' => 'url'
];
return $this->contentObject->typoLink('dummy', $linkConfiguration);
}
/**
* Check if sendPost is activated
*
* @return bool
*/
protected function isEnabled()
{
return $this->contentObject->cObjGetSingle($this->configuration['_enable'] ?? 'TEXT', $this->configuration['_enable.'] ?? '0')
=== '1';
}
}
@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Persistence\Generic\Mapper;
use In2code\Femanager\Domain\Model\User;
use In2code\Femanager\Domain\Model\UserGroup;
use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMap as DataMapExtbase;
/**
* Disable tx_extbase_type='0' in where clause for femanager
*/
class DataMap extends DataMapExtbase
{
/**
* Disable record type for femanager
*
* @param string $recordType The record type
*/
public function setRecordType($recordType)
{
parent::setRecordType($recordType);
if ($this->getClassName() === User::class || $this->getClassName() === UserGroup::class) {
$this->recordType = null;
}
}
}
@@ -0,0 +1,77 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\UserFunc;
use In2code\Femanager\DataProvider\CountryDataProvider;
use In2code\Femanager\DataProvider\CountryZonesDataProvider;
use In2code\Femanager\DataProvider\FallbackCountryDataProvider;
use TYPO3\CMS\Backend\Form\FormDataProvider\TcaSelectItems;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
class StaticInfoTables
{
public function isStaticInfoTablesInstalled(): bool
{
return ExtensionManagementUtility::isLoaded('static_info_tables');
}
public function getStatesOptions(array $data, TcaSelectItems $tcaSelectItems)
{
if (ExtensionManagementUtility::isLoaded('static_info_tables')) {
$countryZonesDataProvider = GeneralUtility::makeInstance(CountryZonesDataProvider::class);
$country = $data['row']['country'] ?? null;
if (is_array($country)) {
if (count($country) > 0) {
$country = array_values($country)[0];
} else {
$country = null;
}
}
if (empty($country)) {
$data['items'] = [
[
'LLL:EXT:femanager/Resources/Private/Language/locallang_db.xlf:pleaseChooseCountry',
''
]
];
} else {
$countryZones = $countryZonesDataProvider->getCountryZonesForCountryIso3($country);
if (empty($countryZones)) {
$data['items'] = [
[
'LLL:EXT:femanager/Resources/Private/Language/locallang_db.xlf:noZonesForThisCountry',
''
]
];
} else {
foreach ($countryZones as $countryZone) {
$data['items'][] = [$countryZone->getLocalName(), $countryZone->getIsoCode()];
}
}
}
} else {
$data['items'][] = ['Error: static_info_tables is not installed', ''];
}
}
public function getCountryOptions(array $data, TcaSelectItems $tcaSelectItems)
{
if (ExtensionManagementUtility::isLoaded('static_info_tables')) {
$countryDataProvider = GeneralUtility::makeInstance(CountryDataProvider::class);
$countries = $countryDataProvider->getCountries();
foreach ($countries as $country) {
$data['items'][] = [$country->getShortNameEn(), $country->getIsoCodeA3()];
}
} else {
$fallbackCountryDataProvider = GeneralUtility::makeInstance(FallbackCountryDataProvider::class);
$countries = $fallbackCountryDataProvider->getCountries();
foreach ($countries as $key => $name) {
$data['items'][] = [$name, $key];
}
}
}
}
@@ -0,0 +1,113 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\UserFunc;
use In2code\Femanager\Utility\StringUtility;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Class UserFieldsOptions
*/
class UserFieldsOptions
{
/**
* @var LanguageService
*/
protected $languageService;
/**
* @var string
*/
protected $localLangPrefix = 'LLL:EXT:femanager/Resources/Private/Language/locallang_db.xlf:';
/**
* Add options to FlexForm Selection - Options can be defined in TSConfig
*
* @param array $params
*/
public function addOptions(&$params)
{
$this->initialize();
$tSconfig = BackendUtility::getPagesTSconfig($this->getPid());
$this->addCaptchaOption($params);
$this->addStateOption($params);
$tab = $params['config']['itemsProcFuncTab'] . '.';
if (!empty($tSconfig['tx_femanager.']['flexForm.'][$tab]['addFieldOptions.'])) {
$options = $tSconfig['tx_femanager.']['flexForm.'][$tab]['addFieldOptions.'];
foreach ((array)$options as $value => $label) {
$params['items'][] = [
StringUtility::startsWith($label, 'LLL:') ? $this->languageService->sL($label) : $label,
$value
];
}
}
}
/**
* Add captcha option
*
* @param array $params
*/
protected function addCaptchaOption(&$params)
{
if (ExtensionManagementUtility::isLoaded('sr_freecap')) {
$params['items'][] = [
$this->languageService->sL($this->localLangPrefix . 'tx_femanager_domain_model_user.captcha'),
'captcha'
];
}
}
/**
* Add captcha option
*
* @param array $params
*/
protected function addStateOption(&$params)
{
if (ExtensionManagementUtility::isLoaded('static_info_tables')) {
$params['items'][] = [
$this->languageService->sL($this->localLangPrefix . 'tx_femanager_domain_model_user.state'),
'state'
];
}
}
/**
* Read pid from current URL
* URL example:
* http://femanager.localhost.de/typo3/alt_doc.php?&returnUrl=
* %2Ftypo3%2Fsysext%2Fcms%2Flayout%2Fdb_layout.php
* %3Fid%3D17%23element-tt_content-14
* &edit[tt_content][14]=edit
*
* @return int
*/
protected function getPid()
{
$pid = 0;
$backUrl = str_replace('?', '&', GeneralUtility::_GP('returnUrl'));
$urlParts = GeneralUtility::trimExplode('&', $backUrl, true);
foreach ($urlParts as $part) {
if (stristr($part, 'id=')) {
$pid = str_replace('id=', '', $part);
}
}
return (int)$pid;
}
/**
* Initialize
*
* @SuppressWarnings(PHPMD.Superglobals)
*/
protected function initialize()
{
$this->languageService = $GLOBALS['LANG'];
}
}
@@ -0,0 +1,84 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\UserFunc;
use In2code\Femanager\Domain\Model\User;
use In2code\Femanager\Utility\ObjectUtility;
use TYPO3\CMS\Core\Database\QueryGenerator;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Class UserOptions
*/
class UserOptions
{
/**
* Just add users from start point to selection
*
* @param array $params
*/
public function addOptions(&$params)
{
if ($this->getPages($params) !== []) {
$params['items'] = [
$params['items'][0], // please choose
$params['items'][1] // currently logged in user
];
foreach ($this->getUsers($params) as $user) {
$params['items'][] = [$user['username'], $user['uid']];
}
}
}
/**
* @param array $params
* @return array
*/
protected function getUsers(array $params): array
{
$queryBuilder = ObjectUtility::getQueryBuilder(User::TABLE_NAME);
$result = $queryBuilder
->select('uid', 'username')
->from(User::TABLE_NAME)
->where('pid in (' . $this->getPageUidList($params) . ')')
->setMaxResults(10000)
->orderBy('username', 'ASC')
->execute();
return $result->fetchAll();
}
/**
* Get lists of page uids where the users are stored with recursive setting
*
* @param array $params
* @return string
*/
protected function getPageUidList(array $params): string
{
$list = '';
$queryGenerator = GeneralUtility::makeInstance(QueryGenerator::class);
$depth = $params['flexParentDatabaseRow']['recursive'];
foreach ($this->getPages($params) as $pageIdentifier) {
$list .= $queryGenerator->getTreeList($pageIdentifier, $depth, 0, 1);
$list .= ',';
}
return rtrim($list, ',');
}
/**
* @param array $params
* @return array
*/
protected function getPages(array $params): array
{
$pages = [];
if (!empty($params['flexParentDatabaseRow']['pages'][0]['uid'])) {
foreach ($params['flexParentDatabaseRow']['pages'] as $page) {
$pages[] = $page['uid'];
}
}
return $pages;
}
}
@@ -0,0 +1,129 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Utility;
use Exception;
use In2code\Femanager\Domain\Repository\UserGroupRepository;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Configuration\Exception\ExtensionConfigurationExtensionNotConfiguredException;
use TYPO3\CMS\Core\Configuration\Exception\ExtensionConfigurationPathDoesNotExistException;
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
/**
* Class AbstractUtility
*/
abstract class AbstractUtility
{
/**
* Get table configuration array for a defined table
*
* @param string $table
* @return array
* @SuppressWarnings(PHPMD.Superglobals)
*/
protected static function getTcaFromTable($table = 'fe_users'): array
{
$tca = [];
if (!empty($GLOBALS['TCA'][$table])) {
$tca = $GLOBALS['TCA'][$table];
}
return $tca;
}
/**
* @return ConnectionPool
* @SuppressWarnings(PHPMD.Superglobals)
* @codeCoverageIgnore
*/
public static function getConnectionPool(): ConnectionPool
{
return GeneralUtility::makeInstance(ConnectionPool::class);
}
/**
* @return array
* @SuppressWarnings(PHPMD.Superglobals)
*/
protected static function getFilesArray(): array
{
return (array)$_FILES;
}
/**
* @return UserGroupRepository
*/
protected static function getUserGroupRepository(): UserGroupRepository
{
return GeneralUtility::makeInstance(UserGroupRepository::class);
}
/**
* @return TypoScriptFrontendController|null
* @SuppressWarnings(PHPMD.Superglobals)
*/
protected static function getTypoScriptFrontendController()
{
return $GLOBALS['TSFE'] ?? null;
}
/**
* Get TYPO3 encryption key
*
* @return string
* @throws \Exception
* @SuppressWarnings(PHPMD.Superglobals)
*/
protected static function getEncryptionKey(): string
{
if (empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'])) {
throw new \UnexpectedValueException('No encryption key found in this TYPO3 installation', 1516373945265);
}
return $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'];
}
/**
* Get extension configuration from LocalConfiguration.php
*
* @return array
* @throws ExtensionConfigurationExtensionNotConfiguredException
* @throws ExtensionConfigurationPathDoesNotExistException
*/
protected static function getExtensionConfiguration(): array
{
return (array)GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('femanager');
}
/**
* @return ContentObjectRenderer
* @throws Exception
*/
protected static function getContentObject(): ContentObjectRenderer
{
return GeneralUtility::makeInstance(ContentObjectRenderer::class);
}
/**
* @return ConfigurationManagerInterface
* @codeCoverageIgnore
* @throws Exception
*/
protected static function getConfigurationManager(): ConfigurationManagerInterface
{
return GeneralUtility::makeInstance(ConfigurationManagerInterface::class);
}
/**
* @return BackendUserAuthentication
* @SuppressWarnings(PHPMD.Superglobals)
*/
protected static function getBackendUserAuthentication()
{
return $GLOBALS['BE_USER'];
}
}
@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Utility;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
/**
* Class BackendUserUtility
*/
class BackendUserUtility extends AbstractUtility
{
/**
* @return bool
*/
public static function isAdminAuthentication()
{
$userAuthentication = self::getBackendUserAuthentication();
if ($userAuthentication->user['admin'] === 1) {
return true;
}
$tsConfigEnableLoginAs = (int)($userAuthentication->getTSConfig()['tx_femanager.']['UserBackend.']['enableLoginAs'] ?? 0);
return $tsConfigEnableLoginAs === 1;
}
/**
* @return BackendUserAuthentication
*/
public static function getBackendUserAuthentication()
{
return parent::getBackendUserAuthentication();
}
}
@@ -0,0 +1,125 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Utility;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Routing\UriBuilder;
use TYPO3\CMS\Core\Http\ApplicationType;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\MathUtility;
use TYPO3\CMS\Core\Utility\RootlineUtility;
/**
* Class BackendUtility
*/
class BackendUtility
{
/**
* @return int
*/
public static function getPageIdentifier(): int
{
return (int)GeneralUtility::_GET('id');
}
/**
* Get URI to edit a record in backend
*
* @param string $tableName like "fe_users"
* @param int $identifier record identifier to edit
* @param bool $addReturnUrl add current URI as returnUrl
* @return string
*/
public static function getBackendEditUri(string $tableName, int $identifier, bool $addReturnUrl = true): string
{
$uriParameters = [
'edit' => [
$tableName => [
$identifier => 'edit'
]
]
];
if ($addReturnUrl) {
$uriParameters['returnUrl'] = GeneralUtility::getIndpEnv('REQUEST_URI');
}
return (string)GeneralUtility::makeInstance(UriBuilder::class)
->buildUriFromRoute('record_edit', $uriParameters);
}
/**
* Get URI to create a new record in backend
*
* @param string $tableName like "fe_users"
* @param int $pageIdentifier page identifier to store the new record in
* @param bool $addReturnUrl add current URI as returnUrl
* @return string
*/
public static function getBackendNewUri(string $tableName, int $pageIdentifier, bool $addReturnUrl = true): string
{
$uriParameters = [
'edit' => [
$tableName => [
$pageIdentifier => 'new'
]
]
];
if ($addReturnUrl) {
// @codeCoverageIgnoreStart
$uriParameters['returnUrl'] = GeneralUtility::getIndpEnv('REQUEST_URI');
// @codeCoverageIgnoreEnd
}
return (string)GeneralUtility::makeInstance(UriBuilder::class)
->buildUriFromRoute('record_edit', $uriParameters);
}
/**
* @return string "plugin" or "module"
*/
public static function getPluginOrModuleString(): string
{
$string = 'plugin';
if (($GLOBALS['TYPO3_REQUEST'] ?? null) instanceof ServerRequestInterface
&& ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isBackend()
) {
$string = 'module';
}
return $string;
}
/**
* Get all GET/POST params without module name and token
*
* @return array
*/
protected static function getCurrentParameters(): array
{
$parameters = [];
$ignoreKeys = [
'M',
'moduleToken'
];
foreach ((array)GeneralUtility::_GET() as $key => $value) {
if (in_array($key, $ignoreKeys)) {
continue;
}
$parameters[$key] = $value;
}
return $parameters;
}
/**
* @param int $pageUid [optional] the current pageuid
* @return array
*/
public static function loadTS($pageUid = null)
{
$pageUid = ($pageUid && MathUtility::canBeInterpretedAsInteger($pageUid)) ? $pageUid : GeneralUtility::_GP('id');
$TSObj = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\TypoScript\\TemplateService');
$TSObj->tt_track = 0;
$TSObj->runThroughTemplates(GeneralUtility::makeInstance(RootlineUtility::class, $pageUid, '')->get());
$TSObj->generateConfig();
return $TSObj->setup;
}
}
@@ -0,0 +1,212 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Utility;
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Core\Utility\Exception\MissingArrayPathException;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
/**
* Class ConfigurationUtility
*/
class ConfigurationUtility extends AbstractUtility
{
const DEFAULT_CONFIGURATION = [
'_TypoScriptIncluded' => 0,
'_enable' => '',
'_enable.' => [],
'dataProcessors' => [],
'edit./forceValues./beforeAnyConfirmation.' => [],
'edit./redirect' => 'TEXT',
'edit./redirect.' => [],
'edit./requestRedirect' => 'TEXT',
'edit./requestRedirect.' => [],
'edit/fillEmailWithUsername' => '0',
'edit/notifyAdmin' => '',
'edit/email/notifyAdmin/receiver/email/value' => '',
'edit/misc/passwordSave' => '',
'invitation/misc/passwordSave' => '',
'invitation./email./invitationAdminNotify.' => [],
'invitation./email./invitationAdminNotifyStep1.' => [],
'invitation./email./invitationRefused.' => [],
'invitation./forceValues./beforeAnyConfirmation.' => [],
'invitation./redirect' => 'TEXT',
'invitation./redirect.' => [],
'invitation./redirectDelete' => 'TEXT',
'invitation./redirectDelete.' => [],
'invitation./redirectStep1' => 'TEXT',
'invitation./redirectStep1.' => [],
'invitation./redirectPasswordChanged' => 'TEXT',
'invitation./redirectPasswordChanged.' => [],
'invitation./requestRedirect' => 'TEXT',
'invitation./requestRedirect.' => [],
'invitation/fillEmailWithUsername' => '0',
'invitation/notifyAdmin' => 0,
'invitation/email/invitationAdminNotify/receiver/name/value' => 'femanager',
'invitation/email/invitationRefused/receiver/name/value' => 'femanager',
'invitation/notifyAdminStep1' => 0,
'invitation/email/invitationAdminNotifyStep1/receiver/name/value' => 'femanager',
'new./adminConfirmationRedirect' => '',
'new./email./createAdminConfirmation.' => [],
'new./email./createAdminNotify.' => [],
'new./email./createAdminNotify./receiver./email./value' => '',
'new./email./createAdminNotify./subject' => 'TEXT',
'new./email./createAdminNotify./subject.' => [],
'new./email./createUserConfirmation.' => [],
'new./email./createUserConfirmation./sender./email./value' => '',
'new./email./createUserConfirmation./sender./name./value' => '',
'new./email./createUserConfirmation./subject' => 'TEXT',
'new./email./createUserConfirmation./subject.' => [],
'new./email./createUserConfirmation./confirmUserConfirmation' => '0',
'new./email./createUserConfirmation./confirmUserConfirmationRefused' => '0',
'new./email./createUserConfirmation./confirmAdminConfirmation' => '0',
'new./email./createUserNotify.' => [],
'new./email./createUserNotify./sender./email./value' => '9999',
'new./email./createUserNotify./sender./name./value' => '9999',
'new./email./createUserNotify./subject' => 'TEXT',
'new./email./createUserNotify./subject.' => [],
'new./email./createUserNotifyRefused.' => [],
'new./fillEmailWithUsername' => 0,
'new./forceValues./beforeAnyConfirmation.' => [],
'new./forceValues./onAdminConfirmation.' => [],
'new./forceValues./onUserConfirmation.' => [],
'new./login' => '0',
'new./misc./passwordSave' => 0,
'new./notifyAdmin' => '',
'new./redirect' => 'TEXT',
'new./redirect.' => [],
'new./requestRedirect' => 'TEXT',
'new./requestRedirect.' => [],
'new./userConfirmationRedirect' => 'TEXT',
'new./userConfirmationRedirect.' => [],
'new/misc/passwordSave' => 0,
'persistence./storagePid' => '0',
'receiver./email' => 'TEXT',
'receiver./email.' => [],
'receiver./name' => 'TEXT',
'receiver./name.' => [],
'sender./email' => 'TEXT',
'sender./email.' => [],
'sender./name' => 'TEXT',
'sender./name.' => [],
'priority' => 'TEXT',
'priority.' => [],
];
/**
* @return bool
*/
public static function isDisableModuleActive(): bool
{
$configuration = self::getExtensionConfiguration();
return $configuration['disableModule'] === '1';
}
/**
* @return bool
*/
public static function isConfirmationModuleActive(): bool
{
$configuration = self::getExtensionConfiguration();
return $configuration['enableConfirmationModule'] === '1';
}
/**
* @return bool
*/
public static function isDisableLogActive(): bool
{
$configuration = self::getExtensionConfiguration();
return $configuration['disableLog'] === '1';
}
/**
* Get complete Typoscript or only a special value by a given path
*
* @param string $path "misc.uploadFolder" or empty for complete TypoScript array
* @return string
* @codeCoverageIgnore
*/
public static function getConfiguration(string $path = '')
{
$configurationManager = GeneralUtility::makeInstance(ConfigurationManagerInterface::class);
$typoscript = $configurationManager->getConfiguration(
ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS,
'Femanager',
'Pi1'
);
if (!empty($path)) {
$typoscript = ArrayUtility::getValueByPath($typoscript, $path, '.');
}
return $typoscript;
}
/**
* @return bool
* @codeCoverageIgnore
*/
public static function isBackendModuleFilterUserConfirmation(): bool
{
$config = BackendUserUtility::getBackendUserAuthentication()->getTSConfig(
)['tx_femanager.']['UserBackend.']['confirmation.']['filter.']['userConfirmation'] ?? false;
return (bool)$config;
}
/**
* @return bool
* @codeCoverageIgnore
*/
public static function IsResendUserConfirmationRequestActive(): bool
{
$config = BackendUserUtility::getBackendUserAuthentication()->getTSConfig(
)['tx_femanager.']['UserBackend.']['confirmation.']['ResendUserConfirmationRequest'] ?? false;
return (bool)$config;
}
public static function IsCreateUserNotifyActive($config): bool
{
if (ConfigurationUtility::getValue(
'new/email/createUserNotify/sender/email/value',
$config
) && ConfigurationUtility::getValue(
'new./email./createUserNotify./sender./name./value',
$config
)) {
return true;
}
return false;
}
/**
* @return mixed
*/
public static function getDefaultConfiguration($key)
{
return self::DEFAULT_CONFIGURATION[$key] ?? null;
}
public static function getValue($key, $config)
{
try {
return ArrayUtility::getValueByPath($config, $key);
} catch (MissingArrayPathException $ex) {
return self::getDefaultConfiguration($key);
}
}
public static function notifyAdminAboutEdits($settings)
{
return self::getValue('edit/email/notifyAdmin/_enable/value', $settings)
&& (
self::getValue('edit/email/notifyAdmin/receiver/email/value', $settings)
|| self::getValue('edit/notifyAdmin', $settings)
);
}
}
@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Utility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
/**
* Class FileUtility
*/
class FileUtility extends AbstractUtility
{
/**
* @param string $path Absolute path
* @throws \Exception
*/
public static function createFolderIfNotExists(string $path)
{
if (!is_dir($path) && !GeneralUtility::mkdir($path)) {
throw new \UnexpectedValueException(
'Folder ' . self::getRelativeFolderFromAbsolutePath($path) . ' does not exists and can not be created!',
1516373962125
);
}
}
/**
* Get relative path from absolute path, but don't touch if it's already a relative path
*
* @param string $path
* @return string
*/
public static function getRelativeFolderFromAbsolutePath(string $path): string
{
if (PathUtility::isAbsolutePath($path)) {
$path = PathUtility::getRelativePathTo($path);
}
return $path;
}
}
@@ -0,0 +1,148 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Utility;
use In2code\Femanager\Domain\Model\User;
use In2code\Femanager\Domain\Model\UserGroup;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Class FrontendUtility
*/
class FrontendUtility extends AbstractUtility
{
/**
* @var array
*/
protected static $pluginNames = [
'tx_femanager_pi1',
'tx_femanager_pi2'
];
/**
* Get current pid
*
* @return int
*/
public static function getCurrentPid(): int
{
return (int)self::getTypoScriptFrontendController()->id;
}
/**
* Get frontend language uid
*
* @return int
*/
public static function getFrontendLanguageUid(): int
{
$languageUid = 0;
$languageAspect = GeneralUtility::makeInstance(Context::class)->getAspect('language');
$languageUid = $languageAspect->getId();
return $languageUid;
}
/**
* @return string
*/
public static function getCharset(): string
{
return self::getTypoScriptFrontendController()->metaCharset;
}
/**
* @return string
* @codeCoverageIgnore
*/
public static function getUriToCurrentPage(): string
{
$contentObject = ObjectUtility::getContentObject();
$configuration = [
'parameter' => self::getCurrentPid()
];
return $contentObject->typoLink_URL($configuration);
}
/**
* Set object properties from forceValues in TypoScript
*
* @param User $user
* @param array $settings
* @return User $object
* @codeCoverageIgnore
*/
public static function forceValues(User $user, array $settings)
{
foreach ((array)$settings as $field => $config) {
$config = null;
if (stristr($field, '.')) {
continue;
}
// value to set
$value = self::getContentObject()->cObjGetSingle($settings[$field], $settings[$field . '.']);
self::forceValue($user, $field, $value);
}
return $user;
}
/**
* Set single object property from forceValues in TypoScript
*
* @param User $user
* @param string $field
* @param any $value
*/
public static function forceValue(User $user, string $field, $value): void
{
if ($field === 'usergroup') {
// need objectstorage for usergroup field
$user->removeAllUsergroups();
$values = GeneralUtility::trimExplode(',', $value, true);
$userGroupRepository = self::getUserGroupRepository();
foreach ($values as $usergroupUid) {
/** @var UserGroup $usergroup */
$usergroup = $userGroupRepository->findByUid($usergroupUid);
$user->addUsergroup($usergroup);
}
} else {
// set value
$setterMethod = 'set' . str_replace(' ', '', ucwords(str_replace('_', ' ', $field)));
if (method_exists($user, $setterMethod)) {
$user->{$setterMethod}($value);
}
}
}
/**
* @return string
*/
public static function getControllerName(): string
{
$controllerName = '';
foreach (self::$pluginNames as $pluginName) {
$variables = GeneralUtility::_GPmerged($pluginName);
if (!empty($variables['controller'])) {
$controllerName = $variables['controller'];
}
}
return $controllerName;
}
/**
* @return string
*/
public static function getActionName(): string
{
$actionName = '';
foreach (self::$pluginNames as $pluginName) {
$variables = GeneralUtility::_GPmerged($pluginName);
if (!empty($variables['action'])) {
$actionName = $variables['action'];
}
}
return $actionName;
}
}
@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Utility;
use In2code\Femanager\Domain\Model\User;
/**
* Class HashUtility
*/
class HashUtility extends AbstractUtility
{
/**
* Check if given hash is correct
*
* @param string $hash
* @param User $user
* @return bool
*/
public static function validHash(string $hash, User $user, string $suffix = '')
{
return self::createHashForUser($user, $suffix) === $hash;
}
/**
* Create hash for a user
*
* @param User $user
* @return string
*/
public static function createHashForUser(User $user, string $suffix = '')
{
return self::hashString($user->getUsername() . $suffix);
}
/**
* Create Hash from String and TYPO3 Encryption Key (if available)
*
* @param string $string Any String to hash
* @param int $length Hash Length
* @return string $hash Hashed String
*/
protected static function hashString($string, $length = 16)
{
return substr(md5($string . self::getEncryptionKey()), 0, $length);
}
}
@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Utility;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility as ExtbaseLocalizationUtility;
/**
* Class LocalizationUtility
* @codeCoverageIgnore
*/
class LocalizationUtility
{
/**
* Returns the localized label of the LOCAL_LANG key, but prefill extensionName
*
* @param string $key The key from the LOCAL_LANG array for which to return the value.
* @param string $extensionName The name of the extension
* @param array $arguments the arguments of the extension, being passed over to vsprintf
* @return string|null
*/
public static function translate($key, $extensionName = 'femanager', $arguments = null)
{
return ExtbaseLocalizationUtility::translate($key, $extensionName, $arguments);
}
/**
* Get locallang translation with key prefix tx_femanager_domain_model_log.state.
*
* @param int $state
* @return string|null
*/
public static function translateByState($state)
{
return self::translate('tx_femanager_domain_model_log.state.' . $state);
}
}
@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Utility;
use In2code\Femanager\Domain\Model\Log;
use In2code\Femanager\Domain\Model\User;
use In2code\Femanager\Domain\Repository\LogRepository;
use In2code\Femanager\Event\UserLogEvent;
use Psr\EventDispatcher\EventDispatcherInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Class LogUtility
*/
class LogUtility
{
/**
* @var LogRepository
*/
private $logRepository;
/**
* @var EventDispatcherInterface
*/
private $eventDispatcher;
public function __construct(LogRepository $logRepository, EventDispatcherInterface $eventDispatcher)
{
$this->logRepository = $logRepository;
$this->eventDispatcher = $eventDispatcher;
}
/**
* @param int $state State to log
* @param User $user Related User
* @param array $additionalProperties for individual logging
* @codeCoverageIgnore
*/
public function log($state, User $user, array $additionalProperties = [])
{
if (!ConfigurationUtility::isDisableLogActive()) {
$log = GeneralUtility::makeInstance(Log::class);
$log->setTitle(LocalizationUtility::translateByState($state));
$log->setState($state);
$log->setUser($user);
$this->logRepository->add($log);
}
$this->eventDispatcher->dispatch(new UserLogEvent($user, $state, $additionalProperties));
}
}
@@ -0,0 +1,99 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Utility;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
use TYPO3\CMS\Extbase\Reflection\Exception\PropertyNotAccessibleException;
use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
/**
* Class ObjectUtility
*/
class ObjectUtility extends AbstractUtility
{
/**
* @param string $tableName
* @return QueryBuilder
*/
public static function getQueryBuilder(string $tableName): QueryBuilder
{
return GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($tableName);
}
/**
* @return ContentObjectRenderer
*/
public static function getContentObject(): ContentObjectRenderer
{
return GeneralUtility::makeInstance(ContentObjectRenderer::class);
}
/**
* Checks if object was changed or not
*
* @param object $object
* @return bool
* @codeCoverageIgnore
*/
public static function isDirtyObject($object): bool
{
foreach (array_keys($object->_getProperties()) as $propertyName) {
try {
$property = ObjectAccess::getProperty($object, $propertyName);
} catch (PropertyNotAccessibleException $e) {
// if property can not be accessed
continue;
}
/**
* std::Property (string, int, etc..),
* PHP-Objects (DateTime, RecursiveIterator, etc...),
* TYPO3-Objects (user, page, etc...)
*/
if (!$property instanceof ObjectStorage) {
if ($object->_isDirty($propertyName)) {
return true;
}
} else {
/**
* ObjectStorage
*/
if ($property->_isDirty()) {
return true;
}
}
}
return false;
}
/**
* Implode subjobjects on a property (example for usergroups: "ug1, ug2, ug3")
*
* @param ObjectStorage $objectStorage
* @param string $property
* @param string $glue
* @return string
*/
public static function implodeObjectStorageOnProperty(
ObjectStorage $objectStorage,
string $property = 'uid',
string $glue = ', '
): string {
$values = [];
if (!empty($objectStorage)) {
foreach ($objectStorage as $object) {
try {
$values[] = ObjectAccess::getProperty($object, $property);
} catch (\Exception $exception) {
unset($exception);
}
}
}
return implode($glue, $values);
}
}
@@ -0,0 +1,157 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Utility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Class StringUtility
*/
class StringUtility extends AbstractUtility
{
/**
* Only allowed a-z, A-Z, 0-9, -, .
* Others will be replaced
*
* @param string $filename
* @param string $replace
* @return string
*/
public static function cleanString($filename, $replace = '_')
{
return preg_replace('/[^a-zA-Z0-9-\.]/', $replace, trim($filename));
}
/**
* Read values between brackets
*
* test(1,2,3) => 1,2,3
*
* @param string $value
* @return string
*/
public static function getValuesInBrackets($value)
{
preg_match_all('/\(.*?\)/i', $value, $result);
return str_replace(['(', ')'], '', $result[0][0]);
}
/**
* Read values before brackets
*
* test(1,2,3) => test
*
* @param string $value
* @return string
*/
public static function getValuesBeforeBrackets($value)
{
$valueParts = GeneralUtility::trimExplode('(', $value, true);
return $valueParts[0];
}
/**
* Check if string starts with another string
*
* @param string $haystack
* @param string $needle
* @return bool
*/
public static function startsWith($haystack, $needle)
{
return stristr($haystack, $needle) && strrpos($haystack, $needle, -strlen($haystack)) !== false;
}
/**
* Check if string ends with another string
*
* @param string $haystack
* @param string $needle
* @return string
*/
public static function endsWith($haystack, $needle)
{
return stristr($haystack, $needle) && strlen($haystack) - strlen($needle) === strpos($haystack, $needle);
}
/**
* Create array for swiftmailer
* sender and receiver mail/name combination with fallback
*
* @param string $emailString String with separated emails (splitted by \n)
* @param string $name Name for every email name combination
* @return array $mailArray
*/
public static function makeEmailArray($emailString, $name = 'femanager')
{
$emails = GeneralUtility::trimExplode(PHP_EOL, $emailString, true);
$mailArray = [];
foreach ($emails as $email) {
if (GeneralUtility::validEmail($email)) {
$mailArray[$email] = $name;
}
}
return $mailArray;
}
/**
* createRandomFileName
*
* @param int $length
* @param bool $addUpperCase
* @param bool $addSpecialCharacters
* @return string
*/
public static function getRandomString($length = 32, $addUpperCase = true, $addSpecialCharacters = true)
{
$characters = self::getNumbersString() . self::getCharactersString();
if ($addUpperCase) {
$characters .= self::getUpperCharactersString();
}
if ($addSpecialCharacters) {
$characters .= '#+*&%$§()[]{}!.:-_,;';
}
$string = '';
for ($i = 0; $i < $length; $i++) {
$key = mt_rand(0, strlen($characters) - 1);
$string .= $characters[$key];
}
return $string;
}
/**
* @return string "0123456789"
*/
public static function getNumbersString()
{
return implode('', range(0, 9));
}
/**
* @return string "abcdefghijklmnopqrstuvwxyz"
*/
public static function getCharactersString()
{
return implode('', range('a', 'z'));
}
/**
* @return string "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
*/
public static function getUpperCharactersString()
{
return implode('', range('A', 'Z'));
}
/**
* Remove double slashes from URI but don't touch the protocol (http:// e.g.)
*
* @param string $string
* @return string
*/
public static function removeDoubleSlashesFromUri($string)
{
return preg_replace('~([^:]|^)(/{2,})~', '$1/', $string);
}
}
@@ -0,0 +1,129 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Utility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Fluid\View\StandaloneView;
/**
* Class TemplateUtility
* @codeCoverageIgnore
*/
class TemplateUtility extends AbstractUtility
{
/**
* Get absolute path for templates with fallback
* In case of multiple paths this will just return the first one.
* See getTemplateFolders() for an array of paths.
*
* @param string $part "template", "partial", "layout"
* @return string
* @see getTemplateFolders()
*/
public static function getTemplateFolder($part = 'template')
{
$matches = self::getTemplateFolders($part);
return !empty($matches) ? $matches[0] : '';
}
/**
* Get absolute paths for templates with fallback
* Returns paths from *RootPaths and *RootPath and "hardcoded"
* paths pointing to the EXT:femanager-resources.
*
* @param string $part "template", "partial", "layout"
* @param bool $returnAllPaths Default: FALSE, If FALSE only paths
* for the first configuration (Paths, Path, hardcoded)
* will be returned. If TRUE all (possible) paths will be returned.
* @return array
*/
public static function getTemplateFolders($part = 'template', $returnAllPaths = false)
{
$templatePaths = [];
$configuration = self::getConfigurationManager()
->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK, 'femanager');
if (!empty($configuration['view'][$part . 'RootPaths'] ?? '')) {
$templatePaths = $configuration['view'][$part . 'RootPaths'] ?? '';
$templatePaths = array_values($templatePaths);
}
if ($returnAllPaths || empty($templatePaths)) {
$path = $configuration['view'][$part . 'RootPath'] ?? '';
if (!empty($path)) {
$templatePaths[] = $path;
}
}
if ($returnAllPaths || empty($templatePaths)) {
$templatePaths[] = 'EXT:femanager/Resources/Private/' . ucfirst($part) . 's/';
}
$templatePaths = array_unique($templatePaths);
$absolutePaths = [];
foreach ($templatePaths as $templatePath) {
$absolutePaths[] = GeneralUtility::getFileAbsFileName($templatePath);
}
return $absolutePaths;
}
/**
* Return path and filename for a file or path.
* Only the first existing file/path will be returned.
* respect *RootPaths and *RootPath
*
* @param string $pathAndFilename e.g. Email/Name.html
* @param string $part "template", "partial", "layout"
* @return string Filename/path
*/
public static function getTemplatePath($pathAndFilename, $part = 'template')
{
$matches = self::getTemplatePaths($pathAndFilename, $part);
return !empty($matches) ? end($matches) : '';
}
/**
* Return path and filename for one or many files/paths.
* Only existing files/paths will be returned.
* respect *RootPaths and *RootPath
*
* @param string $pathAndFilename Path/filename (Email/Name.html) or path
* @param string $part "template", "partial", "layout"
* @return array All existing matches found
*/
public static function getTemplatePaths($pathAndFilename, $part = 'template')
{
$pathAndFilenames = [];
$absolutePaths = self::getTemplateFolders($part, true);
foreach ($absolutePaths as $absolutePath) {
if (file_exists($absolutePath . $pathAndFilename)) {
$pathAndFilenames[] = $absolutePath . $pathAndFilename;
}
}
return $pathAndFilenames;
}
/**
* Get standaloneview with default properties
*
* @param string $controllerName
* @param string $extensionName
* @param string $pluginName
* @param string $format
* @return StandaloneView
*/
public static function getDefaultStandAloneView(
$controllerName = 'New',
$extensionName = 'Femanager',
$pluginName = 'Pi1',
$format = 'html'
) {
/** @var StandaloneView $standAloneView */
$standAloneView = GeneralUtility::makeInstance(StandaloneView::class);
$standAloneView->getRequest()->setControllerExtensionName($extensionName);
$standAloneView->getRequest()->setPluginName($pluginName);
$standAloneView->getRequest()->setControllerName($controllerName);
$standAloneView->setFormat($format);
$standAloneView->setLayoutRootPaths(TemplateUtility::getTemplateFolders('layout'));
$standAloneView->setPartialRootPaths(TemplateUtility::getTemplateFolders('partial'));
return $standAloneView;
}
}
@@ -0,0 +1,362 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\Utility;
use In2code\Femanager\Domain\Model\User;
use In2code\Femanager\Domain\Model\UserGroup;
use In2code\Femanager\Domain\Repository\UserRepository;
use TYPO3\CMS\Core\Crypto\PasswordHashing\Argon2iPasswordHash;
use TYPO3\CMS\Core\Crypto\PasswordHashing\BcryptPasswordHash;
use TYPO3\CMS\Core\Crypto\PasswordHashing\BlowfishPasswordHash;
use TYPO3\CMS\Core\Crypto\PasswordHashing\InvalidPasswordHashException;
use TYPO3\CMS\Core\Crypto\PasswordHashing\Md5PasswordHash;
use TYPO3\CMS\Core\Crypto\PasswordHashing\PasswordHashFactory;
use TYPO3\CMS\Core\Crypto\PasswordHashing\Pbkdf2PasswordHash;
use TYPO3\CMS\Core\Crypto\PasswordHashing\PhpassPasswordHash;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
/**
* Class UserUtility
* @codeCoverageIgnore
*/
class UserUtility extends AbstractUtility
{
/**
* Return current logged in fe_user
*
* @return User|null
*/
public static function getCurrentUser()
{
if (self::getPropertyFromUser() !== null) {
$userRepository = GeneralUtility::makeInstance(UserRepository::class);
return $userRepository->findByUid((int)self::getPropertyFromUser());
}
return null;
}
/**
* Get property from current logged in Frontend User
*
* @param string $propertyName
* @return string|null
*/
public static function getPropertyFromUser($propertyName = 'uid')
{
if (!empty(self::getTypoScriptFrontendController()->fe_user->user[$propertyName])) {
return self::getTypoScriptFrontendController()->fe_user->user[$propertyName];
}
return null;
}
/**
* Get Usergroups from current logged in user
*
* array(
* 1,
* 5,
* 7
* )
*
* @return array
*/
public static function getCurrentUsergroupUids()
{
$currentLoggedInUser = self::getCurrentUser();
$usergroupUids = [];
if ($currentLoggedInUser !== null) {
foreach ($currentLoggedInUser->getUsergroup() as $usergroup) {
$usergroupUids[] = $usergroup->getUid();
}
}
return $usergroupUids;
}
/**
* Autogenerate username and password if it's empty
*
* @param User $user
* @return User $user
*/
public static function fallbackUsernameAndPassword(User $user)
{
$settings = self::getConfigurationManager()->getConfiguration(
ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS,
'Femanager',
'Pi1'
);
if (isset($settings['new']['misc']['autogenerate'])) {
$autogenerateSettings = $settings['new']['misc']['autogenerate'];
if (!$user->getUsername()) {
$user->setUsername(
StringUtility::getRandomString(
$autogenerateSettings['username']['length'],
$autogenerateSettings['username']['addUpperCase'],
$autogenerateSettings['username']['addSpecialCharacters']
)
);
if ($user->getEmail()) {
$user->setUsername($user->getEmail());
}
}
if (!$user->getPassword()) {
$password = StringUtility::getRandomString(
$autogenerateSettings['password']['length'],
$autogenerateSettings['password']['addUpperCase'],
$autogenerateSettings['password']['addSpecialCharacters']
);
$user->setPassword($password);
$user->setPasswordAutoGenerated($password);
}
}
return $user;
}
/**
* @param User $user
* @param array $settings
* @return User
*/
public static function takeEmailAsUsername(User $user, array $settings)
{
if (ConfigurationUtility::getValue('new/fillEmailWithUsername', $settings) === '1') {
$user->setEmail($user->getUsername());
}
return $user;
}
/**
* Overwrite usergroups from user by flexform settings
*
* @param User $user
* @param array $settings
* @param string $controllerName
* @return User $object
*/
public static function overrideUserGroup(User $user, $settings, $controllerName = 'new')
{
if (!empty($settings[$controllerName]['overrideUserGroup'])) {
$user->removeAllUsergroups();
$usergroupUids = GeneralUtility::trimExplode(',', $settings[$controllerName]['overrideUserGroup'], true);
foreach ($usergroupUids as $usergroupUid) {
/** @var UserGroup $usergroup */
$usergroup = self::getUserGroupRepository()->findByUid($usergroupUid);
$user->addUsergroup($usergroup);
}
}
return $user;
}
/**
* Convert password to Argon2i, Bcrypt, Pbkdf2, Phpass, Blowfish or Md5 hash
*
* @param User $user
* @param string $method
* @throws InvalidPasswordHashException
*/
public static function convertPassword(User $user, $method)
{
if (array_key_exists('password', UserUtility::getDirtyPropertiesFromUser($user))) {
self::hashPassword($user, $method);
}
}
/**
* Hash a password from $user->getPassword()
*
* @param User $user
* @param string $method "Argon2i", "Bcrypt", "Pbkdf2", "Phpass", "Blowfish", "md5" or "none" ("sha1" for TYPO3 V8)
* @throws InvalidPasswordHashException
*/
public static function hashPassword(User &$user, $method)
{
$hashInstance = false;
$saltedHashPassword = $user->getPassword();
/** @var PasswordHashFactory $passwordHashFactory */
$passwordHashFactory = GeneralUtility::makeInstance(PasswordHashFactory::class);
switch ($method) {
case 'Argon2i':
$hashInstance = GeneralUtility::makeInstance(Argon2iPasswordHash::class);
break;
case 'Bcrypt':
$hashInstance = GeneralUtility::makeInstance(BcryptPasswordHash::class);
break;
case 'Pbkdf2':
$hashInstance = GeneralUtility::makeInstance(Pbkdf2PasswordHash::class);
break;
case 'Phpass':
$hashInstance = GeneralUtility::makeInstance(PhpassPasswordHash::class);
break;
case 'Blowfish':
$hashInstance = GeneralUtility::makeInstance(BlowfishPasswordHash::class);
break;
case 'md5':
$hashInstance = GeneralUtility::makeInstance(Md5PasswordHash::class);
break;
case 'none':
break;
default:
$hashInstance = $passwordHashFactory->getDefaultHashInstance(TYPO3_MODE);
}
if ($hashInstance === false) {
$user->setPassword($saltedHashPassword);
} else {
$user->setPassword($hashInstance->getHashedPassword($saltedHashPassword));
}
}
/**
* Get changed properties (compare two objects with same getter methods)
*
* @param User $changedObject
* @return array
* [firstName][old] = Alex
* [firstName][new] = Alexander
*/
public static function getDirtyPropertiesFromUser(User $changedObject)
{
$dirtyProperties = [];
$ignoreProperties = [
'txFemanagerChangerequest',
'ignoreDirty',
'isOnline',
'lastlogin'
];
foreach ($changedObject->_getCleanProperties() as $propertyName => $oldPropertyValue) {
if (method_exists($changedObject, 'get' . ucfirst($propertyName))
&& !in_array($propertyName, $ignoreProperties)
) {
$newPropertyValue = $changedObject->{'get' . ucfirst($propertyName)}();
if (!is_object($oldPropertyValue) || !is_object($newPropertyValue)) {
if ($oldPropertyValue !== $newPropertyValue) {
$dirtyProperties[$propertyName]['old'] = $oldPropertyValue;
$dirtyProperties[$propertyName]['new'] = $newPropertyValue;
}
} else {
if (get_class($oldPropertyValue) === 'DateTime') {
/** @var $oldPropertyValue \DateTime */
/** @var $newPropertyValue \DateTime */
if ($oldPropertyValue->getTimestamp() !== $newPropertyValue->getTimestamp()) {
$dirtyProperties[$propertyName]['old'] = $oldPropertyValue->getTimestamp();
$dirtyProperties[$propertyName]['new'] = $newPropertyValue->getTimestamp();
}
} elseif (get_class($oldPropertyValue) === ObjectStorage::class) {
$titlesOld = ObjectUtility::implodeObjectStorageOnProperty($oldPropertyValue);
$titlesNew = ObjectUtility::implodeObjectStorageOnProperty($newPropertyValue);
if ($titlesOld !== $titlesNew) {
$dirtyProperties[$propertyName]['old'] = $titlesOld;
$dirtyProperties[$propertyName]['new'] = $titlesNew;
}
} else {
$uidOld = ObjectAccess::getProperty($oldPropertyValue, 'uid');
$uidNew = ObjectAccess::getProperty($newPropertyValue, 'uid');
if ($uidOld !== $uidNew) {
$dirtyProperties[$propertyName]['old'] = $uidOld;
$dirtyProperties[$propertyName]['new'] = $uidNew;
}
}
}
}
}
return $dirtyProperties;
}
/**
* overwrite user with old values and xml with new values
*
* @param User $user
* @param array $dirtyProperties
* @return User $user
*/
public static function rollbackUserWithChangeRequest($user, $dirtyProperties)
{
$existingProperties = $user->_getCleanProperties();
// reset old values
$user->setUserGroup($existingProperties['usergroup']);
foreach ($dirtyProperties as $propertyName => $propertyValue) {
$propertyValue = null;
$user->{'set' . ucfirst($propertyName)}($existingProperties[$propertyName]);
}
// store changes as xml in field fe_users.tx_femanager_changerequest
$user->setTxFemanagerChangerequest(GeneralUtility::array2xml($dirtyProperties, '', 0, 'changes'));
return $user;
}
/**
* Remove FE Session to a given user
*
* @param User $user
*/
public static function removeFrontendSessionToUser(User $user)
{
self::getConnectionPool()->getConnectionForTable('fe_sessions')->delete(
'fe_sessions',
['ses_userid' => (int)$user->getUid()]
);
}
/**
* Check if FE Session exists
*
* @param User $user
* @return bool
*/
public static function checkFrontendSessionToUser(User $user)
{
$queryBuilder = self::getConnectionPool()->getQueryBuilderForTable('fe_sessions');
$row = $queryBuilder->select('ses_id')
->from('fe_sessions')
->where(
$queryBuilder->expr()->eq('ses_userid', (int)$user->getUid())
)
->execute()
->fetch();
return !empty($row['ses_id']);
}
/**
* Login FE-User
*
* @param User $user
* @param string|null $storagePids
*/
public static function login(User $user, $storagePids = null)
{
// ensure a session cookie is set (in case there is no session yet)
$GLOBALS['TSFE']->fe_user->setAndSaveSessionData('dummy', true);
// create the session (destroys all existing session data in the session backend!)
$GLOBALS['TSFE']->fe_user->createUserSession(['uid' => (int)$user->getUid()]);
// re-fetch the session from the database so that the internal 'userSession' property of TSFE gets updated
$GLOBALS['TSFE']->fe_user->fetchUserSession();
// write the session data again to the session backend; preserves what was there before!!
$GLOBALS['TSFE']->fe_user->setAndSaveSessionData('dummy', true);
}
}
@@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
namespace In2code\Femanager\ViewHelpers\Be;
use TYPO3\CMS\Fluid\Core\Rendering\RenderingContext;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Exception as FluidViewHelperException;
/**
* Class GetClassNameOnActionViewHelper
*/
class GetClassNameOnActionViewHelper extends AbstractViewHelper
{
/**
* Return className if actionName fits to current action
*
* @return string
* @throws FluidViewHelperException
*/
public function render(): string
{
$actionName = $this->arguments['actionName'];
$className = $this->arguments['className'];
$fallbackClassName = $this->arguments['fallbackClassName'];
if ($this->getCurrentActionName() === $actionName) {
return $className;
}
return $fallbackClassName;
}
/**
* Return the current action name from the controller context
*
* @return string
* @throws FluidViewHelperException
*/
protected function getCurrentActionName(): string
{
if (! $this->renderingContext instanceof RenderingContext) {
throw new FluidViewHelperException(
'Something went wrong; RenderingContext should be available in ViewHelper',
1638341674
);
}
return $this->renderingContext->getRequest()->getControllerActionName();
}
/**
* Register all arguments for this viewhelper
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('actionName', 'string', 'action name to compare with current action', true);
$this->registerArgument('className', 'string', 'classname that should be returned if action fits', false, ' btn-info');
$this->registerArgument('fallbackClassName', 'string', 'fallback classname if action does not fit', false, '');
}
}
Loaded 100 of 2984 files, more files were not shown because too many files have changed in this diff. Show more