Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • alexander.vorwerk/studip
  • hochschule-wismar/stud-ip
  • tleilax/studip
  • marcus/studip
  • manschwa/studip
  • eberhardt/studip
  • uol/studip
  • pluta/studip
  • thienel/extern-uni-b
  • studip/studip
  • strohm/studip
  • uni-osnabrueck/studip
  • FloB/studip
  • universit-t-rostock/studip
  • Robinyyy/studip
  • jakob.diel/studip
  • HyperSpeeed/studip
  • ann/studip
  • nod3zer0/stud-ip-siple-saml-php-plugin
  • erik.hillmann/studip
20 results
Select Git revision
  • 135-translatable-study-areas
  • ci-opt
  • course-members-export-as-word
  • data-vue-app
  • docblocks-for-magic-methods
  • extensible-sorm-action-parameters
  • http-client-and-factories
  • icon-renewal
  • jsonapi-atomic-operations
  • jsonapi-mvv-routes
  • main
  • pipeline-improvements
  • rector
  • sorm-configuration-trait
  • studip-rector
  • tic-2341
  • vueify-messages
  • webpack-optimizations
18 results
Show changes
Commits on Source (1311)
Showing
with 1277 additions and 406 deletions
......@@ -20,3 +20,6 @@ block_comment_start = /*
block_comment_end = */
line_comment = //
quote_type = single
[*.yml]
indent_size = 2
\ No newline at end of file
# MYSQL_HOST=""
# MYSQL_USER=""
# MYSQL_PASSWORD=""
# MYSQL_DATABASE=""
# Enable the next line to display the debug bar in development mode
# DEBUG_BAR=1
# Enable the following to allow opening files from exception displays in your
# editor. Beware: You need to provide a full path for prefix your files since
# the exception only displays the relative path.
#
# Variables being substituted: %{file} and %{line}
#
# EDITOR_URL="phpstorm://open?file=<path-to-your-studip>/%{file}&line=%{line}"
# EDITOR_URL="vscode://file/<path-to-your-studip>/%{file}:%{line}:0
# STUDIP_CACHING_ENABLE=""
# STUDIP_CACHE_IS_SESSION_STORAGE=""
# STUDIP_ENV=""
# STUDIP_MAIL_TRANSPORT=""
# STUDIP_PLUGINS_UPLOAD_ENABLE=""
## You may also change configuration settings from the environment. To do so, prefix the configuration name with
## "STUDIP_CONFIG_". Example for changing the site name:
#
# STUDIP_CONFIG_UNI_NAME_CLEAN="new uni name"
{
"parserOptions": {
"parser": "@typescript-eslint/parser",
"sourceType": "module"
},
"env": {
"browser": true,
"es6": true
},
"extends": [
"eslint:recommended",
"plugin:vue/essential",
"@vue/eslint-config-typescript"
],
"globals": {
"STUDIP": "writable",
"CKEDITOR": "writable",
"$": "writable",
"_": "writable",
"jQuery": "writable"
},
"plugins": [
"vue"
],
"rules": {
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": "off",
"no-async-promise-executor": "error",
"no-await-in-loop": "error",
"no-promise-executor-return": "error",
"require-atomic-updates": "error",
"max-nested-callbacks": ["error", 4],
"no-return-await": "error",
"prefer-promise-reject-errors": "error",
"vue/multi-word-component-names": "off"
}
}
......@@ -23,6 +23,7 @@ data/oer_logos/*
data/upload_doc/*
public/.htaccess
public/.rnd
public/assets/javascripts/*.js
public/assets/javascripts/*.js.map
public/assets/stylesheets/*.css
......
image: studip/studip:tests-php8.3
image: studip/studip:tests-php8.1
variables:
FF_NETWORK_PER_BUILD: 1
......@@ -17,7 +17,7 @@ variables:
# Use faster docker driver
DOCKER_DRIVER: overlay2
# Images
NODE_IMAGE: node:16-slim
NODE_IMAGE: node:18-slim
# Directories
CACHE_DIR: .caches
REPORT_DIR: .reports
......@@ -29,6 +29,7 @@ stages:
- checks
- analyse
- test
- cache
- packaging
- release
......@@ -66,8 +67,11 @@ stages:
.definitions:
mariadb-service: &mariadb-service
- name: mariadb
command: [ "--sql_mode=","--character-set-client=utf8","--character-set-server=utf8","--collation-server=utf8_unicode_ci"]
- name: mariadb:10.2.7
command: [ "--sql_mode=","--character-set-client=utf8","--character-set-server=utf8","--collation-server=utf8_unicode_ci"]
mysql-service: &mysql-service
- name: mysql:8.0
command: [ "--sql_mode=","--character-set-server=utf8mb4","--collation-server=utf8mb4_general_ci"]
build-composer:
stage: build
......@@ -107,7 +111,8 @@ lint-php:
- COMPOSER_MEMORY_LIMIT=-1
composer exec phplint
--
--json $PHPLINT_JSON_REPORT
--output=$PHPLINT_JSON_REPORT
--format=json
--cache=$CACHE_LOCATION
after_script:
- ./.gitlab/scripts/convert-phplint-report $PHPLINT_JSON_REPORT > $PHPLINT_CODE_QUALITY_REPORT
......@@ -115,14 +120,14 @@ lint-php:
reports:
codequality: $PHPLINT_CODE_QUALITY_REPORT
lint-php-7.4:
image: studip/studip:tests-php8.2
lint-php-8.3:
image: studip/studip:tests-php8.3
stage: checks
needs: [build-composer]
variables:
CACHE_LOCATION: $CACHE_DIR/phplint-cache
PHPLINT_JSON_REPORT: $REPORT_DIR/phplint-report-7.4.json
PHPLINT_CODE_QUALITY_REPORT: $REPORT_DIR/phplint-codequality-7.4.json
PHPLINT_JSON_REPORT: $REPORT_DIR/phplint-report-8.3.json
PHPLINT_CODE_QUALITY_REPORT: $REPORT_DIR/phplint-codequality-8.3.json
interruptible: true
cache:
- *composer-cache
......@@ -137,7 +142,8 @@ lint-php-7.4:
- COMPOSER_MEMORY_LIMIT=-1
composer exec phplint
--
--json $PHPLINT_JSON_REPORT
--output=$PHPLINT_JSON_REPORT
--format=json
--cache=$CACHE_LOCATION
after_script:
- ./.gitlab/scripts/convert-phplint-report $PHPLINT_JSON_REPORT > $PHPLINT_CODE_QUALITY_REPORT
......@@ -159,17 +165,11 @@ lint-js:
interruptible: true
before_script:
- *mkdir-reports
- npm install -g npm@7
- npm install
--no-save --no-audit --no-fund
--loglevel=error
eslint eslint-formatter-gitlab
- npm ci --prefer--offline
script:
- npx eslint
--ext .js,.vue
- npm run lint-js --
--cache --cache-location $CACHE_LOCATION
--format gitlab
resources/assets/javascripts resources/vue
artifacts:
reports:
codequality: $ESLINT_CODE_QUALITY_REPORT
......@@ -183,24 +183,20 @@ lint-css:
STYLELINT_CODE_QUALITY_REPORT: $REPORT_DIR/stylelint-codequality.json
interruptible: true
cache:
- key: "$CI_JOB_NAME_SLUG:CI_COMMIT_REF_SLUG"
- key: "CI_COMMIT_REF_SLUG-lint-css"
paths:
- $CACHE_LOCATION
policy: pull-push
when: always
before_script:
- *mkdir-caches
- *mkdir-reports
- npm install
--no-save --no-audit --no-fund
--loglevel=error
stylelint@^14.9.1
stylelint-config-standard@^26.0.0
stylelint-formatter-gitlab
script:
- npx
stylelint
- npm run lint-css --
--cache --cache-location $CACHE_LOCATION
--custom-formatter=node_modules/stylelint-formatter-gitlab
--custom-formatter=node_modules/stylelint-formatter-gitlab
--output-file $STYLELINT_CODE_QUALITY_REPORT
resources/assets/stylesheets
artifacts:
reports:
codequality: $STYLELINT_CODE_QUALITY_REPORT
......@@ -211,9 +207,7 @@ phpstan:
variables:
CACHE_LOCATION: $CACHE_DIR/phpstan
PHPSTAN_CODE_QUALITY_REPORT: $REPORT_DIR/phpstan-codequality.json
allow_failure: true
interruptible: true
when: manual
cache:
- *composer-cache
- key: "$CO_JOB_NAME_SLUG:$CI_COMMIT_REF_SLUG"
......@@ -223,12 +217,12 @@ phpstan:
- *mkdir-caches
- *mkdir-reports
- *install-composer
- 'echo "includes:\n - phpstan.neon.dist\n\nparameters:\n tmpDir: $PHPSTAN_CACHE_PATH" > phpstan.neon'
- 'echo -e "includes:\n - phpstan.neon.dist\n\nparameters:\n tmpDir: $PHPSTAN_CACHE_PATH" > phpstan.neon'
script:
- php
composer/bin/phpstan analyse
--memory-limit=1G
--no-progress
composer/bin/phpstan analyse
--memory-limit=1G
--no-progress
--level=$PHPSTAN_LEVEL
--error-format=gitlab > $PHPSTAN_CODE_QUALITY_REPORT
after_script:
......@@ -284,11 +278,12 @@ test-functional:
variables:
FUNCTIONAL_XML_REPORT: $REPORT_DIR/functional-report.xml
FUNCTIONAL_CODE_QUALITY_REPORT: $REPORT_DIR/functional-codequality.json
MYSQL_HOST: mysql
cache:
<<: *composer-cache
policy: pull
services:
- *mariadb-service
- *mysql-service
allow_failure: false
interruptible: true
before_script:
......@@ -414,7 +409,7 @@ packaging:
expire_in: never
build_image:
image:
image:
name: gcr.io/kaniko-project/executor:debug
entrypoint: [""]
stage: build
......
#!/bin/bash
set -e
importSQLFile() {
mysql --default-character-set=utf8mb4\
--init-command="SET NAMES UTF8;"\
-u $MYSQL_USER\
-h $MYSQL_HOST\
-p$MYSQL_PASSWORD\
$MYSQL_DATABASE\
< $1
}
if [ $(mysql -u $MYSQL_USER -h $MYSQL_HOST -p$MYSQL_PASSWORD $MYSQL_DATABASE -e "show tables;" --batch | wc -l) -eq 0 ]; then
# Setup mysql database
echo "INSTALL DB"
mysql -u $MYSQL_USER -h $MYSQL_HOST -p$MYSQL_PASSWORD $MYSQL_DATABASE < ./db/studip.sql
importSQLFile ./db/studip.sql
echo "INSTALL DEFAULT DATA"
mysql -u $MYSQL_USER -h $MYSQL_HOST -p$MYSQL_PASSWORD $MYSQL_DATABASE < ./db/studip_default_data.sql
mysql -u $MYSQL_USER -h $MYSQL_HOST -p$MYSQL_PASSWORD $MYSQL_DATABASE < ./db/studip_resources_default_data.sql
importSQLFile ./db/studip_default_data.sql
importSQLFile ./db/studip_resources_default_data.sql
echo "INSTALL ROOTUSER"
mysql -u $MYSQL_USER -h $MYSQL_HOST -p$MYSQL_PASSWORD $MYSQL_DATABASE < ./db/studip_root_user.sql
importSQLFile ./db/studip_root_user.sql
# Check if demodata is required
if [ ! -z $DEMO_DATA ]; then
echo "INSTALL DEMODATA"
mysql -u $MYSQL_USER -h $MYSQL_HOST -p$MYSQL_PASSWORD $MYSQL_DATABASE < ./db/studip_demo_data.sql
importSQLFile ./db/studip_demo_data.sql
echo "INSTALL MVV_DEMODATA"
mysql -u $MYSQL_USER -h $MYSQL_HOST -p$MYSQL_PASSWORD $MYSQL_DATABASE < ./db/studip_mvv_demo_data.sql
importSQLFile ./db/studip_mvv_demo_data.sql
echo "INSTALL RESOURCES-DEMODATA"
mysql -u $MYSQL_USER -h $MYSQL_HOST -p$MYSQL_PASSWORD $MYSQL_DATABASE < ./db/studip_resources_demo_data.sql
importSQLFile ./db/studip_resources_demo_data.sql
fi
echo "INSTALLATION FINISHED"
......
{
"extends": "stylelint-config-standard",
"extends": "stylelint-config-standard-scss",
"rules": {
"alpha-value-notation": null,
"at-rule-empty-line-before": null,
"at-rule-no-vendor-prefix": null,
"block-closing-brace-empty-line-before": null,
"block-closing-brace-newline-after": null,
"block-closing-brace-newline-before": null,
"block-closing-brace-space-before": null,
"block-no-empty": null,
"block-opening-brace-newline-after": null,
"block-opening-brace-space-after": null,
"block-opening-brace-space-before": null,
"color-function-notation": null,
"color-hex-case": null,
"color-hex-length": null,
"color-no-invalid-hex": null,
"comment-empty-line-before": null,
"comment-whitespace-inside": null,
"declaration-bang-space-after": null,
"declaration-bang-space-before": null,
"custom-property-empty-line-before": null,
"declaration-block-no-duplicate-properties": null,
"declaration-block-no-redundant-longhand-properties": null,
"declaration-block-no-shorthand-property-overrides": null,
"declaration-block-semicolon-newline-after": null,
"declaration-block-semicolon-space-before": null,
"declaration-block-single-line-max-declarations": null,
"declaration-block-trailing-semicolon": null,
"declaration-colon-newline-after": null,
"declaration-colon-space-after": null,
"declaration-colon-space-before": null,
"declaration-empty-line-before": null,
"font-family-name-quotes": null,
"font-family-no-missing-generic-family-keyword": null,
"function-comma-space-after": null,
"function-comma-space-before": null,
"function-name-case": null,
"function-no-unknown": null,
"function-parentheses-newline-inside": null,
"function-parentheses-space-inside": null,
"function-url-quotes": null,
"hue-degree-notation": null,
"indentation": null,
"import-notation": "string",
"keyframes-name-pattern": null,
"length-zero-no-unit": null,
"max-empty-lines": null,
"max-line-length": null,
"media-feature-range-notation": "prefix",
"media-feature-name-no-vendor-prefix": null,
"no-descending-specificity": null,
"no-duplicate-selectors": null,
"no-empty-first-line": null,
"no-empty-source": null,
"no-eol-whitespace": null,
"no-extra-semicolons": null,
"no-invalid-position-at-import-rule": null,
"no-irregular-whitespace": null,
"no-missing-end-of-source-newline": null,
"number-leading-zero": null,
"number-max-precision": null,
"number-no-trailing-zeros": null,
"property-no-vendor-prefix": null,
"rule-empty-line-before": null,
"selector-attribute-quotes": null,
"selector-class-pattern": null,
"selector-combinator-space-after": null,
"selector-combinator-space-before": null,
"selector-descendant-combinator-no-non-space": null,
"selector-id-pattern": null,
"selector-list-comma-newline-after": null,
"selector-no-vendor-prefix": null,
"selector-not-notation": null,
"selector-pseudo-element-colon-notation": null,
"shorthand-property-no-redundant-values": null,
"string-quotes": null,
"value-keyword-case": null,
"value-list-max-empty-lines": null,
"value-no-vendor-prefix": null
"value-no-vendor-prefix": null,
"custom-property-pattern": [
"^([a-z][a-z0-9]*-+)*[a-z0-9]+$",
{"message": "Expected custom property name to be kebab-case-ish (multiple dashes are allowed"}
],
"scss/at-extend-no-missing-placeholder": null,
"scss/at-mixin-pattern": null,
"scss/dollar-variable-empty-line-before": null,
"scss/dollar-variable-pattern": null,
"scss/double-slash-comment-empty-line-before": null,
"scss/function-unquote-no-unquoted-strings-inside": null,
"scss/no-global-function-names": null
},
"overrides": [
{
"files": ["**/*.scss"],
"customSyntax": "postcss-scss",
"rules": {
"at-rule-no-unknown": null
}
}
],
"ignoreFiles": [
"resource/assets/stylesheets/jquery-ui.structure.css",
"resources/assets/stylesheets/vendor/*"
......
This diff is collapsed.
......@@ -38,7 +38,7 @@ PROJECT_NAME = Stud.IP
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 5.4
PROJECT_NUMBER = 6.1
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
......
......@@ -2,8 +2,8 @@ Die Installation von Stud.IP ist halb so schlimm:
es müssen lediglich ein paar Dateien kopiert werden und ein paar
Programme laufen ;-)
Vorausgesetzt wird ein Webserver wie Apache2 oder nginx mit PHP-7.4 Modulen und
eine MySQL 5.7.6 Datenbank.
Vorausgesetzt wird ein Webserver wie Apache2 oder nginx mit PHP-8.1 Modulen und
eine MySQL 8 oder MariaDB 10.2.2 Datenbank.
Was genau zu tun ist, steht in
> doc/de/studip-installation-guide-de-401.pdf
......
......@@ -87,12 +87,12 @@ optimize-icons: npm
find public/assets/images/icons/blue -type f | xargs -P0 npx svgo -q --config=config/svgo.config.js
icons: optimize-icons
find public/assets/images/icons/blue -type f -print0 | xargs -0 -n1 -I{} echo 'sed "s/#28497c/#000000/" {} > {}' | sed 's#icons/blue#icons/black#2' | sh
find public/assets/images/icons/blue -type f -print0 | xargs -0 -n1 -I{} echo 'sed "s/#28497c/#00962d/" {} > {}' | sed 's#icons/blue#icons/green#2' | sh
find public/assets/images/icons/blue -type f -print0 | xargs -0 -n1 -I{} echo 'sed "s/#28497c/#6e6e6e/" {} > {}' | sed 's#icons/blue#icons/grey#2' | sh
find public/assets/images/icons/blue -type f -print0 | xargs -0 -n1 -I{} echo 'sed "s/#28497c/#cb1800/" {} > {}' | sed 's#icons/blue#icons/red#2' | sh
find public/assets/images/icons/blue -type f -print0 | xargs -0 -n1 -I{} echo 'sed "s/#28497c/#ffffff/" {} > {}' | sed 's#icons/blue#icons/white#2' | sh
find public/assets/images/icons/blue -type f -print0 | xargs -0 -n1 -I{} echo 'sed "s/#28497c/#ffad00/" {} > {}' | sed 's#icons/blue#icons/yellow#2' | sh
find public/assets/images/icons/blue -type f -print0 | xargs -0 -n1 -I{} echo 'sed "s/#28497c/#000000/g" {} > {}' | sed 's#icons/blue#icons/black#2' | sh
find public/assets/images/icons/blue -type f -print0 | xargs -0 -n1 -I{} echo 'sed "s/#28497c/#00962d/g" {} > {}' | sed 's#icons/blue#icons/green#2' | sh
find public/assets/images/icons/blue -type f -print0 | xargs -0 -n1 -I{} echo 'sed "s/#28497c/#6e6e6e/g" {} > {}' | sed 's#icons/blue#icons/grey#2' | sh
find public/assets/images/icons/blue -type f -print0 | xargs -0 -n1 -I{} echo 'sed "s/#28497c/#cb1800/g" {} > {}' | sed 's#icons/blue#icons/red#2' | sh
find public/assets/images/icons/blue -type f -print0 | xargs -0 -n1 -I{} echo 'sed "s/#28497c/#ffffff/g" {} > {}' | sed 's#icons/blue#icons/white#2' | sh
find public/assets/images/icons/blue -type f -print0 | xargs -0 -n1 -I{} echo 'sed "s/#28497c/#ffad00/g" {} > {}' | sed 's#icons/blue#icons/yellow#2' | sh
# default rules for gettext handling
js-%.pot: $(VUE_SOURCES)
......
# Stud.IP v6.0
# Stud.IP v6.1
**15.03.2024**
**dd.mm.jjjj**
## Neue Features
......@@ -8,16 +8,7 @@
## Breaking changes
- Im Rahmen von [Issue #3788](https://gitlab.studip.de/studip/studip/-/issues/3788) wurden die Zusätze an allen Icons entfernt. Dadurch kann es sein, dass manche Plugins nicht mehr erscheinen. Diese müssen dann auf eine Variante ohne Zusätze umgestellt werden.
- Die Funktion `get_config()` wurde entfernt. Stattdessen muss die Methode `Config::get()->getValue('CONFIG_KEY')` bzw. der Shortcut `Config::get()->CONFIG_KEY` verwendet werden. ([Issue #2797](https://gitlab.studip.de/studip/studip/-/issues/2797))
- Die Funktion `smile()` wurde entfernt. Sie kann ersatzlos entfernt werden. ([Issue #3158](https://gitlab.studip.de/studip/studip/-/issues/3158))
- Die Funktion `transformBeforeSave()` wurde entfernt. Sie kann ersatzlos entfernt werden. ([Issue #3159](https://gitlab.studip.de/studip/studip/-/issues/3159))
- Die schon lange nicht mehr genutzten Methoden zum Setzen, Auslesen und Enfernen von Schmuckgrafiken von Bildern für die Sidebar wurde entfernt. Die Methoden `Sidebar::setImage()`, `Sidebar::getImage()` sowie `Sidebar::removeImage()` müssen ersatzlos entfernt werden. ([Issue #3157](https://gitlab.studip.de/studip/studip/-/issues/3157))
- Der zweite Parameter für die Methode `Navigation::setImage()` wurde entfernt. Der Parameter schien sich auf das Bild zu beziehen, hat aber Attribute an dem Link gesetzt. Stattdessen muss die Methode `Navigation::setLinkAttributes()` verwendet werden. ([Issue #3578](https://gitlab.studip.de/studip/studip/-/issues/3578))
- Die Unterstützung für LESS-Stylsheets in Plugins wurde entfernt. Als Alternative wird SCSS unterstützt. ([Issue #2720](https://gitlab.studip.de/studip/studip/-/issues/2720))
- Die Funktionen `studip_json_encode()` und `studip_json_decode()` wurden entfernt. Stattdessen müssen die Methode `json_encode()` und `json_decode()` verwendet werden. ([Issue #3814](https://gitlab.studip.de/studip/studip/-/issues/3814))
- Die `MembersModel.php` wurde entfernt ([Issue #3811](https://gitlab.studip.de/studip/studip/-/issues/3811))
- Die `admission.inc.php` wurde entfernt. ([Issue #3812](https://gitlab.studip.de/studip/studip/-/issues/3812))
-
## Security related issues
......
RELEASE 5.4.alpha
RELEASE 6.1.alpha
......@@ -3,6 +3,21 @@ class Accessibility_FormsController extends StudipController
{
protected $with_session = true;
public function before_filter(&$action, &$args)
{
parent::before_filter($action, $args);
if (
Config::get()->REPORT_BARRIER_MODE === 'off'
|| (
Config::get()->REPORT_BARRIER_MODE === 'logged-in'
&& !User::findCurrent()
)
) {
throw new AccessDeniedException();
}
}
public function report_barrier_action()
{
PageLayout::setTitle(_('Barriere melden'));
......@@ -131,6 +146,9 @@ class Accessibility_FormsController extends StudipController
}
$this->form->addPart($personal_data_part);
$this->form->addPart(new \Studip\Forms\Captcha());
$this->form->setSaveButtonText(_('Barriere melden'));
$this->form->setSaveButtonName('report');
$this->form->setURL($this->report_barrierURL());
......
......@@ -53,7 +53,7 @@ class ActivityfeedController extends AuthenticatedController
unset($modules[Context::INSTITUTE]['participants']);
unset($modules[Context::INSTITUTE]['schedule']);
$standard_plugins = PluginManager::getInstance()->getPlugins("StandardPlugin");
$standard_plugins = PluginManager::getInstance()->getPlugins(StandardPlugin::class);
foreach ($standard_plugins as $plugin) {
if ($plugin instanceof ActivityProvider) {
$modules[Context::COURSE][$plugin->getPluginName()] = $plugin->getPluginName();
......@@ -67,7 +67,7 @@ class ActivityfeedController extends AuthenticatedController
'blubber' => _('Blubber'),
];
$homepage_plugins = PluginEngine::getPlugins('HomepagePlugin');
$homepage_plugins = PluginEngine::getPlugins(HomepagePlugin::class);
foreach ($homepage_plugins as $plugin) {
if ($plugin->isActivated($GLOBALS['user']->id, 'user')) {
if ($plugin instanceof ActivityProvider) {
......@@ -92,4 +92,193 @@ class ActivityfeedController extends AuthenticatedController
PageLayout::setTitle(_('Aktivitäten konfigurieren'));
}
public function load_action(): void
{
$user = User::findCurrent();
// failsafe einbauen - falls es keine älteren Aktivitäten mehr im System gibt, Abbruch!
$oldest_activity = \Studip\Activity\Activity::getOldestActivity();
$max_age = $oldest_activity ? $oldest_activity->mkdate : time();
$contexts = [];
// create system context
$system_context = new \Studip\Activity\SystemContext($user);
$contexts[] = $system_context;
$contexts[] = new \Studip\Activity\UserContext($user, $user);
$user->contacts->each(function ($another_user) use (&$contexts, $user) {
$contexts[] = new \Studip\Activity\UserContext($another_user, $user);
});
if (!in_array($user->perms, ['admin','root'])) {
// create courses and institutes context
foreach (\Course::findMany($user->course_memberships->pluck('seminar_id')) as $course) {
$contexts[] = new \Studip\Activity\CourseContext($course, $user);
}
foreach (\Institute::findMany($user->institute_memberships->pluck('institut_id')) as $institute) {
$contexts[] = new \Studip\Activity\InstituteContext($institute, $user);
}
}
// add filters
$filter = new \Studip\Activity\Filter();
$start = Request::int('start', strtotime('yesterday'));
$end = Request::int('end', time());
$scrollfrom = Request::int('scrollfrom', false);
$filtertype = Request::get('filtertype', '');
$objectType = Request::get('object_type');
$filter->setObjectType($objectType);
$objectId = Request::get('object_id');
$filter->setObjectId($objectId);
$context = Request::get('context_type');
$filter->setContext($context);
$contextId = Request::get('context_id');
$filter->setContextId($contextId);
if (!empty($filtertype)) {
$filter->setType(json_decode($filtertype));
}
if ($scrollfrom) {
// shorten "watch-window" by one second to prevent duplication of activities
$scrollfrom -= 1;
if ($scrollfrom > $max_age){
$end = $scrollfrom;
$start = strtotime('yesterday', $end);
$data = [];
$backtrack = 1;
while (empty($data)) {
$filter->setStartDate($start);
$filter->setEndDate($end);
$data = $this->getStreamData($contexts, $filter);
if ($start < $max_age) {
break;
}
// move "watch-window" back one day at a time
$end = $start - 1;
$start = strtotime("-{$backtrack} days", $start);
// enforce maximum "watch-window", currently 2 weeks
$backtrack = min(14, $backtrack + 1);
}
} else {
$data = false;
}
} else {
$filter->setStartDate($start);
$filter->setEndDate($end);
$data = $this->getStreamData($contexts, $filter);
}
// set etag for preventing resending the same stuff over and over again
$etag = md5(serialize($data));
$this->response->add_header('ETag', '"' . $etag . '"');
if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && $this->etagMatches($etag, $_SERVER['HTTP_IF_NONE_MATCH'])) {
$this->set_status(304);
$this->render_nothing();
return;
}
if (isset($_SERVER['HTTP_IF_MATCH']) && !$this->etagMatches($etag, $_SERVER['HTTP_IF_MATCH'])) {
$this->set_status(412);
$this->render_nothing();
return;
}
$this->render_json($data);
}
/**
* private helper function to get stream data for given contexts and filter
*
* @param $contexts
* @param $filter
* @return array
*/
private function getStreamData($contexts, $filter): array
{
$stream = new Studip\Activity\Stream($contexts, $filter);
$data = $stream->toArray();
foreach ($data as $key => $act) {
$actor = [
'type' => $act['actor_type'],
'id' => $act['actor_id'],
];
if ($act['actor_type'] == 'user') {
$a_user = \User::findFull($act['actor_id']);
$actor['details'] = $this->getMiniUser($a_user ?: new \User());
} elseif ($act['actor_type'] === 'anonymous') {
$actor['details'] = [
'name' => _('Anonym'),
];
}
unset($data[$key]['actor_type']);
unset($data[$key]['actor_id']);
$data[$key]['actor'] = $actor;
}
return $data;
}
private function getMiniUser(User $user): array
{
$avatar = \Avatar::getAvatar($user->id);
return [
'id' => $user->id,
'name' => $this->getNamesOfUser($user),
'avatar_small' => $avatar->getURL(\Avatar::SMALL),
'avatar_medium' => $avatar->getURL(\Avatar::MEDIUM),
'avatar_normal' => $avatar->getURL(\Avatar::NORMAL),
'avatar_original' => $avatar->getURL(\Avatar::NORMAL)
];
}
private function getNamesOfUser(User $user): array
{
return [
'username' => $user->username,
'formatted' => $user->getFullName(),
'family' => $user->nachname,
'given' => $user->vorname,
'prefix' => $user->title_front,
'suffix' => $user->title_rear,
];
}
// Helper method checking if a ETag value list includes the current ETag.
private function etagMatches(string $etag, string $list)
{
if ($list === '*') {
return true;
}
return in_array(
$etag,
preg_split('/\s*,\s*/', $list)
);
}
}
......@@ -31,7 +31,9 @@ class Admin_AdditionalController extends AuthenticatedController
"Veranstaltung zu verändern."));
}
Sidebar::get()->addWidget(new CourseManagementSelectWidget());
if ($GLOBALS['perm']->have_studip_perm('admin', $this->course->id)) {
Sidebar::get()->addWidget(new CourseManagementSelectWidget());
}
}
/**
......
<?php
/**
*
**/
class Admin_ApiController extends AuthenticatedController
{
/**
*
**/
public function before_filter(&$action, &$args)
{
parent::before_filter($action, $args);
require_once 'lib/bootstrap-api.php';
$GLOBALS['perm']->check('root');
Navigation::activateItem('/admin/config/api');
PageLayout::setTitle(_('API Verwaltung'));
$this->types = [
'website' => _('Website'),
'desktop' => _('Herkömmliches Desktopprogramm'),
'mobile' => _('Mobile App')
];
// Sidebar
$views = new ViewsWidget();
$views->addLink(_('Registrierte Applikationen'),
$this->url_for('admin/api'))
->setActive($action === 'index');
$views->addLink(_('Globale Zugriffseinstellungen'),
$this->url_for('admin/api/permissions'))
->setActive($action == 'permissions');
$views->addLink(_('Konfiguration'),
$this->url_for('admin/api/config'))
->setActive($action == 'config');
Sidebar::get()->addWidget($views);
$actions = new ActionsWidget();
$actions->addLink(_('Neue Applikation registrieren'),
$this->url_for('admin/api/edit'),
Icon::create('add', 'clickable'))
->asDialog();
Sidebar::get()->addWidget($actions);
}
/**
*
**/
public function index_action()
{
$this->consumers = RESTAPI\Consumer\Base::findAll();
$this->routes = RESTAPI\Router::getInstance()->getRoutes(true);
}
/**
*
**/
public function render_keys($id)
{
$consumer = RESTAPI\Consumer\Base::find($id);
return [
'Consumer Key = ' . $consumer->auth_key,
'Consumer Secret = ' . $consumer->auth_secret,
];
}
/**
*
**/
public function keys_action($id)
{
$details = $this->render_keys($id);
if (Request::isXhr()) {
$this->render_text(implode('<br>', $details));
} else {
PageLayout::postMessage(MessageBox::info(_('Die Schlüssel in den Details dieser Meldung sollten vertraulich behandelt werden!'), $details, true));
$this->redirect('admin/api/#' . $id);
}
}
/**
*
**/
public function edit_action($id = null)
{
$consumer = $id
? RESTAPI\Consumer\Base::find($id)
: RESTAPI\Consumer\Base::create(Request::option('consumer_type') ?: 'oauth');
if (Request::submitted('store')) {
$errors = [];
$consumer->active = (bool) Request::int('active');
$consumer->title = Request::get('title');
$consumer->contact = Request::get('contact');
$consumer->email = Request::get('email');
$consumer->callback = Request::get('callback');
$consumer->url = Request::get('url');
$consumer->type = Request::get('type') ?: null;
$consumer->commercial = Request::int('commercial');
$consumer->notes = Request::get('notes');
$consumer->description = Request::get('description');
if (!empty($errors)) {
$message = MessageBox::error(_('Folgende Fehler sind aufgetreten:'), $errors);
PageLayout::postMessage($message);
return;
}
$consumer->store();
if ($id) {
$message = MessageBox::success(_('Die Applikation wurde erfolgreich gespeichert.'));
} else {
$details = $this->render_keys($consumer->id);
$message = MessageBox::success(_('Die Applikation wurde erfolgreich erstellt, die Schlüssel finden Sie in den Details dieser Meldung.'), $details, true);
}
PageLayout::postMessage($message);
$this->redirect('admin/api/index#' . $consumer->id);
return;
}
$this->consumer = $consumer;
$this->id = $id;
}
/**
*
**/
public function toggle_action($id, $state = null)
{
$consumer = RESTAPI\Consumer\Base::find($id);
$consumer->active = $state === null ? !$consumer->active : ($state === 'on');
$consumer->store();
$message = $state
? _('Die Applikation wurde erfolgreich aktiviert.')
: _('Die Applikation wurde erfolgreich deaktiviert.');
PageLayout::postMessage(MessageBox::success($message));
$this->redirect('admin/api/#' . $consumer->id);
}
/**
*
**/
public function delete_action($id)
{
if (!Request::isPost()) {
throw new MethodNotAllowedException();
}
if ($consumer = RESTAPI\Consumer\Base::find($id)) {
$consumer->delete();
PageLayout::postSuccess(_('Die Applikation wurde erfolgreich gelöscht.'));
}
$this->redirect('admin/api');
}
/**
*
**/
public function permissions_action($consumer_id = null)
{
if (Request::submitted('store')) {
$perms = Request::getArray('permission');
$permissions = RESTAPI\ConsumerPermissions::get($consumer_id ?: 'global');
foreach ($perms as $route => $methods) {
foreach ($methods as $method => $granted) {
$permissions->set(urldecode($route), urldecode($method), (bool)$granted, true);
}
}
$permissions->store();
PageLayout::postMessage(MessageBox::success(_('Die Zugriffsberechtigungen wurden erfolgreich gespeichert')));
$this->redirect($consumer_id ? 'admin/api' : 'admin/api/permissions');
return;
}
$title = $consumer_id ? _('Zugriffsberechtigungen') : _('Globale Zugriffsberechtigungen');
$title .= ' - ' . PageLayout::getTitle();
PageLayout::setTitle($title);
$this->consumer_id = $consumer_id;
$this->router = RESTAPI\Router::getInstance();
$this->routes = $this->router->getRoutes(true, false);
$this->permissions = RESTAPI\ConsumerPermissions::get($consumer_id ?: 'global');
$this->global = $consumer_id ? RESTAPI\ConsumerPermissions::get('global') : false;
}
public function config_action()
{
$this->config = Config::get();
if (Request::isPost()) {
$this->config->store('API_ENABLED', Request::int('active', 0));
$this->config->store('API_OAUTH_AUTH_PLUGIN', Request::option('auth'));
PageLayout::postMessage(MessageBox::success(_('Die Einstellungen wurden gespeichert.')));
$this->redirect('admin/api/config');
}
}
}
......@@ -44,11 +44,11 @@ class Admin_AutoinsertController extends AuthenticatedController
if (Request::submitted('suchen')) {
if (Request::get('sem_search')) {
$this->sem_search = Request::get('sem_search');
$this->sem_select = Request::option('sem_select');
$this->sem_select = Request::option('sem_select') ?: null;
$search = new SeminarSearch();
$this->seminar_search = $search->getResults
(Request::get('sem_search'),
['search_sem_sem' => Request::option('sem_select')]
$this->seminar_search = $search->getResults(
$this->sem_search,
['search_sem_sem' => $this->sem_select]
);
if (count($this->seminar_search) == 0) {
PageLayout::postInfo(_('Es wurden keine Veranstaltungen gefunden.'));
......@@ -159,7 +159,7 @@ class Admin_AutoinsertController extends AuthenticatedController
} elseif (!count($filters)) {
PageLayout::postError(_('Keine Filterkriterien gewählt'));
} else {
$seminar = Seminar::GetInstance($seminar_id);
$course = Course::find($seminar_id);
$userlookup = new UserLookup();
foreach ($filters as $type => $values) {
......@@ -170,8 +170,11 @@ class Admin_AutoinsertController extends AuthenticatedController
foreach ($user_ids as $user_id) {
if ($force || !AutoInsert::checkAutoInsertUser($seminar_id, $user_id)) {
$real_users += $seminar->addMember($user_id) ? 1 : 0;
AutoInsert::saveAutoInsertUser($seminar_id, $user_id);
$user = User::find($user_id);
if ($user) {
$real_users += $course->addMember($user) ? 1 : 0;
AutoInsert::saveAutoInsertUser($seminar_id, $user_id);
}
}
}
......@@ -182,9 +185,8 @@ class Admin_AutoinsertController extends AuthenticatedController
count($user_ids),
sprintf(
'<a href="%s">%s</a>',
URLHelper::getLink('dispatch.php/course/details/', ['cid' => $seminar->getId()]),
htmlReady($seminar->getName()
)
URLHelper::getLink('dispatch.php/course/details/', ['cid' => $course->id]),
htmlReady($course->name)
)
);
$details = [_('Etwaige Abweichungen der Personenzahlen enstehen durch bereits vorhandene bzw. wieder ausgetragene Personen.')];
......
......@@ -156,9 +156,7 @@ class Admin_BannerController extends AuthenticatedController
}
break;
case 'seminar':
try {
Seminar::getInstance($target);
} catch (Exception $e) {
if (!Course::exists($target)) {
$errors[] = _('Die angegebene Veranstaltung existiert nicht. '
.'Bitte geben Sie eine gültige Veranstaltungs-ID ein.');
}
......@@ -199,14 +197,14 @@ class Admin_BannerController extends AuthenticatedController
->defaultValue($banner->target,$seminar_name['name'])
->render();
}
if ($banner->target_type == 'user') {
$this->user = QuickSearch::get('user', new StandardSearch('username'))
->setInputStyle('width: 240px')
->defaultValue($banner->target, $banner->target)
->render();
}
if ($banner->target_type == 'inst') {
$institut_name = get_object_name($banner->target, 'inst');
$this->institut = QuickSearch::get('institut', new StandardSearch('Institut_id'))
......
......@@ -47,16 +47,14 @@ class Admin_CacheController extends AuthenticatedController
$this->sidebar->addWidget($views);
if ($this->enabled) {
$actions = new ActionsWidget();
$actions->addLink(
_('Cache leeren'),
$this->url_for('admin/cache/flush'),
Icon::create('decline'),
['data-confirm' => _('Soll der gesamte Inhalt des Caches wirklich gelöscht werden?')]
);
$this->sidebar->addWidget($actions);
}
$actions = new ActionsWidget();
$actions->addLink(
_('Cache leeren'),
$this->url_for('admin/cache/flush'),
Icon::create('decline'),
['data-confirm' => _('Soll der gesamte Inhalt des Caches wirklich gelöscht werden?')]
);
$this->sidebar->addWidget($actions);
}
/**
......@@ -64,28 +62,29 @@ class Admin_CacheController extends AuthenticatedController
*/
public function settings_action()
{
if ($this->enabled) {
$this->types = CacheType::findAndMapBySQL(function (CacheType $type) {
return $type->toArray();
}, "1 ORDER BY `cache_id`");
$currentCache = Config::get()->SYSTEMCACHE;
$currentCacheClass = CacheType::findOneByClass_name($currentCache['type']);
$this->cache = $currentCacheClass->class_name;
$this->config = $currentCacheClass->class_name::getConfig();
} else {
PageLayout::postWarning(
_('Caching ist systemweit ausgeschaltet, daher kann hier nichts konfiguriert werden.'));
}
$currentCache = Config::get()->SYSTEMCACHE;
$currentCacheClass = CacheType::findOneByClass_name($currentCache['type']);
$this->render_vue_app(
Studip\VueApp::create('CacheAdministration')
->withProps([
'enabled' => (bool) $this->enabled,
'currentCache' => $currentCacheClass->class_name,
'currentConfig' => $currentCacheClass->class_name::getConfig(),
'cacheTypes' => CacheType::findAndMapBySQL(
fn(CacheType $type) => $type->toArray(),
"1 ORDER BY `cache_id`"
),
])
);
}
/**
* Fetches necessary configuration for given cache type.
*
* @param string $className
*/
public function get_config_action($className)
public function get_config_action()
{
$className = Request::get('cache');
$type = CacheType::findOneByClass_name($className);
$this->render_json($type->class_name::getConfig());
......@@ -111,7 +110,7 @@ class Admin_CacheController extends AuthenticatedController
// Store settings to global config.
if (Config::get()->store('SYSTEMCACHE', $settings)) {
PageLayout::postSuccess(_('Die Einstellungen wurden gespeichert.'));
StudipCacheFactory::unconfigure();
\Studip\Cache\Factory::unconfigure();
}
$this->relocate('admin/cache/settings');
......@@ -122,7 +121,7 @@ class Admin_CacheController extends AuthenticatedController
*/
public function flush_action()
{
$cache = StudipCacheFactory::getCache();
$cache = \Studip\Cache\Factory::loadSystemCache(true);
$cache->flush();
PageLayout::postSuccess(_('Die Inhalte des Caches wurden gelöscht.'));
......@@ -135,7 +134,7 @@ class Admin_CacheController extends AuthenticatedController
*/
public function stats_action()
{
$cache = StudipCacheFactory::getCache();
$cache = \Studip\Cache\Factory::getCache();
$this->stats = $cache->getStats();
}
......